svamp-cli 0.2.258 → 0.2.260
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-CQ89BZon.mjs → agentCommands-BSv2sDdC.mjs} +6 -9
- package/dist/{auth-CaFsKYbx.mjs → auth-U2OQhXs1.mjs} +2 -5
- package/dist/cli.mjs +156 -69
- package/dist/{commands-DutfM3U7.mjs → commands-8qHg0szr.mjs} +8 -9
- package/dist/{commands-D8l74hsL.mjs → commands-BCMW-NM7.mjs} +2 -5
- package/dist/{commands-Dd8xEIf0.mjs → commands-BG8UHYMk.mjs} +2 -5
- package/dist/{commands-DrfYv705.mjs → commands-C3tM_vKp.mjs} +3 -6
- package/dist/{commands--X8f4eSr.mjs → commands-CMGqjCrU.mjs} +8 -16
- package/dist/{commands-B6eGMF5H.mjs → commands-DAZVK3MR.mjs} +2 -5
- package/dist/{commands-BgX0W0uK.mjs → commands-ql8AZ56B.mjs} +2 -5
- package/dist/{fleet-UvQnIWT1.mjs → fleet-CRR4JSTJ.mjs} +3 -6
- package/dist/{frpc-IXxyfgPn.mjs → frpc-k3ZLz2Mu.mjs} +11 -10
- package/dist/{headlessCli-BK7WSS3D.mjs → headlessCli-B78gc29T.mjs} +3 -6
- package/dist/index.mjs +2 -5
- package/dist/{package-BdZX5mDv.mjs → package-CKFmfq0u.mjs} +2 -2
- package/dist/{pinnedClaudeCode-DuLXaoGP.mjs → pinnedClaudeCode-B9O-hKxm.mjs} +1 -1
- package/dist/{rpc-CyACkgY1.mjs → rpc-BXrKU30m.mjs} +2 -5
- package/dist/{rpc-D7DB3XCH.mjs → rpc-DlioTx05.mjs} +2 -5
- package/dist/{run-C1ufZjIW.mjs → run-B8LDPH9R.mjs} +1 -5
- package/dist/{run-C1Unk4Zx.mjs → run-DggHONYC.mjs} +735 -423
- package/dist/{scheduler-7wve0Fy3.mjs → scheduler-B9WW7XGk.mjs} +2 -5
- package/dist/{serveCommands-iQJJLsMJ.mjs → serveCommands-DCA54z5Q.mjs} +5 -5
- package/dist/{serveManager-BszV0oIl.mjs → serveManager-BL3aySTV.mjs} +122 -7
- package/dist/{sideband-DMPZ-ZoR.mjs → sideband-COyK5_4F.mjs} +2 -5
- package/dist/{staticFileServer-FCEOLIYH.mjs → staticFileServer-CbYnj2bH.mjs} +21 -5
- package/package.json +2 -2
|
@@ -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-k3ZLz2Mu.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-COyK5_4F.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-BCMW-NM7.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,657 @@ 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
|
+
/** The app-server child process (for the daemon's liveness watchdog). */
|
|
8394
|
+
get childProcess() {
|
|
8395
|
+
return this.process;
|
|
8396
|
+
}
|
|
8397
|
+
// ── lifecycle ────────────────────────────────────────────────────────────
|
|
8398
|
+
async start() {
|
|
8399
|
+
if (this.connected) return;
|
|
8400
|
+
let command = "codex";
|
|
8401
|
+
let args = ["app-server", "--listen", "stdio://", ...this.opts.extraArgs ?? []];
|
|
8402
|
+
if (this.opts.isolationConfig) {
|
|
8403
|
+
const wrapped = wrapWithIsolation(command, args, this.opts.isolationConfig);
|
|
8404
|
+
command = wrapped.command;
|
|
8405
|
+
args = wrapped.args;
|
|
8406
|
+
}
|
|
8407
|
+
const child = spawn(command, args, {
|
|
8408
|
+
cwd: this.opts.cwd,
|
|
8409
|
+
// RUST_LOG mutes rollout noise on stderr; keep the rest of env for auth (~/.codex or key).
|
|
8410
|
+
env: { RUST_LOG: "error", ...process.env, ...this.opts.env ?? {} },
|
|
8411
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8312
8412
|
});
|
|
8313
|
-
this.
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
this.
|
|
8413
|
+
this.process = child;
|
|
8414
|
+
child.stderr?.on("data", (d) => this.log("[codex-app-server:stderr]", d.toString().trim()));
|
|
8415
|
+
child.on("exit", (code) => {
|
|
8416
|
+
const wasConnected = this.connected;
|
|
8417
|
+
this.connected = false;
|
|
8418
|
+
this.failAllPending(new Error(`codex app-server exited (code ${code})`));
|
|
8419
|
+
this.resolvePendingTurn(true);
|
|
8420
|
+
if (wasConnected) this.opts.onExit?.(code);
|
|
8421
|
+
});
|
|
8422
|
+
child.on("error", (err) => {
|
|
8423
|
+
this.log("[codex-app-server] spawn error", err);
|
|
8424
|
+
});
|
|
8425
|
+
this.readline = createInterface({ input: child.stdout });
|
|
8426
|
+
this.readline.on("line", (line) => this.onLine(line));
|
|
8427
|
+
const initParams = {
|
|
8428
|
+
clientInfo: { name: "svamp-codex", title: "Svamp Codex Client", version: "2.0.0" },
|
|
8429
|
+
capabilities: { experimentalApi: true }
|
|
8430
|
+
};
|
|
8431
|
+
await this.request("initialize", initParams);
|
|
8432
|
+
this.notify("initialized");
|
|
8433
|
+
this.connected = true;
|
|
8434
|
+
this.log("[codex-app-server] connected + initialized");
|
|
8435
|
+
}
|
|
8436
|
+
async startThread(o) {
|
|
8437
|
+
const params = {
|
|
8438
|
+
model: o.model ?? null,
|
|
8439
|
+
modelProvider: null,
|
|
8440
|
+
profile: null,
|
|
8441
|
+
cwd: o.cwd ?? this.opts.cwd,
|
|
8442
|
+
approvalPolicy: o.approvalPolicy ?? null,
|
|
8443
|
+
sandbox: o.sandbox ?? null,
|
|
8444
|
+
config: null,
|
|
8445
|
+
baseInstructions: null,
|
|
8446
|
+
developerInstructions: null,
|
|
8447
|
+
compactPrompt: null,
|
|
8448
|
+
includeApplyPatchTool: null,
|
|
8449
|
+
experimentalRawEvents: false,
|
|
8450
|
+
persistExtendedHistory: true
|
|
8451
|
+
};
|
|
8452
|
+
const r = await this.request("thread/start", params);
|
|
8453
|
+
this._threadId = r.thread.id;
|
|
8454
|
+
this._turnId = null;
|
|
8455
|
+
this.log("[codex-app-server] thread started", this._threadId);
|
|
8456
|
+
return { threadId: r.thread.id, model: r.model };
|
|
8457
|
+
}
|
|
8458
|
+
async resumeThread(o) {
|
|
8459
|
+
const params = {
|
|
8460
|
+
threadId: o.threadId,
|
|
8461
|
+
model: o.model ?? null,
|
|
8462
|
+
modelProvider: null,
|
|
8463
|
+
cwd: o.cwd ?? this.opts.cwd,
|
|
8464
|
+
approvalPolicy: o.approvalPolicy ?? null,
|
|
8465
|
+
sandbox: o.sandbox ?? null,
|
|
8466
|
+
config: null,
|
|
8467
|
+
baseInstructions: null,
|
|
8468
|
+
developerInstructions: null,
|
|
8469
|
+
persistExtendedHistory: true
|
|
8470
|
+
};
|
|
8471
|
+
const r = await this.request("thread/resume", params);
|
|
8472
|
+
this._threadId = r.thread.id;
|
|
8473
|
+
this._turnId = null;
|
|
8474
|
+
this.log("[codex-app-server] thread resumed", this._threadId);
|
|
8475
|
+
return { threadId: r.thread.id, model: r.model };
|
|
8476
|
+
}
|
|
8477
|
+
/** Build the turn/start params (exported shape kept minimal; only set fields are sent). */
|
|
8478
|
+
buildTurnParams(prompt, o) {
|
|
8479
|
+
const params = { threadId: this._threadId, input: [{ type: "text", text: prompt }] };
|
|
8480
|
+
if (o?.approvalPolicy) params.approvalPolicy = o.approvalPolicy;
|
|
8481
|
+
if (o?.model) params.model = o.model;
|
|
8482
|
+
if (o?.effort) params.effort = o.effort;
|
|
8483
|
+
if (o?.sandbox) {
|
|
8484
|
+
params.sandboxPolicy = { type: o.sandbox === "workspace-write" ? "workspaceWrite" : o.sandbox === "danger-full-access" ? "dangerFullAccess" : "readOnly" };
|
|
8485
|
+
}
|
|
8486
|
+
return params;
|
|
8487
|
+
}
|
|
8488
|
+
/** Start a turn; resolves immediately (completion is tracked via events / waitForTurn). */
|
|
8489
|
+
async startTurn(prompt, o) {
|
|
8490
|
+
if (!this._threadId) throw new Error("No active codex thread \u2014 call startThread first");
|
|
8491
|
+
const result = await this.request("turn/start", this.buildTurnParams(prompt, o));
|
|
8492
|
+
const id = result?.turn?.id;
|
|
8493
|
+
if (typeof id === "string" && id) this._turnId = id;
|
|
8494
|
+
}
|
|
8495
|
+
/** Send a turn and await completion (resolves {aborted} on task_complete/turn_aborted/timeout). */
|
|
8496
|
+
async sendTurnAndWait(prompt, o) {
|
|
8497
|
+
const timeoutMs = o?.timeoutMs ?? 14 * 24 * 60 * 60 * 1e3;
|
|
8498
|
+
let timer = null;
|
|
8499
|
+
const completion = new Promise((resolve) => {
|
|
8500
|
+
this.pendingTurn = { resolve };
|
|
8501
|
+
timer = setTimeout(() => {
|
|
8502
|
+
this.log("[codex-app-server] turn timeout");
|
|
8503
|
+
this.resolvePendingTurn(true);
|
|
8504
|
+
}, timeoutMs);
|
|
8505
|
+
timer?.unref?.();
|
|
8324
8506
|
});
|
|
8507
|
+
try {
|
|
8508
|
+
await this.startTurn(prompt, o);
|
|
8509
|
+
} catch (e) {
|
|
8510
|
+
if (timer) clearTimeout(timer);
|
|
8511
|
+
this.pendingTurn = null;
|
|
8512
|
+
throw e;
|
|
8513
|
+
}
|
|
8514
|
+
const aborted = await completion;
|
|
8515
|
+
if (timer) clearTimeout(timer);
|
|
8516
|
+
return { aborted };
|
|
8325
8517
|
}
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
this.
|
|
8518
|
+
/** Interrupt the in-flight turn (real server-side abort — the old MCP path couldn't do this). */
|
|
8519
|
+
async interrupt() {
|
|
8520
|
+
if (!this._threadId || !this._turnId) return;
|
|
8521
|
+
const params = { threadId: this._threadId, turnId: this._turnId };
|
|
8522
|
+
try {
|
|
8523
|
+
await this.request("turn/interrupt", params, 1e4);
|
|
8524
|
+
} catch (e) {
|
|
8525
|
+
this.log("[codex-app-server] interrupt err (may be benign)", e);
|
|
8526
|
+
}
|
|
8329
8527
|
}
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8528
|
+
async dispose() {
|
|
8529
|
+
this.readline?.close();
|
|
8530
|
+
this.readline = null;
|
|
8531
|
+
const proc = this.process;
|
|
8532
|
+
const pid = proc?.pid;
|
|
8533
|
+
try {
|
|
8534
|
+
proc?.stdin?.end();
|
|
8535
|
+
proc?.kill("SIGTERM");
|
|
8536
|
+
} catch {
|
|
8537
|
+
}
|
|
8538
|
+
if (pid) {
|
|
8539
|
+
const t = setTimeout(() => {
|
|
8540
|
+
try {
|
|
8541
|
+
process.kill(pid, 0);
|
|
8542
|
+
process.kill(pid, "SIGKILL");
|
|
8543
|
+
} catch {
|
|
8544
|
+
}
|
|
8545
|
+
}, 2e3);
|
|
8546
|
+
t.unref?.();
|
|
8547
|
+
}
|
|
8548
|
+
this.process = null;
|
|
8549
|
+
this.connected = false;
|
|
8550
|
+
this._threadId = null;
|
|
8551
|
+
this._turnId = null;
|
|
8552
|
+
this.failAllPending(new Error("codex app-server disposed"));
|
|
8553
|
+
this.resolvePendingTurn(true);
|
|
8554
|
+
}
|
|
8555
|
+
// ── JSON-RPC plumbing ──────────────────────────────────────────────────────
|
|
8556
|
+
request(method, params, timeoutMs) {
|
|
8557
|
+
const id = this.nextId++;
|
|
8558
|
+
return new Promise((resolve, reject) => {
|
|
8559
|
+
this.pending.set(id, { resolve, reject, method });
|
|
8560
|
+
if (timeoutMs) {
|
|
8561
|
+
const t = setTimeout(() => {
|
|
8562
|
+
if (this.pending.has(id)) {
|
|
8563
|
+
this.pending.delete(id);
|
|
8564
|
+
reject(new Error(`codex ${method} timed out after ${timeoutMs}ms`));
|
|
8565
|
+
}
|
|
8566
|
+
}, timeoutMs);
|
|
8567
|
+
t.unref?.();
|
|
8568
|
+
}
|
|
8569
|
+
this.write({ jsonrpc: "2.0", id, method, params });
|
|
8570
|
+
});
|
|
8333
8571
|
}
|
|
8572
|
+
notify(method, params) {
|
|
8573
|
+
this.write({ jsonrpc: "2.0", method, params });
|
|
8574
|
+
}
|
|
8575
|
+
respond(id, result) {
|
|
8576
|
+
this.write({ jsonrpc: "2.0", id, result });
|
|
8577
|
+
}
|
|
8578
|
+
write(obj) {
|
|
8579
|
+
try {
|
|
8580
|
+
this.process?.stdin?.write(JSON.stringify(obj) + "\n");
|
|
8581
|
+
} catch (e) {
|
|
8582
|
+
this.log("[codex-app-server] write error", e);
|
|
8583
|
+
}
|
|
8584
|
+
}
|
|
8585
|
+
failAllPending(err) {
|
|
8586
|
+
for (const [id, p] of this.pending) {
|
|
8587
|
+
p.reject(err);
|
|
8588
|
+
this.pending.delete(id);
|
|
8589
|
+
}
|
|
8590
|
+
}
|
|
8591
|
+
onLine(line) {
|
|
8592
|
+
const trimmed = line.trim();
|
|
8593
|
+
if (!trimmed) return;
|
|
8594
|
+
let msg;
|
|
8595
|
+
try {
|
|
8596
|
+
msg = JSON.parse(trimmed);
|
|
8597
|
+
} catch {
|
|
8598
|
+
this.log("[codex-app-server] non-JSON line", trimmed.slice(0, 200));
|
|
8599
|
+
return;
|
|
8600
|
+
}
|
|
8601
|
+
if (msg.id !== void 0 && msg.method === void 0) {
|
|
8602
|
+
const p = this.pending.get(msg.id);
|
|
8603
|
+
if (!p) return;
|
|
8604
|
+
this.pending.delete(msg.id);
|
|
8605
|
+
const r = msg;
|
|
8606
|
+
if (r.error) p.reject(new Error(`codex ${p.method}: ${r.error.message}`));
|
|
8607
|
+
else p.resolve(r.result);
|
|
8608
|
+
return;
|
|
8609
|
+
}
|
|
8610
|
+
if (msg.id !== void 0 && typeof msg.method === "string") {
|
|
8611
|
+
void this.handleServerRequest(msg.id, msg.method, msg.params);
|
|
8612
|
+
return;
|
|
8613
|
+
}
|
|
8614
|
+
if (typeof msg.method === "string") {
|
|
8615
|
+
this.handleNotification(msg.method, msg.params);
|
|
8616
|
+
return;
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
// ── server→client approval requests ────────────────────────────────────────
|
|
8620
|
+
async handleServerRequest(id, method, params) {
|
|
8621
|
+
const handler = this.opts.onApproval;
|
|
8622
|
+
if (method === "item/commandExecution/requestApproval" || method === "execCommandApproval") {
|
|
8623
|
+
const callId = String(params?.itemId ?? params?.callId ?? id);
|
|
8624
|
+
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";
|
|
8625
|
+
this.respond(id, { decision: mapDecisionToWire(decision) });
|
|
8626
|
+
return;
|
|
8627
|
+
}
|
|
8628
|
+
if (method === "item/fileChange/requestApproval" || method === "applyPatchApproval") {
|
|
8629
|
+
const callId = String(params?.itemId ?? params?.callId ?? id);
|
|
8630
|
+
const decision = handler ? await handler({ type: "patch", callId, fileChanges: params?.fileChanges, reason: params?.reason }).catch(() => "denied") : "denied";
|
|
8631
|
+
this.respond(id, { decision: mapDecisionToWire(decision) });
|
|
8632
|
+
return;
|
|
8633
|
+
}
|
|
8634
|
+
if (method === "mcpServer/elicitation/request") {
|
|
8635
|
+
const callId = `${params?.serverName ?? "mcp"}:${id}`;
|
|
8636
|
+
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";
|
|
8637
|
+
const wire = mapDecisionToWire(decision);
|
|
8638
|
+
this.respond(id, { action: wire === "acceptForSession" ? "accept" : wire });
|
|
8639
|
+
return;
|
|
8640
|
+
}
|
|
8641
|
+
this.respond(id, {});
|
|
8642
|
+
}
|
|
8643
|
+
// ── notifications (events + lifecycle) ──────────────────────────────────────
|
|
8644
|
+
handleNotification(method, params) {
|
|
8645
|
+
if (method === "codex/event" || method.startsWith("codex/event/")) {
|
|
8646
|
+
const emsg = params?.msg;
|
|
8647
|
+
if (!emsg) return;
|
|
8648
|
+
if (emsg.type === "task_started" && emsg.turn_id) this._turnId = String(emsg.turn_id);
|
|
8649
|
+
this.opts.onEvent?.(emsg);
|
|
8650
|
+
if (emsg.type === "task_complete" || emsg.type === "turn_aborted") {
|
|
8651
|
+
const tid = emsg.turn_id ?? emsg.turnId ?? null;
|
|
8652
|
+
if (tid) this.completedTurnIds.add(String(tid));
|
|
8653
|
+
this.resolvePendingTurn(emsg.type === "turn_aborted");
|
|
8654
|
+
this._turnId = null;
|
|
8655
|
+
}
|
|
8656
|
+
return;
|
|
8657
|
+
}
|
|
8658
|
+
if (method === "turn/started") {
|
|
8659
|
+
const tid = params?.turn?.id ?? params?.turnId;
|
|
8660
|
+
if (tid) this._turnId = String(tid);
|
|
8661
|
+
return;
|
|
8662
|
+
}
|
|
8663
|
+
if (method === "turn/completed") {
|
|
8664
|
+
const tid = params?.turn?.id ?? params?.turnId ?? null;
|
|
8665
|
+
if (tid && this.completedTurnIds.has(String(tid))) return;
|
|
8666
|
+
const status = params?.turn?.status ?? params?.status;
|
|
8667
|
+
const aborted = typeof status === "string" && /abort|cancel|error/i.test(status);
|
|
8668
|
+
this.resolvePendingTurn(aborted);
|
|
8669
|
+
this._turnId = null;
|
|
8670
|
+
return;
|
|
8671
|
+
}
|
|
8672
|
+
const normalized = normalizeNotification(method, params);
|
|
8673
|
+
if (normalized) this.opts.onEvent?.(normalized);
|
|
8674
|
+
}
|
|
8675
|
+
resolvePendingTurn(aborted) {
|
|
8676
|
+
const p = this.pendingTurn;
|
|
8677
|
+
this.pendingTurn = null;
|
|
8678
|
+
p?.resolve(aborted);
|
|
8679
|
+
}
|
|
8680
|
+
}
|
|
8681
|
+
function mapDecisionToWire(d) {
|
|
8682
|
+
switch (d) {
|
|
8683
|
+
case "approved":
|
|
8684
|
+
return "accept";
|
|
8685
|
+
case "approved_for_session":
|
|
8686
|
+
return "acceptForSession";
|
|
8687
|
+
case "denied":
|
|
8688
|
+
return "decline";
|
|
8689
|
+
case "abort":
|
|
8690
|
+
return "cancel";
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
|
|
8694
|
+
const CODEX_PROVIDER_KEY_ENV = "SVAMP_CODEX_API_KEY";
|
|
8695
|
+
const PROVIDER_NAME = "svamp";
|
|
8696
|
+
function resolveCodexProvider(env = process.env) {
|
|
8697
|
+
return {
|
|
8698
|
+
apiBase: env.SVAMP_CODEX_API_BASE || env.LLM_API_BASE || void 0,
|
|
8699
|
+
apiKey: env.SVAMP_CODEX_API_KEY || env.LLM_API_KEY || void 0,
|
|
8700
|
+
model: env.SVAMP_CODEX_MODEL || env.LLM_MODEL || void 0
|
|
8701
|
+
};
|
|
8702
|
+
}
|
|
8703
|
+
function hasCustomProvider(cfg) {
|
|
8704
|
+
return !!(cfg.apiBase && cfg.apiKey);
|
|
8705
|
+
}
|
|
8706
|
+
function buildCodexProviderArgs(cfg) {
|
|
8707
|
+
if (!hasCustomProvider(cfg)) {
|
|
8708
|
+
return { configArgs: cfg.model ? ["-c", `model=${JSON.stringify(cfg.model)}`] : [], env: {}, model: cfg.model };
|
|
8709
|
+
}
|
|
8710
|
+
const configArgs = [
|
|
8711
|
+
"-c",
|
|
8712
|
+
`model_provider=${JSON.stringify(PROVIDER_NAME)}`,
|
|
8713
|
+
"-c",
|
|
8714
|
+
`model_providers.${PROVIDER_NAME}.name=${JSON.stringify(PROVIDER_NAME)}`,
|
|
8715
|
+
"-c",
|
|
8716
|
+
`model_providers.${PROVIDER_NAME}.base_url=${JSON.stringify(cfg.apiBase)}`,
|
|
8717
|
+
"-c",
|
|
8718
|
+
`model_providers.${PROVIDER_NAME}.env_key=${JSON.stringify(CODEX_PROVIDER_KEY_ENV)}`,
|
|
8719
|
+
"-c",
|
|
8720
|
+
`model_providers.${PROVIDER_NAME}.wire_api=${JSON.stringify("responses")}`
|
|
8721
|
+
];
|
|
8722
|
+
if (cfg.model) configArgs.push("-c", `model=${JSON.stringify(cfg.model)}`);
|
|
8723
|
+
return { configArgs, env: { [CODEX_PROVIDER_KEY_ENV]: cfg.apiKey }, model: cfg.model };
|
|
8724
|
+
}
|
|
8725
|
+
|
|
8726
|
+
var codexProvider = /*#__PURE__*/Object.freeze({
|
|
8727
|
+
__proto__: null,
|
|
8728
|
+
CODEX_PROVIDER_KEY_ENV: CODEX_PROVIDER_KEY_ENV,
|
|
8729
|
+
buildCodexProviderArgs: buildCodexProviderArgs,
|
|
8730
|
+
hasCustomProvider: hasCustomProvider,
|
|
8731
|
+
resolveCodexProvider: resolveCodexProvider
|
|
8732
|
+
});
|
|
8733
|
+
|
|
8734
|
+
class CodexAppServerBackend {
|
|
8735
|
+
constructor(opts) {
|
|
8736
|
+
this.opts = opts;
|
|
8737
|
+
this.log = opts.log ?? (() => {
|
|
8738
|
+
});
|
|
8739
|
+
const provider = opts.provider ?? resolveCodexProvider();
|
|
8740
|
+
const { configArgs, env: providerEnv, model: providerModel } = buildCodexProviderArgs(provider);
|
|
8741
|
+
this.model = opts.model ?? providerModel;
|
|
8742
|
+
this.client = new CodexAppServerClient({
|
|
8743
|
+
cwd: opts.cwd,
|
|
8744
|
+
env: { ...opts.env ?? {}, ...providerEnv },
|
|
8745
|
+
extraArgs: configArgs,
|
|
8746
|
+
log: this.log,
|
|
8747
|
+
isolationConfig: opts.isolationConfig,
|
|
8748
|
+
onEvent: (m) => this.handleCodexEvent(m),
|
|
8749
|
+
onApproval: (r) => this.handleApproval(r),
|
|
8750
|
+
onExit: (code) => this.emit({ type: "status", status: "error", detail: `codex app-server exited (code ${code})` })
|
|
8751
|
+
});
|
|
8752
|
+
}
|
|
8753
|
+
listeners = [];
|
|
8754
|
+
client;
|
|
8755
|
+
sessionId = randomUUID();
|
|
8756
|
+
log;
|
|
8757
|
+
model;
|
|
8758
|
+
started = false;
|
|
8759
|
+
// Approval promises keyed by callId, resolved by respondToPermission().
|
|
8760
|
+
pendingApprovals = /* @__PURE__ */ new Map();
|
|
8761
|
+
// Whether we streamed any delta for the in-flight assistant message (to avoid a duplicate
|
|
8762
|
+
// fullText push on the final agent_message when the model DID stream).
|
|
8763
|
+
streamedThisMessage = false;
|
|
8764
|
+
// Tracks the in-flight turn so waitForResponseComplete() can await it.
|
|
8765
|
+
turnDone = null;
|
|
8766
|
+
/** True/false if codex supports app-server; null if codex isn't installed. */
|
|
8767
|
+
static available() {
|
|
8768
|
+
return codexAppServerAvailable();
|
|
8769
|
+
}
|
|
8770
|
+
// ── AgentBackend ───────────────────────────────────────────────────────────
|
|
8334
8771
|
async startSession(initialPrompt) {
|
|
8335
|
-
const sessionId = randomUUID();
|
|
8336
|
-
this.svampSessionId = sessionId;
|
|
8337
8772
|
this.emit({ type: "status", status: "starting" });
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
if (
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8773
|
+
const avail = codexAppServerAvailable();
|
|
8774
|
+
if (avail === null) throw new Error("Codex CLI not found. Install it (`npm i -g @openai/codex`) and authenticate (`svamp daemon codex-auth`).");
|
|
8775
|
+
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`).");
|
|
8776
|
+
await this.client.start();
|
|
8777
|
+
if (this.opts.resumeThreadId) {
|
|
8778
|
+
await this.client.resumeThread({ threadId: this.opts.resumeThreadId, model: this.model, approvalPolicy: this.opts.approvalPolicy, sandbox: this.opts.sandbox });
|
|
8779
|
+
} else {
|
|
8780
|
+
const r = await this.client.startThread({ model: this.model, approvalPolicy: this.opts.approvalPolicy, sandbox: this.opts.sandbox });
|
|
8781
|
+
this.model = this.model ?? r.model;
|
|
8345
8782
|
}
|
|
8346
|
-
|
|
8783
|
+
this.started = true;
|
|
8784
|
+
this.emit({ type: "status", status: "idle" });
|
|
8785
|
+
if (initialPrompt) await this.sendPrompt(this.sessionId, initialPrompt);
|
|
8786
|
+
return { sessionId: this.sessionId };
|
|
8347
8787
|
}
|
|
8348
|
-
async sendPrompt(
|
|
8349
|
-
if (!this.
|
|
8350
|
-
this.turnCancelled = false;
|
|
8351
|
-
const myTurnId = ++this.turnId;
|
|
8788
|
+
async sendPrompt(_sessionId, prompt) {
|
|
8789
|
+
if (!this.started) throw new Error("Codex session not started");
|
|
8352
8790
|
this.emit({ type: "status", status: "running" });
|
|
8353
|
-
|
|
8791
|
+
this.streamedThisMessage = false;
|
|
8792
|
+
let resolveTurn;
|
|
8793
|
+
this.turnDone = new Promise((res) => {
|
|
8794
|
+
resolveTurn = res;
|
|
8795
|
+
});
|
|
8354
8796
|
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
|
-
}
|
|
8797
|
+
await this.client.sendTurnAndWait(prompt, { model: this.model, approvalPolicy: this.opts.approvalPolicy, sandbox: this.opts.sandbox });
|
|
8373
8798
|
} 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;
|
|
8799
|
+
this.emit({ type: "status", status: "error", detail: err instanceof Error ? err.message : String(err) });
|
|
8378
8800
|
} finally {
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8801
|
+
this.emit({ type: "status", status: "idle" });
|
|
8802
|
+
resolveTurn();
|
|
8803
|
+
this.turnDone = null;
|
|
8382
8804
|
}
|
|
8383
8805
|
}
|
|
8384
8806
|
async cancel(_sessionId) {
|
|
8385
|
-
this.
|
|
8386
|
-
this.turnCancelled = true;
|
|
8807
|
+
await this.client.interrupt();
|
|
8387
8808
|
this.emit({ type: "status", status: "cancelled" });
|
|
8388
8809
|
}
|
|
8810
|
+
onMessage(handler) {
|
|
8811
|
+
this.listeners.push(handler);
|
|
8812
|
+
}
|
|
8813
|
+
offMessage(handler) {
|
|
8814
|
+
this.listeners = this.listeners.filter((h) => h !== handler);
|
|
8815
|
+
}
|
|
8389
8816
|
async respondToPermission(requestId, approved) {
|
|
8390
|
-
const
|
|
8391
|
-
if (
|
|
8817
|
+
const resolve = this.pendingApprovals.get(requestId);
|
|
8818
|
+
if (resolve) {
|
|
8392
8819
|
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 });
|
|
8820
|
+
resolve(approved ? "approved" : "denied");
|
|
8398
8821
|
}
|
|
8822
|
+
this.emit({ type: "permission-response", id: requestId, approved });
|
|
8823
|
+
}
|
|
8824
|
+
async waitForResponseComplete(timeoutMs = 14 * 24 * 60 * 60 * 1e3) {
|
|
8825
|
+
if (!this.turnDone) return;
|
|
8826
|
+
await Promise.race([this.turnDone, new Promise((res) => {
|
|
8827
|
+
const t = setTimeout(res, timeoutMs);
|
|
8828
|
+
t.unref?.();
|
|
8829
|
+
})]);
|
|
8399
8830
|
}
|
|
8400
8831
|
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
|
-
}
|
|
8832
|
+
for (const [, resolve] of this.pendingApprovals) resolve("denied");
|
|
8407
8833
|
this.pendingApprovals.clear();
|
|
8408
|
-
await this.
|
|
8409
|
-
this.listeners = [];
|
|
8834
|
+
await this.client.dispose();
|
|
8410
8835
|
}
|
|
8411
|
-
/**
|
|
8412
|
-
|
|
8413
|
-
return this.
|
|
8836
|
+
/** The live Codex thread id (for the daemon to persist and resume after a restart). */
|
|
8837
|
+
getThreadId() {
|
|
8838
|
+
return this.client.threadId;
|
|
8414
8839
|
}
|
|
8415
|
-
/**
|
|
8416
|
-
* Return a process-like object for TrackedSession.childProcess.
|
|
8417
|
-
* We expose a minimal { kill } object that closes the transport.
|
|
8418
|
-
*/
|
|
8840
|
+
/** The underlying `codex app-server` child process, for the daemon's liveness watchdog. */
|
|
8419
8841
|
getProcess() {
|
|
8420
|
-
|
|
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
|
-
};
|
|
8842
|
+
return this.client.childProcess ?? void 0;
|
|
8432
8843
|
}
|
|
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
|
-
}
|
|
8844
|
+
/** Update the model used for subsequent turns (per-turn override — no restart needed). */
|
|
8845
|
+
setModel(model) {
|
|
8846
|
+
this.model = model;
|
|
8484
8847
|
}
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
const
|
|
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) {
|
|
8848
|
+
// ── internal ─────────────────────────────────────────────────────────────
|
|
8849
|
+
emit(msg) {
|
|
8850
|
+
for (const h of this.listeners) {
|
|
8498
8851
|
try {
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
} catch {
|
|
8503
|
-
}
|
|
8504
|
-
} catch {
|
|
8852
|
+
h(msg);
|
|
8853
|
+
} catch (e) {
|
|
8854
|
+
this.log("[codex-backend] listener error", e);
|
|
8505
8855
|
}
|
|
8506
8856
|
}
|
|
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
8857
|
}
|
|
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
|
-
);
|
|
8858
|
+
handleApproval(req) {
|
|
8859
|
+
const id = req.callId || randomUUID();
|
|
8860
|
+
const reason = req.type === "exec" ? `Execute: ${(req.command ?? []).join(" ")}` : req.type === "patch" ? "Apply file changes" : req.toolName ? `Tool: ${req.toolName}` : "Codex requires approval";
|
|
8861
|
+
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 };
|
|
8862
|
+
this.emit({ type: "permission-request", id, reason, payload });
|
|
8863
|
+
return new Promise((resolve) => {
|
|
8864
|
+
this.pendingApprovals.set(id, resolve);
|
|
8865
|
+
const t = setTimeout(() => {
|
|
8866
|
+
if (this.pendingApprovals.delete(id)) resolve("denied");
|
|
8867
|
+
}, 5 * 60 * 1e3);
|
|
8868
|
+
t.unref?.();
|
|
8869
|
+
});
|
|
8547
8870
|
}
|
|
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
8871
|
handleCodexEvent(event) {
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
this.log(`[Codex] Event: ${eventType}`);
|
|
8577
|
-
switch (eventType) {
|
|
8872
|
+
const t = event.type;
|
|
8873
|
+
switch (t) {
|
|
8578
8874
|
case "task_started":
|
|
8579
8875
|
this.emit({ type: "status", status: "running" });
|
|
8580
8876
|
break;
|
|
8581
8877
|
case "task_complete":
|
|
8582
|
-
break;
|
|
8583
8878
|
case "turn_aborted":
|
|
8584
8879
|
break;
|
|
8585
|
-
case "
|
|
8586
|
-
const
|
|
8587
|
-
if (
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
this.emit({ type: "model-output", fullText: block.text });
|
|
8591
|
-
}
|
|
8592
|
-
}
|
|
8880
|
+
case "agent_message_delta": {
|
|
8881
|
+
const delta = event.delta ?? (typeof event.text === "string" ? event.text : "");
|
|
8882
|
+
if (delta) {
|
|
8883
|
+
this.streamedThisMessage = true;
|
|
8884
|
+
this.emit({ type: "model-output", textDelta: delta });
|
|
8593
8885
|
}
|
|
8594
8886
|
break;
|
|
8595
8887
|
}
|
|
8596
|
-
case "
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8888
|
+
case "agent_message": {
|
|
8889
|
+
if (this.streamedThisMessage) {
|
|
8890
|
+
this.streamedThisMessage = false;
|
|
8891
|
+
break;
|
|
8600
8892
|
}
|
|
8893
|
+
const text = mapCodexEventToText(event);
|
|
8894
|
+
if (text) this.emit({ type: "model-output", fullText: text });
|
|
8601
8895
|
break;
|
|
8602
8896
|
}
|
|
8897
|
+
case "agent_reasoning_delta":
|
|
8603
8898
|
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
|
-
}
|
|
8899
|
+
const text = event.delta?.text || event.text || (Array.isArray(event.content) ? event.content.map((c) => c?.text).join("") : "");
|
|
8900
|
+
if (text) this.emit({ type: "event", name: "thinking", payload: { text } });
|
|
8608
8901
|
break;
|
|
8609
8902
|
}
|
|
8610
8903
|
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
|
-
});
|
|
8904
|
+
const callId = String(event.call_id ?? event.callId ?? randomUUID());
|
|
8905
|
+
const command = Array.isArray(event.command) ? event.command.join(" ") : String(event.command ?? "");
|
|
8906
|
+
this.emit({ type: "tool-call", toolName: "CodexBash", callId, args: { command, cwd: event.cwd } });
|
|
8619
8907
|
break;
|
|
8620
8908
|
}
|
|
8621
8909
|
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
|
-
});
|
|
8910
|
+
const callId = String(event.call_id ?? event.callId ?? "");
|
|
8911
|
+
this.emit({ type: "tool-result", toolName: "CodexBash", callId, result: { exitCode: event.exit_code ?? event.exitCode, stdout: event.stdout ?? "", stderr: event.stderr ?? "" } });
|
|
8633
8912
|
break;
|
|
8634
8913
|
}
|
|
8635
8914
|
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
|
-
});
|
|
8915
|
+
const callId = String(event.call_id ?? event.callId ?? randomUUID());
|
|
8916
|
+
this.emit({ type: "tool-call", toolName: "CodexPatch", callId, args: { filePath: event.file_path ?? event.filePath, patch: event.patch } });
|
|
8643
8917
|
break;
|
|
8644
8918
|
}
|
|
8645
8919
|
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
|
-
});
|
|
8920
|
+
const callId = String(event.call_id ?? event.callId ?? "");
|
|
8921
|
+
this.emit({ type: "tool-result", toolName: "CodexPatch", callId, result: { filePath: event.file_path ?? event.filePath, applied: event.applied, error: event.error } });
|
|
8657
8922
|
break;
|
|
8658
8923
|
}
|
|
8659
|
-
|
|
8660
|
-
|
|
8924
|
+
case "mcp_tool_call_begin": {
|
|
8925
|
+
const callId = String(event.call_id ?? event.callId ?? randomUUID());
|
|
8926
|
+
this.emit({ type: "tool-call", toolName: String(event.tool ?? "CodexMcpTool"), callId, args: { server: event.server, arguments: event.arguments } });
|
|
8661
8927
|
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
8928
|
}
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8929
|
+
case "mcp_tool_call_end": {
|
|
8930
|
+
const callId = String(event.call_id ?? event.callId ?? "");
|
|
8931
|
+
this.emit({ type: "tool-result", toolName: String(event.tool ?? "CodexMcpTool"), callId, result: { result: event.result, error: event.error } });
|
|
8932
|
+
break;
|
|
8933
|
+
}
|
|
8934
|
+
case "token_count":
|
|
8935
|
+
case "token_usage": {
|
|
8936
|
+
this.emit({ type: "event", name: "usage", payload: event.usage ?? event.info ?? event });
|
|
8937
|
+
break;
|
|
8695
8938
|
}
|
|
8939
|
+
case "error": {
|
|
8940
|
+
const msg = event.message ?? event.error ?? "Codex error";
|
|
8941
|
+
this.emit({ type: "status", status: "error", detail: String(msg) });
|
|
8942
|
+
break;
|
|
8943
|
+
}
|
|
8944
|
+
default:
|
|
8945
|
+
if (t === "_rpc/thread/tokenUsage/updated") {
|
|
8946
|
+
this.emit({ type: "event", name: "usage", payload: event.params });
|
|
8947
|
+
}
|
|
8948
|
+
break;
|
|
8696
8949
|
}
|
|
8697
8950
|
}
|
|
8698
8951
|
}
|
|
8699
8952
|
|
|
8700
|
-
var
|
|
8953
|
+
var codexAppServerBackend = /*#__PURE__*/Object.freeze({
|
|
8701
8954
|
__proto__: null,
|
|
8702
|
-
|
|
8955
|
+
CodexAppServerBackend: CodexAppServerBackend
|
|
8703
8956
|
});
|
|
8704
8957
|
|
|
8705
8958
|
const GEMINI_TIMEOUTS = {
|
|
@@ -9676,9 +9929,11 @@ class TokenCache {
|
|
|
9676
9929
|
const oldest = this.cache.keys().next().value;
|
|
9677
9930
|
if (oldest) this.cache.delete(oldest);
|
|
9678
9931
|
}
|
|
9932
|
+
const ttlExpiry = Date.now() + this.ttlMs;
|
|
9933
|
+
const jwtExpMs = parseJwtExpMs(token);
|
|
9679
9934
|
this.cache.set(token, {
|
|
9680
9935
|
email,
|
|
9681
|
-
expiresAt:
|
|
9936
|
+
expiresAt: jwtExpMs !== null ? Math.min(ttlExpiry, jwtExpMs) : ttlExpiry
|
|
9682
9937
|
});
|
|
9683
9938
|
}
|
|
9684
9939
|
clear() {
|
|
@@ -9710,6 +9965,16 @@ function parseCookies(header) {
|
|
|
9710
9965
|
}
|
|
9711
9966
|
return cookies;
|
|
9712
9967
|
}
|
|
9968
|
+
function parseJwtExpMs(token) {
|
|
9969
|
+
try {
|
|
9970
|
+
const parts = token.split(".");
|
|
9971
|
+
if (parts.length !== 3) return null;
|
|
9972
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
9973
|
+
return typeof payload.exp === "number" ? payload.exp * 1e3 : null;
|
|
9974
|
+
} catch {
|
|
9975
|
+
return null;
|
|
9976
|
+
}
|
|
9977
|
+
}
|
|
9713
9978
|
function parseJwtEmail(token) {
|
|
9714
9979
|
try {
|
|
9715
9980
|
const parts = token.split(".");
|
|
@@ -9728,6 +9993,19 @@ function parseJwtEmail(token) {
|
|
|
9728
9993
|
return null;
|
|
9729
9994
|
}
|
|
9730
9995
|
}
|
|
9996
|
+
function isStructurallyLiveJwt(token) {
|
|
9997
|
+
try {
|
|
9998
|
+
const parts = token.split(".");
|
|
9999
|
+
if (parts.length !== 3) return false;
|
|
10000
|
+
const payload = JSON.parse(
|
|
10001
|
+
Buffer.from(parts[1], "base64url").toString("utf-8")
|
|
10002
|
+
);
|
|
10003
|
+
if (payload.exp && payload.exp * 1e3 < Date.now()) return false;
|
|
10004
|
+
return true;
|
|
10005
|
+
} catch {
|
|
10006
|
+
return false;
|
|
10007
|
+
}
|
|
10008
|
+
}
|
|
9731
10009
|
async function verifyTokenViaHypha(token, hyphaServerUrl) {
|
|
9732
10010
|
try {
|
|
9733
10011
|
const baseUrl = hyphaServerUrl.replace(/\/$/, "");
|
|
@@ -9766,11 +10044,7 @@ class ServeAuth {
|
|
|
9766
10044
|
if (!token) return null;
|
|
9767
10045
|
const cached = this.cache.get(token);
|
|
9768
10046
|
if (cached) return cached.email;
|
|
9769
|
-
|
|
9770
|
-
if (localEmail) {
|
|
9771
|
-
this.cache.set(token, localEmail);
|
|
9772
|
-
return localEmail;
|
|
9773
|
-
}
|
|
10047
|
+
if (!isStructurallyLiveJwt(token)) return null;
|
|
9774
10048
|
const serverEmail = await verifyTokenViaHypha(token, this.hyphaServerUrl);
|
|
9775
10049
|
if (serverEmail) {
|
|
9776
10050
|
this.cache.set(token, serverEmail);
|
|
@@ -11321,7 +11595,27 @@ function escalateWorkflowFailure(projectRoot, wf, run) {
|
|
|
11321
11595
|
return void 0;
|
|
11322
11596
|
}
|
|
11323
11597
|
}
|
|
11324
|
-
|
|
11598
|
+
const inFlightRuns = /* @__PURE__ */ new Map();
|
|
11599
|
+
function workflowLockKey(projectRoot, name) {
|
|
11600
|
+
return `${projectRoot}\0${name}`;
|
|
11601
|
+
}
|
|
11602
|
+
function runWorkflow(projectRoot, wf, opts) {
|
|
11603
|
+
if (opts.allowConcurrent) return runWorkflowInner(projectRoot, wf, opts);
|
|
11604
|
+
const key = workflowLockKey(projectRoot, wf.name);
|
|
11605
|
+
const existing = inFlightRuns.get(key);
|
|
11606
|
+
if (existing) {
|
|
11607
|
+
opts.log?.(`[workflow] run coalesced "${wf.name}" in ${projectRoot} \u2014 a run of this workflow is already in flight`);
|
|
11608
|
+
opts.onCoalesced?.();
|
|
11609
|
+
return existing;
|
|
11610
|
+
}
|
|
11611
|
+
const p = runWorkflowInner(projectRoot, wf, opts);
|
|
11612
|
+
inFlightRuns.set(key, p);
|
|
11613
|
+
void p.finally(() => {
|
|
11614
|
+
if (inFlightRuns.get(key) === p) inFlightRuns.delete(key);
|
|
11615
|
+
});
|
|
11616
|
+
return p;
|
|
11617
|
+
}
|
|
11618
|
+
async function runWorkflowInner(projectRoot, wf, opts) {
|
|
11325
11619
|
const now = opts.now || (() => {
|
|
11326
11620
|
const d = /* @__PURE__ */ new Date();
|
|
11327
11621
|
return { iso: d.toISOString(), ms: d.getTime() };
|
|
@@ -11570,6 +11864,7 @@ function computeSoftTurnCap(hardMax, cadence) {
|
|
|
11570
11864
|
const DEFAULT_LOOP_MAX_EXTENSIONS = 3;
|
|
11571
11865
|
const DEFAULT_LOOP_EXTENSION_TURNS = 25;
|
|
11572
11866
|
function resolveMaxExtensions(envValue) {
|
|
11867
|
+
if (envValue == null || envValue.trim() === "") return DEFAULT_LOOP_MAX_EXTENSIONS;
|
|
11573
11868
|
const n = Number(envValue);
|
|
11574
11869
|
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
11575
11870
|
return DEFAULT_LOOP_MAX_EXTENSIONS;
|
|
@@ -11623,6 +11918,7 @@ function resolveStuckLimit(envValue) {
|
|
|
11623
11918
|
return DEFAULT_LOOP_STUCK_CHECKPOINTS;
|
|
11624
11919
|
}
|
|
11625
11920
|
function resolveMaxAutoResumes(envValue) {
|
|
11921
|
+
if (envValue == null || envValue.trim() === "") return DEFAULT_LOOP_MAX_AUTORESUMES;
|
|
11626
11922
|
const n = Number(envValue);
|
|
11627
11923
|
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
11628
11924
|
return DEFAULT_LOOP_MAX_AUTORESUMES;
|
|
@@ -13558,7 +13854,7 @@ async function startDaemon(options) {
|
|
|
13558
13854
|
try {
|
|
13559
13855
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
13560
13856
|
if (!dir) return;
|
|
13561
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
13857
|
+
const { reconcileServiceLinks } = await import('./agentCommands-BSv2sDdC.mjs');
|
|
13562
13858
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
13563
13859
|
const config = readSvampConfig(configPath);
|
|
13564
13860
|
const entries = Array.from(urls.entries());
|
|
@@ -13576,7 +13872,7 @@ async function startDaemon(options) {
|
|
|
13576
13872
|
}
|
|
13577
13873
|
}
|
|
13578
13874
|
async function createExposedTunnel(spec) {
|
|
13579
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
13875
|
+
const { FrpcTunnel } = await import('./frpc-k3ZLz2Mu.mjs');
|
|
13580
13876
|
const tunnel = new FrpcTunnel({
|
|
13581
13877
|
name: spec.name,
|
|
13582
13878
|
ports: spec.ports,
|
|
@@ -13597,14 +13893,14 @@ async function startDaemon(options) {
|
|
|
13597
13893
|
}
|
|
13598
13894
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
13599
13895
|
const tunnelRecreateInFlight = /* @__PURE__ */ new Set();
|
|
13600
|
-
const { ServeManager } = await import('./serveManager-
|
|
13896
|
+
const { ServeManager } = await import('./serveManager-BL3aySTV.mjs');
|
|
13601
13897
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
13602
13898
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
13603
13899
|
});
|
|
13604
13900
|
ensureAutoInstalledCommands(logger);
|
|
13605
13901
|
(async () => {
|
|
13606
13902
|
try {
|
|
13607
|
-
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-
|
|
13903
|
+
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-B9O-hKxm.mjs');
|
|
13608
13904
|
beginClaudeVersionReconcile((msg) => logger.log(msg));
|
|
13609
13905
|
} catch (e) {
|
|
13610
13906
|
logger.log(`[claude-version] check failed: ${e?.message || e}`);
|
|
@@ -13843,7 +14139,7 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
13843
14139
|
}
|
|
13844
14140
|
const sessionId = options2.sessionId || composeSessionId(generateFriendlyName(collectKnownFriendlyNames()), collectKnownSessionIds());
|
|
13845
14141
|
const agentName = options2.agent || agentConfig.agent_type || "claude";
|
|
13846
|
-
if (agentName !== "claude" && (KNOWN_ACP_AGENTS[agentName] ||
|
|
14142
|
+
if (agentName !== "claude" && (KNOWN_ACP_AGENTS[agentName] || KNOWN_CODEX_AGENTS[agentName])) {
|
|
13847
14143
|
return await spawnAgentSession(sessionId, directory, agentName, options2, resumeSessionId);
|
|
13848
14144
|
}
|
|
13849
14145
|
let stagedCredentials = null;
|
|
@@ -15713,11 +16009,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15713
16009
|
});
|
|
15714
16010
|
},
|
|
15715
16011
|
onIssue: async (params) => {
|
|
15716
|
-
const { issueRpc } = await import('./rpc-
|
|
16012
|
+
const { issueRpc } = await import('./rpc-DlioTx05.mjs');
|
|
15717
16013
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
15718
16014
|
},
|
|
15719
16015
|
onWorkflow: async (params) => {
|
|
15720
|
-
const { workflowRpc } = await import('./rpc-
|
|
16016
|
+
const { workflowRpc } = await import('./rpc-BXrKU30m.mjs');
|
|
15721
16017
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
15722
16018
|
},
|
|
15723
16019
|
onRipgrep: async (args, cwd) => {
|
|
@@ -16266,11 +16562,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16266
16562
|
});
|
|
16267
16563
|
},
|
|
16268
16564
|
onIssue: async (params) => {
|
|
16269
|
-
const { issueRpc } = await import('./rpc-
|
|
16565
|
+
const { issueRpc } = await import('./rpc-DlioTx05.mjs');
|
|
16270
16566
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
16271
16567
|
},
|
|
16272
16568
|
onWorkflow: async (params) => {
|
|
16273
|
-
const { workflowRpc } = await import('./rpc-
|
|
16569
|
+
const { workflowRpc } = await import('./rpc-BXrKU30m.mjs');
|
|
16274
16570
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
16275
16571
|
},
|
|
16276
16572
|
onRipgrep: async (args, cwd) => {
|
|
@@ -16417,9 +16713,17 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16417
16713
|
}
|
|
16418
16714
|
}
|
|
16419
16715
|
let agentBackend;
|
|
16420
|
-
if (
|
|
16421
|
-
|
|
16716
|
+
if (KNOWN_CODEX_AGENTS[agentName]) {
|
|
16717
|
+
const avail = codexAppServerAvailable();
|
|
16718
|
+
if (avail === null) throw new Error("Codex CLI not found \u2014 install it (`npm i -g @openai/codex`) and authenticate (`svamp daemon codex-auth`).");
|
|
16719
|
+
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`).");
|
|
16720
|
+
const provider = resolveCodexProvider();
|
|
16721
|
+
const codexModel = options2.model || agentConfig?.default_model || provider.model || void 0;
|
|
16722
|
+
logger.log(`[Agent Session ${sessionId}] Codex backend: app-server (model=${codexModel ?? "default"}${provider.apiBase ? `, provider=${provider.apiBase}` : ""})`);
|
|
16723
|
+
agentBackend = new CodexAppServerBackend({
|
|
16422
16724
|
cwd: directory,
|
|
16725
|
+
model: codexModel,
|
|
16726
|
+
provider,
|
|
16423
16727
|
env: options2.environmentVariables,
|
|
16424
16728
|
log: logger.log,
|
|
16425
16729
|
isolationConfig: agentIsoConfig
|
|
@@ -16483,6 +16787,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16483
16787
|
return { ok: pending.length === 0, output: pending.length ? `${pending.length} pending: ${pending.map((i) => "#" + i.id).join(" ")}` : "No pending issues." };
|
|
16484
16788
|
};
|
|
16485
16789
|
try {
|
|
16790
|
+
if (!ls.acp_budget_warned && ls.budget && (ls.budget.max_tokens || ls.budget.max_tokens_per_hour)) {
|
|
16791
|
+
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");
|
|
16792
|
+
ls.acp_budget_warned = true;
|
|
16793
|
+
}
|
|
16486
16794
|
const acpMaxExt = resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS);
|
|
16487
16795
|
const acpEffMax = effectiveSoftCap(ls.max_iterations, ls.extensions, acpMaxExt);
|
|
16488
16796
|
const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: acpEffMax, ...ls.budget || {} }, now, startedAt);
|
|
@@ -16500,6 +16808,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
16500
16808
|
return;
|
|
16501
16809
|
}
|
|
16502
16810
|
const oracle = await runOracle();
|
|
16811
|
+
if (loopCancelledDuringVerify(readLoopState(directory, sessionId))) return;
|
|
16503
16812
|
if (!oracle.ok) {
|
|
16504
16813
|
writeGoalLoopState(directory, sessionId, { ...ls, active: true, phase: "continue", ledger });
|
|
16505
16814
|
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 +16830,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16521
16830
|
log: (m) => logger.log(`[Session ${sessionId}] ${m}`)
|
|
16522
16831
|
}
|
|
16523
16832
|
);
|
|
16833
|
+
if (loopCancelledDuringVerify(readLoopState(directory, sessionId))) return;
|
|
16524
16834
|
if (result.action === "rekick") {
|
|
16525
16835
|
writeGoalLoopState(directory, sessionId, { ...ls, active: true, phase: "continue", holds: holds + 1, started_at: startedAt, ledger });
|
|
16526
16836
|
const guidance = result.guidance.replace(/\s+/g, " ").slice(0, 1200);
|
|
@@ -16700,6 +17010,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16700
17010
|
}
|
|
16701
17011
|
if (!loopDir) loopDir = loadSessionIndex()[sessionId]?.directory;
|
|
16702
17012
|
const wasInMemory = teardownTrackedSession(sessionId);
|
|
17013
|
+
idleTriggerTracker.forget(sessionId);
|
|
16703
17014
|
const markedArchived = markSessionAsArchived(sessionId);
|
|
16704
17015
|
if (loopDir && isLoopActiveForSession(loopDir, sessionId)) {
|
|
16705
17016
|
deactivateLoop(loopDir, sessionId);
|
|
@@ -16757,6 +17068,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16757
17068
|
}
|
|
16758
17069
|
if (!loopDir) loopDir = loadSessionIndex()[sessionId]?.directory;
|
|
16759
17070
|
teardownTrackedSession(sessionId);
|
|
17071
|
+
idleTriggerTracker.forget(sessionId);
|
|
16760
17072
|
deletePersistedSession(sessionId);
|
|
16761
17073
|
if (loopDir && isLoopActiveForSession(loopDir, sessionId)) {
|
|
16762
17074
|
deactivateLoop(loopDir, sessionId);
|
|
@@ -17038,7 +17350,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
17038
17350
|
}
|
|
17039
17351
|
if (persistedSessions.length > 0) {
|
|
17040
17352
|
try {
|
|
17041
|
-
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-
|
|
17353
|
+
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-B9O-hKxm.mjs');
|
|
17042
17354
|
await awaitClaudeVersionReady();
|
|
17043
17355
|
} catch {
|
|
17044
17356
|
}
|
|
@@ -17235,7 +17547,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
17235
17547
|
const PING_TIMEOUT_MS = 15e3;
|
|
17236
17548
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
17237
17549
|
const RECONNECT_JITTER_MS = 2500;
|
|
17238
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
17550
|
+
const { WorkflowScheduler } = await import('./scheduler-B9WW7XGk.mjs');
|
|
17239
17551
|
const workflowScheduler = new WorkflowScheduler({
|
|
17240
17552
|
projectRoots: () => {
|
|
17241
17553
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -17846,4 +18158,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
17846
18158
|
writeStopMarker: writeStopMarker
|
|
17847
18159
|
});
|
|
17848
18160
|
|
|
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,
|
|
18161
|
+
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 };
|