@meistrari/agent-core 0.2.0-experimental.9 → 0.3.1-experimental.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/agent-core",
3
3
  "type": "module",
4
- "version": "0.2.0-experimental.9",
4
+ "version": "0.3.1-experimental.0",
5
5
  "packageManager": "bun@1.3.12",
6
6
  "description": "Shared contracts and runtime modules for Tela coding-agent sandboxes: agent protocol, supervisor wire protocol, resident supervisor, worker runtime client, and Claude/Codex harness adapters.",
7
7
  "license": "UNLICENSED",
@@ -57,6 +57,9 @@ export interface CodexEventMapperState {
57
57
  readonly endedSubagentIds: Set<string>
58
58
  readonly subagentByThreadId: Map<string, CodexSubagentRef>
59
59
  readonly subagentById: Map<string, CodexSubagentRef>
60
+ readonly ownerTurnIdBySubagentId: Map<string, string>
61
+ readonly draftsAwaitingOwnerByActorId: Map<string, AgentEventDraft[]>
62
+ readonly unreportedEndedSubagentsByParent: Map<string, Map<string, string>>
60
63
  readonly subagentNameByThreadId: Map<string, string>
61
64
  readonly pendingDraftsByThreadId: Map<string, CodexUnownedTurnEventDraft[]>
62
65
  readonly turnErrorsById: Map<string, AgentError>
@@ -80,6 +83,9 @@ export function createCodexEventMapperState(): CodexEventMapperState {
80
83
  endedSubagentIds: new Set(),
81
84
  subagentByThreadId: new Map(),
82
85
  subagentById: new Map(),
86
+ ownerTurnIdBySubagentId: new Map(),
87
+ draftsAwaitingOwnerByActorId: new Map(),
88
+ unreportedEndedSubagentsByParent: new Map(),
83
89
  subagentNameByThreadId: new Map(),
84
90
  pendingDraftsByThreadId: new Map(),
85
91
  turnErrorsById: new Map(),
@@ -106,6 +112,52 @@ export function cacheCodexThreadMetadata(thread: CodexThreadMetadata, state: Cod
106
112
  }
107
113
 
108
114
  export function mapCodexNotification(notification: CodexNotification, state: CodexEventMapperState): AgentEventDraft[] {
115
+ return attributeSubagentDraftsToOwnerTurn(mapNotificationDrafts(notification, state), state)
116
+ }
117
+
118
+ function attributeSubagentDraftsToOwnerTurn(drafts: AgentEventDraft[], state: CodexEventMapperState): AgentEventDraft[] {
119
+ return drafts.flatMap(draft => attributeSubagentDraft(draft, state))
120
+ }
121
+
122
+ function attributeSubagentDraft(draft: AgentEventDraft, state: CodexEventMapperState): AgentEventDraft[] {
123
+ if (!('actor' in draft) || draft.actor.type !== 'subagent')
124
+ return [draft]
125
+ const { subagentId, parentActorId } = draft.actor
126
+ if (draft.type === 'turn.started' || draft.type === 'turn.ended')
127
+ return []
128
+ let ownerTurnId = state.ownerTurnIdBySubagentId.get(subagentId)
129
+ if (!ownerTurnId && draft.type === 'subagent.started') {
130
+ ownerTurnId = parentActorId === mainActor.actorId ? draft.turnId : state.ownerTurnIdBySubagentId.get(parentActorId)
131
+ if (ownerTurnId)
132
+ state.ownerTurnIdBySubagentId.set(subagentId, ownerTurnId)
133
+ else
134
+ return awaitOwner(parentActorId, draft, state)
135
+ }
136
+ if (!ownerTurnId && parentActorId === mainActor.actorId)
137
+ ownerTurnId = state.currentMainTurn.status === 'open' ? state.currentMainTurn.turnId : draft.turnId
138
+ if (!ownerTurnId)
139
+ return awaitOwner(subagentId, draft, state)
140
+ if (draft.type === 'subagent.ended') {
141
+ const pending = state.unreportedEndedSubagentsByParent.get(parentActorId) ?? new Map<string, string>()
142
+ pending.set(subagentId, draft.payload.status)
143
+ state.unreportedEndedSubagentsByParent.set(parentActorId, pending)
144
+ }
145
+ const attributed: AgentEventDraft = { ...draft, turnId: ownerTurnId }
146
+ if (draft.type !== 'subagent.started')
147
+ return [attributed]
148
+ const awaiting = state.draftsAwaitingOwnerByActorId.get(subagentId) ?? []
149
+ state.draftsAwaitingOwnerByActorId.delete(subagentId)
150
+ return [attributed, ...attributeSubagentDraftsToOwnerTurn(awaiting, state)]
151
+ }
152
+
153
+ function awaitOwner(actorId: string, draft: AgentEventDraft, state: CodexEventMapperState): AgentEventDraft[] {
154
+ const awaiting = state.draftsAwaitingOwnerByActorId.get(actorId) ?? []
155
+ awaiting.push(draft)
156
+ state.draftsAwaitingOwnerByActorId.set(actorId, awaiting)
157
+ return []
158
+ }
159
+
160
+ function mapNotificationDrafts(notification: CodexNotification, state: CodexEventMapperState): AgentEventDraft[] {
109
161
  // Codex has many app-server notifications; the runtime maps the subset that has a provider-neutral AgentEvent.
110
162
 
111
163
  switch (notification.method) {
@@ -265,11 +317,11 @@ function toolCallStart(item: CodexStartedItem): { toolName: string, input: JsonV
265
317
  return undefined
266
318
 
267
319
  if (item.type === 'commandExecution')
268
- return { toolName, input: toJsonObject({ command: Array.isArray(item.command) ? item.command.join(' ') : String(item.command ?? ''), cwd: item.cwd }) }
320
+ return { toolName, input: toJsonObject({ command: unwrapShellCommand(Array.isArray(item.command) ? item.command.join(' ') : String(item.command ?? '')), cwd: item.cwd }) }
269
321
  if (item.type === 'webSearch')
270
322
  return { toolName, input: toJsonObject({ query: optionalNonEmpty(item.query) }) }
271
323
  if (item.type === 'fileChange')
272
- return { toolName, input: jsonInput({ files: item.changes.map(change => removeUndefined({ path: change.path, kind: change.kind?.type })) }) }
324
+ return { toolName, input: jsonInput({ file_path: item.changes.map(change => change.path).join(', '), files: item.changes.map(change => removeUndefined({ path: change.path, kind: change.kind?.type })) }) }
273
325
  if (item.type === 'imageView')
274
326
  return { toolName, input: toJsonObject({ path: item.path }) }
275
327
  if (item.type === 'imageGeneration')
@@ -283,6 +335,17 @@ function toolCallStart(item: CodexStartedItem): { toolName: string, input: JsonV
283
335
  return undefined
284
336
  }
285
337
 
338
+ const SHELL_WRAPPER = /^(?:\/(?:usr\/)?bin\/)?(?:ba|z)?sh -l?c (?:'((?:[^']|'\\'')*)'|"((?:[^"\\]|\\.)*)")$/s
339
+
340
+ export function unwrapShellCommand(command: string): string {
341
+ const match = SHELL_WRAPPER.exec(command.trim())
342
+ if (!match)
343
+ return command
344
+ if (match[1] !== undefined)
345
+ return match[1].replaceAll(`'\\''`, `'`)
346
+ return match[2]!.replace(/\\([\\"$`])/g, '$1')
347
+ }
348
+
286
349
  function toolNameForItem(item: CodexStartedItem | CodexCompletedItem): string | undefined {
287
350
  if (item.type === 'commandExecution')
288
351
  return CANONICAL_TOOL.bash
@@ -586,6 +649,7 @@ function mapCollabAgentToolCompleted({ turnId, threadId, item, mapperState }: {
586
649
  toolCallId: item.id,
587
650
  toolName: item.tool,
588
651
  status: item.status === 'failed' ? 'failed' as const : 'completed' as const,
652
+ output: item.status === 'failed' ? undefined : collabToolOutput(collabToolSummary(item, mapperState)),
589
653
  error: item.status === 'failed' ? { message: `Codex ${item.tool} failed.` } : undefined,
590
654
  }),
591
655
  },
@@ -666,7 +730,7 @@ function mapSubAgentActivityCompleted(input: { turnId: string, threadId: string,
666
730
  state.subagentById.set(item.agentThreadId, actor)
667
731
  return [
668
732
  ...emitOrBufferByThread({ draft: { type: 'tool.call.started', turnId, payload: { toolCallId: item.id, toolName: 'spawnAgent' } }, threadId, state }),
669
- ...emitOrBufferByThread({ draft: { type: 'tool.call.completed', turnId, payload: { toolCallId: item.id, toolName: 'spawnAgent', status: 'completed' } }, threadId, state }),
733
+ ...emitOrBufferByThread({ draft: { type: 'tool.call.completed', turnId, payload: { toolCallId: item.id, toolName: 'spawnAgent', status: 'completed', output: collabToolOutput(item.agentPath) } }, threadId, state }),
670
734
  {
671
735
  type: 'subagent.started',
672
736
  turnId,
@@ -680,7 +744,7 @@ function mapSubAgentActivityCompleted(input: { turnId: string, threadId: string,
680
744
  if (item.kind === 'interacted') {
681
745
  return [
682
746
  ...emitOrBufferByThread({ draft: { type: 'tool.call.started', turnId, payload: { toolCallId: item.id, toolName: 'sendInput' } }, threadId, state }),
683
- ...emitOrBufferByThread({ draft: { type: 'tool.call.completed', turnId, payload: { toolCallId: item.id, toolName: 'sendInput', status: 'completed' } }, threadId, state }),
747
+ ...emitOrBufferByThread({ draft: { type: 'tool.call.completed', turnId, payload: { toolCallId: item.id, toolName: 'sendInput', status: 'completed', output: collabToolOutput(item.agentPath) } }, threadId, state }),
684
748
  ]
685
749
  }
686
750
 
@@ -703,6 +767,26 @@ function mapSubAgentActivityCompleted(input: { turnId: string, threadId: string,
703
767
  }]
704
768
  }
705
769
 
770
+ function collabToolSummary(item: CodexCollabAgentToolCompletedItem, state: CodexEventMapperState): string {
771
+ const name = (threadId: string) => state.subagentByThreadId.get(threadId)?.name ?? state.subagentNameByThreadId.get(threadId) ?? threadId
772
+ const parentActorId = item.senderThreadId === state.mainThreadId ? mainActor.actorId : item.senderThreadId
773
+ const unreported = state.unreportedEndedSubagentsByParent.get(parentActorId)
774
+ const states = Object.entries(item.agentsStates).map(([threadId, agentState]) => {
775
+ unreported?.delete(threadId)
776
+ return `${name(threadId)}: ${agentState.status}`
777
+ })
778
+ if (states.length)
779
+ return states.join('\n')
780
+ if (item.receiverThreadIds.length)
781
+ return item.receiverThreadIds.map(name).join('\n')
782
+ state.unreportedEndedSubagentsByParent.delete(parentActorId)
783
+ return [...unreported ?? []].map(([threadId, status]) => `${name(threadId)}: ${status}`).join('\n') || item.tool
784
+ }
785
+
786
+ function collabToolOutput(text: string): AgentToolResult {
787
+ return { success: true, content: [{ type: 'text', text }] }
788
+ }
789
+
706
790
  function mapChildTurnCompleted(input: {
707
791
  threadId: string
708
792
  turnId: string
@@ -853,10 +937,9 @@ function subagentPayloadRef(actor: CodexSubagentRef): Pick<CodexSubagentRef, 'ac
853
937
 
854
938
  function collabToolInput(item: CodexCollabAgentToolStartedItem): JsonObject {
855
939
  return toJsonObject({
856
- senderThreadId: item.senderThreadId,
857
- receiverThreadIds: item.receiverThreadIds.join(','),
858
- prompt: item.prompt,
859
- model: item.model,
940
+ prompt: optionalNonEmpty(item.prompt),
941
+ receiverThreadIds: optionalNonEmpty(item.receiverThreadIds.join(',')),
942
+ model: optionalNonEmpty(item.model),
860
943
  reasoningEffort: typeof item.reasoningEffort === 'string' ? item.reasoningEffort : undefined,
861
944
  })
862
945
  }
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/write-provenance.ts. Do not edit by hand.
2
2
  export const generatedProvenance = {
3
- version: "0.2.0-experimental.9",
4
- sha: "8cc96eb90e17628946dfd6f7cc0bac298722a4c1",
5
- buildTime: "2026-10-20T14:42:52.340Z",
3
+ version: "0.3.1-experimental.0",
4
+ sha: "0fe0d37e1a4d2965fb8da08b3160ac676f1229e1",
5
+ buildTime: "2026-10-26T12:38:01.769Z",
6
6
  } as const
@@ -152,14 +152,16 @@ export class DurableStepProjector {
152
152
  : event.type.startsWith('reasoning.') ? 'reasoning' : undefined
153
153
  if (!kind || !('turnId' in event)) {
154
154
  const frames: WireEventBody[] = []
155
- if (event.type === 'turn.ended' || event.type === 'session.ended') {
155
+ if (event.type === 'turn.ended' || event.type === 'session.ended' || event.type === 'subagent.ended') {
156
156
  for (const row of this.db.query<{ body: string, last_event_id: string | null }, []>('select body, last_event_id from steps').all()) {
157
157
  const step = JSON.parse(row.body) as StepDetailV1
158
158
  if (step.sessionId !== event.sessionId || step.providerSessionId !== event.providerSessionId)
159
159
  continue
160
160
  if ((step.status !== 'running' && row.last_event_id !== event.eventId) || (event.type === 'turn.ended' && (step.turnId !== event.turnId || step.actor.actorId !== event.actor.actorId)))
161
161
  continue
162
- step.status = event.type === 'turn.ended' && event.payload.status === 'failed' ? 'failed' : 'interrupted'
162
+ if (event.type === 'subagent.ended' && step.actor.actorId !== event.actor.actorId)
163
+ continue
164
+ step.status = event.type !== 'session.ended' && event.payload.status === 'failed' ? 'failed' : 'interrupted'
163
165
  step.incomplete = true
164
166
  step.endedAt = event.timestamp
165
167
  frames.push(...await this.finish(step))
@@ -75,9 +75,10 @@ export async function createResidentSupervisor(input: {
75
75
  credentials: activation.envelope.credentials,
76
76
  gitToken: activation.envelope.gitToken,
77
77
  logger: input.logger,
78
- onFatal: error => requestShutdown(
79
- error instanceof SupervisorConfigurationError ? TERMINAL_ERROR_CODE : RECOVERABLE_ERROR_CODE,
80
- ),
78
+ onFatal: (error) => {
79
+ input.logger.error({ err: error }, 'Supervisor agent runtime failed')
80
+ requestShutdown(error instanceof SupervisorConfigurationError ? TERMINAL_ERROR_CODE : RECOVERABLE_ERROR_CODE)
81
+ },
81
82
  })
82
83
  }
83
84
  else {