@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.
@@ -2633,6 +2633,561 @@ var init_prompt_media = __esm(() => {
2633
2633
  };
2634
2634
  });
2635
2635
 
2636
+ // src/transport/background-followup.ts
2637
+ import { access, open as open2, readFile, readdir, stat } from "node:fs/promises";
2638
+ import { constants as fsConstants } from "node:fs";
2639
+ import { homedir as homedir2 } from "node:os";
2640
+ import { basename, dirname as dirname2, join as join2 } from "node:path";
2641
+ function isAsyncAgentLaunch(event) {
2642
+ return isAsyncAgentLaunchOutput(event.rawOutput);
2643
+ }
2644
+ function isAsyncAgentLaunchOutput(rawOutput) {
2645
+ const candidate = typeof rawOutput === "string" ? tryParseJsonObject(rawOutput) ?? rawOutput : rawOutput;
2646
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
2647
+ const status = candidate.status;
2648
+ if (status !== undefined)
2649
+ return status === "async_launched";
2650
+ }
2651
+ return ASYNC_AGENT_LAUNCH.test(textOfUnknown(rawOutput));
2652
+ }
2653
+ function tryParseJsonObject(text) {
2654
+ if (!text.trimStart().startsWith("{"))
2655
+ return;
2656
+ try {
2657
+ const parsed = JSON.parse(text);
2658
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
2659
+ } catch {
2660
+ return;
2661
+ }
2662
+ }
2663
+ function asyncAgentId(event) {
2664
+ const fromObject = findStringField(event.rawOutput, new Set(["agentId", "agent_id"]));
2665
+ if (fromObject)
2666
+ return fromObject;
2667
+ return /\bagent(?:Id|_id)["']?\s*[:=]\s*["']?([A-Za-z0-9_-]+)/i.exec(textOfUnknown(event.rawOutput))?.[1];
2668
+ }
2669
+ async function findNativeTranscriptPath(input) {
2670
+ const configDirName = input.driver === "qoder" ? ".qoder" : ".claude";
2671
+ const projectsDir = join2(input.homeDir ?? homedir2(), configDirName, "projects");
2672
+ const direct = join2(projectsDir, encodeClaudeProjectDirectory(input.cwd), `${input.sessionId}.jsonl`);
2673
+ if (await exists(direct))
2674
+ return direct;
2675
+ try {
2676
+ const entries = await readdir(projectsDir, { withFileTypes: true });
2677
+ for (const entry of entries) {
2678
+ if (!entry.isDirectory())
2679
+ continue;
2680
+ const candidate = join2(projectsDir, entry.name, `${input.sessionId}.jsonl`);
2681
+ if (await exists(candidate))
2682
+ return candidate;
2683
+ }
2684
+ } catch {
2685
+ return;
2686
+ }
2687
+ return;
2688
+ }
2689
+ async function followBackgroundTurn(options) {
2690
+ const transcriptPath = options.transcriptPath ?? await findNativeTranscriptPath(options);
2691
+ if (!transcriptPath) {
2692
+ return { status: "unavailable", completedToolCallIds: [], failedToolCallIds: [] };
2693
+ }
2694
+ let initial;
2695
+ try {
2696
+ initial = await readFile(transcriptPath);
2697
+ } catch {
2698
+ return { status: "unavailable", transcriptPath, completedToolCallIds: [], failedToolCallIds: [] };
2699
+ }
2700
+ const initialCompleteEnd = initial.lastIndexOf(10);
2701
+ const initialComplete = initial.subarray(0, initialCompleteEnd >= 0 ? initialCompleteEnd : 0).toString("utf8");
2702
+ const mainCursor = {
2703
+ offset: initial.length,
2704
+ partial: initialCompleteEnd >= 0 ? initial.subarray(initialCompleteEnd + 1).toString("utf8") : initial.toString("utf8")
2705
+ };
2706
+ const initialRecords = initialComplete.split(`
2707
+ `).map(parseRecord);
2708
+ const launched = new Set([...options.launchedToolCallIds].filter(Boolean));
2709
+ const completed = new Set;
2710
+ const failed = new Set;
2711
+ const toolEvents = new Map;
2712
+ for (const event of options.initialToolEvents ?? [])
2713
+ toolEvents.set(event.toolCallId, event);
2714
+ const parentToolCallIdByAgentId = new Map;
2715
+ for (const [toolCallId, agentId] of options.subagentIdsByToolCallId ?? []) {
2716
+ if (toolCallId && agentId)
2717
+ parentToolCallIdByAgentId.set(agentId, toolCallId);
2718
+ }
2719
+ const boundary = findPromptBoundary(initialRecords, launched);
2720
+ let launchSeen = boundary >= 0 || initialRecords.some((record) => hasTrackedLaunch(record, launched));
2721
+ let boundarySeen = boundary >= 0;
2722
+ let sequence = 0;
2723
+ let lastNotificationSequence = -1;
2724
+ let done = false;
2725
+ const emitTool = async (event) => {
2726
+ toolEvents.set(event.toolCallId, event);
2727
+ await options.onToolEvent?.(event);
2728
+ };
2729
+ const processRecord = async (record) => {
2730
+ if (!record)
2731
+ return;
2732
+ sequence += 1;
2733
+ const blocks = contentBlocks(record.message?.content);
2734
+ if (record.type === "user") {
2735
+ const userText = blocks.map(blockText).filter(Boolean).join(`
2736
+ `);
2737
+ for (const notification of taskNotifications(userText)) {
2738
+ if (launched.has(notification.toolCallId)) {
2739
+ completed.add(notification.toolCallId);
2740
+ if (notification.failed)
2741
+ failed.add(notification.toolCallId);
2742
+ lastNotificationSequence = sequence;
2743
+ const previous = toolEvents.get(notification.toolCallId);
2744
+ if (previous) {
2745
+ await emitTool({
2746
+ ...previous,
2747
+ status: notification.failed ? "error" : "success",
2748
+ ...notification.failed ? { rawOutput: { message: "background Agent failed" } } : {}
2749
+ });
2750
+ }
2751
+ }
2752
+ }
2753
+ for (const block of blocks) {
2754
+ if (readString(block, "type") !== "tool_result")
2755
+ continue;
2756
+ const toolCallId = readString(block, "tool_use_id");
2757
+ if (!toolCallId)
2758
+ continue;
2759
+ const output = blockText(block);
2760
+ if (ASYNC_AGENT_LAUNCH.test(output))
2761
+ launched.add(toolCallId);
2762
+ const previous = toolEvents.get(toolCallId);
2763
+ await emitTool({
2764
+ toolCallId,
2765
+ toolName: previous?.toolName ?? "Tool",
2766
+ kind: previous?.kind ?? "other",
2767
+ ...previous?.parentToolCallId ? { parentToolCallId: previous.parentToolCallId } : {},
2768
+ ...previous?.isSubagent ? { isSubagent: true } : {},
2769
+ ...previous?.summary ? { summary: previous.summary } : {},
2770
+ ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : {},
2771
+ ...output ? { rawOutput: output } : {},
2772
+ status: readBoolean(block, "is_error") ? "error" : "success"
2773
+ });
2774
+ }
2775
+ return;
2776
+ }
2777
+ if (record.type !== "assistant" || record.isSidechain === true)
2778
+ return;
2779
+ for (const block of blocks) {
2780
+ const type = readString(block, "type");
2781
+ if (type === "text") {
2782
+ const text = readString(block, "text");
2783
+ if (text)
2784
+ await options.onText?.(text);
2785
+ } else if (type === "thinking") {
2786
+ const thought = readString(block, "thinking");
2787
+ if (thought)
2788
+ await options.onThought?.(thought);
2789
+ } else if (type === "tool_use") {
2790
+ const toolCallId = readString(block, "id");
2791
+ if (!toolCallId)
2792
+ continue;
2793
+ const name = readString(block, "name") ?? "Tool";
2794
+ const rawInput = block.input;
2795
+ const event = {
2796
+ toolCallId,
2797
+ toolName: name,
2798
+ kind: classifyToolKind(name),
2799
+ ...toolSummary(rawInput) ? { summary: toolSummary(rawInput) } : {},
2800
+ ...rawInput !== undefined ? { rawInput } : {},
2801
+ status: "running"
2802
+ };
2803
+ await emitTool(event);
2804
+ }
2805
+ }
2806
+ if (record.message?.stop_reason === "end_turn" && launched.size > 0 && [...launched].every((id) => completed.has(id)) && lastNotificationSequence >= 0 && sequence > lastNotificationSequence) {
2807
+ done = true;
2808
+ }
2809
+ };
2810
+ const processAfterBoundary = async (record) => {
2811
+ if (!record)
2812
+ return;
2813
+ if (!boundarySeen) {
2814
+ if (hasTrackedLaunch(record, launched))
2815
+ launchSeen = true;
2816
+ if (launchSeen && isMainEndTurn(record))
2817
+ boundarySeen = true;
2818
+ return;
2819
+ }
2820
+ await processRecord(record);
2821
+ };
2822
+ const processSubagentRecord = async (record, parentToolCallId) => {
2823
+ if (!record)
2824
+ return;
2825
+ const blocks = contentBlocks(record.message?.content);
2826
+ if (record.type === "assistant") {
2827
+ for (const block of blocks) {
2828
+ if (readString(block, "type") !== "tool_use")
2829
+ continue;
2830
+ const toolCallId = readString(block, "id");
2831
+ if (!toolCallId)
2832
+ continue;
2833
+ const name = readString(block, "name") ?? "Tool";
2834
+ const rawInput = block.input;
2835
+ const previous = toolEvents.get(toolCallId);
2836
+ await emitTool({
2837
+ ...previous,
2838
+ toolCallId,
2839
+ parentToolCallId: previous?.parentToolCallId ?? parentToolCallId,
2840
+ ...previous?.isSubagent || isAgentToolName(name) ? { isSubagent: true } : {},
2841
+ toolName: previous?.toolName ?? name,
2842
+ kind: previous?.kind ?? classifyToolKind(name),
2843
+ ...previous?.summary ? { summary: previous.summary } : toolSummary(rawInput) ? { summary: toolSummary(rawInput) } : {},
2844
+ ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : rawInput !== undefined ? { rawInput } : {},
2845
+ status: previous?.status === "success" || previous?.status === "error" ? previous.status : "running"
2846
+ });
2847
+ }
2848
+ return;
2849
+ }
2850
+ if (record.type !== "user")
2851
+ return;
2852
+ for (const block of blocks) {
2853
+ if (readString(block, "type") !== "tool_result")
2854
+ continue;
2855
+ const toolCallId = readString(block, "tool_use_id");
2856
+ if (!toolCallId)
2857
+ continue;
2858
+ const previous = toolEvents.get(toolCallId);
2859
+ const output = blockText(block);
2860
+ const event = {
2861
+ ...previous,
2862
+ toolCallId,
2863
+ parentToolCallId: previous?.parentToolCallId ?? parentToolCallId,
2864
+ ...previous?.isSubagent ? { isSubagent: true } : {},
2865
+ toolName: previous?.toolName ?? "Tool",
2866
+ kind: previous?.kind ?? "other",
2867
+ ...previous?.summary ? { summary: previous.summary } : {},
2868
+ ...previous?.rawInput !== undefined ? { rawInput: previous.rawInput } : {},
2869
+ ...previous?.rawOutput !== undefined ? { rawOutput: previous.rawOutput } : output ? { rawOutput: output } : {},
2870
+ status: readBoolean(block, "is_error") || previous?.status === "error" ? "error" : "success"
2871
+ };
2872
+ if (event.isSubagent && isAsyncAgentLaunch(event)) {
2873
+ const agentId = asyncAgentId(event);
2874
+ if (agentId)
2875
+ parentToolCallIdByAgentId.set(agentId, event.toolCallId);
2876
+ }
2877
+ await emitTool(event);
2878
+ }
2879
+ };
2880
+ const subagentCursors = new Map;
2881
+ const processSubagentTranscripts = async () => {
2882
+ const transcripts = await findSubagentTranscripts(transcriptPath);
2883
+ let advanced = false;
2884
+ let discoveredNestedAgent = true;
2885
+ while (discoveredNestedAgent) {
2886
+ const mappedAgentCount = parentToolCallIdByAgentId.size;
2887
+ for (const subagent of transcripts) {
2888
+ const parentToolCallId = parentToolCallIdByAgentId.get(subagent.agentId);
2889
+ if (!parentToolCallId)
2890
+ continue;
2891
+ const cursor = subagentCursors.get(subagent.path) ?? { offset: 0, partial: "" };
2892
+ const previousOffset = cursor.offset;
2893
+ const lines = await readJsonlDelta(subagent.path, cursor);
2894
+ if (cursor.offset !== previousOffset)
2895
+ advanced = true;
2896
+ subagentCursors.set(subagent.path, cursor);
2897
+ for (const line of lines)
2898
+ await processSubagentRecord(parseRecord(line), parentToolCallId);
2899
+ }
2900
+ discoveredNestedAgent = parentToolCallIdByAgentId.size > mappedAgentCount;
2901
+ }
2902
+ return advanced;
2903
+ };
2904
+ await processSubagentTranscripts();
2905
+ if (boundary >= 0) {
2906
+ for (const record of initialRecords.slice(boundary + 1)) {
2907
+ await processRecord(record);
2908
+ if (done)
2909
+ break;
2910
+ }
2911
+ }
2912
+ const startedAt = Date.now();
2913
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
2914
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2915
+ while (!done) {
2916
+ throwIfAborted(options.signal);
2917
+ if (Date.now() - startedAt >= timeoutMs) {
2918
+ return {
2919
+ status: "timeout",
2920
+ transcriptPath,
2921
+ completedToolCallIds: [...completed],
2922
+ failedToolCallIds: [...failed]
2923
+ };
2924
+ }
2925
+ await waitForPoll(pollIntervalMs, options.signal);
2926
+ await processSubagentTranscripts();
2927
+ const lines = await readJsonlDelta(transcriptPath, mainCursor);
2928
+ for (const line of lines) {
2929
+ await processAfterBoundary(parseRecord(line));
2930
+ if (done)
2931
+ break;
2932
+ }
2933
+ }
2934
+ if (parentToolCallIdByAgentId.size > 0) {
2935
+ let stablePolls = 0;
2936
+ for (let poll = 0;poll < FINAL_SUBAGENT_DRAIN_MAX_POLLS && stablePolls < FINAL_SUBAGENT_DRAIN_STABLE_POLLS; poll += 1) {
2937
+ await waitForPoll(pollIntervalMs, options.signal);
2938
+ stablePolls = await processSubagentTranscripts() ? 0 : stablePolls + 1;
2939
+ }
2940
+ }
2941
+ return {
2942
+ status: "completed",
2943
+ transcriptPath,
2944
+ completedToolCallIds: [...completed],
2945
+ failedToolCallIds: [...failed]
2946
+ };
2947
+ }
2948
+ async function readJsonlDelta(path2, cursor) {
2949
+ let size;
2950
+ try {
2951
+ size = (await stat(path2)).size;
2952
+ } catch {
2953
+ return [];
2954
+ }
2955
+ if (size < cursor.offset) {
2956
+ cursor.offset = 0;
2957
+ cursor.partial = "";
2958
+ }
2959
+ if (size <= cursor.offset)
2960
+ return [];
2961
+ let chunk;
2962
+ try {
2963
+ const handle = await open2(path2, "r");
2964
+ try {
2965
+ chunk = Buffer.alloc(size - cursor.offset);
2966
+ const result = await handle.read(chunk, 0, chunk.length, cursor.offset);
2967
+ chunk = chunk.subarray(0, result.bytesRead);
2968
+ } finally {
2969
+ await handle.close();
2970
+ }
2971
+ } catch {
2972
+ return [];
2973
+ }
2974
+ cursor.offset += chunk.length;
2975
+ cursor.partial += chunk.toString("utf8");
2976
+ const lines = cursor.partial.split(`
2977
+ `);
2978
+ cursor.partial = lines.pop() ?? "";
2979
+ return lines;
2980
+ }
2981
+ function findPromptBoundary(records, launched) {
2982
+ let launchIndex = -1;
2983
+ for (let index = 0;index < records.length; index += 1) {
2984
+ const record = records[index];
2985
+ if (record?.type !== "assistant" || record.isSidechain === true)
2986
+ continue;
2987
+ for (const block of contentBlocks(record.message?.content)) {
2988
+ if (readString(block, "type") === "tool_use" && launched.has(readString(block, "id") ?? "")) {
2989
+ launchIndex = index;
2990
+ }
2991
+ }
2992
+ }
2993
+ if (launchIndex < 0)
2994
+ return -1;
2995
+ for (let index = launchIndex + 1;index < records.length; index += 1) {
2996
+ const record = records[index];
2997
+ if (record?.type === "assistant" && record.isSidechain !== true && record.message?.stop_reason === "end_turn") {
2998
+ return index;
2999
+ }
3000
+ }
3001
+ return -1;
3002
+ }
3003
+ function hasTrackedLaunch(record, launched) {
3004
+ if (record?.type !== "assistant" || record.isSidechain === true)
3005
+ return false;
3006
+ return contentBlocks(record.message?.content).some((block) => readString(block, "type") === "tool_use" && launched.has(readString(block, "id") ?? ""));
3007
+ }
3008
+ function isMainEndTurn(record) {
3009
+ return record.type === "assistant" && record.isSidechain !== true && record.message?.stop_reason === "end_turn";
3010
+ }
3011
+ function parseRecord(line) {
3012
+ if (!line.trim())
3013
+ return;
3014
+ try {
3015
+ return JSON.parse(line);
3016
+ } catch {
3017
+ return;
3018
+ }
3019
+ }
3020
+ function contentBlocks(content) {
3021
+ if (typeof content === "string")
3022
+ return [{ type: "text", text: content }];
3023
+ return Array.isArray(content) ? content.filter((block) => !!block && typeof block === "object") : [];
3024
+ }
3025
+ function blockText(block) {
3026
+ if (typeof block.text === "string")
3027
+ return block.text;
3028
+ if (typeof block.content === "string")
3029
+ return block.content;
3030
+ if (Array.isArray(block.content))
3031
+ return block.content.map((item) => textOfUnknown(item)).filter(Boolean).join(`
3032
+ `);
3033
+ return textOfUnknown(block.content);
3034
+ }
3035
+ function textOfUnknown(value) {
3036
+ if (typeof value === "string")
3037
+ return value;
3038
+ if (Array.isArray(value))
3039
+ return value.map(textOfUnknown).filter(Boolean).join(`
3040
+ `);
3041
+ if (!value || typeof value !== "object")
3042
+ return "";
3043
+ const object = value;
3044
+ if (typeof object.text === "string")
3045
+ return object.text;
3046
+ if (object.content !== undefined)
3047
+ return textOfUnknown(object.content);
3048
+ try {
3049
+ return JSON.stringify(value);
3050
+ } catch {
3051
+ return "";
3052
+ }
3053
+ }
3054
+ function findStringField(value, keys) {
3055
+ if (!value || typeof value !== "object")
3056
+ return;
3057
+ if (Array.isArray(value)) {
3058
+ for (const item of value) {
3059
+ const found = findStringField(item, keys);
3060
+ if (found)
3061
+ return found;
3062
+ }
3063
+ return;
3064
+ }
3065
+ const object = value;
3066
+ for (const key of keys) {
3067
+ const candidate = object[key];
3068
+ if (typeof candidate === "string" && candidate.trim())
3069
+ return candidate.trim();
3070
+ }
3071
+ for (const candidate of Object.values(object)) {
3072
+ const found = findStringField(candidate, keys);
3073
+ if (found)
3074
+ return found;
3075
+ }
3076
+ return;
3077
+ }
3078
+ function taskNotifications(text) {
3079
+ const notifications = [];
3080
+ const blocks = [...text.matchAll(/<task-notification>([\s\S]*?)<\/task-notification>/gi)].map((match) => match[1] ?? "");
3081
+ for (const block of blocks.length > 0 ? blocks : [text]) {
3082
+ const toolCallId = /<tool-use-id>([^<]+)<\/tool-use-id>/i.exec(block)?.[1]?.trim();
3083
+ if (!toolCallId)
3084
+ continue;
3085
+ const status = /<status>([^<]+)<\/status>/i.exec(block)?.[1]?.trim().toLowerCase();
3086
+ notifications.push({
3087
+ toolCallId,
3088
+ failed: status === "failed" || status === "error" || status === "cancelled" || status === "canceled"
3089
+ });
3090
+ }
3091
+ return notifications;
3092
+ }
3093
+ function classifyToolKind(name) {
3094
+ const normalized = name.toLowerCase();
3095
+ if (/(read|view|open)/.test(normalized))
3096
+ return "read";
3097
+ if (/(grep|search|find|glob)/.test(normalized))
3098
+ return "search";
3099
+ if (/(edit|write|patch|replace|create)/.test(normalized))
3100
+ return "edit";
3101
+ if (/(bash|shell|exec|run|terminal|command)/.test(normalized))
3102
+ return "execute";
3103
+ if (/(think|reason|plan|agent|task)/.test(normalized))
3104
+ return "think";
3105
+ return "other";
3106
+ }
3107
+ function isAgentToolName(name) {
3108
+ const normalized = name.trim().toLowerCase();
3109
+ return normalized === "agent" || normalized === "task";
3110
+ }
3111
+ async function findSubagentTranscripts(transcriptPath) {
3112
+ const sessionDir = join2(dirname2(transcriptPath), basename(transcriptPath, ".jsonl"), "subagents");
3113
+ const found = [];
3114
+ const visit = async (directory) => {
3115
+ let entries;
3116
+ try {
3117
+ entries = await readdir(directory, { withFileTypes: true });
3118
+ } catch {
3119
+ return;
3120
+ }
3121
+ for (const entry of entries) {
3122
+ const path2 = join2(directory, entry.name);
3123
+ if (entry.isDirectory()) {
3124
+ await visit(path2);
3125
+ } else if (entry.isFile() && entry.name.startsWith("agent-") && entry.name.endsWith(".jsonl")) {
3126
+ found.push({ agentId: entry.name.slice("agent-".length, -".jsonl".length), path: path2 });
3127
+ }
3128
+ }
3129
+ };
3130
+ await visit(sessionDir);
3131
+ return found;
3132
+ }
3133
+ function toolSummary(input) {
3134
+ if (!input || typeof input !== "object")
3135
+ return;
3136
+ const object = input;
3137
+ for (const key of ["description", "summary", "path", "file_path", "query", "pattern", "command"]) {
3138
+ if (typeof object[key] === "string" && object[key].trim())
3139
+ return object[key].trim();
3140
+ }
3141
+ return;
3142
+ }
3143
+ function readString(object, key) {
3144
+ if (!object || typeof object !== "object")
3145
+ return;
3146
+ const value = object[key];
3147
+ return typeof value === "string" ? value : undefined;
3148
+ }
3149
+ function readBoolean(object, key) {
3150
+ return !!object && typeof object === "object" && object[key] === true;
3151
+ }
3152
+ function encodeClaudeProjectDirectory(cwd) {
3153
+ return cwd.replace(/[:\\/]/g, "-");
3154
+ }
3155
+ async function exists(path2) {
3156
+ try {
3157
+ await access(path2, fsConstants.R_OK);
3158
+ return true;
3159
+ } catch {
3160
+ return false;
3161
+ }
3162
+ }
3163
+ function throwIfAborted(signal) {
3164
+ if (signal?.aborted)
3165
+ throw new DOMException("background follow-up aborted", "AbortError");
3166
+ }
3167
+ async function waitForPoll(ms, signal) {
3168
+ if (!signal) {
3169
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
3170
+ return;
3171
+ }
3172
+ throwIfAborted(signal);
3173
+ await new Promise((resolve2, reject) => {
3174
+ const timer = setTimeout(() => {
3175
+ signal.removeEventListener("abort", onAbort);
3176
+ resolve2();
3177
+ }, ms);
3178
+ const onAbort = () => {
3179
+ clearTimeout(timer);
3180
+ reject(new DOMException("background follow-up aborted", "AbortError"));
3181
+ };
3182
+ signal.addEventListener("abort", onAbort, { once: true });
3183
+ });
3184
+ }
3185
+ var DEFAULT_POLL_INTERVAL_MS = 250, DEFAULT_TIMEOUT_MS, FINAL_SUBAGENT_DRAIN_MAX_POLLS = 4, FINAL_SUBAGENT_DRAIN_STABLE_POLLS = 2, ASYNC_AGENT_LAUNCH;
3186
+ var init_background_followup = __esm(() => {
3187
+ DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
3188
+ ASYNC_AGENT_LAUNCH = /Async agent launched successfully|["']?status["']?\s*:\s*["']async_launched["']/i;
3189
+ });
3190
+
2636
3191
  // src/transport/tool-event-mode.ts
2637
3192
  function resolveToolEventMode(input) {
2638
3193
  if (input?.toolEventMode !== undefined) {
@@ -2822,7 +3377,7 @@ function formatToolCallEvent(update, sessionUpdate) {
2822
3377
  return null;
2823
3378
  const emoji = TOOL_KIND_EMOJI[kind] ?? DEFAULT_TOOL_EMOJI;
2824
3379
  const inputSummary = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
2825
- const status = readString(update, "status");
3380
+ const status = readString2(update, "status");
2826
3381
  if (!inputSummary && status === "pending")
2827
3382
  return null;
2828
3383
  if (!inputSummary && isGenericToolTitle(kind, title))
@@ -2894,13 +3449,13 @@ function buildToolUseEvent(update, driver) {
2894
3449
  const toolName = title || "Tool";
2895
3450
  const summaryRaw = summarizeToolInput(update.rawInput, title) || summarizeToolInput(update.rawOutput, title);
2896
3451
  const summary = summaryRaw && summaryRaw !== title ? summaryRaw : undefined;
2897
- const statusRaw = readString(update, "status");
3452
+ const statusRaw = readString2(update, "status");
2898
3453
  const isClaudeDriver = driver === undefined || driver === "claude";
2899
3454
  const claudeMeta = isClaudeDriver ? update._meta?.claudeCode : undefined;
2900
3455
  const claudeToolResponse = claudeMeta?.toolResponse;
2901
3456
  const hasClaudeToolResponse = !isEmptyToolField(claudeToolResponse);
2902
- const isAsyncAgentLaunch = update._meta?.claudeCode?.toolName === "Agent" && isRecord2(claudeToolResponse) && claudeToolResponse.status === "async_launched";
2903
- const status = isAsyncAgentLaunch ? "running" : statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : hasClaudeToolResponse ? "success" : "running";
3457
+ const isAsyncAgentLaunch2 = update._meta?.claudeCode?.toolName === "Agent" && isRecord2(claudeToolResponse) && claudeToolResponse.status === "async_launched" || driver === "qoder" && update._meta?.qoder?.toolName === "Agent" && isAsyncAgentLaunchOutput(update.rawOutput);
3458
+ const status = isAsyncAgentLaunch2 ? "running" : statusRaw === "completed" || statusRaw === "success" ? "success" : statusRaw === "failed" || statusRaw === "error" ? "error" : hasClaudeToolResponse ? "success" : "running";
2904
3459
  const rawInput = update.rawInput;
2905
3460
  const content = update.content;
2906
3461
  const rawOutput = update.rawOutput ?? claudeToolResponse;
@@ -3029,7 +3584,7 @@ function normalizeUsageCost(value) {
3029
3584
  if (!isRecord2(value))
3030
3585
  return;
3031
3586
  const amount = asFiniteNumber(value.amount);
3032
- const currency = readString(value, "currency");
3587
+ const currency = readString2(value, "currency");
3033
3588
  if (amount === undefined && !currency)
3034
3589
  return;
3035
3590
  return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
@@ -3041,10 +3596,10 @@ function normalizeAgentCommands(value) {
3041
3596
  for (const entry of value) {
3042
3597
  if (!isRecord2(entry))
3043
3598
  continue;
3044
- const name = readString(entry, "name");
3599
+ const name = readString2(entry, "name");
3045
3600
  if (!name)
3046
3601
  continue;
3047
- const description = readString(entry, "description");
3602
+ const description = readString2(entry, "description");
3048
3603
  out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
3049
3604
  }
3050
3605
  return out;
@@ -3052,7 +3607,7 @@ function normalizeAgentCommands(value) {
3052
3607
  function isRecord2(value) {
3053
3608
  return typeof value === "object" && value !== null && !Array.isArray(value);
3054
3609
  }
3055
- function readString(rawInput, key) {
3610
+ function readString2(rawInput, key) {
3056
3611
  if (!isRecord2(rawInput))
3057
3612
  return;
3058
3613
  const value = rawInput[key];
@@ -3076,6 +3631,7 @@ function isGenericToolTitle(kind, title) {
3076
3631
  }
3077
3632
  var USAGE_BREAKDOWN_FIELDS;
3078
3633
  var init_streaming_prompt = __esm(() => {
3634
+ init_background_followup();
3079
3635
  init_tool_kind_emoji();
3080
3636
  USAGE_BREAKDOWN_FIELDS = [
3081
3637
  ["inputTokens", ["inputTokens", "input_tokens"]],
@@ -3097,22 +3653,22 @@ function isModelNotAdvertisedError(text) {
3097
3653
  // src/recovery/discover-parent-package-paths.ts
3098
3654
  import { spawn as spawn2 } from "node:child_process";
3099
3655
  import { createRequire as createRequire2 } from "node:module";
3100
- import { access } from "node:fs/promises";
3101
- import { homedir as homedir2 } from "node:os";
3102
- import { dirname as dirname2, join as join2 } from "node:path";
3656
+ import { access as access2 } from "node:fs/promises";
3657
+ import { homedir as homedir3 } from "node:os";
3658
+ import { dirname as dirname3, join as join3 } from "node:path";
3103
3659
  function deriveParentPackageName(platformPackage) {
3104
3660
  return platformPackage.replace(/-(?:linux|darwin|win32|windows|freebsd|openbsd|sunos|aix)(?:-(?:x64|arm64|ia32|arm|ppc64|s390x))?(?:-(?:baseline|musl|gnu|gnueabihf|musleabihf|msvc))?$/, "");
3105
3661
  }
3106
3662
  async function discoverParentPackagePaths(platformPackage, seedPath, deps = {}) {
3107
3663
  const env = deps.env ?? process.env;
3108
- const home = deps.home ?? homedir2();
3664
+ const home = deps.home ?? homedir3();
3109
3665
  const cwd = deps.cwd ?? process.cwd();
3110
3666
  const fsExists = deps.fsExists ?? defaultFsExists;
3111
3667
  const resolveFromCwd = deps.resolveFromCwd ?? defaultResolveFromCwd;
3112
3668
  const queryRoot = deps.queryPackageManagerRoot ?? defaultQueryPackageManagerRoot;
3113
3669
  const parentName = deriveParentPackageName(platformPackage);
3114
3670
  const rawCandidates = [];
3115
- const bunGlobalRoot = env.BUN_INSTALL ? join2(env.BUN_INSTALL, "install", "global", "node_modules") : join2(home, ".bun", "install", "global", "node_modules");
3671
+ const bunGlobalRoot = env.BUN_INSTALL ? join3(env.BUN_INSTALL, "install", "global", "node_modules") : join3(home, ".bun", "install", "global", "node_modules");
3116
3672
  const [npmRoot, pnpmRoot, yarnRoot] = await Promise.all([
3117
3673
  queryRoot("npm"),
3118
3674
  queryRoot("pnpm"),
@@ -3135,20 +3691,20 @@ async function discoverParentPackagePaths(platformPackage, seedPath, deps = {})
3135
3691
  if (resolved)
3136
3692
  rawCandidates.push({ path: resolved, manager: classify(resolved) });
3137
3693
  }
3138
- rawCandidates.push({ path: join2(bunGlobalRoot, parentName), manager: "bun" });
3694
+ rawCandidates.push({ path: join3(bunGlobalRoot, parentName), manager: "bun" });
3139
3695
  if (npmRoot)
3140
- rawCandidates.push({ path: join2(npmRoot, parentName), manager: "npm" });
3696
+ rawCandidates.push({ path: join3(npmRoot, parentName), manager: "npm" });
3141
3697
  if (pnpmRoot)
3142
- rawCandidates.push({ path: join2(pnpmRoot, parentName), manager: "pnpm" });
3698
+ rawCandidates.push({ path: join3(pnpmRoot, parentName), manager: "pnpm" });
3143
3699
  if (yarnRoot)
3144
- rawCandidates.push({ path: join2(yarnRoot, parentName), manager: "yarn" });
3700
+ rawCandidates.push({ path: join3(yarnRoot, parentName), manager: "yarn" });
3145
3701
  const seen = new Set;
3146
3702
  const verified = [];
3147
3703
  for (const candidate of rawCandidates) {
3148
3704
  if (seen.has(candidate.path))
3149
3705
  continue;
3150
3706
  seen.add(candidate.path);
3151
- if (await fsExists(join2(candidate.path, "package.json"))) {
3707
+ if (await fsExists(join3(candidate.path, "package.json"))) {
3152
3708
  verified.push(candidate);
3153
3709
  }
3154
3710
  }
@@ -3161,7 +3717,7 @@ function isUnder(child, parent) {
3161
3717
  }
3162
3718
  async function defaultFsExists(path2) {
3163
3719
  try {
3164
- await access(path2);
3720
+ await access2(path2);
3165
3721
  return true;
3166
3722
  } catch {
3167
3723
  return false;
@@ -3172,7 +3728,7 @@ function defaultResolveFromCwd(name, cwd) {
3172
3728
  const pkgJson = require2.resolve(`${name}/package.json`, {
3173
3729
  paths: [cwd, ...require2.resolve.paths(name) ?? []]
3174
3730
  });
3175
- return dirname2(pkgJson);
3731
+ return dirname3(pkgJson);
3176
3732
  } catch {
3177
3733
  return null;
3178
3734
  }
@@ -3219,7 +3775,7 @@ async function defaultQueryPackageManagerRoot(tool) {
3219
3775
  const trimmed = stdout.trim().split(/\r?\n/).pop()?.trim() ?? "";
3220
3776
  if (!trimmed)
3221
3777
  return done(null);
3222
- done(spec.postfix ? join2(trimmed, spec.postfix) : trimmed);
3778
+ done(spec.postfix ? join3(trimmed, spec.postfix) : trimmed);
3223
3779
  });
3224
3780
  });
3225
3781
  }
@@ -5537,9 +6093,9 @@ var PRIMARY_PREFIX = "XACPX_", LEGACY_PREFIX = "WEACPX_";
5537
6093
  // src/transport/acpx-queue-owner-launcher.ts
5538
6094
  import { createHash as createHash2 } from "node:crypto";
5539
6095
  import { spawn as spawn3 } from "node:child_process";
5540
- import { readFile, unlink } from "node:fs/promises";
5541
- import { homedir as homedir3 } from "node:os";
5542
- import { join as join3 } from "node:path";
6096
+ import { readFile as readFile2, unlink } from "node:fs/promises";
6097
+ import { homedir as homedir4 } from "node:os";
6098
+ import { join as join4 } from "node:path";
5543
6099
  function buildXacpxMcpServerSpec(input) {
5544
6100
  const { command, args } = splitCommandLine(input.xacpxCommand);
5545
6101
  return {
@@ -5701,7 +6257,7 @@ function createDefaultQueueOwnerTerminator(_acpxCommand) {
5701
6257
  async function readQueueOwnerPid(sessionId) {
5702
6258
  let owner;
5703
6259
  try {
5704
- owner = JSON.parse(await readFile(queueLockFilePath(sessionId), "utf8"));
6260
+ owner = JSON.parse(await readFile2(queueLockFilePath(sessionId), "utf8"));
5705
6261
  } catch {
5706
6262
  return;
5707
6263
  }
@@ -5714,7 +6270,7 @@ async function terminateAcpxQueueOwner(sessionId) {
5714
6270
  const lockPath = queueLockFilePath(sessionId);
5715
6271
  let owner;
5716
6272
  try {
5717
- owner = JSON.parse(await readFile(lockPath, "utf8"));
6273
+ owner = JSON.parse(await readFile2(lockPath, "utf8"));
5718
6274
  } catch {
5719
6275
  return;
5720
6276
  }
@@ -5724,7 +6280,7 @@ async function terminateAcpxQueueOwner(sessionId) {
5724
6280
  await unlink(lockPath).catch(() => {});
5725
6281
  }
5726
6282
  function queueLockFilePath(sessionId) {
5727
- return join3(homedir3(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
6283
+ return join4(homedir4(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
5728
6284
  }
5729
6285
  function shortHash(value, length) {
5730
6286
  return createHash2("sha256").update(value).digest("hex").slice(0, length);
@@ -5754,12 +6310,12 @@ var init_acpx_queue_owner_launcher = __esm(() => {
5754
6310
 
5755
6311
  // src/runtime/core-home.ts
5756
6312
  import { existsSync as existsSync2 } from "node:fs";
5757
- import { join as join4 } from "node:path";
6313
+ import { join as join5 } from "node:path";
5758
6314
  function coreHomeDir(home) {
5759
- const primary = join4(home, CORE_HOME_DIR_NAME);
6315
+ const primary = join5(home, CORE_HOME_DIR_NAME);
5760
6316
  if (existsSync2(primary))
5761
6317
  return primary;
5762
- const legacy = join4(home, CORE_HOME_LEGACY_DIR_NAME);
6318
+ const legacy = join5(home, CORE_HOME_LEGACY_DIR_NAME);
5763
6319
  if (existsSync2(legacy))
5764
6320
  return legacy;
5765
6321
  return primary;
@@ -5772,7 +6328,7 @@ var init_core_home = () => {};
5772
6328
 
5773
6329
  // src/orchestration/orchestration-ipc.ts
5774
6330
  import { createHash as createHash3 } from "node:crypto";
5775
- import { join as join5 } from "node:path";
6331
+ import { join as join6 } from "node:path";
5776
6332
  function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
5777
6333
  if (platform === "win32") {
5778
6334
  const suffix = createHash3("sha256").update(runtimeDir).digest("hex").slice(0, 12);
@@ -5783,7 +6339,7 @@ function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
5783
6339
  }
5784
6340
  return {
5785
6341
  kind: "unix",
5786
- path: join5(runtimeDir, "orchestration.sock")
6342
+ path: join6(runtimeDir, "orchestration.sock")
5787
6343
  };
5788
6344
  }
5789
6345
  function createOrchestrationEndpoint(path2, platform = process.platform) {
@@ -5803,20 +6359,20 @@ function encodeOrchestrationRpcResponse(response) {
5803
6359
  var init_orchestration_ipc = () => {};
5804
6360
 
5805
6361
  // src/daemon/daemon-files.ts
5806
- import { dirname as dirname3, join as join6 } from "node:path";
6362
+ import { dirname as dirname4, join as join7 } from "node:path";
5807
6363
  function resolveDaemonPaths(options) {
5808
- const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : join6(coreHomeDir(options.home), "runtime"));
6364
+ const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : join7(coreHomeDir(options.home), "runtime"));
5809
6365
  return {
5810
6366
  runtimeDir,
5811
- pidFile: join6(runtimeDir, "daemon.pid"),
5812
- statusFile: join6(runtimeDir, "status.json"),
5813
- stdoutLog: join6(runtimeDir, "stdout.log"),
5814
- stderrLog: join6(runtimeDir, "stderr.log"),
5815
- appLog: join6(runtimeDir, "app.log")
6367
+ pidFile: join7(runtimeDir, "daemon.pid"),
6368
+ statusFile: join7(runtimeDir, "status.json"),
6369
+ stdoutLog: join7(runtimeDir, "stdout.log"),
6370
+ stderrLog: join7(runtimeDir, "stderr.log"),
6371
+ appLog: join7(runtimeDir, "app.log")
5816
6372
  };
5817
6373
  }
5818
6374
  function resolveRuntimeDirFromConfigPath(configPath) {
5819
- return join6(dirname3(configPath), "runtime");
6375
+ return join7(dirname4(configPath), "runtime");
5820
6376
  }
5821
6377
  function resolveDaemonOrchestrationSocketPath(runtimeDir, platform = process.platform) {
5822
6378
  return resolveOrchestrationEndpoint(runtimeDir, platform).path;
@@ -5836,7 +6392,7 @@ var init_daemon_files = __esm(() => {
5836
6392
 
5837
6393
  // src/util/path.ts
5838
6394
  import path2 from "node:path";
5839
- import { homedir as homedir4 } from "node:os";
6395
+ import { homedir as homedir5 } from "node:os";
5840
6396
  function normalizePath(input) {
5841
6397
  const expanded = expandHome(input);
5842
6398
  if (isWindowsLikePath(expanded)) {
@@ -5864,7 +6420,7 @@ function isWindowsLikePath(input) {
5864
6420
  return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
5865
6421
  }
5866
6422
  function expandHome(input) {
5867
- return input.startsWith("~") ? homedir4() + input.slice(1) : input;
6423
+ return input.startsWith("~") ? homedir5() + input.slice(1) : input;
5868
6424
  }
5869
6425
  var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
5870
6426
  var init_path = __esm(() => {
@@ -5875,11 +6431,11 @@ var init_path = __esm(() => {
5875
6431
 
5876
6432
  // src/transport/codex-subagent-filter.ts
5877
6433
  import { closeSync, openSync, readdirSync, readSync, statSync } from "node:fs";
5878
- import { homedir as homedir5 } from "node:os";
5879
- import { join as join7 } from "node:path";
6434
+ import { homedir as homedir6 } from "node:os";
6435
+ import { join as join8 } from "node:path";
5880
6436
  function resolveCodexHome(env = process.env) {
5881
6437
  const fromEnv = env.CODEX_HOME?.trim();
5882
- return fromEnv ? fromEnv : join7(homedir5(), ".codex");
6438
+ return fromEnv ? fromEnv : join8(homedir6(), ".codex");
5883
6439
  }
5884
6440
  function isPlainObject(value) {
5885
6441
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -5944,7 +6500,7 @@ function filterSubagentSessions(result, isSubagent) {
5944
6500
  };
5945
6501
  }
5946
6502
  function nodeRolloutReader(home) {
5947
- const root = join7(home, "sessions");
6503
+ const root = join8(home, "sessions");
5948
6504
  return {
5949
6505
  listRolloutPaths() {
5950
6506
  const out = [];
@@ -5956,7 +6512,7 @@ function nodeRolloutReader(home) {
5956
6512
  return;
5957
6513
  }
5958
6514
  for (const entry of entries) {
5959
- const full = join7(dir, entry.name);
6515
+ const full = join8(dir, entry.name);
5960
6516
  if (entry.isDirectory())
5961
6517
  walk(full);
5962
6518
  else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl"))
@@ -6070,24 +6626,24 @@ var init_agent_session_list = __esm(() => {
6070
6626
  });
6071
6627
 
6072
6628
  // src/transport/acpx-session-files.ts
6073
- import { readdir, unlink as unlink2 } from "node:fs/promises";
6074
- import { homedir as homedir6 } from "node:os";
6075
- import { join as join8 } from "node:path";
6629
+ import { readdir as readdir2, unlink as unlink2 } from "node:fs/promises";
6630
+ import { homedir as homedir7 } from "node:os";
6631
+ import { join as join9 } from "node:path";
6076
6632
  async function deleteAcpxSessionFiles(options) {
6077
- const dir = options.sessionsDir ?? join8(homedir6(), ".acpx", "sessions");
6633
+ const dir = options.sessionsDir ?? join9(homedir7(), ".acpx", "sessions");
6078
6634
  const safeId = encodeURIComponent(options.acpxRecordId);
6079
- await unlink2(join8(dir, `${safeId}.json`)).catch(() => {
6635
+ await unlink2(join9(dir, `${safeId}.json`)).catch(() => {
6080
6636
  return;
6081
6637
  });
6082
6638
  let entries;
6083
6639
  try {
6084
- entries = await readdir(dir);
6640
+ entries = await readdir2(dir);
6085
6641
  } catch {
6086
6642
  return;
6087
6643
  }
6088
6644
  const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
6089
6645
  for (const name of streamFiles) {
6090
- await unlink2(join8(dir, name)).catch(() => {
6646
+ await unlink2(join9(dir, name)).catch(() => {
6091
6647
  return;
6092
6648
  });
6093
6649
  }
@@ -6352,9 +6908,9 @@ init_terminate_process_tree();
6352
6908
  init_prompt_output();
6353
6909
  init_prompt_media();
6354
6910
  init_streaming_prompt();
6355
- import { copyFile, readdir as readdir2 } from "node:fs/promises";
6356
- import { homedir as homedir7 } from "node:os";
6357
- import { dirname as dirname4, join as join9, win32 } from "node:path";
6911
+ import { copyFile, readdir as readdir3 } from "node:fs/promises";
6912
+ import { homedir as homedir8 } from "node:os";
6913
+ import { dirname as dirname5, join as join10, win32 } from "node:path";
6358
6914
  import { spawn as spawn4 } from "node:child_process";
6359
6915
 
6360
6916
  // src/bridge/parse-missing-optional-dep.ts
@@ -6623,7 +7179,7 @@ class BridgeRuntime {
6623
7179
  const resolved = __require.resolve(`${candidate}/package.json`, {
6624
7180
  paths: [process.cwd(), ...__require.resolve.paths(candidate) ?? []]
6625
7181
  });
6626
- return dirname4(resolved);
7182
+ return dirname5(resolved);
6627
7183
  } catch {
6628
7184
  continue;
6629
7185
  }
@@ -7056,14 +7612,14 @@ async function tryRepairAcpxSessionIndex(deps = {}) {
7056
7612
  if (platform !== "win32") {
7057
7613
  return false;
7058
7614
  }
7059
- const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir7();
7615
+ const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir8();
7060
7616
  if (!home) {
7061
7617
  return false;
7062
7618
  }
7063
- const pathJoin = platform === "win32" ? win32.join : join9;
7619
+ const pathJoin = platform === "win32" ? win32.join : join10;
7064
7620
  const sessionsDir = pathJoin(home, ".acpx", "sessions");
7065
7621
  const indexPath = pathJoin(sessionsDir, "index.json");
7066
- const readdirFn = deps.readdirFn ?? readdir2;
7622
+ const readdirFn = deps.readdirFn ?? readdir3;
7067
7623
  const copyFileFn = deps.copyFileFn ?? copyFile;
7068
7624
  let files;
7069
7625
  try {