Merge remote-tracking branch 'origin/master' into feat/send-unify

# Conflicts:
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/pty/pty-local/tests/index.spec.ts
#	packages/session-query/session-query/tests/tracing.spec.ts
This commit is contained in:
Turtle
2026-07-23 22:41:45 +08:00
333 changed files with 10751 additions and 907 deletions
+9
View File
@@ -5,6 +5,8 @@ import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
const CONTROL_PROBE = '\u001b]2;MODEL_CONTROLLED\u0007\u001b[999CMODEL_CURSOR\u009b31mMODEL_C1'
const INITIAL_TEXT = `I need one decision before I continue. ${CONTROL_PROBE}`
const FINAL_TEXT = 'Decision received. Scripted TUI run complete.'
const DEFAULT_MODE_PROBE = 'Confirm the scripted run left plan mode.'
const DEFAULT_MODE_TEXT = 'Default mode confirmed.'
// The `skill` scenario types `/skill:scripted-skill`; the manual-invocation front
// door delivers the loaded skill as a user turn wrapped in `<skill name="…">`. The
// body marker below lives in the fixture skill, so echoing it back proves the whole
@@ -53,6 +55,13 @@ class ScriptedTuiAdapter extends LlmAdapter {
.filter(block => block.type === 'text')
.map(block => block.text)
.join('\n')
if (lastText.includes(DEFAULT_MODE_PROBE)) {
if (options.system?.includes('Stay in plan mode for this scripted TUI test.')) {
throw new Error('the scripted TUI request retained plan guidance after /plan off')
}
for (const chunk of textChunks(DEFAULT_MODE_TEXT)) yield chunk
return
}
if (lastText.includes(SKILL_BLOCK_OPEN)) {
const ack = lastText.includes(SKILL_BODY_MARKER)
? SKILL_RECEIVED_TEXT
-1
View File
@@ -197,7 +197,6 @@ export async function runTuiPtySmoke(options: TuiPtySmokeOptions): Promise<strin
/* v8 ignore next -- every caller passes configPath or configArgs; the fallback keeps the type total */
: [options.configPath ?? './cordis.yml'],
tsconfigPath: options.tsconfigPath,
exposeInternals: true,
env: {
DSH_HOME: join(cwd, '.dsh'),
DSH_AGENTS_HOME: join(cwd, '.agents'),
@@ -1,7 +1,7 @@
terminal 100x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Reply with exactly the word: — DSH TUI snapshot"
cursor hidden column=1 viewportRow=28 bufferRow=28
cursor hidden column=1 viewportRow=33 bufferRow=33
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-blue bold
@@ -11,8 +11,8 @@ buffer
2| " deepseek-v4-flash • main-session"
style 1-34 dim
3| <blank>
4| " Entering plan mode (applies from the next step). "
style 1-48 fg=bright-black
4| " Entering plan mode (applies from the next step). Use /plan off to leave. "
style 1-72 fg=bright-black
5| <blank>
6| "▌ "
style 0-0 fg=bright-blue
@@ -33,31 +33,38 @@ buffer
style 1-9 fg=bright-magenta bold
15| " ONE "
16| <blank>
17| "▌ "
17| " Leaving plan mode (applies from the next step). "
style 1-47 fg=bright-black
18| <blank>
19| " Context · plan-mode "
style 1-19 dim
20| " The user switched this session back to the default mode. "
style 1-56 fg=bright-black
21| <blank>
22| "▌ "
style 0-0 fg=bright-blue
18| "▌ You "
23| "▌ You "
style 0-0 fg=bright-blue
style 2-4 fg=bright-blue bold
19| "▌ Reply with exactly the word: TWO. No tools. "
24| "▌ Reply with exactly the word: TWO. No tools. "
style 0-0 fg=bright-blue
20| "▌ "
25| "▌ "
style 0-0 fg=bright-blue
21| <blank>
22| " Reasoning "
26| <blank>
27| " Reasoning "
style 1-9 fg=bright-black italic
23| " The user wants me to reply with exactly the word \"TWO\" and no tools. "
28| " The user wants me to reply with exactly the word \"TWO\" and no tools. "
style 1-68 fg=bright-black italic
24| <blank>
25| " Assistant "
29| <blank>
30| " Assistant "
style 1-9 fg=bright-magenta bold
26| " TWO "
27| "────────────────────────────────────────────────────────────────────────────────────────────────────"
31| " TWO "
32| "────────────────────────────────────────────────────────────────────────────────────────────────────"
style 0-99 dim
28| " "
33| " "
style 1-1 inverse
29| "────────────────────────────────────────────────────────────────────────────────────────────────────"
34| "────────────────────────────────────────────────────────────────────────────────────────────────────"
style 0-99 dim
30| "deepseek-v4-flash /workspace/project ↑2.9k ↓41 cache 49% 3% co"
35| "deepseek-v4-flash /workspace/project ↑2.9k ↓41 cache 49% 3% co"
style 0-92 dim
style 95-99 dim
31-35| <blank>
@@ -87,14 +87,16 @@ describe('tui-agent keyless smoke (real Loader tree in a PTY)', () => {
const output = await smoke({
label: 'tui-agent boot',
actions: [
{ waitFor: 'main-session-', send: '/plan\r' },
{ waitFor: 'Entering plan mode (applies from the next step).', send: '/exit\r' },
{ waitFor: 'main-session-', send: '/plan' },
{ waitFor: '[off|message] — Enter or leave plan mode', send: '\r' },
{ waitFor: 'Entering plan mode (applies from the next step). Use /plan off to leave.', send: '/exit\r' },
],
})
expect(output).toContain('DEEPSEEK')
expect(output).toContain('HARNESS')
expect(output).toContain('main-session-')
expect(output).toContain('Entering plan mode (applies from the next step).')
expect(output).toContain('[off|message] — Enter or leave plan mode')
expect(output).toContain('Entering plan mode (applies from the next step). Use /plan off to leave.')
// Borderless: no box-drawing frame around the banner.
expect(output).not.toContain('╭')
expect(output).not.toContain('╮')
@@ -120,12 +122,16 @@ describe('tui-agent keyless smoke (real Loader tree in a PTY)', () => {
// window title as `<session title> — <configured title>` via OSC 0.
// Gating /status on it keeps the assertion race-free; the diagnostics
// card is then exercised through the same real Loader/PTY composition.
{ waitFor: 'scripted session title — DeepSeek Harness', send: '/status\r' },
{ waitFor: 'scripted session title — DeepSeek Harness', send: '/plan off\r' },
{ waitFor: 'Leaving plan mode (applies from the next step).', send: 'Confirm the scripted run left plan mode.\r' },
{ waitFor: 'Default mode confirmed.', send: '/status\r' },
{ waitFor: 'Session status', send: '/exit\r' },
],
})
expect(output).toContain('I need one decision before I continue.')
expect(output).toContain('Entering plan mode (applies from the next step).')
expect(output).toContain('Entering plan mode (applies from the next step). Use /plan off to leave.')
expect(output).toContain('Leaving plan mode (applies from the next step).')
expect(output).toContain('Default mode confirmed.')
expect(output).toContain(String.raw`\x1b]2;MODEL_CONTROLLED\x07`)
expect(output).toContain(String.raw`\x1b[999CMODEL_CURSOR`)
expect(output).toContain(String.raw`\x9b31mMODEL_C1`)
+26 -2
View File
@@ -47,6 +47,7 @@ interface Scenario {
expectedEventCounts?: Record<string, number>
childSessions?: number
enterPlanMode?: boolean
leavePlanModeAfterFirstTurn?: boolean
recorded: boolean
seedWorkspace?: boolean
/**
@@ -62,8 +63,9 @@ const SCENARIOS: Scenario[] = [
name: 'multi-turn-conversation',
composition: 'native',
expectedTools: [],
expectedEventCounts: { 'plan/mode': 1 },
expectedEventCounts: { 'plan/mode': 2 },
enterPlanMode: true,
leavePlanModeAfterFirstTurn: true,
recorded: true,
},
{
@@ -294,6 +296,12 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
remainingPrompts = prompts.slice(1)
}
if (scenario.leavePlanModeAfterFirstTurn === true) {
terminal.send('/plan off')
terminal.send('\r')
await settleTerminal(terminal)
}
for (const prompt of remainingPrompts) {
terminal.send(prompt)
terminal.send('\r')
@@ -310,7 +318,9 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
expect(events.filter(event => event.type === type), `${scenario.name} must emit ${type}`).toHaveLength(count)
}
if (scenario.enterPlanMode === true) {
expect(ctx.planMode.get(agent)).toEqual({ active: true })
expect(ctx.planMode.get(agent)).toEqual({
active: scenario.leavePlanModeAfterFirstTurn !== true,
})
const planMode = events.find(event => event.type === 'plan/mode')
if (planMode === undefined || firstHeader === undefined) {
throw new Error('plan-mode command snapshot needs plan/mode before its first request/header')
@@ -320,6 +330,20 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
const firstMessage = events.find(event => event.type === 'user/message')
expect(firstMessage?.data.content).toEqual([{ type: 'text', text: prompts[0] }])
}
if (scenario.leavePlanModeAfterFirstTurn === true) {
const planModes = events.filter(event => event.type === 'plan/mode')
expect(planModes.map(event => event.data.active)).toEqual([true, false])
const headers = events.filter(event => event.type === 'request/header')
const exit = planModes[1]
const afterExit = headers[1]
if (exit === undefined || afterExit === undefined) {
throw new Error('active plan exit snapshot needs a committed exit and changed request header')
}
expect(exit.seq).toBeLessThan(afterExit.seq)
expect(afterExit.data.header.system).not.toContain('Snapshot plan mode instructions.')
expect(events.filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin').map(event => (event.data as { content: unknown }).content))
.toContainEqual([{ type: 'text', text: 'The user switched this session back to the default mode.' }])
}
expect(events.filter(event => event.type === 'tool/result').every(event => !event.data.isError)).toBe(true)
expect(events.filter(event => event.type === 'turn/end').every(event => event.data.reason.kind !== 'error')).toBe(true)
if (scenario.name === 'dynamic-workflow' || scenario.name === 'cordis-dynamic-toolchain') {