Merge branch 'codex/simp-hide-concrete-agent-loop' into codex/simp-hide-subagent-internals
This commit is contained in:
@@ -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:338`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`stdio-agent`](../packages/ui/stdio-agent) |
|
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:338`](../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:303`](../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:303`](../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:318`](../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:318`](../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:592`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:592`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
||||||
|
|||||||
@@ -422,10 +422,16 @@ export class AgentLoop extends Service implements AgentFactory {
|
|||||||
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: ${String(error)}`)
|
||||||
try {
|
const args: unknown[] = ['agent-loop/config-start-failed', sessionId, error]
|
||||||
this.ctx.emit('agent-loop/config-start-failed', sessionId, error)
|
for (const callback of this.ctx.events.dispatch('emit', args)) {
|
||||||
} catch (listenerError) {
|
try {
|
||||||
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener threw: ${String(listenerError)}`)
|
const returned: unknown = callback(...args)
|
||||||
|
void Promise.resolve(returned).catch((listenerError: unknown) => {
|
||||||
|
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener rejected: ${String(listenerError)}`)
|
||||||
|
})
|
||||||
|
} catch (listenerError: unknown) {
|
||||||
|
this.ctx.logger.warn(`agent "${configId}": config-start-failed listener threw: ${String(listenerError)}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,11 +99,13 @@ describe('config-driven session id', () => {
|
|||||||
await ctx.plugin(SessionPersistenceJsonl, { root })
|
await ctx.plugin(SessionPersistenceJsonl, { root })
|
||||||
const failure = new Error('persistence index failed')
|
const failure = new Error('persistence index failed')
|
||||||
const listenerFailure = new Error('failure observer failed')
|
const listenerFailure = new Error('failure observer failed')
|
||||||
|
const asyncListenerFailure = new Error('async failure observer failed')
|
||||||
const failures: { sessionId: SessionId; error: unknown }[] = []
|
const failures: { sessionId: SessionId; error: unknown }[] = []
|
||||||
|
ctx.on('agent-loop/config-start-failed', () => { throw listenerFailure })
|
||||||
|
ctx.on('agent-loop/config-start-failed', () => Promise.reject(asyncListenerFailure) as never)
|
||||||
ctx.on('agent-loop/config-start-failed', (sessionId, error) => {
|
ctx.on('agent-loop/config-start-failed', (sessionId, error) => {
|
||||||
failures.push({ sessionId, error })
|
failures.push({ sessionId, error })
|
||||||
})
|
})
|
||||||
ctx.on('agent-loop/config-start-failed', () => { throw listenerFailure })
|
|
||||||
vi.spyOn(ctx.sessionPersistence, 'list').mockRejectedValue(failure)
|
vi.spyOn(ctx.sessionPersistence, 'list').mockRejectedValue(failure)
|
||||||
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
|
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
|
||||||
|
|
||||||
@@ -118,6 +120,9 @@ describe('config-driven session id', () => {
|
|||||||
expect(warn).toHaveBeenCalledWith(
|
expect(warn).toHaveBeenCalledWith(
|
||||||
'agent "main": config-start-failed listener threw: Error: failure observer failed',
|
'agent "main": config-start-failed listener threw: Error: failure observer failed',
|
||||||
)
|
)
|
||||||
|
await expect.poll(() => warn).toHaveBeenCalledWith(
|
||||||
|
'agent "main": config-start-failed listener rejected: Error: async failure observer failed',
|
||||||
|
)
|
||||||
expect(ctx.agents.get(SessionId('stdio-exact-failure'))).toBeUndefined()
|
expect(ctx.agents.get(SessionId('stdio-exact-failure'))).toBeUndefined()
|
||||||
warn.mockRestore()
|
warn.mockRestore()
|
||||||
await ctx.fiber.dispose()
|
await ctx.fiber.dispose()
|
||||||
|
|||||||
@@ -245,6 +245,9 @@ const DYNAMIC_EVENT_DISPATCHERS: Array<{ event: string; pkg: string; method: str
|
|||||||
// Registry disposal reuses the stable carrier captured before entry commit
|
// Registry disposal reuses the stable carrier captured before entry commit
|
||||||
// and contains each listener directly rather than rebuilding via agentEvents.
|
// and contains each listener directly rather than rebuilding via agentEvents.
|
||||||
{ event: 'agent/disposed', pkg: 'agent', method: 'events.dispatch' },
|
{ event: 'agent/disposed', pkg: 'agent', method: 'events.dispatch' },
|
||||||
|
// Config startup failures have no live Agent carrier; AgentLoop resolves the
|
||||||
|
// callbacks directly to contain each synchronous throw and async rejection.
|
||||||
|
{ event: 'agent-loop/config-start-failed', pkg: 'agent-loop', method: 'events.dispatch' },
|
||||||
{ event: 'session/created', pkg: 'session', method: 'events.dispatch' },
|
{ event: 'session/created', pkg: 'session', method: 'events.dispatch' },
|
||||||
// Session event callbacks are likewise resolved before the log push, then
|
// Session event callbacks are likewise resolved before the log push, then
|
||||||
// invoked individually after commit so observer failures are contained.
|
// invoked individually after commit so observer failures are contained.
|
||||||
|
|||||||
Reference in New Issue
Block a user