@meistrari/agent-core 0.2.0-experimental.2 → 0.2.0-experimental.4

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/README.md CHANGED
@@ -139,5 +139,5 @@ process lifecycle spec reads `/proc`, so it fails on macOS. Claude Code rejects
139
139
  bypass-permissions mode as root, so run them as a regular user.
140
140
 
141
141
  Regenerate the experimental Codex wire types with `bun run generate:codex-protocol`.
142
- The generator pins Codex 0.153.4 and verifies any `CODEX_EXECUTABLE` override before
142
+ The generator pins Codex 0.156.1 and verifies any `CODEX_EXECUTABLE` override before
143
143
  using it. Commit generated files with generator changes; CI checks reproducibility.
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.2",
4
+ "version": "0.2.0-experimental.4",
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",
@@ -400,6 +400,10 @@ function correlateConsumedPrompts({ message, turnId, drafts, state }: {
400
400
  ? message.user_message_uuids.filter((id): id is string => typeof id === 'string')
401
401
  : message.user_message_uuid ? [message.user_message_uuid] : []
402
402
  for (const inputId of inputIds) {
403
+ // Main-response UUIDs prove consumption even if the started receipt was omitted. This
404
+ // also applies to supervisor inputs, which intentionally have no human-prompt correlation.
405
+ if (state.queuedSteerIds.delete(inputId))
406
+ state.heldTurnEnd = undefined
403
407
  const commandId = state.promptCommandByInputId.get(inputId)
404
408
  if (!commandId)
405
409
  continue
@@ -512,6 +512,17 @@ function createInputQueue(): AsyncIterable<SDKUserMessage> & { push: (message: S
512
512
  }
513
513
  }
514
514
 
515
+ // Claude Code 2.1.280 sanitizePath: replace each non-ASCII-alphanumeric UTF-16 unit,
516
+ // then cap the slug at 200 units and suffix a base-36 signed-32-bit hash of the ORIGINAL
517
+ // cwd. The non-Unicode regex is deliberate: an astral character becomes two hyphens.
518
+ // This is needed before input; SessionStart's transcript_path arrives too late for the
519
+ // session-local stores and resume-context lookup. Real-CLI tests pin open and resume.
515
520
  function encodeClaudeProjectPath(cwd: string): string {
516
- return cwd.replaceAll('/', '-')
521
+ const encoded = cwd.replace(/[^a-z0-9]/gi, '-')
522
+ if (encoded.length <= 200)
523
+ return encoded
524
+ let hash = 0
525
+ for (let index = 0; index < cwd.length; index++)
526
+ hash = ((hash << 5) - hash + cwd.charCodeAt(index)) | 0
527
+ return `${encoded.slice(0, 200)}-${Math.abs(hash).toString(36)}`
517
528
  }
@@ -79,7 +79,7 @@ function isClaudePreInitHookMessage(message: SDKMessage | ClaudeCommandLifecycle
79
79
  // run in this session". Any other shape or state deviation is provider protocol drift and fails
80
80
  // the run closed. Only `queued`/`started`/`completed` are positive. `cancelled` (steered-over,
81
81
  // aborted, or user-cancelled) and `discarded` (session ended with the command still queued) are
82
- // observed non-positive states whose real outcome the run's close settles; `refused` is a definite
82
+ // non-positive states that reject an unsettled waiter as delivery-unknown; `refused` is a definite
83
83
  // terminal non-acceptance while the session keeps running, so it settles the waiter itself.
84
84
  const commandLifecycleFrameSchema = z.strictObject({
85
85
  type: z.literal('command_lifecycle'),
@@ -393,10 +393,11 @@ export class ClaudeRun implements AgentRun {
393
393
  // Register before the push: the CLI's queued receipt can outrun the local push resolution,
394
394
  // and a receipt with no registered waiter would be lost.
395
395
  const receipt = this.registerAcceptanceWaiter(clientMessageId)
396
- if (command.origin === 'user')
396
+ if (command.origin === 'user') {
397
397
  this.eventMapperState.promptCommandByInputId.set(clientMessageId, command.commandId)
398
+ this.pendingMessages.set(command.commandId, { messageId: clientMessageId, mode: command.mode, turnId: command.turnId, started: false })
399
+ }
398
400
  this.commandByClientMessageId.set(clientMessageId, command.commandId)
399
- this.pendingMessages.set(command.commandId, { messageId: clientMessageId, mode: command.mode, turnId: command.turnId, started: false })
400
401
  if (command.mode === 'steer')
401
402
  this.steerInputIds.add(clientMessageId)
402
403
  try {
@@ -740,7 +741,25 @@ export class ClaudeRun implements AgentRun {
740
741
  // Deliver first: on a prebound resume this is the run's first input, and the resolved
741
742
  // event must not precede the lifecycle events the init barrier emits.
742
743
  await this.applyProviderSettings(this.current)
743
- await this.deliverInput(toClaudeUserMessage({ prompt: resolved.prompt, mode: 'next', commandId: command.commandId }))
744
+ // The answer opens its turn as the user's input, correlated like a prompt: the turn it
745
+ // starts carries this command, and the answer is accepted as that turn's user message.
746
+ const clientMessageId = deriveMessageId(command.commandId)
747
+ const receipt = this.registerAcceptanceWaiter(clientMessageId)
748
+ this.eventMapperState.promptCommandByInputId.set(clientMessageId, command.commandId)
749
+ this.pendingMessages.set(command.commandId, { messageId: clientMessageId, mode: 'next', turnId: undefined, started: false })
750
+ this.commandByClientMessageId.set(clientMessageId, command.commandId)
751
+ try {
752
+ await this.deliverInput(toClaudeUserMessage({ prompt: resolved.prompt, mode: 'next', commandId: command.commandId }))
753
+ await receipt.promise
754
+ }
755
+ catch (error) {
756
+ if (error instanceof AgentNotAcceptedError)
757
+ this.eventMapperState.promptCommandByInputId.delete(clientMessageId)
758
+ throw error
759
+ }
760
+ finally {
761
+ this.acceptanceWaiters.delete(clientMessageId)
762
+ }
744
763
  if (resolved.event)
745
764
  this.pushEvent(resolved.event)
746
765
  this.userInputRequests.markDelivered(command.requestId)
@@ -945,9 +964,9 @@ export class ClaudeRun implements AgentRun {
945
964
 
946
965
  // Strict pinned-runtime boundary for the undocumented receipt frame: shape/state deviation and
947
966
  // session identity mismatch are provider protocol drift and fail the run closed. `cancelled`
948
- // and `discarded` are valid non-positive states that are validated and no-op even when a
949
- // waiter is pending: the pending prompt's real outcome is settled by run close/error as
950
- // delivery-unknown. `refused` is the pinned runtime's definite pre-queue rejection: the input
967
+ // and `discarded` reject an unsettled waiter as delivery-unknown; a prior positive acceptance
968
+ // cannot be revoked, and later consumed UUIDs remain valid evidence. `refused` is the pinned
969
+ // runtime's definite pre-queue rejection: the input
951
970
  // will never run in this session, and the session stays open, so the pending waiter is
952
971
  // rejected as a terminal not-accepted outcome instead of waiting for a close that may not
953
972
  // come. Valid positive frames without a pending waiter are internal lifecycle for
@@ -1198,6 +1217,10 @@ export class ClaudeRun implements AgentRun {
1198
1217
  if (!this.initialized) {
1199
1218
  throw new AgentProviderProtocolError({ message: 'Claude SDK ended before emitting system init.' })
1200
1219
  }
1220
+ // A queued steer was accepted but never consumed or explicitly dropped. Its preceding
1221
+ // ask's held result cannot stand in for that owed input's completion.
1222
+ if (this.eventMapperState.queuedSteerIds.size > 0)
1223
+ throw new AgentProviderProtocolError({ message: 'Claude SDK ended before queued steering input started.' })
1201
1224
  // A superseded ask's held end is final once the stream can deliver no steer to continue it.
1202
1225
  await this.emitDrafts(flushHeldTurnEnd(this.eventMapperState), this.eventMapperState.interrupt)
1203
1226
  // The SDK reports non-zero exits and signals as errors, but a clean exit mid-turn ends the stream
@@ -397,10 +397,16 @@ function toolCallCompletion(item: CodexCompletedItem): { toolName: string, statu
397
397
  if (item.type === 'imageGeneration')
398
398
  return { toolName, status: item.status === 'failed' ? 'failed' : 'completed', output: imageGenerationResult(item) }
399
399
  if (item.type === 'commandExecution') {
400
- const status = item.exitCode === undefined || item.exitCode === null || item.exitCode === 0 ? 'completed' : 'failed'
400
+ const status = item.status === 'declined'
401
+ ? 'cancelled'
402
+ : item.status !== 'completed' || (item.exitCode != null && item.exitCode !== 0) ? 'failed' : 'completed'
401
403
  const error = 'error' in item && typeof item.error === 'string'
402
404
  ? { message: item.error }
403
- : status === 'failed' ? { message: `Command exited with code ${item.exitCode}.` } : undefined
405
+ : status === 'failed'
406
+ ? { message: item.exitCode != null && item.exitCode !== 0
407
+ ? `Command exited with code ${item.exitCode}.`
408
+ : item.status === 'inProgress' ? 'Command completion status was inconsistent.' : 'Command execution failed.' }
409
+ : undefined
404
410
  return { toolName, status, output: commandExecutionResult(item, status), error }
405
411
  }
406
412
  if (item.type === 'dynamicToolCall')
@@ -503,7 +509,7 @@ function hookPromptText(item: CodexHookPromptItem): string | undefined {
503
509
  return optionalNonEmpty(item.fragments.map(fragment => fragment.text).join('\n'))
504
510
  }
505
511
 
506
- function commandExecutionResult(item: Extract<CodexCompletedItem, { type: 'commandExecution' }>, status: 'completed' | 'failed'): AgentToolResult | undefined {
512
+ function commandExecutionResult(item: Extract<CodexCompletedItem, { type: 'commandExecution' }>, status: AgentToolCallStatus): AgentToolResult | undefined {
507
513
  // Stdout is content, not a label: whitespace and an explicitly empty
508
514
  // successful output must survive normalization into the complete step.
509
515
  // Native build_command_execution_end_item encodes empty captured output as
@@ -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.2",
4
- sha: "b808a0e0d307bcaa2cd0d47d2fd1e4112f70290c",
5
- buildTime: "2026-09-25T17:58:45.916Z",
3
+ version: "0.2.0-experimental.4",
4
+ sha: "432a69b2d7243b7d5c6651229bf2f005e94afad7",
5
+ buildTime: "2026-09-29T14:26:10.056Z",
6
6
  } as const
@@ -1,6 +1,6 @@
1
1
  import type { AgentProvider, AgentRun } from '../agents'
2
2
  import type { AgentInputAttachmentPreparation } from '../agents/materialized-input-attachment'
3
- import type { AgentEvent, AgentProviderId } from '../protocol'
3
+ import type { AgentEvent, AgentPrompt, AgentProviderId } from '../protocol'
4
4
  import type { EphemeralCredentials, SessionBootstrapBody, SessionBootstrapGitToken } from '../supervisor-protocol/bootstrap'
5
5
  import type { WireSendPromptCommand } from '../supervisor-protocol/command-body'
6
6
  import type { WireEventBody } from '../supervisor-protocol/event-body'
@@ -62,11 +62,16 @@ export interface AgentSupervisorProviderFactoryOptions {
62
62
  rpc: SupervisorRpcClient
63
63
  }) => AgentProvider<'claude'> | AgentProvider<'codex'>
64
64
  bindingStore: AgentProviderBindingStore
65
+ /**
66
+ * Prepares a send-prompt's attachments before the provider sees it. It may also return the
67
+ * prompt the provider receives instead of the command's, for text the command could only
68
+ * reference (a message too long for a wire frame, fetched by the supervisor).
69
+ */
65
70
  prepareInputAttachments?: (input: {
66
71
  commandId: string
67
72
  command: WireSendPromptCommand
68
73
  signal: AbortSignal
69
- }) => Promise<AgentInputAttachmentPreparation | undefined>
74
+ }) => Promise<(AgentInputAttachmentPreparation & { prompt?: AgentPrompt }) | undefined>
70
75
  onSecretsRefreshed?: (input: {
71
76
  credentials?: EphemeralCredentials
72
77
  gitToken?: SessionBootstrapGitToken
@@ -194,7 +199,7 @@ class AgentSupervisorRuntime implements SupervisorAgentRuntime {
194
199
  model: input.body.model,
195
200
  reasoningEffort: input.body.reasoningEffort,
196
201
  origin: 'user',
197
- prompt: input.body.prompt,
202
+ prompt: inputAttachmentPreparation?.prompt ?? input.body.prompt,
198
203
  author: input.body.user
199
204
  ? {
200
205
  name: input.body.user.name,