@autohq/cli 0.1.282 → 0.1.283

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/dist/index.js CHANGED
@@ -15840,7 +15840,7 @@ var init_chat = __esm({
15840
15840
  });
15841
15841
 
15842
15842
  // ../../packages/schemas/src/conversation.ts
15843
- var CONVERSATION_ROLES, CONVERSATION_ENTRY_KINDS, CONVERSATION_ENTRY_STATUSES, UNKNOWN_MESSAGE_ID, ConversationRoleSchema, ConversationEntryKindSchema, ConversationEntryStatusSchema, ConversationTextContentPartSchema, ConversationReasoningContentPartSchema, ConversationToolCallContentPartSchema, ConversationToolResultContentPartSchema, ConversationQuestionOptionSchema, ConversationQuestionSchema, ConversationQuestionContentPartSchema, ConversationContentPartSchema, ConversationEntryContentSchema, ConversationEntryEventSchema, ConversationTextDeltaSchema, ConversationReasoningDeltaSchema, ConversationDeltaSchema, ConversationDeltaEventSchema, ConversationRealtimeEventSchema;
15843
+ var CONVERSATION_ROLES, CONVERSATION_ENTRY_KINDS, CONVERSATION_ENTRY_STATUSES, UNKNOWN_MESSAGE_ID, ConversationRoleSchema, ConversationEntryKindSchema, ConversationEntryStatusSchema, ConversationTextContentPartSchema, ConversationReasoningContentPartSchema, ConversationToolCallContentPartSchema, ConversationToolResultContentPartSchema, ConversationQuestionOptionSchema, ConversationQuestionSchema, ConversationQuestionContentPartSchema, ConversationUiMessageContentPartSchema, ConversationContentPartSchema, ConversationEntryContentSchema, ConversationEntryEventSchema, ConversationTextDeltaSchema, ConversationReasoningDeltaSchema, ConversationDeltaSchema, ConversationDeltaEventSchema, ConversationUiMessageChunkEventSchema, ConversationRealtimeEventSchema;
15844
15844
  var init_conversation = __esm({
15845
15845
  "../../packages/schemas/src/conversation.ts"() {
15846
15846
  "use strict";
@@ -15906,12 +15906,17 @@ var init_conversation = __esm({
15906
15906
  toolCallId: external_exports.string().min(1).nullable(),
15907
15907
  questions: external_exports.array(ConversationQuestionSchema)
15908
15908
  });
15909
+ ConversationUiMessageContentPartSchema = external_exports.object({
15910
+ type: external_exports.literal("ui_message"),
15911
+ message: JsonValueSchema
15912
+ });
15909
15913
  ConversationContentPartSchema = external_exports.discriminatedUnion("type", [
15910
15914
  ConversationTextContentPartSchema,
15911
15915
  ConversationReasoningContentPartSchema,
15912
15916
  ConversationToolCallContentPartSchema,
15913
15917
  ConversationToolResultContentPartSchema,
15914
- ConversationQuestionContentPartSchema
15918
+ ConversationQuestionContentPartSchema,
15919
+ ConversationUiMessageContentPartSchema
15915
15920
  ]);
15916
15921
  ConversationEntryContentSchema = external_exports.object({
15917
15922
  parts: external_exports.array(ConversationContentPartSchema)
@@ -15953,9 +15958,18 @@ var init_conversation = __esm({
15953
15958
  delta: ConversationDeltaSchema,
15954
15959
  createdAt: external_exports.string().datetime()
15955
15960
  });
15961
+ ConversationUiMessageChunkEventSchema = external_exports.object({
15962
+ type: external_exports.literal("ui.message.chunk"),
15963
+ id: external_exports.string().min(1),
15964
+ sessionId: SessionIdSchema,
15965
+ sequence: external_exports.number().int().nonnegative(),
15966
+ chunk: JsonValueSchema,
15967
+ createdAt: external_exports.string().datetime()
15968
+ });
15956
15969
  ConversationRealtimeEventSchema = external_exports.discriminatedUnion("type", [
15957
15970
  ConversationEntryEventSchema,
15958
- ConversationDeltaEventSchema
15971
+ ConversationDeltaEventSchema,
15972
+ ConversationUiMessageChunkEventSchema
15959
15973
  ]);
15960
15974
  }
15961
15975
  });
@@ -16064,6 +16078,7 @@ function assistantContentProjections(message, messageId) {
16064
16078
  projections.push({
16065
16079
  role: "assistant",
16066
16080
  kind: "tool_call",
16081
+ ...messageId ? { messageId } : {},
16067
16082
  content: {
16068
16083
  parts: [
16069
16084
  {
@@ -16305,87 +16320,6 @@ function parseCodexServerFrame(raw) {
16305
16320
  const notification = parseNotification(frame.method, frame.params);
16306
16321
  return notification ? { kind: "notification", notification } : { kind: "ignored" };
16307
16322
  }
16308
- function projectCodexItem(input) {
16309
- const { item, phase } = input;
16310
- switch (item.type) {
16311
- case "agentMessage":
16312
- return phase === "completed" && item.text.length > 0 ? [
16313
- {
16314
- role: "assistant",
16315
- kind: "message",
16316
- messageId: item.id,
16317
- content: textContent2(item.text)
16318
- }
16319
- ] : [];
16320
- case "reasoning": {
16321
- if (phase !== "completed") {
16322
- return [];
16323
- }
16324
- const text = item.summary.map((line) => line.trim()).filter(Boolean).join("\n\n");
16325
- return text.length > 0 ? [
16326
- {
16327
- role: "assistant",
16328
- kind: "message",
16329
- messageId: item.id,
16330
- content: reasoningContent(text)
16331
- }
16332
- ] : [];
16333
- }
16334
- case "commandExecution":
16335
- return toolProjection({
16336
- phase,
16337
- itemId: item.id,
16338
- name: "shell",
16339
- input: {
16340
- command: item.command,
16341
- ...item.cwd ? { cwd: item.cwd } : {}
16342
- },
16343
- output: item.aggregatedOutput ?? "",
16344
- isError: isFailedStatus(item.status) || (item.exitCode ?? 0) !== 0
16345
- });
16346
- case "fileChange":
16347
- return toolProjection({
16348
- phase,
16349
- itemId: item.id,
16350
- name: "apply_patch",
16351
- input: { changes: toJsonValue(item.changes) },
16352
- output: { status: item.status ?? "unknown" },
16353
- isError: isFailedStatus(item.status)
16354
- });
16355
- case "mcpToolCall":
16356
- return toolProjection({
16357
- phase,
16358
- itemId: item.id,
16359
- name: `${item.server}.${item.tool}`,
16360
- input: toJsonValue(item.arguments),
16361
- output: item.error ? { error: item.error.message } : toJsonValue(item.result),
16362
- isError: isFailedStatus(item.status) || item.error != null
16363
- });
16364
- default:
16365
- return [];
16366
- }
16367
- }
16368
- function projectCodexApproval(request) {
16369
- const subject = request.type === "commandExecution" ? `run the command: ${request.command ?? "(unknown command)"}` : "apply file changes";
16370
- const question = {
16371
- question: request.reason ? `Codex requests approval to ${subject}. ${request.reason}` : `Codex requests approval to ${subject}.`,
16372
- header: "Approval",
16373
- options: [
16374
- { label: APPROVE_OPTION_LABEL, description: "Allow this action." },
16375
- { label: DECLINE_OPTION_LABEL, description: "Reject this action." }
16376
- ],
16377
- multiSelect: false
16378
- };
16379
- return {
16380
- role: "assistant",
16381
- kind: "question",
16382
- content: {
16383
- parts: [
16384
- { type: "question", toolCallId: request.itemId, questions: [question] }
16385
- ]
16386
- }
16387
- };
16388
- }
16389
16323
  function codexApprovalDecision(input) {
16390
16324
  const values = [...Object.values(input.answers), input.response ?? ""];
16391
16325
  const approved = values.some(
@@ -16510,51 +16444,6 @@ function parseApprovalRequest(method, requestId, params) {
16510
16444
  return null;
16511
16445
  }
16512
16446
  }
16513
- function toolProjection(input) {
16514
- if (input.phase === "started") {
16515
- return [
16516
- {
16517
- role: "assistant",
16518
- kind: "tool_call",
16519
- content: {
16520
- parts: [
16521
- {
16522
- type: "tool_call",
16523
- toolCallId: input.itemId,
16524
- name: input.name,
16525
- input: input.input
16526
- }
16527
- ]
16528
- }
16529
- }
16530
- ];
16531
- }
16532
- return [
16533
- {
16534
- role: "tool",
16535
- kind: "tool_result",
16536
- content: {
16537
- parts: [
16538
- {
16539
- type: "tool_result",
16540
- toolUseId: input.itemId,
16541
- output: input.output,
16542
- isError: input.isError
16543
- }
16544
- ]
16545
- }
16546
- }
16547
- ];
16548
- }
16549
- function textContent2(text) {
16550
- return { parts: [{ type: "text", text }] };
16551
- }
16552
- function reasoningContent(text) {
16553
- return { parts: [{ type: "reasoning", text }] };
16554
- }
16555
- function isFailedStatus(status) {
16556
- return status === "failed" || status === "declined";
16557
- }
16558
16447
  var CodexRequestIdSchema, CodexTurnStatusSchema, CodexUserMessageItemSchema, CodexAgentMessageItemSchema, CodexReasoningItemSchema, CodexCommandExecutionItemSchema, CodexFileChangeItemSchema, CodexMcpToolCallItemSchema, CodexItemSchema, OptionalCodexItemSchema, CodexItemEnvelopeSchema, CodexTurnEnvelopeSchema, CodexTokenUsageSchema, CodexTokenUsageEnvelopeSchema, CodexFrameSchema, APPROVE_OPTION_LABEL, DECLINE_OPTION_LABEL;
16559
16448
  var init_codex = __esm({
16560
16449
  "../../packages/schemas/src/codex.ts"() {
@@ -16658,6 +16547,9 @@ var init_codex = __esm({
16658
16547
 
16659
16548
  // ../../packages/schemas/src/conversation-reducer.ts
16660
16549
  function reduceConversationRealtimeEvent(entries, event) {
16550
+ if (event.type === "ui.message.chunk") {
16551
+ return [...entries];
16552
+ }
16661
16553
  if (event.type === "conversation.delta") {
16662
16554
  const existingIndex = entries.findIndex(
16663
16555
  (entry) => entry.messageId === event.messageId
@@ -18988,7 +18880,7 @@ function isSessionTerminalStatus(status) {
18988
18880
  (terminalStatus) => terminalStatus === status
18989
18881
  );
18990
18882
  }
18991
- var SESSION_STATUSES, SESSION_RUNTIME_PHASES, SESSION_TERMINAL_STATUSES, SESSION_DISPLAY_TITLE_MAX_LENGTH, SessionStatusSchema, SessionRuntimePhaseSchema, RunDisplayTitleSchema, AmbientStatusSchema, SESSION_CHECK_STATUSES, SESSION_CHECK_CONCLUSIONS, SESSION_CHECK_TIMEOUT_PHASES, SessionCheckStatusSchema, SessionCheckConclusionSchema, SessionCheckTimeoutPhaseSchema, ManualSessionRequestSchema, SessionArchiveRequestSchema, SessionsArchiveRequestSchema, SessionRecordSchema, SessionListItemAgentSchema, SessionListItemSchema, SessionListResponseSchema, SessionsArchiveResponseSchema;
18883
+ var SESSION_STATUSES, SESSION_RUNTIME_PHASES, SESSION_TERMINAL_STATUSES, SESSION_DISPLAY_TITLE_MAX_LENGTH, SESSION_MESSAGE_ROLES, SESSION_MESSAGE_STATUSES, SessionStatusSchema, SessionRuntimePhaseSchema, SessionMessageRoleSchema, SessionMessageStatusSchema, RunDisplayTitleSchema, AmbientStatusSchema, SESSION_CHECK_STATUSES, SESSION_CHECK_CONCLUSIONS, SESSION_CHECK_TIMEOUT_PHASES, SessionCheckStatusSchema, SessionCheckConclusionSchema, SessionCheckTimeoutPhaseSchema, ManualSessionRequestSchema, SessionArchiveRequestSchema, SessionsArchiveRequestSchema, SessionRecordSchema, SessionUiMessageRecordSchema, SessionListItemAgentSchema, SessionListItemSchema, SessionListResponseSchema, SessionsArchiveResponseSchema;
18992
18884
  var init_sessions = __esm({
18993
18885
  "../../packages/schemas/src/sessions.ts"() {
18994
18886
  "use strict";
@@ -19015,8 +18907,16 @@ var init_sessions = __esm({
19015
18907
  ];
19016
18908
  SESSION_TERMINAL_STATUSES = ["failed", "stopped"];
19017
18909
  SESSION_DISPLAY_TITLE_MAX_LENGTH = 64;
18910
+ SESSION_MESSAGE_ROLES = ["system", "user", "assistant"];
18911
+ SESSION_MESSAGE_STATUSES = [
18912
+ "in_progress",
18913
+ "completed",
18914
+ "failed"
18915
+ ];
19018
18916
  SessionStatusSchema = external_exports.enum(SESSION_STATUSES);
19019
18917
  SessionRuntimePhaseSchema = external_exports.enum(SESSION_RUNTIME_PHASES);
18918
+ SessionMessageRoleSchema = external_exports.enum(SESSION_MESSAGE_ROLES);
18919
+ SessionMessageStatusSchema = external_exports.enum(SESSION_MESSAGE_STATUSES);
19020
18920
  RunDisplayTitleSchema = external_exports.string().trim().max(SESSION_DISPLAY_TITLE_MAX_LENGTH);
19021
18921
  AmbientStatusSchema = external_exports.string().trim().min(1);
19022
18922
  SESSION_CHECK_STATUSES = [
@@ -19054,6 +18954,12 @@ var init_sessions = __esm({
19054
18954
  correlationKey: external_exports.string().nullable(),
19055
18955
  workflowId: external_exports.string().min(1),
19056
18956
  displayTitle: RunDisplayTitleSchema,
18957
+ // Default to null so session.upsert entity records emitted before/without the
18958
+ // active-stream metadata validate (undefined -> null) instead of throwing;
18959
+ // the output type stays `string | null`, so consumers are unaffected.
18960
+ activeUiMessageStreamId: external_exports.string().trim().min(1).nullable().default(null),
18961
+ activeUiMessageStreamCommandId: external_exports.string().trim().min(1).nullable().default(null),
18962
+ activeUiMessageStreamStartedAt: external_exports.string().datetime().nullable().default(null),
19057
18963
  ambientStatus: AmbientStatusSchema.nullable(),
19058
18964
  ambientStatusUpdatedAt: external_exports.string().datetime().nullable(),
19059
18965
  starterActor: AuthActorSchema.nullable(),
@@ -19075,6 +18981,20 @@ var init_sessions = __esm({
19075
18981
  archivedAt: external_exports.string().datetime().nullable(),
19076
18982
  error: JsonValueSchema.nullable()
19077
18983
  });
18984
+ SessionUiMessageRecordSchema = external_exports.object({
18985
+ id: external_exports.string().trim().min(1),
18986
+ sessionId: SessionIdSchema,
18987
+ sequence: external_exports.number().int().nonnegative(),
18988
+ messageId: external_exports.string().trim().min(1),
18989
+ commandId: external_exports.string().trim().min(1).nullable(),
18990
+ turnId: external_exports.string().trim().min(1).nullable(),
18991
+ role: SessionMessageRoleSchema,
18992
+ status: SessionMessageStatusSchema,
18993
+ message: JsonValueSchema,
18994
+ createdAt: external_exports.string().datetime(),
18995
+ updatedAt: external_exports.string().datetime(),
18996
+ completedAt: external_exports.string().datetime().nullable()
18997
+ });
19078
18998
  SessionListItemAgentSchema = external_exports.object({
19079
18999
  name: external_exports.string(),
19080
19000
  displayName: external_exports.string().nullable(),
@@ -23220,7 +23140,7 @@ var init_package = __esm({
23220
23140
  "package.json"() {
23221
23141
  package_default = {
23222
23142
  name: "@autohq/cli",
23223
- version: "0.1.282",
23143
+ version: "0.1.283",
23224
23144
  license: "SEE LICENSE IN README.md",
23225
23145
  publishConfig: {
23226
23146
  access: "public"
@@ -23252,6 +23172,7 @@ var init_package = __esm({
23252
23172
  "@anthropic-ai/claude-agent-sdk": "^0.3.153",
23253
23173
  "@inkjs/ui": "^2.0.0",
23254
23174
  "@tanstack/react-query": "^5.100.14",
23175
+ ai: "^6.0.217",
23255
23176
  chalk: "^5.3.0",
23256
23177
  commander: "^14.0.2",
23257
23178
  ink: "^7",
@@ -23860,6 +23781,9 @@ function createConversationStreamWriter(input) {
23860
23781
  input.writeLine(formatConversationRealtimeEvent(event));
23861
23782
  return;
23862
23783
  }
23784
+ if (event.type === "ui.message.chunk") {
23785
+ return;
23786
+ }
23863
23787
  if (event.messageId && streamedMessageIds.has(event.messageId)) {
23864
23788
  streamedMessageIds.delete(event.messageId);
23865
23789
  input.writeChunk?.("\n");
@@ -23872,6 +23796,10 @@ function createConversationStreamWriter(input) {
23872
23796
  };
23873
23797
  }
23874
23798
  function formatConversationRealtimeEvent(event) {
23799
+ if (event.type === "ui.message.chunk") {
23800
+ const chunkType = typeof event.chunk === "object" && event.chunk !== null && !Array.isArray(event.chunk) && typeof event.chunk.type === "string" ? event.chunk.type : "unknown";
23801
+ return `[${event.sequence}] ui message chunk: ${chunkType}`;
23802
+ }
23875
23803
  if (event.type === "conversation.delta") {
23876
23804
  return `[${event.sequence}] assistant/message ${event.delta.type} delta: ${event.delta.text}`;
23877
23805
  }
@@ -23893,6 +23821,9 @@ function formatConversationEntry(entry) {
23893
23821
  (question) => `${question.question} [${question.options.map((option) => option.label).join(" | ")}]`
23894
23822
  ).join("; ");
23895
23823
  }
23824
+ if (part.type === "ui_message") {
23825
+ return JSON.stringify(part.message);
23826
+ }
23896
23827
  return `${part.isError ? "tool error" : "tool result"} ${part.toolUseId ?? ""}: ${JSON.stringify(part.output)}`;
23897
23828
  }).join("");
23898
23829
  return `[${entry.sequence}] ${entry.role}/${entry.kind}: ${content}`;
@@ -27643,6 +27574,10 @@ function ConversationView({
27643
27574
  sawLiveTurnActivity.current = true;
27644
27575
  return;
27645
27576
  }
27577
+ if (event.type === "ui.message.chunk") {
27578
+ sawLiveTurnActivity.current = true;
27579
+ return;
27580
+ }
27646
27581
  if (event.kind === "question") {
27647
27582
  pendingLocalCommand.current = null;
27648
27583
  updateAwaitingReply(false);
@@ -28159,6 +28094,9 @@ function streamedCharCount(event) {
28159
28094
  if (event.type === "conversation.delta") {
28160
28095
  return event.delta.text.length;
28161
28096
  }
28097
+ if (event.type !== "conversation.entry") {
28098
+ return 0;
28099
+ }
28162
28100
  if (event.kind === "tool_call" && event.status !== "in_progress") {
28163
28101
  return event.content.parts.filter((part) => part.type === "tool_call").reduce((total, part) => total + JSON.stringify(part.input).length, 0);
28164
28102
  }
@@ -32996,12 +32934,17 @@ var ConversationQuestionContentPartSchema2 = external_exports.object({
32996
32934
  toolCallId: external_exports.string().min(1).nullable(),
32997
32935
  questions: external_exports.array(ConversationQuestionSchema2)
32998
32936
  });
32937
+ var ConversationUiMessageContentPartSchema2 = external_exports.object({
32938
+ type: external_exports.literal("ui_message"),
32939
+ message: JsonValueSchema2
32940
+ });
32999
32941
  var ConversationContentPartSchema2 = external_exports.discriminatedUnion("type", [
33000
32942
  ConversationTextContentPartSchema2,
33001
32943
  ConversationReasoningContentPartSchema2,
33002
32944
  ConversationToolCallContentPartSchema2,
33003
32945
  ConversationToolResultContentPartSchema2,
33004
- ConversationQuestionContentPartSchema2
32946
+ ConversationQuestionContentPartSchema2,
32947
+ ConversationUiMessageContentPartSchema2
33005
32948
  ]);
33006
32949
  var ConversationEntryContentSchema2 = external_exports.object({
33007
32950
  parts: external_exports.array(ConversationContentPartSchema2)
@@ -33183,9 +33126,35 @@ var RuntimeBridgeOutputDeltaEnvelopeSchema = external_exports.object({
33183
33126
  delta: ConversationDeltaSchema2,
33184
33127
  createdAt: external_exports.string().datetime()
33185
33128
  }).strict();
33129
+ var RuntimeBridgeOutputUiMessageChunkEnvelopeSchema = external_exports.object({
33130
+ type: external_exports.literal("ui.message.chunk"),
33131
+ sessionId: SessionIdSchema2,
33132
+ runtimeId: RuntimeIdSchema2,
33133
+ bridgeLeaseId: RuntimeBridgeLeaseIdSchema2,
33134
+ outputSeq: external_exports.number().int().positive(),
33135
+ chunk: JsonValueSchema2,
33136
+ createdAt: external_exports.string().datetime()
33137
+ }).strict();
33138
+ var RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema = external_exports.object({
33139
+ type: external_exports.literal("ui.message.completed"),
33140
+ sessionId: SessionIdSchema2,
33141
+ runtimeId: RuntimeIdSchema2,
33142
+ bridgeLeaseId: RuntimeBridgeLeaseIdSchema2,
33143
+ outputSeq: external_exports.number().int().positive(),
33144
+ messageId: external_exports.string().trim().min(1),
33145
+ role: external_exports.enum(["system", "user", "assistant"]),
33146
+ status: ConversationEntryStatusSchema2,
33147
+ message: JsonValueSchema2,
33148
+ turnStatus: external_exports.enum(["waiting_for_input", "completed", "failed"]).optional(),
33149
+ usage: RuntimeBridgeTurnUsageSchema.optional(),
33150
+ createdAt: external_exports.string().datetime(),
33151
+ completedAt: external_exports.string().datetime().nullable()
33152
+ }).strict();
33186
33153
  var RuntimeBridgeOutputEnvelopeSchema = external_exports.union([
33187
33154
  RuntimeBridgeOutputEntryEnvelopeSchema,
33188
- RuntimeBridgeOutputDeltaEnvelopeSchema
33155
+ RuntimeBridgeOutputDeltaEnvelopeSchema,
33156
+ RuntimeBridgeOutputUiMessageChunkEnvelopeSchema,
33157
+ RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema
33189
33158
  ]);
33190
33159
  function legacyRuntimeTurnStatus(entry, sessionStatusAfter) {
33191
33160
  if (sessionStatusAfter === "failed" || entry.status === "failed") {
@@ -33665,17 +33634,27 @@ function outputLogContext(output, socketId) {
33665
33634
  kind: "kind" in output ? output.kind : void 0,
33666
33635
  output_status: "status" in output ? output.status : void 0,
33667
33636
  delta_type: output.type === "conversation.delta" ? output.delta.type : void 0,
33637
+ ui_chunk_type: output.type === "ui.message.chunk" ? jsonRecordString(output.chunk, "type") : void 0,
33668
33638
  message_id: "messageId" in output ? output.messageId : void 0
33669
33639
  };
33670
33640
  }
33671
33641
  function errorMessage(error51) {
33672
33642
  return error51 instanceof Error ? error51.message : String(error51);
33673
33643
  }
33644
+ function jsonRecordString(value, key) {
33645
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
33646
+ return void 0;
33647
+ }
33648
+ const field = value[key];
33649
+ return typeof field === "string" ? field : void 0;
33650
+ }
33674
33651
 
33675
33652
  // src/commands/agent-bridge/harness/claude-code/index.ts
33676
33653
  init_src();
33677
33654
 
33678
33655
  // src/commands/agent-bridge/harness/output-buffer.ts
33656
+ init_src();
33657
+ import { readUIMessageStream } from "ai";
33679
33658
  var AGENT_BRIDGE_OUTPUT_DELTA_FLUSH_MS = 50;
33680
33659
  var AgentBridgeOutputBuffer = class {
33681
33660
  constructor(input) {
@@ -33687,12 +33666,17 @@ var AgentBridgeOutputBuffer = class {
33687
33666
  pendingOutputs = /* @__PURE__ */ new Map();
33688
33667
  pendingDelta = null;
33689
33668
  deltaFlushTimer = null;
33669
+ activeUiMessageAssembler = null;
33690
33670
  drainBlocked = false;
33691
33671
  drainPromise = null;
33692
33672
  // ---------------------------------------------------------------------------
33693
33673
  // Public API
33694
33674
  // ---------------------------------------------------------------------------
33695
33675
  async emitProjection(context, projection) {
33676
+ if (projection.type === "ui_message_chunk") {
33677
+ await this.emitUiMessageChunk(context, projection);
33678
+ return;
33679
+ }
33696
33680
  if (projection.type === "delta") {
33697
33681
  await this.bufferDelta(context, projection.delta);
33698
33682
  return;
@@ -33704,6 +33688,75 @@ var AgentBridgeOutputBuffer = class {
33704
33688
  await this.flushPendingDelta({ force: true });
33705
33689
  await this.drainPendingOutputs({ force: true });
33706
33690
  }
33691
+ async emitUiMessageChunk(context, projection) {
33692
+ if (projection.chunk.type === "data-auto-question") {
33693
+ await this.flushPendingDelta();
33694
+ await this.emitLiveUiMessageChunk(context, projection.chunk);
33695
+ await this.enqueueProjectionAndDrain(context, {
33696
+ type: "entry",
33697
+ entry: {
33698
+ role: "assistant",
33699
+ kind: "question",
33700
+ ...projection.turnStatus ? { turnStatus: projection.turnStatus } : {},
33701
+ content: {
33702
+ parts: [questionPart(projection.chunk.data)]
33703
+ }
33704
+ }
33705
+ });
33706
+ return;
33707
+ }
33708
+ await this.flushPendingDelta();
33709
+ await this.emitLiveUiMessageChunk(context, projection.chunk);
33710
+ const completedMessage = await this.appendUiChunkToAssembler(
33711
+ projection.chunk
33712
+ );
33713
+ if (!completedMessage || !completedMessage.id.trim()) {
33714
+ if (isTerminalUiMessageChunk(projection.chunk)) {
33715
+ const statusText = terminalStatusText(projection);
33716
+ if (statusText) {
33717
+ await this.enqueueProjectionAndDrain(context, {
33718
+ type: "entry",
33719
+ entry: {
33720
+ role: "system",
33721
+ kind: "status",
33722
+ status: terminalEntryStatus(projection),
33723
+ ...projection.turnStatus ? { turnStatus: projection.turnStatus } : {},
33724
+ ...projection.usage ? { usage: projection.usage } : {},
33725
+ content: {
33726
+ parts: [{ type: "text", text: statusText }]
33727
+ }
33728
+ }
33729
+ });
33730
+ }
33731
+ }
33732
+ return;
33733
+ }
33734
+ this.outputSeq += 1;
33735
+ const completedAt = now2();
33736
+ const completedOutput = buildUiMessageCompletedOutputEnvelope({
33737
+ context,
33738
+ outputSeq: this.outputSeq,
33739
+ message: completedMessage,
33740
+ status: terminalEntryStatus(projection),
33741
+ turnStatus: projection.turnStatus,
33742
+ usage: projection.usage,
33743
+ createdAt: completedAt,
33744
+ completedAt
33745
+ });
33746
+ this.enqueueOutput(completedOutput);
33747
+ await this.drainPendingOutputs();
33748
+ }
33749
+ async emitLiveUiMessageChunk(context, chunk) {
33750
+ this.outputSeq += 1;
33751
+ const output = buildUiMessageChunkOutputEnvelope({
33752
+ context,
33753
+ outputSeq: this.outputSeq,
33754
+ chunk,
33755
+ createdAt: now2()
33756
+ });
33757
+ this.enqueueOutput(output);
33758
+ await this.drainPendingOutputs();
33759
+ }
33707
33760
  // ---------------------------------------------------------------------------
33708
33761
  // Transport emit
33709
33762
  // ---------------------------------------------------------------------------
@@ -33860,11 +33913,91 @@ var AgentBridgeOutputBuffer = class {
33860
33913
  kind: output && "kind" in output ? output.kind : void 0,
33861
33914
  output_status: output && "status" in output ? output.status : void 0,
33862
33915
  delta_type: output?.type === "conversation.delta" ? output.delta.type : void 0,
33916
+ ui_chunk_type: output?.type === "ui.message.chunk" ? jsonRecordString2(output.chunk, "type") : void 0,
33863
33917
  message_id: output && "messageId" in output ? output.messageId : void 0,
33864
33918
  ...fields
33865
33919
  };
33866
33920
  }
33921
+ async appendUiChunkToAssembler(chunk) {
33922
+ if (chunk.type === "start" || !this.activeUiMessageAssembler || this.activeUiMessageAssembler.done) {
33923
+ this.activeUiMessageAssembler = new UiMessageAssembler();
33924
+ }
33925
+ const completedMessage = await this.activeUiMessageAssembler.appendChunk(chunk);
33926
+ if (completedMessage) {
33927
+ this.activeUiMessageAssembler = null;
33928
+ }
33929
+ return completedMessage;
33930
+ }
33867
33931
  };
33932
+ var UiMessageAssembler = class {
33933
+ controller = null;
33934
+ latestMessage = null;
33935
+ closed = false;
33936
+ drained;
33937
+ constructor() {
33938
+ const stream = new ReadableStream({
33939
+ start: (controller) => {
33940
+ this.controller = controller;
33941
+ }
33942
+ });
33943
+ this.drained = (async () => {
33944
+ try {
33945
+ for await (const message of readUIMessageStream({
33946
+ stream,
33947
+ terminateOnError: false
33948
+ })) {
33949
+ this.latestMessage = message;
33950
+ }
33951
+ } finally {
33952
+ this.closed = true;
33953
+ }
33954
+ })();
33955
+ }
33956
+ get done() {
33957
+ return this.closed;
33958
+ }
33959
+ async appendChunk(chunk) {
33960
+ try {
33961
+ this.controller?.enqueue(chunk);
33962
+ } catch {
33963
+ this.closed = true;
33964
+ return null;
33965
+ }
33966
+ if (!isTerminalUiMessageChunk(chunk)) {
33967
+ return null;
33968
+ }
33969
+ this.closed = true;
33970
+ this.controller?.close();
33971
+ await this.drained;
33972
+ return this.latestMessage;
33973
+ }
33974
+ };
33975
+ function isTerminalUiMessageChunk(chunk) {
33976
+ return chunk.type === "finish" || chunk.type === "error" || chunk.type === "abort";
33977
+ }
33978
+ function questionPart(data) {
33979
+ const parsed = data;
33980
+ return {
33981
+ type: "question",
33982
+ toolCallId: parsed.toolCallId ?? null,
33983
+ questions: parsed.questions
33984
+ };
33985
+ }
33986
+ function terminalStatusText(projection) {
33987
+ if (projection.statusText) {
33988
+ return projection.statusText;
33989
+ }
33990
+ if (projection.chunk.type === "error") {
33991
+ return projection.chunk.errorText;
33992
+ }
33993
+ return null;
33994
+ }
33995
+ function terminalEntryStatus(projection) {
33996
+ if (projection.turnStatus === "failed" || projection.chunk.type === "error" || projection.chunk.type === "abort") {
33997
+ return "failed";
33998
+ }
33999
+ return "completed";
34000
+ }
33868
34001
  function canCoalesceDelta(pending, context, delta) {
33869
34002
  return pending.context.sessionId === context.sessionId && pending.context.runtimeId === context.runtimeId && pending.context.bridgeLeaseId === context.bridgeLeaseId && pending.delta.messageId === delta.messageId && pending.delta.partId === delta.partId && pending.delta.role === delta.role && pending.delta.kind === delta.kind && pending.delta.delta.type === delta.delta.type;
33870
34003
  }
@@ -33924,67 +34057,209 @@ function buildDeltaOutputEnvelope(input) {
33924
34057
  createdAt: input.createdAt
33925
34058
  });
33926
34059
  }
34060
+ function buildUiMessageChunkOutputEnvelope(input) {
34061
+ const { context } = input;
34062
+ return RuntimeBridgeOutputEnvelopeSchema.parse({
34063
+ type: "ui.message.chunk",
34064
+ sessionId: context.sessionId,
34065
+ runtimeId: context.runtimeId,
34066
+ bridgeLeaseId: context.bridgeLeaseId,
34067
+ outputSeq: input.outputSeq,
34068
+ chunk: toJsonValue(input.chunk),
34069
+ createdAt: input.createdAt
34070
+ });
34071
+ }
34072
+ function buildUiMessageCompletedOutputEnvelope(input) {
34073
+ const { context } = input;
34074
+ return RuntimeBridgeOutputEnvelopeSchema.parse({
34075
+ type: "ui.message.completed",
34076
+ sessionId: context.sessionId,
34077
+ runtimeId: context.runtimeId,
34078
+ bridgeLeaseId: context.bridgeLeaseId,
34079
+ outputSeq: input.outputSeq,
34080
+ messageId: input.message.id,
34081
+ role: input.message.role,
34082
+ status: input.status,
34083
+ message: toJsonValue(input.message),
34084
+ ...input.turnStatus ? { turnStatus: input.turnStatus } : {},
34085
+ ...input.usage ? { usage: input.usage } : {},
34086
+ createdAt: input.createdAt,
34087
+ completedAt: input.completedAt
34088
+ });
34089
+ }
33927
34090
  function isSuccessfulOutputAck(ack) {
33928
34091
  return ack.status === "persisted" || ack.status === "duplicate" || ack.status === "published";
33929
34092
  }
33930
34093
  function now2() {
33931
34094
  return (/* @__PURE__ */ new Date()).toISOString();
33932
34095
  }
34096
+ function jsonRecordString2(value, key) {
34097
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
34098
+ return void 0;
34099
+ }
34100
+ const field = value[key];
34101
+ return typeof field === "string" ? field : void 0;
34102
+ }
33933
34103
 
33934
34104
  // src/commands/agent-bridge/harness/claude-code/projector.ts
33935
34105
  init_src();
33936
34106
  var ClaudeCodeProjector = class {
33937
34107
  currentMessageId = null;
34108
+ // The turn's UI message id. Claude emits a `message_start` per assistant
34109
+ // message, but a tool-using turn spans several (message A with the tool call,
34110
+ // the tool result, then message B). We open the UI message once per turn and
34111
+ // bracket each message as a step (finish-step at message_stop), closing it
34112
+ // with the single terminal `finish` at the turn result — so the whole turn,
34113
+ // including paired tool input/output, assembles into one UIMessage. Mirrors
34114
+ // the Codex projector's `ensureResponseStarted`.
34115
+ activeResponseMessageId = null;
34116
+ activeParts = /* @__PURE__ */ new Map();
34117
+ activeToolInputs = /* @__PURE__ */ new Map();
34118
+ streamedMessageIds = /* @__PURE__ */ new Set();
33938
34119
  project(message) {
33939
34120
  if (message.type === "stream_event") {
33940
- if (message.event.type === "message_start") {
33941
- this.currentMessageId = anthropicMessageIdFromStreamStart(message) ?? UNKNOWN_MESSAGE_ID;
33942
- return [];
33943
- }
33944
- if (message.event.type === "message_stop") {
33945
- this.currentMessageId = null;
33946
- return [];
33947
- }
33948
- const delta = partialAssistantDeltaPayload(
33949
- message,
33950
- this.currentMessageId ?? UNKNOWN_MESSAGE_ID
33951
- );
33952
- return delta ? [{ type: "delta", delta }] : [];
34121
+ return this.projectStreamEvent(message);
33953
34122
  }
33954
34123
  const parsed = parseClaudeCodeStreamRecord(message);
33955
- const outputs = parsed.projections.map(
33956
- (entry) => ({
33957
- type: "entry",
33958
- // A question means the delivered turn is parked on AskUserQuestion
33959
- // waiting for an operator answer.
33960
- entry: entry.kind === "question" ? { ...entry, turnStatus: "waiting_for_input" } : entry
33961
- })
33962
- );
34124
+ const snapshotMessageId = assistantSnapshotMessageId(message);
34125
+ const suppressStreamed = snapshotMessageId !== null && this.streamedMessageIds.has(snapshotMessageId);
34126
+ const outputs = !suppressStreamed && message.type === "assistant" ? projectAssistantSnapshot(parsed.projections) : parsed.projections.flatMap((entry) => {
34127
+ if (suppressStreamed && (entry.kind === "message" || entry.kind === "tool_call")) {
34128
+ return [];
34129
+ }
34130
+ return conversationProjectionToUiChunks(entry);
34131
+ });
33963
34132
  if (parsed.result) {
34133
+ outputs.push(...this.endActiveParts(), ...this.finishActiveToolInputs());
34134
+ this.currentMessageId = null;
34135
+ this.activeResponseMessageId = null;
33964
34136
  outputs.push(projectClaudeCodeResult(parsed.result));
33965
34137
  }
33966
34138
  return outputs;
33967
34139
  }
34140
+ flushPendingAssistantMessages() {
34141
+ const outputs = [
34142
+ ...this.endActiveParts(),
34143
+ uiChunk({ type: "finish", finishReason: "stop" })
34144
+ ];
34145
+ this.currentMessageId = null;
34146
+ this.activeResponseMessageId = null;
34147
+ return outputs;
34148
+ }
34149
+ projectStreamEvent(message) {
34150
+ if (message.event.type === "message_start") {
34151
+ this.currentMessageId = anthropicMessageIdFromStreamStart(message) ?? UNKNOWN_MESSAGE_ID;
34152
+ this.streamedMessageIds.add(this.currentMessageId);
34153
+ this.activeParts.clear();
34154
+ this.activeToolInputs.clear();
34155
+ return this.ensureResponseStarted(this.currentMessageId);
34156
+ }
34157
+ if (message.event.type === "message_stop") {
34158
+ const outputs = [
34159
+ ...this.endActiveParts(),
34160
+ ...this.finishActiveToolInputs(),
34161
+ uiChunk({ type: "finish-step" })
34162
+ ];
34163
+ this.currentMessageId = null;
34164
+ return outputs;
34165
+ }
34166
+ if (message.event.type === "content_block_start") {
34167
+ return this.startToolInput(message);
34168
+ }
34169
+ if (message.event.type === "content_block_stop") {
34170
+ return [
34171
+ ...this.endActivePart(blockIndexId(message.event.index)),
34172
+ ...this.finishActiveToolInput(blockIndexId(message.event.index))
34173
+ ];
34174
+ }
34175
+ return partialAssistantDeltaProjections(
34176
+ message,
34177
+ this.activeParts,
34178
+ this.activeToolInputs
34179
+ );
34180
+ }
34181
+ // Open the turn's UI message exactly once. Subsequent messages in the same
34182
+ // turn reuse it (their content becomes additional steps), so the turn
34183
+ // assembles into a single UIMessage instead of the assembler resetting on
34184
+ // each `start` and dropping every message but the last.
34185
+ ensureResponseStarted(messageId) {
34186
+ if (this.activeResponseMessageId !== null) {
34187
+ return [];
34188
+ }
34189
+ this.activeResponseMessageId = messageId;
34190
+ return [uiChunk({ type: "start", messageId })];
34191
+ }
34192
+ endActiveParts() {
34193
+ const outputs = [...this.activeParts].map(([id]) => this.endActivePart(id));
34194
+ this.activeParts.clear();
34195
+ return outputs.flat();
34196
+ }
34197
+ endActivePart(id) {
34198
+ const type = this.activeParts.get(id);
34199
+ if (!type) {
34200
+ return [];
34201
+ }
34202
+ this.activeParts.delete(id);
34203
+ return [uiChunk({ type: `${type}-end`, id })];
34204
+ }
34205
+ startToolInput(message) {
34206
+ const started = toolInputStartFromEvent(message);
34207
+ if (!started) {
34208
+ return [];
34209
+ }
34210
+ this.activeToolInputs.set(started.index, {
34211
+ toolCallId: started.toolCallId,
34212
+ toolName: started.toolName,
34213
+ initialInput: started.input,
34214
+ inputText: ""
34215
+ });
34216
+ return [
34217
+ uiChunk({
34218
+ type: "tool-input-start",
34219
+ toolCallId: started.toolCallId,
34220
+ toolName: started.toolName
34221
+ })
34222
+ ];
34223
+ }
34224
+ finishActiveToolInput(index) {
34225
+ const active = this.activeToolInputs.get(index);
34226
+ if (!active) {
34227
+ return [];
34228
+ }
34229
+ this.activeToolInputs.delete(index);
34230
+ return [
34231
+ uiChunk({
34232
+ type: "tool-input-available",
34233
+ toolCallId: active.toolCallId,
34234
+ toolName: active.toolName,
34235
+ input: completeToolInput(active)
34236
+ })
34237
+ ];
34238
+ }
34239
+ finishActiveToolInputs() {
34240
+ return [...this.activeToolInputs.keys()].flatMap(
34241
+ (index) => this.finishActiveToolInput(index)
34242
+ );
34243
+ }
33968
34244
  };
33969
34245
  function projectClaudeCodeResult(result) {
33970
34246
  return {
33971
- type: "entry",
33972
- entry: {
33973
- role: "system",
33974
- kind: "status",
33975
- status: result.isError ? "failed" : "completed",
33976
- turnStatus: result.isError ? "failed" : "completed",
33977
- content: {
33978
- parts: [
33979
- {
33980
- type: "text",
33981
- text: result.isError ? `claude-code failed: ${result.errorMessage ?? "unknown error"}` : "claude-code completed"
33982
- }
33983
- ]
33984
- }
33985
- }
34247
+ type: "ui_message_chunk",
34248
+ chunk: {
34249
+ type: "finish",
34250
+ finishReason: result.isError ? "error" : "stop"
34251
+ },
34252
+ statusText: result.isError ? `claude-code failed: ${result.errorMessage ?? "unknown error"}` : "claude-code completed",
34253
+ turnStatus: result.isError ? "failed" : "completed"
33986
34254
  };
33987
34255
  }
34256
+ function assistantSnapshotMessageId(message) {
34257
+ if (message.type !== "assistant") {
34258
+ return null;
34259
+ }
34260
+ const id = message.message.id;
34261
+ return typeof id === "string" && id.trim() ? id : UNKNOWN_MESSAGE_ID;
34262
+ }
33988
34263
  function anthropicMessageIdFromStreamStart(message) {
33989
34264
  const event = message.event;
33990
34265
  if (event.type !== "message_start") {
@@ -33993,27 +34268,284 @@ function anthropicMessageIdFromStreamStart(message) {
33993
34268
  const id = event.message.id;
33994
34269
  return typeof id === "string" && id.trim() ? id : null;
33995
34270
  }
33996
- function partialAssistantDeltaPayload(message, messageId) {
34271
+ function partialAssistantDeltaProjections(message, activeParts, activeToolInputs) {
33997
34272
  const event = message.event;
33998
34273
  if (event.type !== "content_block_delta") {
33999
- return null;
34274
+ return [];
34275
+ }
34276
+ const toolInputDelta = toolInputDeltaFromDelta(event.delta);
34277
+ if (toolInputDelta) {
34278
+ const active = activeToolInputs.get(blockIndexId(event.index));
34279
+ if (!active) {
34280
+ return [];
34281
+ }
34282
+ active.inputText += toolInputDelta;
34283
+ return [
34284
+ uiChunk({
34285
+ type: "tool-input-delta",
34286
+ toolCallId: active.toolCallId,
34287
+ inputTextDelta: toolInputDelta
34288
+ })
34289
+ ];
34290
+ }
34291
+ const sourceChunk = sourceChunkFromDelta(event.delta, event.index);
34292
+ if (sourceChunk) {
34293
+ return [uiChunk(sourceChunk)];
34000
34294
  }
34001
34295
  const delta = event.delta;
34002
- const text = delta.type === "text_delta" ? delta.text : delta.type === "thinking_delta" ? delta.thinking : "";
34003
- if (text.length === 0) {
34296
+ const content = textContentFromDelta(delta);
34297
+ if (!content || content.text.length === 0) {
34298
+ return [];
34299
+ }
34300
+ const id = typeof event.index === "number" ? String(event.index) : "0";
34301
+ const outputs = [];
34302
+ if (!activeParts.has(id)) {
34303
+ activeParts.set(id, content.type);
34304
+ outputs.push(
34305
+ uiChunk({ type: `${content.type}-start`, id })
34306
+ );
34307
+ }
34308
+ outputs.push(
34309
+ uiChunk({
34310
+ type: `${content.type}-delta`,
34311
+ id,
34312
+ delta: content.text
34313
+ })
34314
+ );
34315
+ return outputs;
34316
+ }
34317
+ function toolInputStartFromEvent(message) {
34318
+ const event = message.event;
34319
+ if (event.type !== "content_block_start") {
34320
+ return null;
34321
+ }
34322
+ const contentBlock = event.content_block;
34323
+ if (contentBlock.type !== "tool_use" && contentBlock.type !== "mcp_tool_use" && contentBlock.type !== "server_tool_use") {
34324
+ return null;
34325
+ }
34326
+ const id = contentBlock.id;
34327
+ const name = contentBlock.name;
34328
+ if (typeof id !== "string" || typeof name !== "string") {
34329
+ return null;
34330
+ }
34331
+ if (name === ASK_USER_QUESTION_TOOL_NAME) {
34004
34332
  return null;
34005
34333
  }
34006
- const index = event.index;
34007
- const type = delta.type === "thinking_delta" ? "reasoning" : "text";
34008
34334
  return {
34009
- messageId,
34010
- partId: typeof index === "number" ? String(index) : "0",
34011
- role: "assistant",
34012
- kind: "message",
34013
- delta: {
34014
- type,
34015
- text
34335
+ index: blockIndexId(event.index),
34336
+ toolCallId: id,
34337
+ toolName: toolNameFromContentBlock(contentBlock, name),
34338
+ input: contentBlock.input
34339
+ };
34340
+ }
34341
+ function toolInputDeltaFromDelta(delta) {
34342
+ if (delta.type !== "input_json_delta") {
34343
+ return null;
34344
+ }
34345
+ return delta.partial_json;
34346
+ }
34347
+ function completeToolInput(input) {
34348
+ const inputText = input.inputText.trim();
34349
+ if (inputText.length > 0) {
34350
+ try {
34351
+ return JSON.parse(inputText);
34352
+ } catch {
34353
+ return input.inputText;
34354
+ }
34355
+ }
34356
+ return input.initialInput ?? {};
34357
+ }
34358
+ function sourceChunkFromDelta(delta, index) {
34359
+ if (delta.type !== "citations_delta") {
34360
+ return null;
34361
+ }
34362
+ const citation = delta.citation;
34363
+ if (citation.type === "web_search_result_location" && typeof citation.url === "string") {
34364
+ return {
34365
+ type: "source-url",
34366
+ sourceId: citationSourceId(citation, index),
34367
+ url: citation.url,
34368
+ ...typeof citation.title === "string" ? { title: citation.title } : {},
34369
+ providerMetadata: anthropicCitationMetadata(citation)
34370
+ };
34371
+ }
34372
+ return {
34373
+ type: "source-document",
34374
+ sourceId: citationSourceId(citation, index),
34375
+ mediaType: "text/plain",
34376
+ title: citationTitle(citation, index),
34377
+ providerMetadata: anthropicCitationMetadata(citation)
34378
+ };
34379
+ }
34380
+ function toolNameFromContentBlock(contentBlock, name) {
34381
+ return contentBlock.type === "mcp_tool_use" && typeof contentBlock.server_name === "string" ? `${contentBlock.server_name}.${name}` : name;
34382
+ }
34383
+ function citationSourceId(citation, index) {
34384
+ for (const key of ["encrypted_index", "file_id", "source"]) {
34385
+ const value = citation[key];
34386
+ if (typeof value === "string" && value.trim().length > 0) {
34387
+ return value;
34388
+ }
34389
+ }
34390
+ if (typeof citation.document_index === "number") {
34391
+ return `document:${citation.document_index.toString()}`;
34392
+ }
34393
+ return `citation:${index.toString()}`;
34394
+ }
34395
+ function citationTitle(citation, index) {
34396
+ for (const key of ["title", "document_title", "source", "file_id"]) {
34397
+ const value = citation[key];
34398
+ if (typeof value === "string" && value.trim().length > 0) {
34399
+ return value;
34400
+ }
34401
+ }
34402
+ return `Claude citation ${index.toString()}`;
34403
+ }
34404
+ function anthropicCitationMetadata(citation) {
34405
+ return { anthropic: { citation: toJsonValue(citation) } };
34406
+ }
34407
+ function blockIndexId(index) {
34408
+ return index.toString();
34409
+ }
34410
+ function textContentFromDelta(delta) {
34411
+ switch (delta.type) {
34412
+ case "text_delta":
34413
+ return { type: "text", text: delta.text };
34414
+ case "thinking_delta":
34415
+ return { type: "reasoning", text: delta.thinking };
34416
+ default:
34417
+ return null;
34418
+ }
34419
+ }
34420
+ function conversationProjectionToUiChunks(projection) {
34421
+ if (projection.role === "assistant" && projection.kind === "message") {
34422
+ return messageContentChunks(projection);
34423
+ }
34424
+ if (projection.kind === "tool_call") {
34425
+ return toolCallChunks(projection);
34426
+ }
34427
+ if (projection.kind === "tool_result") {
34428
+ return projection.content.parts.flatMap((part) => {
34429
+ if (part.type !== "tool_result") {
34430
+ return [];
34431
+ }
34432
+ return [
34433
+ uiChunk(
34434
+ part.isError ? {
34435
+ type: "tool-output-error",
34436
+ toolCallId: part.toolUseId ?? UNKNOWN_MESSAGE_ID,
34437
+ errorText: JSON.stringify(part.output)
34438
+ } : {
34439
+ type: "tool-output-available",
34440
+ toolCallId: part.toolUseId ?? UNKNOWN_MESSAGE_ID,
34441
+ output: part.output
34442
+ }
34443
+ )
34444
+ ];
34445
+ });
34446
+ }
34447
+ if (projection.kind === "question") {
34448
+ return projection.content.parts.flatMap(
34449
+ (part) => part.type === "question" ? [
34450
+ {
34451
+ type: "ui_message_chunk",
34452
+ chunk: {
34453
+ type: "data-auto-question",
34454
+ data: {
34455
+ toolCallId: part.toolCallId,
34456
+ questions: part.questions
34457
+ }
34458
+ },
34459
+ turnStatus: "waiting_for_input"
34460
+ }
34461
+ ] : []
34462
+ );
34463
+ }
34464
+ return [
34465
+ {
34466
+ type: "entry",
34467
+ entry: projection
34468
+ }
34469
+ ];
34470
+ }
34471
+ function projectAssistantSnapshot(projections) {
34472
+ return [
34473
+ ...assistantSnapshotMessageChunks(projections),
34474
+ ...projections.filter((projection) => !isAssistantMessageOrToolCall(projection)).flatMap((projection) => conversationProjectionToUiChunks(projection))
34475
+ ];
34476
+ }
34477
+ function assistantSnapshotMessageChunks(projections) {
34478
+ const messageProjection = projections.find(
34479
+ (projection) => projection.role === "assistant" && projection.kind === "message"
34480
+ );
34481
+ const toolCallProjections = projections.filter(
34482
+ (projection) => projection.role === "assistant" && projection.kind === "tool_call"
34483
+ );
34484
+ if (!messageProjection && toolCallProjections.length === 0) {
34485
+ return [];
34486
+ }
34487
+ const messageId = messageProjection?.messageId ?? toolCallProjections.find((projection) => projection.messageId)?.messageId ?? UNKNOWN_MESSAGE_ID;
34488
+ const outputs = [
34489
+ uiChunk({ type: "start", messageId })
34490
+ ];
34491
+ if (messageProjection) {
34492
+ outputs.push(...messagePartChunks(messageProjection));
34493
+ }
34494
+ outputs.push(
34495
+ ...toolCallProjections.flatMap((projection) => toolCallChunks(projection))
34496
+ );
34497
+ if (toolCallProjections.length === 0) {
34498
+ outputs.push(uiChunk({ type: "finish", finishReason: "stop" }));
34499
+ }
34500
+ return outputs;
34501
+ }
34502
+ function isAssistantMessageOrToolCall(projection) {
34503
+ return projection.role === "assistant" && (projection.kind === "message" || projection.kind === "tool_call");
34504
+ }
34505
+ function messageContentChunks(projection) {
34506
+ const messageId = projection.messageId ?? UNKNOWN_MESSAGE_ID;
34507
+ const outputs = [
34508
+ uiChunk({ type: "start", messageId })
34509
+ ];
34510
+ outputs.push(...messagePartChunks(projection));
34511
+ outputs.push(uiChunk({ type: "finish", finishReason: "stop" }));
34512
+ return outputs;
34513
+ }
34514
+ function messagePartChunks(projection) {
34515
+ const outputs = [];
34516
+ for (const [index, part] of projection.content.parts.entries()) {
34517
+ if (part.type !== "text" && part.type !== "reasoning") {
34518
+ continue;
34016
34519
  }
34520
+ const id = `${part.type}-${index.toString()}`;
34521
+ outputs.push(
34522
+ uiChunk({ type: `${part.type}-start`, id }),
34523
+ uiChunk({
34524
+ type: `${part.type}-delta`,
34525
+ id,
34526
+ delta: part.text
34527
+ }),
34528
+ uiChunk({ type: `${part.type}-end`, id })
34529
+ );
34530
+ }
34531
+ return outputs;
34532
+ }
34533
+ function toolCallChunks(projection) {
34534
+ return projection.content.parts.flatMap(
34535
+ (part) => part.type === "tool_call" ? [
34536
+ uiChunk({
34537
+ type: "tool-input-available",
34538
+ toolCallId: part.toolCallId ?? UNKNOWN_MESSAGE_ID,
34539
+ toolName: part.name,
34540
+ input: part.input
34541
+ })
34542
+ ] : []
34543
+ );
34544
+ }
34545
+ function uiChunk(chunk) {
34546
+ return {
34547
+ type: "ui_message_chunk",
34548
+ chunk
34017
34549
  };
34018
34550
  }
34019
34551
 
@@ -35264,6 +35796,9 @@ var ClaudeCodeCommandHandler = class {
35264
35796
  if (!activeContext) {
35265
35797
  return;
35266
35798
  }
35799
+ for (const projection of this.projector.flushPendingAssistantMessages()) {
35800
+ await this.emitBridgeOutput(activeContext, projection);
35801
+ }
35267
35802
  await this.emitBridgeOutput(
35268
35803
  activeContext,
35269
35804
  projectClaudeCodeResult({
@@ -35377,30 +35912,27 @@ init_src();
35377
35912
  // src/commands/agent-bridge/harness/codex/projector.ts
35378
35913
  init_src();
35379
35914
  var CodexProjector = class {
35915
+ activeAgentMessages = /* @__PURE__ */ new Set();
35916
+ activeToolCalls = /* @__PURE__ */ new Set();
35917
+ activeResponseMessageId = null;
35380
35918
  project(notification) {
35381
35919
  switch (notification.type) {
35382
35920
  case "agentMessageDelta":
35383
35921
  return [
35384
- {
35385
- type: "delta",
35386
- delta: {
35387
- messageId: notification.itemId,
35388
- partId: "0",
35389
- role: "assistant",
35390
- kind: "message",
35391
- delta: { type: "text", text: notification.delta }
35392
- }
35393
- }
35922
+ ...this.ensureResponseStarted(notification.itemId),
35923
+ ...this.projectAgentMessageDelta(
35924
+ notification.itemId,
35925
+ notification.delta
35926
+ )
35394
35927
  ];
35395
35928
  case "itemStarted":
35396
- return entryProjections(
35397
- projectCodexItem({ item: notification.item, phase: "started" })
35398
- );
35929
+ return this.projectItem(notification.item, "started");
35399
35930
  case "itemCompleted":
35400
- return entryProjections(
35401
- projectCodexItem({ item: notification.item, phase: "completed" })
35402
- );
35931
+ return this.projectItem(notification.item, "completed");
35403
35932
  case "turnCompleted":
35933
+ this.activeResponseMessageId = null;
35934
+ this.activeAgentMessages.clear();
35935
+ this.activeToolCalls.clear();
35404
35936
  return [turnCompletionEntry(notification)];
35405
35937
  case "error":
35406
35938
  return [errorEntry(notification)];
@@ -35408,81 +35940,267 @@ var CodexProjector = class {
35408
35940
  return [];
35409
35941
  }
35410
35942
  }
35411
- // An approval parks the turn on operator input, so the question entry also
35943
+ // An approval parks the turn on operator input, so the question chunk also
35412
35944
  // marks the delivered turn as waiting for input.
35413
35945
  projectApproval(request) {
35414
- return {
35415
- type: "entry",
35416
- entry: {
35417
- ...projectCodexApproval(request),
35946
+ return [
35947
+ uiChunk2({
35948
+ type: "tool-approval-request",
35949
+ approvalId: String(request.requestId),
35950
+ toolCallId: request.itemId
35951
+ }),
35952
+ {
35953
+ type: "ui_message_chunk",
35954
+ chunk: {
35955
+ type: "data-auto-question",
35956
+ data: {
35957
+ toolCallId: request.itemId,
35958
+ questions: [approvalQuestion(request)]
35959
+ }
35960
+ },
35418
35961
  turnStatus: "waiting_for_input"
35419
35962
  }
35420
- };
35963
+ ];
35421
35964
  }
35422
35965
  // Surfaces a session-level failure (e.g. the app-server process dying) as a
35423
35966
  // durable failed status entry rather than only a diagnostic log line.
35424
35967
  projectSessionFailure(message) {
35425
35968
  return {
35426
- type: "entry",
35427
- entry: {
35428
- role: "system",
35429
- kind: "status",
35430
- status: "failed",
35431
- turnStatus: "failed",
35432
- content: {
35433
- parts: [{ type: "text", text: `codex failed: ${message}` }]
35434
- }
35435
- }
35969
+ type: "ui_message_chunk",
35970
+ chunk: { type: "error", errorText: `codex failed: ${message}` }
35436
35971
  };
35437
35972
  }
35973
+ projectAgentMessageDelta(itemId, delta) {
35974
+ const outputs = [];
35975
+ if (!this.activeAgentMessages.has(itemId)) {
35976
+ this.activeAgentMessages.add(itemId);
35977
+ outputs.push(uiChunk2({ type: "text-start", id: textPartId(itemId) }));
35978
+ }
35979
+ outputs.push(
35980
+ uiChunk2({ type: "text-delta", id: textPartId(itemId), delta })
35981
+ );
35982
+ return outputs;
35983
+ }
35984
+ projectItem(item, phase) {
35985
+ switch (item.type) {
35986
+ case "agentMessage":
35987
+ return [
35988
+ ...this.ensureResponseStarted(item.id),
35989
+ ...this.projectAgentMessageItem(item, phase)
35990
+ ];
35991
+ case "reasoning":
35992
+ return phase === "completed" ? [
35993
+ ...this.ensureResponseStarted(item.id),
35994
+ ...reasoningItemChunks(item)
35995
+ ] : [];
35996
+ case "commandExecution":
35997
+ return this.projectToolItem({
35998
+ phase,
35999
+ itemId: item.id,
36000
+ name: "shell",
36001
+ input: {
36002
+ command: item.command,
36003
+ ...item.cwd ? { cwd: item.cwd } : {}
36004
+ },
36005
+ output: item.aggregatedOutput ?? "",
36006
+ isError: isFailedStatus(item.status) || (item.exitCode ?? 0) !== 0,
36007
+ title: item.command,
36008
+ toolMetadata: statusMetadata(item.status, {
36009
+ ...item.exitCode !== void 0 ? { exitCode: item.exitCode } : {}
36010
+ })
36011
+ });
36012
+ case "fileChange":
36013
+ return this.projectToolItem({
36014
+ phase,
36015
+ itemId: item.id,
36016
+ name: "apply_patch",
36017
+ input: { changes: toJsonValue(item.changes) },
36018
+ output: { status: item.status ?? "unknown" },
36019
+ isError: isFailedStatus(item.status),
36020
+ title: "apply_patch",
36021
+ toolMetadata: statusMetadata(item.status)
36022
+ });
36023
+ case "mcpToolCall":
36024
+ return this.projectToolItem({
36025
+ phase,
36026
+ itemId: item.id,
36027
+ name: `${item.server}.${item.tool}`,
36028
+ input: toJsonValue(item.arguments),
36029
+ output: item.error ? { error: item.error.message } : toJsonValue(item.result),
36030
+ isError: isFailedStatus(item.status) || item.error != null,
36031
+ title: `${item.server}.${item.tool}`,
36032
+ toolMetadata: statusMetadata(item.status, {
36033
+ server: item.server,
36034
+ tool: item.tool
36035
+ })
36036
+ });
36037
+ default:
36038
+ return [];
36039
+ }
36040
+ }
36041
+ projectToolItem(input) {
36042
+ const outputs = [];
36043
+ outputs.push(...this.ensureResponseStarted(input.itemId));
36044
+ if (input.phase === "started" || !this.activeToolCalls.has(input.itemId)) {
36045
+ outputs.push(toolInputChunk(input));
36046
+ this.activeToolCalls.add(input.itemId);
36047
+ }
36048
+ if (input.phase === "started") {
36049
+ return outputs;
36050
+ }
36051
+ outputs.push(toolOutputChunk(input));
36052
+ outputs.push(uiChunk2({ type: "finish-step" }));
36053
+ this.activeToolCalls.delete(input.itemId);
36054
+ return outputs;
36055
+ }
36056
+ projectAgentMessageItem(item, phase) {
36057
+ if (phase === "started") {
36058
+ if (this.activeAgentMessages.has(item.id)) {
36059
+ return [];
36060
+ }
36061
+ this.activeAgentMessages.add(item.id);
36062
+ return [uiChunk2({ type: "text-start", id: textPartId(item.id) })];
36063
+ }
36064
+ const outputs = [];
36065
+ if (!this.activeAgentMessages.has(item.id)) {
36066
+ outputs.push(uiChunk2({ type: "text-start", id: textPartId(item.id) }));
36067
+ if (item.text.length > 0) {
36068
+ outputs.push(
36069
+ uiChunk2({
36070
+ type: "text-delta",
36071
+ id: textPartId(item.id),
36072
+ delta: item.text
36073
+ })
36074
+ );
36075
+ }
36076
+ }
36077
+ outputs.push(
36078
+ uiChunk2({ type: "text-end", id: textPartId(item.id) }),
36079
+ uiChunk2({ type: "finish-step" })
36080
+ );
36081
+ this.activeAgentMessages.delete(item.id);
36082
+ return outputs;
36083
+ }
36084
+ ensureResponseStarted(messageId) {
36085
+ if (this.activeResponseMessageId !== null) {
36086
+ return [];
36087
+ }
36088
+ this.activeResponseMessageId = messageId;
36089
+ return [uiChunk2({ type: "start", messageId })];
36090
+ }
35438
36091
  };
35439
- function entryProjections(projections) {
35440
- return projections.map((projection) => ({
35441
- type: "entry",
35442
- entry: projection
35443
- }));
36092
+ function reasoningItemChunks(item) {
36093
+ const text = item.summary.map((line) => line.trim()).filter(Boolean).join("\n\n");
36094
+ if (text.length === 0) {
36095
+ return [];
36096
+ }
36097
+ const id = reasoningPartId(item.id);
36098
+ return [
36099
+ uiChunk2({ type: "reasoning-start", id }),
36100
+ uiChunk2({ type: "reasoning-delta", id, delta: text }),
36101
+ uiChunk2({ type: "reasoning-end", id }),
36102
+ uiChunk2({ type: "finish-step" })
36103
+ ];
36104
+ }
36105
+ function toolInputChunk(input) {
36106
+ return uiChunk2({
36107
+ type: "tool-input-available",
36108
+ toolCallId: input.itemId,
36109
+ toolName: input.name,
36110
+ input: input.input,
36111
+ ...input.title ? { title: input.title } : {},
36112
+ ...input.toolMetadata ? { toolMetadata: input.toolMetadata } : {}
36113
+ });
36114
+ }
36115
+ function toolOutputChunk(input) {
36116
+ return uiChunk2(
36117
+ input.isError ? {
36118
+ type: "tool-output-error",
36119
+ toolCallId: input.itemId,
36120
+ errorText: JSON.stringify(input.output),
36121
+ ...input.toolMetadata ? { toolMetadata: input.toolMetadata } : {}
36122
+ } : {
36123
+ type: "tool-output-available",
36124
+ toolCallId: input.itemId,
36125
+ output: input.output,
36126
+ ...input.toolMetadata ? { toolMetadata: input.toolMetadata } : {}
36127
+ }
36128
+ );
35444
36129
  }
35445
36130
  function turnCompletionEntry(notification) {
35446
36131
  if (notification.status === "failed") {
35447
36132
  const detail = notification.errorMessage ?? "unknown error";
35448
- return statusEntry({
36133
+ return statusFinish({
35449
36134
  text: `codex turn failed: ${detail}`,
35450
- status: "failed",
35451
36135
  turnStatus: "failed"
35452
36136
  });
35453
36137
  }
35454
- const text = notification.status === "interrupted" ? "codex turn interrupted" : "codex turn completed";
35455
- return statusEntry({
35456
- text,
35457
- status: "completed",
36138
+ return statusFinish({
36139
+ text: notification.status === "interrupted" ? "codex turn interrupted" : "codex turn completed",
35458
36140
  turnStatus: "completed"
35459
36141
  });
35460
36142
  }
35461
36143
  function errorEntry(notification) {
35462
36144
  if (notification.willRetry) {
35463
- return statusEntry({
35464
- text: `codex retrying after error: ${notification.message}`,
35465
- status: "completed"
36145
+ return statusFinish({
36146
+ text: `codex retrying after error: ${notification.message}`
35466
36147
  });
35467
36148
  }
35468
- return statusEntry({
36149
+ return statusFinish({
35469
36150
  text: `codex error: ${notification.message}`,
35470
- status: "failed",
35471
36151
  turnStatus: "failed"
35472
36152
  });
35473
36153
  }
35474
- function statusEntry(input) {
36154
+ function statusFinish(input) {
35475
36155
  return {
35476
- type: "entry",
35477
- entry: {
35478
- role: "system",
35479
- kind: "status",
35480
- status: input.status,
35481
- ...input.turnStatus ? { turnStatus: input.turnStatus } : {},
35482
- content: { parts: [{ type: "text", text: input.text }] }
35483
- }
36156
+ type: "ui_message_chunk",
36157
+ chunk: {
36158
+ type: "finish",
36159
+ finishReason: input.turnStatus === "failed" ? "error" : "stop"
36160
+ },
36161
+ statusText: input.text,
36162
+ ...input.turnStatus ? { turnStatus: input.turnStatus } : {}
35484
36163
  };
35485
36164
  }
36165
+ function approvalQuestion(request) {
36166
+ const subject = request.type === "commandExecution" ? `run the command: ${request.command ?? "(unknown command)"}` : "apply file changes";
36167
+ return {
36168
+ question: request.reason ? `Codex requests approval to ${subject}. ${request.reason}` : `Codex requests approval to ${subject}.`,
36169
+ header: "Approval",
36170
+ options: [
36171
+ { label: APPROVE_OPTION_LABEL, description: "Allow this action." },
36172
+ { label: DECLINE_OPTION_LABEL, description: "Reject this action." }
36173
+ ],
36174
+ multiSelect: false
36175
+ };
36176
+ }
36177
+ function textPartId(itemId) {
36178
+ return `${itemId}:text`;
36179
+ }
36180
+ function reasoningPartId(itemId) {
36181
+ return `${itemId}:reasoning`;
36182
+ }
36183
+ function isFailedStatus(status) {
36184
+ return status === "failed" || status === "declined";
36185
+ }
36186
+ function statusMetadata(status, fields = {}) {
36187
+ return {
36188
+ status: status ?? "unknown",
36189
+ ...fields
36190
+ };
36191
+ }
36192
+ function uiChunk2(chunk) {
36193
+ return {
36194
+ type: "ui_message_chunk",
36195
+ chunk: normalizeChunk(chunk)
36196
+ };
36197
+ }
36198
+ function normalizeChunk(chunk) {
36199
+ if ("toolCallId" in chunk && (!chunk.toolCallId || chunk.toolCallId.trim().length === 0)) {
36200
+ return { ...chunk, toolCallId: UNKNOWN_MESSAGE_ID };
36201
+ }
36202
+ return chunk;
36203
+ }
35486
36204
 
35487
36205
  // src/commands/agent-bridge/harness/codex/resume-store.ts
35488
36206
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
@@ -36253,7 +36971,9 @@ var CodexCommandHandler = class {
36253
36971
  this.input.writeOutput?.(
36254
36972
  `agent_bridge_codex_question_pending tool_use_id=${request.itemId}`
36255
36973
  );
36256
- await this.emit(activeContext, this.projector.projectApproval(request));
36974
+ for (const projection of this.projector.projectApproval(request)) {
36975
+ await this.emit(activeContext, projection);
36976
+ }
36257
36977
  }
36258
36978
  async handleSessionError(error51) {
36259
36979
  this.input.writeOutput?.(
@@ -40261,6 +40981,8 @@ function entryPreview(event, full) {
40261
40981
  return stringify5(part.output);
40262
40982
  case "question":
40263
40983
  return part.questions.map((question) => question.question).join(" ");
40984
+ case "ui_message":
40985
+ return stringify5(part.message);
40264
40986
  default:
40265
40987
  return "";
40266
40988
  }