Merge branch 'stack/agent-profiles-3-wire' into stack/agent-profiles-5-web-ui

This commit is contained in:
Yichen Jiang
2026-08-07 16:22:02 +08:00
109 changed files with 2801 additions and 320 deletions

View File

@@ -230,7 +230,10 @@ export interface CommandNode {
commandId: CommandId
/** Command name (run payload's structured field); null when the run fell outside the window. */
name: string | null
/** Verbatim rawInput after the name, separator whitespace included (run payload); null when the run fell outside the window. */
/**
* Verbatim rawInput after the name, including separator whitespace; null
* when omitted by the command or when the run fell outside the window.
*/
args: string | null
/** Settlement outcome (done payload); null while the command is still executing. */
outcome: { kind: 'success' | 'error'; text?: string } | null

View File

@@ -313,10 +313,10 @@ export class TranscriptAdapter {
// enter the client program, so this wire consumer narrows structurally
// (the same posture as tool/code-dispatch in session.ts).
if ((event.type as string) === 'command/run') {
const data = event.data as unknown as { commandId: CommandId; name: string; args: string }
const data = event.data as unknown as { commandId: CommandId; name: string; args?: string }
this.commandIdx.set(data.commandId, {
kind: 'command', seq: event.seq, time: event.time,
commandId: data.commandId, name: data.name, args: data.args, outcome: null,
commandId: data.commandId, name: data.name, args: data.args ?? null, outcome: null,
})
return true
}

View File

@@ -90,6 +90,8 @@ export const ev = {
} }),
commandRun: (seq: number, commandId: string, name: string, args = ''): SessionEvent =>
at(seq, { type: 'command/run', data: { commandId, name, args, source: { kind: 'user' } } }),
commandRunWithoutInput: (seq: number, commandId: string, name: string): SessionEvent =>
at(seq, { type: 'command/run', data: { commandId, name, source: { kind: 'user' } } }),
commandDone: (seq: number, commandId: string, kind: 'success' | 'error' = 'success', text?: string): SessionEvent =>
at(seq, { type: 'command/done', data: { commandId, kind, ...text === undefined ? {} : { text } } }),
/** A compaction's log-only `compact/summary` provenance record. */

View File

@@ -432,6 +432,14 @@ describe('TranscriptAdapter', () => {
expect(adapter.nodes()[0]).toMatchObject({ kind: 'command', name: 'goal', args: ' ship it', outcome: null })
})
it('represents command input omitted by the host as null', () => {
const adapter = new TranscriptAdapter()
adapter.reset([ev.commandRunWithoutInput(0, 'cmd-private', 'feedback')])
expect(adapter.nodes()[0]).toMatchObject({
kind: 'command', name: 'feedback', args: null, outcome: null,
})
})
it('soft-falls a done-only window into a node built from the done (cross-window cut)', () => {
const adapter = new TranscriptAdapter()
adapter.reset([ev.commandDone(80, 'cmd-3', 'error', '失败了')])