Merge remote-tracking branch 'origin/master' into feat/ripgrep-packaged-binary

This commit is contained in:
Huanqi Cao
2026-08-03 20:19:49 +08:00
368 changed files with 9510 additions and 1302 deletions

View File

@@ -0,0 +1,10 @@
# An overlay whose `llm-pi-ai` config fails schema validation: `providers` is a
# dict keyed by provider name, and a list is the shape users reach for. The
# entry rejects while `ui-tui` — mounted concurrently by the Loader — already
# holds the terminal, which is the boot failure the fail-loud release hook
# exists for.
- id: llm-pi-ai
config:
providers:
- provider: openai
apiKey: keyless-invalid-shape

View File

@@ -117,8 +117,19 @@ class ScriptedTuiAdapter extends LlmAdapter {
const blocks = lastMessage?.content ?? []
if (blocks.some(block => block.type === 'tool-result')) {
const answered = blocks.some(block => block.type === 'tool-result' && block.toolCallId === BASH_FAILURE_CALL_ID)
for (const chunk of textChunks(answered ? BASH_FAILURE_TEXT : FINAL_TEXT)) yield chunk
const answeredBash = blocks.some(block =>
block.type === 'tool-result' && block.toolCallId === BASH_FAILURE_CALL_ID)
if (answeredBash) {
for (const chunk of textChunks(BASH_FAILURE_TEXT)) yield chunk
return
}
const toolResultText = blocks.flatMap(block => block.type === 'tool-result'
? block.content.flatMap(content => content.type === 'text' ? [content.text] : [])
: []).join('\n')
if (toolResultText !== '{"answers":[{"id":"mode","selected":["Safe"],"custom":"Release notes"}]}') {
throw new Error(`the scripted TUI request received an unexpected question answer: ${toolResultText}`)
}
for (const chunk of textChunks(FINAL_TEXT)) yield chunk
return
}
if (lastText.includes(BASH_FAILURE_PROBE)) {
@@ -140,6 +151,7 @@ class ScriptedTuiAdapter extends LlmAdapter {
id: 'mode',
header: 'Execution mode',
question: 'How should the scripted run proceed?',
multi_select: true,
options: [
{ label: 'Safe', description: 'Use the guarded path.' },
{ label: 'Fast', description: 'Use the shorter path.' },

View File

@@ -1,7 +1,7 @@
terminal 100x36 buffer=normal length=66 base=30 viewport=30
terminal 100x36 buffer=normal length=68 base=32 viewport=32
lifecycle started=1 stopped=0 progress=inactive
title "Reply with exactly the word: — DSH TUI snapshot"
cursor hidden column=7 viewportRow=35 bufferRow=65
cursor hidden column=7 viewportRow=35 bufferRow=67
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
@@ -73,56 +73,60 @@ buffer
style 0-64 dim
37| "/compact — Compact older conversation history "
style 0-44 dim
38| "/exit — Exit after the active turn reaches idle "
38| "/details [collapsed|expanded|hidden] [reasoning [on|off]] — Select tool-card visibility and "
style 0-99 dim
39| "reasoning display "
style 0-16 dim
40| "/exit — Exit after the active turn reaches idle "
style 0-46 dim
39| "/help — Show keyboard shortcuts and commands "
41| "/help — Show keyboard shortcuts and commands "
style 0-43 dim
40| "/model [[provider/]model] — Show or switch this session's model "
42| "/model [[provider/]model] — Show or switch this session's model "
style 0-62 dim
41| "/palette — Show every color and attribute role this terminal renders "
43| "/palette — Show every color and attribute role this terminal renders "
style 0-67 dim
42| "/quit — Exit after the active turn reaches idle "
44| "/quit — Exit after the active turn reaches idle "
style 0-46 dim
43| "/reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
45| "/reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
style 0-87 dim
44| "/resume — List this workspace's resumable sessions "
46| "/resume — List this workspace's resumable sessions "
style 0-49 dim
45| "/status — Show session diagnostics, system prompt, and registered tools "
47| "/status — Show session diagnostics, system prompt, and registered tools "
style 0-70 dim
46| "/skill:<name> [instructions] — load a skill into the conversation "
48| "/skill:<name> [instructions] — load a skill into the conversation "
style 0-64 dim
47| <blank>
48| "Context · snapshot-injector"
49| <blank>
50| "Context · snapshot-injector"
style 0-26 dim
49| "Injected while compaction was running. "
51| "Injected while compaction was running. "
style 0-37 dim
50| <blank>
51| "… earlier context was compacted … "
style 0-32 dim
52| <blank>
53| "You "
53| "… earlier context was compacted … "
style 0-32 dim
54| <blank>
55| "You "
style 0-2 fg=bright-magenta bold underline
54| "Reply with exactly the word: TWO. No tools. "
55| <blank>
56| "Compacted 2 history items (~387 tokens). "
style 0-39 dim
56| "Reply with exactly the word: TWO. No tools. "
57| <blank>
58| "Assistant "
58| "Compacted 2 history items (~387 tokens). "
style 0-39 dim
59| <blank>
60| "Assistant "
style 0-8 fg=bright-magenta bold underline
59| "Reasoning "
61| "Reasoning "
style 0-8 dim italic
60| "The user wants me to reply with exactly the word \"TWO\" and no tools. "
62| "The user wants me to reply with exactly the word \"TWO\" and no tools. "
style 0-67 dim italic
61| "TWO "
62| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
63| "TWO "
64| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
63| <blank>
64| "/workspace/project deepseek-v4-flash ↑2.9k ↓41 cache 49% 3% cont"
65| <blank>
66| "/workspace/project deepseek-v4-flash ↑2.9k ↓41 cache 49% 3% cont"
style 0-49 fg=bright-magenta bold
style 52-68 dim
style 71-90 dim
style 93-99 dim
65| " dsh ◍ "
67| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse

View File

@@ -26,6 +26,9 @@ const dshBinScript = fileURLToPath(new URL('../src/bin.ts', import.meta.url))
// `--config` layers an overlay over the shared base, so the default surface
// needs no config argument at all; these are the overlays under test.
const scriptedConfigPath = fileURLToPath(new URL('./fixtures/tui-scripted.cordis.yml', import.meta.url))
// An overlay whose `llm-pi-ai` config fails validation, so an entry rejects
// while the TUI already holds the terminal.
const invalidProviderConfigPath = fileURLToPath(new URL('./fixtures/tui-invalid-provider.cordis.yml', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
const firstRunSnapshots = fileURLToPath(new URL('./tui-first-run-snapshots/', import.meta.url))
const synchronizedFrameEnd = '\x1b[?2026l'
@@ -256,6 +259,7 @@ const SELECT_PRO_MODEL = [
{ waitFor: 'scripted TUI ready.', send: '/model\r' },
{ waitFor: 'Select model', send: '\x1b[B\x1b[Z\r' },
] as const
const ANSWER_MULTI_WITH_CUSTOM = ' \tRelease notes\r'
describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
it.each([
@@ -414,6 +418,28 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
expect(output).toContain('\u001B[?2004l')
}, PTY_SMOKE_TEST_TIMEOUT_MS)
// The Loader mounts entries concurrently, so `ui-tui` can already own the
// terminal when a sibling entry rejects on its config. Exiting without the
// tree's own teardown left raw mode and bracketed paste set on the user's
// shell, and the pending Device Attributes reply landed there as literal
// text. The transactional mount must settle (an HMR initial-scan refresh
// once deadlocked its rollback into a silent exit 13) so `boot` disposes
// the tree — reaching the TUI's own shutdown — and rejects with the
// labelled diagnostic.
it('restores the terminal when a sibling entry fails to validate during boot', async () => {
const output = await smoke({
label: 'dsh invalid provider config',
tempDirPrefix: 'dsh-tui-invalid-config-',
configPath: invalidProviderConfigPath,
expectedExitCode: 1,
})
expect(output).toContain('dsh: plugin tree failed to load:')
expect(output).toContain('$.providers')
// Bracketed paste is disabled again, which only `ProcessTerminal.stop()`
// writes — proof the tree was disposed rather than exited out from under.
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('switches models, streams a response, answers a user-question dialog, and exits cleanly', async () => {
const output = await smoke({
label: 'dsh conversation',
@@ -425,7 +451,10 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
// The question text first appears in the streamed tool-call card. Wait
// for the dialog's input legend so Enter cannot arrive before it owns
// terminal input when pre-dispatch policy yields.
{ waitFor: 'Tab custom answer • ↑/↓ navigate • Enter submit • Esc interrupt', send: '\r' },
{
waitFor: 'Tab custom answer • ↑/↓ navigate • Space toggle • Enter submit • Esc interrupt',
send: ANSWER_MULTI_WITH_CUSTOM,
},
{ waitFor: 'Decision received. Scripted TUI run complete.', send: '' },
// Session title: the first user message drives the first-message-llm
// provider's tool-less title call; the scripted adapter answers it, the
@@ -451,6 +480,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
expect(output).not.toContain('\u001B[999CMODEL_CURSOR')
expect(output).not.toContain('\u009B31mMODEL_C1')
expect(output).toContain('Safe')
expect(output).toContain('Release notes')
expect(output).toContain('\u001B]0;scripted session title — DeepSeek Harness\u0007')
expect(output).toContain('Session status')
expect(output).toContain('Title')
@@ -836,7 +866,7 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {
actions: [
...SELECT_PRO_MODEL,
{ waitFor: 'Model selected: tui-scripted/tui-scripted-model-pro.', send: 'exercise the TUI\r' },
{ waitFor: 'How should the scripted run proceed?', send: '\r' },
{ waitFor: 'How should the scripted run proceed?', send: ANSWER_MULTI_WITH_CUSTOM },
{ waitFor: 'Decision received. Scripted TUI run complete.', send: '/exit\r' },
],
inspect: async (cwd) => { context = await readLoggedRequestContext(cwd) },