Merge remote-tracking branch 'origin/master' into worktree/acp-automation-protocol
# Conflicts: # .agents/notes/implemented/architecture/2026-06-14-session-persistence.md # .agents/notes/implemented/architecture/2026-06-20-package-hierarchy.md # .agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md # .agents/notes/implemented/feature/2026-06-14-acp-agent-client-protocol.md # .agents/notes/implemented/feature/2026-06-14-acp-multi-session.md # .agents/notes/implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.md # .agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.i18n.yaml # .agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.md # .agents/notes/implemented/feature/2026-07-19-model-facing-goal-tools.zh.md # .agents/notes/implemented/simplification/2026-07-04-trim-acp-bridge-unreachable-surface.md # docs/architecture.i18n.yaml # docs/cookbook/extension-cookbook.i18n.yaml # docs/cookbook/extension-cookbook.md # docs/cookbook/extension-cookbook.zh.md # docs/core-data-structures/approval.md # docs/core-data-structures/user-interaction.md # docs/event-producer-consumer.md # docs/persistence-catalog.md # docs/testing.md # docs/tool-catalog.md # examples/acp-agent/tests/fixtures/live-mode-switching-2026-07-07.session.jsonl # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/permission-switching/session.jsonl # packages/goal/tool-goal/README.md # packages/ui/acp/README.md # packages/ui/acp/acp-feature-support.md # packages/ui/acp/src/index.ts # packages/ui/acp/tests/bridge.spec.ts # packages/ui/acp/tests/dispose.spec.ts # packages/ui/acp/tests/edges.spec.ts # packages/ui/acp/tests/turns.spec.ts
This commit is contained in:
@@ -235,7 +235,7 @@ describe('dsh-agent-spine-demo bundle', () => {
|
||||
agentOptions: { provider: 'mock', model: 'mock' },
|
||||
})
|
||||
|
||||
handle.agent.send([{ type: 'text', text: 'recover' }])
|
||||
handle.agent.followup([{ type: 'text', text: 'recover' }])
|
||||
await waitForIdle(ctx, handle.agent)
|
||||
|
||||
expect(adapter.requests).toBe(2)
|
||||
@@ -335,7 +335,7 @@ describe('dsh-agent-spine-demo bundle', () => {
|
||||
})
|
||||
const agent = handle.agent
|
||||
|
||||
agent.send([{ type: 'text', text: 'hi' }])
|
||||
agent.followup([{ type: 'text', text: 'hi' }])
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
const sentText = adapter.requests[0]?.messages.map(messageText).join('\n')
|
||||
@@ -364,7 +364,7 @@ describe('dsh-agent-spine-demo bundle', () => {
|
||||
agentOptions: { provider: 'mock', model: 'mock' },
|
||||
})
|
||||
|
||||
handle.agent.send([{ type: 'text', text: 'hi' }])
|
||||
handle.agent.followup([{ type: 'text', text: 'hi' }])
|
||||
await waitForIdle(ctx, handle.agent)
|
||||
|
||||
expect(adapter.requests[0]?.messages).toEqual([{ role: 'user', content: [{ type: 'text', text: 'hi' }] }])
|
||||
@@ -454,7 +454,7 @@ describe('dsh-agent-spine-demo bundle', () => {
|
||||
agentOptions: { provider: 'mock', model: 'mock' },
|
||||
})
|
||||
|
||||
handle.agent.send([{ type: 'text', text: 'hi' }])
|
||||
handle.agent.followup([{ type: 'text', text: 'hi' }])
|
||||
await waitForIdle(ctx, handle.agent)
|
||||
|
||||
expect(messageText(adapter.requests[0]?.messages[0])).toContain('workspace rule before skills')
|
||||
|
||||
@@ -290,7 +290,7 @@ export async function runOneShot(ctx: Context, options: OneShotOptions): Promise
|
||||
try {
|
||||
/* v8 ignore next -- skips send only when cancellation wins the listener-registration race above */
|
||||
if (!settled) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition
|
||||
agent.send([{ type: 'text', text: options.task }])
|
||||
agent.followup([{ type: 'text', text: options.task }])
|
||||
}
|
||||
await turnEnded
|
||||
} finally {
|
||||
|
||||
@@ -369,7 +369,7 @@ describe('runOneShot and executeCli', () => {
|
||||
const { ctx, agent } = await harness([textResponse('streamed')])
|
||||
const other = ctx.sessions.create(SessionId('unrelated'))
|
||||
let injected = false
|
||||
ctx.on('agent/queued', (subject) => {
|
||||
ctx.on('agent/inbox/enqueue', (subject) => {
|
||||
if (subject !== agent || injected) return
|
||||
injected = true
|
||||
agent.inject([{ type: 'text', text: 'startup injection' }], { source: { kind: 'plugin', plugin: 'test' } })
|
||||
@@ -383,7 +383,7 @@ describe('runOneShot and executeCli', () => {
|
||||
expect(events[0]).toMatchObject({ type: 'turn/start', data: { turn: 2, trigger: { kind: 'message' } } })
|
||||
expect(events.at(-1)).toMatchObject({ type: 'turn/end', data: { turn: 2 } })
|
||||
expect(lines.slice(0, -1).every(line => line['sessionId'] === agent.session.id)).toBe(true)
|
||||
expect(events.some(event => event.type === 'context/message')).toBe(false)
|
||||
expect(events.some(event => event.type === 'user/message' && event.data.source.kind !== 'user')).toBe(false)
|
||||
})
|
||||
|
||||
it('emits partial data and a diagnostic for non-completed turns', async () => {
|
||||
@@ -471,7 +471,7 @@ describe('runOneShot and executeCli', () => {
|
||||
startup.ctx.on('session/event', (session, event) => {
|
||||
if (session === startup.agent.session && event.type === 'assistant/chunk') started()
|
||||
})
|
||||
startup.agent.send([{ type: 'text', text: 'first' }])
|
||||
startup.agent.followup([{ type: 'text', text: 'first' }])
|
||||
await running
|
||||
const startupAbort = new AbortController()
|
||||
const waiting = runOneShot(startup.ctx, { task: 'second', signal: startupAbort.signal })
|
||||
@@ -481,7 +481,7 @@ describe('runOneShot and executeCli', () => {
|
||||
|
||||
const queued = await harness([textResponse('unused')])
|
||||
const queuedAbort = new AbortController()
|
||||
queued.ctx.on('agent/queued', (agent) => {
|
||||
queued.ctx.on('agent/inbox/enqueue', (agent) => {
|
||||
if (agent === queued.agent) queuedAbort.abort('cancel queued')
|
||||
})
|
||||
await expect(runOneShot(queued.ctx, { task: 'task', signal: queuedAbort.signal })).rejects.toThrow('cancel queued')
|
||||
|
||||
@@ -41,10 +41,11 @@ Swappable LLM, bash, filesystem, and other capability providers remain in the le
|
||||
| `persistenceCompression` | `'zstd'` | JSONL artifact encoding (`'zstd'` or raw `'none'`) |
|
||||
| `sessionReferences` | service defaults | Cross-session candidate and snapshot limits routed to `dsh-session-reference` |
|
||||
| `welcome` | `ready.` | TUI subtitle |
|
||||
| `resumeCommand` | — | Exit and no-host fallback command template; the selector itself uses session query and host handoff |
|
||||
| `ui` | owner defaults | TUI presentation settings such as reasoning, color, and card height |
|
||||
| `resumeSessionId` | — | Exact persisted session to resume |
|
||||
|
||||
Fresh runs mint a `main-session-<uuid>` session id and pass it to both the TUI and configured agent. Resumed runs bind both components to `resumeSessionId`. The TUI mounts before the spine so it can render a matching config-start failure instead of leaving a blank terminal.
|
||||
Fresh runs mint a `main-session-<uuid>` session id and pass it to both the TUI and configured agent. Resumed runs bind both components to `resumeSessionId`. The TUI mounts before the spine so it can render a matching config-start failure instead of leaving a blank terminal. The app composes persistence and session query for `/resume`; an embedding host may additionally provide `tuiResumeHost` for in-place process handoff.
|
||||
|
||||
## The bin
|
||||
|
||||
|
||||
Reference in New Issue
Block a user