Merge origin/master into codex/invariant-service-seam
# Conflicts: # docs/config-catalog.md # docs/event-producer-consumer.md # docs/module-graph.md # packages/examples/agent-spine-demo/README.md # packages/examples/agent-spine-demo/package.json # packages/examples/agent-spine-demo/src/index.ts # packages/examples/tui-demo/package.json # packages/support/invariants/src/scoped-events.generated.ts # packages/ui/acp/package.json # packages/ui/tui/package.json # packages/ui/tui/tests/tui.spec.ts # pnpm-lock.yaml
This commit is contained in:
@@ -41,12 +41,15 @@ export type { AgentUnderTest } from './launcher.ts'
|
||||
* the client observes the selected update (`agent_message_chunk` by default),
|
||||
* then cancels and awaits completion. A named `waitForToolCallUpdate` keeps the
|
||||
* step open for a terminal tool update that may follow the prompt response.
|
||||
* `promptAndWaitForAgentMessage` arms an exact text-chunk waiter before sending
|
||||
* the prompt, then keeps the application live until that later update arrives.
|
||||
*/
|
||||
export type InputStep =
|
||||
| { op: 'initialize'; terminalOutput?: boolean }
|
||||
| { op: 'newSession' }
|
||||
| { op: 'newSessionExpectError'; additionalDirectories?: string[] }
|
||||
| { op: 'prompt'; text: string }
|
||||
| { op: 'promptAndWaitForAgentMessage'; text: string; waitForText: string }
|
||||
| { op: 'promptExpectError'; text: string }
|
||||
| {
|
||||
op: 'promptAndCancel'
|
||||
@@ -331,6 +334,15 @@ async function runStep(
|
||||
await client.prompt({ sessionId, prompt: [{ type: 'text', text: step.text }] })
|
||||
return
|
||||
}
|
||||
case 'promptAndWaitForAgentMessage': {
|
||||
const sessionId = getSessionId()
|
||||
if (sessionId === undefined) throw new Error('snapshot-harness: promptAndWaitForAgentMessage before newSession')
|
||||
const updateDone = waitForUpdate(update => update.sessionUpdate === 'agent_message_chunk'
|
||||
&& update.content.type === 'text' && update.content.text === step.waitForText)
|
||||
await client.prompt({ sessionId, prompt: [{ type: 'text', text: step.text }] })
|
||||
await updateDone
|
||||
return
|
||||
}
|
||||
case 'promptExpectError': {
|
||||
const sessionId = getSessionId()
|
||||
if (sessionId === undefined) throw new Error('snapshot-harness: promptExpectError before newSession')
|
||||
|
||||
@@ -334,6 +334,21 @@ describe('runScenario', () => {
|
||||
expect(result.rawStdout.indexOf('thinking about it')).toBeLessThan(result.rawStdout.indexOf('cancelled'))
|
||||
})
|
||||
|
||||
it('promptAndWaitForAgentMessage keeps the app live through a matching later update', { timeout: 20_000 }, async () => {
|
||||
const { fixtureFile } = await scenario({ prompt: 'respond' })
|
||||
const result = await runScenario(
|
||||
{
|
||||
steps: [...boot, {
|
||||
op: 'promptAndWaitForAgentMessage',
|
||||
text: 'go',
|
||||
waitForText: 'thinking about it',
|
||||
}],
|
||||
},
|
||||
{ agent: AGENT, mode: 'replay', fixtureFile },
|
||||
)
|
||||
expect(result.rawStdout).toContain('thinking about it')
|
||||
})
|
||||
|
||||
it('promptAndCancel can bracket cancellation with tool-call updates', { timeout: 20_000 }, async () => {
|
||||
const { fixtureFile } = await scenario({
|
||||
prompt: 'hang-until-cancel',
|
||||
@@ -443,6 +458,7 @@ describe('runScenario', () => {
|
||||
|
||||
it.each([
|
||||
[{ op: 'prompt', text: 'x' }, /prompt before newSession/],
|
||||
[{ op: 'promptAndWaitForAgentMessage', text: 'x', waitForText: 'later' }, /promptAndWaitForAgentMessage before newSession/],
|
||||
[{ op: 'promptExpectError', text: 'x' }, /promptExpectError before newSession/],
|
||||
[{ op: 'promptAndCancel', text: 'x' }, /promptAndCancel before newSession/],
|
||||
[{ op: 'cancel' }, /cancel before newSession/],
|
||||
|
||||
@@ -38,7 +38,7 @@ The current executable companions protect these relationships:
|
||||
| `dsh-llm`, `dsh-llm-retry`, `dsh-tools`, `dsh-system-prompt` | Stream grammar, durable retry position and bounds, tool-pipeline stages and frozen results, and authoritative prompt-assembly data. |
|
||||
| `dsh-compact`, `dsh-hook-protocol`, `dsh-sandbox-policy` | Durable compaction and hook pairing, compaction metadata, and sandbox-mode vocabulary. |
|
||||
| `dsh-fs`, `dsh-subagent`, `dsh-workflow` | Filesystem event identity, provider/child pairing, and workflow/agent lifecycle identity. |
|
||||
| `dsh-goal` | Durable goal source/content agreement, revision and lifecycle transitions, timestamps, and sequential admitted rounds. |
|
||||
| `dsh-goal`, `dsh-goal-session` | Durable goal source/content agreement, revision and lifecycle transitions, timestamps, sequential admitted rounds, and reconstructed continuation prompts. |
|
||||
| `dsh-permission`, `dsh-user-approval` | Active-preset references and approval asked/decided audit pairing. |
|
||||
| `dsh-tasks`, `dsh-tool-todo` | Task snapshot lifecycle/ownership fields and durable whole-list todo structure. |
|
||||
| `dsh-time-context` | Durable clock readings agree with the session's open turn and next pre-step position, elapsed baseline, and event timestamp. |
|
||||
|
||||
Reference in New Issue
Block a user