Merge origin/master: web permission sandbox, default pi-ai providers
This commit is contained in:
+24
-3
@@ -20,6 +20,13 @@ const SKILL_BLOCK_OPEN = '<skill name="scripted-skill">'
|
||||
const SKILL_BODY_MARKER = 'SCRIPTED SKILL BODY MARKER'
|
||||
const SKILL_RECEIVED_TEXT = 'Scripted skill body received.'
|
||||
const TITLE_TEXT = 'scripted session title'
|
||||
// The failing-bash scenario proves the terminal card reports a non-zero exit
|
||||
// exactly once: the model-facing result carries the `[exit code: N]` marker, and
|
||||
// the card turns it into its own `[exit N]` pill instead of showing both.
|
||||
const BASH_FAILURE_PROBE = 'Run the failing scripted command.'
|
||||
const BASH_FAILURE_COMMAND = 'printf "SCRIPTED_BASH_FAILED\\n"; exit 3'
|
||||
const BASH_FAILURE_TEXT = 'Scripted bash failure observed.'
|
||||
const BASH_FAILURE_CALL_ID = CallId('call-bash-failure')
|
||||
|
||||
function textChunks(text: string): StreamChunk[] {
|
||||
return [
|
||||
@@ -108,9 +115,23 @@ class ScriptedTuiAdapter extends LlmAdapter {
|
||||
return
|
||||
}
|
||||
|
||||
const hasToolResult = lastMessage?.content.some(block => block.type === 'tool-result') ?? false
|
||||
if (hasToolResult) {
|
||||
for (const chunk of textChunks(FINAL_TEXT)) yield chunk
|
||||
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
|
||||
return
|
||||
}
|
||||
if (lastText.includes(BASH_FAILURE_PROBE)) {
|
||||
const bashArgs = JSON.stringify({ command: BASH_FAILURE_COMMAND, description: 'Run the failing scripted command' })
|
||||
yield { type: 'block-start', index: 0, blockType: 'tool-call' }
|
||||
yield { type: 'tool-call-delta', index: 0, id: BASH_FAILURE_CALL_ID, name: 'bash', argumentsDelta: bashArgs }
|
||||
yield {
|
||||
type: 'block-end',
|
||||
index: 0,
|
||||
block: { type: 'tool-call', id: BASH_FAILURE_CALL_ID, name: 'bash', arguments: bashArgs },
|
||||
}
|
||||
yield { type: 'usage', usage: { inputTokens: 20, outputTokens: 10 } }
|
||||
yield { type: 'finish', reason: { kind: 'tool-calls' } }
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
# The smoke's log inspection reads plain `.jsonl`; keep the scripted
|
||||
# fixture uncompressed like the other snapshot-facing configs.
|
||||
persistenceCompression: none
|
||||
resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"
|
||||
resumeCommand: 'dsh --resume {session}'
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
welcome: 'scripted TUI ready.'
|
||||
|
||||
@@ -365,11 +365,11 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {
|
||||
expect(output).toContain('must be a top-level YAML array of loader patch entries')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('routes the --resume flag into the config resume intake, failing loud on a missing id', async () => {
|
||||
// The flag path end to end: apps/cli parses `--resume missing-session` and
|
||||
// provides the id on the boot context, the shipped config's `!!js` reads it
|
||||
// as a bare identifier, and the resume fails loud — proving the printed
|
||||
// `dsh --resume <id>` hint reaches the config resume intake with no env var.
|
||||
it('routes the --resume flag into the launcher session-identity slot, failing loud on a missing id', async () => {
|
||||
// The flag path end to end: apps/cli parses `--resume missing-session`,
|
||||
// provides it as the launcher-owned identity on the boot context, and the
|
||||
// resume fails loud — proving the printed hint reaches the app's resume
|
||||
// intake with no config key and no environment variable.
|
||||
const output = await smoke({
|
||||
label: 'dsh resume flag failure',
|
||||
tempDirPrefix: 'dsh-resume-flag-',
|
||||
@@ -380,6 +380,70 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {
|
||||
expect(output).toContain('ui-tui: session "missing-session" failed to start:')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('prints the launcher-owned resume command on exit, naming the booted config', async () => {
|
||||
// The exit line is built by apps/cli from this invocation, so it must carry
|
||||
// `--config`: a hint that omitted it would resume into the default tree.
|
||||
const output = await smoke({
|
||||
label: 'dsh goodbye message',
|
||||
tempDirPrefix: 'dsh-goodbye-',
|
||||
binScript: dshBinScript,
|
||||
configPath: scriptedConfigPath,
|
||||
actions: [{ waitFor: 'scripted TUI ready.', send: '/exit\r' }],
|
||||
})
|
||||
expect(output).toMatch(/To resume this session: dsh --resume=main-session-[0-9a-f-]{36} --config/)
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('keeps resume working when the personal overlay replaces the whole tui-agent config', async () => {
|
||||
// Loader patches replace a targeted `config` key wholesale, so a personal
|
||||
// overlay that omits a resume key used to silently disable the exit hint.
|
||||
// Launcher-owned identity and exit line make that unreachable.
|
||||
const output = await smoke({
|
||||
label: 'dsh overlay keeps resume',
|
||||
tempDirPrefix: 'dsh-overlay-resume-',
|
||||
binScript: dshBinScript,
|
||||
configArgs: [],
|
||||
prepare: seedWorkspace({
|
||||
personal: {
|
||||
'config.yaml': [
|
||||
'- id: tui-agent',
|
||||
" name: '@deepseek-ai/dsh-tui-demo'",
|
||||
' config:',
|
||||
' provider: deepseek',
|
||||
' model: deepseek-v4-flash',
|
||||
' workspaceContext: false',
|
||||
' welcome: OVERLAY REPLACED THE CONFIG.',
|
||||
'',
|
||||
].join('\n'),
|
||||
},
|
||||
}),
|
||||
actions: [{ waitFor: 'OVERLAY REPLACED THE CONFIG.', send: '/exit\r' }],
|
||||
})
|
||||
expect(output).toMatch(/To resume this session: dsh --resume=main-session-[0-9a-f-]{36}/)
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('reports a failing bash command exactly once, as the terminal card exit pill', async () => {
|
||||
// The model-facing result ends in `[exit code: 3]`, which the terminal card
|
||||
// consumes into its own `[exit 3]` pill. Rendering both would report the same
|
||||
// exit twice, so the marker must not survive into the card body.
|
||||
const output = await smoke({
|
||||
label: 'tui-agent bash exit pill',
|
||||
tempDirPrefix: 'dsh-bash-exit-pill-',
|
||||
configPath: scriptedConfigPath,
|
||||
actions: [
|
||||
...SELECT_PRO_MODEL,
|
||||
{
|
||||
waitFor: 'Model selected: tui-scripted/tui-scripted-model-pro.',
|
||||
send: 'Run the failing scripted command.\r',
|
||||
},
|
||||
{ waitFor: 'Scripted bash failure observed.', send: '/exit\r' },
|
||||
],
|
||||
})
|
||||
// The command really ran: its stdout is in the card body.
|
||||
expect(output).toContain('SCRIPTED_BASH_FAILED')
|
||||
expect(output).toContain('[exit 3]')
|
||||
expect(output).not.toContain('[exit code: 3]')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('tells the model its source path and offers the bundled maintenance skills', async () => {
|
||||
// The launcher resolves the checkout root three hops up from apps/cli/{src,lib};
|
||||
// this test file sits an equal depth under the same root, so the same hop applies.
|
||||
|
||||
Reference in New Issue
Block a user