@ganglion/xacpx 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -33177,2125 +33177,2146 @@ 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/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 isAsyncAgentLaunch(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 asyncAgentId(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 findNativeTranscriptPath(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 followBackgroundTurn(options) {
33234
+ const transcriptPath = options.transcriptPath ?? await findNativeTranscriptPath(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";
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);
33348
+ }
33349
+ }
33350
+ if (record3.message?.stop_reason === "end_turn" && launched.size > 0 && [...launched].every((id) => completed.has(id)) && lastNotificationSequence >= 0 && sequence > lastNotificationSequence) {
33351
+ done = true;
33424
33352
  }
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
33353
  };
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);
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;
33362
+ return;
33363
+ }
33364
+ await processRecord(record3);
33365
+ };
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
+ });
33485
33391
  }
33392
+ return;
33486
33393
  }
33487
- if (parts.length > 0) {
33488
- return parts.join(" ");
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 && isAsyncAgentLaunch(event)) {
33417
+ const agentId = asyncAgentId(event);
33418
+ if (agentId)
33419
+ parentToolCallIdByAgentId.set(agentId, event.toolCallId);
33420
+ }
33421
+ await emitTool(event);
33422
+ }
33423
+ };
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;
33489
33454
  }
33490
33455
  }
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}`;
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
+ }
33513
33477
  }
33514
- if (subagentType)
33515
- return subagentType;
33516
- 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
+ }
33484
+ }
33485
+ return {
33486
+ status: "completed",
33487
+ transcriptPath,
33488
+ completedToolCallIds: [...completed],
33489
+ failedToolCallIds: [...failed]
33490
+ };
33517
33491
  }
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();
33492
+ async function readJsonlDelta(path14, cursor) {
33493
+ let size;
33494
+ try {
33495
+ size = (await stat3(path14)).size;
33496
+ } catch {
33497
+ return [];
33498
+ }
33499
+ if (size < cursor.offset) {
33500
+ cursor.offset = 0;
33501
+ cursor.partial = "";
33502
+ }
33503
+ if (size <= cursor.offset)
33504
+ return [];
33505
+ let chunk;
33506
+ try {
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();
33523
33514
  }
33515
+ } catch {
33516
+ return [];
33524
33517
  }
33525
- return;
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;
33526
33524
  }
33527
- function readFirstStringArray(record3, keys) {
33528
- for (const key of keys) {
33529
- const value = record3[key];
33530
- if (!Array.isArray(value))
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)
33531
33530
  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;
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
33535
  }
33536
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;
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
+ }
33547
33544
  }
33548
- return;
33545
+ return -1;
33549
33546
  }
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;
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") ?? ""));
33560
33551
  }
33561
- function normalizeUsageCost(value) {
33562
- if (!isRecord4(value))
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())
33563
33557
  return;
33564
- const amount = asFiniteNumber(value.amount);
33565
- const currency = readString(value, "currency");
33566
- if (amount === undefined && !currency)
33558
+ try {
33559
+ return JSON.parse(line);
33560
+ } catch {
33567
33561
  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
33562
  }
33583
- return out;
33584
33563
  }
33585
- function isRecord4(value) {
33586
- return typeof value === "object" && value !== null && !Array.isArray(value);
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") : [];
33587
33568
  }
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;
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);
33593
33578
  }
33594
- function truncateToolDisplay(text) {
33595
- return text.length > 60 ? `${text.slice(0, 57)}...` : text;
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
+ }
33596
33597
  }
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;
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;
33601
33608
  }
33602
- if (kind === "search" && ["grep", "rg", "search"].includes(normalizedTitle)) {
33603
- return true;
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();
33604
33614
  }
33605
- if (kind === "read" && ["read", "cat"].includes(normalizedTitle)) {
33606
- return true;
33615
+ for (const candidate of Object.values(object4)) {
33616
+ const found = findStringField(candidate, keys);
33617
+ if (found)
33618
+ return found;
33607
33619
  }
33608
- return false;
33620
+ return;
33609
33621
  }
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;
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
+ });
33629
33634
  }
33630
- return join20(dirname12(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
33635
+ return notifications;
33631
33636
  }
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") {
33640
- return;
33641
- }
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");
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";
33665
33650
  }
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);
33651
+ function isAgentToolName(name) {
33652
+ const normalized = name.trim().toLowerCase();
33653
+ return normalized === "agent" || normalized === "task";
33677
33654
  }
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;
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;
33686
33660
  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));
33661
+ entries = await readdir3(directory, { withFileTypes: true });
33698
33662
  } catch {
33699
- result = false;
33663
+ return;
33700
33664
  }
33701
- cache.set(sessionId, result);
33702
- return result;
33703
- };
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;
33713
- }
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
- }
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 });
33770
33671
  }
33771
33672
  }
33772
33673
  };
33674
+ await visit(sessionDir);
33675
+ return found;
33773
33676
  }
33774
- function codexSubagentPredicate(env) {
33775
- return createSubagentPredicate(nodeRolloutReader(resolveCodexHome(env)));
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();
33684
+ }
33685
+ return;
33776
33686
  }
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");
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;
33787
33692
  }
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;
33798
- }
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
- }
33805
- return parsed;
33693
+ function readBoolean(object4, key) {
33694
+ return !!object4 && typeof object4 === "object" && object4[key] === true;
33806
33695
  }
33807
- function parseAgentSessionListOutput(stdout2, filterCwd) {
33808
- let parsed;
33696
+ function encodeClaudeProjectDirectory(cwd) {
33697
+ return cwd.replace(/[:\\/]/g, "-");
33698
+ }
33699
+ async function exists(path14) {
33809
33700
  try {
33810
- parsed = JSON.parse(stdout2);
33701
+ await access4(path14, fsConstants.R_OK);
33702
+ return true;
33811
33703
  } catch {
33812
- throw new Error("failed to parse acpx sessions list output");
33704
+ return false;
33813
33705
  }
33814
- if (!isAgentSessionListResult(parsed)) {
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));
33815
33714
  return;
33816
33715
  }
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";
33716
+ throwIfAborted(signal);
33717
+ await new Promise((resolve4, reject) => {
33718
+ const timer = setTimeout(() => {
33719
+ signal.removeEventListener("abort", onAbort);
33720
+ resolve4();
33721
+ }, ms);
33722
+ const onAbort = () => {
33723
+ clearTimeout(timer);
33724
+ reject(new DOMException("background follow-up aborted", "AbortError"));
33725
+ };
33726
+ signal.addEventListener("abort", onAbort, { once: true });
33830
33727
  });
33831
33728
  }
33832
- function filterAgentSessionListByCwd(result, cwd) {
33833
- return {
33834
- ...result,
33835
- sessions: result.sessions.filter((session3) => session3.cwd && isSamePath(session3.cwd, cwd))
33836
- };
33837
- }
33838
- var init_agent_session_list = __esm(() => {
33839
- init_path();
33840
- init_codex_subagent_filter();
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_background_followup = __esm(() => {
33731
+ DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
33732
+ ASYNC_AGENT_LAUNCH = /Async agent launched successfully|["']?status["']?\s*:\s*["']async_launched["']/i;
33841
33733
  });
33842
33734
 
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;
33854
- try {
33855
- entries = await readdir3(dir);
33856
- } 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;
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
33863
33776
  });
33864
33777
  }
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);
33797
+ }
33798
+ const remaining = this.rawStream ? this.buffer : this.buffer.trim();
33799
+ this.buffer = "";
33800
+ this.pendingLine = "";
33801
+ return remaining;
33802
+ }
33803
+ };
33865
33804
  }
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");
33873
- }
33874
- if (!advertised.available.includes(value)) {
33875
- throw new Error(`reasoning effort "${value}" is not advertised by the active agent`);
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);
33876
33813
  }
33877
- return advertised;
33878
33814
  }
33879
- function parseSessionEffortRecord(raw) {
33880
- let record3;
33815
+ function parseStreamingChunks(state, line) {
33816
+ const trimmed = line.trim();
33817
+ if (trimmed.length === 0)
33818
+ return;
33819
+ let event;
33881
33820
  try {
33882
- record3 = JSON.parse(raw);
33821
+ event = JSON.parse(trimmed);
33883
33822
  } catch {
33884
33823
  return;
33885
33824
  }
33886
- if (!isRecord5(record3) || !isRecord5(record3.acpx) || !Array.isArray(record3.acpx.config_options)) {
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);
33838
+ }
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);
33847
+ }
33848
+ state.segments.push(formatted);
33849
+ }
33850
+ }
33887
33851
  return;
33888
33852
  }
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))
33893
- 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
- };
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;
33900
33858
  }
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";
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 } : {} });
33866
+ }
33867
+ return;
33932
33868
  }
33933
- }
33869
+ if (update.sessionUpdate === "available_commands_update") {
33870
+ if (Array.isArray(update.availableCommands)) {
33871
+ state.onCommands?.(normalizeAgentCommands(update.availableCommands));
33872
+ }
33873
+ return;
33874
+ }
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);
33880
+ }
33881
+ return;
33882
+ }
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(`
33934
33895
 
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);
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
+ }
33940
33902
  }
33941
- return args;
33942
- }
33943
- function buildQueueOwnerTtlArgs(queueOwnerTtlSeconds) {
33944
- if (typeof queueOwnerTtlSeconds !== "number" || !Number.isFinite(queueOwnerTtlSeconds))
33945
- return [];
33946
- return ["--ttl", String(queueOwnerTtlSeconds)];
33947
33903
  }
33948
- function buildModelArgs(model) {
33949
- const trimmed = model?.trim();
33950
- return trimmed ? ["--model", trimmed] : [];
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}`;
33951
33921
  }
33952
- function appendAgentAndTail(prefix, input, tail2) {
33953
- if (input.agentCommand)
33954
- return [...prefix, "--agent", input.agentCommand, ...tail2];
33955
- return [...prefix, input.agent, ...tail2];
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;
33956
33932
  }
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);
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;
33940
+ }
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 } } : {}
33956
+ };
33957
+ }
33958
+ merged.toolCallId = toolCallId;
33959
+ state.toolCalls.set(toolCallId, merged);
33960
+ return merged;
33969
33961
  }
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);
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 isAsyncAgentLaunch2 = update._meta?.claudeCode?.toolName === "Agent" && isRecord4(claudeToolResponse) && claudeToolResponse.status === "async_launched" || driver === "qoder" && update._meta?.qoder?.toolName === "Agent" && isAsyncAgentLaunchOutput(update.rawOutput);
33991
+ const status = isAsyncAgentLaunch2 ? "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
+ };
33982
34011
  }
33983
- function buildAgentQueryArgs(input, format, tail2) {
33984
- const prefix = ["--format", format, "--cwd", input.cwd, ...buildPermissionArgs(input.permission)];
33985
- return appendAgentAndTail(prefix, input, tail2);
34012
+ function isKimiSubagentInput(rawInput) {
34013
+ return isRecord4(rawInput) && readFirstString(rawInput, ["prompt"]) !== undefined && readFirstString(rawInput, ["subagent_type", "subagentType"]) !== undefined;
33986
34014
  }
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");
34015
+ function isCodexSubagentMeta(meta2) {
34016
+ return typeof meta2?.threadId === "string" && meta2.threadId.trim().length > 0 && typeof meta2.activity === "string" && meta2.activity.trim().length > 0;
33991
34017
  }
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 };
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);
34023
+ }
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(" ");
34033
+ }
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
+ }
34001
34041
  }
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 };
34042
+ if (parts.length > 0) {
34043
+ return parts.join(" ");
34006
34044
  }
34007
34045
  }
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;
34008
34071
  return;
34009
34072
  }
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
- });
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();
34078
+ }
34079
+ }
34080
+ return;
34022
34081
  }
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
- });
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;
34090
+ }
34091
+ }
34092
+ return;
34059
34093
  }
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 = "";
34073
- const onAbort = () => {
34074
- child.kill();
34075
- };
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
- });
34090
- });
34094
+ function asFiniteNumber(value) {
34095
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
34091
34096
  }
34092
-
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 } : {}
34119
- });
34120
- this.streamingHooks = streamingHooks;
34121
- this.resolveSpawnEnvironment = options.resolveSpawnEnvironment ?? resolveClaudeSpawnEnvironment;
34097
+ function firstFiniteNumber(record3, keys) {
34098
+ for (const key of keys) {
34099
+ const n = asFiniteNumber(record3[key]);
34100
+ if (n !== undefined)
34101
+ return n;
34122
34102
  }
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;
34132
- }
34133
- throw error2;
34134
- }
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;
34135
34113
  }
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
- });
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 });
34148
34137
  }
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
- });
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;
34167
34156
  }
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);
34157
+ if (kind === "search" && ["grep", "rg", "search"].includes(normalizedTitle)) {
34158
+ return true;
34193
34159
  }
34194
- async prompt(session3, text, reply, replyContext, options) {
34195
- if (session3.effort?.trim()) {
34196
- await this.reapplySessionEffort(session3, session3.effort.trim());
34197
- }
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 };
34214
- }
34215
- const summary = buildOverflowSummary(overflowCount);
34216
- return { text: summary ? `${summary}
34160
+ if (kind === "read" && ["read", "cat"].includes(normalizedTitle)) {
34161
+ return true;
34162
+ }
34163
+ return false;
34164
+ }
34165
+ var USAGE_BREAKDOWN_FIELDS;
34166
+ var init_streaming_prompt = __esm(() => {
34167
+ init_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
+ });
34217
34178
 
34218
- ${baseText}` : "" };
34219
- }
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
- }
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;
34228
34185
  }
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
- }));
34186
+ return join21(dirname13(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
34187
+ }
34188
+ async function ensureNodePtyHelperExecutable(helperPath, chmod5 = chmodFs) {
34189
+ if (!helperPath) {
34190
+ return;
34239
34191
  }
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
- }));
34251
- }
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);
34192
+ try {
34193
+ await chmod5(helperPath, 493);
34194
+ } catch (error2) {
34195
+ if (error2.code === "ENOENT") {
34196
+ return;
34264
34197
  }
34198
+ throw error2;
34199
+ }
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;
34230
+ }
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;
34265
34242
  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
- };
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);
34249
+ }
34250
+ }
34251
+ const path14 = index.get(sessionId.toLowerCase());
34252
+ if (path14)
34253
+ result = sessionMetaLineIsSubagent(reader.readFirstLine(path14));
34271
34254
  } catch {
34272
- return { available: [] };
34255
+ result = false;
34273
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;
34284
+ }
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);
34291
+ }
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)
34315
+ return;
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;
34274
34350
  }
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);
34290
- }
34291
- async reapplySessionEffort(session3, effort) {
34292
- const record3 = await this.readSessionEffortRecord(session3);
34293
- const observed = parseSessionEffortRecord(record3);
34294
- if (!observed?.available.includes(effort)) {
34351
+ if (result.code !== 0) {
34352
+ if ((result.stdout + result.stderr).includes("sessionCapabilities.list")) {
34295
34353
  return;
34296
34354
  }
34297
- await this.applyAdvertisedSessionEffort(session3, effort, record3);
34355
+ throw new Error(options.formatError(result));
34298
34356
  }
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
- }));
34357
+ const parsed = parseAgentSessionListOutput(result.stdout, filterLocally ? options.filterCwd : undefined);
34358
+ if (parsed && options.isSubagentSession) {
34359
+ return filterSubagentSessions(parsed, options.isSubagentSession);
34308
34360
  }
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
- }));
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");
34321
34369
  }
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()
34334
- };
34370
+ if (!isAgentSessionListResult(parsed)) {
34371
+ return;
34335
34372
  }
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
- });
34351
- }
34352
- async updatePermissionPolicy(policy) {
34353
- this.permissionMode = policy.permissionMode;
34354
- this.nonInteractivePermissions = policy.nonInteractivePermissions;
34355
- this.permissionPolicy = policy.permissionPolicy;
34356
- }
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;
34369
- }
34370
- if (isMissingAcpxSessionError(result.stderr, result.stdout)) {
34371
- return;
34372
- }
34373
- const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
34374
- throw new Error(detail);
34375
- }
34376
- async deleteSession(session3) {
34377
- let acpxRecordId;
34378
- try {
34379
- ({ acpxRecordId } = await this.readSessionRecord(session3));
34380
- } catch {
34381
- return;
34382
- }
34383
- await this.removeSession(session3);
34384
- await deleteAcpxSessionFiles({ acpxRecordId });
34385
- }
34386
- async freeWarmProcess(session3) {
34387
- let acpxRecordId;
34388
- try {
34389
- ({ acpxRecordId } = await this.readSessionRecord(session3));
34390
- } catch {
34391
- return;
34392
- }
34393
- await terminateAcpxQueueOwner(acpxRecordId);
34394
- }
34395
- recordIdCache = new Map;
34396
- recordIdCacheKey(session3) {
34397
- return JSON.stringify([session3.agent, session3.agentCommand ?? null, session3.cwd, session3.transportSession]);
34398
- }
34399
- invalidateRecordIdCache(session3) {
34400
- this.recordIdCache.delete(this.recordIdCacheKey(session3));
34401
- }
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) {
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))
34415
34383
  return false;
34416
- }
34417
- return isProcessAlive(pid);
34418
- }
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;
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;
34429
34414
  }
34430
- async launchMcpQueueOwnerIfNeeded(session3) {
34431
- if (!session3.mcpCoordinatorSession) {
34415
+ const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
34416
+ for (const name of streamFiles) {
34417
+ await unlink2(join23(dir, name)).catch(() => {
34432
34418
  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
34419
  });
34445
34420
  }
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");
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");
34463
34429
  }
34464
- async getAgentSessionId(session3) {
34465
- const record3 = await this.readSessionRecord(session3);
34466
- return record3.agentSessionId;
34430
+ if (!advertised.available.includes(value)) {
34431
+ throw new Error(`reasoning effort "${value}" is not advertised by the active agent`);
34467
34432
  }
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;
34433
+ return advertised;
34434
+ }
34435
+ function parseSessionEffortRecord(raw) {
34436
+ let record3;
34437
+ try {
34438
+ record3 = JSON.parse(raw);
34439
+ } catch {
34440
+ return;
34475
34441
  }
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);
34481
- }
34482
- return result.stdout;
34442
+ if (!isRecord5(record3) || !isRecord5(record3.acpx) || !Array.isArray(record3.acpx.config_options)) {
34443
+ return;
34483
34444
  }
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
- ]);
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) : [];
34451
+ return {
34452
+ configId: candidate.id,
34453
+ current: typeof candidate.currentValue === "string" ? candidate.currentValue : undefined,
34454
+ available
34455
+ };
34503
34456
  }
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();
34601
- }
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);
34609
- }
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);
34623
- }
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);
34661
- return;
34662
- }
34663
- resolve4({
34664
- result: { code: code ?? 1, stdout: stdout2, stderr },
34665
- overflowCount
34666
- });
34667
- }).catch((error2) => {
34668
- reject(error2);
34669
- });
34670
- });
34671
- });
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";
34488
+ }
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);
34496
+ }
34497
+ return args;
34498
+ }
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 };
34557
+ }
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 };
34562
+ }
34672
34563
  }
34673
- permissionInput() {
34674
- return {
34675
- permissionMode: this.permissionMode,
34676
- nonInteractivePermissions: this.nonInteractivePermissions,
34677
- permissionPolicy: this.permissionPolicy
34564
+ return;
34565
+ }
34566
+ var DEFAULT_PERMISSION_MODE2 = "approve-all", DEFAULT_NON_INTERACTIVE = "deny";
34567
+ var init_acpx_command_builder = () => {};
34568
+
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
+ });
34578
+ }
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
+ });
34615
+ }
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();
34678
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;
34679
34678
  }
34680
- spawnEnvironment(input) {
34681
- return this.resolveSpawnEnvironment(input);
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;
34690
+ }
34682
34691
  }
34683
- withSpawnEnvironment(input, options) {
34684
- const env = this.spawnEnvironment(input);
34685
- return env ? { ...options ?? {}, env } : options;
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
+ });
34686
34704
  }
34687
- sessionInput(session3) {
34688
- return {
34689
- agent: session3.agent,
34690
- agentCommand: session3.agentCommand,
34691
- cwd: session3.cwd,
34692
- model: session3.model,
34693
- permission: this.permissionInput()
34694
- };
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
+ });
34695
34723
  }
34696
- buildArgs(session3, tail2, format = "quiet") {
34697
- return buildSessionArgs(this.sessionInput(session3), tail2, { format });
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);
34698
34749
  }
34699
- buildAgentQueryArgs(query, format, tail2) {
34700
- return buildAgentQueryArgs({ agent: query.agent, agentCommand: query.agentCommand, cwd: query.cwd, permission: this.permissionInput() }, format, tail2);
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";
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}` : "" };
34775
+ }
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 {}
34783
+ }
34701
34784
  }
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);
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
+ }));
34705
34795
  }
34706
- buildPermissionArgs() {
34707
- return buildPermissionArgs(this.permissionInput());
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
+ }));
34708
34807
  }
34709
- }
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;
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);
34720
34820
  }
34721
- if (arg === "--cwd") {
34722
- index += 1;
34723
- continue;
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: [] };
34724
34829
  }
34725
- rendered.push(/\s/.test(arg) || arg.includes(":") ? `"${arg}"` : arg);
34726
34830
  }
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;
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: [] };
34735
34842
  }
34736
- env.XACPX_LANG = locale;
34737
- return env;
34738
- }
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
- });
34760
-
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));
34768
- }
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];
34774
- }
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;
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)) {
34851
+ return;
34788
34852
  }
34789
- } catch {
34790
- return;
34853
+ await this.applyAdvertisedSessionEffort(session3, effort, record3);
34791
34854
  }
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: [] };
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
+ }));
34798
34864
  }
34799
- let initial;
34800
- try {
34801
- initial = await readFile14(transcriptPath);
34802
- } catch {
34803
- return { status: "unavailable", transcriptPath, completedToolCallIds: [], failedToolCallIds: [] };
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
+ }));
34804
34877
  }
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);
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
+ };
34823
34891
  }
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
- }
34856
- }
34857
- }
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;
34881
- }
34882
- if (record3.type !== "assistant" || record3.isSidechain === true)
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) {
34883
34924
  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
- }
34910
34925
  }
34911
- if (record3.message?.stop_reason === "end_turn" && launched.size > 0 && [...launched].every((id) => completed.has(id)) && lastNotificationSequence >= 0 && sequence > lastNotificationSequence) {
34912
- done = true;
34913
- }
34914
- };
34915
- const processAfterBoundary = async (record3) => {
34916
- if (!record3)
34917
- return;
34918
- if (!boundarySeen) {
34919
- if (hasTrackedLaunch(record3, launched))
34920
- launchSeen = true;
34921
- if (launchSeen && isMainEndTurn(record3))
34922
- boundarySeen = true;
34926
+ if (isMissingAcpxSessionError(result.stderr, result.stdout)) {
34923
34927
  return;
34924
34928
  }
34925
- await processRecord(record3);
34926
- };
34927
- const processSubagentRecord = async (record3, parentToolCallId) => {
34928
- if (!record3)
34929
- 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
- }
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;
35037
- }
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;
35038
34985
  }
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;
34986
+ async launchMcpQueueOwnerIfNeeded(session3) {
34987
+ if (!session3.mcpCoordinatorSession) {
34988
+ return;
35044
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
+ });
35045
35001
  }
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 [];
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);
35014
+ }
35015
+ const record3 = parseAcpxSessionRecordId(result.stdout);
35016
+ if (record3)
35017
+ return record3;
35018
+ throw new Error("failed to resolve acpx session record id");
35059
35019
  }
35060
- if (size < cursor.offset) {
35061
- cursor.offset = 0;
35062
- cursor.partial = "";
35020
+ async getAgentSessionId(session3) {
35021
+ const record3 = await this.readSessionRecord(session3);
35022
+ return record3.agentSessionId;
35063
35023
  }
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();
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);
35075
35029
  }
35076
- } catch {
35077
- return [];
35030
+ return result.stdout;
35078
35031
  }
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
- }
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);
35096
35037
  }
35038
+ return result.stdout;
35097
35039
  }
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;
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);
35104
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
+ });
35228
+ }
35229
+ permissionInput() {
35230
+ return {
35231
+ permissionMode: this.permissionMode,
35232
+ nonInteractivePermissions: this.nonInteractivePermissions,
35233
+ permissionPolicy: this.permissionPolicy
35234
+ };
35235
+ }
35236
+ spawnEnvironment(input) {
35237
+ return this.resolveSpawnEnvironment(input);
35238
+ }
35239
+ withSpawnEnvironment(input, options) {
35240
+ const env = this.spawnEnvironment(input);
35241
+ return env ? { ...options ?? {}, env } : options;
35105
35242
  }
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;
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
+ };
35123
35251
  }
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 "";
35252
+ buildArgs(session3, tail2, format = "quiet") {
35253
+ return buildSessionArgs(this.sessionInput(session3), tail2, { format });
35157
35254
  }
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;
35255
+ buildAgentQueryArgs(query, format, tail2) {
35256
+ return buildAgentQueryArgs({ agent: query.agent, agentCommand: query.agentCommand, cwd: query.cwd, permission: this.permissionInput() }, format, tail2);
35169
35257
  }
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();
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);
35175
35261
  }
35176
- for (const candidate of Object.values(object4)) {
35177
- const found = findStringField(candidate, keys);
35178
- if (found)
35179
- return found;
35262
+ buildPermissionArgs() {
35263
+ return buildPermissionArgs(this.permissionInput());
35180
35264
  }
35181
- return;
35182
35265
  }
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)
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) {
35189
35271
  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
- });
35195
- }
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
- }
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;
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
- // src/transport/claude-background-followup-transport.ts
35297
- function createClaudeBackgroundFollowupTransport(delegate, options) {
35298
- const followBackgroundTurn = options.followBackgroundTurn ?? followClaudeBackgroundTurn;
35317
+ // src/transport/background-followup-transport.ts
35318
+ function createBackgroundFollowupTransport(delegate, options) {
35319
+ const runBackgroundFollowup = options.followBackgroundTurn ?? followBackgroundTurn;
35299
35320
  const prompt = async (session3, text, reply, replyContext, promptOptions) => {
35300
35321
  const latestToolEvents = new Map;
35301
35322
  const pendingToolEvents = new Map;
@@ -35308,9 +35329,9 @@ 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 && isAsyncAgentLaunch(event)) {
35312
35333
  asyncToolCallIds.add(event.toolCallId);
35313
- const agentId = claudeAsyncAgentId(event);
35334
+ const agentId = asyncAgentId(event);
35314
35335
  if (agentId)
35315
35336
  subagentIdsByToolCallId.set(event.toolCallId, agentId);
35316
35337
  }
@@ -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(`${LEGACY_LOG_EVENT_PREFIX}.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 });
35345
- const followup = await followBackgroundTurn({
35366
+ await options.logger.info(`${LEGACY_LOG_EVENT_PREFIX}.started`, "following background-agent continuation", { alias: session3.alias, agentSessionId, driver, taskCount: asyncToolCallIds.size });
35367
+ const followup = await runBackgroundFollowup({
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(`${LEGACY_LOG_EVENT_PREFIX}.${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
  });
@@ -35393,8 +35417,9 @@ function createClaudeBackgroundFollowupTransport(delegate, options) {
35393
35417
  }
35394
35418
  });
35395
35419
  }
35396
- var init_claude_background_followup_transport = __esm(() => {
35397
- init_claude_background_followup();
35420
+ var LEGACY_LOG_EVENT_PREFIX = "transport.claude_background_followup";
35421
+ var init_background_followup_transport = __esm(() => {
35422
+ init_background_followup();
35398
35423
  });
35399
35424
 
35400
35425
  // src/util/async.ts
@@ -36758,21 +36783,28 @@ class WorkspaceGit {
36758
36783
  }
36759
36784
  return startPoint;
36760
36785
  }
36761
- async includeRenameSources(root, paths) {
36762
- const selected = new Set(paths);
36763
- const expanded = new Set(paths);
36786
+ async readPorcelain(root) {
36764
36787
  const fields = (await this.runRaw(root, ["-c", "core.quotePath=false", "status", "--porcelain", "-z"])).split("\x00");
36788
+ const entries = [];
36765
36789
  for (let index = 0;index < fields.length; index += 1) {
36766
36790
  const field = fields[index];
36767
36791
  if (!field)
36768
36792
  continue;
36769
36793
  const status = field.slice(0, 2);
36770
36794
  const path14 = field.slice(3);
36771
- if (status[0] !== "R" && status[0] !== "C")
36772
- continue;
36773
- const originalPath = fields[++index];
36774
- if (originalPath && selected.has(path14))
36775
- expanded.add(originalPath);
36795
+ const entry = { status, path: path14 };
36796
+ if (status[0] === "R" || status[0] === "C")
36797
+ entry.originalPath = fields[++index];
36798
+ entries.push(entry);
36799
+ }
36800
+ return entries;
36801
+ }
36802
+ async includeRenameSources(root, paths) {
36803
+ const selected = new Set(paths);
36804
+ const expanded = new Set(paths);
36805
+ for (const entry of await this.readPorcelain(root)) {
36806
+ if (entry.originalPath && selected.has(entry.path))
36807
+ expanded.add(entry.originalPath);
36776
36808
  }
36777
36809
  return [...expanded];
36778
36810
  }
@@ -36810,6 +36842,38 @@ class WorkspaceGit {
36810
36842
  }
36811
36843
  });
36812
36844
  }
36845
+ async untrack(workspace3, paths) {
36846
+ await this.withWrite(workspace3, async () => {
36847
+ const root = await this.rootFor(workspace3);
36848
+ await this.run(root, ["--literal-pathspecs", "rm", "--cached", "--force", "--", ...this.validatePaths(paths)]);
36849
+ });
36850
+ }
36851
+ async discard(workspace3, paths) {
36852
+ await this.withWrite(workspace3, async () => {
36853
+ const root = await this.rootFor(workspace3);
36854
+ const selected = new Set(this.validatePaths(paths));
36855
+ const deletePaths = new Set;
36856
+ const restorePaths = new Set;
36857
+ for (const entry of await this.readPorcelain(root)) {
36858
+ if (!selected.has(entry.path))
36859
+ continue;
36860
+ if (entry.status === "??" || entry.status[0] === "A" || entry.originalPath !== undefined) {
36861
+ deletePaths.add(entry.path);
36862
+ if (entry.originalPath)
36863
+ restorePaths.add(entry.originalPath);
36864
+ } else {
36865
+ restorePaths.add(entry.path);
36866
+ }
36867
+ }
36868
+ if (deletePaths.size) {
36869
+ await this.run(root, ["--literal-pathspecs", "reset", "-q", "--", ...deletePaths]);
36870
+ await this.run(root, ["--literal-pathspecs", "clean", "-qf", "--", ...deletePaths]);
36871
+ }
36872
+ if (restorePaths.size) {
36873
+ await this.run(root, ["--literal-pathspecs", "restore", "--source=HEAD", "--staged", "--worktree", "--", ...restorePaths]);
36874
+ }
36875
+ });
36876
+ }
36813
36877
  async commit(workspace3, message) {
36814
36878
  return this.withWrite(workspace3, async () => {
36815
36879
  const root = await this.rootFor(workspace3);
@@ -37540,6 +37604,12 @@ class ControlService {
37540
37604
  gitUnstage(workspace3, paths) {
37541
37605
  return this.mutateWorkspaceGit(() => this.workspaceGit.unstage(workspace3, paths));
37542
37606
  }
37607
+ gitUntrack(workspace3, paths) {
37608
+ return this.mutateWorkspaceGit(() => this.workspaceGit.untrack(workspace3, paths));
37609
+ }
37610
+ gitDiscard(workspace3, paths) {
37611
+ return this.mutateWorkspaceGit(() => this.workspaceGit.discard(workspace3, paths));
37612
+ }
37543
37613
  gitCommit(workspace3, message) {
37544
37614
  return this.mutateWorkspaceGit(() => this.workspaceGit.commit(workspace3, message));
37545
37615
  }
@@ -38570,7 +38640,7 @@ async function buildApp(paths, deps = {}) {
38570
38640
  });
38571
38641
  }
38572
38642
  })))) : deps.createCliTransport?.(acpxCommand) ?? new AcpxCliTransport({ ...config4.transport, command: acpxCommand });
38573
- const transport = createClaudeBackgroundFollowupTransport(baseTransport, {
38643
+ const transport = createBackgroundFollowupTransport(baseTransport, {
38574
38644
  logger,
38575
38645
  resolveDriver: (agent4) => config4.agents[agent4]?.driver
38576
38646
  });
@@ -39245,7 +39315,7 @@ var init_main = __esm(async () => {
39245
39315
  init_acpx_bridge_client();
39246
39316
  init_acpx_bridge_transport();
39247
39317
  init_acpx_cli_transport();
39248
- init_claude_background_followup_transport();
39318
+ init_background_followup_transport();
39249
39319
  init_queue_owner_reaper();
39250
39320
  init_collect_reap_targets();
39251
39321
  init_channel_registry();