svamp-cli 0.2.257 → 0.2.259
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/{agentCommands-fCsOp7ko.mjs → agentCommands-w72BRYhk.mjs} +6 -9
- package/dist/{auth-BH6Awl3O.mjs → auth-CLFNmaXL.mjs} +2 -5
- package/dist/cli.mjs +156 -69
- package/dist/{commands-DNIxtTXe.mjs → commands-BPxSEwAc.mjs} +3 -6
- package/dist/{commands-B6BkPRui.mjs → commands-BpcWHrQ7.mjs} +8 -9
- package/dist/{commands-CE1PdEJJ.mjs → commands-BtXxn3tI.mjs} +2 -5
- package/dist/{commands-B5veY89L.mjs → commands-CXiGBP6w.mjs} +2 -5
- package/dist/{commands-BpNDUqvv.mjs → commands-D3jsQ9Sj.mjs} +21 -18
- package/dist/{commands-BzF6GzdR.mjs → commands-DVEsNZUB.mjs} +2 -5
- package/dist/{commands-BCmpKBjg.mjs → commands-bQxTpr-M.mjs} +2 -5
- package/dist/{fleet-XYsLuf-3.mjs → fleet-BC9JHEWF.mjs} +3 -6
- package/dist/{frpc-QESvQN-s.mjs → frpc-BCOnJSAM.mjs} +11 -10
- package/dist/{headlessCli-DuCmcrAL.mjs → headlessCli-fagwmUja.mjs} +3 -6
- package/dist/index.mjs +2 -5
- package/dist/{package-BA3c2JLu.mjs → package-Dn7kNPhz.mjs} +2 -2
- package/dist/{pinnedClaudeCode-DuLXaoGP.mjs → pinnedClaudeCode-B9O-hKxm.mjs} +1 -1
- package/dist/{rpc-DCeDG4c7.mjs → rpc-D-1o_gWF.mjs} +2 -5
- package/dist/{rpc-Bscxex4z.mjs → rpc-Dko-Fkk6.mjs} +2 -5
- package/dist/{run-BmOCcZRG.mjs → run-BWBtm0gY.mjs} +1 -5
- package/dist/{run-BtGnnEMI.mjs → run-CT8Leg3-.mjs} +729 -425
- package/dist/{scheduler-Dbqmnd_o.mjs → scheduler-CN8jChyQ.mjs} +2 -5
- package/dist/{serveCommands-DueTsJSe.mjs → serveCommands-sWtLnJRW.mjs} +5 -5
- package/dist/{serveManager-BclzoPIb.mjs → serveManager-BuXk1_SH.mjs} +180 -77
- package/dist/{sideband-C_ifGgde.mjs → sideband-ByYvGtj3.mjs} +2 -5
- package/dist/staticFileServer-CbYnj2bH.mjs +255 -0
- package/package.json +2 -2
- package/dist/caddy-CuTbE3NY.mjs +0 -322
|
@@ -13,10 +13,7 @@ import { extname, resolve, join, sep, basename, dirname } from 'node:path';
|
|
|
13
13
|
import { EventEmitter } from 'node:events';
|
|
14
14
|
import os, { homedir, platform } from 'node:os';
|
|
15
15
|
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
|
|
16
|
-
import {
|
|
17
|
-
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
18
|
-
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
19
|
-
import { z } from 'zod';
|
|
16
|
+
import { createInterface } from 'node:readline';
|
|
20
17
|
import { mkdir, rm, chmod, access, mkdtemp, copyFile, writeFile, readdir, stat, readFile as readFile$1 } from 'node:fs/promises';
|
|
21
18
|
import { promisify as promisify$1 } from 'node:util';
|
|
22
19
|
import { parse, stringify } from 'yaml';
|
|
@@ -1670,7 +1667,7 @@ function resolveSender(channel, input = {}) {
|
|
|
1670
1667
|
const wsLc = hyphaWorkspace?.toLowerCase();
|
|
1671
1668
|
if (id.hypha_allow.includes("*") || allowLc.includes(userLc) || wsLc && allowLc.includes(wsLc))
|
|
1672
1669
|
return { sender: { name: hyphaUser, kind: "agent", verified: true } };
|
|
1673
|
-
return { error: "caller not in hypha_allow" };
|
|
1670
|
+
if (id.mode !== "per-key") return { error: "caller not in hypha_allow" };
|
|
1674
1671
|
}
|
|
1675
1672
|
if (id.mode === "fixed") {
|
|
1676
1673
|
if (!id.fixed?.name) return { error: "fixed identity not configured" };
|
|
@@ -1727,6 +1724,11 @@ class ChannelOutbox {
|
|
|
1727
1724
|
seqByChannel = /* @__PURE__ */ new Map();
|
|
1728
1725
|
emitter = new EventEmitter();
|
|
1729
1726
|
appendsSinceCompact = 0;
|
|
1727
|
+
// #0362: change-detection for reload() — a long-poll receiver reuses ONE instance and only
|
|
1728
|
+
// re-reads the on-disk log when it actually changed (mtime+size), instead of constructing a
|
|
1729
|
+
// fresh ChannelOutbox (full sync readFileSync) every 500ms poll tick.
|
|
1730
|
+
_lastMtimeMs = 0;
|
|
1731
|
+
_lastSize = -1;
|
|
1730
1732
|
constructor(projectDir) {
|
|
1731
1733
|
const dir = join(projectDir, ".svamp", "channels");
|
|
1732
1734
|
this.file = join(dir, "_outbox.jsonl");
|
|
@@ -1737,6 +1739,29 @@ class ChannelOutbox {
|
|
|
1737
1739
|
}
|
|
1738
1740
|
this._load();
|
|
1739
1741
|
}
|
|
1742
|
+
/** Current on-disk (mtimeMs, size), or (0, -1) if the file does not exist. */
|
|
1743
|
+
_stat() {
|
|
1744
|
+
try {
|
|
1745
|
+
const st = statSync(this.file);
|
|
1746
|
+
return { mtimeMs: st.mtimeMs, size: st.size };
|
|
1747
|
+
} catch {
|
|
1748
|
+
return { mtimeMs: 0, size: -1 };
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Re-read the on-disk log ONLY if it changed since the last load (mtime+size), so a
|
|
1753
|
+
* cross-process append (another session/daemon replying) is still picked up. Returns true
|
|
1754
|
+
* if a reload happened. Cheap no-op when nothing changed — the fix for the receive() hot
|
|
1755
|
+
* loop that re-instantiated + fully re-read the outbox every poll tick (#0362).
|
|
1756
|
+
*/
|
|
1757
|
+
reload() {
|
|
1758
|
+
const { mtimeMs, size } = this._stat();
|
|
1759
|
+
if (mtimeMs === this._lastMtimeMs && size === this._lastSize) return false;
|
|
1760
|
+
this.byChannel.clear();
|
|
1761
|
+
this.seqByChannel.clear();
|
|
1762
|
+
this._load();
|
|
1763
|
+
return true;
|
|
1764
|
+
}
|
|
1740
1765
|
_load() {
|
|
1741
1766
|
if (!existsSync(this.file)) return;
|
|
1742
1767
|
const cutoff = Date.now() - TTL_MS;
|
|
@@ -1764,6 +1789,9 @@ class ChannelOutbox {
|
|
|
1764
1789
|
let kept = 0;
|
|
1765
1790
|
for (const arr of this.byChannel.values()) kept += arr.length;
|
|
1766
1791
|
if (rawLines > kept) this._compact();
|
|
1792
|
+
const s = this._stat();
|
|
1793
|
+
this._lastMtimeMs = s.mtimeMs;
|
|
1794
|
+
this._lastSize = s.size;
|
|
1767
1795
|
}
|
|
1768
1796
|
/** Rewrite _outbox.jsonl to the current in-memory (TTL/MAX-bounded) window. Best-effort. */
|
|
1769
1797
|
_compact() {
|
|
@@ -2985,7 +3013,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2985
3013
|
const tunnels = handlers.tunnels;
|
|
2986
3014
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2987
3015
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2988
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
3016
|
+
const { FrpcTunnel } = await import('./frpc-BCOnJSAM.mjs');
|
|
2989
3017
|
const tunnel = new FrpcTunnel({
|
|
2990
3018
|
name: params.name,
|
|
2991
3019
|
ports: params.ports,
|
|
@@ -3452,7 +3480,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3452
3480
|
}
|
|
3453
3481
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3454
3482
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3455
|
-
const { toolsForRole } = await import('./sideband-
|
|
3483
|
+
const { toolsForRole } = await import('./sideband-ByYvGtj3.mjs');
|
|
3456
3484
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3457
3485
|
return fmt(r2);
|
|
3458
3486
|
}
|
|
@@ -3551,7 +3579,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3551
3579
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3552
3580
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3553
3581
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3554
|
-
const { queryCore } = await import('./commands-
|
|
3582
|
+
const { queryCore } = await import('./commands-BtXxn3tI.mjs');
|
|
3555
3583
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3556
3584
|
let result;
|
|
3557
3585
|
try {
|
|
@@ -3647,14 +3675,15 @@ ${d?.error || "not found"}`;
|
|
|
3647
3675
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3648
3676
|
const cursor = Math.max(0, Number(kwargs.cursor) || 0);
|
|
3649
3677
|
const waitMs = Math.min(Math.max(0, Number(kwargs.wait ?? 25) * 1e3), 6e4);
|
|
3678
|
+
const outbox = new ChannelOutbox(dir);
|
|
3650
3679
|
const deadline = Date.now() + waitMs;
|
|
3651
3680
|
for (; ; ) {
|
|
3652
|
-
const outbox = new ChannelOutbox(dir);
|
|
3653
3681
|
const replies = outbox.since(c.id, cursor, r.sender.name, kwargs.correlationId);
|
|
3654
3682
|
if (replies.length || Date.now() >= deadline) {
|
|
3655
3683
|
return { ok: true, replies, cursor: outbox.cursor(c.id) };
|
|
3656
3684
|
}
|
|
3657
3685
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3686
|
+
outbox.reload();
|
|
3658
3687
|
}
|
|
3659
3688
|
},
|
|
3660
3689
|
// Safety-restricted file upload (#0093). Deny-by-default: only channels whose
|
|
@@ -3798,7 +3827,10 @@ function readAwaiting(sessionId) {
|
|
|
3798
3827
|
function writeAwaiting(sessionId, map) {
|
|
3799
3828
|
try {
|
|
3800
3829
|
mkdirSync(join(SVAMP_HOME$2, "awaiting"), { recursive: true });
|
|
3801
|
-
|
|
3830
|
+
const p = awaitPath(sessionId);
|
|
3831
|
+
const tmp = `${p}.${process.pid}.${randomUUID()}.tmp`;
|
|
3832
|
+
writeFileSync(tmp, JSON.stringify(map));
|
|
3833
|
+
renameSync(tmp, p);
|
|
3802
3834
|
} catch {
|
|
3803
3835
|
}
|
|
3804
3836
|
}
|
|
@@ -3834,7 +3866,17 @@ function computeOutboundHop(sessionId) {
|
|
|
3834
3866
|
return { hopCount: 1, fromInboxTurn: false };
|
|
3835
3867
|
}
|
|
3836
3868
|
const buckets = /* @__PURE__ */ new Map();
|
|
3869
|
+
const FULL_REFILL_MS = URGENT_BURST * URGENT_REFILL_MS;
|
|
3870
|
+
let lastBucketSweep = 0;
|
|
3871
|
+
function pruneIdleBuckets(now) {
|
|
3872
|
+
if (now - lastBucketSweep < URGENT_REFILL_MS) return;
|
|
3873
|
+
lastBucketSweep = now;
|
|
3874
|
+
for (const [k, b] of buckets) {
|
|
3875
|
+
if (now - b.last >= FULL_REFILL_MS) buckets.delete(k);
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3837
3878
|
function takeUrgentToken(sender, now) {
|
|
3879
|
+
pruneIdleBuckets(now);
|
|
3838
3880
|
let b = buckets.get(sender);
|
|
3839
3881
|
if (!b) {
|
|
3840
3882
|
b = { tokens: URGENT_BURST, last: now };
|
|
@@ -8023,8 +8065,8 @@ var acpBackend = /*#__PURE__*/Object.freeze({
|
|
|
8023
8065
|
const KNOWN_ACP_AGENTS = {
|
|
8024
8066
|
gemini: { command: "gemini", args: ["--experimental-acp"] }
|
|
8025
8067
|
};
|
|
8026
|
-
const
|
|
8027
|
-
codex: { command: "codex", args: ["
|
|
8068
|
+
const KNOWN_CODEX_AGENTS = {
|
|
8069
|
+
codex: { command: "codex", args: ["app-server", "--listen", "stdio://"] }
|
|
8028
8070
|
};
|
|
8029
8071
|
function resolveAcpAgentConfig(cliArgs) {
|
|
8030
8072
|
if (cliArgs.length === 0) {
|
|
@@ -8051,12 +8093,12 @@ function resolveAcpAgentConfig(cliArgs) {
|
|
|
8051
8093
|
args: [...knownAcp.args, ...passthroughArgs]
|
|
8052
8094
|
};
|
|
8053
8095
|
}
|
|
8054
|
-
const
|
|
8055
|
-
if (
|
|
8096
|
+
const knownCodex = KNOWN_CODEX_AGENTS[agentName];
|
|
8097
|
+
if (knownCodex) {
|
|
8056
8098
|
return {
|
|
8057
8099
|
agentName,
|
|
8058
|
-
command:
|
|
8059
|
-
args: [...
|
|
8100
|
+
command: knownCodex.command,
|
|
8101
|
+
args: [...knownCodex.args, ...cliArgs.slice(1)]
|
|
8060
8102
|
};
|
|
8061
8103
|
}
|
|
8062
8104
|
return {
|
|
@@ -8069,7 +8111,7 @@ function resolveAcpAgentConfig(cliArgs) {
|
|
|
8069
8111
|
var acpAgentConfig = /*#__PURE__*/Object.freeze({
|
|
8070
8112
|
__proto__: null,
|
|
8071
8113
|
KNOWN_ACP_AGENTS: KNOWN_ACP_AGENTS,
|
|
8072
|
-
|
|
8114
|
+
KNOWN_CODEX_AGENTS: KNOWN_CODEX_AGENTS,
|
|
8073
8115
|
resolveAcpAgentConfig: resolveAcpAgentConfig
|
|
8074
8116
|
});
|
|
8075
8117
|
|
|
@@ -8078,12 +8120,12 @@ function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, l
|
|
|
8078
8120
|
let turnText = "";
|
|
8079
8121
|
let flushTimer = null;
|
|
8080
8122
|
let bridgeStopped = false;
|
|
8123
|
+
const pushAssistant = (content) => {
|
|
8124
|
+
sessionService.pushMessage({ type: "assistant", message: { role: "assistant", content } }, "agent");
|
|
8125
|
+
};
|
|
8081
8126
|
function flushText() {
|
|
8082
8127
|
if (pendingText) {
|
|
8083
|
-
|
|
8084
|
-
type: "assistant",
|
|
8085
|
-
content: [{ type: "text", text: pendingText }]
|
|
8086
|
-
}, "agent");
|
|
8128
|
+
pushAssistant([{ type: "text", text: pendingText }]);
|
|
8087
8129
|
pendingText = "";
|
|
8088
8130
|
}
|
|
8089
8131
|
if (flushTimer) {
|
|
@@ -8104,10 +8146,7 @@ function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, l
|
|
|
8104
8146
|
} else if (msg.fullText) {
|
|
8105
8147
|
turnText += msg.fullText;
|
|
8106
8148
|
flushText();
|
|
8107
|
-
|
|
8108
|
-
type: "assistant",
|
|
8109
|
-
content: [{ type: "text", text: msg.fullText }]
|
|
8110
|
-
}, "agent");
|
|
8149
|
+
pushAssistant([{ type: "text", text: msg.fullText }]);
|
|
8111
8150
|
}
|
|
8112
8151
|
break;
|
|
8113
8152
|
}
|
|
@@ -8144,25 +8183,25 @@ function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, l
|
|
|
8144
8183
|
}
|
|
8145
8184
|
case "tool-call": {
|
|
8146
8185
|
flushText();
|
|
8147
|
-
|
|
8148
|
-
type: "
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
input: msg.args
|
|
8154
|
-
}]
|
|
8155
|
-
}, "agent");
|
|
8186
|
+
pushAssistant([{
|
|
8187
|
+
type: "tool_use",
|
|
8188
|
+
id: msg.callId,
|
|
8189
|
+
name: msg.toolName,
|
|
8190
|
+
input: msg.args
|
|
8191
|
+
}]);
|
|
8156
8192
|
break;
|
|
8157
8193
|
}
|
|
8158
8194
|
case "tool-result": {
|
|
8159
8195
|
sessionService.pushMessage({
|
|
8160
|
-
type: "
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8196
|
+
type: "user",
|
|
8197
|
+
message: {
|
|
8198
|
+
role: "user",
|
|
8199
|
+
content: [{
|
|
8200
|
+
type: "tool_result",
|
|
8201
|
+
tool_use_id: msg.callId,
|
|
8202
|
+
content: typeof msg.result === "string" ? msg.result : JSON.stringify(msg.result)
|
|
8203
|
+
}]
|
|
8204
|
+
}
|
|
8166
8205
|
}, "agent");
|
|
8167
8206
|
break;
|
|
8168
8207
|
}
|
|
@@ -8205,10 +8244,7 @@ function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, l
|
|
|
8205
8244
|
if (msg.name === "thinking") {
|
|
8206
8245
|
const payload = msg.payload;
|
|
8207
8246
|
const text = payload?.text || JSON.stringify(payload);
|
|
8208
|
-
|
|
8209
|
-
type: "assistant",
|
|
8210
|
-
content: [{ type: "thinking", thinking: text }]
|
|
8211
|
-
}, "agent");
|
|
8247
|
+
pushAssistant([{ type: "thinking", thinking: text }]);
|
|
8212
8248
|
} else {
|
|
8213
8249
|
sessionService.pushMessage({
|
|
8214
8250
|
type: "session_event",
|
|
@@ -8266,440 +8302,649 @@ class HyphaPermissionHandler {
|
|
|
8266
8302
|
}
|
|
8267
8303
|
}
|
|
8268
8304
|
|
|
8269
|
-
|
|
8270
|
-
|
|
8305
|
+
function mapCodexEventToText(event) {
|
|
8306
|
+
if (!event || typeof event !== "object") return "";
|
|
8307
|
+
if (typeof event.message === "string") return event.message;
|
|
8308
|
+
if (typeof event.text === "string") return event.text;
|
|
8309
|
+
const content = event.content;
|
|
8310
|
+
if (Array.isArray(content)) {
|
|
8311
|
+
return content.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("");
|
|
8312
|
+
}
|
|
8313
|
+
const delta = event.delta;
|
|
8314
|
+
if (delta && typeof delta === "object" && typeof delta.text === "string") return delta.text;
|
|
8315
|
+
if (typeof delta === "string") return delta;
|
|
8316
|
+
return "";
|
|
8317
|
+
}
|
|
8318
|
+
function normalizeNotification(method, params) {
|
|
8319
|
+
if (method === "item/agentMessage/delta") {
|
|
8320
|
+
return typeof params?.delta === "string" ? { type: "agent_message_delta", delta: params.delta } : null;
|
|
8321
|
+
}
|
|
8322
|
+
if (method === "item/reasoning/delta") {
|
|
8323
|
+
return typeof params?.delta === "string" ? { type: "agent_reasoning_delta", delta: params.delta } : null;
|
|
8324
|
+
}
|
|
8325
|
+
if (method === "item/started" || method === "item/completed" || method === "item/updated") {
|
|
8326
|
+
const item = params?.item;
|
|
8327
|
+
if (!item || typeof item !== "object") return null;
|
|
8328
|
+
const done = method === "item/completed";
|
|
8329
|
+
switch (item.type) {
|
|
8330
|
+
case "agentMessage":
|
|
8331
|
+
return done && typeof item.text === "string" && item.text ? { type: "agent_message", message: item.text } : null;
|
|
8332
|
+
case "reasoning": {
|
|
8333
|
+
if (!done) return null;
|
|
8334
|
+
const text = Array.isArray(item.content) ? item.content.join("") : Array.isArray(item.summary) ? item.summary.join("") : typeof item.text === "string" ? item.text : "";
|
|
8335
|
+
return text ? { type: "agent_reasoning", text } : null;
|
|
8336
|
+
}
|
|
8337
|
+
case "commandExecution":
|
|
8338
|
+
return done ? { type: "exec_command_end", call_id: item.id, exit_code: item.exitCode, stdout: item.aggregatedOutput ?? "", stderr: "" } : { type: "exec_command_begin", call_id: item.id, command: item.command, cwd: item.cwd };
|
|
8339
|
+
case "fileChange":
|
|
8340
|
+
return done ? { type: "patch_apply_end", call_id: item.id, applied: item.status === "completed" || item.status === "applied" } : { type: "patch_apply_begin", call_id: item.id, changes: item.changes };
|
|
8341
|
+
case "mcpToolCall":
|
|
8342
|
+
return done ? { type: "mcp_tool_call_end", call_id: item.id, tool: item.tool, result: item.result, error: item.error } : { type: "mcp_tool_call_begin", call_id: item.id, tool: item.tool, server: item.server, arguments: item.arguments };
|
|
8343
|
+
case "userMessage":
|
|
8344
|
+
default:
|
|
8345
|
+
return null;
|
|
8346
|
+
}
|
|
8347
|
+
}
|
|
8348
|
+
if (method === "thread/tokenUsage/updated") {
|
|
8349
|
+
return { type: "token_usage", usage: params?.usage ?? params };
|
|
8350
|
+
}
|
|
8351
|
+
return null;
|
|
8352
|
+
}
|
|
8353
|
+
|
|
8354
|
+
function parseCodexVersion(v) {
|
|
8355
|
+
const m = v.match(/codex-cli\s+(\d+)\.(\d+)\.(\d+)/);
|
|
8356
|
+
if (!m) return null;
|
|
8357
|
+
const [major, minor, patch] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
8358
|
+
if (![major, minor, patch].every(Number.isFinite)) return null;
|
|
8359
|
+
return { major, minor, patch };
|
|
8360
|
+
}
|
|
8361
|
+
function codexAppServerAvailable() {
|
|
8271
8362
|
try {
|
|
8272
|
-
const
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
const versionStr = match[1];
|
|
8276
|
-
const [major, minor, patch] = versionStr.split(/[-.]/).map(Number);
|
|
8277
|
-
if (major > 0 || minor > 43) return "mcp-server";
|
|
8278
|
-
if (minor === 43 && patch === 0) {
|
|
8279
|
-
if (versionStr.includes("-alpha.")) {
|
|
8280
|
-
const alphaNum = parseInt(versionStr.split("-alpha.")[1]);
|
|
8281
|
-
return alphaNum >= 5 ? "mcp-server" : "mcp";
|
|
8282
|
-
}
|
|
8283
|
-
return "mcp-server";
|
|
8284
|
-
}
|
|
8285
|
-
return "mcp";
|
|
8363
|
+
const v = parseCodexVersion(execSync("codex --version", { encoding: "utf8" }).trim());
|
|
8364
|
+
if (!v) return false;
|
|
8365
|
+
return v.major > 0 || v.minor >= 100;
|
|
8286
8366
|
} catch {
|
|
8287
8367
|
return null;
|
|
8288
8368
|
}
|
|
8289
8369
|
}
|
|
8290
|
-
class
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
conversationId = null;
|
|
8301
|
-
svampSessionId = null;
|
|
8302
|
-
log;
|
|
8303
|
-
options;
|
|
8370
|
+
class CodexAppServerClient {
|
|
8371
|
+
constructor(opts) {
|
|
8372
|
+
this.opts = opts;
|
|
8373
|
+
this.log = opts.log ?? (() => {
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8376
|
+
process = null;
|
|
8377
|
+
readline = null;
|
|
8378
|
+
nextId = 1;
|
|
8379
|
+
pending = /* @__PURE__ */ new Map();
|
|
8304
8380
|
connected = false;
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
//
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8381
|
+
_threadId = null;
|
|
8382
|
+
_turnId = null;
|
|
8383
|
+
// A turn is "in flight" from turn/start until task_complete / turn_aborted (or turn/completed).
|
|
8384
|
+
pendingTurn = null;
|
|
8385
|
+
completedTurnIds = /* @__PURE__ */ new Set();
|
|
8386
|
+
log;
|
|
8387
|
+
get threadId() {
|
|
8388
|
+
return this._threadId;
|
|
8389
|
+
}
|
|
8390
|
+
get isConnected() {
|
|
8391
|
+
return this.connected;
|
|
8392
|
+
}
|
|
8393
|
+
// ── lifecycle ────────────────────────────────────────────────────────────
|
|
8394
|
+
async start() {
|
|
8395
|
+
if (this.connected) return;
|
|
8396
|
+
let command = "codex";
|
|
8397
|
+
let args = ["app-server", "--listen", "stdio://", ...this.opts.extraArgs ?? []];
|
|
8398
|
+
if (this.opts.isolationConfig) {
|
|
8399
|
+
const wrapped = wrapWithIsolation(command, args, this.opts.isolationConfig);
|
|
8400
|
+
command = wrapped.command;
|
|
8401
|
+
args = wrapped.args;
|
|
8402
|
+
}
|
|
8403
|
+
const child = spawn(command, args, {
|
|
8404
|
+
cwd: this.opts.cwd,
|
|
8405
|
+
// RUST_LOG mutes rollout noise on stderr; keep the rest of env for auth (~/.codex or key).
|
|
8406
|
+
env: { RUST_LOG: "error", ...process.env, ...this.opts.env ?? {} },
|
|
8407
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8312
8408
|
});
|
|
8313
|
-
this.
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
this.
|
|
8409
|
+
this.process = child;
|
|
8410
|
+
child.stderr?.on("data", (d) => this.log("[codex-app-server:stderr]", d.toString().trim()));
|
|
8411
|
+
child.on("exit", (code) => {
|
|
8412
|
+
const wasConnected = this.connected;
|
|
8413
|
+
this.connected = false;
|
|
8414
|
+
this.failAllPending(new Error(`codex app-server exited (code ${code})`));
|
|
8415
|
+
this.resolvePendingTurn(true);
|
|
8416
|
+
if (wasConnected) this.opts.onExit?.(code);
|
|
8417
|
+
});
|
|
8418
|
+
child.on("error", (err) => {
|
|
8419
|
+
this.log("[codex-app-server] spawn error", err);
|
|
8420
|
+
});
|
|
8421
|
+
this.readline = createInterface({ input: child.stdout });
|
|
8422
|
+
this.readline.on("line", (line) => this.onLine(line));
|
|
8423
|
+
const initParams = {
|
|
8424
|
+
clientInfo: { name: "svamp-codex", title: "Svamp Codex Client", version: "2.0.0" },
|
|
8425
|
+
capabilities: { experimentalApi: true }
|
|
8426
|
+
};
|
|
8427
|
+
await this.request("initialize", initParams);
|
|
8428
|
+
this.notify("initialized");
|
|
8429
|
+
this.connected = true;
|
|
8430
|
+
this.log("[codex-app-server] connected + initialized");
|
|
8431
|
+
}
|
|
8432
|
+
async startThread(o) {
|
|
8433
|
+
const params = {
|
|
8434
|
+
model: o.model ?? null,
|
|
8435
|
+
modelProvider: null,
|
|
8436
|
+
profile: null,
|
|
8437
|
+
cwd: o.cwd ?? this.opts.cwd,
|
|
8438
|
+
approvalPolicy: o.approvalPolicy ?? null,
|
|
8439
|
+
sandbox: o.sandbox ?? null,
|
|
8440
|
+
config: null,
|
|
8441
|
+
baseInstructions: null,
|
|
8442
|
+
developerInstructions: null,
|
|
8443
|
+
compactPrompt: null,
|
|
8444
|
+
includeApplyPatchTool: null,
|
|
8445
|
+
experimentalRawEvents: false,
|
|
8446
|
+
persistExtendedHistory: true
|
|
8447
|
+
};
|
|
8448
|
+
const r = await this.request("thread/start", params);
|
|
8449
|
+
this._threadId = r.thread.id;
|
|
8450
|
+
this._turnId = null;
|
|
8451
|
+
this.log("[codex-app-server] thread started", this._threadId);
|
|
8452
|
+
return { threadId: r.thread.id, model: r.model };
|
|
8453
|
+
}
|
|
8454
|
+
async resumeThread(o) {
|
|
8455
|
+
const params = {
|
|
8456
|
+
threadId: o.threadId,
|
|
8457
|
+
model: o.model ?? null,
|
|
8458
|
+
modelProvider: null,
|
|
8459
|
+
cwd: o.cwd ?? this.opts.cwd,
|
|
8460
|
+
approvalPolicy: o.approvalPolicy ?? null,
|
|
8461
|
+
sandbox: o.sandbox ?? null,
|
|
8462
|
+
config: null,
|
|
8463
|
+
baseInstructions: null,
|
|
8464
|
+
developerInstructions: null,
|
|
8465
|
+
persistExtendedHistory: true
|
|
8466
|
+
};
|
|
8467
|
+
const r = await this.request("thread/resume", params);
|
|
8468
|
+
this._threadId = r.thread.id;
|
|
8469
|
+
this._turnId = null;
|
|
8470
|
+
this.log("[codex-app-server] thread resumed", this._threadId);
|
|
8471
|
+
return { threadId: r.thread.id, model: r.model };
|
|
8472
|
+
}
|
|
8473
|
+
/** Build the turn/start params (exported shape kept minimal; only set fields are sent). */
|
|
8474
|
+
buildTurnParams(prompt, o) {
|
|
8475
|
+
const params = { threadId: this._threadId, input: [{ type: "text", text: prompt }] };
|
|
8476
|
+
if (o?.approvalPolicy) params.approvalPolicy = o.approvalPolicy;
|
|
8477
|
+
if (o?.model) params.model = o.model;
|
|
8478
|
+
if (o?.effort) params.effort = o.effort;
|
|
8479
|
+
if (o?.sandbox) {
|
|
8480
|
+
params.sandboxPolicy = { type: o.sandbox === "workspace-write" ? "workspaceWrite" : o.sandbox === "danger-full-access" ? "dangerFullAccess" : "readOnly" };
|
|
8481
|
+
}
|
|
8482
|
+
return params;
|
|
8483
|
+
}
|
|
8484
|
+
/** Start a turn; resolves immediately (completion is tracked via events / waitForTurn). */
|
|
8485
|
+
async startTurn(prompt, o) {
|
|
8486
|
+
if (!this._threadId) throw new Error("No active codex thread \u2014 call startThread first");
|
|
8487
|
+
const result = await this.request("turn/start", this.buildTurnParams(prompt, o));
|
|
8488
|
+
const id = result?.turn?.id;
|
|
8489
|
+
if (typeof id === "string" && id) this._turnId = id;
|
|
8490
|
+
}
|
|
8491
|
+
/** Send a turn and await completion (resolves {aborted} on task_complete/turn_aborted/timeout). */
|
|
8492
|
+
async sendTurnAndWait(prompt, o) {
|
|
8493
|
+
const timeoutMs = o?.timeoutMs ?? 14 * 24 * 60 * 60 * 1e3;
|
|
8494
|
+
let timer = null;
|
|
8495
|
+
const completion = new Promise((resolve) => {
|
|
8496
|
+
this.pendingTurn = { resolve };
|
|
8497
|
+
timer = setTimeout(() => {
|
|
8498
|
+
this.log("[codex-app-server] turn timeout");
|
|
8499
|
+
this.resolvePendingTurn(true);
|
|
8500
|
+
}, timeoutMs);
|
|
8501
|
+
timer?.unref?.();
|
|
8324
8502
|
});
|
|
8503
|
+
try {
|
|
8504
|
+
await this.startTurn(prompt, o);
|
|
8505
|
+
} catch (e) {
|
|
8506
|
+
if (timer) clearTimeout(timer);
|
|
8507
|
+
this.pendingTurn = null;
|
|
8508
|
+
throw e;
|
|
8509
|
+
}
|
|
8510
|
+
const aborted = await completion;
|
|
8511
|
+
if (timer) clearTimeout(timer);
|
|
8512
|
+
return { aborted };
|
|
8325
8513
|
}
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
this.
|
|
8514
|
+
/** Interrupt the in-flight turn (real server-side abort — the old MCP path couldn't do this). */
|
|
8515
|
+
async interrupt() {
|
|
8516
|
+
if (!this._threadId || !this._turnId) return;
|
|
8517
|
+
const params = { threadId: this._threadId, turnId: this._turnId };
|
|
8518
|
+
try {
|
|
8519
|
+
await this.request("turn/interrupt", params, 1e4);
|
|
8520
|
+
} catch (e) {
|
|
8521
|
+
this.log("[codex-app-server] interrupt err (may be benign)", e);
|
|
8522
|
+
}
|
|
8329
8523
|
}
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8524
|
+
async dispose() {
|
|
8525
|
+
this.readline?.close();
|
|
8526
|
+
this.readline = null;
|
|
8527
|
+
const proc = this.process;
|
|
8528
|
+
const pid = proc?.pid;
|
|
8529
|
+
try {
|
|
8530
|
+
proc?.stdin?.end();
|
|
8531
|
+
proc?.kill("SIGTERM");
|
|
8532
|
+
} catch {
|
|
8533
|
+
}
|
|
8534
|
+
if (pid) {
|
|
8535
|
+
const t = setTimeout(() => {
|
|
8536
|
+
try {
|
|
8537
|
+
process.kill(pid, 0);
|
|
8538
|
+
process.kill(pid, "SIGKILL");
|
|
8539
|
+
} catch {
|
|
8540
|
+
}
|
|
8541
|
+
}, 2e3);
|
|
8542
|
+
t.unref?.();
|
|
8543
|
+
}
|
|
8544
|
+
this.process = null;
|
|
8545
|
+
this.connected = false;
|
|
8546
|
+
this._threadId = null;
|
|
8547
|
+
this._turnId = null;
|
|
8548
|
+
this.failAllPending(new Error("codex app-server disposed"));
|
|
8549
|
+
this.resolvePendingTurn(true);
|
|
8550
|
+
}
|
|
8551
|
+
// ── JSON-RPC plumbing ──────────────────────────────────────────────────────
|
|
8552
|
+
request(method, params, timeoutMs) {
|
|
8553
|
+
const id = this.nextId++;
|
|
8554
|
+
return new Promise((resolve, reject) => {
|
|
8555
|
+
this.pending.set(id, { resolve, reject, method });
|
|
8556
|
+
if (timeoutMs) {
|
|
8557
|
+
const t = setTimeout(() => {
|
|
8558
|
+
if (this.pending.has(id)) {
|
|
8559
|
+
this.pending.delete(id);
|
|
8560
|
+
reject(new Error(`codex ${method} timed out after ${timeoutMs}ms`));
|
|
8561
|
+
}
|
|
8562
|
+
}, timeoutMs);
|
|
8563
|
+
t.unref?.();
|
|
8564
|
+
}
|
|
8565
|
+
this.write({ jsonrpc: "2.0", id, method, params });
|
|
8566
|
+
});
|
|
8567
|
+
}
|
|
8568
|
+
notify(method, params) {
|
|
8569
|
+
this.write({ jsonrpc: "2.0", method, params });
|
|
8570
|
+
}
|
|
8571
|
+
respond(id, result) {
|
|
8572
|
+
this.write({ jsonrpc: "2.0", id, result });
|
|
8333
8573
|
}
|
|
8574
|
+
write(obj) {
|
|
8575
|
+
try {
|
|
8576
|
+
this.process?.stdin?.write(JSON.stringify(obj) + "\n");
|
|
8577
|
+
} catch (e) {
|
|
8578
|
+
this.log("[codex-app-server] write error", e);
|
|
8579
|
+
}
|
|
8580
|
+
}
|
|
8581
|
+
failAllPending(err) {
|
|
8582
|
+
for (const [id, p] of this.pending) {
|
|
8583
|
+
p.reject(err);
|
|
8584
|
+
this.pending.delete(id);
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
onLine(line) {
|
|
8588
|
+
const trimmed = line.trim();
|
|
8589
|
+
if (!trimmed) return;
|
|
8590
|
+
let msg;
|
|
8591
|
+
try {
|
|
8592
|
+
msg = JSON.parse(trimmed);
|
|
8593
|
+
} catch {
|
|
8594
|
+
this.log("[codex-app-server] non-JSON line", trimmed.slice(0, 200));
|
|
8595
|
+
return;
|
|
8596
|
+
}
|
|
8597
|
+
if (msg.id !== void 0 && msg.method === void 0) {
|
|
8598
|
+
const p = this.pending.get(msg.id);
|
|
8599
|
+
if (!p) return;
|
|
8600
|
+
this.pending.delete(msg.id);
|
|
8601
|
+
const r = msg;
|
|
8602
|
+
if (r.error) p.reject(new Error(`codex ${p.method}: ${r.error.message}`));
|
|
8603
|
+
else p.resolve(r.result);
|
|
8604
|
+
return;
|
|
8605
|
+
}
|
|
8606
|
+
if (msg.id !== void 0 && typeof msg.method === "string") {
|
|
8607
|
+
void this.handleServerRequest(msg.id, msg.method, msg.params);
|
|
8608
|
+
return;
|
|
8609
|
+
}
|
|
8610
|
+
if (typeof msg.method === "string") {
|
|
8611
|
+
this.handleNotification(msg.method, msg.params);
|
|
8612
|
+
return;
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
// ── server→client approval requests ────────────────────────────────────────
|
|
8616
|
+
async handleServerRequest(id, method, params) {
|
|
8617
|
+
const handler = this.opts.onApproval;
|
|
8618
|
+
if (method === "item/commandExecution/requestApproval" || method === "execCommandApproval") {
|
|
8619
|
+
const callId = String(params?.itemId ?? params?.callId ?? id);
|
|
8620
|
+
const decision = handler ? await handler({ type: "exec", callId, command: Array.isArray(params?.command) ? params.command : params?.command != null ? [params.command] : [], cwd: params?.cwd, reason: params?.reason }).catch(() => "denied") : "denied";
|
|
8621
|
+
this.respond(id, { decision: mapDecisionToWire(decision) });
|
|
8622
|
+
return;
|
|
8623
|
+
}
|
|
8624
|
+
if (method === "item/fileChange/requestApproval" || method === "applyPatchApproval") {
|
|
8625
|
+
const callId = String(params?.itemId ?? params?.callId ?? id);
|
|
8626
|
+
const decision = handler ? await handler({ type: "patch", callId, fileChanges: params?.fileChanges, reason: params?.reason }).catch(() => "denied") : "denied";
|
|
8627
|
+
this.respond(id, { decision: mapDecisionToWire(decision) });
|
|
8628
|
+
return;
|
|
8629
|
+
}
|
|
8630
|
+
if (method === "mcpServer/elicitation/request") {
|
|
8631
|
+
const callId = `${params?.serverName ?? "mcp"}:${id}`;
|
|
8632
|
+
const decision = handler ? await handler({ type: "mcp", callId, serverName: params?.serverName, toolName: params?.serverName, message: params?.message, input: params?._meta?.tool_params ?? {} }).catch(() => "denied") : "denied";
|
|
8633
|
+
const wire = mapDecisionToWire(decision);
|
|
8634
|
+
this.respond(id, { action: wire === "acceptForSession" ? "accept" : wire });
|
|
8635
|
+
return;
|
|
8636
|
+
}
|
|
8637
|
+
this.respond(id, {});
|
|
8638
|
+
}
|
|
8639
|
+
// ── notifications (events + lifecycle) ──────────────────────────────────────
|
|
8640
|
+
handleNotification(method, params) {
|
|
8641
|
+
if (method === "codex/event" || method.startsWith("codex/event/")) {
|
|
8642
|
+
const emsg = params?.msg;
|
|
8643
|
+
if (!emsg) return;
|
|
8644
|
+
if (emsg.type === "task_started" && emsg.turn_id) this._turnId = String(emsg.turn_id);
|
|
8645
|
+
this.opts.onEvent?.(emsg);
|
|
8646
|
+
if (emsg.type === "task_complete" || emsg.type === "turn_aborted") {
|
|
8647
|
+
const tid = emsg.turn_id ?? emsg.turnId ?? null;
|
|
8648
|
+
if (tid) this.completedTurnIds.add(String(tid));
|
|
8649
|
+
this.resolvePendingTurn(emsg.type === "turn_aborted");
|
|
8650
|
+
this._turnId = null;
|
|
8651
|
+
}
|
|
8652
|
+
return;
|
|
8653
|
+
}
|
|
8654
|
+
if (method === "turn/started") {
|
|
8655
|
+
const tid = params?.turn?.id ?? params?.turnId;
|
|
8656
|
+
if (tid) this._turnId = String(tid);
|
|
8657
|
+
return;
|
|
8658
|
+
}
|
|
8659
|
+
if (method === "turn/completed") {
|
|
8660
|
+
const tid = params?.turn?.id ?? params?.turnId ?? null;
|
|
8661
|
+
if (tid && this.completedTurnIds.has(String(tid))) return;
|
|
8662
|
+
const status = params?.turn?.status ?? params?.status;
|
|
8663
|
+
const aborted = typeof status === "string" && /abort|cancel|error/i.test(status);
|
|
8664
|
+
this.resolvePendingTurn(aborted);
|
|
8665
|
+
this._turnId = null;
|
|
8666
|
+
return;
|
|
8667
|
+
}
|
|
8668
|
+
const normalized = normalizeNotification(method, params);
|
|
8669
|
+
if (normalized) this.opts.onEvent?.(normalized);
|
|
8670
|
+
}
|
|
8671
|
+
resolvePendingTurn(aborted) {
|
|
8672
|
+
const p = this.pendingTurn;
|
|
8673
|
+
this.pendingTurn = null;
|
|
8674
|
+
p?.resolve(aborted);
|
|
8675
|
+
}
|
|
8676
|
+
}
|
|
8677
|
+
function mapDecisionToWire(d) {
|
|
8678
|
+
switch (d) {
|
|
8679
|
+
case "approved":
|
|
8680
|
+
return "accept";
|
|
8681
|
+
case "approved_for_session":
|
|
8682
|
+
return "acceptForSession";
|
|
8683
|
+
case "denied":
|
|
8684
|
+
return "decline";
|
|
8685
|
+
case "abort":
|
|
8686
|
+
return "cancel";
|
|
8687
|
+
}
|
|
8688
|
+
}
|
|
8689
|
+
|
|
8690
|
+
const CODEX_PROVIDER_KEY_ENV = "SVAMP_CODEX_API_KEY";
|
|
8691
|
+
const PROVIDER_NAME = "svamp";
|
|
8692
|
+
function resolveCodexProvider(env = process.env) {
|
|
8693
|
+
return {
|
|
8694
|
+
apiBase: env.SVAMP_CODEX_API_BASE || env.LLM_API_BASE || void 0,
|
|
8695
|
+
apiKey: env.SVAMP_CODEX_API_KEY || env.LLM_API_KEY || void 0,
|
|
8696
|
+
model: env.SVAMP_CODEX_MODEL || env.LLM_MODEL || void 0
|
|
8697
|
+
};
|
|
8698
|
+
}
|
|
8699
|
+
function hasCustomProvider(cfg) {
|
|
8700
|
+
return !!(cfg.apiBase && cfg.apiKey);
|
|
8701
|
+
}
|
|
8702
|
+
function buildCodexProviderArgs(cfg) {
|
|
8703
|
+
if (!hasCustomProvider(cfg)) {
|
|
8704
|
+
return { configArgs: cfg.model ? ["-c", `model=${JSON.stringify(cfg.model)}`] : [], env: {}, model: cfg.model };
|
|
8705
|
+
}
|
|
8706
|
+
const configArgs = [
|
|
8707
|
+
"-c",
|
|
8708
|
+
`model_provider=${JSON.stringify(PROVIDER_NAME)}`,
|
|
8709
|
+
"-c",
|
|
8710
|
+
`model_providers.${PROVIDER_NAME}.name=${JSON.stringify(PROVIDER_NAME)}`,
|
|
8711
|
+
"-c",
|
|
8712
|
+
`model_providers.${PROVIDER_NAME}.base_url=${JSON.stringify(cfg.apiBase)}`,
|
|
8713
|
+
"-c",
|
|
8714
|
+
`model_providers.${PROVIDER_NAME}.env_key=${JSON.stringify(CODEX_PROVIDER_KEY_ENV)}`,
|
|
8715
|
+
"-c",
|
|
8716
|
+
`model_providers.${PROVIDER_NAME}.wire_api=${JSON.stringify("responses")}`
|
|
8717
|
+
];
|
|
8718
|
+
if (cfg.model) configArgs.push("-c", `model=${JSON.stringify(cfg.model)}`);
|
|
8719
|
+
return { configArgs, env: { [CODEX_PROVIDER_KEY_ENV]: cfg.apiKey }, model: cfg.model };
|
|
8720
|
+
}
|
|
8721
|
+
|
|
8722
|
+
var codexProvider = /*#__PURE__*/Object.freeze({
|
|
8723
|
+
__proto__: null,
|
|
8724
|
+
CODEX_PROVIDER_KEY_ENV: CODEX_PROVIDER_KEY_ENV,
|
|
8725
|
+
buildCodexProviderArgs: buildCodexProviderArgs,
|
|
8726
|
+
hasCustomProvider: hasCustomProvider,
|
|
8727
|
+
resolveCodexProvider: resolveCodexProvider
|
|
8728
|
+
});
|
|
8729
|
+
|
|
8730
|
+
class CodexAppServerBackend {
|
|
8731
|
+
constructor(opts) {
|
|
8732
|
+
this.opts = opts;
|
|
8733
|
+
this.log = opts.log ?? (() => {
|
|
8734
|
+
});
|
|
8735
|
+
const provider = opts.provider ?? resolveCodexProvider();
|
|
8736
|
+
const { configArgs, env: providerEnv, model: providerModel } = buildCodexProviderArgs(provider);
|
|
8737
|
+
this.model = opts.model ?? providerModel;
|
|
8738
|
+
this.client = new CodexAppServerClient({
|
|
8739
|
+
cwd: opts.cwd,
|
|
8740
|
+
env: { ...opts.env ?? {}, ...providerEnv },
|
|
8741
|
+
extraArgs: configArgs,
|
|
8742
|
+
log: this.log,
|
|
8743
|
+
isolationConfig: opts.isolationConfig,
|
|
8744
|
+
onEvent: (m) => this.handleCodexEvent(m),
|
|
8745
|
+
onApproval: (r) => this.handleApproval(r),
|
|
8746
|
+
onExit: (code) => this.emit({ type: "status", status: "error", detail: `codex app-server exited (code ${code})` })
|
|
8747
|
+
});
|
|
8748
|
+
}
|
|
8749
|
+
listeners = [];
|
|
8750
|
+
client;
|
|
8751
|
+
sessionId = randomUUID();
|
|
8752
|
+
log;
|
|
8753
|
+
model;
|
|
8754
|
+
started = false;
|
|
8755
|
+
// Approval promises keyed by callId, resolved by respondToPermission().
|
|
8756
|
+
pendingApprovals = /* @__PURE__ */ new Map();
|
|
8757
|
+
// Whether we streamed any delta for the in-flight assistant message (to avoid a duplicate
|
|
8758
|
+
// fullText push on the final agent_message when the model DID stream).
|
|
8759
|
+
streamedThisMessage = false;
|
|
8760
|
+
// Tracks the in-flight turn so waitForResponseComplete() can await it.
|
|
8761
|
+
turnDone = null;
|
|
8762
|
+
/** True/false if codex supports app-server; null if codex isn't installed. */
|
|
8763
|
+
static available() {
|
|
8764
|
+
return codexAppServerAvailable();
|
|
8765
|
+
}
|
|
8766
|
+
// ── AgentBackend ───────────────────────────────────────────────────────────
|
|
8334
8767
|
async startSession(initialPrompt) {
|
|
8335
|
-
const sessionId = randomUUID();
|
|
8336
|
-
this.svampSessionId = sessionId;
|
|
8337
8768
|
this.emit({ type: "status", status: "starting" });
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
if (
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8769
|
+
const avail = codexAppServerAvailable();
|
|
8770
|
+
if (avail === null) throw new Error("Codex CLI not found. Install it (`npm i -g @openai/codex`) and authenticate (`svamp daemon codex-auth`).");
|
|
8771
|
+
if (avail === false) throw new Error("Installed Codex is too old for app-server \u2014 upgrade to >= 0.100 (`npm i -g @openai/codex@latest`).");
|
|
8772
|
+
await this.client.start();
|
|
8773
|
+
if (this.opts.resumeThreadId) {
|
|
8774
|
+
await this.client.resumeThread({ threadId: this.opts.resumeThreadId, model: this.model, approvalPolicy: this.opts.approvalPolicy, sandbox: this.opts.sandbox });
|
|
8775
|
+
} else {
|
|
8776
|
+
const r = await this.client.startThread({ model: this.model, approvalPolicy: this.opts.approvalPolicy, sandbox: this.opts.sandbox });
|
|
8777
|
+
this.model = this.model ?? r.model;
|
|
8345
8778
|
}
|
|
8346
|
-
|
|
8779
|
+
this.started = true;
|
|
8780
|
+
this.emit({ type: "status", status: "idle" });
|
|
8781
|
+
if (initialPrompt) await this.sendPrompt(this.sessionId, initialPrompt);
|
|
8782
|
+
return { sessionId: this.sessionId };
|
|
8347
8783
|
}
|
|
8348
|
-
async sendPrompt(
|
|
8349
|
-
if (!this.
|
|
8350
|
-
this.turnCancelled = false;
|
|
8351
|
-
const myTurnId = ++this.turnId;
|
|
8784
|
+
async sendPrompt(_sessionId, prompt) {
|
|
8785
|
+
if (!this.started) throw new Error("Codex session not started");
|
|
8352
8786
|
this.emit({ type: "status", status: "running" });
|
|
8353
|
-
|
|
8787
|
+
this.streamedThisMessage = false;
|
|
8788
|
+
let resolveTurn;
|
|
8789
|
+
this.turnDone = new Promise((res) => {
|
|
8790
|
+
resolveTurn = res;
|
|
8791
|
+
});
|
|
8354
8792
|
try {
|
|
8355
|
-
|
|
8356
|
-
if (this.codexSessionId) {
|
|
8357
|
-
response = await this.continueSession(prompt);
|
|
8358
|
-
} else {
|
|
8359
|
-
const config = {
|
|
8360
|
-
prompt,
|
|
8361
|
-
cwd: this.options.cwd,
|
|
8362
|
-
...this.options.model ? { model: this.options.model } : {}
|
|
8363
|
-
};
|
|
8364
|
-
response = await this.startCodexSession(config);
|
|
8365
|
-
}
|
|
8366
|
-
if (response?.content && Array.isArray(response.content)) {
|
|
8367
|
-
for (const block of response.content) {
|
|
8368
|
-
if (block.type === "text" && block.text) {
|
|
8369
|
-
this.emit({ type: "model-output", fullText: block.text });
|
|
8370
|
-
}
|
|
8371
|
-
}
|
|
8372
|
-
}
|
|
8793
|
+
await this.client.sendTurnAndWait(prompt, { model: this.model, approvalPolicy: this.opts.approvalPolicy, sandbox: this.opts.sandbox });
|
|
8373
8794
|
} catch (err) {
|
|
8374
|
-
|
|
8375
|
-
this.log(`[Codex] Error in sendPrompt: ${err.message}`);
|
|
8376
|
-
this.emit({ type: "status", status: "error", detail: err.message });
|
|
8377
|
-
throw err;
|
|
8795
|
+
this.emit({ type: "status", status: "error", detail: err instanceof Error ? err.message : String(err) });
|
|
8378
8796
|
} finally {
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8797
|
+
this.emit({ type: "status", status: "idle" });
|
|
8798
|
+
resolveTurn();
|
|
8799
|
+
this.turnDone = null;
|
|
8382
8800
|
}
|
|
8383
8801
|
}
|
|
8384
8802
|
async cancel(_sessionId) {
|
|
8385
|
-
this.
|
|
8386
|
-
this.turnCancelled = true;
|
|
8803
|
+
await this.client.interrupt();
|
|
8387
8804
|
this.emit({ type: "status", status: "cancelled" });
|
|
8388
8805
|
}
|
|
8806
|
+
onMessage(handler) {
|
|
8807
|
+
this.listeners.push(handler);
|
|
8808
|
+
}
|
|
8809
|
+
offMessage(handler) {
|
|
8810
|
+
this.listeners = this.listeners.filter((h) => h !== handler);
|
|
8811
|
+
}
|
|
8389
8812
|
async respondToPermission(requestId, approved) {
|
|
8390
|
-
const
|
|
8391
|
-
if (
|
|
8813
|
+
const resolve = this.pendingApprovals.get(requestId);
|
|
8814
|
+
if (resolve) {
|
|
8392
8815
|
this.pendingApprovals.delete(requestId);
|
|
8393
|
-
|
|
8394
|
-
action: approved ? "accept" : "decline",
|
|
8395
|
-
content: { approval: approved ? "approve" : "deny" }
|
|
8396
|
-
});
|
|
8397
|
-
this.emit({ type: "permission-response", id: requestId, approved });
|
|
8816
|
+
resolve(approved ? "approved" : "denied");
|
|
8398
8817
|
}
|
|
8818
|
+
this.emit({ type: "permission-response", id: requestId, approved });
|
|
8819
|
+
}
|
|
8820
|
+
async waitForResponseComplete(timeoutMs = 14 * 24 * 60 * 60 * 1e3) {
|
|
8821
|
+
if (!this.turnDone) return;
|
|
8822
|
+
await Promise.race([this.turnDone, new Promise((res) => {
|
|
8823
|
+
const t = setTimeout(res, timeoutMs);
|
|
8824
|
+
t.unref?.();
|
|
8825
|
+
})]);
|
|
8399
8826
|
}
|
|
8400
8827
|
async dispose() {
|
|
8401
|
-
|
|
8402
|
-
this.disposed = true;
|
|
8403
|
-
this.log("[Codex] Disposing backend");
|
|
8404
|
-
for (const [, pending] of this.pendingApprovals) {
|
|
8405
|
-
pending.resolve({ action: "decline" });
|
|
8406
|
-
}
|
|
8828
|
+
for (const [, resolve] of this.pendingApprovals) resolve("denied");
|
|
8407
8829
|
this.pendingApprovals.clear();
|
|
8408
|
-
await this.
|
|
8409
|
-
this.listeners = [];
|
|
8830
|
+
await this.client.dispose();
|
|
8410
8831
|
}
|
|
8411
|
-
/**
|
|
8412
|
-
|
|
8413
|
-
return this.
|
|
8832
|
+
/** The live Codex thread id (for the daemon to persist and resume after a restart). */
|
|
8833
|
+
getThreadId() {
|
|
8834
|
+
return this.client.threadId;
|
|
8414
8835
|
}
|
|
8415
|
-
/**
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
*/
|
|
8419
|
-
getProcess() {
|
|
8420
|
-
if (!this.transport) return null;
|
|
8421
|
-
const pid = this.pid;
|
|
8422
|
-
return {
|
|
8423
|
-
kill: (signal) => {
|
|
8424
|
-
if (pid) {
|
|
8425
|
-
try {
|
|
8426
|
-
process.kill(pid, signal || "SIGTERM");
|
|
8427
|
-
} catch {
|
|
8428
|
-
}
|
|
8429
|
-
}
|
|
8430
|
-
}
|
|
8431
|
-
};
|
|
8836
|
+
/** Update the model used for subsequent turns (per-turn override — no restart needed). */
|
|
8837
|
+
setModel(model) {
|
|
8838
|
+
this.model = model;
|
|
8432
8839
|
}
|
|
8433
|
-
// ──
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
const mcpCommand = getCodexMcpCommand();
|
|
8437
|
-
if (mcpCommand === null) {
|
|
8438
|
-
throw new Error(
|
|
8439
|
-
"Codex CLI not found or not executable.\n\nTo install codex:\n npm install -g @openai/codex\n"
|
|
8440
|
-
);
|
|
8441
|
-
}
|
|
8442
|
-
this.log(`[Codex] Connecting via: codex ${mcpCommand}`);
|
|
8443
|
-
const env = {};
|
|
8444
|
-
for (const key of Object.keys(process.env)) {
|
|
8445
|
-
const value = process.env[key];
|
|
8446
|
-
if (typeof value === "string") env[key] = value;
|
|
8447
|
-
}
|
|
8448
|
-
if (this.options.env) {
|
|
8449
|
-
Object.assign(env, this.options.env);
|
|
8450
|
-
}
|
|
8451
|
-
const rolloutFilter = "codex_core::rollout::list=off";
|
|
8452
|
-
const existingRustLog = env.RUST_LOG?.trim();
|
|
8453
|
-
if (!existingRustLog) {
|
|
8454
|
-
env.RUST_LOG = rolloutFilter;
|
|
8455
|
-
} else if (!existingRustLog.includes("codex_core::rollout::list=")) {
|
|
8456
|
-
env.RUST_LOG = `${existingRustLog},${rolloutFilter}`;
|
|
8457
|
-
}
|
|
8458
|
-
let transportCommand = "codex";
|
|
8459
|
-
let transportArgs = [mcpCommand];
|
|
8460
|
-
if (this.options.isolationConfig) {
|
|
8461
|
-
const wrapped = wrapWithIsolation(transportCommand, transportArgs, this.options.isolationConfig);
|
|
8462
|
-
transportCommand = wrapped.command;
|
|
8463
|
-
transportArgs = wrapped.args;
|
|
8464
|
-
if (wrapped.env) Object.assign(env, wrapped.env);
|
|
8465
|
-
if (wrapped.cleanupFiles) {
|
|
8466
|
-
this._isolationCleanupFiles = wrapped.cleanupFiles;
|
|
8467
|
-
}
|
|
8468
|
-
this.log(`[Codex] Isolation: ${this.options.isolationConfig.method}`);
|
|
8469
|
-
}
|
|
8470
|
-
this.transport = new StdioClientTransport({
|
|
8471
|
-
command: transportCommand,
|
|
8472
|
-
args: transportArgs,
|
|
8473
|
-
env
|
|
8474
|
-
});
|
|
8475
|
-
this.registerPermissionHandlers();
|
|
8476
|
-
try {
|
|
8477
|
-
await this.client.connect(this.transport);
|
|
8478
|
-
this.connected = true;
|
|
8479
|
-
this.log("[Codex] MCP connection established");
|
|
8480
|
-
} catch (err) {
|
|
8481
|
-
this.transport = null;
|
|
8482
|
-
throw err;
|
|
8483
|
-
}
|
|
8484
|
-
}
|
|
8485
|
-
async disconnect() {
|
|
8486
|
-
if (!this.connected) return;
|
|
8487
|
-
const pid = this.pid;
|
|
8488
|
-
this.log(`[Codex] Disconnecting (pid=${pid ?? "none"})`);
|
|
8489
|
-
try {
|
|
8490
|
-
await this.client.close();
|
|
8491
|
-
} catch {
|
|
8492
|
-
try {
|
|
8493
|
-
await this.transport?.close?.();
|
|
8494
|
-
} catch {
|
|
8495
|
-
}
|
|
8496
|
-
}
|
|
8497
|
-
if (pid) {
|
|
8840
|
+
// ── internal ─────────────────────────────────────────────────────────────
|
|
8841
|
+
emit(msg) {
|
|
8842
|
+
for (const h of this.listeners) {
|
|
8498
8843
|
try {
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
} catch {
|
|
8503
|
-
}
|
|
8504
|
-
} catch {
|
|
8844
|
+
h(msg);
|
|
8845
|
+
} catch (e) {
|
|
8846
|
+
this.log("[codex-backend] listener error", e);
|
|
8505
8847
|
}
|
|
8506
8848
|
}
|
|
8507
|
-
this.transport = null;
|
|
8508
|
-
this.connected = false;
|
|
8509
|
-
if (this._isolationCleanupFiles.length > 0) {
|
|
8510
|
-
const { rm } = await import('node:fs/promises');
|
|
8511
|
-
for (const f of this._isolationCleanupFiles) rm(f, { force: true }).catch(() => {
|
|
8512
|
-
});
|
|
8513
|
-
this._isolationCleanupFiles = [];
|
|
8514
|
-
}
|
|
8515
8849
|
}
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
this.
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
payload: {
|
|
8529
|
-
toolName: "CodexBash",
|
|
8530
|
-
input: {
|
|
8531
|
-
command: params.codex_command,
|
|
8532
|
-
cwd: params.codex_cwd
|
|
8533
|
-
}
|
|
8534
|
-
}
|
|
8535
|
-
});
|
|
8536
|
-
return new Promise((resolve) => {
|
|
8537
|
-
this.pendingApprovals.set(callId, { resolve });
|
|
8538
|
-
setTimeout(() => {
|
|
8539
|
-
if (this.pendingApprovals.has(callId)) {
|
|
8540
|
-
this.pendingApprovals.delete(callId);
|
|
8541
|
-
resolve({ action: "decline" });
|
|
8542
|
-
}
|
|
8543
|
-
}, 5 * 60 * 1e3);
|
|
8544
|
-
});
|
|
8545
|
-
}
|
|
8546
|
-
);
|
|
8850
|
+
handleApproval(req) {
|
|
8851
|
+
const id = req.callId || randomUUID();
|
|
8852
|
+
const reason = req.type === "exec" ? `Execute: ${(req.command ?? []).join(" ")}` : req.type === "patch" ? "Apply file changes" : req.toolName ? `Tool: ${req.toolName}` : "Codex requires approval";
|
|
8853
|
+
const payload = req.type === "exec" ? { toolName: "CodexBash", input: { command: req.command, cwd: req.cwd } } : req.type === "patch" ? { toolName: "CodexPatch", input: { fileChanges: req.fileChanges } } : { toolName: req.toolName ?? "CodexTool", input: req.input };
|
|
8854
|
+
this.emit({ type: "permission-request", id, reason, payload });
|
|
8855
|
+
return new Promise((resolve) => {
|
|
8856
|
+
this.pendingApprovals.set(id, resolve);
|
|
8857
|
+
const t = setTimeout(() => {
|
|
8858
|
+
if (this.pendingApprovals.delete(id)) resolve("denied");
|
|
8859
|
+
}, 5 * 60 * 1e3);
|
|
8860
|
+
t.unref?.();
|
|
8861
|
+
});
|
|
8547
8862
|
}
|
|
8548
|
-
// ── Codex MCP tools ────────────────────────────────────────────────
|
|
8549
|
-
async startCodexSession(config) {
|
|
8550
|
-
const response = await this.client.callTool({
|
|
8551
|
-
name: "codex",
|
|
8552
|
-
arguments: config
|
|
8553
|
-
}, void 0, { timeout: DEFAULT_TIMEOUT });
|
|
8554
|
-
this.extractIdentifiers(response);
|
|
8555
|
-
return response;
|
|
8556
|
-
}
|
|
8557
|
-
async continueSession(prompt) {
|
|
8558
|
-
if (!this.conversationId) {
|
|
8559
|
-
this.conversationId = this.codexSessionId;
|
|
8560
|
-
}
|
|
8561
|
-
const response = await this.client.callTool({
|
|
8562
|
-
name: "codex-reply",
|
|
8563
|
-
arguments: {
|
|
8564
|
-
sessionId: this.codexSessionId,
|
|
8565
|
-
conversationId: this.conversationId,
|
|
8566
|
-
prompt
|
|
8567
|
-
}
|
|
8568
|
-
}, void 0, { timeout: DEFAULT_TIMEOUT });
|
|
8569
|
-
this.extractIdentifiers(response);
|
|
8570
|
-
return response;
|
|
8571
|
-
}
|
|
8572
|
-
// ── Event handling ─────────────────────────────────────────────────
|
|
8573
8863
|
handleCodexEvent(event) {
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
this.log(`[Codex] Event: ${eventType}`);
|
|
8577
|
-
switch (eventType) {
|
|
8864
|
+
const t = event.type;
|
|
8865
|
+
switch (t) {
|
|
8578
8866
|
case "task_started":
|
|
8579
8867
|
this.emit({ type: "status", status: "running" });
|
|
8580
8868
|
break;
|
|
8581
8869
|
case "task_complete":
|
|
8582
|
-
break;
|
|
8583
8870
|
case "turn_aborted":
|
|
8584
8871
|
break;
|
|
8585
|
-
case "
|
|
8586
|
-
const
|
|
8587
|
-
if (
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
this.emit({ type: "model-output", fullText: block.text });
|
|
8591
|
-
}
|
|
8592
|
-
}
|
|
8872
|
+
case "agent_message_delta": {
|
|
8873
|
+
const delta = event.delta ?? (typeof event.text === "string" ? event.text : "");
|
|
8874
|
+
if (delta) {
|
|
8875
|
+
this.streamedThisMessage = true;
|
|
8876
|
+
this.emit({ type: "model-output", textDelta: delta });
|
|
8593
8877
|
}
|
|
8594
8878
|
break;
|
|
8595
8879
|
}
|
|
8596
|
-
case "
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8880
|
+
case "agent_message": {
|
|
8881
|
+
if (this.streamedThisMessage) {
|
|
8882
|
+
this.streamedThisMessage = false;
|
|
8883
|
+
break;
|
|
8600
8884
|
}
|
|
8885
|
+
const text = mapCodexEventToText(event);
|
|
8886
|
+
if (text) this.emit({ type: "model-output", fullText: text });
|
|
8601
8887
|
break;
|
|
8602
8888
|
}
|
|
8889
|
+
case "agent_reasoning_delta":
|
|
8603
8890
|
case "agent_reasoning": {
|
|
8604
|
-
const text = event.text || (Array.isArray(event.content) ? event.content.map((c) => c
|
|
8605
|
-
if (text) {
|
|
8606
|
-
this.emit({ type: "event", name: "thinking", payload: { text } });
|
|
8607
|
-
}
|
|
8891
|
+
const text = event.delta?.text || event.text || (Array.isArray(event.content) ? event.content.map((c) => c?.text).join("") : "");
|
|
8892
|
+
if (text) this.emit({ type: "event", name: "thinking", payload: { text } });
|
|
8608
8893
|
break;
|
|
8609
8894
|
}
|
|
8610
8895
|
case "exec_command_begin": {
|
|
8611
|
-
const callId = event.call_id
|
|
8612
|
-
const command = Array.isArray(event.command) ? event.command.join(" ") : String(event.command
|
|
8613
|
-
this.emit({
|
|
8614
|
-
type: "tool-call",
|
|
8615
|
-
toolName: "CodexBash",
|
|
8616
|
-
callId,
|
|
8617
|
-
args: { command, cwd: event.cwd }
|
|
8618
|
-
});
|
|
8896
|
+
const callId = String(event.call_id ?? event.callId ?? randomUUID());
|
|
8897
|
+
const command = Array.isArray(event.command) ? event.command.join(" ") : String(event.command ?? "");
|
|
8898
|
+
this.emit({ type: "tool-call", toolName: "CodexBash", callId, args: { command, cwd: event.cwd } });
|
|
8619
8899
|
break;
|
|
8620
8900
|
}
|
|
8621
8901
|
case "exec_command_end": {
|
|
8622
|
-
const callId = event.call_id
|
|
8623
|
-
this.emit({
|
|
8624
|
-
type: "tool-result",
|
|
8625
|
-
toolName: "CodexBash",
|
|
8626
|
-
callId,
|
|
8627
|
-
result: {
|
|
8628
|
-
exitCode: event.exit_code ?? event.exitCode,
|
|
8629
|
-
stdout: event.stdout || "",
|
|
8630
|
-
stderr: event.stderr || ""
|
|
8631
|
-
}
|
|
8632
|
-
});
|
|
8902
|
+
const callId = String(event.call_id ?? event.callId ?? "");
|
|
8903
|
+
this.emit({ type: "tool-result", toolName: "CodexBash", callId, result: { exitCode: event.exit_code ?? event.exitCode, stdout: event.stdout ?? "", stderr: event.stderr ?? "" } });
|
|
8633
8904
|
break;
|
|
8634
8905
|
}
|
|
8635
8906
|
case "patch_apply_begin": {
|
|
8636
|
-
const callId = event.call_id
|
|
8637
|
-
this.emit({
|
|
8638
|
-
type: "tool-call",
|
|
8639
|
-
toolName: "CodexPatch",
|
|
8640
|
-
callId,
|
|
8641
|
-
args: { filePath: event.file_path || event.filePath, patch: event.patch }
|
|
8642
|
-
});
|
|
8907
|
+
const callId = String(event.call_id ?? event.callId ?? randomUUID());
|
|
8908
|
+
this.emit({ type: "tool-call", toolName: "CodexPatch", callId, args: { filePath: event.file_path ?? event.filePath, patch: event.patch } });
|
|
8643
8909
|
break;
|
|
8644
8910
|
}
|
|
8645
8911
|
case "patch_apply_end": {
|
|
8646
|
-
const callId = event.call_id
|
|
8647
|
-
this.emit({
|
|
8648
|
-
type: "tool-result",
|
|
8649
|
-
toolName: "CodexPatch",
|
|
8650
|
-
callId,
|
|
8651
|
-
result: {
|
|
8652
|
-
filePath: event.file_path || event.filePath,
|
|
8653
|
-
applied: event.applied,
|
|
8654
|
-
error: event.error
|
|
8655
|
-
}
|
|
8656
|
-
});
|
|
8912
|
+
const callId = String(event.call_id ?? event.callId ?? "");
|
|
8913
|
+
this.emit({ type: "tool-result", toolName: "CodexPatch", callId, result: { filePath: event.file_path ?? event.filePath, applied: event.applied, error: event.error } });
|
|
8657
8914
|
break;
|
|
8658
8915
|
}
|
|
8659
|
-
|
|
8660
|
-
|
|
8916
|
+
case "mcp_tool_call_begin": {
|
|
8917
|
+
const callId = String(event.call_id ?? event.callId ?? randomUUID());
|
|
8918
|
+
this.emit({ type: "tool-call", toolName: String(event.tool ?? "CodexMcpTool"), callId, args: { server: event.server, arguments: event.arguments } });
|
|
8661
8919
|
break;
|
|
8662
|
-
}
|
|
8663
|
-
}
|
|
8664
|
-
// ── Identifier extraction ──────────────────────────────────────────
|
|
8665
|
-
updateIdentifiersFromEvent(event) {
|
|
8666
|
-
if (!event || typeof event !== "object") return;
|
|
8667
|
-
const candidates = [event];
|
|
8668
|
-
if (event.data && typeof event.data === "object") candidates.push(event.data);
|
|
8669
|
-
for (const c of candidates) {
|
|
8670
|
-
const sid = c.session_id ?? c.sessionId;
|
|
8671
|
-
if (sid) this.codexSessionId = sid;
|
|
8672
|
-
const cid = c.conversation_id ?? c.conversationId;
|
|
8673
|
-
if (cid) this.conversationId = cid;
|
|
8674
|
-
}
|
|
8675
|
-
}
|
|
8676
|
-
extractIdentifiers(response) {
|
|
8677
|
-
const meta = response?.meta || {};
|
|
8678
|
-
this.codexSessionId = meta.sessionId || response?.sessionId || this.codexSessionId;
|
|
8679
|
-
this.conversationId = meta.conversationId || response?.conversationId || this.conversationId;
|
|
8680
|
-
const content = response?.content;
|
|
8681
|
-
if (Array.isArray(content)) {
|
|
8682
|
-
for (const item of content) {
|
|
8683
|
-
if (!this.codexSessionId && item?.sessionId) this.codexSessionId = item.sessionId;
|
|
8684
|
-
if (!this.conversationId && item?.conversationId) this.conversationId = item.conversationId;
|
|
8685
8920
|
}
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8921
|
+
case "mcp_tool_call_end": {
|
|
8922
|
+
const callId = String(event.call_id ?? event.callId ?? "");
|
|
8923
|
+
this.emit({ type: "tool-result", toolName: String(event.tool ?? "CodexMcpTool"), callId, result: { result: event.result, error: event.error } });
|
|
8924
|
+
break;
|
|
8925
|
+
}
|
|
8926
|
+
case "token_count":
|
|
8927
|
+
case "token_usage": {
|
|
8928
|
+
this.emit({ type: "event", name: "usage", payload: event.usage ?? event.info ?? event });
|
|
8929
|
+
break;
|
|
8695
8930
|
}
|
|
8931
|
+
case "error": {
|
|
8932
|
+
const msg = event.message ?? event.error ?? "Codex error";
|
|
8933
|
+
this.emit({ type: "status", status: "error", detail: String(msg) });
|
|
8934
|
+
break;
|
|
8935
|
+
}
|
|
8936
|
+
default:
|
|
8937
|
+
if (t === "_rpc/thread/tokenUsage/updated") {
|
|
8938
|
+
this.emit({ type: "event", name: "usage", payload: event.params });
|
|
8939
|
+
}
|
|
8940
|
+
break;
|
|
8696
8941
|
}
|
|
8697
8942
|
}
|
|
8698
8943
|
}
|
|
8699
8944
|
|
|
8700
|
-
var
|
|
8945
|
+
var codexAppServerBackend = /*#__PURE__*/Object.freeze({
|
|
8701
8946
|
__proto__: null,
|
|
8702
|
-
|
|
8947
|
+
CodexAppServerBackend: CodexAppServerBackend
|
|
8703
8948
|
});
|
|
8704
8949
|
|
|
8705
8950
|
const GEMINI_TIMEOUTS = {
|
|
@@ -9676,9 +9921,11 @@ class TokenCache {
|
|
|
9676
9921
|
const oldest = this.cache.keys().next().value;
|
|
9677
9922
|
if (oldest) this.cache.delete(oldest);
|
|
9678
9923
|
}
|
|
9924
|
+
const ttlExpiry = Date.now() + this.ttlMs;
|
|
9925
|
+
const jwtExpMs = parseJwtExpMs(token);
|
|
9679
9926
|
this.cache.set(token, {
|
|
9680
9927
|
email,
|
|
9681
|
-
expiresAt:
|
|
9928
|
+
expiresAt: jwtExpMs !== null ? Math.min(ttlExpiry, jwtExpMs) : ttlExpiry
|
|
9682
9929
|
});
|
|
9683
9930
|
}
|
|
9684
9931
|
clear() {
|
|
@@ -9710,6 +9957,16 @@ function parseCookies(header) {
|
|
|
9710
9957
|
}
|
|
9711
9958
|
return cookies;
|
|
9712
9959
|
}
|
|
9960
|
+
function parseJwtExpMs(token) {
|
|
9961
|
+
try {
|
|
9962
|
+
const parts = token.split(".");
|
|
9963
|
+
if (parts.length !== 3) return null;
|
|
9964
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
9965
|
+
return typeof payload.exp === "number" ? payload.exp * 1e3 : null;
|
|
9966
|
+
} catch {
|
|
9967
|
+
return null;
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
9713
9970
|
function parseJwtEmail(token) {
|
|
9714
9971
|
try {
|
|
9715
9972
|
const parts = token.split(".");
|
|
@@ -9728,6 +9985,19 @@ function parseJwtEmail(token) {
|
|
|
9728
9985
|
return null;
|
|
9729
9986
|
}
|
|
9730
9987
|
}
|
|
9988
|
+
function isStructurallyLiveJwt(token) {
|
|
9989
|
+
try {
|
|
9990
|
+
const parts = token.split(".");
|
|
9991
|
+
if (parts.length !== 3) return false;
|
|
9992
|
+
const payload = JSON.parse(
|
|
9993
|
+
Buffer.from(parts[1], "base64url").toString("utf-8")
|
|
9994
|
+
);
|
|
9995
|
+
if (payload.exp && payload.exp * 1e3 < Date.now()) return false;
|
|
9996
|
+
return true;
|
|
9997
|
+
} catch {
|
|
9998
|
+
return false;
|
|
9999
|
+
}
|
|
10000
|
+
}
|
|
9731
10001
|
async function verifyTokenViaHypha(token, hyphaServerUrl) {
|
|
9732
10002
|
try {
|
|
9733
10003
|
const baseUrl = hyphaServerUrl.replace(/\/$/, "");
|
|
@@ -9766,11 +10036,7 @@ class ServeAuth {
|
|
|
9766
10036
|
if (!token) return null;
|
|
9767
10037
|
const cached = this.cache.get(token);
|
|
9768
10038
|
if (cached) return cached.email;
|
|
9769
|
-
|
|
9770
|
-
if (localEmail) {
|
|
9771
|
-
this.cache.set(token, localEmail);
|
|
9772
|
-
return localEmail;
|
|
9773
|
-
}
|
|
10039
|
+
if (!isStructurallyLiveJwt(token)) return null;
|
|
9774
10040
|
const serverEmail = await verifyTokenViaHypha(token, this.hyphaServerUrl);
|
|
9775
10041
|
if (serverEmail) {
|
|
9776
10042
|
this.cache.set(token, serverEmail);
|
|
@@ -11321,7 +11587,27 @@ function escalateWorkflowFailure(projectRoot, wf, run) {
|
|
|
11321
11587
|
return void 0;
|
|
11322
11588
|
}
|
|
11323
11589
|
}
|
|
11324
|
-
|
|
11590
|
+
const inFlightRuns = /* @__PURE__ */ new Map();
|
|
11591
|
+
function workflowLockKey(projectRoot, name) {
|
|
11592
|
+
return `${projectRoot}\0${name}`;
|
|
11593
|
+
}
|
|
11594
|
+
function runWorkflow(projectRoot, wf, opts) {
|
|
11595
|
+
if (opts.allowConcurrent) return runWorkflowInner(projectRoot, wf, opts);
|
|
11596
|
+
const key = workflowLockKey(projectRoot, wf.name);
|
|
11597
|
+
const existing = inFlightRuns.get(key);
|
|
11598
|
+
if (existing) {
|
|
11599
|
+
opts.log?.(`[workflow] run coalesced "${wf.name}" in ${projectRoot} \u2014 a run of this workflow is already in flight`);
|
|
11600
|
+
opts.onCoalesced?.();
|
|
11601
|
+
return existing;
|
|
11602
|
+
}
|
|
11603
|
+
const p = runWorkflowInner(projectRoot, wf, opts);
|
|
11604
|
+
inFlightRuns.set(key, p);
|
|
11605
|
+
void p.finally(() => {
|
|
11606
|
+
if (inFlightRuns.get(key) === p) inFlightRuns.delete(key);
|
|
11607
|
+
});
|
|
11608
|
+
return p;
|
|
11609
|
+
}
|
|
11610
|
+
async function runWorkflowInner(projectRoot, wf, opts) {
|
|
11325
11611
|
const now = opts.now || (() => {
|
|
11326
11612
|
const d = /* @__PURE__ */ new Date();
|
|
11327
11613
|
return { iso: d.toISOString(), ms: d.getTime() };
|
|
@@ -11570,6 +11856,7 @@ function computeSoftTurnCap(hardMax, cadence) {
|
|
|
11570
11856
|
const DEFAULT_LOOP_MAX_EXTENSIONS = 3;
|
|
11571
11857
|
const DEFAULT_LOOP_EXTENSION_TURNS = 25;
|
|
11572
11858
|
function resolveMaxExtensions(envValue) {
|
|
11859
|
+
if (envValue == null || envValue.trim() === "") return DEFAULT_LOOP_MAX_EXTENSIONS;
|
|
11573
11860
|
const n = Number(envValue);
|
|
11574
11861
|
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
11575
11862
|
return DEFAULT_LOOP_MAX_EXTENSIONS;
|
|
@@ -11623,6 +11910,7 @@ function resolveStuckLimit(envValue) {
|
|
|
11623
11910
|
return DEFAULT_LOOP_STUCK_CHECKPOINTS;
|
|
11624
11911
|
}
|
|
11625
11912
|
function resolveMaxAutoResumes(envValue) {
|
|
11913
|
+
if (envValue == null || envValue.trim() === "") return DEFAULT_LOOP_MAX_AUTORESUMES;
|
|
11626
11914
|
const n = Number(envValue);
|
|
11627
11915
|
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
11628
11916
|
return DEFAULT_LOOP_MAX_AUTORESUMES;
|
|
@@ -13558,7 +13846,7 @@ async function startDaemon(options) {
|
|
|
13558
13846
|
try {
|
|
13559
13847
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
13560
13848
|
if (!dir) return;
|
|
13561
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
13849
|
+
const { reconcileServiceLinks } = await import('./agentCommands-w72BRYhk.mjs');
|
|
13562
13850
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
13563
13851
|
const config = readSvampConfig(configPath);
|
|
13564
13852
|
const entries = Array.from(urls.entries());
|
|
@@ -13576,7 +13864,7 @@ async function startDaemon(options) {
|
|
|
13576
13864
|
}
|
|
13577
13865
|
}
|
|
13578
13866
|
async function createExposedTunnel(spec) {
|
|
13579
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
13867
|
+
const { FrpcTunnel } = await import('./frpc-BCOnJSAM.mjs');
|
|
13580
13868
|
const tunnel = new FrpcTunnel({
|
|
13581
13869
|
name: spec.name,
|
|
13582
13870
|
ports: spec.ports,
|
|
@@ -13597,14 +13885,14 @@ async function startDaemon(options) {
|
|
|
13597
13885
|
}
|
|
13598
13886
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
13599
13887
|
const tunnelRecreateInFlight = /* @__PURE__ */ new Set();
|
|
13600
|
-
const { ServeManager } = await import('./serveManager-
|
|
13888
|
+
const { ServeManager } = await import('./serveManager-BuXk1_SH.mjs');
|
|
13601
13889
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
13602
13890
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
13603
13891
|
});
|
|
13604
13892
|
ensureAutoInstalledCommands(logger);
|
|
13605
13893
|
(async () => {
|
|
13606
13894
|
try {
|
|
13607
|
-
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-
|
|
13895
|
+
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-B9O-hKxm.mjs');
|
|
13608
13896
|
beginClaudeVersionReconcile((msg) => logger.log(msg));
|
|
13609
13897
|
} catch (e) {
|
|
13610
13898
|
logger.log(`[claude-version] check failed: ${e?.message || e}`);
|
|
@@ -13843,7 +14131,7 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
13843
14131
|
}
|
|
13844
14132
|
const sessionId = options2.sessionId || composeSessionId(generateFriendlyName(collectKnownFriendlyNames()), collectKnownSessionIds());
|
|
13845
14133
|
const agentName = options2.agent || agentConfig.agent_type || "claude";
|
|
13846
|
-
if (agentName !== "claude" && (KNOWN_ACP_AGENTS[agentName] ||
|
|
14134
|
+
if (agentName !== "claude" && (KNOWN_ACP_AGENTS[agentName] || KNOWN_CODEX_AGENTS[agentName])) {
|
|
13847
14135
|
return await spawnAgentSession(sessionId, directory, agentName, options2, resumeSessionId);
|
|
13848
14136
|
}
|
|
13849
14137
|
let stagedCredentials = null;
|
|
@@ -15713,11 +16001,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15713
16001
|
});
|
|
15714
16002
|
},
|
|
15715
16003
|
onIssue: async (params) => {
|
|
15716
|
-
const { issueRpc } = await import('./rpc-
|
|
16004
|
+
const { issueRpc } = await import('./rpc-Dko-Fkk6.mjs');
|
|
15717
16005
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
15718
16006
|
},
|
|
15719
16007
|
onWorkflow: async (params) => {
|
|
15720
|
-
const { workflowRpc } = await import('./rpc-
|
|
16008
|
+
const { workflowRpc } = await import('./rpc-D-1o_gWF.mjs');
|
|
15721
16009
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
15722
16010
|
},
|
|
15723
16011
|
onRipgrep: async (args, cwd) => {
|
|
@@ -16266,11 +16554,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16266
16554
|
});
|
|
16267
16555
|
},
|
|
16268
16556
|
onIssue: async (params) => {
|
|
16269
|
-
const { issueRpc } = await import('./rpc-
|
|
16557
|
+
const { issueRpc } = await import('./rpc-Dko-Fkk6.mjs');
|
|
16270
16558
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
16271
16559
|
},
|
|
16272
16560
|
onWorkflow: async (params) => {
|
|
16273
|
-
const { workflowRpc } = await import('./rpc-
|
|
16561
|
+
const { workflowRpc } = await import('./rpc-D-1o_gWF.mjs');
|
|
16274
16562
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
16275
16563
|
},
|
|
16276
16564
|
onRipgrep: async (args, cwd) => {
|
|
@@ -16417,9 +16705,17 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16417
16705
|
}
|
|
16418
16706
|
}
|
|
16419
16707
|
let agentBackend;
|
|
16420
|
-
if (
|
|
16421
|
-
|
|
16708
|
+
if (KNOWN_CODEX_AGENTS[agentName]) {
|
|
16709
|
+
const avail = codexAppServerAvailable();
|
|
16710
|
+
if (avail === null) throw new Error("Codex CLI not found \u2014 install it (`npm i -g @openai/codex`) and authenticate (`svamp daemon codex-auth`).");
|
|
16711
|
+
if (avail === false) throw new Error("Installed Codex is too old for app-server \u2014 upgrade to >= 0.100 (`npm i -g @openai/codex@latest`).");
|
|
16712
|
+
const provider = resolveCodexProvider();
|
|
16713
|
+
const codexModel = options2.model || agentConfig?.default_model || provider.model || void 0;
|
|
16714
|
+
logger.log(`[Agent Session ${sessionId}] Codex backend: app-server (model=${codexModel ?? "default"}${provider.apiBase ? `, provider=${provider.apiBase}` : ""})`);
|
|
16715
|
+
agentBackend = new CodexAppServerBackend({
|
|
16422
16716
|
cwd: directory,
|
|
16717
|
+
model: codexModel,
|
|
16718
|
+
provider,
|
|
16423
16719
|
env: options2.environmentVariables,
|
|
16424
16720
|
log: logger.log,
|
|
16425
16721
|
isolationConfig: agentIsoConfig
|
|
@@ -16483,6 +16779,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16483
16779
|
return { ok: pending.length === 0, output: pending.length ? `${pending.length} pending: ${pending.map((i) => "#" + i.id).join(" ")}` : "No pending issues." };
|
|
16484
16780
|
};
|
|
16485
16781
|
try {
|
|
16782
|
+
if (!ls.acp_budget_warned && ls.budget && (ls.budget.max_tokens || ls.budget.max_tokens_per_hour)) {
|
|
16783
|
+
sessionService.pushMessage({ type: "message", message: "\u26A0\uFE0F A token/rate cost cap (--max-tokens / --max-tokens-per-hour) is INERT for this agent \u2014 Codex/Gemini (ACP) expose no token usage, so this loop is bounded only by --max (iterations) and --max-runtime-sec. Set one of those to cap cost.", level: "warning" }, "event");
|
|
16784
|
+
ls.acp_budget_warned = true;
|
|
16785
|
+
}
|
|
16486
16786
|
const acpMaxExt = resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS);
|
|
16487
16787
|
const acpEffMax = effectiveSoftCap(ls.max_iterations, ls.extensions, acpMaxExt);
|
|
16488
16788
|
const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: acpEffMax, ...ls.budget || {} }, now, startedAt);
|
|
@@ -16500,6 +16800,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16500
16800
|
return;
|
|
16501
16801
|
}
|
|
16502
16802
|
const oracle = await runOracle();
|
|
16803
|
+
if (loopCancelledDuringVerify(readLoopState(directory, sessionId))) return;
|
|
16503
16804
|
if (!oracle.ok) {
|
|
16504
16805
|
writeGoalLoopState(directory, sessionId, { ...ls, active: true, phase: "continue", ledger });
|
|
16505
16806
|
dispatchAcpPrompt(`Continue toward the goal: ${ls.goal_task || ls.until || "the task"}. Not done yet \u2014 the loop oracle still reports pending work:
|
|
@@ -16521,6 +16822,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16521
16822
|
log: (m) => logger.log(`[Session ${sessionId}] ${m}`)
|
|
16522
16823
|
}
|
|
16523
16824
|
);
|
|
16825
|
+
if (loopCancelledDuringVerify(readLoopState(directory, sessionId))) return;
|
|
16524
16826
|
if (result.action === "rekick") {
|
|
16525
16827
|
writeGoalLoopState(directory, sessionId, { ...ls, active: true, phase: "continue", holds: holds + 1, started_at: startedAt, ledger });
|
|
16526
16828
|
const guidance = result.guidance.replace(/\s+/g, " ").slice(0, 1200);
|
|
@@ -16700,6 +17002,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16700
17002
|
}
|
|
16701
17003
|
if (!loopDir) loopDir = loadSessionIndex()[sessionId]?.directory;
|
|
16702
17004
|
const wasInMemory = teardownTrackedSession(sessionId);
|
|
17005
|
+
idleTriggerTracker.forget(sessionId);
|
|
16703
17006
|
const markedArchived = markSessionAsArchived(sessionId);
|
|
16704
17007
|
if (loopDir && isLoopActiveForSession(loopDir, sessionId)) {
|
|
16705
17008
|
deactivateLoop(loopDir, sessionId);
|
|
@@ -16757,6 +17060,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16757
17060
|
}
|
|
16758
17061
|
if (!loopDir) loopDir = loadSessionIndex()[sessionId]?.directory;
|
|
16759
17062
|
teardownTrackedSession(sessionId);
|
|
17063
|
+
idleTriggerTracker.forget(sessionId);
|
|
16760
17064
|
deletePersistedSession(sessionId);
|
|
16761
17065
|
if (loopDir && isLoopActiveForSession(loopDir, sessionId)) {
|
|
16762
17066
|
deactivateLoop(loopDir, sessionId);
|
|
@@ -17038,7 +17342,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
17038
17342
|
}
|
|
17039
17343
|
if (persistedSessions.length > 0) {
|
|
17040
17344
|
try {
|
|
17041
|
-
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-
|
|
17345
|
+
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-B9O-hKxm.mjs');
|
|
17042
17346
|
await awaitClaudeVersionReady();
|
|
17043
17347
|
} catch {
|
|
17044
17348
|
}
|
|
@@ -17235,7 +17539,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
17235
17539
|
const PING_TIMEOUT_MS = 15e3;
|
|
17236
17540
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
17237
17541
|
const RECONNECT_JITTER_MS = 2500;
|
|
17238
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
17542
|
+
const { WorkflowScheduler } = await import('./scheduler-CN8jChyQ.mjs');
|
|
17239
17543
|
const workflowScheduler = new WorkflowScheduler({
|
|
17240
17544
|
projectRoots: () => {
|
|
17241
17545
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -17846,4 +18150,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
17846
18150
|
writeStopMarker: writeStopMarker
|
|
17847
18151
|
});
|
|
17848
18152
|
|
|
17849
|
-
export { downloadSkillFile as $, listRuns as A, getWorkflow as B, runWorkflow as C, setWorkflowEnabled as D, removeWorkflow as E, saveWorkflow as F, rawWorkflow as G, listWorkflows as H, isWorkflowEnabled as I, workflowCrons as J, cronMatches as K, summarize as L, workflowSteps as M, loadMachineContext as N, buildMachineInstructions as O, machineToolsForRole as P, buildMachineTools as Q, READ_ONLY_TOOLS as R, ServeAuth as S, parseFrontmatter as T, getSkillsServer as U, getSkillsWorkspaceName as V, getSkillsCollectionName as W, fetchWithTimeout as X, searchSkills as Y, SKILLS_DIR as Z, getSkillInfo as _, createSessionStore as a, listSkillFiles as a0, resolveModel as a1, formatHandle as a2, normalizeAllowedUser as a3, loadSecurityContextConfig as a4, resolveSecurityContext as a5, buildSecurityContextFromFlags as a6, mergeSecurityContexts as a7, buildSessionShareUrl as a8, computeOutboundHop as a9, registerAwaitingReply as aa, buildMachineShareUrl as ab, parseHandle as ac, handleMatchesMetadata as ad, describeMisconfiguration as ae, buildMachineDeps as af, applyClaudeProxyEnv as ag, composeSessionId as ah, generateFriendlyName as ai, generateHookSettings as aj, claudeAuth as ak, projectInfo as al, DefaultTransport$1 as am, acpBackend as an, acpAgentConfig as ao,
|
|
18153
|
+
export { downloadSkillFile as $, listRuns as A, getWorkflow as B, runWorkflow as C, setWorkflowEnabled as D, removeWorkflow as E, saveWorkflow as F, rawWorkflow as G, listWorkflows as H, isWorkflowEnabled as I, workflowCrons as J, cronMatches as K, summarize as L, workflowSteps as M, loadMachineContext as N, buildMachineInstructions as O, machineToolsForRole as P, buildMachineTools as Q, READ_ONLY_TOOLS as R, ServeAuth as S, parseFrontmatter as T, getSkillsServer as U, getSkillsWorkspaceName as V, getSkillsCollectionName as W, fetchWithTimeout as X, searchSkills as Y, SKILLS_DIR as Z, getSkillInfo as _, createSessionStore as a, listSkillFiles as a0, resolveModel as a1, formatHandle as a2, normalizeAllowedUser as a3, loadSecurityContextConfig as a4, resolveSecurityContext as a5, buildSecurityContextFromFlags as a6, mergeSecurityContexts as a7, buildSessionShareUrl as a8, computeOutboundHop as a9, registerAwaitingReply as aa, buildMachineShareUrl as ab, parseHandle as ac, handleMatchesMetadata as ad, describeMisconfiguration as ae, buildMachineDeps as af, applyClaudeProxyEnv as ag, composeSessionId as ah, generateFriendlyName as ai, generateHookSettings as aj, claudeAuth as ak, projectInfo as al, DefaultTransport$1 as am, acpBackend as an, acpAgentConfig as ao, codexProvider as ap, codexAppServerBackend as aq, GeminiTransport$1 as ar, instanceConfig as as, api as at, run as au, stopDaemon as b, connectToHypha as c, daemonStatus as d, clearStopMarker as e, stopMarkerExists as f, getHyphaServerUrl$1 as g, getFrpsSubdomainHost as h, getFrpsServerPort as i, getFrpsServerAddr as j, shortId as k, getHyphaServerUrl as l, hasCookieToken as m, resolveProjectRoot as n, getIssue as o, resumeIssue as p, pauseIssue as q, registerMachineService as r, startDaemon as s, addComment as t, updateIssue as u, addIssue as v, listIssues as w, searchIssues as x, isVisibleTo as y, getRun as z };
|