@ganglion/xacpx 0.17.1 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2633,6 +2633,561 @@ var init_prompt_media = __esm(() => {
|
|
|
2633
2633
|
};
|
|
2634
2634
|
});
|
|
2635
2635
|
|
|
2636
|
+
// src/transport/claude-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 isClaudeAsyncAgentLaunch(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 claudeAsyncAgentId(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 findClaudeTranscriptPath(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 followClaudeBackgroundTurn(options) {
|
|
2690
|
+
const transcriptPath = options.transcriptPath ?? await findClaudeTranscriptPath(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 && isClaudeAsyncAgentLaunch(event)) {
|
|
2873
|
+
const agentId = claudeAsyncAgentId(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_claude_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 =
|
|
3380
|
+
const status = readString2(update, "status");
|
|
2826
3381
|
if (!inputSummary && status === "pending")
|
|
2827
3382
|
return null;
|
|
2828
3383
|
if (!inputSummary && isGenericToolTitle(kind, title))
|
|
@@ -2894,12 +3449,12 @@ 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 =
|
|
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";
|
|
3457
|
+
const isAsyncAgentLaunch = update._meta?.claudeCode?.toolName === "Agent" && isRecord2(claudeToolResponse) && claudeToolResponse.status === "async_launched" || driver === "qoder" && update._meta?.qoder?.toolName === "Agent" && isAsyncAgentLaunchOutput(update.rawOutput);
|
|
2903
3458
|
const status = isAsyncAgentLaunch ? "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;
|
|
@@ -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 =
|
|
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 =
|
|
3599
|
+
const name = readString2(entry, "name");
|
|
3045
3600
|
if (!name)
|
|
3046
3601
|
continue;
|
|
3047
|
-
const 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
|
|
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_claude_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
|
|
3102
|
-
import { dirname as
|
|
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 ??
|
|
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 ?
|
|
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:
|
|
3694
|
+
rawCandidates.push({ path: join3(bunGlobalRoot, parentName), manager: "bun" });
|
|
3139
3695
|
if (npmRoot)
|
|
3140
|
-
rawCandidates.push({ path:
|
|
3696
|
+
rawCandidates.push({ path: join3(npmRoot, parentName), manager: "npm" });
|
|
3141
3697
|
if (pnpmRoot)
|
|
3142
|
-
rawCandidates.push({ path:
|
|
3698
|
+
rawCandidates.push({ path: join3(pnpmRoot, parentName), manager: "pnpm" });
|
|
3143
3699
|
if (yarnRoot)
|
|
3144
|
-
rawCandidates.push({ path:
|
|
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(
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
5542
|
-
import { join as
|
|
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 {
|
|
@@ -5698,11 +6254,23 @@ function createDefaultQueueOwnerTerminator(_acpxCommand) {
|
|
|
5698
6254
|
await terminateAcpxQueueOwner(sessionId);
|
|
5699
6255
|
};
|
|
5700
6256
|
}
|
|
6257
|
+
async function readQueueOwnerPid(sessionId) {
|
|
6258
|
+
let owner;
|
|
6259
|
+
try {
|
|
6260
|
+
owner = JSON.parse(await readFile2(queueLockFilePath(sessionId), "utf8"));
|
|
6261
|
+
} catch {
|
|
6262
|
+
return;
|
|
6263
|
+
}
|
|
6264
|
+
if (typeof owner.pid === "number" && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
6265
|
+
return owner.pid;
|
|
6266
|
+
}
|
|
6267
|
+
return;
|
|
6268
|
+
}
|
|
5701
6269
|
async function terminateAcpxQueueOwner(sessionId) {
|
|
5702
6270
|
const lockPath = queueLockFilePath(sessionId);
|
|
5703
6271
|
let owner;
|
|
5704
6272
|
try {
|
|
5705
|
-
owner = JSON.parse(await
|
|
6273
|
+
owner = JSON.parse(await readFile2(lockPath, "utf8"));
|
|
5706
6274
|
} catch {
|
|
5707
6275
|
return;
|
|
5708
6276
|
}
|
|
@@ -5712,7 +6280,7 @@ async function terminateAcpxQueueOwner(sessionId) {
|
|
|
5712
6280
|
await unlink(lockPath).catch(() => {});
|
|
5713
6281
|
}
|
|
5714
6282
|
function queueLockFilePath(sessionId) {
|
|
5715
|
-
return
|
|
6283
|
+
return join4(homedir4(), ".acpx", "queues", `${shortHash(sessionId, 24)}.lock`);
|
|
5716
6284
|
}
|
|
5717
6285
|
function shortHash(value, length) {
|
|
5718
6286
|
return createHash2("sha256").update(value).digest("hex").slice(0, length);
|
|
@@ -5740,9 +6308,91 @@ var init_acpx_queue_owner_launcher = __esm(() => {
|
|
|
5740
6308
|
init_i18n();
|
|
5741
6309
|
});
|
|
5742
6310
|
|
|
6311
|
+
// src/runtime/core-home.ts
|
|
6312
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
6313
|
+
import { join as join5 } from "node:path";
|
|
6314
|
+
function coreHomeDir(home) {
|
|
6315
|
+
const primary = join5(home, CORE_HOME_DIR_NAME);
|
|
6316
|
+
if (existsSync2(primary))
|
|
6317
|
+
return primary;
|
|
6318
|
+
const legacy = join5(home, CORE_HOME_LEGACY_DIR_NAME);
|
|
6319
|
+
if (existsSync2(legacy))
|
|
6320
|
+
return legacy;
|
|
6321
|
+
return primary;
|
|
6322
|
+
}
|
|
6323
|
+
function coreHomeDisplayPath(...segments) {
|
|
6324
|
+
return ["~", CORE_HOME_DIR_NAME, ...segments].join("/");
|
|
6325
|
+
}
|
|
6326
|
+
var CORE_HOME_DIR_NAME = ".xacpx", CORE_HOME_LEGACY_DIR_NAME = ".weacpx";
|
|
6327
|
+
var init_core_home = () => {};
|
|
6328
|
+
|
|
6329
|
+
// src/orchestration/orchestration-ipc.ts
|
|
6330
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
6331
|
+
import { join as join6 } from "node:path";
|
|
6332
|
+
function resolveOrchestrationEndpoint(runtimeDir, platform = process.platform) {
|
|
6333
|
+
if (platform === "win32") {
|
|
6334
|
+
const suffix = createHash3("sha256").update(runtimeDir).digest("hex").slice(0, 12);
|
|
6335
|
+
return {
|
|
6336
|
+
kind: "named-pipe",
|
|
6337
|
+
path: `\\\\.\\pipe\\xacpx-orchestration-${suffix}`
|
|
6338
|
+
};
|
|
6339
|
+
}
|
|
6340
|
+
return {
|
|
6341
|
+
kind: "unix",
|
|
6342
|
+
path: join6(runtimeDir, "orchestration.sock")
|
|
6343
|
+
};
|
|
6344
|
+
}
|
|
6345
|
+
function createOrchestrationEndpoint(path2, platform = process.platform) {
|
|
6346
|
+
return {
|
|
6347
|
+
kind: platform === "win32" || path2.startsWith("\\\\.\\pipe\\") ? "named-pipe" : "unix",
|
|
6348
|
+
path: path2
|
|
6349
|
+
};
|
|
6350
|
+
}
|
|
6351
|
+
function encodeOrchestrationRpcRequest(request) {
|
|
6352
|
+
return `${JSON.stringify(request)}
|
|
6353
|
+
`;
|
|
6354
|
+
}
|
|
6355
|
+
function encodeOrchestrationRpcResponse(response) {
|
|
6356
|
+
return `${JSON.stringify(response)}
|
|
6357
|
+
`;
|
|
6358
|
+
}
|
|
6359
|
+
var init_orchestration_ipc = () => {};
|
|
6360
|
+
|
|
6361
|
+
// src/daemon/daemon-files.ts
|
|
6362
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
6363
|
+
function resolveDaemonPaths(options) {
|
|
6364
|
+
const runtimeDir = options.runtimeDir ?? (options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : join7(coreHomeDir(options.home), "runtime"));
|
|
6365
|
+
return {
|
|
6366
|
+
runtimeDir,
|
|
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")
|
|
6372
|
+
};
|
|
6373
|
+
}
|
|
6374
|
+
function resolveRuntimeDirFromConfigPath(configPath) {
|
|
6375
|
+
return join7(dirname4(configPath), "runtime");
|
|
6376
|
+
}
|
|
6377
|
+
function resolveDaemonOrchestrationSocketPath(runtimeDir, platform = process.platform) {
|
|
6378
|
+
return resolveOrchestrationEndpoint(runtimeDir, platform).path;
|
|
6379
|
+
}
|
|
6380
|
+
function isProcessAlive(pid) {
|
|
6381
|
+
try {
|
|
6382
|
+
process.kill(pid, 0);
|
|
6383
|
+
return true;
|
|
6384
|
+
} catch (error) {
|
|
6385
|
+
return error.code === "EPERM";
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6388
|
+
var init_daemon_files = __esm(() => {
|
|
6389
|
+
init_core_home();
|
|
6390
|
+
init_orchestration_ipc();
|
|
6391
|
+
});
|
|
6392
|
+
|
|
5743
6393
|
// src/util/path.ts
|
|
5744
6394
|
import path2 from "node:path";
|
|
5745
|
-
import { homedir as
|
|
6395
|
+
import { homedir as homedir5 } from "node:os";
|
|
5746
6396
|
function normalizePath(input) {
|
|
5747
6397
|
const expanded = expandHome(input);
|
|
5748
6398
|
if (isWindowsLikePath(expanded)) {
|
|
@@ -5770,7 +6420,7 @@ function isWindowsLikePath(input) {
|
|
|
5770
6420
|
return WINDOWS_DRIVE_PATH_RE.test(input) || WINDOWS_UNC_PATH_RE.test(input);
|
|
5771
6421
|
}
|
|
5772
6422
|
function expandHome(input) {
|
|
5773
|
-
return input.startsWith("~") ?
|
|
6423
|
+
return input.startsWith("~") ? homedir5() + input.slice(1) : input;
|
|
5774
6424
|
}
|
|
5775
6425
|
var WINDOWS_DRIVE_PATH_RE, WINDOWS_UNC_PATH_RE, ROOT_PATH_RE;
|
|
5776
6426
|
var init_path = __esm(() => {
|
|
@@ -5781,11 +6431,11 @@ var init_path = __esm(() => {
|
|
|
5781
6431
|
|
|
5782
6432
|
// src/transport/codex-subagent-filter.ts
|
|
5783
6433
|
import { closeSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
5784
|
-
import { homedir as
|
|
5785
|
-
import { join as
|
|
6434
|
+
import { homedir as homedir6 } from "node:os";
|
|
6435
|
+
import { join as join8 } from "node:path";
|
|
5786
6436
|
function resolveCodexHome(env = process.env) {
|
|
5787
6437
|
const fromEnv = env.CODEX_HOME?.trim();
|
|
5788
|
-
return fromEnv ? fromEnv :
|
|
6438
|
+
return fromEnv ? fromEnv : join8(homedir6(), ".codex");
|
|
5789
6439
|
}
|
|
5790
6440
|
function isPlainObject(value) {
|
|
5791
6441
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -5850,7 +6500,7 @@ function filterSubagentSessions(result, isSubagent) {
|
|
|
5850
6500
|
};
|
|
5851
6501
|
}
|
|
5852
6502
|
function nodeRolloutReader(home) {
|
|
5853
|
-
const root =
|
|
6503
|
+
const root = join8(home, "sessions");
|
|
5854
6504
|
return {
|
|
5855
6505
|
listRolloutPaths() {
|
|
5856
6506
|
const out = [];
|
|
@@ -5862,7 +6512,7 @@ function nodeRolloutReader(home) {
|
|
|
5862
6512
|
return;
|
|
5863
6513
|
}
|
|
5864
6514
|
for (const entry of entries) {
|
|
5865
|
-
const full =
|
|
6515
|
+
const full = join8(dir, entry.name);
|
|
5866
6516
|
if (entry.isDirectory())
|
|
5867
6517
|
walk(full);
|
|
5868
6518
|
else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl"))
|
|
@@ -5976,24 +6626,24 @@ var init_agent_session_list = __esm(() => {
|
|
|
5976
6626
|
});
|
|
5977
6627
|
|
|
5978
6628
|
// src/transport/acpx-session-files.ts
|
|
5979
|
-
import { readdir, unlink as unlink2 } from "node:fs/promises";
|
|
5980
|
-
import { homedir as
|
|
5981
|
-
import { join as
|
|
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";
|
|
5982
6632
|
async function deleteAcpxSessionFiles(options) {
|
|
5983
|
-
const dir = options.sessionsDir ??
|
|
6633
|
+
const dir = options.sessionsDir ?? join9(homedir7(), ".acpx", "sessions");
|
|
5984
6634
|
const safeId = encodeURIComponent(options.acpxRecordId);
|
|
5985
|
-
await unlink2(
|
|
6635
|
+
await unlink2(join9(dir, `${safeId}.json`)).catch(() => {
|
|
5986
6636
|
return;
|
|
5987
6637
|
});
|
|
5988
6638
|
let entries;
|
|
5989
6639
|
try {
|
|
5990
|
-
entries = await
|
|
6640
|
+
entries = await readdir2(dir);
|
|
5991
6641
|
} catch {
|
|
5992
6642
|
return;
|
|
5993
6643
|
}
|
|
5994
6644
|
const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
|
|
5995
6645
|
for (const name of streamFiles) {
|
|
5996
|
-
await unlink2(
|
|
6646
|
+
await unlink2(join9(dir, name)).catch(() => {
|
|
5997
6647
|
return;
|
|
5998
6648
|
});
|
|
5999
6649
|
}
|
|
@@ -6258,9 +6908,9 @@ init_terminate_process_tree();
|
|
|
6258
6908
|
init_prompt_output();
|
|
6259
6909
|
init_prompt_media();
|
|
6260
6910
|
init_streaming_prompt();
|
|
6261
|
-
import { copyFile, readdir as
|
|
6262
|
-
import { homedir as
|
|
6263
|
-
import { dirname as
|
|
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";
|
|
6264
6914
|
import { spawn as spawn4 } from "node:child_process";
|
|
6265
6915
|
|
|
6266
6916
|
// src/bridge/parse-missing-optional-dep.ts
|
|
@@ -6279,6 +6929,7 @@ function parseMissingOptionalDep(text) {
|
|
|
6279
6929
|
// src/bridge/bridge-runtime.ts
|
|
6280
6930
|
init_discover_parent_package_paths();
|
|
6281
6931
|
init_acpx_queue_owner_launcher();
|
|
6932
|
+
init_daemon_files();
|
|
6282
6933
|
init_agent_session_list();
|
|
6283
6934
|
init_codex_subagent_filter();
|
|
6284
6935
|
init_acpx_session_files();
|
|
@@ -6343,6 +6994,7 @@ class BridgeRuntime {
|
|
|
6343
6994
|
});
|
|
6344
6995
|
}
|
|
6345
6996
|
async resumeAgentSession(input) {
|
|
6997
|
+
this.invalidateRecordIdCache(input);
|
|
6346
6998
|
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
6347
6999
|
"sessions",
|
|
6348
7000
|
"new",
|
|
@@ -6412,6 +7064,7 @@ class BridgeRuntime {
|
|
|
6412
7064
|
throw new Error(message);
|
|
6413
7065
|
}
|
|
6414
7066
|
async ensureSession(input, onProgress) {
|
|
7067
|
+
this.invalidateRecordIdCache(input);
|
|
6415
7068
|
try {
|
|
6416
7069
|
return await this.attemptEnsureSession(input, onProgress);
|
|
6417
7070
|
} catch (error) {
|
|
@@ -6526,7 +7179,7 @@ class BridgeRuntime {
|
|
|
6526
7179
|
const resolved = __require.resolve(`${candidate}/package.json`, {
|
|
6527
7180
|
paths: [process.cwd(), ...__require.resolve.paths(candidate) ?? []]
|
|
6528
7181
|
});
|
|
6529
|
-
return
|
|
7182
|
+
return dirname5(resolved);
|
|
6530
7183
|
} catch {
|
|
6531
7184
|
continue;
|
|
6532
7185
|
}
|
|
@@ -6713,6 +7366,7 @@ class BridgeRuntime {
|
|
|
6713
7366
|
};
|
|
6714
7367
|
}
|
|
6715
7368
|
async removeSession(input) {
|
|
7369
|
+
this.invalidateRecordIdCache(input);
|
|
6716
7370
|
const spawnSpec = resolveSpawnCommand(this.command, this.buildSessionArgs(input, [
|
|
6717
7371
|
"sessions",
|
|
6718
7372
|
"close",
|
|
@@ -6751,6 +7405,27 @@ class BridgeRuntime {
|
|
|
6751
7405
|
await terminateAcpxQueueOwner(acpxRecordId);
|
|
6752
7406
|
return {};
|
|
6753
7407
|
}
|
|
7408
|
+
recordIdCache = new Map;
|
|
7409
|
+
recordIdCacheKey(input) {
|
|
7410
|
+
return JSON.stringify([input.agent, input.agentCommand ?? null, input.cwd, input.name]);
|
|
7411
|
+
}
|
|
7412
|
+
invalidateRecordIdCache(input) {
|
|
7413
|
+
this.recordIdCache.delete(this.recordIdCacheKey(input));
|
|
7414
|
+
}
|
|
7415
|
+
async isSessionWarm(input) {
|
|
7416
|
+
const cacheKey = this.recordIdCacheKey(input);
|
|
7417
|
+
let acpxRecordId = this.recordIdCache.get(cacheKey);
|
|
7418
|
+
if (!acpxRecordId) {
|
|
7419
|
+
try {
|
|
7420
|
+
({ acpxRecordId } = await this.readSessionRecord(input));
|
|
7421
|
+
} catch {
|
|
7422
|
+
return { warm: false };
|
|
7423
|
+
}
|
|
7424
|
+
this.recordIdCache.set(cacheKey, acpxRecordId);
|
|
7425
|
+
}
|
|
7426
|
+
const pid = await readQueueOwnerPid(acpxRecordId);
|
|
7427
|
+
return { warm: pid !== undefined && isProcessAlive(pid) };
|
|
7428
|
+
}
|
|
6754
7429
|
async shutdown() {
|
|
6755
7430
|
return {};
|
|
6756
7431
|
}
|
|
@@ -6937,14 +7612,14 @@ async function tryRepairAcpxSessionIndex(deps = {}) {
|
|
|
6937
7612
|
if (platform !== "win32") {
|
|
6938
7613
|
return false;
|
|
6939
7614
|
}
|
|
6940
|
-
const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ??
|
|
7615
|
+
const home = deps.home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir8();
|
|
6941
7616
|
if (!home) {
|
|
6942
7617
|
return false;
|
|
6943
7618
|
}
|
|
6944
|
-
const pathJoin = platform === "win32" ? win32.join :
|
|
7619
|
+
const pathJoin = platform === "win32" ? win32.join : join10;
|
|
6945
7620
|
const sessionsDir = pathJoin(home, ".acpx", "sessions");
|
|
6946
7621
|
const indexPath = pathJoin(sessionsDir, "index.json");
|
|
6947
|
-
const readdirFn = deps.readdirFn ??
|
|
7622
|
+
const readdirFn = deps.readdirFn ?? readdir3;
|
|
6948
7623
|
const copyFileFn = deps.copyFileFn ?? copyFile;
|
|
6949
7624
|
let files;
|
|
6950
7625
|
try {
|
|
@@ -6991,6 +7666,7 @@ var BRIDGE_METHODS = new Set([
|
|
|
6991
7666
|
"removeSession",
|
|
6992
7667
|
"deleteSession",
|
|
6993
7668
|
"freeWarmProcess",
|
|
7669
|
+
"isSessionWarm",
|
|
6994
7670
|
"getAgentSessionId"
|
|
6995
7671
|
]);
|
|
6996
7672
|
var SESSION_SCOPED_METHODS = new Set([
|
|
@@ -7008,6 +7684,7 @@ var SESSION_SCOPED_METHODS = new Set([
|
|
|
7008
7684
|
"removeSession",
|
|
7009
7685
|
"deleteSession",
|
|
7010
7686
|
"freeWarmProcess",
|
|
7687
|
+
"isSessionWarm",
|
|
7011
7688
|
"getAgentSessionId"
|
|
7012
7689
|
]);
|
|
7013
7690
|
|
|
@@ -7068,7 +7745,7 @@ class BridgeServer {
|
|
|
7068
7745
|
if (!sessionKey) {
|
|
7069
7746
|
return await this.dispatch(requestId, method, params, writeLine);
|
|
7070
7747
|
}
|
|
7071
|
-
const lane = method === "cancel" ? "control" : "normal";
|
|
7748
|
+
const lane = method === "cancel" || method === "isSessionWarm" ? "control" : "normal";
|
|
7072
7749
|
return await this.scheduler.run(sessionKey, lane, () => this.dispatch(requestId, method, params, writeLine));
|
|
7073
7750
|
}
|
|
7074
7751
|
async dispatch(requestId, method, params, writeLine) {
|
|
@@ -7279,6 +7956,14 @@ class BridgeServer {
|
|
|
7279
7956
|
cwd: requireString(params, "cwd"),
|
|
7280
7957
|
name: requireString(params, "name")
|
|
7281
7958
|
});
|
|
7959
|
+
case "isSessionWarm":
|
|
7960
|
+
return await this.runtime.isSessionWarm({
|
|
7961
|
+
agent: requireString(params, "agent"),
|
|
7962
|
+
...agentExecutionSettings(params),
|
|
7963
|
+
agentCommand: asOptionalString(params.agentCommand),
|
|
7964
|
+
cwd: requireString(params, "cwd"),
|
|
7965
|
+
name: requireString(params, "name")
|
|
7966
|
+
});
|
|
7282
7967
|
case "getAgentSessionId":
|
|
7283
7968
|
return await this.runtime.getAgentSessionId({
|
|
7284
7969
|
agent: requireString(params, "agent"),
|