Merge branch 'codex/simp-unify-agent-session-id' into codex/simp-ui-identity-residue
# Conflicts: # docs/event-producer-consumer.md
This commit is contained in:
@@ -131,7 +131,7 @@ export interface Config {
|
|||||||
|
|
||||||
Depends on: [`AgentOptions`](../packages/core/agent/src/index.ts) · [`SessionId`](../packages/core/session/src/index.ts)
|
Depends on: [`AgentOptions`](../packages/core/agent/src/index.ts) · [`SessionId`](../packages/core/session/src/index.ts)
|
||||||
|
|
||||||
Source: [`packages/core/agent-loop/src/index.ts:344`](../packages/core/agent-loop/src/index.ts)
|
Source: [`packages/core/agent-loop/src/index.ts:353`](../packages/core/agent-loop/src/index.ts)
|
||||||
|
|
||||||
## `@deepseek-ai/dsh-bash-local`
|
## `@deepseek-ai/dsh-bash-local`
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ A declarative agent entry failed before it could publish a live agent. Consumers
|
|||||||
'agent-loop/config-start-failed'(sessionId: SessionId, error: unknown): void
|
'agent-loop/config-start-failed'(sessionId: SessionId, error: unknown): void
|
||||||
```
|
```
|
||||||
|
|
||||||
Source: [`packages/core/agent-loop/src/index.ts:339`](../../packages/core/agent-loop/src/index.ts)
|
Source: [`packages/core/agent-loop/src/index.ts:348`](../../packages/core/agent-loop/src/index.ts)
|
||||||
|
|
||||||
## `approval/*`
|
## `approval/*`
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ async createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<Agent
|
|||||||
async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>
|
async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>
|
||||||
```
|
```
|
||||||
|
|
||||||
Source: [`packages/core/agent-loop/src/index.ts:359`](../../packages/core/agent-loop/src/index.ts)
|
Source: [`packages/core/agent-loop/src/index.ts:368`](../../packages/core/agent-loop/src/index.ts)
|
||||||
|
|
||||||
## `ctx.agents` — `AgentRegistry`
|
## `ctx.agents` — `AgentRegistry`
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
|||||||
|
|
||||||
| Event | Mode | Declared in | Dispatchers | Listeners |
|
| Event | Mode | Declared in | Dispatchers | Listeners |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:339`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:348`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
||||||
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:304`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:304`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
||||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:319`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:319`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
||||||
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:593`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:593`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ const INACTIVE_STATES: ReadonlySet<FiberState> = new Set([
|
|||||||
FiberState.FAILED,
|
FiberState.FAILED,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
/** Render an arbitrary thrown value without letting coercion escape containment. */
|
||||||
|
function renderThrown(value: unknown): string {
|
||||||
|
try {
|
||||||
|
return String(value)
|
||||||
|
} catch {
|
||||||
|
return '<unrenderable thrown value>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Factory-level ownership of every preparing or live transaction. */
|
/** Factory-level ownership of every preparing or live transaction. */
|
||||||
class FactoryOwnership {
|
class FactoryOwnership {
|
||||||
private accepting = true
|
private accepting = true
|
||||||
@@ -422,16 +431,16 @@ export class AgentLoop extends Service implements AgentFactory {
|
|||||||
sessionId: SessionId,
|
sessionId: SessionId,
|
||||||
error: unknown,
|
error: unknown,
|
||||||
): void {
|
): void {
|
||||||
this.ctx.logger.warn(`agent "${configId}": config-driven ${action} of "${sessionId}" failed: ${String(error)}`)
|
this.ctx.logger.warn(`agent "${configId}": config-driven ${action} of "${sessionId}" failed: ${renderThrown(error)}`)
|
||||||
const args: unknown[] = ['agent-loop/config-start-failed', sessionId, error]
|
const args: unknown[] = ['agent-loop/config-start-failed', sessionId, error]
|
||||||
for (const callback of this.ctx.events.dispatch('emit', args)) {
|
for (const callback of this.ctx.events.dispatch('emit', args)) {
|
||||||
try {
|
try {
|
||||||
const returned: unknown = callback(...args)
|
const returned: unknown = callback(...args)
|
||||||
void Promise.resolve(returned).catch((listenerError: unknown) => {
|
void Promise.resolve(returned).catch((listenerError: unknown) => {
|
||||||
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener rejected: ${String(listenerError)}`)
|
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener rejected: ${renderThrown(listenerError)}`)
|
||||||
})
|
})
|
||||||
} catch (listenerError: unknown) {
|
} catch (listenerError: unknown) {
|
||||||
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener threw: ${String(listenerError)}`)
|
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener threw: ${renderThrown(listenerError)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,42 @@ describe('config-driven session id', () => {
|
|||||||
await ctx.fiber.dispose()
|
await ctx.fiber.dispose()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('contains startup and observer failures whose string coercion throws', async () => {
|
||||||
|
const root = await mkdtemp(join(tmpdir(), 'dsh-cfg-exact-unrenderable-'))
|
||||||
|
dirs.push(root)
|
||||||
|
const ctx = await makeCoreContext()
|
||||||
|
await ctx.plugin(SessionPersistenceJsonl, { root })
|
||||||
|
const unrenderable = {
|
||||||
|
[Symbol.toPrimitive](): never {
|
||||||
|
throw new Error('coercion escaped')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const failures: unknown[] = []
|
||||||
|
ctx.on('agent-loop/config-start-failed', () => { throw unrenderable })
|
||||||
|
// Deliberately violate the normal Error-only rejection rule to exercise the unknown boundary.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||||
|
ctx.on('agent-loop/config-start-failed', () => Promise.reject(unrenderable) as never)
|
||||||
|
ctx.on('agent-loop/config-start-failed', (_sessionId, error) => { failures.push(error) })
|
||||||
|
vi.spyOn(ctx.sessionPersistence, 'list').mockRejectedValue(unrenderable)
|
||||||
|
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
|
||||||
|
|
||||||
|
await ctx.plugin(AgentLoop, {
|
||||||
|
agents: [{ id: 'main', sessionId: SessionId('stdio-exact-unrenderable'), model: 'mock' }],
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect.poll(() => failures).toEqual([unrenderable])
|
||||||
|
expect(warn).toHaveBeenCalledWith(
|
||||||
|
'agent "main": config-driven restore of "stdio-exact-unrenderable" failed: <unrenderable thrown value>',
|
||||||
|
)
|
||||||
|
expect(warn).toHaveBeenCalledWith(
|
||||||
|
'agent "main": config-start-failed listener threw: <unrenderable thrown value>',
|
||||||
|
)
|
||||||
|
await expect.poll(() => warn).toHaveBeenCalledWith(
|
||||||
|
'agent "main": config-start-failed listener rejected: <unrenderable thrown value>',
|
||||||
|
)
|
||||||
|
await ctx.fiber.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
it('joins an exact-id persistence lookup before AgentLoop disposal completes', async () => {
|
it('joins an exact-id persistence lookup before AgentLoop disposal completes', async () => {
|
||||||
const root = await mkdtemp(join(tmpdir(), 'dsh-cfg-exact-dispose-'))
|
const root = await mkdtemp(join(tmpdir(), 'dsh-cfg-exact-dispose-'))
|
||||||
dirs.push(root)
|
dirs.push(root)
|
||||||
|
|||||||
Reference in New Issue
Block a user