replicas-engine 0.1.461 → 0.1.463

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.
Files changed (2) hide show
  1. package/dist/src/index.js +1213 -13
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -51,6 +51,9 @@ function isValidAgentProvider(value) {
51
51
  return VALID_AGENT_PROVIDERS.some((p) => p === value);
52
52
  }
53
53
  var VALID_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
54
+ function isValidThinkingLevel(value) {
55
+ return VALID_THINKING_LEVELS.some((l) => l === value);
56
+ }
54
57
  var CODEX_REASONING_EFFORT_BY_THINKING_LEVEL = {
55
58
  low: "low",
56
59
  medium: "medium",
@@ -64,6 +67,21 @@ function codexReasoningEffortForThinkingLevel(thinkingLevel) {
64
67
 
65
68
  // ../shared/src/event.ts
66
69
  var CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE = "claude-partial-message";
70
+ function coerceClaudePartialMessagePayload(payload) {
71
+ const streamId = payload.streamId;
72
+ if (typeof streamId !== "string" || !streamId) return null;
73
+ const parentToolUseId = payload.parent_tool_use_id;
74
+ const text = typeof payload.text === "string" && payload.text ? payload.text : void 0;
75
+ const thinking = typeof payload.thinking === "string" && payload.thinking ? payload.thinking : void 0;
76
+ if (!text && !thinking) return null;
77
+ return {
78
+ streamId,
79
+ parent_tool_use_id: typeof parentToolUseId === "string" ? parentToolUseId : null,
80
+ ...text ? { text } : {},
81
+ ...thinking ? { thinking } : {},
82
+ status: payload.status === "completed" ? "completed" : "in_progress"
83
+ };
84
+ }
67
85
  var ACCEPTED_USER_MESSAGE_SOURCE = "replicas-chat-turn-accepted";
68
86
  var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
69
87
  var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
@@ -543,7 +561,7 @@ var WORKSPACE_SIZES = ["small", "large"];
543
561
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
544
562
 
545
563
  // ../shared/src/e2b.ts
546
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-19-v13";
564
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-19-v15";
547
565
 
548
566
  // ../shared/src/runtime-env.ts
549
567
  function shellQuotePosix(value) {
@@ -3154,6 +3172,654 @@ function isTerminalBackgroundTaskStatus(status) {
3154
3172
  // ../shared/src/display-message/constants.ts
3155
3173
  var USER_MESSAGE_MATCH_GRACE_PERIOD_MS = 3e4;
3156
3174
 
3175
+ // ../shared/src/json.ts
3176
+ function safeJsonParse(str, fallback) {
3177
+ try {
3178
+ return JSON.parse(str);
3179
+ } catch {
3180
+ return fallback;
3181
+ }
3182
+ }
3183
+
3184
+ // ../shared/src/display-message/parsers/codex-parser.ts
3185
+ function getStatusFromExitCode(exitCode) {
3186
+ return exitCode === 0 ? "completed" : "failed";
3187
+ }
3188
+ function getPayloadString(event, key) {
3189
+ const value = event.payload?.[key];
3190
+ return typeof value === "string" ? value : null;
3191
+ }
3192
+ function displayId(event, eventIndex, prefix) {
3193
+ const stableId = getPayloadString(event, USER_MESSAGE_ID_PAYLOAD_KEY) ?? getPayloadString(event, CODEX_ASP_ITEM_ID_PAYLOAD_KEY);
3194
+ return stableId ? `${prefix}-${stableId}` : `${prefix}-${event.timestamp}-${eventIndex}`;
3195
+ }
3196
+ function userMessageImages(value) {
3197
+ if (!Array.isArray(value)) return void 0;
3198
+ const images = value.filter((item) => {
3199
+ if (!isRecord(item)) return false;
3200
+ return item.type === "image" && typeof item.mediaType === "string" && typeof item.data === "string";
3201
+ });
3202
+ return images.length > 0 ? images : void 0;
3203
+ }
3204
+ function parseShellOutput(raw) {
3205
+ const exitCodeMatch = raw.match(/^Exit code: (\d+)/m) || raw.match(/Process exited with code (\d+)/);
3206
+ const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 0;
3207
+ const outputIndex = raw.indexOf("Output:\n");
3208
+ const output = outputIndex >= 0 ? raw.slice(outputIndex + "Output:\n".length) : raw;
3209
+ return { exitCode, output };
3210
+ }
3211
+ function parsePatch(input) {
3212
+ const operations = [];
3213
+ const lines = input.split("\n");
3214
+ let currentOp = null;
3215
+ let diffLines = [];
3216
+ for (let i = 0; i < lines.length; i++) {
3217
+ const line = lines[i];
3218
+ if (line.startsWith("*** Add File:")) {
3219
+ if (currentOp) {
3220
+ if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
3221
+ operations.push(currentOp);
3222
+ diffLines = [];
3223
+ }
3224
+ currentOp = {
3225
+ action: "add",
3226
+ path: line.replace("*** Add File:", "").trim()
3227
+ };
3228
+ } else if (line.startsWith("*** Update File:")) {
3229
+ if (currentOp) {
3230
+ if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
3231
+ operations.push(currentOp);
3232
+ diffLines = [];
3233
+ }
3234
+ currentOp = {
3235
+ action: "update",
3236
+ path: line.replace("*** Update File:", "").trim()
3237
+ };
3238
+ } else if (line.startsWith("*** Delete File:")) {
3239
+ if (currentOp) {
3240
+ if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
3241
+ operations.push(currentOp);
3242
+ diffLines = [];
3243
+ }
3244
+ currentOp = {
3245
+ action: "delete",
3246
+ path: line.replace("*** Delete File:", "").trim()
3247
+ };
3248
+ } else if (line.startsWith("*** Move to:")) {
3249
+ if (currentOp) {
3250
+ currentOp.moveTo = line.replace("*** Move to:", "").trim();
3251
+ }
3252
+ } else if (line.startsWith("*** Begin Patch") || line.startsWith("*** End Patch")) {
3253
+ continue;
3254
+ } else if (currentOp && (line.startsWith("+") || line.startsWith("-") || line.startsWith(" ") || line.startsWith("@@") || line.trim() === "")) {
3255
+ diffLines.push(line);
3256
+ }
3257
+ }
3258
+ if (currentOp) {
3259
+ if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
3260
+ operations.push(currentOp);
3261
+ }
3262
+ return operations;
3263
+ }
3264
+ function parseCodexEvents(events) {
3265
+ const messages = [];
3266
+ const pendingCommands = /* @__PURE__ */ new Map();
3267
+ const pendingToolCalls = /* @__PURE__ */ new Map();
3268
+ const pendingPatches = /* @__PURE__ */ new Map();
3269
+ events.forEach((event, eventIndex) => {
3270
+ if (event.type === CODEX_QUOTA_STATUS_EVENT_TYPE) {
3271
+ const state = event.payload?.state;
3272
+ if (state) {
3273
+ messages.push({
3274
+ id: `codex-quota-${event.timestamp}-${eventIndex}`,
3275
+ type: "quota_status",
3276
+ provider: "codex",
3277
+ state,
3278
+ balance: event.payload?.balance ?? null,
3279
+ rateLimitResetType: event.payload?.rateLimitResetType ?? null,
3280
+ planType: event.payload?.planType ?? null,
3281
+ timestamp: event.timestamp
3282
+ });
3283
+ }
3284
+ return;
3285
+ }
3286
+ if (event.type === "event_msg" && event.payload?.type === "user_message") {
3287
+ const message = getPayloadString(event, "message");
3288
+ messages.push({
3289
+ id: displayId(event, eventIndex, "user"),
3290
+ type: "user",
3291
+ content: message ?? "",
3292
+ images: userMessageImages(event.payload.images),
3293
+ timestamp: event.timestamp
3294
+ });
3295
+ }
3296
+ if (event.type === "event_msg" && event.payload?.type === "agent_reasoning") {
3297
+ messages.push({
3298
+ id: displayId(event, eventIndex, "reasoning"),
3299
+ type: "reasoning",
3300
+ content: event.payload.text || "",
3301
+ status: "completed",
3302
+ timestamp: event.timestamp
3303
+ });
3304
+ }
3305
+ if (event.type === "response_item") {
3306
+ const payloadType = event.payload?.type;
3307
+ if (payloadType === "message" && event.payload?.role === "assistant") {
3308
+ const content = event.payload.content || [];
3309
+ const textContent = content.filter((c) => c.type === "output_text").map((c) => c.text || "").join("\n");
3310
+ if (textContent) {
3311
+ messages.push({
3312
+ id: displayId(event, eventIndex, "agent"),
3313
+ type: "agent",
3314
+ content: textContent,
3315
+ timestamp: event.timestamp
3316
+ });
3317
+ }
3318
+ }
3319
+ if (payloadType === "function_call" && (event.payload?.name === "shell" || event.payload?.name === "shell_command" || event.payload?.name === "exec_command")) {
3320
+ const callId = event.payload.call_id;
3321
+ const args = safeJsonParse(event.payload.arguments || "{}", {});
3322
+ const command = args.cmd || (Array.isArray(args.command) ? args.command.join(" ") : args.command) || "";
3323
+ const msg = {
3324
+ id: `command-${callId || "no-call-id"}-${eventIndex}`,
3325
+ type: "command",
3326
+ command,
3327
+ output: "",
3328
+ status: "in_progress",
3329
+ timestamp: event.timestamp
3330
+ };
3331
+ messages.push(msg);
3332
+ if (callId) {
3333
+ pendingCommands.set(callId, msg);
3334
+ }
3335
+ }
3336
+ if (payloadType === "function_call_output") {
3337
+ const callId = event.payload.call_id;
3338
+ const rawOutput = event.payload.output || "";
3339
+ const commandMsg = callId ? pendingCommands.get(callId) : void 0;
3340
+ if (commandMsg) {
3341
+ const { exitCode, output } = parseShellOutput(rawOutput);
3342
+ commandMsg.output = output;
3343
+ commandMsg.exitCode = exitCode;
3344
+ commandMsg.status = getStatusFromExitCode(exitCode);
3345
+ pendingCommands.delete(callId);
3346
+ }
3347
+ }
3348
+ if (payloadType === "custom_tool_call") {
3349
+ const callId = event.payload.call_id;
3350
+ const name = event.payload.name;
3351
+ const input = event.payload.input || "";
3352
+ const server = typeof event.payload.server === "string" ? event.payload.server : "custom";
3353
+ const status = event.payload.status || "in_progress";
3354
+ if (name === "apply_patch") {
3355
+ const operations = parsePatch(input);
3356
+ pendingPatches.set(callId, { input, status, timestamp: event.timestamp, operations });
3357
+ } else {
3358
+ const msg = {
3359
+ id: `toolcall-${callId || getPayloadString(event, CODEX_ASP_ITEM_ID_PAYLOAD_KEY) || `${event.timestamp}-${eventIndex}`}`,
3360
+ type: "tool_call",
3361
+ server,
3362
+ tool: name,
3363
+ input,
3364
+ status,
3365
+ timestamp: event.timestamp
3366
+ };
3367
+ messages.push(msg);
3368
+ if (callId) {
3369
+ pendingToolCalls.set(callId, msg);
3370
+ }
3371
+ }
3372
+ }
3373
+ if (payloadType === "custom_tool_call_output") {
3374
+ const callId = event.payload.call_id;
3375
+ const output = safeJsonParse(
3376
+ event.payload.output || "{}",
3377
+ {}
3378
+ );
3379
+ const pendingPatch = pendingPatches.get(callId);
3380
+ if (pendingPatch) {
3381
+ messages.push({
3382
+ id: `patch-${pendingPatch.timestamp}-${eventIndex}`,
3383
+ type: "patch",
3384
+ operations: pendingPatch.operations,
3385
+ output: output.output || "",
3386
+ exitCode: output.metadata?.exit_code,
3387
+ status: getStatusFromExitCode(output.metadata?.exit_code),
3388
+ timestamp: pendingPatch.timestamp
3389
+ });
3390
+ pendingPatches.delete(callId);
3391
+ } else {
3392
+ const fallbackToolCallMsg = messages.findLast((m) => m.type === "tool_call");
3393
+ const toolCallMsg = pendingToolCalls.get(callId) ?? fallbackToolCallMsg;
3394
+ if (toolCallMsg) {
3395
+ toolCallMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
3396
+ toolCallMsg.output = output.output ?? (typeof event.payload.output === "string" ? event.payload.output : "");
3397
+ if (callId) {
3398
+ pendingToolCalls.delete(callId);
3399
+ }
3400
+ }
3401
+ }
3402
+ }
3403
+ if (payloadType === "function_call" && event.payload?.name === "update_plan") {
3404
+ const args = safeJsonParse(
3405
+ event.payload.arguments || "{}",
3406
+ {}
3407
+ );
3408
+ if (args.plan && Array.isArray(args.plan)) {
3409
+ const todoItems = args.plan.map((item) => ({
3410
+ text: item.step,
3411
+ completed: item.status === "completed"
3412
+ }));
3413
+ messages.push({
3414
+ id: `todo-${event.timestamp}-${eventIndex}`,
3415
+ type: "todo_list",
3416
+ items: todoItems,
3417
+ status: "completed",
3418
+ timestamp: event.timestamp
3419
+ });
3420
+ }
3421
+ }
3422
+ }
3423
+ });
3424
+ return messages;
3425
+ }
3426
+
3427
+ // ../shared/src/display-message/parsers/utils.ts
3428
+ function stringifyDisplayValue(value) {
3429
+ if (value === void 0 || value === null) return void 0;
3430
+ if (typeof value === "string") return value;
3431
+ try {
3432
+ return JSON.stringify(value, null, 2);
3433
+ } catch {
3434
+ return String(value);
3435
+ }
3436
+ }
3437
+
3438
+ // ../shared/src/display-message/parsers/cursor-parser.ts
3439
+ function getTextContent(value) {
3440
+ if (!Array.isArray(value)) return "";
3441
+ return value.map((block) => isRecord(block) && block.type === "text" && typeof block.text === "string" ? block.text : "").join("");
3442
+ }
3443
+ function cursorStatusToDisplayStatus(status) {
3444
+ return status === "completed" || status === "FINISHED" ? "completed" : status === "error" || status === "ERROR" || status === "CANCELLED" || status === "EXPIRED" ? "failed" : "in_progress";
3445
+ }
3446
+ function cursorRunId(event) {
3447
+ const runId = event.payload.run_id;
3448
+ return typeof runId === "string" ? runId : "unknown-run";
3449
+ }
3450
+ function getCursorAssistantText(event) {
3451
+ const message = isRecord(event.payload.message) ? event.payload.message : null;
3452
+ return getTextContent(message?.content);
3453
+ }
3454
+ function getCursorThinkingText(event) {
3455
+ const text = event.payload.text;
3456
+ if (typeof text === "string") return text;
3457
+ const message = isRecord(event.payload.message) ? event.payload.message : null;
3458
+ return typeof message?.text === "string" ? message.text : "";
3459
+ }
3460
+ function isTerminalStatus(status) {
3461
+ return status === "FINISHED" || status === "ERROR" || status === "CANCELLED" || status === "EXPIRED";
3462
+ }
3463
+ function finalizeOpenTools(messages, toolIndexes, status) {
3464
+ for (const index of toolIndexes.values()) {
3465
+ const message = messages[index];
3466
+ if (message?.type === "tool_call" && message.status === "in_progress") {
3467
+ messages[index] = {
3468
+ ...message,
3469
+ status
3470
+ };
3471
+ }
3472
+ }
3473
+ }
3474
+ function parseCursorEvents(events) {
3475
+ const messages = [];
3476
+ const toolIndexes = /* @__PURE__ */ new Map();
3477
+ const runAssistantSegments = /* @__PURE__ */ new Map();
3478
+ const runThinkingSegments = /* @__PURE__ */ new Map();
3479
+ let activeAssistant = null;
3480
+ let activeThinking = null;
3481
+ const finalizeActiveThinking = (status) => {
3482
+ if (!activeThinking) return;
3483
+ const message = messages[activeThinking.index];
3484
+ if (message?.type === "reasoning") {
3485
+ messages[activeThinking.index] = {
3486
+ ...message,
3487
+ status
3488
+ };
3489
+ }
3490
+ };
3491
+ for (const event of events) {
3492
+ if (event.type === "event_msg" && event.payload.type === "user_message") {
3493
+ activeAssistant = null;
3494
+ activeThinking = null;
3495
+ const message = event.payload.message;
3496
+ if (typeof message === "string" && message.trim()) {
3497
+ messages.push({
3498
+ id: `cursor-user-${event.timestamp}-${messages.length}`,
3499
+ type: "user",
3500
+ content: message,
3501
+ timestamp: event.timestamp
3502
+ });
3503
+ }
3504
+ continue;
3505
+ }
3506
+ if (event.type === "cursor-assistant") {
3507
+ const text = getCursorAssistantText(event);
3508
+ if (text) {
3509
+ const runId = cursorRunId(event);
3510
+ if (!activeAssistant || activeAssistant.runId !== runId) {
3511
+ const segment = runAssistantSegments.get(runId) ?? 0;
3512
+ runAssistantSegments.set(runId, segment + 1);
3513
+ activeAssistant = {
3514
+ runId,
3515
+ index: messages.push({
3516
+ id: `cursor-agent-${runId}-${segment}`,
3517
+ type: "agent",
3518
+ content: "",
3519
+ timestamp: event.timestamp
3520
+ }) - 1
3521
+ };
3522
+ }
3523
+ const message = messages[activeAssistant.index];
3524
+ if (message?.type === "agent") {
3525
+ messages[activeAssistant.index] = {
3526
+ ...message,
3527
+ content: `${message.content}${text}`
3528
+ };
3529
+ }
3530
+ }
3531
+ continue;
3532
+ }
3533
+ if (event.type === "cursor-thinking") {
3534
+ const text = getCursorThinkingText(event);
3535
+ if (text.trim()) {
3536
+ const runId = cursorRunId(event);
3537
+ if (!activeThinking || activeThinking.runId !== runId) {
3538
+ const segment = runThinkingSegments.get(runId) ?? 0;
3539
+ runThinkingSegments.set(runId, segment + 1);
3540
+ activeThinking = {
3541
+ runId,
3542
+ index: messages.push({
3543
+ id: `cursor-reasoning-${runId}-${segment}`,
3544
+ type: "reasoning",
3545
+ content: "",
3546
+ status: "in_progress",
3547
+ timestamp: event.timestamp
3548
+ }) - 1
3549
+ };
3550
+ }
3551
+ const message = messages[activeThinking.index];
3552
+ if (message?.type === "reasoning") {
3553
+ messages[activeThinking.index] = {
3554
+ ...message,
3555
+ content: `${message.content}${text}`,
3556
+ status: "in_progress"
3557
+ };
3558
+ }
3559
+ }
3560
+ continue;
3561
+ }
3562
+ if (event.type === "cursor-tool_call") {
3563
+ activeAssistant = null;
3564
+ const callId = typeof event.payload.call_id === "string" ? event.payload.call_id : `cursor-tool-${event.timestamp}-${messages.length}`;
3565
+ const tool2 = typeof event.payload.name === "string" ? event.payload.name : "tool";
3566
+ const status = cursorStatusToDisplayStatus(event.payload.status);
3567
+ const input = isRecord(event.payload.args) ? event.payload.args : typeof event.payload.args === "string" ? event.payload.args : void 0;
3568
+ const existing = toolIndexes.get(callId);
3569
+ const next = {
3570
+ id: callId,
3571
+ type: "tool_call",
3572
+ server: "cursor",
3573
+ tool: tool2,
3574
+ ...input !== void 0 ? { input } : {},
3575
+ output: stringifyDisplayValue(event.payload.result),
3576
+ status,
3577
+ timestamp: event.timestamp
3578
+ };
3579
+ if (existing === void 0) {
3580
+ toolIndexes.set(callId, messages.push(next) - 1);
3581
+ } else {
3582
+ messages[existing] = { ...messages[existing], ...next };
3583
+ }
3584
+ continue;
3585
+ }
3586
+ if (event.type === "cursor-task") {
3587
+ activeAssistant = null;
3588
+ const text = event.payload.text;
3589
+ if (typeof text === "string" && text.trim()) {
3590
+ messages.push({
3591
+ id: `cursor-task-${event.timestamp}-${messages.length}`,
3592
+ type: "agent",
3593
+ content: text,
3594
+ timestamp: event.timestamp
3595
+ });
3596
+ }
3597
+ continue;
3598
+ }
3599
+ if (event.type === "cursor-status") {
3600
+ const status = event.payload.status;
3601
+ if (isTerminalStatus(status)) {
3602
+ const displayStatus = cursorStatusToDisplayStatus(status);
3603
+ finalizeActiveThinking(displayStatus);
3604
+ finalizeOpenTools(messages, toolIndexes, displayStatus);
3605
+ activeThinking = null;
3606
+ activeAssistant = null;
3607
+ }
3608
+ continue;
3609
+ }
3610
+ if (event.type === "cursor-error") {
3611
+ finalizeActiveThinking("failed");
3612
+ finalizeOpenTools(messages, toolIndexes, "failed");
3613
+ activeAssistant = null;
3614
+ activeThinking = null;
3615
+ const message = typeof event.payload.message === "string" ? event.payload.message : "Cursor run failed";
3616
+ messages.push({
3617
+ id: `cursor-error-${event.timestamp}-${messages.length}`,
3618
+ type: "error",
3619
+ message,
3620
+ timestamp: event.timestamp
3621
+ });
3622
+ }
3623
+ }
3624
+ return messages;
3625
+ }
3626
+
3627
+ // ../shared/src/display-message/parsers/opencode-parser.ts
3628
+ function timestampFromMs(value, fallback) {
3629
+ return typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : fallback;
3630
+ }
3631
+ function partFromEvent(event) {
3632
+ return isRecord(event.payload.part) ? event.payload.part : null;
3633
+ }
3634
+ function toolStatus(status) {
3635
+ return status === "completed" ? "completed" : status === "error" ? "failed" : "in_progress";
3636
+ }
3637
+ function setById(messages, id, next) {
3638
+ const index = messages.findIndex((message) => message.id === id);
3639
+ if (index === -1) {
3640
+ messages.push(next);
3641
+ } else {
3642
+ messages[index] = next;
3643
+ }
3644
+ }
3645
+ function assistantError(info) {
3646
+ if (!isRecord(info) || !isRecord(info.error)) return null;
3647
+ const data = isRecord(info.error.data) ? info.error.data : null;
3648
+ if (typeof data?.message === "string") return data.message;
3649
+ if (typeof info.error.message === "string") return info.error.message;
3650
+ return stringifyDisplayValue(info.error) ?? "Opencode run failed";
3651
+ }
3652
+ function parseOpencodeEvents(events) {
3653
+ const messages = [];
3654
+ for (const event of events) {
3655
+ if (event.type === "event_msg" && event.payload.type === "user_message") {
3656
+ const message = event.payload.message;
3657
+ if (typeof message === "string" && message.trim()) {
3658
+ messages.push({
3659
+ id: `opencode-user-${event.timestamp}-${messages.length}`,
3660
+ type: "user",
3661
+ content: message,
3662
+ timestamp: event.timestamp
3663
+ });
3664
+ }
3665
+ continue;
3666
+ }
3667
+ if (event.type === "opencode-message.updated") {
3668
+ const error = assistantError(event.payload.info);
3669
+ if (error) {
3670
+ messages.push({
3671
+ id: `opencode-error-${event.timestamp}-${messages.length}`,
3672
+ type: "error",
3673
+ message: error,
3674
+ timestamp: event.timestamp
3675
+ });
3676
+ }
3677
+ continue;
3678
+ }
3679
+ if (event.type === "opencode-error") {
3680
+ const message = typeof event.payload.message === "string" ? event.payload.message : "Opencode run failed";
3681
+ messages.push({
3682
+ id: `opencode-error-${event.timestamp}-${messages.length}`,
3683
+ type: "error",
3684
+ message,
3685
+ timestamp: event.timestamp
3686
+ });
3687
+ continue;
3688
+ }
3689
+ const part = partFromEvent(event);
3690
+ if (!part) continue;
3691
+ const id = typeof part.id === "string" ? `opencode-${part.id}` : `opencode-${event.timestamp}-${messages.length}`;
3692
+ const time = isRecord(part.time) ? part.time : null;
3693
+ const timestamp = timestampFromMs(time?.start, event.timestamp);
3694
+ if (part.type === "text") {
3695
+ const text = typeof part.text === "string" ? part.text : "";
3696
+ if (text.trim()) {
3697
+ setById(messages, id, {
3698
+ id,
3699
+ type: "agent",
3700
+ content: text,
3701
+ timestamp
3702
+ });
3703
+ }
3704
+ continue;
3705
+ }
3706
+ if (part.type === "reasoning") {
3707
+ const text = typeof part.text === "string" ? part.text : "";
3708
+ if (text.trim()) {
3709
+ setById(messages, id, {
3710
+ id,
3711
+ type: "reasoning",
3712
+ content: text,
3713
+ status: time?.end ? "completed" : "in_progress",
3714
+ timestamp
3715
+ });
3716
+ }
3717
+ continue;
3718
+ }
3719
+ if (part.type === "tool" && isRecord(part.state)) {
3720
+ const state = part.state;
3721
+ const status = toolStatus(state.status);
3722
+ const output = state.status === "completed" ? stringifyDisplayValue(state.output) : state.status === "error" ? stringifyDisplayValue(state.error) : void 0;
3723
+ setById(messages, id, {
3724
+ id,
3725
+ type: "tool_call",
3726
+ server: "opencode",
3727
+ tool: typeof part.tool === "string" ? part.tool : "tool",
3728
+ input: isRecord(state.input) ? state.input : stringifyDisplayValue(state.input),
3729
+ output,
3730
+ status,
3731
+ timestamp
3732
+ });
3733
+ continue;
3734
+ }
3735
+ if (part.type === "patch") {
3736
+ const files = Array.isArray(part.files) ? part.files.filter((file) => typeof file === "string") : [];
3737
+ if (files.length > 0) {
3738
+ setById(messages, id, {
3739
+ id,
3740
+ type: "file_change",
3741
+ changes: files.map((path6) => ({ path: path6, kind: "update" })),
3742
+ status: "completed",
3743
+ timestamp
3744
+ });
3745
+ }
3746
+ }
3747
+ }
3748
+ return messages;
3749
+ }
3750
+
3751
+ // ../shared/src/display-message/parsers/pi-parser.ts
3752
+ function nestedPayload(event) {
3753
+ return isRecord(event.payload.assistantMessageEvent) ? event.payload.assistantMessageEvent : event.payload;
3754
+ }
3755
+ function setById2(messages, id, message) {
3756
+ const index = messages.findIndex((candidate) => candidate.id === id);
3757
+ if (index === -1) messages.push(message);
3758
+ else messages[index] = message;
3759
+ }
3760
+ function toolStatus2(value) {
3761
+ return value === "completed" || value === "success" ? "completed" : value === "error" ? "failed" : "in_progress";
3762
+ }
3763
+ function assistantErrorMessage(value) {
3764
+ if (!isRecord(value) || value.role !== "assistant" || value.stopReason !== "error") return null;
3765
+ return typeof value.errorMessage === "string" && value.errorMessage ? value.errorMessage : "Pi run failed";
3766
+ }
3767
+ function parsePiEvents(events) {
3768
+ const messages = [];
3769
+ const text = /* @__PURE__ */ new Map();
3770
+ const thinking = /* @__PURE__ */ new Map();
3771
+ let activeMessageId = "assistant";
3772
+ for (const event of events) {
3773
+ if (event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string") {
3774
+ messages.push({ id: `pi-user-${event.timestamp}-${messages.length}`, type: "user", content: event.payload.message, timestamp: event.timestamp });
3775
+ continue;
3776
+ }
3777
+ if (event.type === "pi-error") {
3778
+ messages.push({ id: `pi-error-${event.timestamp}-${messages.length}`, type: "error", message: String(event.payload.message ?? "Pi run failed"), timestamp: event.timestamp });
3779
+ continue;
3780
+ }
3781
+ if (event.type === "pi-message_end") {
3782
+ const error = assistantErrorMessage(event.payload.message);
3783
+ if (error) {
3784
+ messages.push({ id: `pi-error-${event.timestamp}-${messages.length}`, type: "error", message: error, timestamp: event.timestamp });
3785
+ }
3786
+ continue;
3787
+ }
3788
+ if (event.type === "pi-message_update") {
3789
+ const payload = nestedPayload(event);
3790
+ const id = typeof payload.id === "string" ? payload.id : activeMessageId;
3791
+ if (payload.type === "text_delta" && typeof payload.delta === "string") text.set(id, `${text.get(id) ?? ""}${payload.delta}`);
3792
+ if (payload.type === "thinking_delta" && typeof payload.delta === "string") thinking.set(id, `${thinking.get(id) ?? ""}${payload.delta}`);
3793
+ const textContent = text.get(id);
3794
+ const thinkingContent = thinking.get(id);
3795
+ if (textContent !== void 0) setById2(messages, `pi-${id}`, { id: `pi-${id}`, type: "agent", content: textContent, timestamp: event.timestamp });
3796
+ if (thinkingContent !== void 0) setById2(messages, `pi-thinking-${id}`, { id: `pi-thinking-${id}`, type: "reasoning", content: thinkingContent, status: "in_progress", timestamp: event.timestamp });
3797
+ continue;
3798
+ }
3799
+ if (event.type === "pi-message_start") {
3800
+ const message = isRecord(event.payload.message) ? event.payload.message : null;
3801
+ activeMessageId = typeof message?.id === "string" ? message.id : `assistant-${event.timestamp}`;
3802
+ continue;
3803
+ }
3804
+ if (event.type === "pi-tool_execution_start" || event.type === "pi-tool_execution_update" || event.type === "pi-tool_execution_end") {
3805
+ const payload = event.payload;
3806
+ const input = payload.args ?? payload.input;
3807
+ const id = typeof payload.toolCallId === "string" ? payload.toolCallId : typeof payload.id === "string" ? payload.id : `tool-${event.timestamp}`;
3808
+ setById2(messages, `pi-tool-${id}`, {
3809
+ id: `pi-tool-${id}`,
3810
+ type: "tool_call",
3811
+ server: "pi",
3812
+ tool: typeof payload.toolName === "string" ? payload.toolName : "tool",
3813
+ input: isRecord(input) ? input : stringifyDisplayValue(input),
3814
+ output: event.type === "pi-tool_execution_end" ? stringifyDisplayValue(payload.result ?? payload.error) : void 0,
3815
+ status: event.type === "pi-tool_execution_end" ? toolStatus2(payload.isError ? "error" : "completed") : "in_progress",
3816
+ timestamp: event.timestamp
3817
+ });
3818
+ }
3819
+ }
3820
+ return messages;
3821
+ }
3822
+
3157
3823
  // ../shared/src/display-message/task-accumulator.ts
3158
3824
  function mapTaskStatus(status) {
3159
3825
  if (status === "in_progress" || status === "completed") return status;
@@ -3281,6 +3947,461 @@ var TaskAccumulator = class {
3281
3947
  }
3282
3948
  };
3283
3949
 
3950
+ // ../shared/src/display-message/parsers/mcp.ts
3951
+ function parseMcpToolName(name) {
3952
+ const prefix = "mcp__";
3953
+ if (!name.startsWith(prefix)) return null;
3954
+ const rest = name.slice(prefix.length);
3955
+ const sep = rest.indexOf("__");
3956
+ if (sep <= 0) return null;
3957
+ const server = rest.slice(0, sep);
3958
+ const tool2 = rest.slice(sep + 2);
3959
+ if (!server || !tool2) return null;
3960
+ return { server, tool: tool2 };
3961
+ }
3962
+
3963
+ // ../shared/src/display-message/parsers/claude-parser.ts
3964
+ function coerceClaudeResultPayload(payload) {
3965
+ return {
3966
+ is_error: typeof payload.is_error === "boolean" ? payload.is_error : void 0,
3967
+ subtype: typeof payload.subtype === "string" ? payload.subtype : void 0,
3968
+ errors: Array.isArray(payload.errors) ? payload.errors.filter((e) => typeof e === "string") : void 0
3969
+ };
3970
+ }
3971
+ function isClaudeResultError(payload) {
3972
+ return Boolean(payload.is_error) || payload.subtype !== "success";
3973
+ }
3974
+ function upsertDisplayMessage(messages, message) {
3975
+ const index = messages.findIndex((existing) => existing.id === message.id);
3976
+ if (index === -1) {
3977
+ messages.push(message);
3978
+ } else {
3979
+ messages[index] = message;
3980
+ }
3981
+ }
3982
+ var LOCAL_COMMAND_ECHO_REGEX = /^<(?:command-name|command-message|local-command-stdout|local-command-stderr)>/;
3983
+ function parseClaudeEvents(events, parentToolUseId) {
3984
+ const messages = [];
3985
+ const filterValue = parentToolUseId !== void 0 ? parentToolUseId : null;
3986
+ const filteredEvents = events.filter((e) => {
3987
+ const eventParentToolUseId = e.payload.parent_tool_use_id ?? null;
3988
+ return eventParentToolUseId === filterValue;
3989
+ });
3990
+ const toolCallMap = /* @__PURE__ */ new Map();
3991
+ const taskMessageMap = /* @__PURE__ */ new Map();
3992
+ const partialIndexes = /* @__PURE__ */ new Map();
3993
+ const completedStreamIds = /* @__PURE__ */ new Set();
3994
+ const supersededStreamIds = /* @__PURE__ */ new Set();
3995
+ let liveStreamId = null;
3996
+ const assistantThinking = /* @__PURE__ */ new Map();
3997
+ const assistantTextCounts = /* @__PURE__ */ new Map();
3998
+ const taskAccumulator = new TaskAccumulator();
3999
+ const taskSnapshot = () => taskAccumulator.getTasks().map((task) => ({
4000
+ text: task.subject,
4001
+ completed: task.status === "completed",
4002
+ itemStatus: task.status
4003
+ }));
4004
+ filteredEvents.forEach((event) => {
4005
+ if (event.type === CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE) {
4006
+ const payload = coerceClaudePartialMessagePayload(event.payload);
4007
+ if (!payload) return;
4008
+ if (liveStreamId !== null && liveStreamId !== payload.streamId) {
4009
+ supersededStreamIds.add(liveStreamId);
4010
+ }
4011
+ liveStreamId = payload.streamId;
4012
+ const existing = partialIndexes.get(payload.streamId) ?? {};
4013
+ if (payload.thinking) {
4014
+ const reasoningMessage = {
4015
+ id: `reasoning-${payload.streamId}-thinking`,
4016
+ type: "reasoning",
4017
+ content: payload.thinking,
4018
+ status: payload.status,
4019
+ timestamp: event.timestamp
4020
+ };
4021
+ if (existing.reasoning !== void 0) messages[existing.reasoning] = reasoningMessage;
4022
+ else existing.reasoning = messages.push(reasoningMessage) - 1;
4023
+ }
4024
+ if (payload.text) {
4025
+ const agentMessage = {
4026
+ id: `agent-${payload.streamId}-0`,
4027
+ type: "agent",
4028
+ content: payload.text,
4029
+ timestamp: event.timestamp
4030
+ };
4031
+ if (existing.agent !== void 0) messages[existing.agent] = agentMessage;
4032
+ else existing.agent = messages.push(agentMessage) - 1;
4033
+ }
4034
+ partialIndexes.set(payload.streamId, existing);
4035
+ return;
4036
+ }
4037
+ if (event.type === "claude-user") {
4038
+ const content = normalizeContentBlocks(event.payload.message?.content);
4039
+ const toolResult = content.find((c) => c.type === "tool_result");
4040
+ if (toolResult && toolResult.tool_use_id) {
4041
+ if (!toolResult.is_error) {
4042
+ taskAccumulator.processContentBlock(toolResult);
4043
+ }
4044
+ return;
4045
+ }
4046
+ if (event.payload.isSynthetic) {
4047
+ return;
4048
+ }
4049
+ const textContent = content.filter((c) => c.type === "text").map((c) => c.text || "").join("\n");
4050
+ if (LOCAL_COMMAND_ECHO_REGEX.test(textContent.trim())) {
4051
+ return;
4052
+ }
4053
+ const images = content.filter((c) => c.type === "image" && c.source).map((c) => {
4054
+ const source = c.source;
4055
+ return {
4056
+ type: "image",
4057
+ mediaType: source.media_type || "image/png",
4058
+ data: source.data || ""
4059
+ };
4060
+ }).filter((img) => img.data);
4061
+ if (textContent || images.length > 0) {
4062
+ messages.push({
4063
+ id: `user-${event.timestamp}`,
4064
+ type: "user",
4065
+ content: textContent || (images.length > 0 ? `[${images.length} image${images.length > 1 ? "s" : ""} attached]` : ""),
4066
+ images: images.length > 0 ? images : void 0,
4067
+ timestamp: event.timestamp
4068
+ });
4069
+ }
4070
+ }
4071
+ if (event.type === "claude-assistant") {
4072
+ const messageId = event.payload.message?.id;
4073
+ const contentBlocks = normalizeContentBlocks(event.payload.message?.content);
4074
+ const messageKey = messageId || event.timestamp;
4075
+ const streamRefs = messageId ? partialIndexes.get(messageId) : void 0;
4076
+ if (messageId) {
4077
+ completedStreamIds.add(messageId);
4078
+ if (liveStreamId !== null && liveStreamId !== messageId) {
4079
+ supersededStreamIds.add(liveStreamId);
4080
+ }
4081
+ liveStreamId = null;
4082
+ }
4083
+ const thinkingBlocks = contentBlocks.flatMap(
4084
+ (block) => block.type === "thinking" && typeof block.thinking === "string" ? [block.thinking] : []
4085
+ );
4086
+ if (thinkingBlocks.length > 0) {
4087
+ const allThinking = [...assistantThinking.get(messageKey) ?? [], ...thinkingBlocks];
4088
+ assistantThinking.set(messageKey, allThinking);
4089
+ const reasoningMessage = {
4090
+ id: `reasoning-${messageKey}-thinking`,
4091
+ type: "reasoning",
4092
+ content: allThinking.join("\n\n"),
4093
+ status: "completed",
4094
+ timestamp: event.timestamp
4095
+ };
4096
+ if (streamRefs?.reasoning !== void 0) {
4097
+ messages[streamRefs.reasoning] = reasoningMessage;
4098
+ streamRefs.reasoning = void 0;
4099
+ } else {
4100
+ upsertDisplayMessage(messages, reasoningMessage);
4101
+ }
4102
+ }
4103
+ contentBlocks.forEach((block) => {
4104
+ if (block.type === "text" && block.text) {
4105
+ const textIndex = assistantTextCounts.get(messageKey) ?? 0;
4106
+ assistantTextCounts.set(messageKey, textIndex + 1);
4107
+ const agentMessage = {
4108
+ id: `agent-${messageKey}-${textIndex}`,
4109
+ type: "agent",
4110
+ content: block.text,
4111
+ timestamp: event.timestamp
4112
+ };
4113
+ if (streamRefs?.agent !== void 0) {
4114
+ messages[streamRefs.agent] = agentMessage;
4115
+ streamRefs.agent = void 0;
4116
+ } else {
4117
+ upsertDisplayMessage(messages, agentMessage);
4118
+ }
4119
+ }
4120
+ if (block.type === "tool_use" && block.id) {
4121
+ const toolName = block.name || "unknown";
4122
+ const toolInput = block.input || {};
4123
+ const toolUseId = block.id;
4124
+ toolCallMap.set(toolUseId, {
4125
+ messageIndex: messages.length,
4126
+ toolName,
4127
+ input: toolInput
4128
+ });
4129
+ if (toolName === "Task" || toolName === "Agent") {
4130
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4131
+ const nestedEvents = events.filter((e) => e.payload.parent_tool_use_id === toolUseId).map((e) => ({
4132
+ timestamp: e.timestamp,
4133
+ type: e.type,
4134
+ payload: e.payload
4135
+ }));
4136
+ messages.push({
4137
+ id: `subagent-${event.timestamp}-${messages.length}`,
4138
+ type: "subagent",
4139
+ toolUseId,
4140
+ description: inputObj.description || "Subagent Task",
4141
+ prompt: inputObj.prompt || "",
4142
+ subagentType: inputObj.subagent_type || "general",
4143
+ model: inputObj.model,
4144
+ status: "in_progress",
4145
+ nestedEvents,
4146
+ timestamp: event.timestamp
4147
+ });
4148
+ } else if (toolName === "Bash" || toolName === "bash" || toolName === "shell") {
4149
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4150
+ const command = inputObj.command || "";
4151
+ messages.push({
4152
+ id: `command-${event.timestamp}-${messages.length}`,
4153
+ type: "command",
4154
+ command,
4155
+ output: "",
4156
+ status: "in_progress",
4157
+ timestamp: event.timestamp
4158
+ });
4159
+ } else if (toolName === "Write" || toolName === "Edit") {
4160
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4161
+ const filePath = inputObj.file_path || "";
4162
+ const action = toolName === "Write" ? "add" : "update";
4163
+ let diff;
4164
+ if (toolName === "Edit" && inputObj.old_string != null && inputObj.new_string != null) {
4165
+ const oldLines = inputObj.old_string.replace(/\n$/, "").split("\n");
4166
+ const newLines = inputObj.new_string.replace(/\n$/, "").split("\n");
4167
+ const hunk = `@@ -1,${oldLines.length} +1,${newLines.length} @@`;
4168
+ diff = [
4169
+ `--- a/${filePath}`,
4170
+ `+++ b/${filePath}`,
4171
+ hunk,
4172
+ ...oldLines.map((l) => `-${l}`),
4173
+ ...newLines.map((l) => `+${l}`)
4174
+ ].join("\n");
4175
+ } else if (toolName === "Write" && inputObj.content != null) {
4176
+ const newLines = inputObj.content.replace(/\n$/, "").split("\n");
4177
+ const hunk = `@@ -0,0 +1,${newLines.length} @@`;
4178
+ diff = [
4179
+ `--- /dev/null`,
4180
+ `+++ b/${filePath}`,
4181
+ hunk,
4182
+ ...newLines.map((l) => `+${l}`)
4183
+ ].join("\n");
4184
+ }
4185
+ messages.push({
4186
+ id: `patch-${event.timestamp}-${messages.length}`,
4187
+ type: "patch",
4188
+ operations: [{
4189
+ action,
4190
+ path: filePath,
4191
+ diff
4192
+ }],
4193
+ status: "in_progress",
4194
+ timestamp: event.timestamp
4195
+ });
4196
+ } else if (toolName === "TaskCreate" || toolName === "TaskUpdate" || toolName === "TaskList") {
4197
+ taskAccumulator.processContentBlock(block);
4198
+ const snapshot = taskSnapshot();
4199
+ if (snapshot.length > 0) {
4200
+ messages.push({
4201
+ id: `todo-${event.timestamp}-${messages.length}`,
4202
+ type: "todo_list",
4203
+ items: snapshot,
4204
+ status: "completed",
4205
+ timestamp: event.timestamp
4206
+ });
4207
+ }
4208
+ } else if (toolName === "WebSearch") {
4209
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4210
+ const query2 = inputObj.query || "";
4211
+ messages.push({
4212
+ id: `search-${event.timestamp}-${messages.length}`,
4213
+ type: "web_search",
4214
+ query: query2,
4215
+ status: "in_progress",
4216
+ timestamp: event.timestamp
4217
+ });
4218
+ } else if (toolName === "Skill") {
4219
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4220
+ messages.push({
4221
+ id: `skill-${event.timestamp}-${messages.length}`,
4222
+ type: "skill",
4223
+ skillName: inputObj.skill || "unknown",
4224
+ args: inputObj.args,
4225
+ status: "in_progress",
4226
+ timestamp: event.timestamp
4227
+ });
4228
+ } else if (toolName === "mcp__relay-subagent-tools__spawn_agent") {
4229
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4230
+ const provider = typeof inputObj.provider === "string" ? inputObj.provider : "unknown";
4231
+ const prompt = typeof inputObj.prompt === "string" ? inputObj.prompt : "";
4232
+ const model = typeof inputObj.model === "string" ? inputObj.model : void 0;
4233
+ const title = typeof inputObj.title === "string" ? inputObj.title : void 0;
4234
+ messages.push({
4235
+ id: `subagent-${event.timestamp}-${messages.length}`,
4236
+ type: "subagent",
4237
+ toolUseId,
4238
+ description: title || `Relay subagent (${provider})`,
4239
+ prompt,
4240
+ subagentType: provider,
4241
+ model,
4242
+ thinkingLevel: isValidThinkingLevel(inputObj.thinking_level) ? inputObj.thinking_level : void 0,
4243
+ status: "in_progress",
4244
+ nestedEvents: [],
4245
+ isChatBased: true,
4246
+ timestamp: event.timestamp
4247
+ });
4248
+ } else if (toolName === "mcp__relay-subagent-tools__delete_agent") {
4249
+ const inputObj = typeof toolInput === "string" ? safeJsonParse(toolInput, {}) : toolInput;
4250
+ const mcp = parseMcpToolName(toolName);
4251
+ messages.push({
4252
+ id: `toolcall-${event.timestamp}-${messages.length}`,
4253
+ type: "tool_call",
4254
+ server: mcp?.server ?? "relay-subagent-tools",
4255
+ tool: mcp?.tool ?? "delete_agent",
4256
+ input: inputObj,
4257
+ status: "in_progress",
4258
+ timestamp: event.timestamp
4259
+ });
4260
+ } else {
4261
+ messages.push({
4262
+ id: `toolcall-${event.timestamp}-${messages.length}`,
4263
+ type: "tool_call",
4264
+ server: "claude",
4265
+ tool: toolName,
4266
+ input: toolInput,
4267
+ status: "in_progress",
4268
+ timestamp: event.timestamp
4269
+ });
4270
+ }
4271
+ }
4272
+ });
4273
+ }
4274
+ if (event.type === "claude-result") {
4275
+ const payload = coerceClaudeResultPayload(event.payload);
4276
+ if (isClaudeResultError(payload)) {
4277
+ const errorList = payload.errors || [];
4278
+ const errorMessage = errorList.length > 0 ? errorList.join("\n") : "Claude session encountered an unexpected error.";
4279
+ messages.push({
4280
+ id: `error-${event.timestamp}`,
4281
+ type: "error",
4282
+ message: errorMessage,
4283
+ timestamp: event.timestamp
4284
+ });
4285
+ }
4286
+ }
4287
+ if (event.type === "claude-system") {
4288
+ const payload = coerceBackgroundTaskPayload(event.payload);
4289
+ if (payload) {
4290
+ const existing = taskMessageMap.get(payload.taskId);
4291
+ const nextStatus = payload.subtype === "task_notification" ? normalizeBackgroundTaskStatus(payload.status) : payload.subtype === "task_updated" ? normalizeBackgroundTaskStatus(payload.patch?.status) : existing?.status ?? "in_progress";
4292
+ const nextMessage = {
4293
+ id: existing?.id ?? `task-${event.timestamp}-${messages.length}`,
4294
+ type: "background_task",
4295
+ taskId: payload.taskId,
4296
+ status: nextStatus,
4297
+ description: payload.description ?? payload.patch?.description ?? existing?.description,
4298
+ summary: payload.summary ?? existing?.summary,
4299
+ outputFile: payload.outputFile ?? existing?.outputFile,
4300
+ taskType: payload.taskType ?? existing?.taskType,
4301
+ workflowName: payload.workflowName ?? existing?.workflowName,
4302
+ prompt: payload.prompt ?? existing?.prompt,
4303
+ lastToolName: payload.lastToolName ?? existing?.lastToolName,
4304
+ usage: payload.usage ?? existing?.usage,
4305
+ error: payload.patch?.error ?? existing?.error,
4306
+ isBackgrounded: payload.patch?.isBackgrounded ?? existing?.isBackgrounded,
4307
+ timestamp: existing?.timestamp ?? event.timestamp
4308
+ };
4309
+ if (existing) {
4310
+ Object.assign(existing, nextMessage);
4311
+ } else {
4312
+ taskMessageMap.set(payload.taskId, nextMessage);
4313
+ messages.push(nextMessage);
4314
+ }
4315
+ }
4316
+ }
4317
+ });
4318
+ filteredEvents.forEach((event) => {
4319
+ if (event.type === "replicas-tool-input-request") {
4320
+ const payload = event.payload;
4321
+ if (!payload.toolUseId || !payload.requestId) return;
4322
+ if (!Array.isArray(payload.options) && !Array.isArray(payload.questions)) return;
4323
+ const toolInfo = toolCallMap.get(payload.toolUseId);
4324
+ if (!toolInfo) return;
4325
+ const message = messages[toolInfo.messageIndex];
4326
+ if (!message || message.type !== "tool_call") return;
4327
+ message.inputRequest = {
4328
+ requestId: payload.requestId,
4329
+ status: "pending",
4330
+ options: payload.options,
4331
+ questions: payload.questions
4332
+ };
4333
+ }
4334
+ if (event.type === "replicas-tool-input-resolved") {
4335
+ const payload = event.payload;
4336
+ if (!payload.toolUseId || !payload.selectionId) return;
4337
+ const toolInfo = toolCallMap.get(payload.toolUseId);
4338
+ if (!toolInfo) return;
4339
+ const message = messages[toolInfo.messageIndex];
4340
+ if (!message || message.type !== "tool_call" || !message.inputRequest) return;
4341
+ message.inputRequest = {
4342
+ ...message.inputRequest,
4343
+ status: payload.selectionId === "aborted" ? "aborted" : "resolved",
4344
+ selectionId: payload.selectionId,
4345
+ selectionSummary: payload.selectionSummary
4346
+ };
4347
+ }
4348
+ });
4349
+ filteredEvents.forEach((event) => {
4350
+ if (event.type === "claude-user") {
4351
+ const content = normalizeContentBlocks(event.payload.message?.content);
4352
+ const toolResult = content.find((c) => c.type === "tool_result");
4353
+ if (toolResult && toolResult.tool_use_id) {
4354
+ const toolInfo = toolCallMap.get(toolResult.tool_use_id);
4355
+ if (!toolInfo) return;
4356
+ const resultContent = extractToolResultText(toolResult.content);
4357
+ const isError = toolResult.is_error || false;
4358
+ const status = isError ? "failed" : "completed";
4359
+ if (toolInfo.toolName === "TaskCreate" && !isError) {
4360
+ taskAccumulator.processContentBlock(toolResult);
4361
+ }
4362
+ const message = messages[toolInfo.messageIndex];
4363
+ if (!message) return;
4364
+ if (message.type === "command") {
4365
+ message.output = resultContent;
4366
+ message.status = status;
4367
+ const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
4368
+ if (exitCodeMatch) {
4369
+ message.exitCode = parseInt(exitCodeMatch[1], 10);
4370
+ } else {
4371
+ message.exitCode = isError ? 1 : 0;
4372
+ }
4373
+ } else if (message.type === "patch") {
4374
+ message.status = status;
4375
+ } else if (message.type === "web_search") {
4376
+ message.status = status;
4377
+ } else if (message.type === "tool_call") {
4378
+ message.output = resultContent;
4379
+ message.status = status;
4380
+ } else if (message.type === "skill") {
4381
+ message.status = status;
4382
+ } else if (message.type === "subagent") {
4383
+ message.output = resultContent;
4384
+ message.status = status;
4385
+ if (!message.chatId && resultContent) {
4386
+ const parsed = safeJsonParse(resultContent, {});
4387
+ if (parsed.chatId) {
4388
+ message.chatId = parsed.chatId;
4389
+ }
4390
+ }
4391
+ }
4392
+ }
4393
+ }
4394
+ });
4395
+ const staleIndexes = /* @__PURE__ */ new Set();
4396
+ for (const streamId of [...completedStreamIds, ...supersededStreamIds]) {
4397
+ const refs = partialIndexes.get(streamId);
4398
+ if (refs?.reasoning !== void 0) staleIndexes.add(refs.reasoning);
4399
+ if (refs?.agent !== void 0) staleIndexes.add(refs.agent);
4400
+ }
4401
+ if (staleIndexes.size === 0) return messages;
4402
+ return messages.filter((_, index) => !staleIndexes.has(index));
4403
+ }
4404
+
3284
4405
  // ../shared/src/agent-event-utils.ts
3285
4406
  function getUserMessage(event) {
3286
4407
  return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
@@ -3357,6 +4478,21 @@ function parseAgentEventJsonlWithCodexAspTranscript(content, options = {}) {
3357
4478
  var DUPLICATE_WINDOW_MS = 5 * 60 * 1e3;
3358
4479
 
3359
4480
  // ../shared/src/display-message/parsers/index.ts
4481
+ function parseAgentEvents(events, agentType) {
4482
+ if (agentType === "codex") {
4483
+ return parseCodexEvents(events);
4484
+ }
4485
+ if (agentType === "cursor") {
4486
+ return parseCursorEvents(events);
4487
+ }
4488
+ if (agentType === "opencode") {
4489
+ return parseOpencodeEvents(events);
4490
+ }
4491
+ if (agentType === "pi") {
4492
+ return parsePiEvents(events);
4493
+ }
4494
+ return parseClaudeEvents(events);
4495
+ }
3360
4496
  function isAgentBackendEvent(value) {
3361
4497
  if (!value || typeof value !== "object") return false;
3362
4498
  const candidate = value;
@@ -8605,7 +9741,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
8605
9741
  var MIN_CODEX_CLI_VERSION = "0.144.0";
8606
9742
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
8607
9743
  var codexCliVersionEnsured = null;
8608
- var ENGINE_PACKAGE_VERSION = "0.1.461";
9744
+ var ENGINE_PACKAGE_VERSION = "0.1.463";
8609
9745
  var INITIALIZE_METHOD = "initialize";
8610
9746
  var INITIALIZED_NOTIFICATION = "initialized";
8611
9747
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -9816,16 +10952,25 @@ var CodexAspManager = class extends CodingAgentManager {
9816
10952
  try {
9817
10953
  await dispatch();
9818
10954
  } catch (error) {
9819
- if (isCodexAuthError(error)) {
9820
- const refreshed = await codexTokenManager.fetchFreshCredentials(error instanceof Error ? error.message : String(error));
10955
+ let terminalError = error;
10956
+ if (isCodexAuthError(terminalError)) {
10957
+ const refreshed = await codexTokenManager.fetchFreshCredentials(terminalError instanceof Error ? terminalError.message : String(terminalError));
9821
10958
  if (refreshed) {
9822
- await restartCodexAspHost();
9823
- this.threadAttached = false;
9824
- await dispatch();
9825
- return;
10959
+ try {
10960
+ await restartCodexAspHost();
10961
+ this.threadAttached = false;
10962
+ await dispatch();
10963
+ return;
10964
+ } catch (retryError) {
10965
+ terminalError = retryError;
10966
+ }
9826
10967
  }
9827
10968
  }
9828
- throw error;
10969
+ const event = this.recordHistoryEvent("codex-asp-error", {
10970
+ message: terminalError instanceof Error ? terminalError.message : String(terminalError)
10971
+ });
10972
+ this.onEvent(event);
10973
+ throw terminalError;
9829
10974
  } finally {
9830
10975
  this.transcriptUpdateCoalescer.flushPending();
9831
10976
  this.transcriptUpdateCoalescer.dispose();
@@ -12972,6 +14117,28 @@ function getCodexTranscriptFromEvent(event) {
12972
14117
  }
12973
14118
  return applyCodexAspTranscriptDelta(null, transcriptDelta);
12974
14119
  }
14120
+ function terminalErrorsFromEvent(event, provider, codexTranscript) {
14121
+ if (event.type === "claude-result") {
14122
+ const payload = coerceClaudeResultPayload(event.payload);
14123
+ if (!isClaudeResultError(payload)) return null;
14124
+ return payload.errors?.length ? payload.errors : ["Claude run failed"];
14125
+ }
14126
+ if (event.type === CODEX_QUOTA_STATUS_EVENT_TYPE) {
14127
+ return event.payload.state === "out_of_credits" ? ["Codex is out of credits. Top up the connected OpenAI account to resume."] : null;
14128
+ }
14129
+ if (event.type === "codex-asp-error") {
14130
+ return [typeof event.payload.message === "string" ? event.payload.message : "Codex run failed"];
14131
+ }
14132
+ const failedCodexTurn = codexTranscript?.turns.at(-1);
14133
+ if (failedCodexTurn?.status === "failed") {
14134
+ const errors2 = failedCodexTurn.items.flatMap((item) => item.type === "error" ? [item.message] : []);
14135
+ return errors2.length > 0 ? errors2 : ["Codex run failed"];
14136
+ }
14137
+ const mayContainProviderError = provider === "cursor" && event.type === "cursor-error" || provider === "opencode" && (event.type === "opencode-error" || event.type === "opencode-message.updated") || provider === "pi" && (event.type === "pi-error" || event.type === "pi-message_end");
14138
+ if (!mayContainProviderError) return void 0;
14139
+ const errors = parseAgentEvents([event], provider).flatMap((message) => message.type === "error" ? [message.message] : []);
14140
+ return errors.length > 0 ? errors : void 0;
14141
+ }
12975
14142
  function acceptedEventInCodexTranscript(acceptedEvent, transcript) {
12976
14143
  const message = getUserMessage(acceptedEvent);
12977
14144
  if (!message) return false;
@@ -13137,6 +14304,9 @@ var ChatService = class {
13137
14304
  }
13138
14305
  const acceptedEvent = createUserMessageEvent(request.message, result.messageId, request.images);
13139
14306
  chat.pendingMessageIds.push(result.messageId);
14307
+ if (request.errorNotificationTarget) {
14308
+ chat.errorNotificationTargets.set(result.messageId, request.errorNotificationTarget);
14309
+ }
13140
14310
  chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
13141
14311
  chat.persisted.lastMessageText = request.message.trim().slice(0, LAST_MESSAGE_PREVIEW_MAX) || null;
13142
14312
  this.touch(chat);
@@ -13502,7 +14672,10 @@ var ChatService = class {
13502
14672
  acceptedSendResponses: /* @__PURE__ */ new Map(),
13503
14673
  activeMessageId: null,
13504
14674
  hasActiveTurn: false,
13505
- observedBranchesByRepo: /* @__PURE__ */ new Map()
14675
+ observedBranchesByRepo: /* @__PURE__ */ new Map(),
14676
+ lastTurnErrors: null,
14677
+ errorNotificationTargets: /* @__PURE__ */ new Map(),
14678
+ activeErrorNotificationTarget: null
13506
14679
  };
13507
14680
  }
13508
14681
  touch(chat) {
@@ -13547,6 +14720,8 @@ var ChatService = class {
13547
14720
  }
13548
14721
  chat.hasActiveTurn = true;
13549
14722
  chat.activeMessageId = messageId;
14723
+ chat.activeErrorNotificationTarget = chat.errorNotificationTargets.get(messageId) ?? null;
14724
+ chat.errorNotificationTargets.delete(messageId);
13550
14725
  this.publish({
13551
14726
  type: "chat.turn.started",
13552
14727
  payload: {
@@ -13556,6 +14731,11 @@ var ChatService = class {
13556
14731
  }).catch(() => {
13557
14732
  });
13558
14733
  }
14734
+ const codexTranscript = getCodexTranscriptFromEvent(event);
14735
+ const terminalErrors = terminalErrorsFromEvent(event, chat.persisted.provider, codexTranscript);
14736
+ if (terminalErrors !== void 0) {
14737
+ chat.lastTurnErrors = terminalErrors;
14738
+ }
13559
14739
  let eventToPublish = event;
13560
14740
  if (event.type === "event_msg" && event.payload.type === "user_message") {
13561
14741
  if (chat.activeMessageId) {
@@ -13575,7 +14755,6 @@ var ChatService = class {
13575
14755
  }
13576
14756
  }
13577
14757
  }
13578
- const codexTranscript = getCodexTranscriptFromEvent(event);
13579
14758
  if (codexTranscript) {
13580
14759
  for (const [messageId, acceptedEvent] of chat.acceptedUserEvents) {
13581
14760
  if (acceptedEventInCodexTranscript(acceptedEvent, codexTranscript)) {
@@ -13727,6 +14906,10 @@ var ChatService = class {
13727
14906
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
13728
14907
  const observedBranches = chat.observedBranchesByRepo;
13729
14908
  chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
14909
+ const errors = chat.persisted.parentChatId === null ? chat.lastTurnErrors : null;
14910
+ chat.lastTurnErrors = null;
14911
+ const errorNotificationTarget = chat.activeErrorNotificationTarget;
14912
+ chat.activeErrorNotificationTarget = null;
13730
14913
  let repoStatuses = [];
13731
14914
  let repoStateComplete = false;
13732
14915
  try {
@@ -13743,7 +14926,12 @@ var ChatService = class {
13743
14926
  });
13744
14927
  }
13745
14928
  try {
13746
- const payload = linearSessionId ? { linearSessionId, repoStatuses } : { repoStatuses };
14929
+ const payload = {
14930
+ ...linearSessionId ? { linearSessionId } : {},
14931
+ repoStatuses,
14932
+ ...errors?.length ? { errors } : {},
14933
+ ...errors?.length && errorNotificationTarget ? { errorNotificationTarget } : {}
14934
+ };
13747
14935
  await monolithService.sendEvent({ type: "agent_turn_complete", payload });
13748
14936
  } catch (error) {
13749
14937
  console.error("[ChatService] Failed to send agent_turn_complete event:", error);
@@ -14580,7 +15768,19 @@ var sendMessageSchema = z4.object({
14580
15768
  senderUserId: z4.string().optional(),
14581
15769
  senderEmail: z4.string().optional(),
14582
15770
  senderDisplayName: z4.string().optional(),
14583
- senderAvatarUrl: z4.string().optional()
15771
+ senderAvatarUrl: z4.string().optional(),
15772
+ errorNotificationTarget: z4.discriminatedUnion("type", [
15773
+ z4.object({ type: z4.literal("slack") }),
15774
+ z4.object({ type: z4.literal("linear"), sessionId: z4.string().min(1) }),
15775
+ z4.object({
15776
+ type: z4.literal("code_host"),
15777
+ provider: z4.enum(["github", "gitlab"]),
15778
+ resource: z4.enum(["issue", "pull_request"]),
15779
+ repositoryId: z4.string().min(1),
15780
+ resourceNumber: z4.number().int().positive()
15781
+ }),
15782
+ z4.object({ type: z4.literal("automation"), executionId: z4.string().min(1) })
15783
+ ]).optional()
14584
15784
  });
14585
15785
  var respondToolInputSchema = z4.object({
14586
15786
  requestId: z4.string().min(1),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.461",
3
+ "version": "0.1.463",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",