@ganglion/xacpx 0.18.0 → 0.18.1

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/cli.js CHANGED
@@ -33177,2120 +33177,2141 @@ var init_prompt_media = __esm(() => {
33177
33177
  };
33178
33178
  });
33179
33179
 
33180
- // src/transport/tool-kind-emoji.ts
33181
- var TOOL_KIND_EMOJI, DEFAULT_TOOL_EMOJI;
33182
- var init_tool_kind_emoji = __esm(() => {
33183
- TOOL_KIND_EMOJI = {
33184
- read: "\uD83D\uDCD6",
33185
- search: "\uD83D\uDD0D",
33186
- execute: "\uD83D\uDCBB",
33187
- edit: "✏️",
33188
- think: "\uD83E\uDDE0",
33189
- other: "\uD83D\uDD27"
33190
- };
33191
- DEFAULT_TOOL_EMOJI = TOOL_KIND_EMOJI.other;
33192
- });
33193
-
33194
- // src/transport/streaming-prompt.ts
33195
- function createStreamingPromptState(formatToolCalls = false, options) {
33196
- let toolEventMode;
33197
- let onToolEvent;
33198
- let onThought;
33199
- let onPlan;
33200
- let onUsage;
33201
- let onCommands;
33202
- let rawStream = false;
33203
- let driver;
33204
- if (options === undefined) {
33205
- toolEventMode = "text";
33206
- onToolEvent = undefined;
33207
- } else if (typeof options === "function") {
33208
- onToolEvent = options;
33209
- toolEventMode = "structured";
33210
- } else {
33211
- onToolEvent = options.onToolEvent;
33212
- onThought = options.onThought;
33213
- onPlan = options.onPlan;
33214
- onUsage = options.onUsage;
33215
- onCommands = options.onCommands;
33216
- rawStream = options.rawStream ?? false;
33217
- driver = options.driver?.trim().toLowerCase() || undefined;
33218
- toolEventMode = resolveToolEventMode({
33219
- toolEventMode: options.mode,
33220
- onToolEvent
33221
- });
33222
- }
33223
- return {
33224
- buffer: "",
33225
- segments: [],
33226
- hasAgentMessage: false,
33227
- pendingLine: "",
33228
- formatToolCalls,
33229
- emittedToolCallIds: new Set,
33230
- toolCalls: new Map,
33231
- toolEventMode,
33232
- driver,
33233
- rawStream,
33234
- onToolEvent,
33235
- onThought,
33236
- onPlan,
33237
- onUsage,
33238
- onCommands,
33239
- finalize() {
33240
- if (this.pendingLine.trim().length > 0) {
33241
- parseStreamingChunks(this, this.pendingLine);
33242
- }
33243
- const remaining = this.rawStream ? this.buffer : this.buffer.trim();
33244
- this.buffer = "";
33245
- this.pendingLine = "";
33246
- return remaining;
33247
- }
33248
- };
33180
+ // src/transport/claude-background-followup.ts
33181
+ import { access as access4, open as open5, readFile as readFile14, readdir as readdir3, stat as stat3 } from "node:fs/promises";
33182
+ import { constants as fsConstants } from "node:fs";
33183
+ import { homedir as homedir10 } from "node:os";
33184
+ import { basename as basename3, dirname as dirname12, join as join20 } from "node:path";
33185
+ function isClaudeAsyncAgentLaunch(event) {
33186
+ return isAsyncAgentLaunchOutput(event.rawOutput);
33249
33187
  }
33250
- function parseStreamingDataChunk(state, chunk) {
33251
- state.pendingLine += chunk;
33252
- let boundary;
33253
- while ((boundary = state.pendingLine.indexOf(`
33254
- `)) !== -1) {
33255
- const line = state.pendingLine.slice(0, boundary);
33256
- state.pendingLine = state.pendingLine.slice(boundary + 1);
33257
- parseStreamingChunks(state, line);
33188
+ function isAsyncAgentLaunchOutput(rawOutput) {
33189
+ const candidate = typeof rawOutput === "string" ? tryParseJsonObject(rawOutput) ?? rawOutput : rawOutput;
33190
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
33191
+ const status = candidate.status;
33192
+ if (status !== undefined)
33193
+ return status === "async_launched";
33258
33194
  }
33195
+ return ASYNC_AGENT_LAUNCH.test(textOfUnknown(rawOutput));
33259
33196
  }
33260
- function parseStreamingChunks(state, line) {
33261
- const trimmed = line.trim();
33262
- if (trimmed.length === 0)
33197
+ function tryParseJsonObject(text) {
33198
+ if (!text.trimStart().startsWith("{"))
33263
33199
  return;
33264
- let event;
33265
33200
  try {
33266
- event = JSON.parse(trimmed);
33201
+ const parsed = JSON.parse(text);
33202
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
33267
33203
  } catch {
33268
33204
  return;
33269
33205
  }
33270
- if (event.method !== "session/update")
33271
- return;
33272
- const update = event.params?.update;
33273
- if (!update)
33274
- return;
33275
- if (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") {
33276
- const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
33277
- const wantsText = (state.toolEventMode === "text" || state.toolEventMode === "both") && state.formatToolCalls;
33278
- if (wantsStructured && state.onToolEvent) {
33279
- const merged = update.toolCallId ? mergeToolCallUpdate(state, update.toolCallId, update) : update;
33280
- const toolEvent = buildToolUseEvent(merged, state.driver);
33281
- if (toolEvent)
33282
- state.onToolEvent(toolEvent);
33283
- }
33284
- if (wantsText) {
33285
- const formatted = formatToolCallEvent(update, update.sessionUpdate);
33286
- if (formatted) {
33287
- const toolCallId = update.toolCallId;
33288
- if (toolCallId) {
33289
- if (state.emittedToolCallIds.has(toolCallId))
33290
- return;
33291
- state.emittedToolCallIds.add(toolCallId);
33292
- }
33293
- state.segments.push(formatted);
33294
- }
33206
+ }
33207
+ function claudeAsyncAgentId(event) {
33208
+ const fromObject = findStringField(event.rawOutput, new Set(["agentId", "agent_id"]));
33209
+ if (fromObject)
33210
+ return fromObject;
33211
+ return /\bagent(?:Id|_id)["']?\s*[:=]\s*["']?([A-Za-z0-9_-]+)/i.exec(textOfUnknown(event.rawOutput))?.[1];
33212
+ }
33213
+ async function findClaudeTranscriptPath(input) {
33214
+ const configDirName = input.driver === "qoder" ? ".qoder" : ".claude";
33215
+ const projectsDir = join20(input.homeDir ?? homedir10(), configDirName, "projects");
33216
+ const direct = join20(projectsDir, encodeClaudeProjectDirectory(input.cwd), `${input.sessionId}.jsonl`);
33217
+ if (await exists(direct))
33218
+ return direct;
33219
+ try {
33220
+ const entries = await readdir3(projectsDir, { withFileTypes: true });
33221
+ for (const entry of entries) {
33222
+ if (!entry.isDirectory())
33223
+ continue;
33224
+ const candidate = join20(projectsDir, entry.name, `${input.sessionId}.jsonl`);
33225
+ if (await exists(candidate))
33226
+ return candidate;
33295
33227
  }
33228
+ } catch {
33296
33229
  return;
33297
33230
  }
33298
- if (update.sessionUpdate === "plan") {
33299
- const entries = Array.isArray(update.entries) ? update.entries.filter((x) => !!x && typeof x === "object" && typeof x.content === "string" && typeof x.status === "string") : [];
33300
- if (entries.length > 0)
33301
- state.onPlan?.(entries);
33302
- return;
33303
- }
33304
- if (update.sessionUpdate === "usage_update") {
33305
- const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
33306
- const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
33307
- if (used !== undefined && size !== undefined && size > 0) {
33308
- const cost = normalizeUsageCost(update.cost);
33309
- const breakdown = normalizeUsageBreakdown(update._meta?.usage);
33310
- state.onUsage?.({ used, size, ...cost ? { cost } : {}, ...breakdown ? { breakdown } : {} });
33311
- }
33312
- return;
33231
+ return;
33232
+ }
33233
+ async function followClaudeBackgroundTurn(options) {
33234
+ const transcriptPath = options.transcriptPath ?? await findClaudeTranscriptPath(options);
33235
+ if (!transcriptPath) {
33236
+ return { status: "unavailable", completedToolCallIds: [], failedToolCallIds: [] };
33313
33237
  }
33314
- if (update.sessionUpdate === "available_commands_update") {
33315
- if (Array.isArray(update.availableCommands)) {
33316
- state.onCommands?.(normalizeAgentCommands(update.availableCommands));
33317
- }
33318
- return;
33238
+ let initial;
33239
+ try {
33240
+ initial = await readFile14(transcriptPath);
33241
+ } catch {
33242
+ return { status: "unavailable", transcriptPath, completedToolCallIds: [], failedToolCallIds: [] };
33319
33243
  }
33320
- const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
33321
- if (isThoughtChunk) {
33322
- const chunk2 = update.content.text;
33323
- if (chunk2.length > 0) {
33324
- state.onThought?.(chunk2);
33325
- }
33326
- return;
33244
+ const initialCompleteEnd = initial.lastIndexOf(10);
33245
+ const initialComplete = initial.subarray(0, initialCompleteEnd >= 0 ? initialCompleteEnd : 0).toString("utf8");
33246
+ const mainCursor = {
33247
+ offset: initial.length,
33248
+ partial: initialCompleteEnd >= 0 ? initial.subarray(initialCompleteEnd + 1).toString("utf8") : initial.toString("utf8")
33249
+ };
33250
+ const initialRecords = initialComplete.split(`
33251
+ `).map(parseRecord);
33252
+ const launched = new Set([...options.launchedToolCallIds].filter(Boolean));
33253
+ const completed = new Set;
33254
+ const failed = new Set;
33255
+ const toolEvents = new Map;
33256
+ for (const event of options.initialToolEvents ?? [])
33257
+ toolEvents.set(event.toolCallId, event);
33258
+ const parentToolCallIdByAgentId = new Map;
33259
+ for (const [toolCallId, agentId] of options.subagentIdsByToolCallId ?? []) {
33260
+ if (toolCallId && agentId)
33261
+ parentToolCallIdByAgentId.set(agentId, toolCallId);
33327
33262
  }
33328
- const isMessageChunk = update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
33329
- if (!isMessageChunk)
33330
- return;
33331
- state.hasAgentMessage = true;
33332
- const chunk = update.content.text ?? "";
33333
- if (chunk.length === 0)
33334
- return;
33335
- state.buffer += chunk;
33336
- if (state.rawStream)
33337
- return;
33338
- let boundary;
33339
- while ((boundary = state.buffer.indexOf(`
33340
-
33341
- `)) !== -1) {
33342
- const segment = state.buffer.slice(0, boundary).trim();
33343
- state.buffer = state.buffer.slice(boundary + 2);
33344
- if (segment.length > 0) {
33345
- state.segments.push(segment);
33263
+ const boundary = findPromptBoundary(initialRecords, launched);
33264
+ let launchSeen = boundary >= 0 || initialRecords.some((record3) => hasTrackedLaunch(record3, launched));
33265
+ let boundarySeen = boundary >= 0;
33266
+ let sequence = 0;
33267
+ let lastNotificationSequence = -1;
33268
+ let done = false;
33269
+ const emitTool = async (event) => {
33270
+ toolEvents.set(event.toolCallId, event);
33271
+ await options.onToolEvent?.(event);
33272
+ };
33273
+ const processRecord = async (record3) => {
33274
+ if (!record3)
33275
+ return;
33276
+ sequence += 1;
33277
+ const blocks = contentBlocks(record3.message?.content);
33278
+ if (record3.type === "user") {
33279
+ const userText = blocks.map(blockText).filter(Boolean).join(`
33280
+ `);
33281
+ for (const notification of taskNotifications(userText)) {
33282
+ if (launched.has(notification.toolCallId)) {
33283
+ completed.add(notification.toolCallId);
33284
+ if (notification.failed)
33285
+ failed.add(notification.toolCallId);
33286
+ lastNotificationSequence = sequence;
33287
+ const previous = toolEvents.get(notification.toolCallId);
33288
+ if (previous) {
33289
+ await emitTool({
33290
+ ...previous,
33291
+ status: notification.failed ? "error" : "success",
33292
+ ...notification.failed ? { rawOutput: { message: "background Agent failed" } } : {}
33293
+ });
33294
+ }
33295
+ }
33296
+ }
33297
+ for (const block of blocks) {
33298
+ if (readString(block, "type") !== "tool_result")
33299
+ continue;
33300
+ const toolCallId = readString(block, "tool_use_id");
33301
+ if (!toolCallId)
33302
+ continue;
33303
+ const output = blockText(block);
33304
+ if (ASYNC_AGENT_LAUNCH.test(output))
33305
+ launched.add(toolCallId);
33306
+ const previous = toolEvents.get(toolCallId);
33307
+ await emitTool({
33308
+ toolCallId,
33309
+ toolName: previous?.toolName ?? "Tool",
33310
+ kind: previous?.kind ?? "other",
33311
+ ...previous?.parentToolCallId ? { parentToolCallId: previous.parentToolCallId } : {},
33312
+ ...previous?.isSubagent ? { isSubagent: true } : {},
33313
+ ...previous?.summary ? { summary: previous.summary } : {},
33314
+ ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : {},
33315
+ ...output ? { rawOutput: output } : {},
33316
+ status: readBoolean(block, "is_error") ? "error" : "success"
33317
+ });
33318
+ }
33319
+ return;
33346
33320
  }
33347
- }
33348
- }
33349
- function formatToolCallEvent(update, sessionUpdate) {
33350
- if (!update)
33351
- return null;
33352
- const kind = update.kind ?? "";
33353
- const title = update.title ?? "";
33354
- if (title.length === 0)
33355
- return null;
33356
- const emoji2 = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
33357
- const inputSummary = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
33358
- const status = readString(update, "status");
33359
- if (!inputSummary && status === "pending")
33360
- return null;
33361
- if (!inputSummary && isGenericToolTitle(kind, title))
33362
- return null;
33363
- const summaryText = inputSummary && inputSummary !== title ? `: ${truncateToolDisplay(inputSummary)}` : "";
33364
- const statusText = status ? ` (${status})` : "";
33365
- return `${emoji2} ${title}${statusText}${summaryText}`;
33366
- }
33367
- function isEmptyToolField(v) {
33368
- if (v === undefined || v === null)
33369
- return true;
33370
- if (typeof v === "string")
33371
- return v.trim().length === 0;
33372
- if (Array.isArray(v))
33373
- return v.length === 0;
33374
- if (typeof v === "object")
33375
- return Object.keys(v).length === 0;
33376
- return false;
33377
- }
33378
- function mergeToolCallUpdate(state, toolCallId, update) {
33379
- const prev = state.toolCalls.get(toolCallId) ?? { toolCallId };
33380
- const merged = { ...prev };
33381
- for (const key of ["kind", "title", "rawInput", "content", "rawOutput", "locations", "status"]) {
33382
- const next = update[key];
33383
- if (!isEmptyToolField(next))
33384
- merged[key] = next;
33385
- }
33386
- const nextMeta = update._meta;
33387
- if (nextMeta) {
33388
- const previousMeta = merged._meta;
33389
- merged._meta = {
33390
- ...previousMeta,
33391
- ...nextMeta,
33392
- ...previousMeta?.qoder || nextMeta.qoder ? { qoder: { ...previousMeta?.qoder, ...nextMeta.qoder } } : {},
33393
- ...previousMeta?.codex || nextMeta.codex ? {
33394
- codex: {
33395
- ...previousMeta?.codex,
33396
- ...nextMeta.codex,
33397
- ...previousMeta?.codex?.subagent || nextMeta.codex?.subagent ? { subagent: { ...previousMeta?.codex?.subagent, ...nextMeta.codex?.subagent } } : {}
33398
- }
33399
- } : {},
33400
- ...previousMeta?.claudeCode || nextMeta.claudeCode ? { claudeCode: { ...previousMeta?.claudeCode, ...nextMeta.claudeCode } } : {}
33401
- };
33402
- }
33403
- merged.toolCallId = toolCallId;
33404
- state.toolCalls.set(toolCallId, merged);
33405
- return merged;
33406
- }
33407
- function buildToolUseEvent(update, driver) {
33408
- if (!update)
33409
- return null;
33410
- const toolCallId = update.toolCallId;
33411
- if (!toolCallId)
33412
- return null;
33413
- const kindRaw = update.kind ?? "";
33414
- const kind = (() => {
33415
- switch (kindRaw) {
33416
- case "read":
33417
- case "search":
33418
- case "execute":
33419
- case "edit":
33420
- case "think":
33421
- return kindRaw;
33422
- default:
33423
- return "other";
33424
- }
33425
- })();
33426
- const title = (update.title ?? "").trim();
33427
- const toolName = title || "Tool";
33428
- const summaryRaw = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
33429
- const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
33430
- const statusRaw = readString(update, "status");
33431
- const isClaudeDriver = driver === undefined || driver === "claude";
33432
- const claudeMeta = isClaudeDriver ? update._meta?.claudeCode : undefined;
33433
- const claudeToolResponse = claudeMeta?.toolResponse;
33434
- const hasClaudeToolResponse = !isEmptyToolField(claudeToolResponse);
33435
- const isAsyncAgentLaunch = update._meta?.claudeCode?.toolName === "Agent" && isRecord4(claudeToolResponse) && claudeToolResponse.status === "async_launched";
33436
- const status = isAsyncAgentLaunch ? "running" : statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : hasClaudeToolResponse ? "success" : "running";
33437
- const rawInput = update.rawInput;
33438
- const content = update.content;
33439
- const rawOutput = update.rawOutput ?? claudeToolResponse;
33440
- const locations = update.locations;
33441
- const parentToolCallId = claudeMeta?.parentToolUseId?.trim();
33442
- const isSubagent = claudeMeta?.toolName === "Agent" || driver === "qoder" && update._meta?.qoder?.toolName === "Agent" || driver === "kimi" && isKimiSubagentInput(rawInput) || driver === "codex" && isCodexSubagentMeta(update._meta?.codex?.subagent);
33443
- return {
33444
- toolCallId,
33445
- ...parentToolCallId ? { parentToolCallId } : {},
33446
- ...isSubagent ? { isSubagent: true } : {},
33447
- toolName,
33448
- kind,
33449
- ...summary ? { summary } : {},
33450
- ...rawInput !== undefined ? { rawInput } : {},
33451
- ...content !== undefined ? { content } : {},
33452
- ...rawOutput !== undefined ? { rawOutput } : {},
33453
- ...locations !== undefined ? { locations } : {},
33454
- status
33455
- };
33456
- }
33457
- function isKimiSubagentInput(rawInput) {
33458
- return isRecord4(rawInput) && readFirstString(rawInput, ["prompt"]) !== undefined && readFirstString(rawInput, ["subagent_type", "subagentType"]) !== undefined;
33459
- }
33460
- function isCodexSubagentMeta(meta2) {
33461
- return typeof meta2?.threadId === "string" && meta2.threadId.trim().length > 0 && typeof meta2.activity === "string" && meta2.activity.trim().length > 0;
33462
- }
33463
- function summarizeToolInput(rawInput, title = "") {
33464
- if (rawInput == null)
33465
- return;
33466
- if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
33467
- return String(rawInput);
33468
- }
33469
- if (!isRecord4(rawInput))
33470
- return;
33471
- const taskSummary = summarizeTaskInput(rawInput, title);
33472
- if (taskSummary)
33473
- return taskSummary;
33474
- const command = readFirstString(rawInput, ["command", "cmd", "program"]);
33475
- const args = readFirstStringArray(rawInput, ["args", "arguments"]);
33476
- if (command) {
33477
- return [command, ...args ?? []].join(" ");
33478
- }
33479
- const parsedCmd = rawInput.parsed_cmd;
33480
- if (Array.isArray(parsedCmd) && parsedCmd.length > 0) {
33481
- const parts = [];
33482
- for (const entry of parsedCmd) {
33483
- if (isRecord4(entry) && typeof entry.cmd === "string" && entry.cmd.length > 0) {
33484
- parts.push(entry.cmd);
33321
+ if (record3.type !== "assistant" || record3.isSidechain === true)
33322
+ return;
33323
+ for (const block of blocks) {
33324
+ const type = readString(block, "type");
33325
+ if (type === "text") {
33326
+ const text = readString(block, "text");
33327
+ if (text)
33328
+ await options.onText?.(text);
33329
+ } else if (type === "thinking") {
33330
+ const thought = readString(block, "thinking");
33331
+ if (thought)
33332
+ await options.onThought?.(thought);
33333
+ } else if (type === "tool_use") {
33334
+ const toolCallId = readString(block, "id");
33335
+ if (!toolCallId)
33336
+ continue;
33337
+ const name = readString(block, "name") ?? "Tool";
33338
+ const rawInput = block.input;
33339
+ const event = {
33340
+ toolCallId,
33341
+ toolName: name,
33342
+ kind: classifyToolKind(name),
33343
+ ...toolSummary(rawInput) ? { summary: toolSummary(rawInput) } : {},
33344
+ ...rawInput !== undefined ? { rawInput } : {},
33345
+ status: "running"
33346
+ };
33347
+ await emitTool(event);
33485
33348
  }
33486
33349
  }
33487
- if (parts.length > 0) {
33488
- return parts.join(" ");
33489
- }
33490
- }
33491
- return readFirstString(rawInput, [
33492
- "path",
33493
- "file",
33494
- "filePath",
33495
- "filepath",
33496
- "file_path",
33497
- "target",
33498
- "uri",
33499
- "url",
33500
- "query",
33501
- "pattern",
33502
- "text",
33503
- "search",
33504
- "name",
33505
- "description"
33506
- ]);
33507
- }
33508
- function summarizeTaskInput(rawInput, title) {
33509
- const subagentType = readFirstString(rawInput, ["subagent_type", "subagentType", "agent", "agentType"]);
33510
- const description = readFirstString(rawInput, ["description", "task", "summary"]);
33511
- if (subagentType && description) {
33512
- return description === title ? subagentType : `${subagentType}: ${description}`;
33513
- }
33514
- if (subagentType)
33515
- return subagentType;
33516
- return;
33517
- }
33518
- function readFirstString(record3, keys) {
33519
- for (const key of keys) {
33520
- const value = record3[key];
33521
- if (typeof value === "string" && value.trim().length > 0) {
33522
- return value.trim();
33523
- }
33524
- }
33525
- return;
33526
- }
33527
- function readFirstStringArray(record3, keys) {
33528
- for (const key of keys) {
33529
- const value = record3[key];
33530
- if (!Array.isArray(value))
33531
- continue;
33532
- const entries = value.map((entry) => typeof entry === "string" && entry.trim().length > 0 ? entry.trim() : undefined).filter((entry) => entry !== undefined);
33533
- if (entries.length > 0) {
33534
- return entries;
33350
+ if (record3.message?.stop_reason === "end_turn" && launched.size > 0 && [...launched].every((id) => completed.has(id)) && lastNotificationSequence >= 0 && sequence > lastNotificationSequence) {
33351
+ done = true;
33535
33352
  }
33536
- }
33537
- return;
33538
- }
33539
- function asFiniteNumber(value) {
33540
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
33541
- }
33542
- function firstFiniteNumber(record3, keys) {
33543
- for (const key of keys) {
33544
- const n = asFiniteNumber(record3[key]);
33545
- if (n !== undefined)
33546
- return n;
33547
- }
33548
- return;
33549
- }
33550
- function normalizeUsageBreakdown(value) {
33551
- if (!isRecord4(value))
33552
- return;
33553
- const out = {};
33554
- for (const [key, aliases] of USAGE_BREAKDOWN_FIELDS) {
33555
- const n = firstFiniteNumber(value, aliases);
33556
- if (n !== undefined)
33557
- out[key] = n;
33558
- }
33559
- return Object.keys(out).length > 0 ? out : undefined;
33560
- }
33561
- function normalizeUsageCost(value) {
33562
- if (!isRecord4(value))
33563
- return;
33564
- const amount = asFiniteNumber(value.amount);
33565
- const currency = readString(value, "currency");
33566
- if (amount === undefined && !currency)
33567
- return;
33568
- return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
33569
- }
33570
- function normalizeAgentCommands(value) {
33571
- if (!Array.isArray(value))
33572
- return [];
33573
- const out = [];
33574
- for (const entry of value) {
33575
- if (!isRecord4(entry))
33576
- continue;
33577
- const name = readString(entry, "name");
33578
- if (!name)
33579
- continue;
33580
- const description = readString(entry, "description");
33581
- out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
33582
- }
33583
- return out;
33584
- }
33585
- function isRecord4(value) {
33586
- return typeof value === "object" && value !== null && !Array.isArray(value);
33587
- }
33588
- function readString(rawInput, key) {
33589
- if (!isRecord4(rawInput))
33590
- return;
33591
- const value = rawInput[key];
33592
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
33593
- }
33594
- function truncateToolDisplay(text) {
33595
- return text.length > 60 ? `${text.slice(0, 57)}...` : text;
33596
- }
33597
- function isGenericToolTitle(kind, title) {
33598
- const normalizedTitle = title.trim().toLowerCase();
33599
- if (kind === "execute" && ["bash", "shell", "sh", "powershell", "cmd"].includes(normalizedTitle)) {
33600
- return true;
33601
- }
33602
- if (kind === "search" && ["grep", "rg", "search"].includes(normalizedTitle)) {
33603
- return true;
33604
- }
33605
- if (kind === "read" && ["read", "cat"].includes(normalizedTitle)) {
33606
- return true;
33607
- }
33608
- return false;
33609
- }
33610
- var USAGE_BREAKDOWN_FIELDS;
33611
- var init_streaming_prompt = __esm(() => {
33612
- init_tool_kind_emoji();
33613
- USAGE_BREAKDOWN_FIELDS = [
33614
- ["inputTokens", ["inputTokens", "input_tokens"]],
33615
- ["outputTokens", ["outputTokens", "output_tokens"]],
33616
- ["cachedReadTokens", ["cachedReadTokens", "cacheReadInputTokens", "cache_read_input_tokens"]],
33617
- ["cachedWriteTokens", ["cachedWriteTokens", "cacheCreationInputTokens", "cache_creation_input_tokens"]],
33618
- ["thoughtTokens", ["thoughtTokens", "thought_tokens"]],
33619
- ["totalTokens", ["totalTokens", "total_tokens"]]
33620
- ];
33621
- });
33622
-
33623
- // src/transport/acpx-cli/node-pty-helper.ts
33624
- import { chmod as chmodFs } from "node:fs/promises";
33625
- import { dirname as dirname12, join as join20 } from "node:path";
33626
- function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
33627
- if (platform === "win32") {
33628
- return null;
33629
- }
33630
- return join20(dirname12(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
33631
- }
33632
- async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
33633
- if (!helperPath) {
33634
- return;
33635
- }
33636
- try {
33637
- await chmod5(helperPath, 493);
33638
- } catch (error2) {
33639
- if (error2.code === "ENOENT") {
33353
+ };
33354
+ const processAfterBoundary = async (record3) => {
33355
+ if (!record3)
33356
+ return;
33357
+ if (!boundarySeen) {
33358
+ if (hasTrackedLaunch(record3, launched))
33359
+ launchSeen = true;
33360
+ if (launchSeen && isMainEndTurn(record3))
33361
+ boundarySeen = true;
33640
33362
  return;
33641
33363
  }
33642
- throw error2;
33643
- }
33644
- }
33645
- var init_node_pty_helper = () => {};
33646
-
33647
- // src/transport/codex-subagent-filter.ts
33648
- import { closeSync, openSync, readdirSync, readSync, statSync as statSync2 } from "node:fs";
33649
- import { homedir as homedir10 } from "node:os";
33650
- import { join as join21 } from "node:path";
33651
- function resolveCodexHome(env = process.env) {
33652
- const fromEnv = env.CODEX_HOME?.trim();
33653
- return fromEnv ? fromEnv : join21(homedir10(), ".codex");
33654
- }
33655
- function isPlainObject3(value) {
33656
- return !!value && typeof value === "object" && !Array.isArray(value);
33657
- }
33658
- function isSubagentSource(source) {
33659
- if (!isPlainObject3(source) || !Object.hasOwn(source, "subagent"))
33660
- return false;
33661
- const subagent = source.subagent;
33662
- if (!isPlainObject3(subagent))
33663
- return false;
33664
- return Object.values(subagent).some((variant) => isPlainObject3(variant) && typeof variant.parent_thread_id === "string");
33665
- }
33666
- function sessionMetaLineIsSubagent(line) {
33667
- if (!line)
33668
- return false;
33669
- let parsed;
33670
- try {
33671
- parsed = JSON.parse(line);
33672
- } catch {
33673
- return false;
33674
- }
33675
- const payload = parsed?.payload;
33676
- return isSubagentSource(payload?.source);
33677
- }
33678
- function createSubagentPredicate(reader) {
33679
- let index = null;
33680
- const cache = new Map;
33681
- return (sessionId) => {
33682
- const cached2 = cache.get(sessionId);
33683
- if (cached2 !== undefined)
33684
- return cached2;
33685
- let result = false;
33686
- try {
33687
- if (!index) {
33688
- index = new Map;
33689
- for (const path15 of reader.listRolloutPaths()) {
33690
- const id = path15.match(ROLLOUT_RE)?.[1];
33691
- if (id)
33692
- index.set(id.toLowerCase(), path15);
33693
- }
33694
- }
33695
- const path14 = index.get(sessionId.toLowerCase());
33696
- if (path14)
33697
- result = sessionMetaLineIsSubagent(reader.readFirstLine(path14));
33698
- } catch {
33699
- result = false;
33700
- }
33701
- cache.set(sessionId, result);
33702
- return result;
33364
+ await processRecord(record3);
33703
33365
  };
33704
- }
33705
- function filterSubagentSessions(result, isSubagent) {
33706
- return {
33707
- ...result,
33708
- sessions: result.sessions.filter((session3) => {
33709
- try {
33710
- return !isSubagent(session3.sessionId);
33711
- } catch {
33712
- return true;
33366
+ const processSubagentRecord = async (record3, parentToolCallId) => {
33367
+ if (!record3)
33368
+ return;
33369
+ const blocks = contentBlocks(record3.message?.content);
33370
+ if (record3.type === "assistant") {
33371
+ for (const block of blocks) {
33372
+ if (readString(block, "type") !== "tool_use")
33373
+ continue;
33374
+ const toolCallId = readString(block, "id");
33375
+ if (!toolCallId)
33376
+ continue;
33377
+ const name = readString(block, "name") ?? "Tool";
33378
+ const rawInput = block.input;
33379
+ const previous = toolEvents.get(toolCallId);
33380
+ await emitTool({
33381
+ ...previous,
33382
+ toolCallId,
33383
+ parentToolCallId: previous?.parentToolCallId ?? parentToolCallId,
33384
+ ...previous?.isSubagent || isAgentToolName(name) ? { isSubagent: true } : {},
33385
+ toolName: previous?.toolName ?? name,
33386
+ kind: previous?.kind ?? classifyToolKind(name),
33387
+ ...previous?.summary ? { summary: previous.summary } : toolSummary(rawInput) ? { summary: toolSummary(rawInput) } : {},
33388
+ ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : rawInput !== undefined ? { rawInput } : {},
33389
+ status: previous?.status === "success" || previous?.status === "error" ? previous.status : "running"
33390
+ });
33713
33391
  }
33714
- })
33715
- };
33716
- }
33717
- function nodeRolloutReader(home) {
33718
- const root = join21(home, "sessions");
33719
- return {
33720
- listRolloutPaths() {
33721
- const out = [];
33722
- const walk = (dir) => {
33723
- let entries;
33724
- try {
33725
- entries = readdirSync(dir, { withFileTypes: true });
33726
- } catch {
33727
- return;
33728
- }
33729
- for (const entry of entries) {
33730
- const full = join21(dir, entry.name);
33731
- if (entry.isDirectory())
33732
- walk(full);
33733
- else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl"))
33734
- out.push(full);
33735
- }
33736
- };
33737
- walk(root);
33738
- return out;
33739
- },
33740
- readFirstLine(path14) {
33741
- let fd;
33742
- try {
33743
- statSync2(path14);
33744
- fd = openSync(path14, "r");
33745
- const buf = Buffer.alloc(READ_CHUNK);
33746
- let acc = "";
33747
- let total = 0;
33748
- for (;; ) {
33749
- const bytes = readSync(fd, buf, 0, READ_CHUNK, total);
33750
- if (bytes <= 0)
33751
- break;
33752
- acc += buf.toString("utf8", 0, bytes);
33753
- total += bytes;
33754
- const nl = acc.indexOf(`
33755
- `);
33756
- if (nl !== -1)
33757
- return acc.slice(0, nl);
33758
- if (total >= FIRST_LINE_READ_CAP)
33759
- return;
33760
- }
33761
- return acc.length ? acc : undefined;
33762
- } catch {
33763
- return;
33764
- } finally {
33765
- if (fd !== undefined) {
33766
- try {
33767
- closeSync(fd);
33768
- } catch {}
33769
- }
33392
+ return;
33393
+ }
33394
+ if (record3.type !== "user")
33395
+ return;
33396
+ for (const block of blocks) {
33397
+ if (readString(block, "type") !== "tool_result")
33398
+ continue;
33399
+ const toolCallId = readString(block, "tool_use_id");
33400
+ if (!toolCallId)
33401
+ continue;
33402
+ const previous = toolEvents.get(toolCallId);
33403
+ const output = blockText(block);
33404
+ const event = {
33405
+ ...previous,
33406
+ toolCallId,
33407
+ parentToolCallId: previous?.parentToolCallId ?? parentToolCallId,
33408
+ ...previous?.isSubagent ? { isSubagent: true } : {},
33409
+ toolName: previous?.toolName ?? "Tool",
33410
+ kind: previous?.kind ?? "other",
33411
+ ...previous?.summary ? { summary: previous.summary } : {},
33412
+ ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : {},
33413
+ ...previous?.rawOutput !== undefined ? { rawOutput: previous.rawOutput } : output ? { rawOutput: output } : {},
33414
+ status: readBoolean(block, "is_error") || previous?.status === "error" ? "error" : "success"
33415
+ };
33416
+ if (event.isSubagent && isClaudeAsyncAgentLaunch(event)) {
33417
+ const agentId = claudeAsyncAgentId(event);
33418
+ if (agentId)
33419
+ parentToolCallIdByAgentId.set(agentId, event.toolCallId);
33770
33420
  }
33421
+ await emitTool(event);
33771
33422
  }
33772
33423
  };
33773
- }
33774
- function codexSubagentPredicate(env) {
33775
- return createSubagentPredicate(nodeRolloutReader(resolveCodexHome(env)));
33776
- }
33777
- var CODEX_AGENT_NAME = "codex", FIRST_LINE_READ_CAP, READ_CHUNK, ROLLOUT_RE;
33778
- var init_codex_subagent_filter = __esm(() => {
33779
- FIRST_LINE_READ_CAP = 1024 * 1024;
33780
- READ_CHUNK = 64 * 1024;
33781
- ROLLOUT_RE = /rollout-.*-([0-9a-fA-F-]{36})\.jsonl$/;
33782
- });
33783
-
33784
- // src/transport/agent-session-list.ts
33785
- function isUnknownFilterCwdOption(output) {
33786
- return /(?:unknown|unrecognized) option/i.test(output) && output.includes("--filter-cwd");
33787
- }
33788
- async function runAgentSessionList(options) {
33789
- let result = await options.runList(true);
33790
- let filterLocally = false;
33791
- if (result.code !== 0 && options.filterCwd && isUnknownFilterCwdOption(result.stdout + result.stderr)) {
33792
- result = await options.runList(false);
33793
- filterLocally = true;
33794
- }
33795
- if (result.code !== 0) {
33796
- if ((result.stdout + result.stderr).includes("sessionCapabilities.list")) {
33797
- return;
33424
+ const subagentCursors = new Map;
33425
+ const processSubagentTranscripts = async () => {
33426
+ const transcripts = await findSubagentTranscripts(transcriptPath);
33427
+ let advanced = false;
33428
+ let discoveredNestedAgent = true;
33429
+ while (discoveredNestedAgent) {
33430
+ const mappedAgentCount = parentToolCallIdByAgentId.size;
33431
+ for (const subagent of transcripts) {
33432
+ const parentToolCallId = parentToolCallIdByAgentId.get(subagent.agentId);
33433
+ if (!parentToolCallId)
33434
+ continue;
33435
+ const cursor = subagentCursors.get(subagent.path) ?? { offset: 0, partial: "" };
33436
+ const previousOffset = cursor.offset;
33437
+ const lines = await readJsonlDelta(subagent.path, cursor);
33438
+ if (cursor.offset !== previousOffset)
33439
+ advanced = true;
33440
+ subagentCursors.set(subagent.path, cursor);
33441
+ for (const line of lines)
33442
+ await processSubagentRecord(parseRecord(line), parentToolCallId);
33443
+ }
33444
+ discoveredNestedAgent = parentToolCallIdByAgentId.size > mappedAgentCount;
33445
+ }
33446
+ return advanced;
33447
+ };
33448
+ await processSubagentTranscripts();
33449
+ if (boundary >= 0) {
33450
+ for (const record3 of initialRecords.slice(boundary + 1)) {
33451
+ await processRecord(record3);
33452
+ if (done)
33453
+ break;
33798
33454
  }
33799
- throw new Error(options.formatError(result));
33800
- }
33801
- const parsed = parseAgentSessionListOutput(result.stdout, filterLocally ? options.filterCwd : undefined);
33802
- if (parsed && options.isSubagentSession) {
33803
- return filterSubagentSessions(parsed, options.isSubagentSession);
33804
33455
  }
33805
- return parsed;
33806
- }
33807
- function parseAgentSessionListOutput(stdout2, filterCwd) {
33808
- let parsed;
33809
- try {
33810
- parsed = JSON.parse(stdout2);
33811
- } catch {
33812
- throw new Error("failed to parse acpx sessions list output");
33456
+ const startedAt = Date.now();
33457
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
33458
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
33459
+ while (!done) {
33460
+ throwIfAborted(options.signal);
33461
+ if (Date.now() - startedAt >= timeoutMs) {
33462
+ return {
33463
+ status: "timeout",
33464
+ transcriptPath,
33465
+ completedToolCallIds: [...completed],
33466
+ failedToolCallIds: [...failed]
33467
+ };
33468
+ }
33469
+ await waitForPoll(pollIntervalMs, options.signal);
33470
+ await processSubagentTranscripts();
33471
+ const lines = await readJsonlDelta(transcriptPath, mainCursor);
33472
+ for (const line of lines) {
33473
+ await processAfterBoundary(parseRecord(line));
33474
+ if (done)
33475
+ break;
33476
+ }
33813
33477
  }
33814
- if (!isAgentSessionListResult(parsed)) {
33815
- return;
33478
+ if (parentToolCallIdByAgentId.size > 0) {
33479
+ let stablePolls = 0;
33480
+ for (let poll = 0;poll < FINAL_SUBAGENT_DRAIN_MAX_POLLS && stablePolls < FINAL_SUBAGENT_DRAIN_STABLE_POLLS; poll += 1) {
33481
+ await waitForPoll(pollIntervalMs, options.signal);
33482
+ stablePolls = await processSubagentTranscripts() ? 0 : stablePolls + 1;
33483
+ }
33816
33484
  }
33817
- return filterCwd ? filterAgentSessionListByCwd(parsed, filterCwd) : parsed;
33818
- }
33819
- function isAgentSessionListResult(value) {
33820
- if (!value || typeof value !== "object" || Array.isArray(value))
33821
- return false;
33822
- const record3 = value;
33823
- if (record3.source !== "agent" || !Array.isArray(record3.sessions))
33824
- return false;
33825
- return record3.sessions.every((session3) => {
33826
- if (!session3 || typeof session3 !== "object" || Array.isArray(session3))
33827
- return false;
33828
- const item = session3;
33829
- return typeof item.sessionId === "string";
33830
- });
33831
- }
33832
- function filterAgentSessionListByCwd(result, cwd) {
33833
33485
  return {
33834
- ...result,
33835
- sessions: result.sessions.filter((session3) => session3.cwd && isSamePath(session3.cwd, cwd))
33486
+ status: "completed",
33487
+ transcriptPath,
33488
+ completedToolCallIds: [...completed],
33489
+ failedToolCallIds: [...failed]
33836
33490
  };
33837
33491
  }
33838
- var init_agent_session_list = __esm(() => {
33839
- init_path();
33840
- init_codex_subagent_filter();
33841
- });
33842
-
33843
- // src/transport/acpx-session-files.ts
33844
- import { readdir as readdir3, unlink as unlink2 } from "node:fs/promises";
33845
- import { homedir as homedir11 } from "node:os";
33846
- import { join as join22 } from "node:path";
33847
- async function deleteAcpxSessionFiles(options) {
33848
- const dir = options.sessionsDir ?? join22(homedir11(), ".acpx", "sessions");
33849
- const safeId = encodeURIComponent(options.acpxRecordId);
33850
- await unlink2(join22(dir, `${safeId}.json`)).catch(() => {
33851
- return;
33852
- });
33853
- let entries;
33492
+ async function readJsonlDelta(path14, cursor) {
33493
+ let size;
33854
33494
  try {
33855
- entries = await readdir3(dir);
33495
+ size = (await stat3(path14)).size;
33856
33496
  } catch {
33857
- return;
33858
- }
33859
- const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
33860
- for (const name of streamFiles) {
33861
- await unlink2(join22(dir, name)).catch(() => {
33862
- return;
33863
- });
33864
- }
33865
- }
33866
- var init_acpx_session_files = () => {};
33867
-
33868
- // src/transport/session-effort.ts
33869
- function requireAdvertisedSessionEffort(raw, value) {
33870
- const advertised = parseSessionEffortRecord(raw);
33871
- if (!advertised) {
33872
- throw new Error("the active agent does not advertise a reasoning-effort option");
33497
+ return [];
33873
33498
  }
33874
- if (!advertised.available.includes(value)) {
33875
- throw new Error(`reasoning effort "${value}" is not advertised by the active agent`);
33499
+ if (size < cursor.offset) {
33500
+ cursor.offset = 0;
33501
+ cursor.partial = "";
33876
33502
  }
33877
- return advertised;
33878
- }
33879
- function parseSessionEffortRecord(raw) {
33880
- let record3;
33503
+ if (size <= cursor.offset)
33504
+ return [];
33505
+ let chunk;
33881
33506
  try {
33882
- record3 = JSON.parse(raw);
33507
+ const handle = await open5(path14, "r");
33508
+ try {
33509
+ chunk = Buffer.alloc(size - cursor.offset);
33510
+ const result = await handle.read(chunk, 0, chunk.length, cursor.offset);
33511
+ chunk = chunk.subarray(0, result.bytesRead);
33512
+ } finally {
33513
+ await handle.close();
33514
+ }
33883
33515
  } catch {
33884
- return;
33885
- }
33886
- if (!isRecord5(record3) || !isRecord5(record3.acpx) || !Array.isArray(record3.acpx.config_options)) {
33887
- return;
33516
+ return [];
33888
33517
  }
33889
- for (const candidate of record3.acpx.config_options) {
33890
- if (!isRecord5(candidate) || typeof candidate.id !== "string")
33891
- continue;
33892
- if (candidate.category !== "thought_level" && !EFFORT_CONFIG_IDS.has(candidate.id))
33518
+ cursor.offset += chunk.length;
33519
+ cursor.partial += chunk.toString("utf8");
33520
+ const lines = cursor.partial.split(`
33521
+ `);
33522
+ cursor.partial = lines.pop() ?? "";
33523
+ return lines;
33524
+ }
33525
+ function findPromptBoundary(records, launched) {
33526
+ let launchIndex = -1;
33527
+ for (let index = 0;index < records.length; index += 1) {
33528
+ const record3 = records[index];
33529
+ if (record3?.type !== "assistant" || record3.isSidechain === true)
33893
33530
  continue;
33894
- const available = Array.isArray(candidate.options) ? candidate.options.flatMap(effortOptionValues) : [];
33895
- return {
33896
- configId: candidate.id,
33897
- current: typeof candidate.currentValue === "string" ? candidate.currentValue : undefined,
33898
- available
33899
- };
33531
+ for (const block of contentBlocks(record3.message?.content)) {
33532
+ if (readString(block, "type") === "tool_use" && launched.has(readString(block, "id") ?? "")) {
33533
+ launchIndex = index;
33534
+ }
33535
+ }
33900
33536
  }
33901
- return;
33902
- }
33903
- function effortOptionValues(option) {
33904
- if (!isRecord5(option))
33905
- return [];
33906
- if (typeof option.value === "string")
33907
- return [option.value];
33908
- return Array.isArray(option.options) ? option.options.flatMap(effortOptionValues) : [];
33909
- }
33910
- function isRecord5(value) {
33911
- return typeof value === "object" && value !== null;
33912
- }
33913
- var EFFORT_CONFIG_IDS;
33914
- var init_session_effort = __esm(() => {
33915
- EFFORT_CONFIG_IDS = new Set([
33916
- "reasoning_effort",
33917
- "effort",
33918
- "thought_level",
33919
- "thinking"
33920
- ]);
33921
- });
33922
-
33923
- // src/transport/permission-mode-flag.ts
33924
- function permissionModeToFlag(permissionMode) {
33925
- switch (permissionMode) {
33926
- case "approve-reads":
33927
- return "--approve-reads";
33928
- case "deny-all":
33929
- return "--deny-all";
33930
- case "approve-all":
33931
- return "--approve-all";
33537
+ if (launchIndex < 0)
33538
+ return -1;
33539
+ for (let index = launchIndex + 1;index < records.length; index += 1) {
33540
+ const record3 = records[index];
33541
+ if (record3?.type === "assistant" && record3.isSidechain !== true && record3.message?.stop_reason === "end_turn") {
33542
+ return index;
33543
+ }
33932
33544
  }
33545
+ return -1;
33546
+ }
33547
+ function hasTrackedLaunch(record3, launched) {
33548
+ if (record3?.type !== "assistant" || record3.isSidechain === true)
33549
+ return false;
33550
+ return contentBlocks(record3.message?.content).some((block) => readString(block, "type") === "tool_use" && launched.has(readString(block, "id") ?? ""));
33933
33551
  }
33934
-
33935
- // src/transport/acpx-command-builder.ts
33936
- function buildPermissionArgs(input) {
33937
- const args = [permissionModeToFlag(input.permissionMode), "--non-interactive-permissions", input.nonInteractivePermissions];
33938
- if (typeof input.permissionPolicy === "string" && input.permissionPolicy.trim().length > 0) {
33939
- args.push("--permission-policy", input.permissionPolicy);
33552
+ function isMainEndTurn(record3) {
33553
+ return record3.type === "assistant" && record3.isSidechain !== true && record3.message?.stop_reason === "end_turn";
33554
+ }
33555
+ function parseRecord(line) {
33556
+ if (!line.trim())
33557
+ return;
33558
+ try {
33559
+ return JSON.parse(line);
33560
+ } catch {
33561
+ return;
33940
33562
  }
33941
- return args;
33942
33563
  }
33943
- function buildQueueOwnerTtlArgs(queueOwnerTtlSeconds) {
33944
- if (typeof queueOwnerTtlSeconds !== "number" || !Number.isFinite(queueOwnerTtlSeconds))
33945
- return [];
33946
- return ["--ttl", String(queueOwnerTtlSeconds)];
33564
+ function contentBlocks(content) {
33565
+ if (typeof content === "string")
33566
+ return [{ type: "text", text: content }];
33567
+ return Array.isArray(content) ? content.filter((block) => !!block && typeof block === "object") : [];
33947
33568
  }
33948
- function buildModelArgs(model) {
33949
- const trimmed = model?.trim();
33950
- return trimmed ? ["--model", trimmed] : [];
33569
+ function blockText(block) {
33570
+ if (typeof block.text === "string")
33571
+ return block.text;
33572
+ if (typeof block.content === "string")
33573
+ return block.content;
33574
+ if (Array.isArray(block.content))
33575
+ return block.content.map((item) => textOfUnknown(item)).filter(Boolean).join(`
33576
+ `);
33577
+ return textOfUnknown(block.content);
33951
33578
  }
33952
- function appendAgentAndTail(prefix, input, tail2) {
33953
- if (input.agentCommand)
33954
- return [...prefix, "--agent", input.agentCommand, ...tail2];
33955
- return [...prefix, input.agent, ...tail2];
33579
+ function textOfUnknown(value) {
33580
+ if (typeof value === "string")
33581
+ return value;
33582
+ if (Array.isArray(value))
33583
+ return value.map(textOfUnknown).filter(Boolean).join(`
33584
+ `);
33585
+ if (!value || typeof value !== "object")
33586
+ return "";
33587
+ const object4 = value;
33588
+ if (typeof object4.text === "string")
33589
+ return object4.text;
33590
+ if (object4.content !== undefined)
33591
+ return textOfUnknown(object4.content);
33592
+ try {
33593
+ return JSON.stringify(value);
33594
+ } catch {
33595
+ return "";
33596
+ }
33956
33597
  }
33957
- function buildSessionArgs(input, tail2, options = {}) {
33958
- const prefix = [
33959
- "--format",
33960
- options.format ?? "quiet",
33961
- "--cwd",
33962
- input.cwd,
33963
- ...buildPermissionArgs(input.permission),
33964
- ...buildModelArgs(input.model)
33965
- ];
33966
- if (options.verbose)
33967
- prefix.push("--verbose");
33968
- return appendAgentAndTail(prefix, input, tail2);
33598
+ function findStringField(value, keys) {
33599
+ if (!value || typeof value !== "object")
33600
+ return;
33601
+ if (Array.isArray(value)) {
33602
+ for (const item of value) {
33603
+ const found = findStringField(item, keys);
33604
+ if (found)
33605
+ return found;
33606
+ }
33607
+ return;
33608
+ }
33609
+ const object4 = value;
33610
+ for (const key of keys) {
33611
+ const candidate = object4[key];
33612
+ if (typeof candidate === "string" && candidate.trim())
33613
+ return candidate.trim();
33614
+ }
33615
+ for (const candidate of Object.values(object4)) {
33616
+ const found = findStringField(candidate, keys);
33617
+ if (found)
33618
+ return found;
33619
+ }
33620
+ return;
33969
33621
  }
33970
- function buildPromptArgs(input, tail2) {
33971
- const prefix = [
33972
- "--format",
33973
- "json",
33974
- "--json-strict",
33975
- "--cwd",
33976
- input.cwd,
33977
- ...buildPermissionArgs(input.permission),
33978
- ...buildModelArgs(input.model),
33979
- ...buildQueueOwnerTtlArgs(input.queueOwnerTtlSeconds)
33980
- ];
33981
- return appendAgentAndTail(prefix, input, tail2);
33622
+ function taskNotifications(text) {
33623
+ const notifications = [];
33624
+ const blocks = [...text.matchAll(/<task-notification>([\s\S]*?)<\/task-notification>/gi)].map((match) => match[1] ?? "");
33625
+ for (const block of blocks.length > 0 ? blocks : [text]) {
33626
+ const toolCallId = /<tool-use-id>([^<]+)<\/tool-use-id>/i.exec(block)?.[1]?.trim();
33627
+ if (!toolCallId)
33628
+ continue;
33629
+ const status = /<status>([^<]+)<\/status>/i.exec(block)?.[1]?.trim().toLowerCase();
33630
+ notifications.push({
33631
+ toolCallId,
33632
+ failed: status === "failed" || status === "error" || status === "cancelled" || status === "canceled"
33633
+ });
33634
+ }
33635
+ return notifications;
33982
33636
  }
33983
- function buildAgentQueryArgs(input, format, tail2) {
33984
- const prefix = ["--format", format, "--cwd", input.cwd, ...buildPermissionArgs(input.permission)];
33985
- return appendAgentAndTail(prefix, input, tail2);
33637
+ function classifyToolKind(name) {
33638
+ const normalized = name.toLowerCase();
33639
+ if (/(read|view|open)/.test(normalized))
33640
+ return "read";
33641
+ if (/(grep|search|find|glob)/.test(normalized))
33642
+ return "search";
33643
+ if (/(edit|write|patch|replace|create)/.test(normalized))
33644
+ return "edit";
33645
+ if (/(bash|shell|exec|run|terminal|command)/.test(normalized))
33646
+ return "execute";
33647
+ if (/(think|reason|plan|agent|task)/.test(normalized))
33648
+ return "think";
33649
+ return "other";
33986
33650
  }
33987
- function isMissingAcpxSessionError(stderr, stdout2) {
33988
- const combined = `${stderr}
33989
- ${stdout2}`.toLowerCase();
33990
- return combined.includes("no named session") || combined.includes("no cwd session") || combined.includes("session not found") || combined.includes("unknown session") || combined.includes("no acpx session found");
33651
+ function isAgentToolName(name) {
33652
+ const normalized = name.trim().toLowerCase();
33653
+ return normalized === "agent" || normalized === "task";
33991
33654
  }
33992
- function parseAcpxSessionRecordId(stdout2) {
33993
- try {
33994
- const parsed = JSON.parse(stdout2);
33995
- const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
33996
- const providerSessionId = typeof parsed.agentSessionId === "string" && parsed.agentSessionId.length > 0 ? parsed.agentSessionId : undefined;
33997
- const acpSessionId = typeof parsed.acpSessionId === "string" && parsed.acpSessionId.length > 0 ? parsed.acpSessionId : undefined;
33998
- const agentSessionId = providerSessionId ?? acpSessionId;
33999
- if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
34000
- return { acpxRecordId, agentSessionId };
33655
+ async function findSubagentTranscripts(transcriptPath) {
33656
+ const sessionDir = join20(dirname12(transcriptPath), basename3(transcriptPath, ".jsonl"), "subagents");
33657
+ const found = [];
33658
+ const visit = async (directory) => {
33659
+ let entries;
33660
+ try {
33661
+ entries = await readdir3(directory, { withFileTypes: true });
33662
+ } catch {
33663
+ return;
34001
33664
  }
34002
- } catch {
34003
- const firstLine = stdout2.trim().split(/\r?\n/, 1)[0];
34004
- if (firstLine && /^[\w.:-]+$/.test(firstLine) && firstLine.length >= 8) {
34005
- return { acpxRecordId: firstLine };
33665
+ for (const entry of entries) {
33666
+ const path14 = join20(directory, entry.name);
33667
+ if (entry.isDirectory()) {
33668
+ await visit(path14);
33669
+ } else if (entry.isFile() && entry.name.startsWith("agent-") && entry.name.endsWith(".jsonl")) {
33670
+ found.push({ agentId: entry.name.slice("agent-".length, -".jsonl".length), path: path14 });
33671
+ }
34006
33672
  }
33673
+ };
33674
+ await visit(sessionDir);
33675
+ return found;
33676
+ }
33677
+ function toolSummary(input) {
33678
+ if (!input || typeof input !== "object")
33679
+ return;
33680
+ const object4 = input;
33681
+ for (const key of ["description", "summary", "path", "file_path", "query", "pattern", "command"]) {
33682
+ if (typeof object4[key] === "string" && object4[key].trim())
33683
+ return object4[key].trim();
34007
33684
  }
34008
33685
  return;
34009
33686
  }
34010
- var DEFAULT_PERMISSION_MODE2 = "approve-all", DEFAULT_NON_INTERACTIVE = "deny";
34011
- var init_acpx_command_builder = () => {};
34012
-
34013
- // src/transport/acpx-cli/acpx-cli-transport.ts
34014
- import { createRequire as createRequire5 } from "node:module";
34015
- import { spawn as spawn11 } from "node:child_process";
34016
- import { spawn as spawnPty } from "node-pty";
34017
- function managementTimeoutError(args, options, output = {}) {
34018
- return new CommandTimeoutError(options.timeoutMs, renderCommandForError(args), {
34019
- stage: options.stage,
34020
- ...output
34021
- });
33687
+ function readString(object4, key) {
33688
+ if (!object4 || typeof object4 !== "object")
33689
+ return;
33690
+ const value = object4[key];
33691
+ return typeof value === "string" ? value : undefined;
34022
33692
  }
34023
- async function defaultRunner(command, args, options) {
34024
- return await new Promise((resolve4, reject) => {
34025
- const spawnSpec = resolveSpawnCommand(command, args);
34026
- const child = spawn11(spawnSpec.command, spawnSpec.args, {
34027
- env: options?.env,
34028
- stdio: ["ignore", "pipe", "pipe"]
34029
- });
34030
- let stdout2 = "";
34031
- let stderr = "";
34032
- const onAbort = () => {
34033
- terminateProcessTree(child.pid ?? 0, { detachedProcessGroup: false });
34034
- };
34035
- options?.signal?.addEventListener("abort", onAbort, { once: true });
34036
- const timeoutId = options?.timeoutMs ? setTimeout(() => {
34037
- onAbort();
34038
- reject(managementTimeoutError(args, options, { stdout: stdout2, stderr }));
34039
- }, options.timeoutMs) : undefined;
34040
- child.stdout.on("data", (chunk) => {
34041
- stdout2 += String(chunk);
34042
- });
34043
- child.stderr.on("data", (chunk) => {
34044
- stderr += String(chunk);
34045
- });
34046
- child.on("error", (error2) => {
34047
- options?.signal?.removeEventListener("abort", onAbort);
34048
- if (timeoutId)
34049
- clearTimeout(timeoutId);
34050
- reject(error2);
34051
- });
34052
- child.on("close", (code) => {
34053
- options?.signal?.removeEventListener("abort", onAbort);
34054
- if (timeoutId)
34055
- clearTimeout(timeoutId);
34056
- resolve4({ code: code ?? 1, stdout: stdout2, stderr });
34057
- });
34058
- });
33693
+ function readBoolean(object4, key) {
33694
+ return !!object4 && typeof object4 === "object" && object4[key] === true;
34059
33695
  }
34060
- async function defaultPtyRunner(command, args, options) {
34061
- const helperPath = resolveNodePtyHelperPath(require4.resolve("node-pty/package.json"), process.platform, process.arch);
34062
- await ensureNodePtyHelperExecutable(helperPath);
34063
- return await new Promise((resolve4, reject) => {
34064
- const spawnSpec = resolveSpawnCommand(command, args);
34065
- const child = spawnPty(spawnSpec.command, spawnSpec.args, {
34066
- name: "xterm-color",
34067
- cols: 80,
34068
- rows: 24,
34069
- cwd: process.cwd(),
34070
- env: resolveChildEnvironment(process.env, options?.env, getLocale())
34071
- });
34072
- let output = "";
33696
+ function encodeClaudeProjectDirectory(cwd) {
33697
+ return cwd.replace(/[:\\/]/g, "-");
33698
+ }
33699
+ async function exists(path14) {
33700
+ try {
33701
+ await access4(path14, fsConstants.R_OK);
33702
+ return true;
33703
+ } catch {
33704
+ return false;
33705
+ }
33706
+ }
33707
+ function throwIfAborted(signal) {
33708
+ if (signal?.aborted)
33709
+ throw new DOMException("background follow-up aborted", "AbortError");
33710
+ }
33711
+ async function waitForPoll(ms, signal) {
33712
+ if (!signal) {
33713
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
33714
+ return;
33715
+ }
33716
+ throwIfAborted(signal);
33717
+ await new Promise((resolve4, reject) => {
33718
+ const timer = setTimeout(() => {
33719
+ signal.removeEventListener("abort", onAbort);
33720
+ resolve4();
33721
+ }, ms);
34073
33722
  const onAbort = () => {
34074
- child.kill();
33723
+ clearTimeout(timer);
33724
+ reject(new DOMException("background follow-up aborted", "AbortError"));
34075
33725
  };
34076
- options?.signal?.addEventListener("abort", onAbort, { once: true });
34077
- const timeoutId = options?.timeoutMs ? setTimeout(() => {
34078
- onAbort();
34079
- reject(managementTimeoutError(args, options, { stdout: output }));
34080
- }, options.timeoutMs) : undefined;
34081
- child.onData((chunk) => {
34082
- output += chunk;
34083
- });
34084
- child.onExit(({ exitCode }) => {
34085
- options?.signal?.removeEventListener("abort", onAbort);
34086
- if (timeoutId)
34087
- clearTimeout(timeoutId);
34088
- resolve4({ code: exitCode, stdout: output, stderr: "" });
34089
- });
33726
+ signal.addEventListener("abort", onAbort, { once: true });
34090
33727
  });
34091
33728
  }
33729
+ var DEFAULT_POLL_INTERVAL_MS = 250, DEFAULT_TIMEOUT_MS, FINAL_SUBAGENT_DRAIN_MAX_POLLS = 4, FINAL_SUBAGENT_DRAIN_STABLE_POLLS = 2, ASYNC_AGENT_LAUNCH;
33730
+ var init_claude_background_followup = __esm(() => {
33731
+ DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
33732
+ ASYNC_AGENT_LAUNCH = /Async agent launched successfully|["']?status["']?\s*:\s*["']async_launched["']/i;
33733
+ });
34092
33734
 
34093
- class AcpxCliTransport {
34094
- command;
34095
- sessionInitTimeoutMs;
34096
- managementCommandTimeoutMs;
34097
- permissionMode;
34098
- nonInteractivePermissions;
34099
- permissionPolicy;
34100
- queueOwnerTtlSeconds;
34101
- runCommand;
34102
- runPtyCommand;
34103
- queueOwnerLauncher;
34104
- streamingHooks;
34105
- resolveSpawnEnvironment;
34106
- constructor(options, runCommand = defaultRunner, runPtyCommand = defaultPtyRunner, queueOwnerLauncher, streamingHooks = {}) {
34107
- this.command = options.command ?? "acpx";
34108
- this.sessionInitTimeoutMs = options.sessionInitTimeoutMs ?? 120000;
34109
- this.managementCommandTimeoutMs = options.managementCommandTimeoutMs ?? DEFAULT_MANAGEMENT_COMMAND_TIMEOUT_MS;
34110
- this.permissionMode = options.permissionMode ?? DEFAULT_PERMISSION_MODE2;
34111
- this.nonInteractivePermissions = options.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE;
34112
- this.permissionPolicy = options.permissionPolicy;
34113
- this.queueOwnerTtlSeconds = options.queueOwnerTtlSeconds;
34114
- this.runCommand = runCommand;
34115
- this.runPtyCommand = runPtyCommand;
34116
- this.queueOwnerLauncher = queueOwnerLauncher ?? new AcpxQueueOwnerLauncher({
34117
- acpxCommand: this.command,
34118
- ...typeof this.queueOwnerTtlSeconds === "number" && Number.isFinite(this.queueOwnerTtlSeconds) ? { ttlMs: this.queueOwnerTtlSeconds * 1000 } : {}
33735
+ // src/transport/tool-kind-emoji.ts
33736
+ var TOOL_KIND_EMOJI, DEFAULT_TOOL_EMOJI;
33737
+ var init_tool_kind_emoji = __esm(() => {
33738
+ TOOL_KIND_EMOJI = {
33739
+ read: "\uD83D\uDCD6",
33740
+ search: "\uD83D\uDD0D",
33741
+ execute: "\uD83D\uDCBB",
33742
+ edit: "✏️",
33743
+ think: "\uD83E\uDDE0",
33744
+ other: "\uD83D\uDD27"
33745
+ };
33746
+ DEFAULT_TOOL_EMOJI = TOOL_KIND_EMOJI.other;
33747
+ });
33748
+
33749
+ // src/transport/streaming-prompt.ts
33750
+ function createStreamingPromptState(formatToolCalls = false, options) {
33751
+ let toolEventMode;
33752
+ let onToolEvent;
33753
+ let onThought;
33754
+ let onPlan;
33755
+ let onUsage;
33756
+ let onCommands;
33757
+ let rawStream = false;
33758
+ let driver;
33759
+ if (options === undefined) {
33760
+ toolEventMode = "text";
33761
+ onToolEvent = undefined;
33762
+ } else if (typeof options === "function") {
33763
+ onToolEvent = options;
33764
+ toolEventMode = "structured";
33765
+ } else {
33766
+ onToolEvent = options.onToolEvent;
33767
+ onThought = options.onThought;
33768
+ onPlan = options.onPlan;
33769
+ onUsage = options.onUsage;
33770
+ onCommands = options.onCommands;
33771
+ rawStream = options.rawStream ?? false;
33772
+ driver = options.driver?.trim().toLowerCase() || undefined;
33773
+ toolEventMode = resolveToolEventMode({
33774
+ toolEventMode: options.mode,
33775
+ onToolEvent
34119
33776
  });
34120
- this.streamingHooks = streamingHooks;
34121
- this.resolveSpawnEnvironment = options.resolveSpawnEnvironment ?? resolveClaudeSpawnEnvironment;
34122
33777
  }
34123
- async ensureSession(session3, _onProgress) {
34124
- this.invalidateRecordIdCache(session3);
34125
- try {
34126
- await this.runEnsureSession(session3);
34127
- } catch (error2) {
34128
- const requestedModel = session3.model?.trim();
34129
- if (requestedModel && isModelNotAdvertisedError(error2 instanceof Error ? error2.message : null)) {
34130
- await this.runEnsureSession({ ...session3, model: undefined });
34131
- return;
33778
+ return {
33779
+ buffer: "",
33780
+ segments: [],
33781
+ hasAgentMessage: false,
33782
+ pendingLine: "",
33783
+ formatToolCalls,
33784
+ emittedToolCallIds: new Set,
33785
+ toolCalls: new Map,
33786
+ toolEventMode,
33787
+ driver,
33788
+ rawStream,
33789
+ onToolEvent,
33790
+ onThought,
33791
+ onPlan,
33792
+ onUsage,
33793
+ onCommands,
33794
+ finalize() {
33795
+ if (this.pendingLine.trim().length > 0) {
33796
+ parseStreamingChunks(this, this.pendingLine);
34132
33797
  }
34133
- throw error2;
33798
+ const remaining = this.rawStream ? this.buffer : this.buffer.trim();
33799
+ this.buffer = "";
33800
+ this.pendingLine = "";
33801
+ return remaining;
34134
33802
  }
33803
+ };
33804
+ }
33805
+ function parseStreamingDataChunk(state, chunk) {
33806
+ state.pendingLine += chunk;
33807
+ let boundary;
33808
+ while ((boundary = state.pendingLine.indexOf(`
33809
+ `)) !== -1) {
33810
+ const line = state.pendingLine.slice(0, boundary);
33811
+ state.pendingLine = state.pendingLine.slice(boundary + 1);
33812
+ parseStreamingChunks(state, line);
34135
33813
  }
34136
- async runEnsureSession(session3) {
34137
- const args = this.buildArgs(session3, [
34138
- "sessions",
34139
- "new",
34140
- "--name",
34141
- session3.transportSession
34142
- ]);
34143
- const runEnsure = session3.agentCommand ? this.run : this.runWithPty;
34144
- await runEnsure.call(this, args, {
34145
- timeoutMs: this.sessionInitTimeoutMs,
34146
- env: this.spawnEnvironment(session3)
34147
- });
34148
- }
34149
- async listAgentSessions(query) {
34150
- return await runAgentSessionList({
34151
- filterCwd: query.filterCwd,
34152
- runList: async (includeFilterCwd) => {
34153
- const args = this.buildAgentQueryArgs(query, "json", [
34154
- "sessions",
34155
- "list",
34156
- ...includeFilterCwd && query.filterCwd ? ["--filter-cwd", query.filterCwd] : [],
34157
- ...query.cursor ? ["--cursor", query.cursor] : []
34158
- ]);
34159
- return await this.runCommandWithTimeout(this.runCommand, args, {
34160
- timeoutMs: this.sessionInitTimeoutMs,
34161
- env: this.spawnEnvironment(query)
34162
- });
34163
- },
34164
- formatError: (result) => normalizeCommandError(result) ?? `command failed with exit code ${result.code}`,
34165
- isSubagentSession: (query.driver ?? query.agent) === CODEX_AGENT_NAME ? codexSubagentPredicate() : undefined
34166
- });
34167
- }
34168
- async tailSessionHistory(session3, lines) {
34169
- const candidates = [
34170
- ["sessions", "history", session3.transportSession, "--limit", String(lines)],
34171
- ["sessions", "history", "quiet", "-s", session3.transportSession, String(lines)],
34172
- ["sessions", "history", "quiet", session3.transportSession, String(lines)],
34173
- ["sessions", "history", "-s", session3.transportSession, "--tail", String(lines)],
34174
- ["sessions", "history", session3.transportSession, "--tail", String(lines)],
34175
- ["sessions", "history", "--name", session3.transportSession, "--tail", String(lines)]
34176
- ];
34177
- const deadline = Date.now() + this.managementCommandTimeoutMs;
34178
- let lastResult;
34179
- for (const tail2 of candidates) {
34180
- const args = this.buildArgs(session3, tail2);
34181
- const result = await this.runCommandWithTimeout(this.runCommand, args, {
34182
- timeoutMs: Math.max(deadline - Date.now(), 1),
34183
- stage: "session-history",
34184
- env: this.spawnEnvironment(session3)
34185
- });
34186
- if (result.code === 0) {
34187
- return { text: result.stdout.trimEnd() };
34188
- }
34189
- lastResult = result;
34190
- }
34191
- const detail = lastResult ? normalizeCommandError(lastResult) ?? `command failed with exit code ${lastResult.code}` : "command failed";
34192
- throw new Error(detail);
33814
+ }
33815
+ function parseStreamingChunks(state, line) {
33816
+ const trimmed = line.trim();
33817
+ if (trimmed.length === 0)
33818
+ return;
33819
+ let event;
33820
+ try {
33821
+ event = JSON.parse(trimmed);
33822
+ } catch {
33823
+ return;
34193
33824
  }
34194
- async prompt(session3, text, reply, replyContext, options) {
34195
- if (session3.effort?.trim()) {
34196
- await this.reapplySessionEffort(session3, session3.effort.trim());
33825
+ if (event.method !== "session/update")
33826
+ return;
33827
+ const update = event.params?.update;
33828
+ if (!update)
33829
+ return;
33830
+ if (update.sessionUpdate === "tool_call" || update.sessionUpdate === "tool_call_update") {
33831
+ const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
33832
+ const wantsText = (state.toolEventMode === "text" || state.toolEventMode === "both") && state.formatToolCalls;
33833
+ if (wantsStructured && state.onToolEvent) {
33834
+ const merged = update.toolCallId ? mergeToolCallUpdate(state, update.toolCallId, update) : update;
33835
+ const toolEvent = buildToolUseEvent(merged, state.driver);
33836
+ if (toolEvent)
33837
+ state.onToolEvent(toolEvent);
34197
33838
  }
34198
- await this.launchMcpQueueOwnerIfNeeded(session3);
34199
- const structuredPrompt = await createStructuredPromptFile(text, options?.media);
34200
- const args = this.buildPromptArgs(session3, text, structuredPrompt?.filePath);
34201
- try {
34202
- if (reply || options?.onSegment || options?.onToolEvent || options?.onThought || options?.onPlan || options?.onUsage || options?.onCommands) {
34203
- const effectiveReplyMode = session3.effectiveReplyMode ?? session3.replyMode;
34204
- const formatToolCalls = (effectiveReplyMode ?? "verbose") === "verbose";
34205
- const rawStream = effectiveReplyMode === "stream";
34206
- let toolEventMode = resolveToolEventMode(options);
34207
- if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
34208
- toolEventMode = "text";
34209
- }
34210
- const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought, options?.onPlan, options?.onUsage, options?.onCommands, rawStream, this.spawnEnvironment(session3), session3.driver ?? session3.agent);
34211
- const baseText = getPromptText(result2);
34212
- if (!reply) {
34213
- return { text: baseText };
33839
+ if (wantsText) {
33840
+ const formatted = formatToolCallEvent(update, update.sessionUpdate);
33841
+ if (formatted) {
33842
+ const toolCallId = update.toolCallId;
33843
+ if (toolCallId) {
33844
+ if (state.emittedToolCallIds.has(toolCallId))
33845
+ return;
33846
+ state.emittedToolCallIds.add(toolCallId);
34214
33847
  }
34215
- const summary = buildOverflowSummary(overflowCount);
34216
- return { text: summary ? `${summary}
34217
-
34218
- ${baseText}` : "" };
33848
+ state.segments.push(formatted);
34219
33849
  }
34220
- const spawnOptions = this.withSpawnEnvironment(session3);
34221
- const result = spawnOptions ? await this.runCommand(this.command, args, spawnOptions) : await this.runCommand(this.command, args);
34222
- return { text: getPromptText(result) };
34223
- } finally {
34224
- try {
34225
- await structuredPrompt?.cleanup();
34226
- } catch {}
34227
33850
  }
33851
+ return;
34228
33852
  }
34229
- async setMode(session3, modeId) {
34230
- await this.run(this.buildArgs(session3, [
34231
- "set-mode",
34232
- "-s",
34233
- session3.transportSession,
34234
- modeId
34235
- ]), this.withSpawnEnvironment(session3, {
34236
- timeoutMs: this.managementCommandTimeoutMs,
34237
- stage: "set-mode"
34238
- }));
34239
- }
34240
- async setModel(session3, modelId) {
34241
- await this.run(this.buildArgs({ ...session3, model: modelId }, [
34242
- "set",
34243
- "-s",
34244
- session3.transportSession,
34245
- "model",
34246
- modelId
34247
- ]), this.withSpawnEnvironment({ ...session3, model: modelId }, {
34248
- timeoutMs: this.managementCommandTimeoutMs,
34249
- stage: "set-model"
34250
- }));
33853
+ if (update.sessionUpdate === "plan") {
33854
+ const entries = Array.isArray(update.entries) ? update.entries.filter((x) => !!x && typeof x === "object" && typeof x.content === "string" && typeof x.status === "string") : [];
33855
+ if (entries.length > 0)
33856
+ state.onPlan?.(entries);
33857
+ return;
34251
33858
  }
34252
- async getSessionModel(session3) {
34253
- const prefix = ["--format", "json", "--cwd", session3.cwd, ...this.buildPermissionArgs()];
34254
- const tail2 = ["status", "-s", session3.transportSession];
34255
- const args = session3.agentCommand ? [...prefix, "--agent", session3.agentCommand, ...tail2] : [...prefix, session3.agent, ...tail2];
34256
- const result = await this.runCommandWithTimeout(this.runCommand, args, {
34257
- timeoutMs: this.managementCommandTimeoutMs,
34258
- stage: "get-session-model",
34259
- env: this.spawnEnvironment(session3)
34260
- });
34261
- if (result.code !== 0) {
34262
- const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34263
- throw new Error(detail);
34264
- }
34265
- try {
34266
- const json = JSON.parse(result.stdout);
34267
- return {
34268
- current: typeof json.model === "string" ? json.model : undefined,
34269
- available: Array.isArray(json.availableModels) ? json.availableModels.filter((m) => typeof m === "string") : []
34270
- };
34271
- } catch {
34272
- return { available: [] };
33859
+ if (update.sessionUpdate === "usage_update") {
33860
+ const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
33861
+ const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
33862
+ if (used !== undefined && size !== undefined && size > 0) {
33863
+ const cost = normalizeUsageCost(update.cost);
33864
+ const breakdown = normalizeUsageBreakdown(update._meta?.usage);
33865
+ state.onUsage?.({ used, size, ...cost ? { cost } : {}, ...breakdown ? { breakdown } : {} });
34273
33866
  }
33867
+ return;
34274
33868
  }
34275
- async getSessionEffort(session3) {
34276
- const output = await this.run(this.buildArgs(session3, [
34277
- "sessions",
34278
- "show",
34279
- session3.transportSession
34280
- ], "json"), this.withSpawnEnvironment(session3, {
34281
- timeoutMs: this.managementCommandTimeoutMs,
34282
- stage: "get-session-effort"
34283
- }));
34284
- const effort = parseSessionEffortRecord(output);
34285
- return effort ? { current: effort.current, available: effort.available } : { available: [] };
34286
- }
34287
- async setSessionEffort(session3, effort) {
34288
- const record3 = await this.readSessionEffortRecord(session3);
34289
- await this.applyAdvertisedSessionEffort(session3, effort, record3);
33869
+ if (update.sessionUpdate === "available_commands_update") {
33870
+ if (Array.isArray(update.availableCommands)) {
33871
+ state.onCommands?.(normalizeAgentCommands(update.availableCommands));
33872
+ }
33873
+ return;
34290
33874
  }
34291
- async reapplySessionEffort(session3, effort) {
34292
- const record3 = await this.readSessionEffortRecord(session3);
34293
- const observed = parseSessionEffortRecord(record3);
34294
- if (!observed?.available.includes(effort)) {
34295
- return;
33875
+ const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
33876
+ if (isThoughtChunk) {
33877
+ const chunk2 = update.content.text;
33878
+ if (chunk2.length > 0) {
33879
+ state.onThought?.(chunk2);
34296
33880
  }
34297
- await this.applyAdvertisedSessionEffort(session3, effort, record3);
33881
+ return;
34298
33882
  }
34299
- async readSessionEffortRecord(session3) {
34300
- return await this.run(this.buildArgs(session3, [
34301
- "sessions",
34302
- "show",
34303
- session3.transportSession
34304
- ], "json"), this.withSpawnEnvironment(session3, {
34305
- timeoutMs: this.managementCommandTimeoutMs,
34306
- stage: "get-session-effort"
34307
- }));
33883
+ const isMessageChunk = update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
33884
+ if (!isMessageChunk)
33885
+ return;
33886
+ state.hasAgentMessage = true;
33887
+ const chunk = update.content.text ?? "";
33888
+ if (chunk.length === 0)
33889
+ return;
33890
+ state.buffer += chunk;
33891
+ if (state.rawStream)
33892
+ return;
33893
+ let boundary;
33894
+ while ((boundary = state.buffer.indexOf(`
33895
+
33896
+ `)) !== -1) {
33897
+ const segment = state.buffer.slice(0, boundary).trim();
33898
+ state.buffer = state.buffer.slice(boundary + 2);
33899
+ if (segment.length > 0) {
33900
+ state.segments.push(segment);
33901
+ }
34308
33902
  }
34309
- async applyAdvertisedSessionEffort(session3, effort, record3) {
34310
- const advertised = requireAdvertisedSessionEffort(record3, effort);
34311
- await this.run(this.buildArgs(session3, [
34312
- "set",
34313
- "-s",
34314
- session3.transportSession,
34315
- advertised.configId,
34316
- effort
34317
- ]), this.withSpawnEnvironment(session3, {
34318
- timeoutMs: this.managementCommandTimeoutMs,
34319
- stage: "set-session-effort"
34320
- }));
33903
+ }
33904
+ function formatToolCallEvent(update, sessionUpdate) {
33905
+ if (!update)
33906
+ return null;
33907
+ const kind = update.kind ?? "";
33908
+ const title = update.title ?? "";
33909
+ if (title.length === 0)
33910
+ return null;
33911
+ const emoji2 = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
33912
+ const inputSummary = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
33913
+ const status = readString2(update, "status");
33914
+ if (!inputSummary && status === "pending")
33915
+ return null;
33916
+ if (!inputSummary && isGenericToolTitle(kind, title))
33917
+ return null;
33918
+ const summaryText = inputSummary && inputSummary !== title ? `: ${truncateToolDisplay(inputSummary)}` : "";
33919
+ const statusText = status ? ` (${status})` : "";
33920
+ return `${emoji2} ${title}${statusText}${summaryText}`;
33921
+ }
33922
+ function isEmptyToolField(v) {
33923
+ if (v === undefined || v === null)
33924
+ return true;
33925
+ if (typeof v === "string")
33926
+ return v.trim().length === 0;
33927
+ if (Array.isArray(v))
33928
+ return v.length === 0;
33929
+ if (typeof v === "object")
33930
+ return Object.keys(v).length === 0;
33931
+ return false;
33932
+ }
33933
+ function mergeToolCallUpdate(state, toolCallId, update) {
33934
+ const prev = state.toolCalls.get(toolCallId) ?? { toolCallId };
33935
+ const merged = { ...prev };
33936
+ for (const key of ["kind", "title", "rawInput", "content", "rawOutput", "locations", "status"]) {
33937
+ const next = update[key];
33938
+ if (!isEmptyToolField(next))
33939
+ merged[key] = next;
34321
33940
  }
34322
- async cancel(session3) {
34323
- const output = await this.run(this.buildArgs(session3, [
34324
- "cancel",
34325
- "-s",
34326
- session3.transportSession
34327
- ]), this.withSpawnEnvironment(session3, {
34328
- timeoutMs: this.managementCommandTimeoutMs,
34329
- stage: "cancel"
34330
- }));
34331
- return {
34332
- cancelled: true,
34333
- message: output.trim()
33941
+ const nextMeta = update._meta;
33942
+ if (nextMeta) {
33943
+ const previousMeta = merged._meta;
33944
+ merged._meta = {
33945
+ ...previousMeta,
33946
+ ...nextMeta,
33947
+ ...previousMeta?.qoder || nextMeta.qoder ? { qoder: { ...previousMeta?.qoder, ...nextMeta.qoder } } : {},
33948
+ ...previousMeta?.codex || nextMeta.codex ? {
33949
+ codex: {
33950
+ ...previousMeta?.codex,
33951
+ ...nextMeta.codex,
33952
+ ...previousMeta?.codex?.subagent || nextMeta.codex?.subagent ? { subagent: { ...previousMeta?.codex?.subagent, ...nextMeta.codex?.subagent } } : {}
33953
+ }
33954
+ } : {},
33955
+ ...previousMeta?.claudeCode || nextMeta.claudeCode ? { claudeCode: { ...previousMeta?.claudeCode, ...nextMeta.claudeCode } } : {}
34334
33956
  };
34335
33957
  }
34336
- async resumeAgentSession(session3, agentSessionId) {
34337
- this.invalidateRecordIdCache(session3);
34338
- const args = this.buildArgs(session3, [
34339
- "sessions",
34340
- "new",
34341
- "--name",
34342
- session3.transportSession,
34343
- "--resume-session",
34344
- agentSessionId
34345
- ]);
34346
- const runResume = session3.agentCommand ? this.run : this.runWithPty;
34347
- await runResume.call(this, args, {
34348
- timeoutMs: this.sessionInitTimeoutMs,
34349
- env: this.spawnEnvironment(session3)
34350
- });
33958
+ merged.toolCallId = toolCallId;
33959
+ state.toolCalls.set(toolCallId, merged);
33960
+ return merged;
33961
+ }
33962
+ function buildToolUseEvent(update, driver) {
33963
+ if (!update)
33964
+ return null;
33965
+ const toolCallId = update.toolCallId;
33966
+ if (!toolCallId)
33967
+ return null;
33968
+ const kindRaw = update.kind ?? "";
33969
+ const kind = (() => {
33970
+ switch (kindRaw) {
33971
+ case "read":
33972
+ case "search":
33973
+ case "execute":
33974
+ case "edit":
33975
+ case "think":
33976
+ return kindRaw;
33977
+ default:
33978
+ return "other";
33979
+ }
33980
+ })();
33981
+ const title = (update.title ?? "").trim();
33982
+ const toolName = title || "Tool";
33983
+ const summaryRaw = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
33984
+ const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
33985
+ const statusRaw = readString2(update, "status");
33986
+ const isClaudeDriver = driver === undefined || driver === "claude";
33987
+ const claudeMeta = isClaudeDriver ? update._meta?.claudeCode : undefined;
33988
+ const claudeToolResponse = claudeMeta?.toolResponse;
33989
+ const hasClaudeToolResponse = !isEmptyToolField(claudeToolResponse);
33990
+ const isAsyncAgentLaunch = update._meta?.claudeCode?.toolName === "Agent" && isRecord4(claudeToolResponse) && claudeToolResponse.status === "async_launched" || driver === "qoder" && update._meta?.qoder?.toolName === "Agent" && isAsyncAgentLaunchOutput(update.rawOutput);
33991
+ const status = isAsyncAgentLaunch ? "running" : statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : hasClaudeToolResponse ? "success" : "running";
33992
+ const rawInput = update.rawInput;
33993
+ const content = update.content;
33994
+ const rawOutput = update.rawOutput ?? claudeToolResponse;
33995
+ const locations = update.locations;
33996
+ const parentToolCallId = claudeMeta?.parentToolUseId?.trim();
33997
+ const isSubagent = claudeMeta?.toolName === "Agent" || driver === "qoder" && update._meta?.qoder?.toolName === "Agent" || driver === "kimi" && isKimiSubagentInput(rawInput) || driver === "codex" && isCodexSubagentMeta(update._meta?.codex?.subagent);
33998
+ return {
33999
+ toolCallId,
34000
+ ...parentToolCallId ? { parentToolCallId } : {},
34001
+ ...isSubagent ? { isSubagent: true } : {},
34002
+ toolName,
34003
+ kind,
34004
+ ...summary ? { summary } : {},
34005
+ ...rawInput !== undefined ? { rawInput } : {},
34006
+ ...content !== undefined ? { content } : {},
34007
+ ...rawOutput !== undefined ? { rawOutput } : {},
34008
+ ...locations !== undefined ? { locations } : {},
34009
+ status
34010
+ };
34011
+ }
34012
+ function isKimiSubagentInput(rawInput) {
34013
+ return isRecord4(rawInput) && readFirstString(rawInput, ["prompt"]) !== undefined && readFirstString(rawInput, ["subagent_type", "subagentType"]) !== undefined;
34014
+ }
34015
+ function isCodexSubagentMeta(meta2) {
34016
+ return typeof meta2?.threadId === "string" && meta2.threadId.trim().length > 0 && typeof meta2.activity === "string" && meta2.activity.trim().length > 0;
34017
+ }
34018
+ function summarizeToolInput(rawInput, title = "") {
34019
+ if (rawInput == null)
34020
+ return;
34021
+ if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
34022
+ return String(rawInput);
34351
34023
  }
34352
- async updatePermissionPolicy(policy) {
34353
- this.permissionMode = policy.permissionMode;
34354
- this.nonInteractivePermissions = policy.nonInteractivePermissions;
34355
- this.permissionPolicy = policy.permissionPolicy;
34024
+ if (!isRecord4(rawInput))
34025
+ return;
34026
+ const taskSummary = summarizeTaskInput(rawInput, title);
34027
+ if (taskSummary)
34028
+ return taskSummary;
34029
+ const command = readFirstString(rawInput, ["command", "cmd", "program"]);
34030
+ const args = readFirstStringArray(rawInput, ["args", "arguments"]);
34031
+ if (command) {
34032
+ return [command, ...args ?? []].join(" ");
34356
34033
  }
34357
- async removeSession(session3) {
34358
- this.invalidateRecordIdCache(session3);
34359
- const result = await this.runCommandWithTimeout(this.runCommand, this.buildArgs(session3, [
34360
- "sessions",
34361
- "close",
34362
- session3.transportSession
34363
- ]), this.withSpawnEnvironment(session3, {
34364
- timeoutMs: this.managementCommandTimeoutMs,
34365
- stage: "remove-session"
34366
- }));
34367
- if (result.code === 0) {
34368
- return;
34034
+ const parsedCmd = rawInput.parsed_cmd;
34035
+ if (Array.isArray(parsedCmd) && parsedCmd.length > 0) {
34036
+ const parts = [];
34037
+ for (const entry of parsedCmd) {
34038
+ if (isRecord4(entry) && typeof entry.cmd === "string" && entry.cmd.length > 0) {
34039
+ parts.push(entry.cmd);
34040
+ }
34369
34041
  }
34370
- if (isMissingAcpxSessionError(result.stderr, result.stdout)) {
34371
- return;
34042
+ if (parts.length > 0) {
34043
+ return parts.join(" ");
34372
34044
  }
34373
- const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34374
- throw new Error(detail);
34375
34045
  }
34376
- async deleteSession(session3) {
34377
- let acpxRecordId;
34378
- try {
34379
- ({ acpxRecordId } = await this.readSessionRecord(session3));
34380
- } catch {
34381
- return;
34046
+ return readFirstString(rawInput, [
34047
+ "path",
34048
+ "file",
34049
+ "filePath",
34050
+ "filepath",
34051
+ "file_path",
34052
+ "target",
34053
+ "uri",
34054
+ "url",
34055
+ "query",
34056
+ "pattern",
34057
+ "text",
34058
+ "search",
34059
+ "name",
34060
+ "description"
34061
+ ]);
34062
+ }
34063
+ function summarizeTaskInput(rawInput, title) {
34064
+ const subagentType = readFirstString(rawInput, ["subagent_type", "subagentType", "agent", "agentType"]);
34065
+ const description = readFirstString(rawInput, ["description", "task", "summary"]);
34066
+ if (subagentType && description) {
34067
+ return description === title ? subagentType : `${subagentType}: ${description}`;
34068
+ }
34069
+ if (subagentType)
34070
+ return subagentType;
34071
+ return;
34072
+ }
34073
+ function readFirstString(record3, keys) {
34074
+ for (const key of keys) {
34075
+ const value = record3[key];
34076
+ if (typeof value === "string" && value.trim().length > 0) {
34077
+ return value.trim();
34382
34078
  }
34383
- await this.removeSession(session3);
34384
- await deleteAcpxSessionFiles({ acpxRecordId });
34385
34079
  }
34386
- async freeWarmProcess(session3) {
34387
- let acpxRecordId;
34388
- try {
34389
- ({ acpxRecordId } = await this.readSessionRecord(session3));
34390
- } catch {
34391
- return;
34080
+ return;
34081
+ }
34082
+ function readFirstStringArray(record3, keys) {
34083
+ for (const key of keys) {
34084
+ const value = record3[key];
34085
+ if (!Array.isArray(value))
34086
+ continue;
34087
+ const entries = value.map((entry) => typeof entry === "string" && entry.trim().length > 0 ? entry.trim() : undefined).filter((entry) => entry !== undefined);
34088
+ if (entries.length > 0) {
34089
+ return entries;
34392
34090
  }
34393
- await terminateAcpxQueueOwner(acpxRecordId);
34394
34091
  }
34395
- recordIdCache = new Map;
34396
- recordIdCacheKey(session3) {
34397
- return JSON.stringify([session3.agent, session3.agentCommand ?? null, session3.cwd, session3.transportSession]);
34092
+ return;
34093
+ }
34094
+ function asFiniteNumber(value) {
34095
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
34096
+ }
34097
+ function firstFiniteNumber(record3, keys) {
34098
+ for (const key of keys) {
34099
+ const n = asFiniteNumber(record3[key]);
34100
+ if (n !== undefined)
34101
+ return n;
34398
34102
  }
34399
- invalidateRecordIdCache(session3) {
34400
- this.recordIdCache.delete(this.recordIdCacheKey(session3));
34103
+ return;
34104
+ }
34105
+ function normalizeUsageBreakdown(value) {
34106
+ if (!isRecord4(value))
34107
+ return;
34108
+ const out = {};
34109
+ for (const [key, aliases] of USAGE_BREAKDOWN_FIELDS) {
34110
+ const n = firstFiniteNumber(value, aliases);
34111
+ if (n !== undefined)
34112
+ out[key] = n;
34401
34113
  }
34402
- async isSessionWarm(session3) {
34403
- const cacheKey = this.recordIdCacheKey(session3);
34404
- let acpxRecordId = this.recordIdCache.get(cacheKey);
34405
- if (!acpxRecordId) {
34406
- try {
34407
- ({ acpxRecordId } = await this.readSessionRecord(session3));
34408
- } catch {
34409
- return false;
34410
- }
34411
- this.recordIdCache.set(cacheKey, acpxRecordId);
34412
- }
34413
- const pid = await readQueueOwnerPid(acpxRecordId);
34414
- if (pid === undefined) {
34415
- return false;
34416
- }
34417
- return isProcessAlive(pid);
34114
+ return Object.keys(out).length > 0 ? out : undefined;
34115
+ }
34116
+ function normalizeUsageCost(value) {
34117
+ if (!isRecord4(value))
34118
+ return;
34119
+ const amount = asFiniteNumber(value.amount);
34120
+ const currency = readString2(value, "currency");
34121
+ if (amount === undefined && !currency)
34122
+ return;
34123
+ return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
34124
+ }
34125
+ function normalizeAgentCommands(value) {
34126
+ if (!Array.isArray(value))
34127
+ return [];
34128
+ const out = [];
34129
+ for (const entry of value) {
34130
+ if (!isRecord4(entry))
34131
+ continue;
34132
+ const name = readString2(entry, "name");
34133
+ if (!name)
34134
+ continue;
34135
+ const description = readString2(entry, "description");
34136
+ out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
34418
34137
  }
34419
- async hasSession(session3) {
34420
- const result = await this.runCommandWithTimeout(this.runCommand, this.buildArgs(session3, [
34421
- "sessions",
34422
- "show",
34423
- session3.transportSession
34424
- ]), this.withSpawnEnvironment(session3, {
34425
- timeoutMs: this.managementCommandTimeoutMs,
34426
- stage: "has-session"
34427
- }));
34428
- return result.code === 0;
34138
+ return out;
34139
+ }
34140
+ function isRecord4(value) {
34141
+ return typeof value === "object" && value !== null && !Array.isArray(value);
34142
+ }
34143
+ function readString2(rawInput, key) {
34144
+ if (!isRecord4(rawInput))
34145
+ return;
34146
+ const value = rawInput[key];
34147
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
34148
+ }
34149
+ function truncateToolDisplay(text) {
34150
+ return text.length > 60 ? `${text.slice(0, 57)}...` : text;
34151
+ }
34152
+ function isGenericToolTitle(kind, title) {
34153
+ const normalizedTitle = title.trim().toLowerCase();
34154
+ if (kind === "execute" && ["bash", "shell", "sh", "powershell", "cmd"].includes(normalizedTitle)) {
34155
+ return true;
34429
34156
  }
34430
- async launchMcpQueueOwnerIfNeeded(session3) {
34431
- if (!session3.mcpCoordinatorSession) {
34432
- return;
34433
- }
34434
- const record3 = await this.readSessionRecord(session3);
34435
- const env = this.spawnEnvironment(session3);
34436
- await this.queueOwnerLauncher.launch({
34437
- acpxRecordId: record3.acpxRecordId,
34438
- coordinatorSession: session3.mcpCoordinatorSession,
34439
- ...session3.mcpSourceHandle ? { sourceHandle: session3.mcpSourceHandle } : {},
34440
- permissionMode: this.permissionMode,
34441
- nonInteractivePermissions: this.nonInteractivePermissions,
34442
- ...session3.model?.trim() ? { sessionOptions: { model: session3.model.trim() } } : {},
34443
- ...env ? { env } : {}
34444
- });
34157
+ if (kind === "search" && ["grep", "rg", "search"].includes(normalizedTitle)) {
34158
+ return true;
34445
34159
  }
34446
- async readSessionRecord(session3) {
34447
- const result = await this.runCommandWithTimeout(this.runCommand, this.buildArgs(session3, [
34448
- "sessions",
34449
- "show",
34450
- session3.transportSession
34451
- ], "json"), this.withSpawnEnvironment(session3, {
34452
- timeoutMs: this.managementCommandTimeoutMs,
34453
- stage: "read-session-record"
34454
- }));
34455
- if (result.code !== 0) {
34456
- const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34457
- throw new Error(detail);
34458
- }
34459
- const record3 = parseAcpxSessionRecordId(result.stdout);
34460
- if (record3)
34461
- return record3;
34462
- throw new Error("failed to resolve acpx session record id");
34160
+ if (kind === "read" && ["read", "cat"].includes(normalizedTitle)) {
34161
+ return true;
34463
34162
  }
34464
- async getAgentSessionId(session3) {
34465
- const record3 = await this.readSessionRecord(session3);
34466
- return record3.agentSessionId;
34163
+ return false;
34164
+ }
34165
+ var USAGE_BREAKDOWN_FIELDS;
34166
+ var init_streaming_prompt = __esm(() => {
34167
+ init_claude_background_followup();
34168
+ init_tool_kind_emoji();
34169
+ USAGE_BREAKDOWN_FIELDS = [
34170
+ ["inputTokens", ["inputTokens", "input_tokens"]],
34171
+ ["outputTokens", ["outputTokens", "output_tokens"]],
34172
+ ["cachedReadTokens", ["cachedReadTokens", "cacheReadInputTokens", "cache_read_input_tokens"]],
34173
+ ["cachedWriteTokens", ["cachedWriteTokens", "cacheCreationInputTokens", "cache_creation_input_tokens"]],
34174
+ ["thoughtTokens", ["thoughtTokens", "thought_tokens"]],
34175
+ ["totalTokens", ["totalTokens", "total_tokens"]]
34176
+ ];
34177
+ });
34178
+
34179
+ // src/transport/acpx-cli/node-pty-helper.ts
34180
+ import { chmod as chmodFs } from "node:fs/promises";
34181
+ import { dirname as dirname13, join as join21 } from "node:path";
34182
+ function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
34183
+ if (platform === "win32") {
34184
+ return null;
34467
34185
  }
34468
- async run(args, options) {
34469
- const result = await this.runCommandWithTimeout(this.runCommand, args, options);
34470
- if (result.code !== 0) {
34471
- const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34472
- throw new Error(detail);
34473
- }
34474
- return result.stdout;
34186
+ return join21(dirname13(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
34187
+ }
34188
+ async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
34189
+ if (!helperPath) {
34190
+ return;
34475
34191
  }
34476
- async runWithPty(args, options) {
34477
- const result = await this.runCommandWithTimeout(this.runPtyCommand, args, options);
34478
- if (result.code !== 0) {
34479
- const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34480
- throw new Error(detail);
34192
+ try {
34193
+ await chmod5(helperPath, 493);
34194
+ } catch (error2) {
34195
+ if (error2.code === "ENOENT") {
34196
+ return;
34481
34197
  }
34482
- return result.stdout;
34198
+ throw error2;
34483
34199
  }
34484
- async runCommandWithTimeout(runner, args, options) {
34485
- const spawnSpec = resolveSpawnCommand(this.command, args);
34486
- if (!options?.timeoutMs) {
34487
- return await runner(spawnSpec.command, spawnSpec.args, options);
34488
- }
34489
- const abortController = new AbortController;
34490
- let timeoutId;
34491
- return await Promise.race([
34492
- runner(spawnSpec.command, spawnSpec.args, { ...options, signal: abortController.signal }).finally(() => {
34493
- if (timeoutId)
34494
- clearTimeout(timeoutId);
34495
- }),
34496
- new Promise((_, reject) => {
34497
- timeoutId = setTimeout(() => {
34498
- reject(managementTimeoutError(args, options));
34499
- abortController.abort();
34500
- }, options.timeoutMs);
34501
- })
34502
- ]);
34200
+ }
34201
+ var init_node_pty_helper = () => {};
34202
+
34203
+ // src/transport/codex-subagent-filter.ts
34204
+ import { closeSync, openSync, readdirSync, readSync, statSync as statSync2 } from "node:fs";
34205
+ import { homedir as homedir11 } from "node:os";
34206
+ import { join as join22 } from "node:path";
34207
+ function resolveCodexHome(env = process.env) {
34208
+ const fromEnv = env.CODEX_HOME?.trim();
34209
+ return fromEnv ? fromEnv : join22(homedir11(), ".codex");
34210
+ }
34211
+ function isPlainObject3(value) {
34212
+ return !!value && typeof value === "object" && !Array.isArray(value);
34213
+ }
34214
+ function isSubagentSource(source) {
34215
+ if (!isPlainObject3(source) || !Object.hasOwn(source, "subagent"))
34216
+ return false;
34217
+ const subagent = source.subagent;
34218
+ if (!isPlainObject3(subagent))
34219
+ return false;
34220
+ return Object.values(subagent).some((variant) => isPlainObject3(variant) && typeof variant.parent_thread_id === "string");
34221
+ }
34222
+ function sessionMetaLineIsSubagent(line) {
34223
+ if (!line)
34224
+ return false;
34225
+ let parsed;
34226
+ try {
34227
+ parsed = JSON.parse(line);
34228
+ } catch {
34229
+ return false;
34503
34230
  }
34504
- async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought, onPlan, onUsage, onCommands, rawStream = false, env, driver) {
34505
- const hooks = this.streamingHooks;
34506
- const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn11(cmd, spawnArgs, {
34507
- env,
34508
- stdio: ["ignore", "pipe", "pipe"]
34509
- }));
34510
- const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
34511
- const clearIntervalFn = hooks.clearIntervalFn ?? ((timer) => clearInterval(timer));
34512
- const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? (rawStream ? 200 : 30000);
34513
- const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? (rawStream ? 80 : 5000);
34514
- const now = hooks.now ?? (() => Date.now());
34515
- return await new Promise((resolve4, reject) => {
34516
- const spawnSpec = resolveSpawnCommand(command, args);
34517
- const child = doSpawn(spawnSpec.command, spawnSpec.args);
34518
- let stdout2 = "";
34519
- let stderr = "";
34520
- let lastReplyAt = now();
34521
- let segmentChain = Promise.resolve();
34522
- let segmentError;
34523
- let toolEventChain = Promise.resolve();
34524
- let toolEventError;
34525
- let thoughtChain = Promise.resolve();
34526
- let thoughtError;
34527
- let planChain = Promise.resolve();
34528
- let planError;
34529
- let usageChain = Promise.resolve();
34530
- let usageError;
34531
- let commandsChain = Promise.resolve();
34532
- let commandsError;
34533
- const userOnToolEvent = onToolEvent;
34534
- const userOnThought = onThought;
34535
- const userOnPlan = onPlan;
34536
- const userOnUsage = onUsage;
34537
- const userOnCommands = onCommands;
34538
- const state = createStreamingPromptState(formatToolCalls, {
34539
- mode: toolEventMode,
34540
- driver,
34541
- rawStream,
34542
- ...userOnToolEvent ? {
34543
- onToolEvent: (event) => {
34544
- toolEventChain = toolEventChain.then(() => userOnToolEvent(event)).catch((error2) => {
34545
- toolEventError ??= error2;
34546
- });
34547
- }
34548
- } : {},
34549
- ...userOnThought ? {
34550
- onThought: (chunk) => {
34551
- thoughtChain = thoughtChain.then(() => userOnThought(chunk)).catch((error2) => {
34552
- thoughtError ??= error2;
34553
- });
34554
- }
34555
- } : {},
34556
- ...userOnPlan ? {
34557
- onPlan: (entries) => {
34558
- planChain = planChain.then(() => userOnPlan(entries)).catch((error2) => {
34559
- planError ??= error2;
34560
- });
34561
- }
34562
- } : {},
34563
- ...userOnUsage ? {
34564
- onUsage: (usage) => {
34565
- usageChain = usageChain.then(() => userOnUsage(usage)).catch((error2) => {
34566
- usageError ??= error2;
34567
- });
34568
- }
34569
- } : {},
34570
- ...userOnCommands ? {
34571
- onCommands: (commands) => {
34572
- commandsChain = commandsChain.then(() => userOnCommands(commands)).catch((error2) => {
34573
- commandsError ??= error2;
34574
- });
34575
- }
34576
- } : {}
34577
- });
34578
- const sink2 = reply ? rawStream ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
34579
- reply,
34580
- ...replyContext ? { replyContext } : {}
34581
- }) : null;
34582
- const feedSegment = (segment) => {
34583
- if (onSegment) {
34584
- segmentChain = segmentChain.then(() => onSegment(segment)).catch((error2) => {
34585
- segmentError ??= error2;
34586
- });
34587
- }
34588
- sink2?.feedSegment(segment);
34589
- lastReplyAt = now();
34590
- };
34591
- const flushBuffer = () => {
34592
- const remaining = rawStream ? state.buffer : state.buffer.trim();
34593
- if (remaining.length > 0) {
34594
- state.buffer = "";
34595
- feedSegment(remaining);
34596
- }
34597
- };
34598
- const timer = setIntervalFn(() => {
34599
- if (state.buffer.trim().length > 0 && now() - lastReplyAt >= maxSegmentWaitMs) {
34600
- flushBuffer();
34231
+ const payload = parsed?.payload;
34232
+ return isSubagentSource(payload?.source);
34233
+ }
34234
+ function createSubagentPredicate(reader) {
34235
+ let index = null;
34236
+ const cache = new Map;
34237
+ return (sessionId) => {
34238
+ const cached2 = cache.get(sessionId);
34239
+ if (cached2 !== undefined)
34240
+ return cached2;
34241
+ let result = false;
34242
+ try {
34243
+ if (!index) {
34244
+ index = new Map;
34245
+ for (const path15 of reader.listRolloutPaths()) {
34246
+ const id = path15.match(ROLLOUT_RE)?.[1];
34247
+ if (id)
34248
+ index.set(id.toLowerCase(), path15);
34601
34249
  }
34602
- }, flushCheckIntervalMs);
34603
- child.stdout.setEncoding("utf8");
34604
- child.stdout.on("data", (chunk) => {
34605
- stdout2 += String(chunk);
34606
- parseStreamingDataChunk(state, String(chunk));
34607
- for (const segment of state.segments.splice(0)) {
34608
- feedSegment(segment);
34250
+ }
34251
+ const path14 = index.get(sessionId.toLowerCase());
34252
+ if (path14)
34253
+ result = sessionMetaLineIsSubagent(reader.readFirstLine(path14));
34254
+ } catch {
34255
+ result = false;
34256
+ }
34257
+ cache.set(sessionId, result);
34258
+ return result;
34259
+ };
34260
+ }
34261
+ function filterSubagentSessions(result, isSubagent) {
34262
+ return {
34263
+ ...result,
34264
+ sessions: result.sessions.filter((session3) => {
34265
+ try {
34266
+ return !isSubagent(session3.sessionId);
34267
+ } catch {
34268
+ return true;
34269
+ }
34270
+ })
34271
+ };
34272
+ }
34273
+ function nodeRolloutReader(home) {
34274
+ const root = join22(home, "sessions");
34275
+ return {
34276
+ listRolloutPaths() {
34277
+ const out = [];
34278
+ const walk = (dir) => {
34279
+ let entries;
34280
+ try {
34281
+ entries = readdirSync(dir, { withFileTypes: true });
34282
+ } catch {
34283
+ return;
34609
34284
  }
34610
- });
34611
- child.stderr.on("data", (chunk) => {
34612
- stderr += String(chunk);
34613
- });
34614
- child.on("error", (err) => {
34615
- clearIntervalFn(timer);
34616
- reject(err);
34617
- });
34618
- child.on("close", (code) => {
34619
- clearIntervalFn(timer);
34620
- const remaining = state.finalize();
34621
- if (remaining.length > 0) {
34622
- feedSegment(remaining);
34285
+ for (const entry of entries) {
34286
+ const full = join22(dir, entry.name);
34287
+ if (entry.isDirectory())
34288
+ walk(full);
34289
+ else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl"))
34290
+ out.push(full);
34623
34291
  }
34624
- const { overflowCount } = sink2?.finalize() ?? { overflowCount: 0 };
34625
- Promise.all([
34626
- sink2?.drain({ timeoutMs: 30000 }) ?? Promise.resolve(),
34627
- segmentChain,
34628
- toolEventChain,
34629
- thoughtChain,
34630
- planChain,
34631
- usageChain,
34632
- commandsChain
34633
- ]).then(() => {
34634
- const deferred = sink2?.getPendingError();
34635
- if (deferred) {
34636
- reject(deferred);
34637
- return;
34638
- }
34639
- if (segmentError) {
34640
- reject(segmentError);
34641
- return;
34642
- }
34643
- if (toolEventError) {
34644
- reject(toolEventError);
34645
- return;
34646
- }
34647
- if (thoughtError) {
34648
- reject(thoughtError);
34649
- return;
34650
- }
34651
- if (planError) {
34652
- reject(planError);
34653
- return;
34654
- }
34655
- if (usageError) {
34656
- reject(usageError);
34657
- return;
34658
- }
34659
- if (commandsError) {
34660
- reject(commandsError);
34292
+ };
34293
+ walk(root);
34294
+ return out;
34295
+ },
34296
+ readFirstLine(path14) {
34297
+ let fd;
34298
+ try {
34299
+ statSync2(path14);
34300
+ fd = openSync(path14, "r");
34301
+ const buf = Buffer.alloc(READ_CHUNK);
34302
+ let acc = "";
34303
+ let total = 0;
34304
+ for (;; ) {
34305
+ const bytes = readSync(fd, buf, 0, READ_CHUNK, total);
34306
+ if (bytes <= 0)
34307
+ break;
34308
+ acc += buf.toString("utf8", 0, bytes);
34309
+ total += bytes;
34310
+ const nl = acc.indexOf(`
34311
+ `);
34312
+ if (nl !== -1)
34313
+ return acc.slice(0, nl);
34314
+ if (total >= FIRST_LINE_READ_CAP)
34661
34315
  return;
34662
- }
34663
- resolve4({
34664
- result: { code: code ?? 1, stdout: stdout2, stderr },
34665
- overflowCount
34666
- });
34667
- }).catch((error2) => {
34668
- reject(error2);
34669
- });
34670
- });
34316
+ }
34317
+ return acc.length ? acc : undefined;
34318
+ } catch {
34319
+ return;
34320
+ } finally {
34321
+ if (fd !== undefined) {
34322
+ try {
34323
+ closeSync(fd);
34324
+ } catch {}
34325
+ }
34326
+ }
34327
+ }
34328
+ };
34329
+ }
34330
+ function codexSubagentPredicate(env) {
34331
+ return createSubagentPredicate(nodeRolloutReader(resolveCodexHome(env)));
34332
+ }
34333
+ var CODEX_AGENT_NAME = "codex", FIRST_LINE_READ_CAP, READ_CHUNK, ROLLOUT_RE;
34334
+ var init_codex_subagent_filter = __esm(() => {
34335
+ FIRST_LINE_READ_CAP = 1024 * 1024;
34336
+ READ_CHUNK = 64 * 1024;
34337
+ ROLLOUT_RE = /rollout-.*-([0-9a-fA-F-]{36})\.jsonl$/;
34338
+ });
34339
+
34340
+ // src/transport/agent-session-list.ts
34341
+ function isUnknownFilterCwdOption(output) {
34342
+ return /(?:unknown|unrecognized) option/i.test(output) && output.includes("--filter-cwd");
34343
+ }
34344
+ async function runAgentSessionList(options) {
34345
+ let result = await options.runList(true);
34346
+ let filterLocally = false;
34347
+ if (result.code !== 0 && options.filterCwd && isUnknownFilterCwdOption(result.stdout + result.stderr)) {
34348
+ result = await options.runList(false);
34349
+ filterLocally = true;
34350
+ }
34351
+ if (result.code !== 0) {
34352
+ if ((result.stdout + result.stderr).includes("sessionCapabilities.list")) {
34353
+ return;
34354
+ }
34355
+ throw new Error(options.formatError(result));
34356
+ }
34357
+ const parsed = parseAgentSessionListOutput(result.stdout, filterLocally ? options.filterCwd : undefined);
34358
+ if (parsed && options.isSubagentSession) {
34359
+ return filterSubagentSessions(parsed, options.isSubagentSession);
34360
+ }
34361
+ return parsed;
34362
+ }
34363
+ function parseAgentSessionListOutput(stdout2, filterCwd) {
34364
+ let parsed;
34365
+ try {
34366
+ parsed = JSON.parse(stdout2);
34367
+ } catch {
34368
+ throw new Error("failed to parse acpx sessions list output");
34369
+ }
34370
+ if (!isAgentSessionListResult(parsed)) {
34371
+ return;
34372
+ }
34373
+ return filterCwd ? filterAgentSessionListByCwd(parsed, filterCwd) : parsed;
34374
+ }
34375
+ function isAgentSessionListResult(value) {
34376
+ if (!value || typeof value !== "object" || Array.isArray(value))
34377
+ return false;
34378
+ const record3 = value;
34379
+ if (record3.source !== "agent" || !Array.isArray(record3.sessions))
34380
+ return false;
34381
+ return record3.sessions.every((session3) => {
34382
+ if (!session3 || typeof session3 !== "object" || Array.isArray(session3))
34383
+ return false;
34384
+ const item = session3;
34385
+ return typeof item.sessionId === "string";
34386
+ });
34387
+ }
34388
+ function filterAgentSessionListByCwd(result, cwd) {
34389
+ return {
34390
+ ...result,
34391
+ sessions: result.sessions.filter((session3) => session3.cwd && isSamePath(session3.cwd, cwd))
34392
+ };
34393
+ }
34394
+ var init_agent_session_list = __esm(() => {
34395
+ init_path();
34396
+ init_codex_subagent_filter();
34397
+ });
34398
+
34399
+ // src/transport/acpx-session-files.ts
34400
+ import { readdir as readdir4, unlink as unlink2 } from "node:fs/promises";
34401
+ import { homedir as homedir12 } from "node:os";
34402
+ import { join as join23 } from "node:path";
34403
+ async function deleteAcpxSessionFiles(options) {
34404
+ const dir = options.sessionsDir ?? join23(homedir12(), ".acpx", "sessions");
34405
+ const safeId = encodeURIComponent(options.acpxRecordId);
34406
+ await unlink2(join23(dir, `${safeId}.json`)).catch(() => {
34407
+ return;
34408
+ });
34409
+ let entries;
34410
+ try {
34411
+ entries = await readdir4(dir);
34412
+ } catch {
34413
+ return;
34414
+ }
34415
+ const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
34416
+ for (const name of streamFiles) {
34417
+ await unlink2(join23(dir, name)).catch(() => {
34418
+ return;
34671
34419
  });
34672
34420
  }
34673
- permissionInput() {
34674
- return {
34675
- permissionMode: this.permissionMode,
34676
- nonInteractivePermissions: this.nonInteractivePermissions,
34677
- permissionPolicy: this.permissionPolicy
34678
- };
34421
+ }
34422
+ var init_acpx_session_files = () => {};
34423
+
34424
+ // src/transport/session-effort.ts
34425
+ function requireAdvertisedSessionEffort(raw, value) {
34426
+ const advertised = parseSessionEffortRecord(raw);
34427
+ if (!advertised) {
34428
+ throw new Error("the active agent does not advertise a reasoning-effort option");
34679
34429
  }
34680
- spawnEnvironment(input) {
34681
- return this.resolveSpawnEnvironment(input);
34430
+ if (!advertised.available.includes(value)) {
34431
+ throw new Error(`reasoning effort "${value}" is not advertised by the active agent`);
34682
34432
  }
34683
- withSpawnEnvironment(input, options) {
34684
- const env = this.spawnEnvironment(input);
34685
- return env ? { ...options ?? {}, env } : options;
34433
+ return advertised;
34434
+ }
34435
+ function parseSessionEffortRecord(raw) {
34436
+ let record3;
34437
+ try {
34438
+ record3 = JSON.parse(raw);
34439
+ } catch {
34440
+ return;
34686
34441
  }
34687
- sessionInput(session3) {
34442
+ if (!isRecord5(record3) || !isRecord5(record3.acpx) || !Array.isArray(record3.acpx.config_options)) {
34443
+ return;
34444
+ }
34445
+ for (const candidate of record3.acpx.config_options) {
34446
+ if (!isRecord5(candidate) || typeof candidate.id !== "string")
34447
+ continue;
34448
+ if (candidate.category !== "thought_level" && !EFFORT_CONFIG_IDS.has(candidate.id))
34449
+ continue;
34450
+ const available = Array.isArray(candidate.options) ? candidate.options.flatMap(effortOptionValues) : [];
34688
34451
  return {
34689
- agent: session3.agent,
34690
- agentCommand: session3.agentCommand,
34691
- cwd: session3.cwd,
34692
- model: session3.model,
34693
- permission: this.permissionInput()
34452
+ configId: candidate.id,
34453
+ current: typeof candidate.currentValue === "string" ? candidate.currentValue : undefined,
34454
+ available
34694
34455
  };
34695
34456
  }
34696
- buildArgs(session3, tail2, format = "quiet") {
34697
- return buildSessionArgs(this.sessionInput(session3), tail2, { format });
34698
- }
34699
- buildAgentQueryArgs(query, format, tail2) {
34700
- return buildAgentQueryArgs({ agent: query.agent, agentCommand: query.agentCommand, cwd: query.cwd, permission: this.permissionInput() }, format, tail2);
34701
- }
34702
- buildPromptArgs(session3, text, promptFile) {
34703
- const tail2 = promptFile ? ["prompt", "-s", session3.transportSession, "--file", promptFile] : ["prompt", "-s", session3.transportSession, text];
34704
- return buildPromptArgs({ ...this.sessionInput(session3), queueOwnerTtlSeconds: this.queueOwnerTtlSeconds }, tail2);
34457
+ return;
34458
+ }
34459
+ function effortOptionValues(option) {
34460
+ if (!isRecord5(option))
34461
+ return [];
34462
+ if (typeof option.value === "string")
34463
+ return [option.value];
34464
+ return Array.isArray(option.options) ? option.options.flatMap(effortOptionValues) : [];
34465
+ }
34466
+ function isRecord5(value) {
34467
+ return typeof value === "object" && value !== null;
34468
+ }
34469
+ var EFFORT_CONFIG_IDS;
34470
+ var init_session_effort = __esm(() => {
34471
+ EFFORT_CONFIG_IDS = new Set([
34472
+ "reasoning_effort",
34473
+ "effort",
34474
+ "thought_level",
34475
+ "thinking"
34476
+ ]);
34477
+ });
34478
+
34479
+ // src/transport/permission-mode-flag.ts
34480
+ function permissionModeToFlag(permissionMode) {
34481
+ switch (permissionMode) {
34482
+ case "approve-reads":
34483
+ return "--approve-reads";
34484
+ case "deny-all":
34485
+ return "--deny-all";
34486
+ case "approve-all":
34487
+ return "--approve-all";
34705
34488
  }
34706
- buildPermissionArgs() {
34707
- return buildPermissionArgs(this.permissionInput());
34489
+ }
34490
+
34491
+ // src/transport/acpx-command-builder.ts
34492
+ function buildPermissionArgs(input) {
34493
+ const args = [permissionModeToFlag(input.permissionMode), "--non-interactive-permissions", input.nonInteractivePermissions];
34494
+ if (typeof input.permissionPolicy === "string" && input.permissionPolicy.trim().length > 0) {
34495
+ args.push("--permission-policy", input.permissionPolicy);
34708
34496
  }
34497
+ return args;
34709
34498
  }
34710
- function renderCommandForError(args) {
34711
- const rendered = [];
34712
- for (let index = 0;index < args.length; index += 1) {
34713
- const arg = args[index];
34714
- if (arg === undefined) {
34715
- continue;
34716
- }
34717
- if (arg === "--format") {
34718
- index += 1;
34719
- continue;
34499
+ function buildQueueOwnerTtlArgs(queueOwnerTtlSeconds) {
34500
+ if (typeof queueOwnerTtlSeconds !== "number" || !Number.isFinite(queueOwnerTtlSeconds))
34501
+ return [];
34502
+ return ["--ttl", String(queueOwnerTtlSeconds)];
34503
+ }
34504
+ function buildModelArgs(model) {
34505
+ const trimmed = model?.trim();
34506
+ return trimmed ? ["--model", trimmed] : [];
34507
+ }
34508
+ function appendAgentAndTail(prefix, input, tail2) {
34509
+ if (input.agentCommand)
34510
+ return [...prefix, "--agent", input.agentCommand, ...tail2];
34511
+ return [...prefix, input.agent, ...tail2];
34512
+ }
34513
+ function buildSessionArgs(input, tail2, options = {}) {
34514
+ const prefix = [
34515
+ "--format",
34516
+ options.format ?? "quiet",
34517
+ "--cwd",
34518
+ input.cwd,
34519
+ ...buildPermissionArgs(input.permission),
34520
+ ...buildModelArgs(input.model)
34521
+ ];
34522
+ if (options.verbose)
34523
+ prefix.push("--verbose");
34524
+ return appendAgentAndTail(prefix, input, tail2);
34525
+ }
34526
+ function buildPromptArgs(input, tail2) {
34527
+ const prefix = [
34528
+ "--format",
34529
+ "json",
34530
+ "--json-strict",
34531
+ "--cwd",
34532
+ input.cwd,
34533
+ ...buildPermissionArgs(input.permission),
34534
+ ...buildModelArgs(input.model),
34535
+ ...buildQueueOwnerTtlArgs(input.queueOwnerTtlSeconds)
34536
+ ];
34537
+ return appendAgentAndTail(prefix, input, tail2);
34538
+ }
34539
+ function buildAgentQueryArgs(input, format, tail2) {
34540
+ const prefix = ["--format", format, "--cwd", input.cwd, ...buildPermissionArgs(input.permission)];
34541
+ return appendAgentAndTail(prefix, input, tail2);
34542
+ }
34543
+ function isMissingAcpxSessionError(stderr, stdout2) {
34544
+ const combined = `${stderr}
34545
+ ${stdout2}`.toLowerCase();
34546
+ return combined.includes("no named session") || combined.includes("no cwd session") || combined.includes("session not found") || combined.includes("unknown session") || combined.includes("no acpx session found");
34547
+ }
34548
+ function parseAcpxSessionRecordId(stdout2) {
34549
+ try {
34550
+ const parsed = JSON.parse(stdout2);
34551
+ const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
34552
+ const providerSessionId = typeof parsed.agentSessionId === "string" && parsed.agentSessionId.length > 0 ? parsed.agentSessionId : undefined;
34553
+ const acpSessionId = typeof parsed.acpSessionId === "string" && parsed.acpSessionId.length > 0 ? parsed.acpSessionId : undefined;
34554
+ const agentSessionId = providerSessionId ?? acpSessionId;
34555
+ if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
34556
+ return { acpxRecordId, agentSessionId };
34720
34557
  }
34721
- if (arg === "--cwd") {
34722
- index += 1;
34723
- continue;
34558
+ } catch {
34559
+ const firstLine = stdout2.trim().split(/\r?\n/, 1)[0];
34560
+ if (firstLine && /^[\w.:-]+$/.test(firstLine) && firstLine.length >= 8) {
34561
+ return { acpxRecordId: firstLine };
34724
34562
  }
34725
- rendered.push(/\s/.test(arg) || arg.includes(":") ? `"${arg}"` : arg);
34726
- }
34727
- return rendered.join(" ");
34728
- }
34729
- function resolveChildEnvironment(baseEnv, explicitEnv, locale) {
34730
- const source = explicitEnv ?? baseEnv;
34731
- const env = {};
34732
- for (const [key, value] of Object.entries(source)) {
34733
- if (typeof value === "string")
34734
- env[key] = value;
34735
34563
  }
34736
- env.XACPX_LANG = locale;
34737
- return env;
34564
+ return;
34738
34565
  }
34739
- var require4;
34740
- var init_acpx_cli_transport = __esm(() => {
34741
- init_spawn_command();
34742
- init_claude_settings_policy();
34743
- init_i18n();
34744
- init_prompt_output();
34745
- init_prompt_media();
34746
- init_streaming_prompt();
34747
- init_quota_gated_reply_sink();
34748
- init_node_pty_helper();
34749
- init_terminate_process_tree();
34750
- init_acpx_queue_owner_launcher();
34751
- init_daemon_files();
34752
- init_agent_session_list();
34753
- init_codex_subagent_filter();
34754
- init_acpx_session_files();
34755
- init_session_effort();
34756
- init_command_timeouts();
34757
- init_acpx_command_builder();
34758
- require4 = createRequire5(import.meta.url);
34759
- });
34566
+ var DEFAULT_PERMISSION_MODE2 = "approve-all", DEFAULT_NON_INTERACTIVE = "deny";
34567
+ var init_acpx_command_builder = () => {};
34760
34568
 
34761
- // src/transport/claude-background-followup.ts
34762
- import { access as access4, open as open5, readFile as readFile14, readdir as readdir4, stat as stat3 } from "node:fs/promises";
34763
- import { constants as fsConstants } from "node:fs";
34764
- import { homedir as homedir12 } from "node:os";
34765
- import { basename as basename3, dirname as dirname13, join as join23 } from "node:path";
34766
- function isClaudeAsyncAgentLaunch(event) {
34767
- return ASYNC_AGENT_LAUNCH.test(textOfUnknown(event.rawOutput));
34569
+ // src/transport/acpx-cli/acpx-cli-transport.ts
34570
+ import { createRequire as createRequire5 } from "node:module";
34571
+ import { spawn as spawn11 } from "node:child_process";
34572
+ import { spawn as spawnPty } from "node-pty";
34573
+ function managementTimeoutError(args, options, output = {}) {
34574
+ return new CommandTimeoutError(options.timeoutMs, renderCommandForError(args), {
34575
+ stage: options.stage,
34576
+ ...output
34577
+ });
34768
34578
  }
34769
- function claudeAsyncAgentId(event) {
34770
- const fromObject = findStringField(event.rawOutput, new Set(["agentId", "agent_id"]));
34771
- if (fromObject)
34772
- return fromObject;
34773
- return /\bagent(?:Id|_id)["']?\s*[:=]\s*["']?([A-Za-z0-9_-]+)/i.exec(textOfUnknown(event.rawOutput))?.[1];
34579
+ async function defaultRunner(command, args, options) {
34580
+ return await new Promise((resolve4, reject) => {
34581
+ const spawnSpec = resolveSpawnCommand(command, args);
34582
+ const child = spawn11(spawnSpec.command, spawnSpec.args, {
34583
+ env: options?.env,
34584
+ stdio: ["ignore", "pipe", "pipe"]
34585
+ });
34586
+ let stdout2 = "";
34587
+ let stderr = "";
34588
+ const onAbort = () => {
34589
+ terminateProcessTree(child.pid ?? 0, { detachedProcessGroup: false });
34590
+ };
34591
+ options?.signal?.addEventListener("abort", onAbort, { once: true });
34592
+ const timeoutId = options?.timeoutMs ? setTimeout(() => {
34593
+ onAbort();
34594
+ reject(managementTimeoutError(args, options, { stdout: stdout2, stderr }));
34595
+ }, options.timeoutMs) : undefined;
34596
+ child.stdout.on("data", (chunk) => {
34597
+ stdout2 += String(chunk);
34598
+ });
34599
+ child.stderr.on("data", (chunk) => {
34600
+ stderr += String(chunk);
34601
+ });
34602
+ child.on("error", (error2) => {
34603
+ options?.signal?.removeEventListener("abort", onAbort);
34604
+ if (timeoutId)
34605
+ clearTimeout(timeoutId);
34606
+ reject(error2);
34607
+ });
34608
+ child.on("close", (code) => {
34609
+ options?.signal?.removeEventListener("abort", onAbort);
34610
+ if (timeoutId)
34611
+ clearTimeout(timeoutId);
34612
+ resolve4({ code: code ?? 1, stdout: stdout2, stderr });
34613
+ });
34614
+ });
34774
34615
  }
34775
- async function findClaudeTranscriptPath(input) {
34776
- const projectsDir = join23(input.homeDir ?? homedir12(), ".claude", "projects");
34777
- const direct = join23(projectsDir, encodeClaudeProjectDirectory(input.cwd), `${input.sessionId}.jsonl`);
34778
- if (await exists(direct))
34779
- return direct;
34780
- try {
34781
- const entries = await readdir4(projectsDir, { withFileTypes: true });
34782
- for (const entry of entries) {
34783
- if (!entry.isDirectory())
34784
- continue;
34785
- const candidate = join23(projectsDir, entry.name, `${input.sessionId}.jsonl`);
34786
- if (await exists(candidate))
34787
- return candidate;
34616
+ async function defaultPtyRunner(command, args, options) {
34617
+ const helperPath = resolveNodePtyHelperPath(require4.resolve("node-pty/package.json"), process.platform, process.arch);
34618
+ await ensureNodePtyHelperExecutable(helperPath);
34619
+ return await new Promise((resolve4, reject) => {
34620
+ const spawnSpec = resolveSpawnCommand(command, args);
34621
+ const child = spawnPty(spawnSpec.command, spawnSpec.args, {
34622
+ name: "xterm-color",
34623
+ cols: 80,
34624
+ rows: 24,
34625
+ cwd: process.cwd(),
34626
+ env: resolveChildEnvironment(process.env, options?.env, getLocale())
34627
+ });
34628
+ let output = "";
34629
+ const onAbort = () => {
34630
+ child.kill();
34631
+ };
34632
+ options?.signal?.addEventListener("abort", onAbort, { once: true });
34633
+ const timeoutId = options?.timeoutMs ? setTimeout(() => {
34634
+ onAbort();
34635
+ reject(managementTimeoutError(args, options, { stdout: output }));
34636
+ }, options.timeoutMs) : undefined;
34637
+ child.onData((chunk) => {
34638
+ output += chunk;
34639
+ });
34640
+ child.onExit(({ exitCode }) => {
34641
+ options?.signal?.removeEventListener("abort", onAbort);
34642
+ if (timeoutId)
34643
+ clearTimeout(timeoutId);
34644
+ resolve4({ code: exitCode, stdout: output, stderr: "" });
34645
+ });
34646
+ });
34647
+ }
34648
+
34649
+ class AcpxCliTransport {
34650
+ command;
34651
+ sessionInitTimeoutMs;
34652
+ managementCommandTimeoutMs;
34653
+ permissionMode;
34654
+ nonInteractivePermissions;
34655
+ permissionPolicy;
34656
+ queueOwnerTtlSeconds;
34657
+ runCommand;
34658
+ runPtyCommand;
34659
+ queueOwnerLauncher;
34660
+ streamingHooks;
34661
+ resolveSpawnEnvironment;
34662
+ constructor(options, runCommand = defaultRunner, runPtyCommand = defaultPtyRunner, queueOwnerLauncher, streamingHooks = {}) {
34663
+ this.command = options.command ?? "acpx";
34664
+ this.sessionInitTimeoutMs = options.sessionInitTimeoutMs ?? 120000;
34665
+ this.managementCommandTimeoutMs = options.managementCommandTimeoutMs ?? DEFAULT_MANAGEMENT_COMMAND_TIMEOUT_MS;
34666
+ this.permissionMode = options.permissionMode ?? DEFAULT_PERMISSION_MODE2;
34667
+ this.nonInteractivePermissions = options.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE;
34668
+ this.permissionPolicy = options.permissionPolicy;
34669
+ this.queueOwnerTtlSeconds = options.queueOwnerTtlSeconds;
34670
+ this.runCommand = runCommand;
34671
+ this.runPtyCommand = runPtyCommand;
34672
+ this.queueOwnerLauncher = queueOwnerLauncher ?? new AcpxQueueOwnerLauncher({
34673
+ acpxCommand: this.command,
34674
+ ...typeof this.queueOwnerTtlSeconds === "number" && Number.isFinite(this.queueOwnerTtlSeconds) ? { ttlMs: this.queueOwnerTtlSeconds * 1000 } : {}
34675
+ });
34676
+ this.streamingHooks = streamingHooks;
34677
+ this.resolveSpawnEnvironment = options.resolveSpawnEnvironment ?? resolveClaudeSpawnEnvironment;
34678
+ }
34679
+ async ensureSession(session3, _onProgress) {
34680
+ this.invalidateRecordIdCache(session3);
34681
+ try {
34682
+ await this.runEnsureSession(session3);
34683
+ } catch (error2) {
34684
+ const requestedModel = session3.model?.trim();
34685
+ if (requestedModel && isModelNotAdvertisedError(error2 instanceof Error ? error2.message : null)) {
34686
+ await this.runEnsureSession({ ...session3, model: undefined });
34687
+ return;
34688
+ }
34689
+ throw error2;
34788
34690
  }
34789
- } catch {
34790
- return;
34791
34691
  }
34792
- return;
34793
- }
34794
- async function followClaudeBackgroundTurn(options) {
34795
- const transcriptPath = options.transcriptPath ?? await findClaudeTranscriptPath(options);
34796
- if (!transcriptPath) {
34797
- return { status: "unavailable", completedToolCallIds: [], failedToolCallIds: [] };
34692
+ async runEnsureSession(session3) {
34693
+ const args = this.buildArgs(session3, [
34694
+ "sessions",
34695
+ "new",
34696
+ "--name",
34697
+ session3.transportSession
34698
+ ]);
34699
+ const runEnsure = session3.agentCommand ? this.run : this.runWithPty;
34700
+ await runEnsure.call(this, args, {
34701
+ timeoutMs: this.sessionInitTimeoutMs,
34702
+ env: this.spawnEnvironment(session3)
34703
+ });
34798
34704
  }
34799
- let initial;
34800
- try {
34801
- initial = await readFile14(transcriptPath);
34802
- } catch {
34803
- return { status: "unavailable", transcriptPath, completedToolCallIds: [], failedToolCallIds: [] };
34705
+ async listAgentSessions(query) {
34706
+ return await runAgentSessionList({
34707
+ filterCwd: query.filterCwd,
34708
+ runList: async (includeFilterCwd) => {
34709
+ const args = this.buildAgentQueryArgs(query, "json", [
34710
+ "sessions",
34711
+ "list",
34712
+ ...includeFilterCwd && query.filterCwd ? ["--filter-cwd", query.filterCwd] : [],
34713
+ ...query.cursor ? ["--cursor", query.cursor] : []
34714
+ ]);
34715
+ return await this.runCommandWithTimeout(this.runCommand, args, {
34716
+ timeoutMs: this.sessionInitTimeoutMs,
34717
+ env: this.spawnEnvironment(query)
34718
+ });
34719
+ },
34720
+ formatError: (result) => normalizeCommandError(result) ?? `command failed with exit code ${result.code}`,
34721
+ isSubagentSession: (query.driver ?? query.agent) === CODEX_AGENT_NAME ? codexSubagentPredicate() : undefined
34722
+ });
34804
34723
  }
34805
- const initialCompleteEnd = initial.lastIndexOf(10);
34806
- const initialComplete = initial.subarray(0, initialCompleteEnd >= 0 ? initialCompleteEnd : 0).toString("utf8");
34807
- const mainCursor = {
34808
- offset: initial.length,
34809
- partial: initialCompleteEnd >= 0 ? initial.subarray(initialCompleteEnd + 1).toString("utf8") : initial.toString("utf8")
34810
- };
34811
- const initialRecords = initialComplete.split(`
34812
- `).map(parseRecord);
34813
- const launched = new Set([...options.launchedToolCallIds].filter(Boolean));
34814
- const completed = new Set;
34815
- const failed = new Set;
34816
- const toolEvents = new Map;
34817
- for (const event of options.initialToolEvents ?? [])
34818
- toolEvents.set(event.toolCallId, event);
34819
- const parentToolCallIdByAgentId = new Map;
34820
- for (const [toolCallId, agentId] of options.subagentIdsByToolCallId ?? []) {
34821
- if (toolCallId && agentId)
34822
- parentToolCallIdByAgentId.set(agentId, toolCallId);
34724
+ async tailSessionHistory(session3, lines) {
34725
+ const candidates = [
34726
+ ["sessions", "history", session3.transportSession, "--limit", String(lines)],
34727
+ ["sessions", "history", "quiet", "-s", session3.transportSession, String(lines)],
34728
+ ["sessions", "history", "quiet", session3.transportSession, String(lines)],
34729
+ ["sessions", "history", "-s", session3.transportSession, "--tail", String(lines)],
34730
+ ["sessions", "history", session3.transportSession, "--tail", String(lines)],
34731
+ ["sessions", "history", "--name", session3.transportSession, "--tail", String(lines)]
34732
+ ];
34733
+ const deadline = Date.now() + this.managementCommandTimeoutMs;
34734
+ let lastResult;
34735
+ for (const tail2 of candidates) {
34736
+ const args = this.buildArgs(session3, tail2);
34737
+ const result = await this.runCommandWithTimeout(this.runCommand, args, {
34738
+ timeoutMs: Math.max(deadline - Date.now(), 1),
34739
+ stage: "session-history",
34740
+ env: this.spawnEnvironment(session3)
34741
+ });
34742
+ if (result.code === 0) {
34743
+ return { text: result.stdout.trimEnd() };
34744
+ }
34745
+ lastResult = result;
34746
+ }
34747
+ const detail = lastResult ? normalizeCommandError(lastResult) ?? `command failed with exit code ${lastResult.code}` : "command failed";
34748
+ throw new Error(detail);
34823
34749
  }
34824
- const boundary = findPromptBoundary(initialRecords, launched);
34825
- let launchSeen = boundary >= 0 || initialRecords.some((record3) => hasTrackedLaunch(record3, launched));
34826
- let boundarySeen = boundary >= 0;
34827
- let sequence = 0;
34828
- let lastNotificationSequence = -1;
34829
- let done = false;
34830
- const emitTool = async (event) => {
34831
- toolEvents.set(event.toolCallId, event);
34832
- await options.onToolEvent?.(event);
34833
- };
34834
- const processRecord = async (record3) => {
34835
- if (!record3)
34836
- return;
34837
- sequence += 1;
34838
- const blocks = contentBlocks(record3.message?.content);
34839
- if (record3.type === "user") {
34840
- const userText = blocks.map(blockText).filter(Boolean).join(`
34841
- `);
34842
- for (const notification of taskNotifications(userText)) {
34843
- if (launched.has(notification.toolCallId)) {
34844
- completed.add(notification.toolCallId);
34845
- if (notification.failed)
34846
- failed.add(notification.toolCallId);
34847
- lastNotificationSequence = sequence;
34848
- const previous = toolEvents.get(notification.toolCallId);
34849
- if (previous) {
34850
- await emitTool({
34851
- ...previous,
34852
- status: notification.failed ? "error" : "success",
34853
- ...notification.failed ? { rawOutput: { message: "Claude background Agent failed" } } : {}
34854
- });
34855
- }
34750
+ async prompt(session3, text, reply, replyContext, options) {
34751
+ if (session3.effort?.trim()) {
34752
+ await this.reapplySessionEffort(session3, session3.effort.trim());
34753
+ }
34754
+ await this.launchMcpQueueOwnerIfNeeded(session3);
34755
+ const structuredPrompt = await createStructuredPromptFile(text, options?.media);
34756
+ const args = this.buildPromptArgs(session3, text, structuredPrompt?.filePath);
34757
+ try {
34758
+ if (reply || options?.onSegment || options?.onToolEvent || options?.onThought || options?.onPlan || options?.onUsage || options?.onCommands) {
34759
+ const effectiveReplyMode = session3.effectiveReplyMode ?? session3.replyMode;
34760
+ const formatToolCalls = (effectiveReplyMode ?? "verbose") === "verbose";
34761
+ const rawStream = effectiveReplyMode === "stream";
34762
+ let toolEventMode = resolveToolEventMode(options);
34763
+ if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
34764
+ toolEventMode = "text";
34856
34765
  }
34766
+ const { result: result2, overflowCount } = await this.runStreamingPrompt(this.command, args, reply, formatToolCalls, toolEventMode, replyContext, options?.onSegment, options?.onToolEvent, options?.onThought, options?.onPlan, options?.onUsage, options?.onCommands, rawStream, this.spawnEnvironment(session3), session3.driver ?? session3.agent);
34767
+ const baseText = getPromptText(result2);
34768
+ if (!reply) {
34769
+ return { text: baseText };
34770
+ }
34771
+ const summary = buildOverflowSummary(overflowCount);
34772
+ return { text: summary ? `${summary}
34773
+
34774
+ ${baseText}` : "" };
34857
34775
  }
34858
- for (const block of blocks) {
34859
- if (readString2(block, "type") !== "tool_result")
34860
- continue;
34861
- const toolCallId = readString2(block, "tool_use_id");
34862
- if (!toolCallId)
34863
- continue;
34864
- const output = blockText(block);
34865
- if (ASYNC_AGENT_LAUNCH.test(output))
34866
- launched.add(toolCallId);
34867
- const previous = toolEvents.get(toolCallId);
34868
- await emitTool({
34869
- toolCallId,
34870
- toolName: previous?.toolName ?? "Tool",
34871
- kind: previous?.kind ?? "other",
34872
- ...previous?.parentToolCallId ? { parentToolCallId: previous.parentToolCallId } : {},
34873
- ...previous?.isSubagent ? { isSubagent: true } : {},
34874
- ...previous?.summary ? { summary: previous.summary } : {},
34875
- ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : {},
34876
- ...output ? { rawOutput: output } : {},
34877
- status: readBoolean(block, "is_error") ? "error" : "success"
34878
- });
34879
- }
34880
- return;
34776
+ const spawnOptions = this.withSpawnEnvironment(session3);
34777
+ const result = spawnOptions ? await this.runCommand(this.command, args, spawnOptions) : await this.runCommand(this.command, args);
34778
+ return { text: getPromptText(result) };
34779
+ } finally {
34780
+ try {
34781
+ await structuredPrompt?.cleanup();
34782
+ } catch {}
34881
34783
  }
34882
- if (record3.type !== "assistant" || record3.isSidechain === true)
34883
- return;
34884
- for (const block of blocks) {
34885
- const type = readString2(block, "type");
34886
- if (type === "text") {
34887
- const text = readString2(block, "text");
34888
- if (text)
34889
- await options.onText?.(text);
34890
- } else if (type === "thinking") {
34891
- const thought = readString2(block, "thinking");
34892
- if (thought)
34893
- await options.onThought?.(thought);
34894
- } else if (type === "tool_use") {
34895
- const toolCallId = readString2(block, "id");
34896
- if (!toolCallId)
34897
- continue;
34898
- const name = readString2(block, "name") ?? "Tool";
34899
- const rawInput = block.input;
34900
- const event = {
34901
- toolCallId,
34902
- toolName: name,
34903
- kind: classifyToolKind(name),
34904
- ...toolSummary(rawInput) ? { summary: toolSummary(rawInput) } : {},
34905
- ...rawInput !== undefined ? { rawInput } : {},
34906
- status: "running"
34907
- };
34908
- await emitTool(event);
34909
- }
34784
+ }
34785
+ async setMode(session3, modeId) {
34786
+ await this.run(this.buildArgs(session3, [
34787
+ "set-mode",
34788
+ "-s",
34789
+ session3.transportSession,
34790
+ modeId
34791
+ ]), this.withSpawnEnvironment(session3, {
34792
+ timeoutMs: this.managementCommandTimeoutMs,
34793
+ stage: "set-mode"
34794
+ }));
34795
+ }
34796
+ async setModel(session3, modelId) {
34797
+ await this.run(this.buildArgs({ ...session3, model: modelId }, [
34798
+ "set",
34799
+ "-s",
34800
+ session3.transportSession,
34801
+ "model",
34802
+ modelId
34803
+ ]), this.withSpawnEnvironment({ ...session3, model: modelId }, {
34804
+ timeoutMs: this.managementCommandTimeoutMs,
34805
+ stage: "set-model"
34806
+ }));
34807
+ }
34808
+ async getSessionModel(session3) {
34809
+ const prefix = ["--format", "json", "--cwd", session3.cwd, ...this.buildPermissionArgs()];
34810
+ const tail2 = ["status", "-s", session3.transportSession];
34811
+ const args = session3.agentCommand ? [...prefix, "--agent", session3.agentCommand, ...tail2] : [...prefix, session3.agent, ...tail2];
34812
+ const result = await this.runCommandWithTimeout(this.runCommand, args, {
34813
+ timeoutMs: this.managementCommandTimeoutMs,
34814
+ stage: "get-session-model",
34815
+ env: this.spawnEnvironment(session3)
34816
+ });
34817
+ if (result.code !== 0) {
34818
+ const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34819
+ throw new Error(detail);
34910
34820
  }
34911
- if (record3.message?.stop_reason === "end_turn" && launched.size > 0 && [...launched].every((id) => completed.has(id)) && lastNotificationSequence >= 0 && sequence > lastNotificationSequence) {
34912
- done = true;
34821
+ try {
34822
+ const json = JSON.parse(result.stdout);
34823
+ return {
34824
+ current: typeof json.model === "string" ? json.model : undefined,
34825
+ available: Array.isArray(json.availableModels) ? json.availableModels.filter((m) => typeof m === "string") : []
34826
+ };
34827
+ } catch {
34828
+ return { available: [] };
34913
34829
  }
34914
- };
34915
- const processAfterBoundary = async (record3) => {
34916
- if (!record3)
34830
+ }
34831
+ async getSessionEffort(session3) {
34832
+ const output = await this.run(this.buildArgs(session3, [
34833
+ "sessions",
34834
+ "show",
34835
+ session3.transportSession
34836
+ ], "json"), this.withSpawnEnvironment(session3, {
34837
+ timeoutMs: this.managementCommandTimeoutMs,
34838
+ stage: "get-session-effort"
34839
+ }));
34840
+ const effort = parseSessionEffortRecord(output);
34841
+ return effort ? { current: effort.current, available: effort.available } : { available: [] };
34842
+ }
34843
+ async setSessionEffort(session3, effort) {
34844
+ const record3 = await this.readSessionEffortRecord(session3);
34845
+ await this.applyAdvertisedSessionEffort(session3, effort, record3);
34846
+ }
34847
+ async reapplySessionEffort(session3, effort) {
34848
+ const record3 = await this.readSessionEffortRecord(session3);
34849
+ const observed = parseSessionEffortRecord(record3);
34850
+ if (!observed?.available.includes(effort)) {
34917
34851
  return;
34918
- if (!boundarySeen) {
34919
- if (hasTrackedLaunch(record3, launched))
34920
- launchSeen = true;
34921
- if (launchSeen && isMainEndTurn(record3))
34922
- boundarySeen = true;
34852
+ }
34853
+ await this.applyAdvertisedSessionEffort(session3, effort, record3);
34854
+ }
34855
+ async readSessionEffortRecord(session3) {
34856
+ return await this.run(this.buildArgs(session3, [
34857
+ "sessions",
34858
+ "show",
34859
+ session3.transportSession
34860
+ ], "json"), this.withSpawnEnvironment(session3, {
34861
+ timeoutMs: this.managementCommandTimeoutMs,
34862
+ stage: "get-session-effort"
34863
+ }));
34864
+ }
34865
+ async applyAdvertisedSessionEffort(session3, effort, record3) {
34866
+ const advertised = requireAdvertisedSessionEffort(record3, effort);
34867
+ await this.run(this.buildArgs(session3, [
34868
+ "set",
34869
+ "-s",
34870
+ session3.transportSession,
34871
+ advertised.configId,
34872
+ effort
34873
+ ]), this.withSpawnEnvironment(session3, {
34874
+ timeoutMs: this.managementCommandTimeoutMs,
34875
+ stage: "set-session-effort"
34876
+ }));
34877
+ }
34878
+ async cancel(session3) {
34879
+ const output = await this.run(this.buildArgs(session3, [
34880
+ "cancel",
34881
+ "-s",
34882
+ session3.transportSession
34883
+ ]), this.withSpawnEnvironment(session3, {
34884
+ timeoutMs: this.managementCommandTimeoutMs,
34885
+ stage: "cancel"
34886
+ }));
34887
+ return {
34888
+ cancelled: true,
34889
+ message: output.trim()
34890
+ };
34891
+ }
34892
+ async resumeAgentSession(session3, agentSessionId) {
34893
+ this.invalidateRecordIdCache(session3);
34894
+ const args = this.buildArgs(session3, [
34895
+ "sessions",
34896
+ "new",
34897
+ "--name",
34898
+ session3.transportSession,
34899
+ "--resume-session",
34900
+ agentSessionId
34901
+ ]);
34902
+ const runResume = session3.agentCommand ? this.run : this.runWithPty;
34903
+ await runResume.call(this, args, {
34904
+ timeoutMs: this.sessionInitTimeoutMs,
34905
+ env: this.spawnEnvironment(session3)
34906
+ });
34907
+ }
34908
+ async updatePermissionPolicy(policy) {
34909
+ this.permissionMode = policy.permissionMode;
34910
+ this.nonInteractivePermissions = policy.nonInteractivePermissions;
34911
+ this.permissionPolicy = policy.permissionPolicy;
34912
+ }
34913
+ async removeSession(session3) {
34914
+ this.invalidateRecordIdCache(session3);
34915
+ const result = await this.runCommandWithTimeout(this.runCommand, this.buildArgs(session3, [
34916
+ "sessions",
34917
+ "close",
34918
+ session3.transportSession
34919
+ ]), this.withSpawnEnvironment(session3, {
34920
+ timeoutMs: this.managementCommandTimeoutMs,
34921
+ stage: "remove-session"
34922
+ }));
34923
+ if (result.code === 0) {
34923
34924
  return;
34924
34925
  }
34925
- await processRecord(record3);
34926
- };
34927
- const processSubagentRecord = async (record3, parentToolCallId) => {
34928
- if (!record3)
34926
+ if (isMissingAcpxSessionError(result.stderr, result.stdout)) {
34929
34927
  return;
34930
- const blocks = contentBlocks(record3.message?.content);
34931
- if (record3.type === "assistant") {
34932
- for (const block of blocks) {
34933
- if (readString2(block, "type") !== "tool_use")
34934
- continue;
34935
- const toolCallId = readString2(block, "id");
34936
- if (!toolCallId)
34937
- continue;
34938
- const name = readString2(block, "name") ?? "Tool";
34939
- const rawInput = block.input;
34940
- const previous = toolEvents.get(toolCallId);
34941
- await emitTool({
34942
- ...previous,
34943
- toolCallId,
34944
- parentToolCallId: previous?.parentToolCallId ?? parentToolCallId,
34945
- ...previous?.isSubagent || isAgentToolName(name) ? { isSubagent: true } : {},
34946
- toolName: previous?.toolName ?? name,
34947
- kind: previous?.kind ?? classifyToolKind(name),
34948
- ...previous?.summary ? { summary: previous.summary } : toolSummary(rawInput) ? { summary: toolSummary(rawInput) } : {},
34949
- ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : rawInput !== undefined ? { rawInput } : {},
34950
- status: previous?.status === "success" || previous?.status === "error" ? previous.status : "running"
34951
- });
34952
- }
34928
+ }
34929
+ const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34930
+ throw new Error(detail);
34931
+ }
34932
+ async deleteSession(session3) {
34933
+ let acpxRecordId;
34934
+ try {
34935
+ ({ acpxRecordId } = await this.readSessionRecord(session3));
34936
+ } catch {
34953
34937
  return;
34954
34938
  }
34955
- if (record3.type !== "user")
34939
+ await this.removeSession(session3);
34940
+ await deleteAcpxSessionFiles({ acpxRecordId });
34941
+ }
34942
+ async freeWarmProcess(session3) {
34943
+ let acpxRecordId;
34944
+ try {
34945
+ ({ acpxRecordId } = await this.readSessionRecord(session3));
34946
+ } catch {
34956
34947
  return;
34957
- for (const block of blocks) {
34958
- if (readString2(block, "type") !== "tool_result")
34959
- continue;
34960
- const toolCallId = readString2(block, "tool_use_id");
34961
- if (!toolCallId)
34962
- continue;
34963
- const previous = toolEvents.get(toolCallId);
34964
- const output = blockText(block);
34965
- const event = {
34966
- ...previous,
34967
- toolCallId,
34968
- parentToolCallId: previous?.parentToolCallId ?? parentToolCallId,
34969
- ...previous?.isSubagent ? { isSubagent: true } : {},
34970
- toolName: previous?.toolName ?? "Tool",
34971
- kind: previous?.kind ?? "other",
34972
- ...previous?.summary ? { summary: previous.summary } : {},
34973
- ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : {},
34974
- ...previous?.rawOutput !== undefined ? { rawOutput: previous.rawOutput } : output ? { rawOutput: output } : {},
34975
- status: readBoolean(block, "is_error") || previous?.status === "error" ? "error" : "success"
34976
- };
34977
- if (event.isSubagent && isClaudeAsyncAgentLaunch(event)) {
34978
- const agentId = claudeAsyncAgentId(event);
34979
- if (agentId)
34980
- parentToolCallIdByAgentId.set(agentId, event.toolCallId);
34981
- }
34982
- await emitTool(event);
34983
34948
  }
34984
- };
34985
- const subagentCursors = new Map;
34986
- const processSubagentTranscripts = async () => {
34987
- const transcripts = await findSubagentTranscripts(transcriptPath);
34988
- let advanced = false;
34989
- let discoveredNestedAgent = true;
34990
- while (discoveredNestedAgent) {
34991
- const mappedAgentCount = parentToolCallIdByAgentId.size;
34992
- for (const subagent of transcripts) {
34993
- const parentToolCallId = parentToolCallIdByAgentId.get(subagent.agentId);
34994
- if (!parentToolCallId)
34995
- continue;
34996
- const cursor = subagentCursors.get(subagent.path) ?? { offset: 0, partial: "" };
34997
- const previousOffset = cursor.offset;
34998
- const lines = await readJsonlDelta(subagent.path, cursor);
34999
- if (cursor.offset !== previousOffset)
35000
- advanced = true;
35001
- subagentCursors.set(subagent.path, cursor);
35002
- for (const line of lines)
35003
- await processSubagentRecord(parseRecord(line), parentToolCallId);
34949
+ await terminateAcpxQueueOwner(acpxRecordId);
34950
+ }
34951
+ recordIdCache = new Map;
34952
+ recordIdCacheKey(session3) {
34953
+ return JSON.stringify([session3.agent, session3.agentCommand ?? null, session3.cwd, session3.transportSession]);
34954
+ }
34955
+ invalidateRecordIdCache(session3) {
34956
+ this.recordIdCache.delete(this.recordIdCacheKey(session3));
34957
+ }
34958
+ async isSessionWarm(session3) {
34959
+ const cacheKey = this.recordIdCacheKey(session3);
34960
+ let acpxRecordId = this.recordIdCache.get(cacheKey);
34961
+ if (!acpxRecordId) {
34962
+ try {
34963
+ ({ acpxRecordId } = await this.readSessionRecord(session3));
34964
+ } catch {
34965
+ return false;
35004
34966
  }
35005
- discoveredNestedAgent = parentToolCallIdByAgentId.size > mappedAgentCount;
34967
+ this.recordIdCache.set(cacheKey, acpxRecordId);
35006
34968
  }
35007
- return advanced;
35008
- };
35009
- await processSubagentTranscripts();
35010
- if (boundary >= 0) {
35011
- for (const record3 of initialRecords.slice(boundary + 1)) {
35012
- await processRecord(record3);
35013
- if (done)
35014
- break;
34969
+ const pid = await readQueueOwnerPid(acpxRecordId);
34970
+ if (pid === undefined) {
34971
+ return false;
35015
34972
  }
34973
+ return isProcessAlive(pid);
35016
34974
  }
35017
- const startedAt = Date.now();
35018
- const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
35019
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
35020
- while (!done) {
35021
- throwIfAborted(options.signal);
35022
- if (Date.now() - startedAt >= timeoutMs) {
35023
- return {
35024
- status: "timeout",
35025
- transcriptPath,
35026
- completedToolCallIds: [...completed],
35027
- failedToolCallIds: [...failed]
35028
- };
35029
- }
35030
- await waitForPoll(pollIntervalMs, options.signal);
35031
- await processSubagentTranscripts();
35032
- const lines = await readJsonlDelta(transcriptPath, mainCursor);
35033
- for (const line of lines) {
35034
- await processAfterBoundary(parseRecord(line));
35035
- if (done)
35036
- break;
34975
+ async hasSession(session3) {
34976
+ const result = await this.runCommandWithTimeout(this.runCommand, this.buildArgs(session3, [
34977
+ "sessions",
34978
+ "show",
34979
+ session3.transportSession
34980
+ ]), this.withSpawnEnvironment(session3, {
34981
+ timeoutMs: this.managementCommandTimeoutMs,
34982
+ stage: "has-session"
34983
+ }));
34984
+ return result.code === 0;
34985
+ }
34986
+ async launchMcpQueueOwnerIfNeeded(session3) {
34987
+ if (!session3.mcpCoordinatorSession) {
34988
+ return;
35037
34989
  }
34990
+ const record3 = await this.readSessionRecord(session3);
34991
+ const env = this.spawnEnvironment(session3);
34992
+ await this.queueOwnerLauncher.launch({
34993
+ acpxRecordId: record3.acpxRecordId,
34994
+ coordinatorSession: session3.mcpCoordinatorSession,
34995
+ ...session3.mcpSourceHandle ? { sourceHandle: session3.mcpSourceHandle } : {},
34996
+ permissionMode: this.permissionMode,
34997
+ nonInteractivePermissions: this.nonInteractivePermissions,
34998
+ ...session3.model?.trim() ? { sessionOptions: { model: session3.model.trim() } } : {},
34999
+ ...env ? { env } : {}
35000
+ });
35038
35001
  }
35039
- if (parentToolCallIdByAgentId.size > 0) {
35040
- let stablePolls = 0;
35041
- for (let poll = 0;poll < FINAL_SUBAGENT_DRAIN_MAX_POLLS && stablePolls < FINAL_SUBAGENT_DRAIN_STABLE_POLLS; poll += 1) {
35042
- await waitForPoll(pollIntervalMs, options.signal);
35043
- stablePolls = await processSubagentTranscripts() ? 0 : stablePolls + 1;
35002
+ async readSessionRecord(session3) {
35003
+ const result = await this.runCommandWithTimeout(this.runCommand, this.buildArgs(session3, [
35004
+ "sessions",
35005
+ "show",
35006
+ session3.transportSession
35007
+ ], "json"), this.withSpawnEnvironment(session3, {
35008
+ timeoutMs: this.managementCommandTimeoutMs,
35009
+ stage: "read-session-record"
35010
+ }));
35011
+ if (result.code !== 0) {
35012
+ const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
35013
+ throw new Error(detail);
35044
35014
  }
35015
+ const record3 = parseAcpxSessionRecordId(result.stdout);
35016
+ if (record3)
35017
+ return record3;
35018
+ throw new Error("failed to resolve acpx session record id");
35045
35019
  }
35046
- return {
35047
- status: "completed",
35048
- transcriptPath,
35049
- completedToolCallIds: [...completed],
35050
- failedToolCallIds: [...failed]
35051
- };
35052
- }
35053
- async function readJsonlDelta(path14, cursor) {
35054
- let size;
35055
- try {
35056
- size = (await stat3(path14)).size;
35057
- } catch {
35058
- return [];
35020
+ async getAgentSessionId(session3) {
35021
+ const record3 = await this.readSessionRecord(session3);
35022
+ return record3.agentSessionId;
35059
35023
  }
35060
- if (size < cursor.offset) {
35061
- cursor.offset = 0;
35062
- cursor.partial = "";
35024
+ async run(args, options) {
35025
+ const result = await this.runCommandWithTimeout(this.runCommand, args, options);
35026
+ if (result.code !== 0) {
35027
+ const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
35028
+ throw new Error(detail);
35029
+ }
35030
+ return result.stdout;
35063
35031
  }
35064
- if (size <= cursor.offset)
35065
- return [];
35066
- let chunk;
35067
- try {
35068
- const handle = await open5(path14, "r");
35069
- try {
35070
- chunk = Buffer.alloc(size - cursor.offset);
35071
- const result = await handle.read(chunk, 0, chunk.length, cursor.offset);
35072
- chunk = chunk.subarray(0, result.bytesRead);
35073
- } finally {
35074
- await handle.close();
35032
+ async runWithPty(args, options) {
35033
+ const result = await this.runCommandWithTimeout(this.runPtyCommand, args, options);
35034
+ if (result.code !== 0) {
35035
+ const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
35036
+ throw new Error(detail);
35075
35037
  }
35076
- } catch {
35077
- return [];
35038
+ return result.stdout;
35078
35039
  }
35079
- cursor.offset += chunk.length;
35080
- cursor.partial += chunk.toString("utf8");
35081
- const lines = cursor.partial.split(`
35082
- `);
35083
- cursor.partial = lines.pop() ?? "";
35084
- return lines;
35085
- }
35086
- function findPromptBoundary(records, launched) {
35087
- let launchIndex = -1;
35088
- for (let index = 0;index < records.length; index += 1) {
35089
- const record3 = records[index];
35090
- if (record3?.type !== "assistant" || record3.isSidechain === true)
35091
- continue;
35092
- for (const block of contentBlocks(record3.message?.content)) {
35093
- if (readString2(block, "type") === "tool_use" && launched.has(readString2(block, "id") ?? "")) {
35094
- launchIndex = index;
35095
- }
35040
+ async runCommandWithTimeout(runner, args, options) {
35041
+ const spawnSpec = resolveSpawnCommand(this.command, args);
35042
+ if (!options?.timeoutMs) {
35043
+ return await runner(spawnSpec.command, spawnSpec.args, options);
35096
35044
  }
35045
+ const abortController = new AbortController;
35046
+ let timeoutId;
35047
+ return await Promise.race([
35048
+ runner(spawnSpec.command, spawnSpec.args, { ...options, signal: abortController.signal }).finally(() => {
35049
+ if (timeoutId)
35050
+ clearTimeout(timeoutId);
35051
+ }),
35052
+ new Promise((_, reject) => {
35053
+ timeoutId = setTimeout(() => {
35054
+ reject(managementTimeoutError(args, options));
35055
+ abortController.abort();
35056
+ }, options.timeoutMs);
35057
+ })
35058
+ ]);
35059
+ }
35060
+ async runStreamingPrompt(command, args, reply, formatToolCalls = false, toolEventMode = "text", replyContext, onSegment, onToolEvent, onThought, onPlan, onUsage, onCommands, rawStream = false, env, driver) {
35061
+ const hooks = this.streamingHooks;
35062
+ const doSpawn = hooks.spawnPrompt ?? ((cmd, spawnArgs) => spawn11(cmd, spawnArgs, {
35063
+ env,
35064
+ stdio: ["ignore", "pipe", "pipe"]
35065
+ }));
35066
+ const setIntervalFn = hooks.setIntervalFn ?? ((fn, delay) => setInterval(fn, delay));
35067
+ const clearIntervalFn = hooks.clearIntervalFn ?? ((timer) => clearInterval(timer));
35068
+ const maxSegmentWaitMs = hooks.maxSegmentWaitMs ?? (rawStream ? 200 : 30000);
35069
+ const flushCheckIntervalMs = hooks.flushCheckIntervalMs ?? (rawStream ? 80 : 5000);
35070
+ const now = hooks.now ?? (() => Date.now());
35071
+ return await new Promise((resolve4, reject) => {
35072
+ const spawnSpec = resolveSpawnCommand(command, args);
35073
+ const child = doSpawn(spawnSpec.command, spawnSpec.args);
35074
+ let stdout2 = "";
35075
+ let stderr = "";
35076
+ let lastReplyAt = now();
35077
+ let segmentChain = Promise.resolve();
35078
+ let segmentError;
35079
+ let toolEventChain = Promise.resolve();
35080
+ let toolEventError;
35081
+ let thoughtChain = Promise.resolve();
35082
+ let thoughtError;
35083
+ let planChain = Promise.resolve();
35084
+ let planError;
35085
+ let usageChain = Promise.resolve();
35086
+ let usageError;
35087
+ let commandsChain = Promise.resolve();
35088
+ let commandsError;
35089
+ const userOnToolEvent = onToolEvent;
35090
+ const userOnThought = onThought;
35091
+ const userOnPlan = onPlan;
35092
+ const userOnUsage = onUsage;
35093
+ const userOnCommands = onCommands;
35094
+ const state = createStreamingPromptState(formatToolCalls, {
35095
+ mode: toolEventMode,
35096
+ driver,
35097
+ rawStream,
35098
+ ...userOnToolEvent ? {
35099
+ onToolEvent: (event) => {
35100
+ toolEventChain = toolEventChain.then(() => userOnToolEvent(event)).catch((error2) => {
35101
+ toolEventError ??= error2;
35102
+ });
35103
+ }
35104
+ } : {},
35105
+ ...userOnThought ? {
35106
+ onThought: (chunk) => {
35107
+ thoughtChain = thoughtChain.then(() => userOnThought(chunk)).catch((error2) => {
35108
+ thoughtError ??= error2;
35109
+ });
35110
+ }
35111
+ } : {},
35112
+ ...userOnPlan ? {
35113
+ onPlan: (entries) => {
35114
+ planChain = planChain.then(() => userOnPlan(entries)).catch((error2) => {
35115
+ planError ??= error2;
35116
+ });
35117
+ }
35118
+ } : {},
35119
+ ...userOnUsage ? {
35120
+ onUsage: (usage) => {
35121
+ usageChain = usageChain.then(() => userOnUsage(usage)).catch((error2) => {
35122
+ usageError ??= error2;
35123
+ });
35124
+ }
35125
+ } : {},
35126
+ ...userOnCommands ? {
35127
+ onCommands: (commands) => {
35128
+ commandsChain = commandsChain.then(() => userOnCommands(commands)).catch((error2) => {
35129
+ commandsError ??= error2;
35130
+ });
35131
+ }
35132
+ } : {}
35133
+ });
35134
+ const sink2 = reply ? rawStream ? createVerbatimReplySink(reply) : createQuotaGatedReplySink({
35135
+ reply,
35136
+ ...replyContext ? { replyContext } : {}
35137
+ }) : null;
35138
+ const feedSegment = (segment) => {
35139
+ if (onSegment) {
35140
+ segmentChain = segmentChain.then(() => onSegment(segment)).catch((error2) => {
35141
+ segmentError ??= error2;
35142
+ });
35143
+ }
35144
+ sink2?.feedSegment(segment);
35145
+ lastReplyAt = now();
35146
+ };
35147
+ const flushBuffer = () => {
35148
+ const remaining = rawStream ? state.buffer : state.buffer.trim();
35149
+ if (remaining.length > 0) {
35150
+ state.buffer = "";
35151
+ feedSegment(remaining);
35152
+ }
35153
+ };
35154
+ const timer = setIntervalFn(() => {
35155
+ if (state.buffer.trim().length > 0 && now() - lastReplyAt >= maxSegmentWaitMs) {
35156
+ flushBuffer();
35157
+ }
35158
+ }, flushCheckIntervalMs);
35159
+ child.stdout.setEncoding("utf8");
35160
+ child.stdout.on("data", (chunk) => {
35161
+ stdout2 += String(chunk);
35162
+ parseStreamingDataChunk(state, String(chunk));
35163
+ for (const segment of state.segments.splice(0)) {
35164
+ feedSegment(segment);
35165
+ }
35166
+ });
35167
+ child.stderr.on("data", (chunk) => {
35168
+ stderr += String(chunk);
35169
+ });
35170
+ child.on("error", (err) => {
35171
+ clearIntervalFn(timer);
35172
+ reject(err);
35173
+ });
35174
+ child.on("close", (code) => {
35175
+ clearIntervalFn(timer);
35176
+ const remaining = state.finalize();
35177
+ if (remaining.length > 0) {
35178
+ feedSegment(remaining);
35179
+ }
35180
+ const { overflowCount } = sink2?.finalize() ?? { overflowCount: 0 };
35181
+ Promise.all([
35182
+ sink2?.drain({ timeoutMs: 30000 }) ?? Promise.resolve(),
35183
+ segmentChain,
35184
+ toolEventChain,
35185
+ thoughtChain,
35186
+ planChain,
35187
+ usageChain,
35188
+ commandsChain
35189
+ ]).then(() => {
35190
+ const deferred = sink2?.getPendingError();
35191
+ if (deferred) {
35192
+ reject(deferred);
35193
+ return;
35194
+ }
35195
+ if (segmentError) {
35196
+ reject(segmentError);
35197
+ return;
35198
+ }
35199
+ if (toolEventError) {
35200
+ reject(toolEventError);
35201
+ return;
35202
+ }
35203
+ if (thoughtError) {
35204
+ reject(thoughtError);
35205
+ return;
35206
+ }
35207
+ if (planError) {
35208
+ reject(planError);
35209
+ return;
35210
+ }
35211
+ if (usageError) {
35212
+ reject(usageError);
35213
+ return;
35214
+ }
35215
+ if (commandsError) {
35216
+ reject(commandsError);
35217
+ return;
35218
+ }
35219
+ resolve4({
35220
+ result: { code: code ?? 1, stdout: stdout2, stderr },
35221
+ overflowCount
35222
+ });
35223
+ }).catch((error2) => {
35224
+ reject(error2);
35225
+ });
35226
+ });
35227
+ });
35097
35228
  }
35098
- if (launchIndex < 0)
35099
- return -1;
35100
- for (let index = launchIndex + 1;index < records.length; index += 1) {
35101
- const record3 = records[index];
35102
- if (record3?.type === "assistant" && record3.isSidechain !== true && record3.message?.stop_reason === "end_turn") {
35103
- return index;
35104
- }
35229
+ permissionInput() {
35230
+ return {
35231
+ permissionMode: this.permissionMode,
35232
+ nonInteractivePermissions: this.nonInteractivePermissions,
35233
+ permissionPolicy: this.permissionPolicy
35234
+ };
35105
35235
  }
35106
- return -1;
35107
- }
35108
- function hasTrackedLaunch(record3, launched) {
35109
- if (record3?.type !== "assistant" || record3.isSidechain === true)
35110
- return false;
35111
- return contentBlocks(record3.message?.content).some((block) => readString2(block, "type") === "tool_use" && launched.has(readString2(block, "id") ?? ""));
35112
- }
35113
- function isMainEndTurn(record3) {
35114
- return record3.type === "assistant" && record3.isSidechain !== true && record3.message?.stop_reason === "end_turn";
35115
- }
35116
- function parseRecord(line) {
35117
- if (!line.trim())
35118
- return;
35119
- try {
35120
- return JSON.parse(line);
35121
- } catch {
35122
- return;
35236
+ spawnEnvironment(input) {
35237
+ return this.resolveSpawnEnvironment(input);
35123
35238
  }
35124
- }
35125
- function contentBlocks(content) {
35126
- if (typeof content === "string")
35127
- return [{ type: "text", text: content }];
35128
- return Array.isArray(content) ? content.filter((block) => !!block && typeof block === "object") : [];
35129
- }
35130
- function blockText(block) {
35131
- if (typeof block.text === "string")
35132
- return block.text;
35133
- if (typeof block.content === "string")
35134
- return block.content;
35135
- if (Array.isArray(block.content))
35136
- return block.content.map((item) => textOfUnknown(item)).filter(Boolean).join(`
35137
- `);
35138
- return textOfUnknown(block.content);
35139
- }
35140
- function textOfUnknown(value) {
35141
- if (typeof value === "string")
35142
- return value;
35143
- if (Array.isArray(value))
35144
- return value.map(textOfUnknown).filter(Boolean).join(`
35145
- `);
35146
- if (!value || typeof value !== "object")
35147
- return "";
35148
- const object4 = value;
35149
- if (typeof object4.text === "string")
35150
- return object4.text;
35151
- if (object4.content !== undefined)
35152
- return textOfUnknown(object4.content);
35153
- try {
35154
- return JSON.stringify(value);
35155
- } catch {
35156
- return "";
35239
+ withSpawnEnvironment(input, options) {
35240
+ const env = this.spawnEnvironment(input);
35241
+ return env ? { ...options ?? {}, env } : options;
35157
35242
  }
35158
- }
35159
- function findStringField(value, keys) {
35160
- if (!value || typeof value !== "object")
35161
- return;
35162
- if (Array.isArray(value)) {
35163
- for (const item of value) {
35164
- const found = findStringField(item, keys);
35165
- if (found)
35166
- return found;
35167
- }
35168
- return;
35243
+ sessionInput(session3) {
35244
+ return {
35245
+ agent: session3.agent,
35246
+ agentCommand: session3.agentCommand,
35247
+ cwd: session3.cwd,
35248
+ model: session3.model,
35249
+ permission: this.permissionInput()
35250
+ };
35169
35251
  }
35170
- const object4 = value;
35171
- for (const key of keys) {
35172
- const candidate = object4[key];
35173
- if (typeof candidate === "string" && candidate.trim())
35174
- return candidate.trim();
35252
+ buildArgs(session3, tail2, format = "quiet") {
35253
+ return buildSessionArgs(this.sessionInput(session3), tail2, { format });
35175
35254
  }
35176
- for (const candidate of Object.values(object4)) {
35177
- const found = findStringField(candidate, keys);
35178
- if (found)
35179
- return found;
35255
+ buildAgentQueryArgs(query, format, tail2) {
35256
+ return buildAgentQueryArgs({ agent: query.agent, agentCommand: query.agentCommand, cwd: query.cwd, permission: this.permissionInput() }, format, tail2);
35180
35257
  }
35181
- return;
35182
- }
35183
- function taskNotifications(text) {
35184
- const notifications = [];
35185
- const blocks = [...text.matchAll(/<task-notification>([\s\S]*?)<\/task-notification>/gi)].map((match) => match[1] ?? "");
35186
- for (const block of blocks.length > 0 ? blocks : [text]) {
35187
- const toolCallId = /<tool-use-id>([^<]+)<\/tool-use-id>/i.exec(block)?.[1]?.trim();
35188
- if (!toolCallId)
35189
- continue;
35190
- const status = /<status>([^<]+)<\/status>/i.exec(block)?.[1]?.trim().toLowerCase();
35191
- notifications.push({
35192
- toolCallId,
35193
- failed: status === "failed" || status === "error" || status === "cancelled" || status === "canceled"
35194
- });
35258
+ buildPromptArgs(session3, text, promptFile) {
35259
+ const tail2 = promptFile ? ["prompt", "-s", session3.transportSession, "--file", promptFile] : ["prompt", "-s", session3.transportSession, text];
35260
+ return buildPromptArgs({ ...this.sessionInput(session3), queueOwnerTtlSeconds: this.queueOwnerTtlSeconds }, tail2);
35261
+ }
35262
+ buildPermissionArgs() {
35263
+ return buildPermissionArgs(this.permissionInput());
35195
35264
  }
35196
- return notifications;
35197
- }
35198
- function classifyToolKind(name) {
35199
- const normalized = name.toLowerCase();
35200
- if (/(read|view|open)/.test(normalized))
35201
- return "read";
35202
- if (/(grep|search|find|glob)/.test(normalized))
35203
- return "search";
35204
- if (/(edit|write|patch|replace|create)/.test(normalized))
35205
- return "edit";
35206
- if (/(bash|shell|exec|run|terminal|command)/.test(normalized))
35207
- return "execute";
35208
- if (/(think|reason|plan|agent|task)/.test(normalized))
35209
- return "think";
35210
- return "other";
35211
- }
35212
- function isAgentToolName(name) {
35213
- const normalized = name.trim().toLowerCase();
35214
- return normalized === "agent" || normalized === "task";
35215
35265
  }
35216
- async function findSubagentTranscripts(transcriptPath) {
35217
- const sessionDir = join23(dirname13(transcriptPath), basename3(transcriptPath, ".jsonl"), "subagents");
35218
- const found = [];
35219
- const visit = async (directory) => {
35220
- let entries;
35221
- try {
35222
- entries = await readdir4(directory, { withFileTypes: true });
35223
- } catch {
35224
- return;
35266
+ function renderCommandForError(args) {
35267
+ const rendered = [];
35268
+ for (let index = 0;index < args.length; index += 1) {
35269
+ const arg = args[index];
35270
+ if (arg === undefined) {
35271
+ continue;
35225
35272
  }
35226
- for (const entry of entries) {
35227
- const path14 = join23(directory, entry.name);
35228
- if (entry.isDirectory()) {
35229
- await visit(path14);
35230
- } else if (entry.isFile() && entry.name.startsWith("agent-") && entry.name.endsWith(".jsonl")) {
35231
- found.push({ agentId: entry.name.slice("agent-".length, -".jsonl".length), path: path14 });
35232
- }
35273
+ if (arg === "--format") {
35274
+ index += 1;
35275
+ continue;
35233
35276
  }
35234
- };
35235
- await visit(sessionDir);
35236
- return found;
35237
- }
35238
- function toolSummary(input) {
35239
- if (!input || typeof input !== "object")
35240
- return;
35241
- const object4 = input;
35242
- for (const key of ["description", "summary", "path", "file_path", "query", "pattern", "command"]) {
35243
- if (typeof object4[key] === "string" && object4[key].trim())
35244
- return object4[key].trim();
35245
- }
35246
- return;
35247
- }
35248
- function readString2(object4, key) {
35249
- if (!object4 || typeof object4 !== "object")
35250
- return;
35251
- const value = object4[key];
35252
- return typeof value === "string" ? value : undefined;
35253
- }
35254
- function readBoolean(object4, key) {
35255
- return !!object4 && typeof object4 === "object" && object4[key] === true;
35256
- }
35257
- function encodeClaudeProjectDirectory(cwd) {
35258
- return cwd.replace(/[:\\/]/g, "-");
35259
- }
35260
- async function exists(path14) {
35261
- try {
35262
- await access4(path14, fsConstants.R_OK);
35263
- return true;
35264
- } catch {
35265
- return false;
35277
+ if (arg === "--cwd") {
35278
+ index += 1;
35279
+ continue;
35280
+ }
35281
+ rendered.push(/\s/.test(arg) || arg.includes(":") ? `"${arg}"` : arg);
35266
35282
  }
35283
+ return rendered.join(" ");
35267
35284
  }
35268
- function throwIfAborted(signal) {
35269
- if (signal?.aborted)
35270
- throw new DOMException("Claude background follow-up aborted", "AbortError");
35271
- }
35272
- async function waitForPoll(ms, signal) {
35273
- if (!signal) {
35274
- await new Promise((resolve4) => setTimeout(resolve4, ms));
35275
- return;
35285
+ function resolveChildEnvironment(baseEnv, explicitEnv, locale) {
35286
+ const source = explicitEnv ?? baseEnv;
35287
+ const env = {};
35288
+ for (const [key, value] of Object.entries(source)) {
35289
+ if (typeof value === "string")
35290
+ env[key] = value;
35276
35291
  }
35277
- throwIfAborted(signal);
35278
- await new Promise((resolve4, reject) => {
35279
- const timer = setTimeout(() => {
35280
- signal.removeEventListener("abort", onAbort);
35281
- resolve4();
35282
- }, ms);
35283
- const onAbort = () => {
35284
- clearTimeout(timer);
35285
- reject(new DOMException("Claude background follow-up aborted", "AbortError"));
35286
- };
35287
- signal.addEventListener("abort", onAbort, { once: true });
35288
- });
35292
+ env.XACPX_LANG = locale;
35293
+ return env;
35289
35294
  }
35290
- var DEFAULT_POLL_INTERVAL_MS = 250, DEFAULT_TIMEOUT_MS, FINAL_SUBAGENT_DRAIN_MAX_POLLS = 4, FINAL_SUBAGENT_DRAIN_STABLE_POLLS = 2, ASYNC_AGENT_LAUNCH;
35291
- var init_claude_background_followup = __esm(() => {
35292
- DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
35293
- ASYNC_AGENT_LAUNCH = /Async agent launched successfully|["']?status["']?\s*:\s*["']async_launched["']/i;
35295
+ var require4;
35296
+ var init_acpx_cli_transport = __esm(() => {
35297
+ init_spawn_command();
35298
+ init_claude_settings_policy();
35299
+ init_i18n();
35300
+ init_prompt_output();
35301
+ init_prompt_media();
35302
+ init_streaming_prompt();
35303
+ init_quota_gated_reply_sink();
35304
+ init_node_pty_helper();
35305
+ init_terminate_process_tree();
35306
+ init_acpx_queue_owner_launcher();
35307
+ init_daemon_files();
35308
+ init_agent_session_list();
35309
+ init_codex_subagent_filter();
35310
+ init_acpx_session_files();
35311
+ init_session_effort();
35312
+ init_command_timeouts();
35313
+ init_acpx_command_builder();
35314
+ require4 = createRequire5(import.meta.url);
35294
35315
  });
35295
35316
 
35296
35317
  // src/transport/claude-background-followup-transport.ts
@@ -35308,7 +35329,7 @@ function createClaudeBackgroundFollowupTransport(delegate, options) {
35308
35329
  pendingToolEvents.set(event.toolCallId, event);
35309
35330
  else
35310
35331
  pendingToolEvents.delete(event.toolCallId);
35311
- if (isClaudeAsyncAgentLaunch(event)) {
35332
+ if (event.isSubagent && isClaudeAsyncAgentLaunch(event)) {
35312
35333
  asyncToolCallIds.add(event.toolCallId);
35313
35334
  const agentId = claudeAsyncAgentId(event);
35314
35335
  if (agentId)
@@ -35320,31 +35341,33 @@ function createClaudeBackgroundFollowupTransport(delegate, options) {
35320
35341
  ...promptOptions,
35321
35342
  ...forwardToolEvent ? { onToolEvent: forwardToolEvent } : {}
35322
35343
  });
35323
- const isClaude = (options.resolveDriver?.(session3.agent) ?? session3.agent) === "claude";
35324
- if (!isClaude || asyncToolCallIds.size === 0 || !reply || !forwardToolEvent)
35344
+ const driver = (options.resolveDriver?.(session3.agent) ?? session3.agent).trim().toLowerCase();
35345
+ const supportsFollowup = driver === "claude" || driver === "qoder";
35346
+ if (!supportsFollowup || asyncToolCallIds.size === 0 || !reply || !forwardToolEvent)
35325
35347
  return result;
35326
35348
  let agentSessionId = session3.agentSessionId;
35327
35349
  if (!agentSessionId) {
35328
35350
  try {
35329
35351
  agentSessionId = await delegate.getAgentSessionId?.(session3);
35330
35352
  } catch (error2) {
35331
- await options.logger.error("transport.claude_background_followup.session_id_failed", "failed to resolve Claude session id for background follow-up", { alias: session3.alias, error: error2 instanceof Error ? error2.message : String(error2) });
35353
+ await options.logger.error("transport.claude_background_followup.session_id_failed", "failed to resolve agent session id for background follow-up", { alias: session3.alias, error: error2 instanceof Error ? error2.message : String(error2) });
35332
35354
  }
35333
35355
  }
35334
35356
  if (!agentSessionId) {
35335
35357
  for (const event of [...pendingToolEvents.values()]) {
35336
35358
  await forwardToolEvent({
35337
35359
  ...event,
35338
- rawOutput: { message: "Claude background continuation could not be tracked: session id unavailable" },
35360
+ rawOutput: { message: "background continuation could not be tracked: session id unavailable" },
35339
35361
  status: "error"
35340
35362
  });
35341
35363
  }
35342
35364
  return result;
35343
35365
  }
35344
- await options.logger.info("transport.claude_background_followup.started", "following Claude background-agent continuation", { alias: session3.alias, agentSessionId, taskCount: asyncToolCallIds.size });
35366
+ await options.logger.info("transport.claude_background_followup.started", "following background-agent continuation", { alias: session3.alias, agentSessionId, driver, taskCount: asyncToolCallIds.size });
35345
35367
  const followup = await followBackgroundTurn({
35346
35368
  cwd: session3.cwd,
35347
35369
  sessionId: agentSessionId,
35370
+ driver,
35348
35371
  launchedToolCallIds: asyncToolCallIds,
35349
35372
  initialToolEvents: latestToolEvents.values(),
35350
35373
  subagentIdsByToolCallId,
@@ -35373,12 +35396,13 @@ function createClaudeBackgroundFollowupTransport(delegate, options) {
35373
35396
  await forwardToolEvent({
35374
35397
  ...event,
35375
35398
  status: followup.status === "completed" && !failed ? "success" : "error",
35376
- ...followup.status === "completed" && !failed ? {} : { rawOutput: { message: failed ? "Claude background Agent failed" : `Claude background continuation ${followup.status}` } }
35399
+ ...followup.status === "completed" && !failed ? {} : { rawOutput: { message: failed ? "background Agent failed" : `background continuation ${followup.status}` } }
35377
35400
  });
35378
35401
  }
35379
- await options.logger.info(`transport.claude_background_followup.${followup.status}`, `Claude background-agent follow-up ${followup.status}`, {
35402
+ await options.logger.info(`transport.claude_background_followup.${followup.status}`, `background-agent follow-up ${followup.status}`, {
35380
35403
  alias: session3.alias,
35381
35404
  agentSessionId,
35405
+ driver,
35382
35406
  completedTaskCount: followup.completedToolCallIds.length,
35383
35407
  failedTaskCount: followup.failedToolCallIds.length
35384
35408
  });