@schoolai/shipyard 3.11.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth-GGM253LQ.js → auth-AUY74PMB.js} +3 -3
- package/dist/capability-detector-worker.js +8 -8
- package/dist/{chunk-R3XQ6W7L.js → chunk-4SYLDZTY.js} +4 -4
- package/dist/{chunk-C6QOTETH.js → chunk-5LWD5W7O.js} +24 -10
- package/dist/chunk-5LWD5W7O.js.map +1 -0
- package/dist/{chunk-IJHF4OM4.js → chunk-5W5N5U2S.js} +2 -2
- package/dist/{chunk-L7ELOV3S.js → chunk-FVZ5BDZS.js} +4 -4
- package/dist/chunk-FVZ5BDZS.js.map +1 -0
- package/dist/{chunk-RW2OTTUA.js → chunk-KYLYGFMH.js} +4 -4
- package/dist/{chunk-QJP7JCIS.js → chunk-LRNGLC4V.js} +41 -3
- package/dist/chunk-LRNGLC4V.js.map +1 -0
- package/dist/{chunk-A2UK6TW2.js → chunk-LZSMNUAI.js} +18 -1
- package/dist/{chunk-A2UK6TW2.js.map → chunk-LZSMNUAI.js.map} +1 -1
- package/dist/{chunk-Z37T5W6S.js → chunk-P2HZDIN7.js} +12 -7
- package/dist/chunk-P2HZDIN7.js.map +1 -0
- package/dist/{chunk-ZRJTZLRF.js → chunk-QKJNVVQ3.js} +4 -4
- package/dist/{chunk-2EQOL57Z.js → chunk-TFRYQDDG.js} +2 -2
- package/dist/{chunk-YXPPZQBJ.js → chunk-VL5RUCRF.js} +164 -37
- package/dist/chunk-VL5RUCRF.js.map +1 -0
- package/dist/{chunk-3WEEGJJN.js → chunk-X5KCX6ZS.js} +2 -2
- package/dist/{chunk-GM6MH4CD.js → chunk-XIEOWUPV.js} +2 -2
- package/dist/{chunk-6LINHACK.js → chunk-Y5UWRARP.js} +47 -24
- package/dist/chunk-Y5UWRARP.js.map +1 -0
- package/dist/cursor-runner.js +88 -62
- package/dist/cursor-runner.js.map +1 -1
- package/dist/electron-utility.js +5 -5
- package/dist/{git-repo-QNGPCJLI.js → git-repo-CTZJS3ER.js} +6 -4
- package/dist/index.js +8 -8
- package/dist/{logger-2F3CBS3V.js → logger-AN7EUK2B.js} +7 -5
- package/dist/{login-U256OVOJ.js → login-YB34LF4L.js} +6 -6
- package/dist/{logout-HY3MPOY5.js → logout-GUXVSWLZ.js} +5 -5
- package/dist/{mcp-servers-ICHOWXZB.js → mcp-servers-OAPQNDA7.js} +4 -4
- package/dist/{roi-YM5OOWHG.js → roi-NXJHL5X2.js} +3 -3
- package/dist/{serve-D5GKV2RU.js → serve-P3U2C5YH.js} +1175 -739
- package/dist/{serve-D5GKV2RU.js.map → serve-P3U2C5YH.js.map} +1 -1
- package/dist/{skills-W2Y6TWHA.js → skills-2UBVHFQ5.js} +2 -2
- package/dist/{start-JY26XC5R.js → start-Y34X3WVF.js} +10 -10
- package/package.json +1 -1
- package/dist/chunk-6LINHACK.js.map +0 -1
- package/dist/chunk-C6QOTETH.js.map +0 -1
- package/dist/chunk-L7ELOV3S.js.map +0 -1
- package/dist/chunk-QJP7JCIS.js.map +0 -1
- package/dist/chunk-YXPPZQBJ.js.map +0 -1
- package/dist/chunk-Z37T5W6S.js.map +0 -1
- /package/dist/{auth-GGM253LQ.js.map → auth-AUY74PMB.js.map} +0 -0
- /package/dist/{chunk-R3XQ6W7L.js.map → chunk-4SYLDZTY.js.map} +0 -0
- /package/dist/{chunk-IJHF4OM4.js.map → chunk-5W5N5U2S.js.map} +0 -0
- /package/dist/{chunk-RW2OTTUA.js.map → chunk-KYLYGFMH.js.map} +0 -0
- /package/dist/{chunk-ZRJTZLRF.js.map → chunk-QKJNVVQ3.js.map} +0 -0
- /package/dist/{chunk-2EQOL57Z.js.map → chunk-TFRYQDDG.js.map} +0 -0
- /package/dist/{chunk-3WEEGJJN.js.map → chunk-X5KCX6ZS.js.map} +0 -0
- /package/dist/{chunk-GM6MH4CD.js.map → chunk-XIEOWUPV.js.map} +0 -0
- /package/dist/{git-repo-QNGPCJLI.js.map → git-repo-CTZJS3ER.js.map} +0 -0
- /package/dist/{logger-2F3CBS3V.js.map → logger-AN7EUK2B.js.map} +0 -0
- /package/dist/{login-U256OVOJ.js.map → login-YB34LF4L.js.map} +0 -0
- /package/dist/{logout-HY3MPOY5.js.map → logout-GUXVSWLZ.js.map} +0 -0
- /package/dist/{mcp-servers-ICHOWXZB.js.map → mcp-servers-OAPQNDA7.js.map} +0 -0
- /package/dist/{roi-YM5OOWHG.js.map → roi-NXJHL5X2.js.map} +0 -0
- /package/dist/{skills-W2Y6TWHA.js.map → skills-2UBVHFQ5.js.map} +0 -0
- /package/dist/{start-JY26XC5R.js.map → start-Y34X3WVF.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
guardedSubscribe,
|
|
13
13
|
makeInitialAttemptState,
|
|
14
14
|
shutdownFileWatcherGuard
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-FVZ5BDZS.js";
|
|
16
16
|
import {
|
|
17
17
|
TRAILER_SCHEMA_VERSION,
|
|
18
18
|
appendTrailerToMessage,
|
|
@@ -25,10 +25,10 @@ import {
|
|
|
25
25
|
} from "./chunk-WN5Q6WBU.js";
|
|
26
26
|
import {
|
|
27
27
|
loadAuthToken
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-X5KCX6ZS.js";
|
|
29
29
|
import {
|
|
30
30
|
getDaemonVersion
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-QKJNVVQ3.js";
|
|
32
32
|
import {
|
|
33
33
|
getRecentWasmPanicMessages
|
|
34
34
|
} from "./chunk-7H34LI75.js";
|
|
@@ -146,12 +146,12 @@ import {
|
|
|
146
146
|
translateWarningNotification,
|
|
147
147
|
unresolveReviewThread,
|
|
148
148
|
writeCursorApiKeyToVault
|
|
149
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-5LWD5W7O.js";
|
|
150
150
|
import {
|
|
151
151
|
buildNodeSpawnEnv,
|
|
152
152
|
nodeSpawnEnvOverlay,
|
|
153
153
|
rewriteElectronNodeScriptSpawn
|
|
154
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-XIEOWUPV.js";
|
|
155
155
|
import {
|
|
156
156
|
TIMEOUT_MS,
|
|
157
157
|
addWorktreeToCache,
|
|
@@ -164,7 +164,7 @@ import {
|
|
|
164
164
|
removeWorktreeFromCache,
|
|
165
165
|
run,
|
|
166
166
|
runWithTimeout
|
|
167
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-Y5UWRARP.js";
|
|
168
168
|
import {
|
|
169
169
|
gitExecSafe
|
|
170
170
|
} from "./chunk-4T2OQAVL.js";
|
|
@@ -182,7 +182,7 @@ import {
|
|
|
182
182
|
redactEnv,
|
|
183
183
|
resolveEnabledMcpServers,
|
|
184
184
|
resolveStdioEnv
|
|
185
|
-
} from "./chunk-
|
|
185
|
+
} from "./chunk-KYLYGFMH.js";
|
|
186
186
|
import {
|
|
187
187
|
atomicWriteFile,
|
|
188
188
|
findProjectRoot,
|
|
@@ -193,13 +193,13 @@ import {
|
|
|
193
193
|
} from "./chunk-RR6V6SNM.js";
|
|
194
194
|
import {
|
|
195
195
|
classifySkill
|
|
196
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-TFRYQDDG.js";
|
|
197
197
|
import {
|
|
198
198
|
createOneShotPool
|
|
199
199
|
} from "./chunk-ZFKJAYAN.js";
|
|
200
200
|
import {
|
|
201
201
|
parseRunnerToDaemon
|
|
202
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-LZSMNUAI.js";
|
|
203
203
|
import {
|
|
204
204
|
AGENT_IDS,
|
|
205
205
|
AGENT_SYSTEM_CLAUDE_CODE,
|
|
@@ -229,7 +229,7 @@ import {
|
|
|
229
229
|
COMMENT_ADD_TOOL,
|
|
230
230
|
COMMENT_LIST_TOOL,
|
|
231
231
|
COMMENT_READ_THREAD_TOOL,
|
|
232
|
-
|
|
232
|
+
COMPACTION_SILENCE_TIMEOUT_MS,
|
|
233
233
|
CREATE_TASK_TOOL,
|
|
234
234
|
CREDENTIAL_STORE_VERSION,
|
|
235
235
|
CanvasRepository,
|
|
@@ -270,6 +270,7 @@ import {
|
|
|
270
270
|
LSP_FIND_REFERENCES_TOOL,
|
|
271
271
|
LSP_GO_TO_DEFINITION_TOOL,
|
|
272
272
|
LSP_RENAME_SYMBOL_TOOL,
|
|
273
|
+
MERMAID_SECURITY_LEVEL,
|
|
273
274
|
Mark,
|
|
274
275
|
MarkType,
|
|
275
276
|
MarkdownParser,
|
|
@@ -399,7 +400,7 @@ import {
|
|
|
399
400
|
unsafeAssertSourceId,
|
|
400
401
|
vizFileExtension,
|
|
401
402
|
withPreferredRuntimeAuthMethod
|
|
402
|
-
} from "./chunk-
|
|
403
|
+
} from "./chunk-VL5RUCRF.js";
|
|
403
404
|
import {
|
|
404
405
|
AuthGitHubCallbackRequestSchema,
|
|
405
406
|
AuthGitHubCallbackResponseSchema,
|
|
@@ -455,7 +456,7 @@ import {
|
|
|
455
456
|
createChildLogger,
|
|
456
457
|
flushLogger,
|
|
457
458
|
logger
|
|
458
|
-
} from "./chunk-
|
|
459
|
+
} from "./chunk-LRNGLC4V.js";
|
|
459
460
|
import {
|
|
460
461
|
getShipyardHome,
|
|
461
462
|
getStallProfilerConfig,
|
|
@@ -463,7 +464,7 @@ import {
|
|
|
463
464
|
isVanillaAgentMode,
|
|
464
465
|
resolveNodeExecPath,
|
|
465
466
|
validateEnv
|
|
466
|
-
} from "./chunk-
|
|
467
|
+
} from "./chunk-P2HZDIN7.js";
|
|
467
468
|
import {
|
|
468
469
|
external_exports
|
|
469
470
|
} from "./chunk-CNR7O5YH.js";
|
|
@@ -472,7 +473,7 @@ import "./chunk-2H7UOFLK.js";
|
|
|
472
473
|
// src/services/serve.ts
|
|
473
474
|
import { mkdir as mkdir44, realpath as realpath3 } from "fs/promises";
|
|
474
475
|
import { homedir as homedir14 } from "os";
|
|
475
|
-
import { join as
|
|
476
|
+
import { join as join87 } from "path";
|
|
476
477
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
477
478
|
|
|
478
479
|
// ../../node_modules/.pnpm/@logtape+logtape@1.3.7/node_modules/@logtape/logtape/dist/level.js
|
|
@@ -10130,11 +10131,11 @@ async function runCapabilityDetectorWithFallback(opts) {
|
|
|
10130
10131
|
);
|
|
10131
10132
|
}
|
|
10132
10133
|
const cleanupGraceMs = opts.cleanupGraceMs ?? CAPABILITY_DETECTOR_WORKER_CLEANUP_GRACE_MS;
|
|
10133
|
-
const
|
|
10134
|
+
const spawn16 = opts.spawn ?? defaultSpawn;
|
|
10134
10135
|
const log = opts.log ?? (() => {
|
|
10135
10136
|
});
|
|
10136
10137
|
const startedAt = Date.now();
|
|
10137
|
-
const worker =
|
|
10138
|
+
const worker = spawn16();
|
|
10138
10139
|
worker.unref?.();
|
|
10139
10140
|
return new Promise((resolve10) => {
|
|
10140
10141
|
let resultSettled = false;
|
|
@@ -13134,7 +13135,7 @@ function nanoid(size2 = 21) {
|
|
|
13134
13135
|
}
|
|
13135
13136
|
|
|
13136
13137
|
// src/services/bootstrap/signaling.ts
|
|
13137
|
-
var DAEMON_NPM_VERSION = true ? "3.11.
|
|
13138
|
+
var DAEMON_NPM_VERSION = true ? "3.11.1" : "unknown";
|
|
13138
13139
|
function createDaemonSignaling(config) {
|
|
13139
13140
|
const agentId = config.agentId ?? nanoid();
|
|
13140
13141
|
function send(msg) {
|
|
@@ -14307,7 +14308,7 @@ function runTaskSearch(args) {
|
|
|
14307
14308
|
}
|
|
14308
14309
|
|
|
14309
14310
|
// src/services/channels/control-channel-wiring.ts
|
|
14310
|
-
import { join as
|
|
14311
|
+
import { join as join35 } from "path";
|
|
14311
14312
|
|
|
14312
14313
|
// src/shared/type-utils.ts
|
|
14313
14314
|
function narrow(value) {
|
|
@@ -16561,10 +16562,10 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16561
16562
|
respondError2(requestId, formatError(err3));
|
|
16562
16563
|
}
|
|
16563
16564
|
}
|
|
16564
|
-
const
|
|
16565
|
+
const execFileAsync4 = promisify4(execFile4);
|
|
16565
16566
|
async function handleGitStatus(requestId) {
|
|
16566
16567
|
try {
|
|
16567
|
-
const { stdout } = await
|
|
16568
|
+
const { stdout } = await execFileAsync4("git", ["status", "--porcelain=v1", "-z", "-u"], {
|
|
16568
16569
|
cwd,
|
|
16569
16570
|
maxBuffer: 5 * 1024 * 1024
|
|
16570
16571
|
});
|
|
@@ -16646,7 +16647,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16646
16647
|
});
|
|
16647
16648
|
return;
|
|
16648
16649
|
}
|
|
16649
|
-
const { stdout } = await
|
|
16650
|
+
const { stdout } = await execFileAsync4(
|
|
16650
16651
|
"git",
|
|
16651
16652
|
["diff", "--name-status", `${mergeBase}..HEAD`],
|
|
16652
16653
|
{ cwd, maxBuffer: 10 * 1024 * 1024 }
|
|
@@ -16736,7 +16737,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16736
16737
|
}
|
|
16737
16738
|
async function handleGitStageFile(requestId, safeRelPath) {
|
|
16738
16739
|
try {
|
|
16739
|
-
await
|
|
16740
|
+
await execFileAsync4("git", ["add", "--", safeRelPath], { cwd });
|
|
16740
16741
|
respond({ type: "git_stage_result", requestId });
|
|
16741
16742
|
} catch (err3) {
|
|
16742
16743
|
respondError2(requestId, formatError(err3));
|
|
@@ -16744,7 +16745,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16744
16745
|
}
|
|
16745
16746
|
async function handleGitUnstageFile(requestId, safeRelPath) {
|
|
16746
16747
|
try {
|
|
16747
|
-
await
|
|
16748
|
+
await execFileAsync4("git", ["restore", "--staged", "--", safeRelPath], { cwd });
|
|
16748
16749
|
respond({ type: "git_unstage_result", requestId });
|
|
16749
16750
|
} catch (err3) {
|
|
16750
16751
|
respondError2(requestId, formatError(err3));
|
|
@@ -16754,7 +16755,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16754
16755
|
try {
|
|
16755
16756
|
const indexContent = await readGitObject(`:${safeRelPath}`);
|
|
16756
16757
|
if (indexContent === null) {
|
|
16757
|
-
await
|
|
16758
|
+
await execFileAsync4("git", ["add", "--", safeRelPath], { cwd });
|
|
16758
16759
|
respond({ type: "git_stage_result", requestId });
|
|
16759
16760
|
return;
|
|
16760
16761
|
}
|
|
@@ -16789,12 +16790,12 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16789
16790
|
const relPath = relative(cwd, absPath);
|
|
16790
16791
|
if (!await validateMutationTarget(requestId, relPath, absPath)) return;
|
|
16791
16792
|
try {
|
|
16792
|
-
await
|
|
16793
|
+
await execFileAsync4("git", ["checkout", "--", relPath], { cwd });
|
|
16793
16794
|
respond({ type: "git_discard_file_result", requestId });
|
|
16794
16795
|
} catch {
|
|
16795
16796
|
try {
|
|
16796
16797
|
await unlink5(absPath);
|
|
16797
|
-
await
|
|
16798
|
+
await execFileAsync4("git", ["reset", "HEAD", "--", relPath], { cwd }).catch(
|
|
16798
16799
|
(resetErr) => {
|
|
16799
16800
|
log({ event: "git_reset_head_failed", path: relPath, error: formatError(resetErr) });
|
|
16800
16801
|
}
|
|
@@ -16807,7 +16808,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16807
16808
|
}
|
|
16808
16809
|
async function readGitObject(ref) {
|
|
16809
16810
|
try {
|
|
16810
|
-
const { stdout } = await
|
|
16811
|
+
const { stdout } = await execFileAsync4("git", ["show", ref], {
|
|
16811
16812
|
cwd,
|
|
16812
16813
|
maxBuffer: 10 * 1024 * 1024
|
|
16813
16814
|
});
|
|
@@ -16818,7 +16819,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16818
16819
|
}
|
|
16819
16820
|
async function getFileMode(safeRelPath) {
|
|
16820
16821
|
try {
|
|
16821
|
-
const { stdout } = await
|
|
16822
|
+
const { stdout } = await execFileAsync4("git", ["ls-files", "-s", "--", safeRelPath], { cwd });
|
|
16822
16823
|
const match = /^(\d+)\s/.exec(stdout);
|
|
16823
16824
|
if (match?.[1]) return match[1];
|
|
16824
16825
|
} catch {
|
|
@@ -16830,7 +16831,7 @@ function handleFileIOChannel(initialCwd, send, log, deps) {
|
|
|
16830
16831
|
const mode = isNew ? "100644" : await getFileMode(safeRelPath);
|
|
16831
16832
|
const args = ["update-index", "--cacheinfo", `${mode},${blobHash},${safeRelPath}`];
|
|
16832
16833
|
if (isNew) args.splice(1, 0, "--add");
|
|
16833
|
-
await
|
|
16834
|
+
await execFileAsync4("git", args, { cwd });
|
|
16834
16835
|
}
|
|
16835
16836
|
function gitHashObject(content, pathHint) {
|
|
16836
16837
|
return new Promise((resolve10, reject) => {
|
|
@@ -17960,7 +17961,7 @@ body { min-height: 100vh; display: flex; align-items: center; justify-content: c
|
|
|
17960
17961
|
<script type="module">
|
|
17961
17962
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
17962
17963
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
17963
|
-
mermaid.initialize({ startOnLoad: true, theme: prefersDark ? 'dark' : 'default', securityLevel: '
|
|
17964
|
+
mermaid.initialize({ startOnLoad: true, theme: prefersDark ? 'dark' : 'default', securityLevel: '${MERMAID_SECURITY_LEVEL}' });
|
|
17964
17965
|
</script>
|
|
17965
17966
|
</body>
|
|
17966
17967
|
</html>
|
|
@@ -20399,6 +20400,13 @@ var TELEMETERED_EVENT_NAMES = [
|
|
|
20399
20400
|
"harness_mcp_registration_failed",
|
|
20400
20401
|
"codex_auth_divergence_detected",
|
|
20401
20402
|
"cursor_auth_divergence_detected",
|
|
20403
|
+
/**
|
|
20404
|
+
* A Codex remote-compact cycle failed with a 401 "Missing bearer" error —
|
|
20405
|
+
* the app-server lost its in-memory token during refresh-token rotation.
|
|
20406
|
+
* Emitted once per compact attempt (not per retry) so fleet dashboards can
|
|
20407
|
+
* surface the storm rate without log amplification.
|
|
20408
|
+
*/
|
|
20409
|
+
"codex_compact_auth_failure",
|
|
20402
20410
|
"event_loop_stall",
|
|
20403
20411
|
"machine_sleep_artifact",
|
|
20404
20412
|
/**
|
|
@@ -20410,7 +20418,16 @@ var TELEMETERED_EVENT_NAMES = [
|
|
|
20410
20418
|
*/
|
|
20411
20419
|
"electron_app_metrics",
|
|
20412
20420
|
/** Electron main cleared a wedged Squirrel.Mac staged install on launch. */
|
|
20413
|
-
"squirrel_install_stuck_recovered"
|
|
20421
|
+
"squirrel_install_stuck_recovered",
|
|
20422
|
+
/**
|
|
20423
|
+
* An ALO control-channel entry exhausted its maxDeliver retry budget
|
|
20424
|
+
* without receiving an ack. Payload: { attempts: number, channel: 'control' }.
|
|
20425
|
+
* Emitted via the `onDeadLetter` callback in control-channel-wiring.ts.
|
|
20426
|
+
* Complements the `alo_resend_exhausted` ERROR log event emitted by the
|
|
20427
|
+
* shell itself; this telemetry path routes it to D1 for fleet-wide
|
|
20428
|
+
* observability so silent resend exhaustion is detectable in aggregate.
|
|
20429
|
+
*/
|
|
20430
|
+
"alo_resend_exhausted"
|
|
20414
20431
|
];
|
|
20415
20432
|
var TELEMETERED_EVENTS = new Set(TELEMETERED_EVENT_NAMES);
|
|
20416
20433
|
var sink = null;
|
|
@@ -22428,8 +22445,54 @@ function withListWorktreeContext(context) {
|
|
|
22428
22445
|
...context?.reason !== void 0 && { reason: context.reason }
|
|
22429
22446
|
};
|
|
22430
22447
|
}
|
|
22448
|
+
function logWorktreePollDegraded(logAdapter, repoPath, err3, requestContext) {
|
|
22449
|
+
const repoExists = existsSync2(repoPath);
|
|
22450
|
+
if (err3 instanceof GitExecError) {
|
|
22451
|
+
const { code, signal, killed, stderr, durationMs } = err3.info;
|
|
22452
|
+
logAdapter({
|
|
22453
|
+
event: "worktree_poll_degraded",
|
|
22454
|
+
repoPath,
|
|
22455
|
+
code,
|
|
22456
|
+
signal,
|
|
22457
|
+
killed,
|
|
22458
|
+
stderr,
|
|
22459
|
+
durationMs,
|
|
22460
|
+
repoExists,
|
|
22461
|
+
permanent: !repoExists,
|
|
22462
|
+
...requestContext
|
|
22463
|
+
});
|
|
22464
|
+
return;
|
|
22465
|
+
}
|
|
22466
|
+
const error = err3 instanceof Error ? err3.message : String(err3);
|
|
22467
|
+
logAdapter({
|
|
22468
|
+
event: "worktree_poll_degraded",
|
|
22469
|
+
repoPath,
|
|
22470
|
+
error,
|
|
22471
|
+
repoExists,
|
|
22472
|
+
permanent: !repoExists,
|
|
22473
|
+
...requestContext
|
|
22474
|
+
});
|
|
22475
|
+
}
|
|
22476
|
+
function handleWorktreeListError(err3, repoPath, degradedRepos, logAdapter, requestContext) {
|
|
22477
|
+
if (err3 instanceof CircuitOpenError) {
|
|
22478
|
+
if (!degradedRepos.has(repoPath)) {
|
|
22479
|
+
degradedRepos.add(repoPath);
|
|
22480
|
+
logAdapter({ event: "worktree_poll_degraded", repoPath, reason: "circuit_open" });
|
|
22481
|
+
}
|
|
22482
|
+
return true;
|
|
22483
|
+
}
|
|
22484
|
+
if (err3 instanceof BackoffWaitError) {
|
|
22485
|
+
return true;
|
|
22486
|
+
}
|
|
22487
|
+
if (!degradedRepos.has(repoPath)) {
|
|
22488
|
+
degradedRepos.add(repoPath);
|
|
22489
|
+
logWorktreePollDegraded(logAdapter, repoPath, err3, requestContext);
|
|
22490
|
+
}
|
|
22491
|
+
return false;
|
|
22492
|
+
}
|
|
22431
22493
|
function buildWorktreeHandlers(deps) {
|
|
22432
22494
|
const { daemon, logAdapter, worktreeRunner } = deps;
|
|
22495
|
+
const degradedRepos = /* @__PURE__ */ new Set();
|
|
22433
22496
|
return {
|
|
22434
22497
|
onCreateWorktree: (requestId, sourceRepoPath, branchName, baseRef, setupScript, mode) => {
|
|
22435
22498
|
logAdapter({ event: "worktree_create_requested", requestId, sourceRepoPath, branchName });
|
|
@@ -22458,54 +22521,26 @@ function buildWorktreeHandlers(deps) {
|
|
|
22458
22521
|
const requestContext = withListWorktreeContext(context);
|
|
22459
22522
|
logAdapter({ event: "worktree_list_requested", repoPath, ...requestContext });
|
|
22460
22523
|
listWorktrees2(repoPath).then((worktrees) => {
|
|
22524
|
+
if (degradedRepos.has(repoPath)) {
|
|
22525
|
+
degradedRepos.delete(repoPath);
|
|
22526
|
+
logAdapter({ event: "worktree_poll_recovered", repoPath });
|
|
22527
|
+
}
|
|
22461
22528
|
deps.controlHandler().sendControl({ type: "worktree_list", repoPath, worktrees });
|
|
22462
22529
|
}).catch((err3) => {
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22475
|
-
waitMs: err3.waitMs,
|
|
22476
|
-
...requestContext
|
|
22477
|
-
});
|
|
22478
|
-
return;
|
|
22479
|
-
}
|
|
22480
|
-
const msg = err3 instanceof Error ? err3.message : String(err3);
|
|
22481
|
-
const repoExists = existsSync2(repoPath);
|
|
22482
|
-
if (err3 instanceof GitExecError) {
|
|
22483
|
-
const { code, signal, killed, stderr, durationMs } = err3.info;
|
|
22484
|
-
logAdapter({
|
|
22485
|
-
event: "worktree_list_failed",
|
|
22486
|
-
repoPath,
|
|
22487
|
-
error: msg,
|
|
22488
|
-
code,
|
|
22489
|
-
signal,
|
|
22490
|
-
killed,
|
|
22491
|
-
stderr,
|
|
22492
|
-
durationMs,
|
|
22493
|
-
repoExists,
|
|
22494
|
-
...requestContext
|
|
22495
|
-
});
|
|
22496
|
-
} else {
|
|
22497
|
-
logAdapter({
|
|
22498
|
-
event: "worktree_list_failed",
|
|
22499
|
-
repoPath,
|
|
22500
|
-
error: msg,
|
|
22501
|
-
repoExists,
|
|
22502
|
-
...requestContext
|
|
22530
|
+
const suppress = handleWorktreeListError(
|
|
22531
|
+
err3,
|
|
22532
|
+
repoPath,
|
|
22533
|
+
degradedRepos,
|
|
22534
|
+
logAdapter,
|
|
22535
|
+
requestContext
|
|
22536
|
+
);
|
|
22537
|
+
if (!suppress) {
|
|
22538
|
+
const msg = err3 instanceof Error ? err3.message : String(err3);
|
|
22539
|
+
deps.controlHandler().sendControl({
|
|
22540
|
+
type: "error",
|
|
22541
|
+
error: `Failed to list worktrees: ${msg}`
|
|
22503
22542
|
});
|
|
22504
22543
|
}
|
|
22505
|
-
deps.controlHandler().sendControl({
|
|
22506
|
-
type: "error",
|
|
22507
|
-
error: `Failed to list worktrees: ${msg}`
|
|
22508
|
-
});
|
|
22509
22544
|
});
|
|
22510
22545
|
},
|
|
22511
22546
|
onRemoveWorktree: (worktreePath) => {
|
|
@@ -23188,7 +23223,7 @@ async function refreshPluginCapabilities(daemon, tokenStore) {
|
|
|
23188
23223
|
const userSettings = await daemon.userSettingsStore.getSettings();
|
|
23189
23224
|
const [updatedMarketplace, updatedMcp, updatedSkills] = await Promise.all([
|
|
23190
23225
|
detectMarketplacePlugins(daemon.capabilities.marketplacePlugins),
|
|
23191
|
-
import("./mcp-servers-
|
|
23226
|
+
import("./mcp-servers-OAPQNDA7.js").then(
|
|
23192
23227
|
(m2) => m2.detectMCPServers(
|
|
23193
23228
|
daemon.capabilities.environments,
|
|
23194
23229
|
tokenStore,
|
|
@@ -23197,7 +23232,7 @@ async function refreshPluginCapabilities(daemon, tokenStore) {
|
|
|
23197
23232
|
readPreferredClaudeAuthMethod(userSettings)
|
|
23198
23233
|
)
|
|
23199
23234
|
),
|
|
23200
|
-
import("./skills-
|
|
23235
|
+
import("./skills-2UBVHFQ5.js").then(
|
|
23201
23236
|
(m2) => m2.detectSkills(daemon.capabilities.environments)
|
|
23202
23237
|
)
|
|
23203
23238
|
]);
|
|
@@ -25666,7 +25701,7 @@ function spawnCodexLogout(callbacks) {
|
|
|
25666
25701
|
|
|
25667
25702
|
// src/services/serve-factory-helpers.ts
|
|
25668
25703
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
25669
|
-
import { join as
|
|
25704
|
+
import { join as join29 } from "path";
|
|
25670
25705
|
|
|
25671
25706
|
// src/shared/capabilities/runtime/rate-limit-account-key.ts
|
|
25672
25707
|
function providerForProfile(profile) {
|
|
@@ -27337,6 +27372,19 @@ function matchCollabQueueCorrelationIds(messages, queueEntries) {
|
|
|
27337
27372
|
return claimedIds;
|
|
27338
27373
|
}
|
|
27339
27374
|
|
|
27375
|
+
// src/services/session/codex-home-isolation.ts
|
|
27376
|
+
import { join as join27 } from "path";
|
|
27377
|
+
var ISOLATED_CODEX_HOME_DIR = "codex-isolated";
|
|
27378
|
+
function getIsolatedCodexHome() {
|
|
27379
|
+
return join27(getShipyardHome(), ISOLATED_CODEX_HOME_DIR);
|
|
27380
|
+
}
|
|
27381
|
+
function buildAgentSubprocessCodexHomeEnv() {
|
|
27382
|
+
return { CODEX_HOME: getIsolatedCodexHome() };
|
|
27383
|
+
}
|
|
27384
|
+
function buildCodexShellCodexHomeConfigOverride() {
|
|
27385
|
+
return { "shell_environment_policy.set.CODEX_HOME": getIsolatedCodexHome() };
|
|
27386
|
+
}
|
|
27387
|
+
|
|
27340
27388
|
// src/services/session/mcp-settle.ts
|
|
27341
27389
|
var STATUSES_STILL_STARTING = /* @__PURE__ */ new Set(["starting", "pending", "connecting"]);
|
|
27342
27390
|
async function waitForMcpServersToSettle(opts) {
|
|
@@ -27687,6 +27735,13 @@ var AnthropicAgentSubprocess = class _AnthropicAgentSubprocess {
|
|
|
27687
27735
|
#onEvent;
|
|
27688
27736
|
#spawnMcpServers;
|
|
27689
27737
|
#harnessTaskIdSetter = null;
|
|
27738
|
+
/**
|
|
27739
|
+
* The epoch bound to this subprocess's in-process harness server at spawn
|
|
27740
|
+
* time (installed by serve-factory immediately after `setHarnessEpoch`).
|
|
27741
|
+
* Surfaced via the `harnessEpoch` getter so the pre-warm adoption handshake
|
|
27742
|
+
* writes the SAME epoch to the registry that the harness fence checks.
|
|
27743
|
+
*/
|
|
27744
|
+
#harnessEpoch = null;
|
|
27690
27745
|
/**
|
|
27691
27746
|
* Mutable slot for the CwdChanged handler. The SDK hook is registered
|
|
27692
27747
|
* unconditionally at spawn time and reads through this slot on each
|
|
@@ -27745,6 +27800,18 @@ var AnthropicAgentSubprocess = class _AnthropicAgentSubprocess {
|
|
|
27745
27800
|
installHarnessTaskIdSetter(setter) {
|
|
27746
27801
|
this.#harnessTaskIdSetter = setter;
|
|
27747
27802
|
}
|
|
27803
|
+
/**
|
|
27804
|
+
* Record the epoch serve-factory minted and bound to this subprocess's
|
|
27805
|
+
* in-process harness server (called right after `setHarnessEpoch`). The
|
|
27806
|
+
* pre-warm manager reads it back via `harnessEpoch` so adoption registers
|
|
27807
|
+
* the same epoch the fence will check — never a second, divergent UUID.
|
|
27808
|
+
*/
|
|
27809
|
+
installHarnessEpoch(epoch) {
|
|
27810
|
+
this.#harnessEpoch = epoch;
|
|
27811
|
+
}
|
|
27812
|
+
get harnessEpoch() {
|
|
27813
|
+
return this.#harnessEpoch;
|
|
27814
|
+
}
|
|
27748
27815
|
setHarnessTaskId(taskId) {
|
|
27749
27816
|
if (!this.#harnessTaskIdSetter) {
|
|
27750
27817
|
throw new Error("setHarnessTaskId called before installHarnessTaskIdSetter");
|
|
@@ -27903,7 +27970,8 @@ var AnthropicAgentSubprocess = class _AnthropicAgentSubprocess {
|
|
|
27903
27970
|
...process.env,
|
|
27904
27971
|
...options.env,
|
|
27905
27972
|
DISABLE_AUTO_COMPACT: "1",
|
|
27906
|
-
CLAUDE_CODE_ENTRYPOINT: "shipyard"
|
|
27973
|
+
CLAUDE_CODE_ENTRYPOINT: "shipyard",
|
|
27974
|
+
...buildAgentSubprocessCodexHomeEnv()
|
|
27907
27975
|
},
|
|
27908
27976
|
disallowedTools: options.disallowedTools,
|
|
27909
27977
|
skills: options.skills,
|
|
@@ -28389,7 +28457,7 @@ import { homedir as homedir4 } from "os";
|
|
|
28389
28457
|
|
|
28390
28458
|
// src/shared/capabilities/capabilities-cache.ts
|
|
28391
28459
|
import { mkdir as mkdir13, readFile as readFile18, rename as rename11, unlink as unlink8, writeFile as writeFile14 } from "fs/promises";
|
|
28392
|
-
import { dirname as dirname15, join as
|
|
28460
|
+
import { dirname as dirname15, join as join28 } from "path";
|
|
28393
28461
|
var CAPABILITIES_CACHE_VERSION = 1;
|
|
28394
28462
|
var GitRepoInfoCacheSchema = external_exports.object({
|
|
28395
28463
|
path: external_exports.string(),
|
|
@@ -28423,7 +28491,7 @@ var CacheFileSchema = external_exports.object({
|
|
|
28423
28491
|
mcpServers: external_exports.array(MCPServerInfoCacheSchema)
|
|
28424
28492
|
});
|
|
28425
28493
|
function cacheFilePath() {
|
|
28426
|
-
return
|
|
28494
|
+
return join28(getShipyardHome(), "data", "capabilities-cache.json");
|
|
28427
28495
|
}
|
|
28428
28496
|
var EMPTY_ENVIRONMENTS = [];
|
|
28429
28497
|
var EMPTY_MCP_SERVERS = [];
|
|
@@ -28689,7 +28757,7 @@ function appendDynamicPrompt(base2, appendix) {
|
|
|
28689
28757
|
${appendix}` : base2;
|
|
28690
28758
|
}
|
|
28691
28759
|
function rehydrateVizRegistry(registry, vizWatcher, resolvedTaskId, vizDir, log) {
|
|
28692
|
-
const registryPath =
|
|
28760
|
+
const registryPath = join29(vizDir, resolvedTaskId, "registry.json");
|
|
28693
28761
|
if (!existsSync3(registryPath)) return Promise.resolve();
|
|
28694
28762
|
try {
|
|
28695
28763
|
const raw = readFileSync4(registryPath, "utf-8");
|
|
@@ -28926,7 +28994,7 @@ async function initializeVaultSync(vaultKey, deps, credentialsVaultStore, oauthS
|
|
|
28926
28994
|
});
|
|
28927
28995
|
}
|
|
28928
28996
|
function buildDefaultRepo(dataDir, identity) {
|
|
28929
|
-
const storage = new FileStorageAdapter(
|
|
28997
|
+
const storage = new FileStorageAdapter(join29(dataDir, "loro"));
|
|
28930
28998
|
return {
|
|
28931
28999
|
repo: new Repo({
|
|
28932
29000
|
identity,
|
|
@@ -29168,6 +29236,9 @@ function buildRejectionStubSubprocess(input) {
|
|
|
29168
29236
|
stopBackgroundTask: noop3,
|
|
29169
29237
|
setHarnessTaskId() {
|
|
29170
29238
|
},
|
|
29239
|
+
get harnessEpoch() {
|
|
29240
|
+
return null;
|
|
29241
|
+
},
|
|
29171
29242
|
setOnCwdChanged() {
|
|
29172
29243
|
},
|
|
29173
29244
|
async rollback(_numTurns) {
|
|
@@ -29712,12 +29783,12 @@ function createBestEffortSender(deps) {
|
|
|
29712
29783
|
|
|
29713
29784
|
// src/services/comments/github-ingest-pipeline.ts
|
|
29714
29785
|
import { readFile as readFile20 } from "fs/promises";
|
|
29715
|
-
import { join as
|
|
29786
|
+
import { join as join31 } from "path";
|
|
29716
29787
|
|
|
29717
29788
|
// src/services/harness/comment-server.ts
|
|
29718
29789
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
29719
29790
|
import { access as access4, readFile as readFile19 } from "fs/promises";
|
|
29720
|
-
import { join as
|
|
29791
|
+
import { join as join30, relative as relative5 } from "path";
|
|
29721
29792
|
|
|
29722
29793
|
// src/shared/xml-utils.ts
|
|
29723
29794
|
function escapeXmlAttr(text) {
|
|
@@ -30606,7 +30677,7 @@ async function resolveDiffScopeForFile(cwd, normalizedPath) {
|
|
|
30606
30677
|
}
|
|
30607
30678
|
async function handleDiffComment(ctx, filePath, lineNumber, comment) {
|
|
30608
30679
|
const normalizedPath = normalizeToWorkspaceRelative(filePath, ctx.environmentKey);
|
|
30609
|
-
const absolutePath =
|
|
30680
|
+
const absolutePath = join30(ctx.environmentKey, normalizedPath);
|
|
30610
30681
|
const fileExists2 = await access4(absolutePath).then(
|
|
30611
30682
|
() => true,
|
|
30612
30683
|
() => false
|
|
@@ -30893,7 +30964,7 @@ function prCommentToAnnotation(prComment, ctx) {
|
|
|
30893
30964
|
// src/services/comments/github-ingest-pipeline.ts
|
|
30894
30965
|
async function readLocalFileContent(cwd, path5) {
|
|
30895
30966
|
try {
|
|
30896
|
-
return await readFile20(
|
|
30967
|
+
return await readFile20(join31(cwd, path5), "utf-8");
|
|
30897
30968
|
} catch {
|
|
30898
30969
|
return getFileAtRef(cwd, path5, "HEAD");
|
|
30899
30970
|
}
|
|
@@ -31701,12 +31772,12 @@ function createPRPoller(callbacks, log, resolveTopLevel = getGitTopLevel) {
|
|
|
31701
31772
|
|
|
31702
31773
|
// src/services/roi/commit-sweep.ts
|
|
31703
31774
|
import { stat as stat11 } from "fs/promises";
|
|
31704
|
-
import { join as
|
|
31775
|
+
import { join as join33 } from "path";
|
|
31705
31776
|
|
|
31706
31777
|
// src/services/git-checkpoint.ts
|
|
31707
31778
|
import { execFile as execFileCb2 } from "child_process";
|
|
31708
31779
|
import { readFile as readFile21, stat as stat10, unlink as unlink9, writeFile as writeFile15 } from "fs/promises";
|
|
31709
|
-
import { join as
|
|
31780
|
+
import { join as join32 } from "path";
|
|
31710
31781
|
import { promisify as promisify7 } from "util";
|
|
31711
31782
|
var execFile9 = promisify7(execFileCb2);
|
|
31712
31783
|
var NOT_A_GIT_REPO = "Not a git repository";
|
|
@@ -31718,10 +31789,10 @@ async function resolveGitPaths(repoDir, taskId) {
|
|
|
31718
31789
|
try {
|
|
31719
31790
|
const { stdout } = await execFile9("git", ["rev-parse", "--git-dir"], { cwd: repoDir });
|
|
31720
31791
|
const gitDir = stdout.trim();
|
|
31721
|
-
const absoluteGitDir = gitDir.startsWith("/") ? gitDir :
|
|
31792
|
+
const absoluteGitDir = gitDir.startsWith("/") ? gitDir : join32(repoDir, gitDir);
|
|
31722
31793
|
return {
|
|
31723
31794
|
gitDir: absoluteGitDir,
|
|
31724
|
-
shadowIndex:
|
|
31795
|
+
shadowIndex: join32(absoluteGitDir, `shipyard-index-${taskId}`)
|
|
31725
31796
|
};
|
|
31726
31797
|
} catch {
|
|
31727
31798
|
return null;
|
|
@@ -31816,7 +31887,7 @@ async function rewindToCheckpointImpl(repoDir, taskId, turnNo) {
|
|
|
31816
31887
|
const untrackedFiles = untrackedRaw.trim().split("\n").filter((f2) => f2.length > 0);
|
|
31817
31888
|
for (const file of untrackedFiles) {
|
|
31818
31889
|
try {
|
|
31819
|
-
const fullPath =
|
|
31890
|
+
const fullPath = join32(repoDir, file);
|
|
31820
31891
|
const s2 = await stat10(fullPath);
|
|
31821
31892
|
if (s2.isFile()) {
|
|
31822
31893
|
await unlink9(fullPath);
|
|
@@ -32083,7 +32154,7 @@ async function revertSingleFile(repoDir, file, restoreCommit, mode) {
|
|
|
32083
32154
|
const isDeletedByAgent = file.status === "deleted";
|
|
32084
32155
|
const shouldDelete = mode === "revert" && isAddedByAgent || mode === "unrevert" && isDeletedByAgent;
|
|
32085
32156
|
if (shouldDelete) {
|
|
32086
|
-
await unlink9(
|
|
32157
|
+
await unlink9(join32(repoDir, file.path)).catch((err3) => {
|
|
32087
32158
|
if (!isEnoent(err3)) throw err3;
|
|
32088
32159
|
});
|
|
32089
32160
|
await execFile9("git", ["reset", "HEAD", "--", file.path], { cwd: repoDir }).catch(() => {
|
|
@@ -32095,7 +32166,7 @@ async function revertSingleFile(repoDir, file, restoreCommit, mode) {
|
|
|
32095
32166
|
maxBuffer: 10 * 1024 * 1024,
|
|
32096
32167
|
encoding: "buffer"
|
|
32097
32168
|
});
|
|
32098
|
-
await writeFile15(
|
|
32169
|
+
await writeFile15(join32(repoDir, file.path), stdout);
|
|
32099
32170
|
await execFile9("git", ["reset", "HEAD", "--", file.path], { cwd: repoDir }).catch(() => {
|
|
32100
32171
|
});
|
|
32101
32172
|
return true;
|
|
@@ -32188,7 +32259,7 @@ async function diffCheckpointToWorkingTreeImpl(repoDir, taskId, fromTurnNo, scop
|
|
|
32188
32259
|
if (!line || statusMap.has(line)) continue;
|
|
32189
32260
|
let ins = 0;
|
|
32190
32261
|
try {
|
|
32191
|
-
ins = countLines(await readFile21(
|
|
32262
|
+
ins = countLines(await readFile21(join32(repoDir, line), "utf-8"));
|
|
32192
32263
|
} catch {
|
|
32193
32264
|
}
|
|
32194
32265
|
entries.push({ path: line, status: "added", insertions: ins, deletions: 0 });
|
|
@@ -32208,7 +32279,7 @@ async function getFileVsWorkingTreeImpl(repoDir, taskId, fromTurnNo, filePath) {
|
|
|
32208
32279
|
if (from2 === "error") return null;
|
|
32209
32280
|
let modifiedContent;
|
|
32210
32281
|
try {
|
|
32211
|
-
modifiedContent = await readFile21(
|
|
32282
|
+
modifiedContent = await readFile21(join32(repoDir, filePath), "utf-8");
|
|
32212
32283
|
} catch {
|
|
32213
32284
|
modifiedContent = "";
|
|
32214
32285
|
}
|
|
@@ -32489,7 +32560,7 @@ async function isGitBinaryMissing() {
|
|
|
32489
32560
|
}
|
|
32490
32561
|
async function isGitWorkingTree(cwd) {
|
|
32491
32562
|
try {
|
|
32492
|
-
await stat11(
|
|
32563
|
+
await stat11(join33(cwd, ".git"));
|
|
32493
32564
|
return true;
|
|
32494
32565
|
} catch {
|
|
32495
32566
|
return false;
|
|
@@ -33453,7 +33524,7 @@ async function sweepOrphanedNotifications(taskStateStore, log) {
|
|
|
33453
33524
|
|
|
33454
33525
|
// src/services/loro-recovery.ts
|
|
33455
33526
|
import { access as access5, mkdir as mkdir14, rename as rename12, rm as rm6 } from "fs/promises";
|
|
33456
|
-
import { join as
|
|
33527
|
+
import { join as join34 } from "path";
|
|
33457
33528
|
var DEFAULT_RECOVERY_TTL_MS = 6e4;
|
|
33458
33529
|
function createLoroRecoveryService(deps) {
|
|
33459
33530
|
const disabled = validateEnv().SHIPYARD_DISABLE_LORO_RECOVERY;
|
|
@@ -33504,9 +33575,9 @@ function createLoroRecoveryService(deps) {
|
|
|
33504
33575
|
}
|
|
33505
33576
|
async function quarantineAndSweep(docId, isoTs) {
|
|
33506
33577
|
const encDocId = encodeURIComponent(docId);
|
|
33507
|
-
const sourcePath =
|
|
33508
|
-
const quarantineRoot =
|
|
33509
|
-
const quarantinePath =
|
|
33578
|
+
const sourcePath = join34(deps.dataDir, "loro", encDocId);
|
|
33579
|
+
const quarantineRoot = join34(deps.dataDir, "loro-quarantine", isoTs);
|
|
33580
|
+
const quarantinePath = join34(quarantineRoot, encDocId);
|
|
33510
33581
|
const sourceExists = await pathExists3(sourcePath);
|
|
33511
33582
|
if (!sourceExists) return "";
|
|
33512
33583
|
await mkdir14(quarantineRoot, { recursive: true, mode: 448 });
|
|
@@ -33515,7 +33586,7 @@ function createLoroRecoveryService(deps) {
|
|
|
33515
33586
|
}
|
|
33516
33587
|
async function postDeleteSweep(docId) {
|
|
33517
33588
|
const encDocId = encodeURIComponent(docId);
|
|
33518
|
-
const sourcePath =
|
|
33589
|
+
const sourcePath = join34(deps.dataDir, "loro", encDocId);
|
|
33519
33590
|
if (await pathExists3(sourcePath)) {
|
|
33520
33591
|
try {
|
|
33521
33592
|
await rm6(sourcePath, { recursive: true, force: true });
|
|
@@ -35342,7 +35413,7 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
35342
35413
|
const aloStreamId = controlChannelStreamId(deps.peerSourceId);
|
|
35343
35414
|
const aloOutboxChannelName = controlChannelOutboxChannel("daemon", deps.peerSourceId);
|
|
35344
35415
|
const channelEpoch = registerControlChannelEpoch(controlPeerId);
|
|
35345
|
-
const dataDir =
|
|
35416
|
+
const dataDir = join35(deps?.shipyardHome ?? getShipyardHome(), "data");
|
|
35346
35417
|
const aloOutbox = new JsonDocOutbox({
|
|
35347
35418
|
dataDir,
|
|
35348
35419
|
channel: aloOutboxChannelName,
|
|
@@ -35467,6 +35538,9 @@ function wireControlChannel(rawChannel, daemon, logAdapter, deps) {
|
|
|
35467
35538
|
onOrphanReaped: () => {
|
|
35468
35539
|
handleControlChannelTerminal("orphan_reaped");
|
|
35469
35540
|
},
|
|
35541
|
+
onDeadLetter: (_entry, attempts) => {
|
|
35542
|
+
telemeter("alo_resend_exhausted", { attempts, channel: "control" });
|
|
35543
|
+
},
|
|
35470
35544
|
log: logAdapter
|
|
35471
35545
|
});
|
|
35472
35546
|
const aloReady = aloShell.initialize().catch((err3) => {
|
|
@@ -35766,7 +35840,7 @@ function createPeerRoleRegistry() {
|
|
|
35766
35840
|
|
|
35767
35841
|
// src/services/epoch-pruning.ts
|
|
35768
35842
|
import { readdir as readdir14, rm as rm7 } from "fs/promises";
|
|
35769
|
-
import { join as
|
|
35843
|
+
import { join as join36 } from "path";
|
|
35770
35844
|
var LEGACY_PREFIXES = [
|
|
35771
35845
|
"task-meta",
|
|
35772
35846
|
"task-conv",
|
|
@@ -35806,7 +35880,7 @@ async function pruneOldEpochData(dataDir, currentEpoch, log) {
|
|
|
35806
35880
|
}
|
|
35807
35881
|
if (!shouldPrune(decoded, currentEpoch)) continue;
|
|
35808
35882
|
removals.push(
|
|
35809
|
-
rm7(
|
|
35883
|
+
rm7(join36(dataDir, entry.name), { recursive: true }).then(() => {
|
|
35810
35884
|
pruned++;
|
|
35811
35885
|
}).catch((err3) => {
|
|
35812
35886
|
log({
|
|
@@ -38031,7 +38105,7 @@ function onConnection(ws, deps, peers) {
|
|
|
38031
38105
|
|
|
38032
38106
|
// src/services/local-direct/local-direct-token.ts
|
|
38033
38107
|
import { chmod as chmod2, mkdir as mkdir15, rename as rename13, rm as rm8, writeFile as writeFile16 } from "fs/promises";
|
|
38034
|
-
import { dirname as dirname17, join as
|
|
38108
|
+
import { dirname as dirname17, join as join37 } from "path";
|
|
38035
38109
|
var ADVERTISEMENT_FILE = "local-direct.json";
|
|
38036
38110
|
var ADVERTISEMENT_MODE = 384;
|
|
38037
38111
|
var ADVERTISEMENT_DIR_MODE = 448;
|
|
@@ -38040,7 +38114,7 @@ function generateLocalDirectToken() {
|
|
|
38040
38114
|
return nanoid(TOKEN_LENGTH);
|
|
38041
38115
|
}
|
|
38042
38116
|
function advertisementPath(shipyardHome) {
|
|
38043
|
-
return
|
|
38117
|
+
return join37(shipyardHome, "data", ADVERTISEMENT_FILE);
|
|
38044
38118
|
}
|
|
38045
38119
|
async function writeAdvertisement(shipyardHome, ad) {
|
|
38046
38120
|
const target = advertisementPath(shipyardHome);
|
|
@@ -38307,7 +38381,7 @@ async function setupPluginEventWiring(deps) {
|
|
|
38307
38381
|
import { exec as execCb2 } from "child_process";
|
|
38308
38382
|
import { existsSync as existsSync5 } from "fs";
|
|
38309
38383
|
import { readdir as readdir15, readFile as readFile22, stat as stat12 } from "fs/promises";
|
|
38310
|
-
import { basename as basename7, dirname as dirname18, join as
|
|
38384
|
+
import { basename as basename7, dirname as dirname18, join as join38 } from "path";
|
|
38311
38385
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
38312
38386
|
import { promisify as promisify8 } from "util";
|
|
38313
38387
|
|
|
@@ -38442,7 +38516,7 @@ var PluginFileWatcher = class {
|
|
|
38442
38516
|
}
|
|
38443
38517
|
const loaded = [];
|
|
38444
38518
|
for (const entry of entries) {
|
|
38445
|
-
const pluginDir =
|
|
38519
|
+
const pluginDir = join38(dir, entry);
|
|
38446
38520
|
let stats;
|
|
38447
38521
|
try {
|
|
38448
38522
|
stats = await stat12(pluginDir);
|
|
@@ -38457,7 +38531,7 @@ var PluginFileWatcher = class {
|
|
|
38457
38531
|
this.#reconcile(loaded);
|
|
38458
38532
|
}
|
|
38459
38533
|
async #loadPlugin(id, pluginDir) {
|
|
38460
|
-
const manifestPath =
|
|
38534
|
+
const manifestPath = join38(pluginDir, "plugin.json");
|
|
38461
38535
|
let manifestRaw;
|
|
38462
38536
|
try {
|
|
38463
38537
|
manifestRaw = await readFile22(manifestPath, "utf-8");
|
|
@@ -38494,7 +38568,7 @@ var PluginFileWatcher = class {
|
|
|
38494
38568
|
return null;
|
|
38495
38569
|
}
|
|
38496
38570
|
let template = "";
|
|
38497
|
-
const templatePath =
|
|
38571
|
+
const templatePath = join38(pluginDir, "template.html");
|
|
38498
38572
|
try {
|
|
38499
38573
|
template = await readFile22(templatePath, "utf-8");
|
|
38500
38574
|
} catch {
|
|
@@ -38509,7 +38583,7 @@ var PluginFileWatcher = class {
|
|
|
38509
38583
|
};
|
|
38510
38584
|
}
|
|
38511
38585
|
async #loadAndRegisterHandler(id, pluginDir, title, manifest) {
|
|
38512
|
-
const handlerPath =
|
|
38586
|
+
const handlerPath = join38(pluginDir, "handler.mjs");
|
|
38513
38587
|
const loaded = await this.#resolveHandler(id, handlerPath);
|
|
38514
38588
|
const { handlerFn, provideResourcesFn } = loaded;
|
|
38515
38589
|
if (manifest.provideResources && provideResourcesFn && this.#config.resourceResolver) {
|
|
@@ -38636,14 +38710,20 @@ var PluginFileWatcher = class {
|
|
|
38636
38710
|
get plugins() {
|
|
38637
38711
|
return this.#currentPlugins;
|
|
38638
38712
|
}
|
|
38639
|
-
dispose() {
|
|
38713
|
+
async dispose() {
|
|
38640
38714
|
this.#disposed = true;
|
|
38641
38715
|
if (this.#debounceTimer) {
|
|
38642
38716
|
clearTimeout(this.#debounceTimer);
|
|
38643
38717
|
this.#debounceTimer = null;
|
|
38644
38718
|
}
|
|
38645
|
-
|
|
38719
|
+
const sub = this.#dirWatcher;
|
|
38646
38720
|
this.#dirWatcher = null;
|
|
38721
|
+
await sub?.unsubscribe().catch((err3) => {
|
|
38722
|
+
this.#config.log({
|
|
38723
|
+
event: "plugin_watcher_dispose_unsubscribe_failed",
|
|
38724
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
38725
|
+
});
|
|
38726
|
+
});
|
|
38647
38727
|
for (const id of this.#registeredIds) {
|
|
38648
38728
|
unregisterPlugin(id);
|
|
38649
38729
|
this.#config.eventBus?.unregisterAll(id);
|
|
@@ -38667,7 +38747,7 @@ async function findNativeDependencyMarker(pluginDir) {
|
|
|
38667
38747
|
}
|
|
38668
38748
|
async function scanNativeDependencyDir(dir, stack) {
|
|
38669
38749
|
for (const entry of await readNativeScanEntries(dir)) {
|
|
38670
|
-
const fullPath =
|
|
38750
|
+
const fullPath = join38(dir, entry.name);
|
|
38671
38751
|
if (isNativeDependencyMarker(entry, dir)) return fullPath;
|
|
38672
38752
|
if (shouldDescendForNativeScan(entry)) stack.push(fullPath);
|
|
38673
38753
|
}
|
|
@@ -38692,7 +38772,7 @@ function isNodeBuildReleaseDir(dir) {
|
|
|
38692
38772
|
|
|
38693
38773
|
// src/services/port-detection.ts
|
|
38694
38774
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
38695
|
-
import { dirname as dirname19, join as
|
|
38775
|
+
import { dirname as dirname19, join as join39 } from "path";
|
|
38696
38776
|
var LSOF_TIMEOUT_MS = 8e3;
|
|
38697
38777
|
var LSOF_TTL_MS = 5e3;
|
|
38698
38778
|
var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
@@ -38875,7 +38955,7 @@ function hasStringName(val) {
|
|
|
38875
38955
|
function resolveProjectRoot(cwd) {
|
|
38876
38956
|
let dir = cwd;
|
|
38877
38957
|
while (true) {
|
|
38878
|
-
const candidate =
|
|
38958
|
+
const candidate = join39(dir, "package.json");
|
|
38879
38959
|
if (existsSync6(candidate)) {
|
|
38880
38960
|
let packageName;
|
|
38881
38961
|
try {
|
|
@@ -38999,7 +39079,7 @@ import {
|
|
|
38999
39079
|
unlinkSync as unlinkSync2,
|
|
39000
39080
|
writeFileSync as writeFileSync2
|
|
39001
39081
|
} from "fs";
|
|
39002
|
-
import { join as
|
|
39082
|
+
import { join as join40 } from "path";
|
|
39003
39083
|
function isEnoent6(err3) {
|
|
39004
39084
|
return err3 instanceof Error && Reflect.get(err3, "code") === "ENOENT";
|
|
39005
39085
|
}
|
|
@@ -39009,7 +39089,7 @@ function atomicWriteSync(filePath, content) {
|
|
|
39009
39089
|
renameSync(tmpPath, filePath);
|
|
39010
39090
|
}
|
|
39011
39091
|
function recordFilePath2(rootDir, taskId, elementId) {
|
|
39012
|
-
return
|
|
39092
|
+
return join40(rootDir, taskId, `${elementId}.json`);
|
|
39013
39093
|
}
|
|
39014
39094
|
function parseRecord2(filePath, logger2) {
|
|
39015
39095
|
let raw;
|
|
@@ -39049,7 +39129,7 @@ function createPreviewStateStore(opts) {
|
|
|
39049
39129
|
return taskMap;
|
|
39050
39130
|
}
|
|
39051
39131
|
function scanTaskDir(taskId) {
|
|
39052
|
-
const taskPath =
|
|
39132
|
+
const taskPath = join40(rootDir, taskId);
|
|
39053
39133
|
let files;
|
|
39054
39134
|
try {
|
|
39055
39135
|
files = readdirSync4(taskPath);
|
|
@@ -39058,7 +39138,7 @@ function createPreviewStateStore(opts) {
|
|
|
39058
39138
|
}
|
|
39059
39139
|
for (const file of files) {
|
|
39060
39140
|
if (!file.endsWith(".json")) continue;
|
|
39061
|
-
const record = parseRecord2(
|
|
39141
|
+
const record = parseRecord2(join40(taskPath, file), logger2);
|
|
39062
39142
|
if (record) getTaskMap(taskId).set(record.elementId, record);
|
|
39063
39143
|
}
|
|
39064
39144
|
}
|
|
@@ -39084,7 +39164,7 @@ function createPreviewStateStore(opts) {
|
|
|
39084
39164
|
function writeToDisk(taskId, state) {
|
|
39085
39165
|
const filePath = recordFilePath2(rootDir, taskId, state.elementId);
|
|
39086
39166
|
try {
|
|
39087
|
-
mkdirSync3(
|
|
39167
|
+
mkdirSync3(join40(rootDir, taskId), { recursive: true });
|
|
39088
39168
|
atomicWriteSync(filePath, JSON.stringify(state));
|
|
39089
39169
|
} catch (err3) {
|
|
39090
39170
|
logger2.warn(
|
|
@@ -41425,7 +41505,7 @@ function wireThreadErrorFallback(daemon, dc, taskId, threadId, channelId, log) {
|
|
|
41425
41505
|
}
|
|
41426
41506
|
|
|
41427
41507
|
// src/services/terminal-handler.ts
|
|
41428
|
-
import { join as
|
|
41508
|
+
import { join as join41 } from "path";
|
|
41429
41509
|
|
|
41430
41510
|
// src/shared/pty-manager.ts
|
|
41431
41511
|
import { accessSync, chmodSync, constants as constants2, createWriteStream } from "fs";
|
|
@@ -41514,7 +41594,7 @@ function createPtyManager() {
|
|
|
41514
41594
|
if (exitSink) exitSink(exitCode3, signal);
|
|
41515
41595
|
for (const cb of exitCallbacks) cb(exitCode3, signal);
|
|
41516
41596
|
}
|
|
41517
|
-
function
|
|
41597
|
+
function spawn16(options) {
|
|
41518
41598
|
if (isAlive) {
|
|
41519
41599
|
throw new Error("PTY already spawned. Call kill() or dispose() first.");
|
|
41520
41600
|
}
|
|
@@ -41690,7 +41770,7 @@ function createPtyManager() {
|
|
|
41690
41770
|
get originTaskId() {
|
|
41691
41771
|
return originTaskId;
|
|
41692
41772
|
},
|
|
41693
|
-
spawn:
|
|
41773
|
+
spawn: spawn16,
|
|
41694
41774
|
write,
|
|
41695
41775
|
resize,
|
|
41696
41776
|
onData,
|
|
@@ -41706,7 +41786,7 @@ function createPtyManager() {
|
|
|
41706
41786
|
// src/services/terminal-handler.ts
|
|
41707
41787
|
var MAX_PTYS_DEFAULT = 20;
|
|
41708
41788
|
function terminalLogPathFor(dir, terminalId) {
|
|
41709
|
-
return
|
|
41789
|
+
return join41(dir, `${terminalId}.log`);
|
|
41710
41790
|
}
|
|
41711
41791
|
function handleTerminalChannel(taskId, terminalId, cwd, send, log, deps) {
|
|
41712
41792
|
const maxPtys = deps.maxPtys ?? MAX_PTYS_DEFAULT;
|
|
@@ -42316,7 +42396,7 @@ function buildCollabRoomManager(deps) {
|
|
|
42316
42396
|
// src/services/serve-factory.ts
|
|
42317
42397
|
import { randomUUID as randomUUID25 } from "crypto";
|
|
42318
42398
|
import { mkdir as mkdir36 } from "fs/promises";
|
|
42319
|
-
import { join as
|
|
42399
|
+
import { join as join79 } from "path";
|
|
42320
42400
|
|
|
42321
42401
|
// src/shared/capabilities/cursor-boot-auth.ts
|
|
42322
42402
|
async function mergeCursorAuthFromVault(caps, detect, log) {
|
|
@@ -42357,12 +42437,12 @@ async function mergeCursorAuthFromVault(caps, detect, log) {
|
|
|
42357
42437
|
|
|
42358
42438
|
// src/shared/capabilities/cursor-hook-shim-path.ts
|
|
42359
42439
|
import { statSync as statSync3 } from "fs";
|
|
42360
|
-
import { join as
|
|
42440
|
+
import { join as join42 } from "path";
|
|
42361
42441
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
42362
42442
|
function getCursorHookShimPath() {
|
|
42363
42443
|
const resourcesPath = Reflect.get(process, "resourcesPath");
|
|
42364
42444
|
if (typeof resourcesPath === "string" && resourcesPath.length > 0) {
|
|
42365
|
-
return
|
|
42445
|
+
return join42(resourcesPath, "daemon", "cursor-hook-shim.js");
|
|
42366
42446
|
}
|
|
42367
42447
|
return fileURLToPath2(new URL("./cursor-hook-shim.js", import.meta.url));
|
|
42368
42448
|
}
|
|
@@ -42597,7 +42677,7 @@ function resolveRuntimeAuthIdentity(runtimeAuth, runtimeId) {
|
|
|
42597
42677
|
// src/shared/capabilities/spinner-verbs-reader.ts
|
|
42598
42678
|
import { readFile as readFile23 } from "fs/promises";
|
|
42599
42679
|
import { homedir as homedir5 } from "os";
|
|
42600
|
-
import { join as
|
|
42680
|
+
import { join as join43 } from "path";
|
|
42601
42681
|
var ClaudeSpinnerVerbsSchema = external_exports.object({
|
|
42602
42682
|
mode: external_exports.enum(["append", "replace"]),
|
|
42603
42683
|
verbs: external_exports.array(external_exports.string())
|
|
@@ -42622,8 +42702,8 @@ function toPersistedVerbs(resolved, defaults2) {
|
|
|
42622
42702
|
return resolved.every((v2, i) => v2 === defaults2[i]) ? [] : [...resolved];
|
|
42623
42703
|
}
|
|
42624
42704
|
async function readSpinnerVerbs() {
|
|
42625
|
-
const claudeDir =
|
|
42626
|
-
const paths = [
|
|
42705
|
+
const claudeDir = join43(homedir5(), ".claude");
|
|
42706
|
+
const paths = [join43(claudeDir, "settings.json"), join43(claudeDir, "settings.local.json")];
|
|
42627
42707
|
const parsed = await Promise.all(
|
|
42628
42708
|
paths.map(async (p2) => {
|
|
42629
42709
|
try {
|
|
@@ -42665,7 +42745,7 @@ async function reimportSpinnerVerbs(store, log) {
|
|
|
42665
42745
|
|
|
42666
42746
|
// src/shared/mcp/oauth-state-store.ts
|
|
42667
42747
|
import { readFile as readFile24 } from "fs/promises";
|
|
42668
|
-
import { join as
|
|
42748
|
+
import { join as join44 } from "path";
|
|
42669
42749
|
import {
|
|
42670
42750
|
OAuthClientInformationFullSchema as OAuthClientInformationFullSchema2,
|
|
42671
42751
|
OAuthClientInformationSchema as OAuthClientInformationSchema2,
|
|
@@ -42711,7 +42791,7 @@ var MCPOAuthStateStore = class {
|
|
|
42711
42791
|
this.#shipyardHome = shipyardHome;
|
|
42712
42792
|
}
|
|
42713
42793
|
#filePath() {
|
|
42714
|
-
return
|
|
42794
|
+
return join44(this.#shipyardHome, STATE_FILE);
|
|
42715
42795
|
}
|
|
42716
42796
|
#lockPath() {
|
|
42717
42797
|
return `${this.#filePath()}.lock`;
|
|
@@ -42810,11 +42890,11 @@ function getShipyardSubagentCatalog() {
|
|
|
42810
42890
|
|
|
42811
42891
|
// src/services/bootstrap/rehydrate.ts
|
|
42812
42892
|
import { readFile as readFile26, rename as rename16, writeFile as writeFile18 } from "fs/promises";
|
|
42813
|
-
import { join as
|
|
42893
|
+
import { join as join46 } from "path";
|
|
42814
42894
|
|
|
42815
42895
|
// src/services/collab/collab-queue-persistence.ts
|
|
42816
42896
|
import { appendFile as appendFile2, mkdir as mkdir16, readdir as readdir16, readFile as readFile25, rename as rename15, rm as rm9, writeFile as writeFile17 } from "fs/promises";
|
|
42817
|
-
import { join as
|
|
42897
|
+
import { join as join45 } from "path";
|
|
42818
42898
|
var PersistedQueueEntrySchema = external_exports.object({
|
|
42819
42899
|
content: external_exports.array(ContentBlockSchema),
|
|
42820
42900
|
settings: DaemonSettingsSchema.optional(),
|
|
@@ -42838,9 +42918,9 @@ function parsePersistedLine(line) {
|
|
|
42838
42918
|
};
|
|
42839
42919
|
}
|
|
42840
42920
|
function buildCollabQueuePersistence(dataDir) {
|
|
42841
|
-
const queuesDir =
|
|
42921
|
+
const queuesDir = join45(dataDir, "collab-queues");
|
|
42842
42922
|
function queuePath(queueKey) {
|
|
42843
|
-
return
|
|
42923
|
+
return join45(queuesDir, `${queueKey}.jsonl`);
|
|
42844
42924
|
}
|
|
42845
42925
|
async function ensureDir() {
|
|
42846
42926
|
await mkdir16(queuesDir, { recursive: true });
|
|
@@ -42933,7 +43013,7 @@ function buildCollabQueuePersistence(dataDir) {
|
|
|
42933
43013
|
}
|
|
42934
43014
|
for (const name of names) {
|
|
42935
43015
|
if (!name.includes(".tmp-")) continue;
|
|
42936
|
-
await rm9(
|
|
43016
|
+
await rm9(join45(queuesDir, name), { force: true }).catch(() => {
|
|
42937
43017
|
});
|
|
42938
43018
|
}
|
|
42939
43019
|
}
|
|
@@ -43252,7 +43332,7 @@ async function stripPinnedFieldFromTasksFile(tasksPath, envelope, records) {
|
|
|
43252
43332
|
}
|
|
43253
43333
|
}
|
|
43254
43334
|
async function migratePinnedToFavoriteTasks(dataDir, userSettingsStore, log) {
|
|
43255
|
-
const tasksPath =
|
|
43335
|
+
const tasksPath = join46(dataDir, "tasks.json");
|
|
43256
43336
|
const parsed = await readRawTasksEnvelope(tasksPath);
|
|
43257
43337
|
if (!parsed) return;
|
|
43258
43338
|
const { records, envelope, fromMigrated } = parsed;
|
|
@@ -43368,7 +43448,7 @@ async function sweepStaleTasks(taskStateStore, taskManager, log) {
|
|
|
43368
43448
|
|
|
43369
43449
|
// src/services/bootstrap/update-status.ts
|
|
43370
43450
|
import { readFile as readFile27, unlink as unlink11 } from "fs/promises";
|
|
43371
|
-
import { join as
|
|
43451
|
+
import { join as join47 } from "path";
|
|
43372
43452
|
var UPDATE_STATUS_FILENAME = "update-status.json";
|
|
43373
43453
|
var RAW_LOG_MAX_CHARS = 500;
|
|
43374
43454
|
var UpdateStatusSchema = external_exports.object({
|
|
@@ -43381,7 +43461,7 @@ var UpdateStatusSchema = external_exports.object({
|
|
|
43381
43461
|
rolledBack: external_exports.boolean()
|
|
43382
43462
|
});
|
|
43383
43463
|
async function readAndClearUpdateStatus(shipyardHome, log) {
|
|
43384
|
-
const path5 =
|
|
43464
|
+
const path5 = join47(shipyardHome, UPDATE_STATUS_FILENAME);
|
|
43385
43465
|
let raw;
|
|
43386
43466
|
try {
|
|
43387
43467
|
raw = await readFile27(path5, "utf-8");
|
|
@@ -43468,11 +43548,11 @@ function wireUpdateStatusForwarding(report, sink2, onMessage, log) {
|
|
|
43468
43548
|
|
|
43469
43549
|
// src/services/bootstrap/updates-cleanup.ts
|
|
43470
43550
|
import { readdir as readdir17, rm as rm10, stat as stat14, unlink as unlink13 } from "fs/promises";
|
|
43471
|
-
import { join as
|
|
43551
|
+
import { join as join49 } from "path";
|
|
43472
43552
|
|
|
43473
43553
|
// src/services/bootstrap/self-update-lock.ts
|
|
43474
43554
|
import { mkdir as mkdir17, readFile as readFile28, stat as stat13, unlink as unlink12, writeFile as writeFile19 } from "fs/promises";
|
|
43475
|
-
import { dirname as dirname21, join as
|
|
43555
|
+
import { dirname as dirname21, join as join48 } from "path";
|
|
43476
43556
|
var LOCK_FILENAME = ".lock";
|
|
43477
43557
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
43478
43558
|
var LockFileSchema = external_exports.object({
|
|
@@ -43480,7 +43560,7 @@ var LockFileSchema = external_exports.object({
|
|
|
43480
43560
|
startedAt: external_exports.number()
|
|
43481
43561
|
});
|
|
43482
43562
|
function lockPath(shipyardHome) {
|
|
43483
|
-
return
|
|
43563
|
+
return join48(shipyardHome, "updates", LOCK_FILENAME);
|
|
43484
43564
|
}
|
|
43485
43565
|
function isStaleLock(lock, now, isProcessAlive2) {
|
|
43486
43566
|
const age = now - lock.startedAt;
|
|
@@ -43603,7 +43683,7 @@ async function cleanupUpdatesDir(shipyardHome, deps = {}) {
|
|
|
43603
43683
|
const now = deps.now ?? Date.now;
|
|
43604
43684
|
const isProcessAlive2 = deps.isProcessAlive ?? defaultIsProcessAlive;
|
|
43605
43685
|
const log = deps.log;
|
|
43606
|
-
const updatesDir =
|
|
43686
|
+
const updatesDir = join49(shipyardHome, "updates");
|
|
43607
43687
|
const empty2 = {
|
|
43608
43688
|
tarballsKept: [],
|
|
43609
43689
|
tarballsDeleted: [],
|
|
@@ -43727,7 +43807,7 @@ async function sortByMtimeDesc(baseDir, names, log) {
|
|
|
43727
43807
|
const rows = [];
|
|
43728
43808
|
for (const name of names) {
|
|
43729
43809
|
try {
|
|
43730
|
-
const s2 = await stat14(
|
|
43810
|
+
const s2 = await stat14(join49(baseDir, name));
|
|
43731
43811
|
rows.push({ name, mtimeMs: s2.mtimeMs });
|
|
43732
43812
|
} catch (err3) {
|
|
43733
43813
|
log?.info({ err: err3, entry: name }, "updates cleanup: stat failed, skipping entry");
|
|
@@ -43740,7 +43820,7 @@ async function filterOlderThan(baseDir, names, nowMs, thresholdMs, log) {
|
|
|
43740
43820
|
const out = [];
|
|
43741
43821
|
for (const name of names) {
|
|
43742
43822
|
try {
|
|
43743
|
-
const s2 = await stat14(
|
|
43823
|
+
const s2 = await stat14(join49(baseDir, name));
|
|
43744
43824
|
if (nowMs - s2.mtimeMs > thresholdMs) {
|
|
43745
43825
|
out.push(name);
|
|
43746
43826
|
}
|
|
@@ -43754,7 +43834,7 @@ async function deleteFiles(baseDir, names, log) {
|
|
|
43754
43834
|
const deleted = [];
|
|
43755
43835
|
for (const name of names) {
|
|
43756
43836
|
try {
|
|
43757
|
-
await unlink13(
|
|
43837
|
+
await unlink13(join49(baseDir, name));
|
|
43758
43838
|
deleted.push(name);
|
|
43759
43839
|
} catch (err3) {
|
|
43760
43840
|
if (isEnoent(err3)) {
|
|
@@ -43770,7 +43850,7 @@ async function deleteDirs(baseDir, names, log) {
|
|
|
43770
43850
|
const deleted = [];
|
|
43771
43851
|
for (const name of names) {
|
|
43772
43852
|
try {
|
|
43773
|
-
await rm10(
|
|
43853
|
+
await rm10(join49(baseDir, name), { recursive: true, force: true });
|
|
43774
43854
|
deleted.push(name);
|
|
43775
43855
|
} catch (err3) {
|
|
43776
43856
|
log?.info({ err: err3, entry: name }, "updates cleanup: rm -rf failed");
|
|
@@ -43779,7 +43859,7 @@ async function deleteDirs(baseDir, names, log) {
|
|
|
43779
43859
|
return deleted;
|
|
43780
43860
|
}
|
|
43781
43861
|
async function maybeClearLock(shipyardHome, nowMs, isProcessAlive2, log) {
|
|
43782
|
-
const lockFilePath =
|
|
43862
|
+
const lockFilePath = join49(shipyardHome, "updates", LOCK_FILENAME);
|
|
43783
43863
|
const outcome = await readLockFileWithOutcome(shipyardHome);
|
|
43784
43864
|
switch (outcome.kind) {
|
|
43785
43865
|
case "absent":
|
|
@@ -45080,7 +45160,7 @@ function wireDevServersAndTerminalsResolvers(deps) {
|
|
|
45080
45160
|
|
|
45081
45161
|
// src/services/file-resource-resolver.ts
|
|
45082
45162
|
import { readFile as readFile29, stat as stat15 } from "fs/promises";
|
|
45083
|
-
import { basename as basename8, join as
|
|
45163
|
+
import { basename as basename8, join as join50, normalize as normalize4, sep as sep3 } from "path";
|
|
45084
45164
|
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
45085
45165
|
var MIME_BY_EXT2 = {
|
|
45086
45166
|
".ts": "text/typescript",
|
|
@@ -45149,7 +45229,7 @@ function createFileResourceResolver(deps) {
|
|
|
45149
45229
|
const { taskId, relativePath } = parseFileUri2(uri);
|
|
45150
45230
|
const cwd = deps.getTaskCwd(taskId) ?? deps.workspaceRoot;
|
|
45151
45231
|
const normalizedCwd = normalize4(cwd);
|
|
45152
|
-
const absolutePath = normalize4(
|
|
45232
|
+
const absolutePath = normalize4(join50(cwd, relativePath));
|
|
45153
45233
|
const cwdPrefix = normalizedCwd.endsWith(sep3) ? normalizedCwd : `${normalizedCwd}${sep3}`;
|
|
45154
45234
|
if (!absolutePath.startsWith(cwdPrefix) && absolutePath !== normalizedCwd) {
|
|
45155
45235
|
throw new Error(`Path traversal rejected: ${relativePath}`);
|
|
@@ -45217,7 +45297,7 @@ function unregisterSubprocessEpoch(taskId, registry = subprocessEpochs) {
|
|
|
45217
45297
|
// src/services/harness/deliverable-server.ts
|
|
45218
45298
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
45219
45299
|
import { copyFile, rm as fsRm, stat as fsStat3, mkdir as mkdir18 } from "fs/promises";
|
|
45220
|
-
import { basename as basename9, extname as extname2, join as
|
|
45300
|
+
import { basename as basename9, extname as extname2, join as join51, resolve as resolve8, sep as sep4 } from "path";
|
|
45221
45301
|
var TOOL_DESCRIPTION2 = [
|
|
45222
45302
|
"Register a proof-of-work deliverable for this task. Call this after producing an artifact (screenshot, video, log, markdown document, AX tree, DOM snapshot) so reviewers can see verifiable evidence of what the agent produced.",
|
|
45223
45303
|
"",
|
|
@@ -45277,7 +45357,7 @@ var RegisterDeliverableInput = {
|
|
|
45277
45357
|
};
|
|
45278
45358
|
var COPY_MAX_BYTES = 50 * 1024 * 1024;
|
|
45279
45359
|
function isInternedFile(dataDir, filePath) {
|
|
45280
|
-
const internBase = resolve8(
|
|
45360
|
+
const internBase = resolve8(join51(dataDir, "deliverable-files")) + sep4;
|
|
45281
45361
|
return resolve8(filePath).startsWith(internBase);
|
|
45282
45362
|
}
|
|
45283
45363
|
async function internDeliverableFile(deliverableId, taskId, filePath, storedSizeBytes, dataDir, prevInternedPath) {
|
|
@@ -45301,8 +45381,8 @@ async function internDeliverableFile(deliverableId, taskId, filePath, storedSize
|
|
|
45301
45381
|
};
|
|
45302
45382
|
}
|
|
45303
45383
|
const ext = extname2(filePath);
|
|
45304
|
-
const destDir =
|
|
45305
|
-
const destPath =
|
|
45384
|
+
const destDir = join51(dataDir, "deliverable-files", taskId);
|
|
45385
|
+
const destPath = join51(destDir, `${deliverableId}${ext}`);
|
|
45306
45386
|
try {
|
|
45307
45387
|
await mkdir18(destDir, { recursive: true });
|
|
45308
45388
|
await copyFile(filePath, destPath);
|
|
@@ -45728,7 +45808,7 @@ function createDeliverableTools(ctx) {
|
|
|
45728
45808
|
|
|
45729
45809
|
// src/services/harness/plugin-server.ts
|
|
45730
45810
|
import { mkdir as mkdir19, writeFile as writeFile20 } from "fs/promises";
|
|
45731
|
-
import { join as
|
|
45811
|
+
import { join as join52 } from "path";
|
|
45732
45812
|
|
|
45733
45813
|
// src/services/harness/sandbox-docs.ts
|
|
45734
45814
|
var SANDBOX_HTML_RULES = `## HTML Rules
|
|
@@ -46346,14 +46426,14 @@ ${addendum}`;
|
|
|
46346
46426
|
events: input.events,
|
|
46347
46427
|
provideResources: input.provideResources
|
|
46348
46428
|
});
|
|
46349
|
-
const pluginDir =
|
|
46429
|
+
const pluginDir = join52(ctx.pluginsDir, input.pluginId);
|
|
46350
46430
|
await mkdir19(pluginDir, { recursive: true });
|
|
46351
|
-
await writeFile20(
|
|
46431
|
+
await writeFile20(join52(pluginDir, "template.html"), input.template, "utf-8");
|
|
46352
46432
|
if (input.handler) {
|
|
46353
|
-
await writeFile20(
|
|
46433
|
+
await writeFile20(join52(pluginDir, "handler.mjs"), input.handler, "utf-8");
|
|
46354
46434
|
}
|
|
46355
46435
|
await writeFile20(
|
|
46356
|
-
|
|
46436
|
+
join52(pluginDir, "plugin.json"),
|
|
46357
46437
|
JSON.stringify(manifest, null, 2),
|
|
46358
46438
|
"utf-8"
|
|
46359
46439
|
);
|
|
@@ -55026,124 +55106,6 @@ function validateHtml(html) {
|
|
|
55026
55106
|
|
|
55027
55107
|
// src/services/harness/mermaid-validator.ts
|
|
55028
55108
|
import { Window } from "happy-dom";
|
|
55029
|
-
|
|
55030
|
-
// src/services/harness/mermaid-label-length.ts
|
|
55031
|
-
var MERMAID_MAX_LABEL_SEGMENT_CHARS = 40;
|
|
55032
|
-
var BR_TAG_SPLIT = /<br\s*\/?>/gi;
|
|
55033
|
-
var QUOTED_LABEL_PATTERNS = [
|
|
55034
|
-
/\[\(\s*(["'])((?:\\.|(?!\1).)*)\1\s*\)\]/g,
|
|
55035
|
-
/\(\[\s*(["'])((?:\\.|(?!\1).)*)\1\s*\]\)/g,
|
|
55036
|
-
/\[\[\s*(["'])((?:\\.|(?!\1).)*)\1\s*\]\]/g,
|
|
55037
|
-
/\{\{\s*(["'])((?:\\.|(?!\1).)*)\1\s*\}\}/g,
|
|
55038
|
-
/\[\s*(["'])((?:\\.|(?!\1).)*)\1\s*\]/g,
|
|
55039
|
-
/\(\(\s*(["'])((?:\\.|(?!\1).)*)\1\s*\)\)/g,
|
|
55040
|
-
/\(\s*(["'])((?:\\.|(?!\1).)*)\1\s*\)/g,
|
|
55041
|
-
/\{\s*(["'])((?:\\.|(?!\1).)*)\1\s*\}/g
|
|
55042
|
-
];
|
|
55043
|
-
var UNQUOTED_LABEL_PATTERNS = [
|
|
55044
|
-
/\[\(\s*([^)"'\n]+?)\s*\)\]/g,
|
|
55045
|
-
/\(\[\s*([^)"'\n]+?)\s*\]\)/g,
|
|
55046
|
-
/\[\[\s*([^\]"\n]+?)\s*\]\]/g,
|
|
55047
|
-
/\[\s*([^\]"(<\n]+?)\s*\]/g,
|
|
55048
|
-
/\(\(\s*([^)"'\n]+?)\s*\)\)/g,
|
|
55049
|
-
/\(\s*([^)"'\n]+?)\s*\)/g,
|
|
55050
|
-
/\{\s*([^}"'\n]+?)\s*\}/g
|
|
55051
|
-
];
|
|
55052
|
-
function stripMermaidFrontmatter(code) {
|
|
55053
|
-
return code.replace(/^%%\{[\s\S]*?\}%%\s*/m, "");
|
|
55054
|
-
}
|
|
55055
|
-
function consumeQuotedSegment(line, start, quote) {
|
|
55056
|
-
let text = "";
|
|
55057
|
-
for (let i = start; i < line.length; i++) {
|
|
55058
|
-
const ch = line[i];
|
|
55059
|
-
if (ch === "\\" && i + 1 < line.length) {
|
|
55060
|
-
text += ch + line[i + 1];
|
|
55061
|
-
i++;
|
|
55062
|
-
continue;
|
|
55063
|
-
}
|
|
55064
|
-
text += ch;
|
|
55065
|
-
if (i > start && ch === quote) return { text, nextIndex: i };
|
|
55066
|
-
}
|
|
55067
|
-
return { text, nextIndex: line.length - 1 };
|
|
55068
|
-
}
|
|
55069
|
-
function stripMermaidCommentFromLine(line) {
|
|
55070
|
-
let result = "";
|
|
55071
|
-
for (let i = 0; i < line.length; i++) {
|
|
55072
|
-
const ch = line[i];
|
|
55073
|
-
if (ch === '"' || ch === "'") {
|
|
55074
|
-
const consumed = consumeQuotedSegment(line, i, ch);
|
|
55075
|
-
result += consumed.text;
|
|
55076
|
-
i = consumed.nextIndex;
|
|
55077
|
-
continue;
|
|
55078
|
-
}
|
|
55079
|
-
if (ch === "%" && line[i + 1] === "%") break;
|
|
55080
|
-
result += ch;
|
|
55081
|
-
}
|
|
55082
|
-
return result;
|
|
55083
|
-
}
|
|
55084
|
-
function stripMermaidComments(code) {
|
|
55085
|
-
return code.split("\n").map(stripMermaidCommentFromLine).join("\n");
|
|
55086
|
-
}
|
|
55087
|
-
function extractMermaidNodeLabels(code) {
|
|
55088
|
-
const stripped = stripMermaidComments(stripMermaidFrontmatter(code));
|
|
55089
|
-
const labels = [];
|
|
55090
|
-
const matchedRanges = [];
|
|
55091
|
-
function overlaps(start, end) {
|
|
55092
|
-
return matchedRanges.some(([s2, e]) => start < e && end > s2);
|
|
55093
|
-
}
|
|
55094
|
-
function record(start, end, text) {
|
|
55095
|
-
if (!overlaps(start, end)) {
|
|
55096
|
-
matchedRanges.push([start, end]);
|
|
55097
|
-
labels.push(text);
|
|
55098
|
-
}
|
|
55099
|
-
}
|
|
55100
|
-
for (const pattern of QUOTED_LABEL_PATTERNS) {
|
|
55101
|
-
pattern.lastIndex = 0;
|
|
55102
|
-
let match = pattern.exec(stripped);
|
|
55103
|
-
while (match !== null) {
|
|
55104
|
-
const text = match[2];
|
|
55105
|
-
if (text !== void 0) {
|
|
55106
|
-
record(match.index, match.index + match[0].length, text);
|
|
55107
|
-
}
|
|
55108
|
-
match = pattern.exec(stripped);
|
|
55109
|
-
}
|
|
55110
|
-
}
|
|
55111
|
-
for (const pattern of UNQUOTED_LABEL_PATTERNS) {
|
|
55112
|
-
pattern.lastIndex = 0;
|
|
55113
|
-
let match = pattern.exec(stripped);
|
|
55114
|
-
while (match !== null) {
|
|
55115
|
-
const text = match[1]?.trim();
|
|
55116
|
-
if (text) {
|
|
55117
|
-
record(match.index, match.index + match[0].length, text);
|
|
55118
|
-
}
|
|
55119
|
-
match = pattern.exec(stripped);
|
|
55120
|
-
}
|
|
55121
|
-
}
|
|
55122
|
-
return labels;
|
|
55123
|
-
}
|
|
55124
|
-
function checkMermaidLabelSegmentLengths(code, maxChars = MERMAID_MAX_LABEL_SEGMENT_CHARS) {
|
|
55125
|
-
const labels = extractMermaidNodeLabels(code);
|
|
55126
|
-
const warnings = [];
|
|
55127
|
-
const seen = /* @__PURE__ */ new Set();
|
|
55128
|
-
for (const label of labels) {
|
|
55129
|
-
const segments = label.split(BR_TAG_SPLIT);
|
|
55130
|
-
for (const segment of segments) {
|
|
55131
|
-
const trimmed = segment.trim();
|
|
55132
|
-
if (trimmed.length > maxChars) {
|
|
55133
|
-
const key = `${trimmed}:${trimmed.length}`;
|
|
55134
|
-
if (!seen.has(key)) {
|
|
55135
|
-
seen.add(key);
|
|
55136
|
-
warnings.push(
|
|
55137
|
-
`Label too long (${trimmed.length} > ${maxChars} chars): "${trimmed}" \u2014 abbreviate or split with <br/>.`
|
|
55138
|
-
);
|
|
55139
|
-
}
|
|
55140
|
-
}
|
|
55141
|
-
}
|
|
55142
|
-
}
|
|
55143
|
-
return warnings;
|
|
55144
|
-
}
|
|
55145
|
-
|
|
55146
|
-
// src/services/harness/mermaid-validator.ts
|
|
55147
55109
|
var domInstalled = false;
|
|
55148
55110
|
function installDom() {
|
|
55149
55111
|
if (domInstalled) return;
|
|
@@ -55185,7 +55147,7 @@ function getMermaid() {
|
|
|
55185
55147
|
mermaidPromise = (async () => {
|
|
55186
55148
|
installDom();
|
|
55187
55149
|
const mod = await import("mermaid");
|
|
55188
|
-
mod.default.initialize({ startOnLoad: false, securityLevel:
|
|
55150
|
+
mod.default.initialize({ startOnLoad: false, securityLevel: MERMAID_SECURITY_LEVEL });
|
|
55189
55151
|
return mod;
|
|
55190
55152
|
})().catch((err3) => {
|
|
55191
55153
|
mermaidPromise = null;
|
|
@@ -55201,8 +55163,7 @@ async function validateMermaid(code) {
|
|
|
55201
55163
|
try {
|
|
55202
55164
|
const { default: mermaid } = await getMermaid();
|
|
55203
55165
|
const result = await mermaid.parse(code);
|
|
55204
|
-
|
|
55205
|
-
return { valid: true, diagramType: result?.diagramType ?? "unknown", warnings };
|
|
55166
|
+
return { valid: true, diagramType: result?.diagramType ?? "unknown" };
|
|
55206
55167
|
} catch (e) {
|
|
55207
55168
|
const message = e instanceof Error ? e.message : String(e);
|
|
55208
55169
|
return { valid: false, error: message };
|
|
@@ -55940,7 +55901,7 @@ async function validateContent(type, content) {
|
|
|
55940
55901
|
switch (type) {
|
|
55941
55902
|
case "mermaid": {
|
|
55942
55903
|
const result = await validateMermaid(content);
|
|
55943
|
-
if (result.valid) return { ok: true, warnings:
|
|
55904
|
+
if (result.valid) return { ok: true, warnings: [] };
|
|
55944
55905
|
return { ok: false, error: result.error };
|
|
55945
55906
|
}
|
|
55946
55907
|
case "html": {
|
|
@@ -56544,7 +56505,7 @@ import { pathToFileURL as pathToFileURL3 } from "url";
|
|
|
56544
56505
|
// src/services/lsp/language-server-registry.ts
|
|
56545
56506
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
56546
56507
|
import { createRequire as createRequire3 } from "module";
|
|
56547
|
-
import { dirname as dirname22, isAbsolute as isAbsolute4, join as
|
|
56508
|
+
import { dirname as dirname22, isAbsolute as isAbsolute4, join as join53 } from "path";
|
|
56548
56509
|
function decideTypescriptServer(d) {
|
|
56549
56510
|
if (d.hasAngular || d.hasNest || d.hasTsserverPlugins) return "vtsls";
|
|
56550
56511
|
return "tsgo";
|
|
@@ -56634,11 +56595,11 @@ function isRecord7(value) {
|
|
|
56634
56595
|
return typeof value === "object" && value !== null;
|
|
56635
56596
|
}
|
|
56636
56597
|
function gatherTsDetection(cwd, deps) {
|
|
56637
|
-
const pkg = deps.readJsonFile(
|
|
56598
|
+
const pkg = deps.readJsonFile(join53(cwd, "package.json"));
|
|
56638
56599
|
const depNames = isRecord7(pkg) ? [...keysOf(pkg.dependencies), ...keysOf(pkg.devDependencies)] : [];
|
|
56639
|
-
const hasAngular = depNames.includes("@angular/core") || deps.fileExists(
|
|
56640
|
-
const hasNest = depNames.includes("@nestjs/core") || deps.fileExists(
|
|
56641
|
-
const tsconfig = deps.readJsonFile(
|
|
56600
|
+
const hasAngular = depNames.includes("@angular/core") || deps.fileExists(join53(cwd, "angular.json"));
|
|
56601
|
+
const hasNest = depNames.includes("@nestjs/core") || deps.fileExists(join53(cwd, "nest-cli.json"));
|
|
56602
|
+
const tsconfig = deps.readJsonFile(join53(cwd, "tsconfig.json"));
|
|
56642
56603
|
const compilerOptions = isRecord7(tsconfig) ? tsconfig.compilerOptions : void 0;
|
|
56643
56604
|
const plugins2 = isRecord7(compilerOptions) ? compilerOptions.plugins : void 0;
|
|
56644
56605
|
const hasTsserverPlugins = Array.isArray(plugins2) && plugins2.length > 0;
|
|
@@ -56653,11 +56614,11 @@ function detectPythonVenv(cwd, deps) {
|
|
|
56653
56614
|
if (envVenv && isAbsolute4(envVenv) && deps.fileExists(envVenv)) {
|
|
56654
56615
|
candidates.push(envVenv);
|
|
56655
56616
|
}
|
|
56656
|
-
candidates.push(
|
|
56617
|
+
candidates.push(join53(cwd, ".venv"), join53(cwd, "venv"));
|
|
56657
56618
|
const isWin = deps.platform === "win32";
|
|
56658
56619
|
for (const root of candidates) {
|
|
56659
|
-
const python = isWin ?
|
|
56660
|
-
const python3 = isWin ? python :
|
|
56620
|
+
const python = isWin ? join53(root, "Scripts", "python.exe") : join53(root, "bin", "python");
|
|
56621
|
+
const python3 = isWin ? python : join53(root, "bin", "python3");
|
|
56661
56622
|
if (deps.fileExists(python) || deps.fileExists(python3)) return root;
|
|
56662
56623
|
}
|
|
56663
56624
|
return null;
|
|
@@ -56690,8 +56651,8 @@ function resolveTsgoBinaryPath(req) {
|
|
|
56690
56651
|
`Unable to resolve ${platformPkg}: ${err3 instanceof Error ? err3.message : String(err3)}`
|
|
56691
56652
|
);
|
|
56692
56653
|
}
|
|
56693
|
-
const exeDir =
|
|
56694
|
-
const exe = process.platform === "win32" ?
|
|
56654
|
+
const exeDir = join53(dirname22(pkgJsonPath), "lib");
|
|
56655
|
+
const exe = process.platform === "win32" ? join53(exeDir, "tsgo.exe") : join53(exeDir, "tsgo");
|
|
56695
56656
|
if (!existsSync7(exe)) {
|
|
56696
56657
|
throw new Error(`tsgo native binary not found: ${exe}`);
|
|
56697
56658
|
}
|
|
@@ -56704,7 +56665,7 @@ function resolveBinViaPackageJson(pkg, binName, req) {
|
|
|
56704
56665
|
const named = isRecord7(binField) ? binField[binName] : void 0;
|
|
56705
56666
|
const bin = typeof binField === "string" ? binField : typeof named === "string" ? named : void 0;
|
|
56706
56667
|
if (!bin) throw new Error(`package '${pkg}' has no bin entry '${binName}'`);
|
|
56707
|
-
return
|
|
56668
|
+
return join53(dirname22(pkgJsonPath), bin);
|
|
56708
56669
|
}
|
|
56709
56670
|
function errorMessage3(err3) {
|
|
56710
56671
|
return err3 instanceof Error ? err3.message : String(err3);
|
|
@@ -57393,7 +57354,7 @@ function getPeerSubscriptionSynchronizer(repo) {
|
|
|
57393
57354
|
}
|
|
57394
57355
|
function repoHasPeerSubscription(repo, docId) {
|
|
57395
57356
|
const synchronizer = getPeerSubscriptionSynchronizer(repo);
|
|
57396
|
-
if (!synchronizer) return
|
|
57357
|
+
if (!synchronizer) return true;
|
|
57397
57358
|
return synchronizer.getPeers().some((peer) => peer.subscriptions.has(docId));
|
|
57398
57359
|
}
|
|
57399
57360
|
|
|
@@ -59567,7 +59528,7 @@ import { execFile as execFileCb3 } from "child_process";
|
|
|
59567
59528
|
import { createHash as createHash3 } from "crypto";
|
|
59568
59529
|
import { readFile as readFile31 } from "fs/promises";
|
|
59569
59530
|
import { homedir as homedir6 } from "os";
|
|
59570
|
-
import { join as
|
|
59531
|
+
import { join as join54 } from "path";
|
|
59571
59532
|
import { promisify as promisify9 } from "util";
|
|
59572
59533
|
var execFile10 = promisify9(execFileCb3);
|
|
59573
59534
|
var CodexCredentialsEntrySchema = external_exports.object({
|
|
@@ -59628,10 +59589,10 @@ async function readKeychainStore() {
|
|
|
59628
59589
|
return null;
|
|
59629
59590
|
}
|
|
59630
59591
|
}
|
|
59631
|
-
async function readCodexMcpCredentials(serverName, serverUrl, codexHome =
|
|
59592
|
+
async function readCodexMcpCredentials(serverName, serverUrl, codexHome = join54(homedir6(), ".codex")) {
|
|
59632
59593
|
if (!serverUrl) return null;
|
|
59633
59594
|
try {
|
|
59634
|
-
const raw = await readFile31(
|
|
59595
|
+
const raw = await readFile31(join54(codexHome, FALLBACK_FILENAME), "utf-8");
|
|
59635
59596
|
const fileResult = CodexCredentialsFileSchema.safeParse(JSON.parse(raw));
|
|
59636
59597
|
if (fileResult.success) {
|
|
59637
59598
|
const entry = findEntry2(fileResult.data, serverName, serverUrl);
|
|
@@ -61828,7 +61789,7 @@ var HealthMetrics = class {
|
|
|
61828
61789
|
// src/services/metrics/stall-profiler.ts
|
|
61829
61790
|
import { mkdir as mkdir20, readdir as readdir18, unlink as unlink14, writeFile as writeFile21 } from "fs/promises";
|
|
61830
61791
|
import { Session as Session2 } from "inspector/promises";
|
|
61831
|
-
import { join as
|
|
61792
|
+
import { join as join55 } from "path";
|
|
61832
61793
|
import { monitorEventLoopDelay as monitorEventLoopDelay3 } from "perf_hooks";
|
|
61833
61794
|
function hasProfile2(value) {
|
|
61834
61795
|
return typeof value === "object" && value !== null && "profile" in value;
|
|
@@ -61938,7 +61899,7 @@ var StallProfiler = class {
|
|
|
61938
61899
|
const isoTs = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
61939
61900
|
const stallRounded = Math.round(stallMs);
|
|
61940
61901
|
const filename = `stall-${isoTs}-${stallRounded}ms.cpuprofile`;
|
|
61941
|
-
const profilePath =
|
|
61902
|
+
const profilePath = join55(this.#outDir, filename);
|
|
61942
61903
|
await mkdir20(this.#outDir, { recursive: true });
|
|
61943
61904
|
await writeFile21(profilePath, JSON.stringify(stopResult.profile));
|
|
61944
61905
|
this.#log({
|
|
@@ -61972,7 +61933,7 @@ var StallProfiler = class {
|
|
|
61972
61933
|
const entries = await readdir18(this.#outDir);
|
|
61973
61934
|
const toDelete = planProfileEviction(entries, this.#maxProfiles, STALL_PROFILE_PREFIX);
|
|
61974
61935
|
if (toDelete.length === 0) return;
|
|
61975
|
-
const results = await Promise.allSettled(toDelete.map((f2) => unlink14(
|
|
61936
|
+
const results = await Promise.allSettled(toDelete.map((f2) => unlink14(join55(this.#outDir, f2))));
|
|
61976
61937
|
const failures = results.filter((r) => r.status === "rejected").length;
|
|
61977
61938
|
if (failures > 0) {
|
|
61978
61939
|
this.#log({
|
|
@@ -63031,7 +62992,7 @@ function joinPoint(doc2, pos, dir = -1) {
|
|
|
63031
62992
|
pos = dir < 0 ? $pos.before(d) : $pos.after(d);
|
|
63032
62993
|
}
|
|
63033
62994
|
}
|
|
63034
|
-
function
|
|
62995
|
+
function join56(tr2, pos, depth) {
|
|
63035
62996
|
let convertNewlines = null;
|
|
63036
62997
|
let { linebreakReplacement } = tr2.doc.type.schema;
|
|
63037
62998
|
let $before = tr2.doc.resolve(pos - depth), beforeType = $before.node().type;
|
|
@@ -63717,7 +63678,7 @@ var Transform = class {
|
|
|
63717
63678
|
last and first siblings are also joined, and so on.
|
|
63718
63679
|
*/
|
|
63719
63680
|
join(pos, depth = 1) {
|
|
63720
|
-
|
|
63681
|
+
join56(this, pos, depth);
|
|
63721
63682
|
return this;
|
|
63722
63683
|
}
|
|
63723
63684
|
/**
|
|
@@ -84131,10 +84092,11 @@ var PreWarmManager = class {
|
|
|
84131
84092
|
#cwd = null;
|
|
84132
84093
|
#generation = 0;
|
|
84133
84094
|
/**
|
|
84134
|
-
*
|
|
84135
|
-
*
|
|
84136
|
-
*
|
|
84137
|
-
*
|
|
84095
|
+
* The harness-bound epoch captured from `subprocess.harnessEpoch` after the
|
|
84096
|
+
* spawn dispatcher minted+bound it (NOT minted here). Surfaced on
|
|
84097
|
+
* `PreWarmClaim` so the adopting Thread registers the SAME epoch the harness
|
|
84098
|
+
* fence checks (AGENTS.md Invariant #2b). Reset to `null` on
|
|
84099
|
+
* `#teardown()` / `dispose()`.
|
|
84138
84100
|
*/
|
|
84139
84101
|
#epoch = null;
|
|
84140
84102
|
#disposed = false;
|
|
@@ -84358,7 +84320,7 @@ var PreWarmManager = class {
|
|
|
84358
84320
|
this.#canUseToolSlot = null;
|
|
84359
84321
|
if (this.#epoch === null) {
|
|
84360
84322
|
throw new Error(
|
|
84361
|
-
`PreWarmManager.claim() invariant violated: warm subprocess has no
|
|
84323
|
+
`PreWarmManager.claim() invariant violated: warm subprocess has no harness epoch (status=${this.#status}, cwd=${cwd})`
|
|
84362
84324
|
);
|
|
84363
84325
|
}
|
|
84364
84326
|
const epoch = this.#epoch;
|
|
@@ -84402,7 +84364,7 @@ var PreWarmManager = class {
|
|
|
84402
84364
|
return;
|
|
84403
84365
|
}
|
|
84404
84366
|
const generation = ++this.#generation;
|
|
84405
|
-
this.#epoch =
|
|
84367
|
+
this.#epoch = null;
|
|
84406
84368
|
this.#cwd = cwd;
|
|
84407
84369
|
this.#status = "spawning";
|
|
84408
84370
|
const slot = createEventSlot();
|
|
@@ -84440,6 +84402,7 @@ var PreWarmManager = class {
|
|
|
84440
84402
|
settings,
|
|
84441
84403
|
cwd
|
|
84442
84404
|
);
|
|
84405
|
+
this.#epoch = this.#subprocess.harnessEpoch;
|
|
84443
84406
|
emitStep("spawn_call", performance.now() - tSpawnCall);
|
|
84444
84407
|
this.#startWarmTimer(generation);
|
|
84445
84408
|
}
|
|
@@ -90542,6 +90505,7 @@ function buildCodexThreadConfigOverrides(writableRoots = []) {
|
|
|
90542
90505
|
const sandboxWorkspaceWrite = writableRoots.length > 0 ? { network_access: true, writable_roots: [...writableRoots] } : { network_access: true };
|
|
90543
90506
|
return {
|
|
90544
90507
|
...CODEX_THREAD_CONFIG_OVERRIDES,
|
|
90508
|
+
...buildCodexShellCodexHomeConfigOverride(),
|
|
90545
90509
|
sandbox_workspace_write: sandboxWorkspaceWrite
|
|
90546
90510
|
};
|
|
90547
90511
|
}
|
|
@@ -91907,6 +91871,10 @@ var CodexAgentSubprocess = class _CodexAgentSubprocess {
|
|
|
91907
91871
|
reason: "Codex prewarm not supported; taskId is set at thread/start only."
|
|
91908
91872
|
});
|
|
91909
91873
|
}
|
|
91874
|
+
/** Codex fences its harness MCP via the HTTP `x-shipyard-generation` header / per-runner bearer, not an in-process epoch. */
|
|
91875
|
+
get harnessEpoch() {
|
|
91876
|
+
return null;
|
|
91877
|
+
}
|
|
91910
91878
|
setOnCwdChanged(handler) {
|
|
91911
91879
|
this.#cwdHandler = handler;
|
|
91912
91880
|
}
|
|
@@ -92424,6 +92392,10 @@ function createCodexSubprocessFacade(options, log) {
|
|
|
92424
92392
|
}
|
|
92425
92393
|
pending.push({ kind: "setHarnessTaskId", taskId });
|
|
92426
92394
|
},
|
|
92395
|
+
/** Codex fences via HTTP header / per-runner bearer — always null (delegated for forward-compat). */
|
|
92396
|
+
get harnessEpoch() {
|
|
92397
|
+
return inner ? inner.harnessEpoch : null;
|
|
92398
|
+
},
|
|
92427
92399
|
setOnCwdChanged(handler) {
|
|
92428
92400
|
cwdHandler = handler;
|
|
92429
92401
|
if (inner) {
|
|
@@ -92502,6 +92474,10 @@ function createCodexFacadeApiBuilder(d) {
|
|
|
92502
92474
|
reconnectMcpServer: (serverName) => codexFacadeReconnectMcpServer(state, serverName),
|
|
92503
92475
|
stopBackgroundTask: (taskId) => codexFacadeStopBackgroundTask(state, taskId),
|
|
92504
92476
|
setHarnessTaskId: (taskId) => codexFacadeSetHarnessTaskId(state, taskId),
|
|
92477
|
+
/** Codex fences its harness via HTTP header / per-runner bearer — delegate (always null today). */
|
|
92478
|
+
get harnessEpoch() {
|
|
92479
|
+
return state.inner?.harnessEpoch ?? null;
|
|
92480
|
+
},
|
|
92505
92481
|
setOnCwdChanged: (handler) => codexFacadeSetOnCwdChanged(state, handler),
|
|
92506
92482
|
getContextUsage: () => state.inner?.getContextUsage() ?? Promise.resolve(null),
|
|
92507
92483
|
restartWithExtendedSandbox: (grant) => codexFacadeRestartWithExtendedSandbox(state, grant),
|
|
@@ -92962,15 +92938,15 @@ function bindToolToMcpServer(server, tool) {
|
|
|
92962
92938
|
|
|
92963
92939
|
// src/services/serve-factory/pure-helpers.ts
|
|
92964
92940
|
function buildCodexSpawnArgs(args) {
|
|
92965
|
-
const
|
|
92941
|
+
const spawn16 = rewriteElectronNodeScriptSpawn({
|
|
92966
92942
|
binary: args.binary,
|
|
92967
92943
|
argv: args.argv,
|
|
92968
92944
|
env: args.env
|
|
92969
92945
|
});
|
|
92970
92946
|
return {
|
|
92971
|
-
binary:
|
|
92972
|
-
argv:
|
|
92973
|
-
...
|
|
92947
|
+
binary: spawn16.binary,
|
|
92948
|
+
argv: spawn16.argv,
|
|
92949
|
+
...spawn16.env !== void 0 ? { env: spawn16.env } : {},
|
|
92974
92950
|
stripEnv: args.stripEnv
|
|
92975
92951
|
};
|
|
92976
92952
|
}
|
|
@@ -93107,14 +93083,14 @@ function createCodexFacadeSubsystem(d) {
|
|
|
93107
93083
|
const engine = new CodexEngineSingleton({
|
|
93108
93084
|
resolveSpawnConfig: async () => {
|
|
93109
93085
|
const binaryPath = await codexProfile.spawn.binaryResolver() ?? "codex";
|
|
93110
|
-
const
|
|
93086
|
+
const spawn16 = rewriteElectronNodeScriptSpawn({
|
|
93111
93087
|
binary: binaryPath,
|
|
93112
93088
|
argv: ["app-server"]
|
|
93113
93089
|
});
|
|
93114
93090
|
return {
|
|
93115
|
-
binaryPath:
|
|
93116
|
-
args:
|
|
93117
|
-
...
|
|
93091
|
+
binaryPath: spawn16.binary,
|
|
93092
|
+
args: spawn16.argv,
|
|
93093
|
+
...spawn16.env !== void 0 ? { env: spawn16.env } : {},
|
|
93118
93094
|
stripEnv: codexProfile.spawn.envStrip
|
|
93119
93095
|
};
|
|
93120
93096
|
},
|
|
@@ -93616,12 +93592,12 @@ function handleTaskStoreBroadcast(deps, event) {
|
|
|
93616
93592
|
}
|
|
93617
93593
|
|
|
93618
93594
|
// src/services/serve-factory/viz-preview.ts
|
|
93619
|
-
import { join as
|
|
93595
|
+
import { join as join59 } from "path";
|
|
93620
93596
|
|
|
93621
93597
|
// src/services/harness/visualization-file-watcher.ts
|
|
93622
93598
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
93623
93599
|
import { mkdir as mkdir21, readFile as readFile32, rename as rename17, writeFile as writeFile22 } from "fs/promises";
|
|
93624
|
-
import { basename as basename10, dirname as dirname23, join as
|
|
93600
|
+
import { basename as basename10, dirname as dirname23, join as join57 } from "path";
|
|
93625
93601
|
var PREVIEW_DEFAULT_W = 1200;
|
|
93626
93602
|
var PREVIEW_DEFAULT_H = 800;
|
|
93627
93603
|
function previewDataToLoroValue(data) {
|
|
@@ -93832,7 +93808,7 @@ var VisualizationFileWatcher = class {
|
|
|
93832
93808
|
}
|
|
93833
93809
|
case "persist_registry":
|
|
93834
93810
|
await atomicWrite2(
|
|
93835
|
-
|
|
93811
|
+
join57(this.#deps.vizDir, effect.taskId, "registry.json"),
|
|
93836
93812
|
JSON.stringify(effect.data, null, 2)
|
|
93837
93813
|
);
|
|
93838
93814
|
break;
|
|
@@ -94100,7 +94076,7 @@ var VisualizationFileWatcher = class {
|
|
|
94100
94076
|
|
|
94101
94077
|
// src/services/harness/visualization-registry.ts
|
|
94102
94078
|
import { createHash as createHash5 } from "crypto";
|
|
94103
|
-
import { join as
|
|
94079
|
+
import { join as join58 } from "path";
|
|
94104
94080
|
function hashContent(content) {
|
|
94105
94081
|
return createHash5("sha256").update(content).digest("hex");
|
|
94106
94082
|
}
|
|
@@ -94114,7 +94090,7 @@ var VisualizationRegistry = class {
|
|
|
94114
94090
|
return null;
|
|
94115
94091
|
}
|
|
94116
94092
|
const ext = vizFileExtension(vizType);
|
|
94117
|
-
const filePath =
|
|
94093
|
+
const filePath = join58(vizDir, taskId, `${slug}${ext}`);
|
|
94118
94094
|
const contentHash3 = hashContent(content);
|
|
94119
94095
|
const viz = {
|
|
94120
94096
|
slug,
|
|
@@ -94265,7 +94241,7 @@ var VisualizationRegistry = class {
|
|
|
94265
94241
|
// src/services/serve-factory/viz-preview.ts
|
|
94266
94242
|
function createVizPreviewRegistry(deps) {
|
|
94267
94243
|
const { canvasRepo } = deps;
|
|
94268
|
-
const vizDir =
|
|
94244
|
+
const vizDir = join59(deps.dataDir, "visualizations");
|
|
94269
94245
|
const vizWatchers = /* @__PURE__ */ new Map();
|
|
94270
94246
|
function getOrCreateVizWatcher(taskId) {
|
|
94271
94247
|
const existing = vizWatchers.get(taskId);
|
|
@@ -95196,6 +95172,9 @@ import { fork } from "child_process";
|
|
|
95196
95172
|
import { randomUUID as cryptoRandomUUID } from "crypto";
|
|
95197
95173
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
95198
95174
|
|
|
95175
|
+
// src/services/session/agent-subprocess.ts
|
|
95176
|
+
var RUN_STATUS_ERROR_SUBTYPE = "run_status_error";
|
|
95177
|
+
|
|
95199
95178
|
// src/services/session/cursor-event-translator.ts
|
|
95200
95179
|
function createCursorTranslatorCtx(generation) {
|
|
95201
95180
|
return {
|
|
@@ -95445,7 +95424,7 @@ function classifyStatusErrorText(text) {
|
|
|
95445
95424
|
return {
|
|
95446
95425
|
type: "sdk_error",
|
|
95447
95426
|
error: "Cursor run error (status: ERROR)",
|
|
95448
|
-
errorSubtype:
|
|
95427
|
+
errorSubtype: RUN_STATUS_ERROR_SUBTYPE
|
|
95449
95428
|
};
|
|
95450
95429
|
}
|
|
95451
95430
|
const lower = text.toLowerCase();
|
|
@@ -95458,7 +95437,7 @@ function classifyStatusErrorText(text) {
|
|
|
95458
95437
|
if (lower.includes("payment") || lower.includes("billing") || lower.includes("insufficient credits") || lower.includes("subscription")) {
|
|
95459
95438
|
return { type: "billing_error" };
|
|
95460
95439
|
}
|
|
95461
|
-
return { type: "sdk_error", error: text, errorSubtype:
|
|
95440
|
+
return { type: "sdk_error", error: text, errorSubtype: RUN_STATUS_ERROR_SUBTYPE };
|
|
95462
95441
|
}
|
|
95463
95442
|
function translateStatusMessage(msg) {
|
|
95464
95443
|
if (msg.status !== "ERROR") return [];
|
|
@@ -95611,6 +95590,9 @@ function buildCursorCompactionCompleted(preTokens) {
|
|
|
95611
95590
|
...preTokens !== void 0 ? { preTokens } : {}
|
|
95612
95591
|
};
|
|
95613
95592
|
}
|
|
95593
|
+
function toolExecutionSubprocessEvent(phase) {
|
|
95594
|
+
return { type: phase === "started" ? "tool_execution_started" : "tool_execution_settled" };
|
|
95595
|
+
}
|
|
95614
95596
|
|
|
95615
95597
|
// src/services/session/cursor-run-result.ts
|
|
95616
95598
|
var RunGitBranchInfoSchema = external_exports.object({
|
|
@@ -96475,6 +96457,10 @@ var CursorAgentSubprocess = class {
|
|
|
96475
96457
|
token: this.#harnessToken
|
|
96476
96458
|
});
|
|
96477
96459
|
}
|
|
96460
|
+
/** Cursor fences its harness MCP via the HTTP `x-shipyard-generation` header / per-runner bearer, not an in-process epoch. */
|
|
96461
|
+
get harnessEpoch() {
|
|
96462
|
+
return null;
|
|
96463
|
+
}
|
|
96478
96464
|
setOnCwdChanged(_handler) {
|
|
96479
96465
|
}
|
|
96480
96466
|
async rollback(_numTurns) {
|
|
@@ -96528,7 +96514,7 @@ var CursorAgentSubprocess = class {
|
|
|
96528
96514
|
* (connectrpc streaming requires HTTP/2), so the agent reaches the model
|
|
96529
96515
|
* and then errors mid-run. Default HTTP/2 is correct; the run completes.
|
|
96530
96516
|
*/
|
|
96531
|
-
env: buildNodeSpawnEnv(process.env)
|
|
96517
|
+
env: { ...buildNodeSpawnEnv(process.env), ...buildAgentSubprocessCodexHomeEnv() }
|
|
96532
96518
|
});
|
|
96533
96519
|
this.#child = child;
|
|
96534
96520
|
const tracker = this.#pidTracker;
|
|
@@ -96916,6 +96902,9 @@ var CursorAgentSubprocess = class {
|
|
|
96916
96902
|
case "stream_activity":
|
|
96917
96903
|
this.#onEvent({ type: msg.kind });
|
|
96918
96904
|
return;
|
|
96905
|
+
case "tool_execution":
|
|
96906
|
+
this.#onEvent(toolExecutionSubprocessEvent(msg.phase));
|
|
96907
|
+
return;
|
|
96919
96908
|
case "user_message_persisted": {
|
|
96920
96909
|
logger.info({
|
|
96921
96910
|
event: "cursor_user_message_persisted",
|
|
@@ -97742,7 +97731,7 @@ function createShipyardResolver() {
|
|
|
97742
97731
|
|
|
97743
97732
|
// src/services/stack-detection.ts
|
|
97744
97733
|
import { access as access6 } from "fs/promises";
|
|
97745
|
-
import { join as
|
|
97734
|
+
import { join as join61 } from "path";
|
|
97746
97735
|
var STACK_TIMEOUT_MS = TIMEOUT_MS;
|
|
97747
97736
|
async function defaultIsCommandAvailable(command2) {
|
|
97748
97737
|
try {
|
|
@@ -97784,7 +97773,7 @@ async function detectStack(cwd, currentBranch, deps = {}) {
|
|
|
97784
97773
|
async function detectGraphite(cwd, currentBranch, isCommandAvailable, fileExists2, exec, getDefaultBranch2) {
|
|
97785
97774
|
const gtAvailable = await isCommandAvailable("gt");
|
|
97786
97775
|
if (!gtAvailable) return null;
|
|
97787
|
-
const markerPresent = await fileExists2(
|
|
97776
|
+
const markerPresent = await fileExists2(join61(cwd, ".git", ".graphite_repo_config"));
|
|
97788
97777
|
if (!markerPresent) return null;
|
|
97789
97778
|
const ancestor = await exec("gt", ["parent"], cwd).then((s2) => normalizeBranchName(s2)).catch(() => null);
|
|
97790
97779
|
const childrenRaw = await exec("gt", ["children"], cwd).then((s2) => s2).catch(() => "");
|
|
@@ -97797,7 +97786,7 @@ async function detectGraphite(cwd, currentBranch, isCommandAvailable, fileExists
|
|
|
97797
97786
|
return { ancestor: resolvedAncestor, descendants };
|
|
97798
97787
|
}
|
|
97799
97788
|
async function detectJujutsu(cwd, fileExists2, exec) {
|
|
97800
|
-
const jjPresent = await fileExists2(
|
|
97789
|
+
const jjPresent = await fileExists2(join61(cwd, ".jj"));
|
|
97801
97790
|
if (!jjPresent) return null;
|
|
97802
97791
|
const ancestorRaw = await exec(
|
|
97803
97792
|
"jj",
|
|
@@ -97846,7 +97835,7 @@ function parseJjLines(raw) {
|
|
|
97846
97835
|
|
|
97847
97836
|
// src/services/storage/annotation-store.ts
|
|
97848
97837
|
import { mkdir as mkdir22, readFile as readFile34 } from "fs/promises";
|
|
97849
|
-
import { dirname as dirname25, join as
|
|
97838
|
+
import { dirname as dirname25, join as join62 } from "path";
|
|
97850
97839
|
var LegacyBaseFields = external_exports.object({
|
|
97851
97840
|
commentId: external_exports.string(),
|
|
97852
97841
|
body: external_exports.string(),
|
|
@@ -97873,7 +97862,7 @@ var LegacyPlanStoreSchema = external_exports.object({
|
|
|
97873
97862
|
versions: external_exports.array(PlanVersionZodSchema)
|
|
97874
97863
|
});
|
|
97875
97864
|
function buildAnnotationStore(dataDir) {
|
|
97876
|
-
const baseDir =
|
|
97865
|
+
const baseDir = join62(dataDir, "annotations");
|
|
97877
97866
|
const cache2 = /* @__PURE__ */ new Map();
|
|
97878
97867
|
const listeners = /* @__PURE__ */ new Set();
|
|
97879
97868
|
const writeQueues = /* @__PURE__ */ new Map();
|
|
@@ -97886,7 +97875,7 @@ function buildAnnotationStore(dataDir) {
|
|
|
97886
97875
|
}
|
|
97887
97876
|
}
|
|
97888
97877
|
function filePath(taskId) {
|
|
97889
|
-
return
|
|
97878
|
+
return join62(baseDir, `${taskId}.json`);
|
|
97890
97879
|
}
|
|
97891
97880
|
function emptyData() {
|
|
97892
97881
|
return {
|
|
@@ -97951,9 +97940,9 @@ function buildAnnotationStore(dataDir) {
|
|
|
97951
97940
|
}
|
|
97952
97941
|
async function migrateLegacyFiles(taskId) {
|
|
97953
97942
|
const [diffRaw, previewRaw, planRaw] = await Promise.all([
|
|
97954
|
-
readLegacyFile(
|
|
97955
|
-
readLegacyFile(
|
|
97956
|
-
readLegacyFile(
|
|
97943
|
+
readLegacyFile(join62(dataDir, "diff-review", `${taskId}.json`)),
|
|
97944
|
+
readLegacyFile(join62(dataDir, "preview-annotations", `${taskId}.json`)),
|
|
97945
|
+
readLegacyFile(join62(dataDir, "plan-comments", `${taskId}.json`))
|
|
97957
97946
|
]);
|
|
97958
97947
|
if (diffRaw === null && previewRaw === null && planRaw === null) {
|
|
97959
97948
|
return null;
|
|
@@ -98156,7 +98145,7 @@ function buildAnnotationStore(dataDir) {
|
|
|
98156
98145
|
|
|
98157
98146
|
// src/services/storage/asset-store.ts
|
|
98158
98147
|
import { mkdir as mkdir23, readFile as readFile35, rename as rename18, writeFile as writeFile23 } from "fs/promises";
|
|
98159
|
-
import { join as
|
|
98148
|
+
import { join as join63 } from "path";
|
|
98160
98149
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
98161
98150
|
function isValidAssetId(id) {
|
|
98162
98151
|
return UUID_RE.test(id);
|
|
@@ -98182,10 +98171,10 @@ function buildAssetStore(assetsDir) {
|
|
|
98182
98171
|
}
|
|
98183
98172
|
}
|
|
98184
98173
|
function binaryPath(assetId) {
|
|
98185
|
-
return
|
|
98174
|
+
return join63(assetsDir, `${assetId}.bin`);
|
|
98186
98175
|
}
|
|
98187
98176
|
function metaPath(assetId) {
|
|
98188
|
-
return
|
|
98177
|
+
return join63(assetsDir, `${assetId}.meta.json`);
|
|
98189
98178
|
}
|
|
98190
98179
|
function parseMeta(raw) {
|
|
98191
98180
|
return AssetMetadataSchema.parse(JSON.parse(raw));
|
|
@@ -98209,8 +98198,8 @@ function buildAssetStore(assetsDir) {
|
|
|
98209
98198
|
size: data.byteLength,
|
|
98210
98199
|
createdAt: Date.now()
|
|
98211
98200
|
};
|
|
98212
|
-
const tmpBin =
|
|
98213
|
-
const tmpMeta =
|
|
98201
|
+
const tmpBin = join63(assetsDir, `${assetId}.bin.tmp`);
|
|
98202
|
+
const tmpMeta = join63(assetsDir, `${assetId}.meta.tmp`);
|
|
98214
98203
|
await writeFile23(tmpBin, data);
|
|
98215
98204
|
await writeFile23(tmpMeta, JSON.stringify(metadata), "utf-8");
|
|
98216
98205
|
await rename18(tmpBin, binaryPath(assetId));
|
|
@@ -98435,7 +98424,7 @@ function buildCredentialsVaultStore(filePath, corruptionLogger) {
|
|
|
98435
98424
|
|
|
98436
98425
|
// src/services/storage/deliverable-store.ts
|
|
98437
98426
|
import { mkdir as mkdir25, readFile as readFile37 } from "fs/promises";
|
|
98438
|
-
import { dirname as dirname27, join as
|
|
98427
|
+
import { dirname as dirname27, join as join64 } from "path";
|
|
98439
98428
|
var DELIVERABLE_STORE_VERSION = 1;
|
|
98440
98429
|
var DeliverableStoreSchema = external_exports.object({
|
|
98441
98430
|
schemaVersion: external_exports.number(),
|
|
@@ -98449,7 +98438,7 @@ function migrateDeliverableStore(raw) {
|
|
|
98449
98438
|
return { schemaVersion: DELIVERABLE_STORE_VERSION, records: [] };
|
|
98450
98439
|
}
|
|
98451
98440
|
function buildDeliverableStore(dataDir) {
|
|
98452
|
-
const baseDir =
|
|
98441
|
+
const baseDir = join64(dataDir, "deliverables");
|
|
98453
98442
|
const cache2 = /* @__PURE__ */ new Map();
|
|
98454
98443
|
const listeners = /* @__PURE__ */ new Set();
|
|
98455
98444
|
const writeQueues = /* @__PURE__ */ new Map();
|
|
@@ -98462,7 +98451,7 @@ function buildDeliverableStore(dataDir) {
|
|
|
98462
98451
|
}
|
|
98463
98452
|
}
|
|
98464
98453
|
function filePath(taskId) {
|
|
98465
|
-
return
|
|
98454
|
+
return join64(baseDir, `${taskId}.json`);
|
|
98466
98455
|
}
|
|
98467
98456
|
function emptyData() {
|
|
98468
98457
|
return { schemaVersion: DELIVERABLE_STORE_VERSION, records: [] };
|
|
@@ -98602,7 +98591,7 @@ function buildDeliverableStore(dataDir) {
|
|
|
98602
98591
|
|
|
98603
98592
|
// src/services/storage/jsonl-conversation-store.ts
|
|
98604
98593
|
import { appendFile as appendFile3, mkdir as mkdir26, open, readFile as readFile38, stat as stat16 } from "fs/promises";
|
|
98605
|
-
import { join as
|
|
98594
|
+
import { join as join65 } from "path";
|
|
98606
98595
|
var StoredMessageSchema = MessageSchema.omit({ seqNo: true, channelId: true });
|
|
98607
98596
|
function logPerf(entry) {
|
|
98608
98597
|
try {
|
|
@@ -98612,12 +98601,12 @@ function logPerf(entry) {
|
|
|
98612
98601
|
}
|
|
98613
98602
|
}
|
|
98614
98603
|
function buildJsonlConversationStore(dataDir, opts = {}) {
|
|
98615
|
-
const channelsDir =
|
|
98604
|
+
const channelsDir = join65(dataDir, "channels");
|
|
98616
98605
|
const seqCounters = /* @__PURE__ */ new Map();
|
|
98617
98606
|
const channelQueues = /* @__PURE__ */ new Map();
|
|
98618
98607
|
const readCache = /* @__PURE__ */ new Map();
|
|
98619
98608
|
function channelPath(channelId) {
|
|
98620
|
-
return
|
|
98609
|
+
return join65(channelsDir, `${channelId}.jsonl`);
|
|
98621
98610
|
}
|
|
98622
98611
|
async function ensureDir() {
|
|
98623
98612
|
await mkdir26(channelsDir, { recursive: true });
|
|
@@ -99389,7 +99378,7 @@ function buildProjectsStore(filePath) {
|
|
|
99389
99378
|
|
|
99390
99379
|
// src/services/storage/rate-limit-store.ts
|
|
99391
99380
|
import { mkdir as mkdir28, readFile as readFile40 } from "fs/promises";
|
|
99392
|
-
import { dirname as dirname29, join as
|
|
99381
|
+
import { dirname as dirname29, join as join66 } from "path";
|
|
99393
99382
|
var RATE_LIMIT_STORE_VERSION = 4;
|
|
99394
99383
|
var RateLimitRecordSchema = external_exports.object({
|
|
99395
99384
|
info: RateLimitInfoSchema,
|
|
@@ -99428,7 +99417,7 @@ function isWindowKey(x2) {
|
|
|
99428
99417
|
return x2 !== void 0 && WINDOW_KEYS.has(x2);
|
|
99429
99418
|
}
|
|
99430
99419
|
async function buildRateLimitStore(dataDir, opts) {
|
|
99431
|
-
const filePath =
|
|
99420
|
+
const filePath = join66(dataDir, "rate-limits.json");
|
|
99432
99421
|
const lockPath2 = `${filePath}.lock`;
|
|
99433
99422
|
const initial = await loadStoreFile(filePath, opts.logger);
|
|
99434
99423
|
const records = { ...initial.records };
|
|
@@ -99681,7 +99670,7 @@ async function atomicWrite4(filePath, data) {
|
|
|
99681
99670
|
}
|
|
99682
99671
|
|
|
99683
99672
|
// src/services/storage/schedule-store.ts
|
|
99684
|
-
import { join as
|
|
99673
|
+
import { join as join67 } from "path";
|
|
99685
99674
|
|
|
99686
99675
|
// src/services/storage/json-document-store.ts
|
|
99687
99676
|
import { mkdir as mkdir29, readFile as readFile41 } from "fs/promises";
|
|
@@ -99872,7 +99861,7 @@ function buildScheduleStore(dataDir) {
|
|
|
99872
99861
|
const store = buildJsonDocumentStore({
|
|
99873
99862
|
storeName: "schedules",
|
|
99874
99863
|
docType: "schedule",
|
|
99875
|
-
filePath:
|
|
99864
|
+
filePath: join67(dataDir, "schedules.json"),
|
|
99876
99865
|
recordSchema: ScheduleRecordSchema,
|
|
99877
99866
|
currentVersion: SCHEDULE_STORE_VERSION,
|
|
99878
99867
|
migrate(raw) {
|
|
@@ -99911,7 +99900,7 @@ function buildScheduleStore(dataDir) {
|
|
|
99911
99900
|
|
|
99912
99901
|
// src/services/storage/session-persistence.ts
|
|
99913
99902
|
import { mkdir as mkdir30, readdir as readdir19, readFile as readFile42, rm as rm12 } from "fs/promises";
|
|
99914
|
-
import { join as
|
|
99903
|
+
import { join as join68 } from "path";
|
|
99915
99904
|
var PersistedSessionSchema = external_exports.object({
|
|
99916
99905
|
sessionId: external_exports.string(),
|
|
99917
99906
|
channelId: external_exports.string(),
|
|
@@ -99968,9 +99957,9 @@ async function loadOneSessionFile(originalPath, corruptionLogger) {
|
|
|
99968
99957
|
}
|
|
99969
99958
|
}
|
|
99970
99959
|
function buildSessionPersistence(dataDir, corruptionLogger) {
|
|
99971
|
-
const channelsDir =
|
|
99960
|
+
const channelsDir = join68(dataDir, "channels");
|
|
99972
99961
|
function sessionPath(channelId) {
|
|
99973
|
-
return
|
|
99962
|
+
return join68(channelsDir, `${channelId}.session.json`);
|
|
99974
99963
|
}
|
|
99975
99964
|
async function ensureDir() {
|
|
99976
99965
|
await mkdir30(channelsDir, { recursive: true });
|
|
@@ -100028,7 +100017,7 @@ function buildSessionPersistence(dataDir, corruptionLogger) {
|
|
|
100028
100017
|
if (i >= sessionEntries.length) return;
|
|
100029
100018
|
const entry = sessionEntries[i];
|
|
100030
100019
|
if (!entry) return;
|
|
100031
|
-
results[i] = await loadOneSessionFile(
|
|
100020
|
+
results[i] = await loadOneSessionFile(join68(channelsDir, entry), corruptionLogger);
|
|
100032
100021
|
}
|
|
100033
100022
|
}
|
|
100034
100023
|
const workers = Array.from(
|
|
@@ -100042,12 +100031,12 @@ function buildSessionPersistence(dataDir, corruptionLogger) {
|
|
|
100042
100031
|
}
|
|
100043
100032
|
|
|
100044
100033
|
// src/services/storage/template-store.ts
|
|
100045
|
-
import { join as
|
|
100034
|
+
import { join as join69 } from "path";
|
|
100046
100035
|
function buildTemplateStore(dataDir) {
|
|
100047
100036
|
const store = buildJsonDocumentStore({
|
|
100048
100037
|
storeName: "templates",
|
|
100049
100038
|
docType: "template",
|
|
100050
|
-
filePath:
|
|
100039
|
+
filePath: join69(dataDir, "templates.json"),
|
|
100051
100040
|
recordSchema: TaskTemplateRecordSchema,
|
|
100052
100041
|
currentVersion: TEMPLATE_STORE_VERSION,
|
|
100053
100042
|
migrate(raw) {
|
|
@@ -102743,6 +102732,20 @@ function emitSessionDeath(b2, event, _origin) {
|
|
|
102743
102732
|
function isCleanExit(event) {
|
|
102744
102733
|
return event.exitCode === 0 && (event.signal === null || event.signal === void 0);
|
|
102745
102734
|
}
|
|
102735
|
+
function stallHeartbeatEffect(event) {
|
|
102736
|
+
switch (event.type) {
|
|
102737
|
+
case "step_progress":
|
|
102738
|
+
return { type: "reset_stall_timer", firstStep: true, toolExecution: "none" };
|
|
102739
|
+
case "stream_activity":
|
|
102740
|
+
return { type: "reset_stall_timer", firstStep: false, toolExecution: "none" };
|
|
102741
|
+
case "tool_execution_started":
|
|
102742
|
+
return { type: "reset_stall_timer", firstStep: false, toolExecution: "latch" };
|
|
102743
|
+
case "tool_execution_settled":
|
|
102744
|
+
return { type: "reset_stall_timer", firstStep: false, toolExecution: "unlatch" };
|
|
102745
|
+
default:
|
|
102746
|
+
return null;
|
|
102747
|
+
}
|
|
102748
|
+
}
|
|
102746
102749
|
function handleSpawning(snapshot, event) {
|
|
102747
102750
|
const b2 = createEffectBuilder();
|
|
102748
102751
|
if (event.type === "init_received") {
|
|
@@ -102833,8 +102836,9 @@ function handleSpawning(snapshot, event) {
|
|
|
102833
102836
|
effects: b2.effects
|
|
102834
102837
|
};
|
|
102835
102838
|
}
|
|
102836
|
-
|
|
102837
|
-
|
|
102839
|
+
const heartbeat = stallHeartbeatEffect(event);
|
|
102840
|
+
if (heartbeat) {
|
|
102841
|
+
b2.effects.push(heartbeat);
|
|
102838
102842
|
return {
|
|
102839
102843
|
state: "spawning",
|
|
102840
102844
|
sessionId: snapshot.sessionId,
|
|
@@ -102862,6 +102866,28 @@ function handleSpawning(snapshot, event) {
|
|
|
102862
102866
|
}
|
|
102863
102867
|
return noop2(snapshot);
|
|
102864
102868
|
}
|
|
102869
|
+
function runningStallTimeout(sessionId, event) {
|
|
102870
|
+
const b2 = createEffectBuilder();
|
|
102871
|
+
b2.log("running", "resumable_idle", event.type);
|
|
102872
|
+
b2.effects.push({ type: "clear_pending_inputs" });
|
|
102873
|
+
b2.effects.push({ type: "clear_queue" });
|
|
102874
|
+
if (!event.recoveryArmed) {
|
|
102875
|
+
b2.taskStatus("input_required");
|
|
102876
|
+
b2.emitError(
|
|
102877
|
+
"Cursor stopped responding mid-run. Send a message to try again.",
|
|
102878
|
+
"sdk_error",
|
|
102879
|
+
"run_stall_timeout"
|
|
102880
|
+
);
|
|
102881
|
+
}
|
|
102882
|
+
b2.effects.push({ type: "kill_runner" });
|
|
102883
|
+
return {
|
|
102884
|
+
state: "resumable_idle",
|
|
102885
|
+
sessionId,
|
|
102886
|
+
rewindAtMessageId: null,
|
|
102887
|
+
awaitingSetup: false,
|
|
102888
|
+
effects: b2.effects
|
|
102889
|
+
};
|
|
102890
|
+
}
|
|
102865
102891
|
function handleRunning(snapshot, event) {
|
|
102866
102892
|
const { sessionId } = snapshot;
|
|
102867
102893
|
const b2 = createEffectBuilder();
|
|
@@ -102987,8 +103013,9 @@ function handleRunning(snapshot, event) {
|
|
|
102987
103013
|
effects: b2.effects
|
|
102988
103014
|
};
|
|
102989
103015
|
}
|
|
102990
|
-
|
|
102991
|
-
|
|
103016
|
+
const heartbeat = stallHeartbeatEffect(event);
|
|
103017
|
+
if (heartbeat) {
|
|
103018
|
+
b2.effects.push(heartbeat);
|
|
102992
103019
|
return {
|
|
102993
103020
|
state: "running",
|
|
102994
103021
|
sessionId,
|
|
@@ -102998,23 +103025,7 @@ function handleRunning(snapshot, event) {
|
|
|
102998
103025
|
};
|
|
102999
103026
|
}
|
|
103000
103027
|
if (event.type === "run_stall_timeout") {
|
|
103001
|
-
|
|
103002
|
-
b2.effects.push({ type: "clear_pending_inputs" });
|
|
103003
|
-
b2.effects.push({ type: "clear_queue" });
|
|
103004
|
-
b2.taskStatus("input_required");
|
|
103005
|
-
b2.emitError(
|
|
103006
|
-
"Cursor stopped responding mid-run. Send a message to try again.",
|
|
103007
|
-
"sdk_error",
|
|
103008
|
-
"run_stall_timeout"
|
|
103009
|
-
);
|
|
103010
|
-
b2.effects.push({ type: "kill_runner" });
|
|
103011
|
-
return {
|
|
103012
|
-
state: "resumable_idle",
|
|
103013
|
-
sessionId,
|
|
103014
|
-
rewindAtMessageId: null,
|
|
103015
|
-
awaitingSetup: false,
|
|
103016
|
-
effects: b2.effects
|
|
103017
|
-
};
|
|
103028
|
+
return runningStallTimeout(sessionId, event);
|
|
103018
103029
|
}
|
|
103019
103030
|
return noop2(snapshot);
|
|
103020
103031
|
}
|
|
@@ -103175,6 +103186,21 @@ var AgentSessionManager = class {
|
|
|
103175
103186
|
* reset can only make the watchdog fire sooner, never mask a hang.
|
|
103176
103187
|
*/
|
|
103177
103188
|
#lastStreamActivityAt = null;
|
|
103189
|
+
/**
|
|
103190
|
+
* True while a Cursor tool call is in flight (between `tool_execution_started`
|
|
103191
|
+
* and `tool_execution_settled` FSM events). When true, `#chooseStallTimeoutMs`
|
|
103192
|
+
* returns `toolExecutionStallTimeoutMs` (if non-null) instead of the shorter
|
|
103193
|
+
* mid-run window. Cleared explicitly in three places: the `spawn` effect
|
|
103194
|
+
* case (a fresh subprocess generation starts unlatched), the `kill_runner`
|
|
103195
|
+
* effect case (a stall death must not leave stale latch state), and
|
|
103196
|
+
* `#manageTimers`'s leaving-active branch (a turn whose final tool never
|
|
103197
|
+
* settled must not leak the long window into a warm-resume turn).
|
|
103198
|
+
*
|
|
103199
|
+
* IMPORTANT: do NOT clear inside `#clearStallTimer` / `#restartStallTimer` —
|
|
103200
|
+
* `#restartStallTimer` calls `#clearStallTimer` before re-arming, so clearing
|
|
103201
|
+
* the latch there would wipe it on every timer reset.
|
|
103202
|
+
*/
|
|
103203
|
+
#toolExecutionInFlight = false;
|
|
103178
103204
|
constructor(config, initialSnapshot2) {
|
|
103179
103205
|
this.#config = config;
|
|
103180
103206
|
if (initialSnapshot2) {
|
|
@@ -103325,6 +103351,21 @@ var AgentSessionManager = class {
|
|
|
103325
103351
|
notifyStepProgress() {
|
|
103326
103352
|
this.#dispatch({ type: "step_progress" });
|
|
103327
103353
|
}
|
|
103354
|
+
/**
|
|
103355
|
+
* Notify the session manager of a tool-execution liveness edge. Phase
|
|
103356
|
+
* `'started'` arms the extended per-tool stall window (0→1 transition);
|
|
103357
|
+
* `'settled'` reverts to the shorter mid-run window (1→0 transition).
|
|
103358
|
+
* No-op when the watchdog is disabled (Claude/Codex set
|
|
103359
|
+
* `toolExecutionStallTimeoutMs: null`).
|
|
103360
|
+
*/
|
|
103361
|
+
notifyToolExecution(phase) {
|
|
103362
|
+
if (this.#config.toolExecutionStallTimeoutMs === null) {
|
|
103363
|
+
return;
|
|
103364
|
+
}
|
|
103365
|
+
this.#dispatch(
|
|
103366
|
+
phase === "started" ? { type: "tool_execution_started" } : { type: "tool_execution_settled" }
|
|
103367
|
+
);
|
|
103368
|
+
}
|
|
103328
103369
|
/**
|
|
103329
103370
|
* Heartbeat for streamed output (assistant/thinking/tool deltas) that proves
|
|
103330
103371
|
* the run is alive mid-step. Resets the stall watchdog WITHOUT retiring the
|
|
@@ -103398,6 +103439,7 @@ var AgentSessionManager = class {
|
|
|
103398
103439
|
this.#lastSpawnAtMs = Date.now();
|
|
103399
103440
|
this.#firstStepReceived = false;
|
|
103400
103441
|
this.#lastStreamActivityAt = null;
|
|
103442
|
+
this.#toolExecutionInFlight = false;
|
|
103401
103443
|
this.#config.onSpawn(effect.reason, initial?.content ?? []);
|
|
103402
103444
|
break;
|
|
103403
103445
|
}
|
|
@@ -103447,12 +103489,10 @@ var AgentSessionManager = class {
|
|
|
103447
103489
|
}
|
|
103448
103490
|
break;
|
|
103449
103491
|
case "reset_stall_timer":
|
|
103450
|
-
|
|
103451
|
-
this.#firstStepReceived = true;
|
|
103452
|
-
}
|
|
103453
|
-
this.#restartStallTimer();
|
|
103492
|
+
this.#applyResetStallTimer(effect);
|
|
103454
103493
|
break;
|
|
103455
103494
|
case "kill_runner":
|
|
103495
|
+
this.#toolExecutionInFlight = false;
|
|
103456
103496
|
this.#clearStallTimer();
|
|
103457
103497
|
this.#config.onKillRunner();
|
|
103458
103498
|
break;
|
|
@@ -103463,13 +103503,45 @@ var AgentSessionManager = class {
|
|
|
103463
103503
|
}
|
|
103464
103504
|
}
|
|
103465
103505
|
}
|
|
103506
|
+
/**
|
|
103507
|
+
* `firstStep` (a completed step boundary from `step_progress`) means the
|
|
103508
|
+
* cold start succeeded and we're in the mid-run cadence from now on — flip
|
|
103509
|
+
* the latch so `#chooseStallTimeoutMs` picks the mid-run budget for
|
|
103510
|
+
* subsequent arms. A `stream_activity` heartbeat (`firstStep: false`) only
|
|
103511
|
+
* proves liveness mid-step, so it resets the timer but leaves the cold
|
|
103512
|
+
* window in force until a real step lands.
|
|
103513
|
+
*
|
|
103514
|
+
* `toolExecution` updates the in-flight latch BEFORE restarting the timer
|
|
103515
|
+
* so the new window is chosen with the updated latch state: `'latch'` arms
|
|
103516
|
+
* the extended per-tool window, `'unlatch'` reverts to the mid-run window,
|
|
103517
|
+
* `'none'` leaves it unchanged (heartbeats like `step_progress` /
|
|
103518
|
+
* `stream_activity` must NOT unlatch mid-tool).
|
|
103519
|
+
*/
|
|
103520
|
+
#applyResetStallTimer(effect) {
|
|
103521
|
+
if (effect.firstStep) {
|
|
103522
|
+
this.#firstStepReceived = true;
|
|
103523
|
+
}
|
|
103524
|
+
if (effect.toolExecution === "latch") {
|
|
103525
|
+
this.#toolExecutionInFlight = true;
|
|
103526
|
+
} else if (effect.toolExecution === "unlatch") {
|
|
103527
|
+
this.#toolExecutionInFlight = false;
|
|
103528
|
+
}
|
|
103529
|
+
this.#restartStallTimer();
|
|
103530
|
+
}
|
|
103531
|
+
/**
|
|
103532
|
+
* Effect-ordering dependency: `log_transition` is emitted BEFORE
|
|
103533
|
+
* `kill_runner` in the `run_stall_timeout` effect list, so the latch (and
|
|
103534
|
+
* `#firstStepReceived`) are still in their at-fire values when this reads
|
|
103535
|
+
* them. Reordering those effects would silently misattribute `stallPhase`.
|
|
103536
|
+
*/
|
|
103466
103537
|
#telemeterRunStallTimeout(effect) {
|
|
103467
103538
|
if (effect.trigger !== "run_stall_timeout") return;
|
|
103539
|
+
const stallPhase = this.#toolExecutionInFlight ? "tool_execution" : this.#firstStepReceived ? "mid_run" : "first_step";
|
|
103468
103540
|
telemeter("run_stall_timeout", {
|
|
103469
103541
|
taskId: this.#config.taskId,
|
|
103470
103542
|
from: effect.from,
|
|
103471
103543
|
to: effect.to,
|
|
103472
|
-
stallPhase
|
|
103544
|
+
stallPhase,
|
|
103473
103545
|
stallTimeoutMs: this.#chooseStallTimeoutMs()
|
|
103474
103546
|
});
|
|
103475
103547
|
}
|
|
@@ -103510,16 +103582,21 @@ var AgentSessionManager = class {
|
|
|
103510
103582
|
}
|
|
103511
103583
|
} else {
|
|
103512
103584
|
this.#clearStallTimer();
|
|
103585
|
+
this.#toolExecutionInFlight = false;
|
|
103513
103586
|
}
|
|
103514
103587
|
}
|
|
103515
103588
|
/**
|
|
103516
|
-
* Pick the active stall timeout.
|
|
103517
|
-
*
|
|
103518
|
-
*
|
|
103519
|
-
*
|
|
103589
|
+
* Pick the active stall timeout. Priority order:
|
|
103590
|
+
* 1. Tool-execution latch active → `toolExecutionStallTimeoutMs` (if non-null).
|
|
103591
|
+
* 2. Before first step → `firstStepStallTimeoutMs` (if configured, else falls
|
|
103592
|
+
* back to `stallTimeoutMs`).
|
|
103593
|
+
* 3. After first step → `stallTimeoutMs`.
|
|
103520
103594
|
* Returns `null` to disable the watchdog entirely (Claude/Codex today).
|
|
103521
103595
|
*/
|
|
103522
103596
|
#chooseStallTimeoutMs() {
|
|
103597
|
+
if (this.#toolExecutionInFlight && this.#config.toolExecutionStallTimeoutMs !== null) {
|
|
103598
|
+
return this.#config.toolExecutionStallTimeoutMs;
|
|
103599
|
+
}
|
|
103523
103600
|
const mid = this.#config.stallTimeoutMs;
|
|
103524
103601
|
if (!this.#firstStepReceived) {
|
|
103525
103602
|
const cold = this.#config.firstStepStallTimeoutMs;
|
|
@@ -103549,8 +103626,12 @@ var AgentSessionManager = class {
|
|
|
103549
103626
|
this.#manageTimers();
|
|
103550
103627
|
return;
|
|
103551
103628
|
}
|
|
103552
|
-
|
|
103553
|
-
|
|
103629
|
+
let recoveryArmed = false;
|
|
103630
|
+
try {
|
|
103631
|
+
recoveryArmed = this.#config.onStallTimeout?.() ?? false;
|
|
103632
|
+
} finally {
|
|
103633
|
+
this.#dispatch({ type: "run_stall_timeout", recoveryArmed });
|
|
103634
|
+
}
|
|
103554
103635
|
}, timeoutMs);
|
|
103555
103636
|
}
|
|
103556
103637
|
#restartStallTimer() {
|
|
@@ -103578,10 +103659,12 @@ var AgentSessionManager = class {
|
|
|
103578
103659
|
// src/services/conversation/cursor-recovery-controller.ts
|
|
103579
103660
|
var MAX_SIGNAL_DEATH_RETRIES = 1;
|
|
103580
103661
|
var MAX_STALL_RESUME_RETRIES = 1;
|
|
103662
|
+
var MAX_CRASH_DEATH_RETRIES = 1;
|
|
103581
103663
|
var CursorRecoveryController = class {
|
|
103582
103664
|
#signalDeathRetries = 0;
|
|
103583
103665
|
#stallResumeRetries = 0;
|
|
103584
103666
|
#stallResumeArmed = false;
|
|
103667
|
+
#crashDeathRetries = 0;
|
|
103585
103668
|
#deps;
|
|
103586
103669
|
constructor(deps) {
|
|
103587
103670
|
this.#deps = deps;
|
|
@@ -103591,6 +103674,7 @@ var CursorRecoveryController = class {
|
|
|
103591
103674
|
this.#signalDeathRetries = 0;
|
|
103592
103675
|
this.#stallResumeRetries = 0;
|
|
103593
103676
|
this.#stallResumeArmed = false;
|
|
103677
|
+
this.#crashDeathRetries = 0;
|
|
103594
103678
|
}
|
|
103595
103679
|
/**
|
|
103596
103680
|
* Fired by the manager when the stall watchdog expires, BEFORE the
|
|
@@ -103599,10 +103683,14 @@ var CursorRecoveryController = class {
|
|
|
103599
103683
|
* banner and strands the orphaned turn until the user returns. Arm a one-shot
|
|
103600
103684
|
* resume so the imminent `kill_runner` death is resumed instead of surfaced.
|
|
103601
103685
|
* On repeat (cap exceeded) stay armed-off and let the existing banner run.
|
|
103686
|
+
*
|
|
103687
|
+
* Returns true when a recovery was armed — the manager passes this as
|
|
103688
|
+
* `recoveryArmed` on the `run_stall_timeout` FSM event so the FSM suppresses
|
|
103689
|
+
* the user-facing error banner during the 5-second self-heal (Fix B2).
|
|
103602
103690
|
*/
|
|
103603
103691
|
onStallTimeout() {
|
|
103604
|
-
if (this.#deps.getState() !== "running") return;
|
|
103605
|
-
if (this.#deps.getAgentSystem() !== AGENT_SYSTEM_CURSOR) return;
|
|
103692
|
+
if (this.#deps.getState() !== "running") return false;
|
|
103693
|
+
if (this.#deps.getAgentSystem() !== AGENT_SYSTEM_CURSOR) return false;
|
|
103606
103694
|
this.#stallResumeRetries++;
|
|
103607
103695
|
if (this.#stallResumeRetries > MAX_STALL_RESUME_RETRIES) {
|
|
103608
103696
|
this.#deps.log({
|
|
@@ -103611,7 +103699,7 @@ var CursorRecoveryController = class {
|
|
|
103611
103699
|
retries: this.#stallResumeRetries
|
|
103612
103700
|
});
|
|
103613
103701
|
this.#stallResumeRetries = 0;
|
|
103614
|
-
return;
|
|
103702
|
+
return false;
|
|
103615
103703
|
}
|
|
103616
103704
|
this.#deps.log({
|
|
103617
103705
|
event: "thread_cursor_stall_resume_armed",
|
|
@@ -103619,6 +103707,7 @@ var CursorRecoveryController = class {
|
|
|
103619
103707
|
retries: this.#stallResumeRetries
|
|
103620
103708
|
});
|
|
103621
103709
|
this.#stallResumeArmed = true;
|
|
103710
|
+
return true;
|
|
103622
103711
|
}
|
|
103623
103712
|
/**
|
|
103624
103713
|
* Consume an armed stall resume on the `kill_runner` death. By now the FSM is
|
|
@@ -103633,6 +103722,48 @@ var CursorRecoveryController = class {
|
|
|
103633
103722
|
this.#deps.respawnForResume();
|
|
103634
103723
|
return true;
|
|
103635
103724
|
}
|
|
103725
|
+
/**
|
|
103726
|
+
* One-shot resume on a Cursor transport-crash death (exit codes 2 or 3).
|
|
103727
|
+
*
|
|
103728
|
+
* Exit 3 = `unhandledRejection` (e.g. NGHTTP2_REFUSED_STREAM /
|
|
103729
|
+
* ENHANCE_YOUR_CALM from the Cursor backend's HTTP/2 rate-limiter escaping
|
|
103730
|
+
* @cursor/sdk). Exit 2 = `uncaughtException` (native or SDK throw). Both
|
|
103731
|
+
* indicate the runner crashed due to a transient transport fault — not a
|
|
103732
|
+
* controlled teardown. One silent auto-retry lets the session resume; a
|
|
103733
|
+
* second failure in the same turn surfaces normally so a deterministic bug
|
|
103734
|
+
* cannot loop.
|
|
103735
|
+
*
|
|
103736
|
+
* Exits 0 (clean) and 1 (parent-disconnect) are never retried here — they
|
|
103737
|
+
* surface as today. Signal deaths are covered by `trySignalDeathRetry`.
|
|
103738
|
+
*/
|
|
103739
|
+
tryCrashDeathRetry(event) {
|
|
103740
|
+
if (this.#deps.getState() !== "running") return false;
|
|
103741
|
+
if (this.#deps.getAgentSystem() !== AGENT_SYSTEM_CURSOR) return false;
|
|
103742
|
+
if (event.forceKilled === true) return false;
|
|
103743
|
+
if (event.signal !== null && event.signal !== void 0) return false;
|
|
103744
|
+
if (event.exitCode !== 2 && event.exitCode !== 3) return false;
|
|
103745
|
+
this.#crashDeathRetries++;
|
|
103746
|
+
if (this.#crashDeathRetries > MAX_CRASH_DEATH_RETRIES) {
|
|
103747
|
+
this.#deps.log({
|
|
103748
|
+
event: "thread_cursor_crash_death_retry_cap_exceeded",
|
|
103749
|
+
threadId: this.#deps.threadId,
|
|
103750
|
+
exitCode: event.exitCode,
|
|
103751
|
+
retries: this.#crashDeathRetries
|
|
103752
|
+
});
|
|
103753
|
+
this.#crashDeathRetries = 0;
|
|
103754
|
+
return false;
|
|
103755
|
+
}
|
|
103756
|
+
this.#deps.log({
|
|
103757
|
+
event: "thread_cursor_crash_death_auto_retry",
|
|
103758
|
+
threadId: this.#deps.threadId,
|
|
103759
|
+
exitCode: event.exitCode,
|
|
103760
|
+
retries: this.#crashDeathRetries
|
|
103761
|
+
});
|
|
103762
|
+
this.#deps.clearSubprocess();
|
|
103763
|
+
this.#deps.notifyAbortRetry();
|
|
103764
|
+
this.#deps.respawnForResume();
|
|
103765
|
+
return true;
|
|
103766
|
+
}
|
|
103636
103767
|
/**
|
|
103637
103768
|
* One-shot resume on a natural Cursor signal death (#4060). Skips forced,
|
|
103638
103769
|
* SIGKILL/SIGABRT, signal-less, and repeated deaths. Returns true when it
|
|
@@ -103853,6 +103984,7 @@ function rewriteSpawnError(error) {
|
|
|
103853
103984
|
var MAX_CONSECUTIVE_RETRIES = 2;
|
|
103854
103985
|
var MAX_EDE_DIAGNOSTIC_RETRIES = 2;
|
|
103855
103986
|
var MAX_REQUEST_ABORTED_RETRIES = 1;
|
|
103987
|
+
var MAX_RUN_STATUS_ERROR_RETRIES = 1;
|
|
103856
103988
|
var REQUEST_ABORTED_PHRASES = ["Request was aborted", "request was aborted"];
|
|
103857
103989
|
var SessionRecoveryController = class {
|
|
103858
103990
|
#deps;
|
|
@@ -103884,6 +104016,13 @@ var SessionRecoveryController = class {
|
|
|
103884
104016
|
#sawResumeFailure = false;
|
|
103885
104017
|
/** Consecutive transient "Request was aborted" retries; reset on turn_complete, cap-surface, and new user turn. */
|
|
103886
104018
|
#requestAbortedRetries = 0;
|
|
104019
|
+
/**
|
|
104020
|
+
* Consecutive Cursor `run_status_error` (backend run marked ERROR) retries;
|
|
104021
|
+
* reset on turn_complete, cap-surface, and new user turn — NOT on
|
|
104022
|
+
* init_received, since the retry respawn itself fires init_received and an
|
|
104023
|
+
* early reset would let a persistently-erroring backend loop forever.
|
|
104024
|
+
*/
|
|
104025
|
+
#runStatusErrorRetries = 0;
|
|
103887
104026
|
/**
|
|
103888
104027
|
* Counts consecutive ede_diagnostic session invalidations. Reset on a
|
|
103889
104028
|
* successful turn_complete. If this exceeds MAX_EDE_DIAGNOSTIC_RETRIES the
|
|
@@ -103941,9 +104080,11 @@ var SessionRecoveryController = class {
|
|
|
103941
104080
|
resetRetriesOnTurnComplete() {
|
|
103942
104081
|
this.#edeDiagnosticRetries = 0;
|
|
103943
104082
|
this.#requestAbortedRetries = 0;
|
|
104083
|
+
this.#runStatusErrorRetries = 0;
|
|
103944
104084
|
}
|
|
103945
104085
|
noteNewUserTurn() {
|
|
103946
104086
|
this.#requestAbortedRetries = 0;
|
|
104087
|
+
this.#runStatusErrorRetries = 0;
|
|
103947
104088
|
}
|
|
103948
104089
|
noteToolUseStarted(toolName, toolUseId) {
|
|
103949
104090
|
this.#lastToolUseName = toolName;
|
|
@@ -104058,7 +104199,8 @@ var SessionRecoveryController = class {
|
|
|
104058
104199
|
*
|
|
104059
104200
|
* A consecutive retry counter prevents infinite loops as a safety net.
|
|
104060
104201
|
*/
|
|
104061
|
-
handleSdkError(error) {
|
|
104202
|
+
handleSdkError(error, errorSubtype) {
|
|
104203
|
+
const isRetrying = error.startsWith("Codex (retrying):");
|
|
104062
104204
|
this.#deps.log({
|
|
104063
104205
|
event: "thread_sdk_error",
|
|
104064
104206
|
threadId: this.#deps.threadId,
|
|
@@ -104068,7 +104210,8 @@ var SessionRecoveryController = class {
|
|
|
104068
104210
|
* error column. Using `error` causes the SDK message to be dropped
|
|
104069
104211
|
* from log triage (3,329 silent failures observed in one window).
|
|
104070
104212
|
*/
|
|
104071
|
-
err: error
|
|
104213
|
+
err: error,
|
|
104214
|
+
...isRetrying ? { level: "info" } : {}
|
|
104072
104215
|
});
|
|
104073
104216
|
if (error.includes("[ede_diagnostic]")) {
|
|
104074
104217
|
this.#edeDiagnosticRetries++;
|
|
@@ -104104,6 +104247,7 @@ var SessionRecoveryController = class {
|
|
|
104104
104247
|
effectiveSpawnMode: this.#effectiveSpawnMode.kind
|
|
104105
104248
|
});
|
|
104106
104249
|
}
|
|
104250
|
+
if (this.#tryRunStatusErrorRetry(error, errorSubtype)) return;
|
|
104107
104251
|
if (this.#tryRequestAbortedRetry(error)) return;
|
|
104108
104252
|
if (this.#tryForkFailedRetry(error)) return;
|
|
104109
104253
|
if (this.#tryStaleResumeRetry(error)) return;
|
|
@@ -104115,6 +104259,43 @@ var SessionRecoveryController = class {
|
|
|
104115
104259
|
}
|
|
104116
104260
|
this.#deps.notifySdkError(surfaceMsg);
|
|
104117
104261
|
}
|
|
104262
|
+
/**
|
|
104263
|
+
* One-shot resume when Cursor's backend marks a run `status: ERROR`
|
|
104264
|
+
* (translator subtype `run_status_error`). The message is opaque server
|
|
104265
|
+
* state with no text — observed in production 1ms before an
|
|
104266
|
+
* NGHTTP2_ENHANCE_YOUR_CALM stream rejection from the same runner, i.e.
|
|
104267
|
+
* backend rate-limiting, not a session problem (#4229). The session JSONL
|
|
104268
|
+
* is intact, so a silent resume absorbs the blip; the cap surfaces a
|
|
104269
|
+
* persistent backend failure with actionable copy instead of the bare
|
|
104270
|
+
* "(status: ERROR)" string.
|
|
104271
|
+
*/
|
|
104272
|
+
#tryRunStatusErrorRetry(error, errorSubtype) {
|
|
104273
|
+
if (errorSubtype !== RUN_STATUS_ERROR_SUBTYPE) return false;
|
|
104274
|
+
if (this.#deps.getState() !== "running") return false;
|
|
104275
|
+
this.#runStatusErrorRetries++;
|
|
104276
|
+
if (this.#runStatusErrorRetries > MAX_RUN_STATUS_ERROR_RETRIES) {
|
|
104277
|
+
this.#deps.log({
|
|
104278
|
+
event: "thread_run_status_error_retry_cap_exceeded",
|
|
104279
|
+
threadId: this.#deps.threadId,
|
|
104280
|
+
retries: this.#runStatusErrorRetries,
|
|
104281
|
+
error
|
|
104282
|
+
});
|
|
104283
|
+
this.#runStatusErrorRetries = 0;
|
|
104284
|
+
const surfaceMsg = "Cursor's backend errored this run twice in a row \u2014 it may be rate-limiting this machine. Wait a moment, then send a message to retry.";
|
|
104285
|
+
this.#deps.persistError(surfaceMsg);
|
|
104286
|
+
this.#deps.notifySdkError(surfaceMsg);
|
|
104287
|
+
return true;
|
|
104288
|
+
}
|
|
104289
|
+
this.#deps.log({
|
|
104290
|
+
event: "thread_run_status_error_auto_retry",
|
|
104291
|
+
threadId: this.#deps.threadId,
|
|
104292
|
+
retries: this.#runStatusErrorRetries,
|
|
104293
|
+
error
|
|
104294
|
+
});
|
|
104295
|
+
this.#deps.notifyAbortRetry();
|
|
104296
|
+
this.#deps.respawnForResume();
|
|
104297
|
+
return true;
|
|
104298
|
+
}
|
|
104118
104299
|
/**
|
|
104119
104300
|
* Retry path 1: the first spawn used a fork/resume from parent and the SDK
|
|
104120
104301
|
* rejected the session (stale/expired). Retry with a fresh session.
|
|
@@ -104526,7 +104707,8 @@ var Thread = class {
|
|
|
104526
104707
|
log: config.log,
|
|
104527
104708
|
onSessionStateChange: (newState, prevState) => this.#notifyStateChange(newState, prevState),
|
|
104528
104709
|
stallTimeoutMs: config.stallTimeoutMs ?? null,
|
|
104529
|
-
firstStepStallTimeoutMs: config.firstStepStallTimeoutMs ?? null
|
|
104710
|
+
firstStepStallTimeoutMs: config.firstStepStallTimeoutMs ?? null,
|
|
104711
|
+
toolExecutionStallTimeoutMs: config.toolExecutionStallTimeoutMs ?? null
|
|
104530
104712
|
},
|
|
104531
104713
|
this.#buildInitialSnapshot(config)
|
|
104532
104714
|
);
|
|
@@ -105427,6 +105609,7 @@ var Thread = class {
|
|
|
105427
105609
|
this.#streamDelta.resetForTurn();
|
|
105428
105610
|
if (this.#cursorRecovery.consumeStallResumeOnDeath()) return;
|
|
105429
105611
|
if (this.#cursorRecovery.trySignalDeathRetry(event)) return;
|
|
105612
|
+
if (this.#cursorRecovery.tryCrashDeathRetry(event)) return;
|
|
105430
105613
|
this.#manager.notifySubprocessDied({
|
|
105431
105614
|
exitCode: event.exitCode ?? null,
|
|
105432
105615
|
signal: event.signal ?? null
|
|
@@ -105451,7 +105634,7 @@ var Thread = class {
|
|
|
105451
105634
|
this.#manager.notifyTurnComplete();
|
|
105452
105635
|
break;
|
|
105453
105636
|
case "sdk_error":
|
|
105454
|
-
this.#recovery.handleSdkError(event.error);
|
|
105637
|
+
this.#recovery.handleSdkError(event.error, event.errorSubtype);
|
|
105455
105638
|
break;
|
|
105456
105639
|
case "subprocess_died":
|
|
105457
105640
|
this.#handleSubprocessDiedEvent(event);
|
|
@@ -105568,6 +105751,12 @@ var Thread = class {
|
|
|
105568
105751
|
/** Handled in #enqueueSubprocessEvent (off-queue heartbeat); unreachable here. */
|
|
105569
105752
|
case "stream_activity":
|
|
105570
105753
|
return;
|
|
105754
|
+
case "tool_execution_started":
|
|
105755
|
+
this.#manager.notifyToolExecution("started");
|
|
105756
|
+
return;
|
|
105757
|
+
case "tool_execution_settled":
|
|
105758
|
+
this.#manager.notifyToolExecution("settled");
|
|
105759
|
+
return;
|
|
105571
105760
|
default: {
|
|
105572
105761
|
const _exhaustive = event;
|
|
105573
105762
|
throw new Error(`Unhandled subprocess event: ${JSON.stringify(_exhaustive)}`);
|
|
@@ -105582,11 +105771,14 @@ import { randomUUID as randomUUID21 } from "crypto";
|
|
|
105582
105771
|
import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
105583
105772
|
import { mkdir as mkdir32, readFile as readFile45, writeFile as writeFile25 } from "fs/promises";
|
|
105584
105773
|
import { homedir as homedir9 } from "os";
|
|
105585
|
-
import { dirname as dirname33, join as
|
|
105774
|
+
import { dirname as dirname33, join as join71 } from "path";
|
|
105586
105775
|
|
|
105587
105776
|
// src/services/plan/plan-detection-policy.ts
|
|
105588
105777
|
function decidePlanDetection(trigger, snapshot) {
|
|
105589
105778
|
if (snapshot.approvalReceived) return { type: "noop" };
|
|
105779
|
+
if (snapshot.priorPlanApproved && snapshot.triggerIsNewPlan) {
|
|
105780
|
+
return decideNewCycle(trigger, snapshot);
|
|
105781
|
+
}
|
|
105590
105782
|
switch (trigger.kind) {
|
|
105591
105783
|
/**
|
|
105592
105784
|
* Both entry triggers share logic; they differ only in whether an
|
|
@@ -105604,6 +105796,20 @@ function decidePlanDetection(trigger, snapshot) {
|
|
|
105604
105796
|
return assertNever(trigger);
|
|
105605
105797
|
}
|
|
105606
105798
|
}
|
|
105799
|
+
function decideNewCycle(trigger, snapshot) {
|
|
105800
|
+
switch (trigger.kind) {
|
|
105801
|
+
case "file_tracked":
|
|
105802
|
+
return snapshot.fileAvailable ? { type: "reset_and_detect" } : { type: "start_watch", failOnExhaust: false };
|
|
105803
|
+
case "exit_plan_mode":
|
|
105804
|
+
return snapshot.fileAvailable ? { type: "reset_and_detect" } : { type: "start_watch", failOnExhaust: true };
|
|
105805
|
+
case "watch_resolved":
|
|
105806
|
+
if (trigger.matched) return { type: "reset_and_detect" };
|
|
105807
|
+
if (trigger.failOnExhaust) return { type: "report_no_plan" };
|
|
105808
|
+
return { type: "noop" };
|
|
105809
|
+
default:
|
|
105810
|
+
return assertNever(trigger);
|
|
105811
|
+
}
|
|
105812
|
+
}
|
|
105607
105813
|
function decideEntry(snapshot, failOnExhaust) {
|
|
105608
105814
|
if (snapshot.hasDetection) return { type: "noop" };
|
|
105609
105815
|
if (snapshot.fileAvailable) return { type: "detect" };
|
|
@@ -106209,8 +106415,8 @@ function greedyLCS(a, b2) {
|
|
|
106209
106415
|
// src/services/plan/plan-file-watcher.ts
|
|
106210
106416
|
import { existsSync as existsSync9 } from "fs";
|
|
106211
106417
|
import { homedir as homedir8 } from "os";
|
|
106212
|
-
import { join as
|
|
106213
|
-
var DEFAULT_PLANS_DIR =
|
|
106418
|
+
import { join as join70 } from "path";
|
|
106419
|
+
var DEFAULT_PLANS_DIR = join70(homedir8(), ".claude", "plans");
|
|
106214
106420
|
var PLAN_WATCH_TIMEOUT_MS = 1e4;
|
|
106215
106421
|
var PLAN_WATCH_DEBOUNCE_MS = 250;
|
|
106216
106422
|
function createPlanFileWatcher(deps) {
|
|
@@ -106337,9 +106543,7 @@ function createPlanFileWatcher(deps) {
|
|
|
106337
106543
|
return { stop };
|
|
106338
106544
|
}
|
|
106339
106545
|
|
|
106340
|
-
// src/services/plan/plan-handler.ts
|
|
106341
|
-
var PLAN_PUBLISHED_DENY_MESSAGE = `Your plan has been published to the Shipyard canvas for collaborative review. Reviewers can comment, edit, and approve your plan in real-time. You will receive feedback as <${XML_TAGS.COMMENT}> children of the <plan> resource and plan edits as <${XML_TAGS.PLAN_UPDATE}> diffs. Wait for <${XML_TAGS.PLAN_REVIEW} decision="approve"> before calling ExitPlanMode again.`;
|
|
106342
|
-
var PLAN_STILL_UNDER_REVIEW_MESSAGE = `Your plan is still under review. Wait for <${XML_TAGS.PLAN_REVIEW} decision="approve"> before calling ExitPlanMode again.`;
|
|
106546
|
+
// src/services/plan/plan-handler-helpers.ts
|
|
106343
106547
|
function parseAllowedPrompts(input) {
|
|
106344
106548
|
if (input === null || input === void 0 || typeof input !== "object" || Array.isArray(input))
|
|
106345
106549
|
return [];
|
|
@@ -106375,6 +106579,149 @@ function mergeCommentSources(crdtComments, browserComments) {
|
|
|
106375
106579
|
}
|
|
106376
106580
|
return result;
|
|
106377
106581
|
}
|
|
106582
|
+
|
|
106583
|
+
// src/services/plan/plan-review-resolution.ts
|
|
106584
|
+
async function injectDecisionMessage(ctx, decision, feedback) {
|
|
106585
|
+
const reviewer = ctx.getHumanParticipantName();
|
|
106586
|
+
ctx.onPlanReviewEnded?.();
|
|
106587
|
+
if (decision === "approve") {
|
|
106588
|
+
ctx.setApprovalReceived(true);
|
|
106589
|
+
const feedbackNote = feedback ? ` Feedback: ${feedback}` : "";
|
|
106590
|
+
ctx.pushSyntheticMessageAndWake([
|
|
106591
|
+
{
|
|
106592
|
+
type: "text",
|
|
106593
|
+
text: `Plan approved by ${reviewer}.${feedbackNote} Continue with the implementation.`
|
|
106594
|
+
}
|
|
106595
|
+
]);
|
|
106596
|
+
} else {
|
|
106597
|
+
ctx.setApprovalReceived(false);
|
|
106598
|
+
}
|
|
106599
|
+
ctx.setPlanReviewState({
|
|
106600
|
+
decision,
|
|
106601
|
+
reviewer,
|
|
106602
|
+
feedback: feedback ?? null,
|
|
106603
|
+
decidedAt: Date.now()
|
|
106604
|
+
});
|
|
106605
|
+
ctx.notifyPlanChange();
|
|
106606
|
+
ctx.appendMessage({
|
|
106607
|
+
channelId: ctx.channelId,
|
|
106608
|
+
messageId: `review-decision-${ctx.taskId}-${Date.now()}`,
|
|
106609
|
+
participantId: ctx.humanParticipantId,
|
|
106610
|
+
senderKind: "human",
|
|
106611
|
+
content: [{ type: "review_decision", decision, feedback, reviewer }],
|
|
106612
|
+
timestamp: Date.now(),
|
|
106613
|
+
isSynthetic: false
|
|
106614
|
+
}).catch((err3) => {
|
|
106615
|
+
ctx.log({
|
|
106616
|
+
event: "plan_decision_append_failed",
|
|
106617
|
+
taskId: ctx.taskId,
|
|
106618
|
+
decision,
|
|
106619
|
+
error: err3 instanceof Error ? err3.message : String(err3)
|
|
106620
|
+
});
|
|
106621
|
+
});
|
|
106622
|
+
broadcastPlanReview(ctx, decision, feedback, reviewer);
|
|
106623
|
+
}
|
|
106624
|
+
function broadcastPlanReview(ctx, decision, feedback, reviewer) {
|
|
106625
|
+
ctx.getSendControlMessage()?.({
|
|
106626
|
+
type: "plan_review",
|
|
106627
|
+
taskId: ctx.taskId,
|
|
106628
|
+
decision,
|
|
106629
|
+
feedback,
|
|
106630
|
+
reviewer
|
|
106631
|
+
});
|
|
106632
|
+
}
|
|
106633
|
+
async function gatherMergedComments(ctx, browserComments) {
|
|
106634
|
+
const allUndelivered = await ctx.annotationStore.getUndeliveredAnnotations(ctx.taskId);
|
|
106635
|
+
const storeComments = allUndelivered.filter(
|
|
106636
|
+
(a) => a.annotationType === "plan-text"
|
|
106637
|
+
).map((a) => ({
|
|
106638
|
+
commentId: a.commentId,
|
|
106639
|
+
anchorText: a.anchorText,
|
|
106640
|
+
body: a.body,
|
|
106641
|
+
authorName: a.authorName
|
|
106642
|
+
}));
|
|
106643
|
+
return mergeCommentSources(storeComments, browserComments);
|
|
106644
|
+
}
|
|
106645
|
+
async function resolveLastAgentVersion(ctx) {
|
|
106646
|
+
const versions = await ctx.annotationStore.getPlanVersions(ctx.taskId);
|
|
106647
|
+
return findLastAgentVersion(versions);
|
|
106648
|
+
}
|
|
106649
|
+
async function preparePlanForResolution(ctx, toolUseId, browserComments) {
|
|
106650
|
+
const currentContent = ctx.planRepo.getContent(ctx.taskId);
|
|
106651
|
+
const mergedComments = await gatherMergedComments(ctx, browserComments);
|
|
106652
|
+
ctx.log({
|
|
106653
|
+
event: "plan_review_context",
|
|
106654
|
+
taskId: ctx.taskId,
|
|
106655
|
+
commentCount: mergedComments.length,
|
|
106656
|
+
browserCommentCount: browserComments.length,
|
|
106657
|
+
contentLength: currentContent.length
|
|
106658
|
+
});
|
|
106659
|
+
if (currentContent.length > 0) {
|
|
106660
|
+
const existingVersions = await ctx.annotationStore.getPlanVersions(ctx.taskId);
|
|
106661
|
+
const lastVersion = existingVersions[existingVersions.length - 1];
|
|
106662
|
+
const contentChanged = !lastVersion || lastVersion.markdown !== currentContent;
|
|
106663
|
+
if (contentChanged) {
|
|
106664
|
+
const humanVersion = {
|
|
106665
|
+
markdown: currentContent,
|
|
106666
|
+
timestamp: Date.now(),
|
|
106667
|
+
source: "human",
|
|
106668
|
+
toolUseId
|
|
106669
|
+
};
|
|
106670
|
+
await ctx.annotationStore.addPlanVersion(ctx.taskId, humanVersion);
|
|
106671
|
+
ctx.getSendControlMessage()?.({
|
|
106672
|
+
type: "annotation_version_added",
|
|
106673
|
+
taskId: ctx.taskId,
|
|
106674
|
+
version: humanVersion
|
|
106675
|
+
});
|
|
106676
|
+
}
|
|
106677
|
+
}
|
|
106678
|
+
if (mergedComments.length > 0) {
|
|
106679
|
+
await deliverPlanAnnotations(ctx, mergedComments);
|
|
106680
|
+
}
|
|
106681
|
+
}
|
|
106682
|
+
async function deliverPlanAnnotations(ctx, mergedComments) {
|
|
106683
|
+
try {
|
|
106684
|
+
const lastAgentVersion = await resolveLastAgentVersion(ctx);
|
|
106685
|
+
const currentContent = ctx.planRepo.getContent(ctx.taskId);
|
|
106686
|
+
const parts = [];
|
|
106687
|
+
if (lastAgentVersion?.markdown && lastAgentVersion.markdown !== currentContent) {
|
|
106688
|
+
const diff = computeUnifiedDiff(lastAgentVersion.markdown, currentContent);
|
|
106689
|
+
if (diff) {
|
|
106690
|
+
const participant = ctx.humanParticipantId;
|
|
106691
|
+
parts.push(
|
|
106692
|
+
`<${XML_TAGS.PLAN_UPDATE} participant="${escapeXmlAttr(participant)}">
|
|
106693
|
+
--- original (agent)
|
|
106694
|
+
+++ reviewed (${escapeXmlAttr(participant)})
|
|
106695
|
+
${diff}
|
|
106696
|
+
</${XML_TAGS.PLAN_UPDATE}>`
|
|
106697
|
+
);
|
|
106698
|
+
}
|
|
106699
|
+
}
|
|
106700
|
+
if (parts.length > 0) {
|
|
106701
|
+
ctx.pushSyntheticMessageAndWake([{ type: "text", text: parts.join("\n\n") }]);
|
|
106702
|
+
}
|
|
106703
|
+
const deliveredIds = mergedComments.map((c) => c.commentId);
|
|
106704
|
+
await ctx.annotationStore.markDelivered(ctx.taskId, deliveredIds);
|
|
106705
|
+
ctx.notifyPlanChange();
|
|
106706
|
+
ctx.log({
|
|
106707
|
+
event: "plan_annotations_delivered",
|
|
106708
|
+
taskId: ctx.taskId,
|
|
106709
|
+
commentCount: mergedComments.length,
|
|
106710
|
+
hasPlanDiff: parts.length > 0
|
|
106711
|
+
});
|
|
106712
|
+
} catch (err3) {
|
|
106713
|
+
const msg = err3 instanceof Error ? err3.message : String(err3);
|
|
106714
|
+
ctx.log({
|
|
106715
|
+
event: "plan_annotations_delivery_failed",
|
|
106716
|
+
taskId: ctx.taskId,
|
|
106717
|
+
error: msg
|
|
106718
|
+
});
|
|
106719
|
+
}
|
|
106720
|
+
}
|
|
106721
|
+
|
|
106722
|
+
// src/services/plan/plan-handler.ts
|
|
106723
|
+
var PLAN_PUBLISHED_DENY_MESSAGE = `Your plan has been published to the Shipyard canvas for collaborative review. Reviewers can comment, edit, and approve your plan in real-time. You will receive feedback as <${XML_TAGS.COMMENT}> children of the <plan> resource and plan edits as <${XML_TAGS.PLAN_UPDATE}> diffs. Wait for <${XML_TAGS.PLAN_REVIEW} decision="approve"> before calling ExitPlanMode again.`;
|
|
106724
|
+
var PLAN_STILL_UNDER_REVIEW_MESSAGE = `Your plan is still under review. Wait for <${XML_TAGS.PLAN_REVIEW} decision="approve"> before calling ExitPlanMode again.`;
|
|
106378
106725
|
var PlanHandler = class {
|
|
106379
106726
|
#deps;
|
|
106380
106727
|
#planFileBridge = null;
|
|
@@ -106659,7 +107006,7 @@ var PlanHandler = class {
|
|
|
106659
107006
|
* Called when a Write tool result mentions a file in ~/.claude/plans/.
|
|
106660
107007
|
*/
|
|
106661
107008
|
trackCreatedFile(filePath) {
|
|
106662
|
-
const plansDir =
|
|
107009
|
+
const plansDir = join71(homedir9(), ".claude", "plans");
|
|
106663
107010
|
if (filePath.startsWith(plansDir) && filePath.endsWith(".md")) {
|
|
106664
107011
|
this.#createdPlanFiles.add(filePath);
|
|
106665
107012
|
this.#deps.log({
|
|
@@ -106691,7 +107038,7 @@ var PlanHandler = class {
|
|
|
106691
107038
|
}
|
|
106692
107039
|
async #runHandleCodexPlanReady(content) {
|
|
106693
107040
|
if (this.#deps.isDisposed()) return;
|
|
106694
|
-
const filePath =
|
|
107041
|
+
const filePath = join71(getShipyardHome(), "plans", `${this.#deps.taskId}.md`);
|
|
106695
107042
|
try {
|
|
106696
107043
|
await mkdir32(dirname33(filePath), { recursive: true });
|
|
106697
107044
|
await writeFile25(filePath, content, "utf-8");
|
|
@@ -106808,8 +107155,9 @@ var PlanHandler = class {
|
|
|
106808
107155
|
decision
|
|
106809
107156
|
});
|
|
106810
107157
|
this.#deps.enqueueAsync(async () => {
|
|
107158
|
+
const ctx = this.#reviewResolutionContext();
|
|
106811
107159
|
try {
|
|
106812
|
-
await
|
|
107160
|
+
await preparePlanForResolution(ctx, toolUseId, opts?.comments ?? []);
|
|
106813
107161
|
this.#applyPermissionMode(opts?.permissionMode);
|
|
106814
107162
|
} catch (err3) {
|
|
106815
107163
|
this.#deps.log({
|
|
@@ -106820,7 +107168,7 @@ var PlanHandler = class {
|
|
|
106820
107168
|
error: err3 instanceof Error ? err3.message : String(err3)
|
|
106821
107169
|
});
|
|
106822
107170
|
}
|
|
106823
|
-
await
|
|
107171
|
+
await injectDecisionMessage(ctx, decision, feedback);
|
|
106824
107172
|
});
|
|
106825
107173
|
}
|
|
106826
107174
|
detectPlanEvents(content) {
|
|
@@ -106914,21 +107262,39 @@ var PlanHandler = class {
|
|
|
106914
107262
|
}
|
|
106915
107263
|
return this.#proactiveToolUseId;
|
|
106916
107264
|
}
|
|
106917
|
-
#detectionSnapshot(fileAvailable) {
|
|
107265
|
+
#detectionSnapshot(fileAvailable, toolUseId, matchPath) {
|
|
107266
|
+
const priorPlanApproved = this.#lastPlanDetection?.approved === true;
|
|
106918
107267
|
return {
|
|
106919
107268
|
approvalReceived: this.#approvalReceived,
|
|
106920
107269
|
hasDetection: this.#lastPlanDetection !== null || this.#detectionInFlight,
|
|
106921
|
-
fileAvailable
|
|
107270
|
+
fileAvailable,
|
|
107271
|
+
priorPlanApproved,
|
|
107272
|
+
/**
|
|
107273
|
+
* New-plan signal (#4498): a different plan than the one already detected —
|
|
107274
|
+
* an unprocessed toolUseId, or a tracked file differing from the bridge's
|
|
107275
|
+
* (Claude mints a fresh UUID + file per plan). Only meaningful once a prior
|
|
107276
|
+
* plan was approved, so it is gated on `priorPlanApproved`: on the FIRST
|
|
107277
|
+
* detection `#processedPlanToolUseIds` is empty (every id reads as "new"),
|
|
107278
|
+
* and conflating that with a new cycle would be a latent trap if a future
|
|
107279
|
+
* caller read this flag independently of `priorPlanApproved`.
|
|
107280
|
+
*/
|
|
107281
|
+
triggerIsNewPlan: priorPlanApproved && (!this.#processedPlanToolUseIds.has(toolUseId) || matchPath !== null && matchPath !== this.#planFileBridge?.filePath)
|
|
106922
107282
|
};
|
|
106923
107283
|
}
|
|
106924
|
-
/** Snapshot → policy → execute; #4075/#3488 logic is table-tested in `plan-detection-policy.test.ts`. */
|
|
107284
|
+
/** Snapshot → policy → execute; #4075/#3488/#4498 logic is table-tested in `plan-detection-policy.test.ts`. */
|
|
106925
107285
|
#ensurePlanDetection(trigger, toolUseId) {
|
|
106926
107286
|
const match = this.#findTaskPlanFile();
|
|
106927
|
-
const action = decidePlanDetection(
|
|
107287
|
+
const action = decidePlanDetection(
|
|
107288
|
+
trigger,
|
|
107289
|
+
this.#detectionSnapshot(match !== null, toolUseId, match)
|
|
107290
|
+
);
|
|
106928
107291
|
switch (action.type) {
|
|
106929
107292
|
case "detect":
|
|
106930
107293
|
if (match) this.#handlePlanDetected(toolUseId, match);
|
|
106931
107294
|
return;
|
|
107295
|
+
case "reset_and_detect":
|
|
107296
|
+
if (match) this.#resetForNewPlanCycle(toolUseId, match);
|
|
107297
|
+
return;
|
|
106932
107298
|
case "start_watch":
|
|
106933
107299
|
this.#startPlanWatch(toolUseId, action.failOnExhaust);
|
|
106934
107300
|
return;
|
|
@@ -106943,6 +107309,35 @@ var PlanHandler = class {
|
|
|
106943
107309
|
}
|
|
106944
107310
|
}
|
|
106945
107311
|
}
|
|
107312
|
+
/**
|
|
107313
|
+
* Begin a new plan cycle for a later phase in the SAME task (#4498). Without
|
|
107314
|
+
* this the new plan is silently dropped (hasDetection stays true) and the UI
|
|
107315
|
+
* keeps showing the stale approved plan. Narrow subset of `resetForRewind`
|
|
107316
|
+
* that preserves `#processedPlanToolUseIds`; a reused processed id gets a
|
|
107317
|
+
* fresh synthetic id so dedup does not skip the new plan.
|
|
107318
|
+
*/
|
|
107319
|
+
#resetForNewPlanCycle(toolUseId, filePath) {
|
|
107320
|
+
const detectToolUseId = this.#processedPlanToolUseIds.has(toolUseId) ? `plan_new_cycle_${this.#deps.taskId}_${randomUUID21()}` : toolUseId;
|
|
107321
|
+
this.#deps.log({
|
|
107322
|
+
event: "plan_new_cycle_detected",
|
|
107323
|
+
taskId: this.#deps.taskId,
|
|
107324
|
+
toolUseId: detectToolUseId,
|
|
107325
|
+
filePath
|
|
107326
|
+
});
|
|
107327
|
+
this.#approvalReceived = false;
|
|
107328
|
+
this.#planPublished = false;
|
|
107329
|
+
this.#proactiveToolUseId = null;
|
|
107330
|
+
this.#stopPlanWatch();
|
|
107331
|
+
this.#detectionInFlight = false;
|
|
107332
|
+
const hadReviewState = this.#planReviewState !== null;
|
|
107333
|
+
this.#planReviewState = null;
|
|
107334
|
+
this.#deps.onPlanReviewEnded?.();
|
|
107335
|
+
if (hadReviewState) this.#deps.notifyPlanChange();
|
|
107336
|
+
this.#lastPlanDetection = null;
|
|
107337
|
+
this.#planFileBridge?.dispose();
|
|
107338
|
+
this.#planFileBridge = null;
|
|
107339
|
+
this.#handlePlanDetected(detectToolUseId, filePath);
|
|
107340
|
+
}
|
|
106946
107341
|
/**
|
|
106947
107342
|
* Subscribe to the plans directory via `@parcel/watcher`. `failOnExhaust`
|
|
106948
107343
|
* decides whether a 10s exhaust surfaces an explicit no-plan state
|
|
@@ -106970,12 +107365,15 @@ var PlanHandler = class {
|
|
|
106970
107365
|
#applyWatchResolution(toolUseId, matched, failOnExhaust, filePath) {
|
|
106971
107366
|
const action = decidePlanDetection(
|
|
106972
107367
|
{ kind: "watch_resolved", matched, failOnExhaust },
|
|
106973
|
-
this.#detectionSnapshot(filePath !== null)
|
|
107368
|
+
this.#detectionSnapshot(filePath !== null, toolUseId, filePath)
|
|
106974
107369
|
);
|
|
106975
107370
|
switch (action.type) {
|
|
106976
107371
|
case "detect":
|
|
106977
107372
|
if (filePath) this.#handlePlanDetected(toolUseId, filePath);
|
|
106978
107373
|
return;
|
|
107374
|
+
case "reset_and_detect":
|
|
107375
|
+
if (filePath) this.#resetForNewPlanCycle(toolUseId, filePath);
|
|
107376
|
+
return;
|
|
106979
107377
|
case "report_no_plan":
|
|
106980
107378
|
this.#reportNoPlan(toolUseId);
|
|
106981
107379
|
return;
|
|
@@ -107071,12 +107469,12 @@ var PlanHandler = class {
|
|
|
107071
107469
|
}).filter((f2) => f2 !== null).sort((a, b2) => b2.mtime - a.mtime)[0];
|
|
107072
107470
|
return newest?.path ?? null;
|
|
107073
107471
|
}
|
|
107074
|
-
const plansDir =
|
|
107472
|
+
const plansDir = join71(homedir9(), ".claude", "plans");
|
|
107075
107473
|
if (!existsSync10(plansDir)) return null;
|
|
107076
107474
|
try {
|
|
107077
107475
|
const files = readdirSync5(plansDir).filter((f2) => f2.endsWith(".md")).map((f2) => ({
|
|
107078
|
-
path:
|
|
107079
|
-
mtime: statSync4(
|
|
107476
|
+
path: join71(plansDir, f2),
|
|
107477
|
+
mtime: statSync4(join71(plansDir, f2)).mtimeMs
|
|
107080
107478
|
})).sort((a, b2) => b2.mtime - a.mtime);
|
|
107081
107479
|
const recent = files[0];
|
|
107082
107480
|
if (recent && Date.now() - recent.mtime < 3e4) return recent.path;
|
|
@@ -107225,161 +107623,33 @@ var PlanHandler = class {
|
|
|
107225
107623
|
if (!permissionMode) return;
|
|
107226
107624
|
this.#deps.applyTaskSettings({ permissionMode });
|
|
107227
107625
|
}
|
|
107228
|
-
async #injectDecisionMessage(decision, feedback) {
|
|
107229
|
-
const reviewer = this.#deps.getHumanParticipantName();
|
|
107230
|
-
this.#deps.onPlanReviewEnded?.();
|
|
107231
|
-
if (decision === "approve") {
|
|
107232
|
-
this.#approvalReceived = true;
|
|
107233
|
-
const feedbackNote = feedback ? ` Feedback: ${feedback}` : "";
|
|
107234
|
-
this.#deps.pushSyntheticMessageAndWake([
|
|
107235
|
-
{
|
|
107236
|
-
type: "text",
|
|
107237
|
-
text: `Plan approved by ${reviewer}.${feedbackNote} Continue with the implementation.`
|
|
107238
|
-
}
|
|
107239
|
-
]);
|
|
107240
|
-
} else {
|
|
107241
|
-
this.#approvalReceived = false;
|
|
107242
|
-
}
|
|
107243
|
-
this.#planReviewState = {
|
|
107244
|
-
decision,
|
|
107245
|
-
reviewer,
|
|
107246
|
-
feedback: feedback ?? null,
|
|
107247
|
-
decidedAt: Date.now()
|
|
107248
|
-
};
|
|
107249
|
-
this.#deps.notifyPlanChange();
|
|
107250
|
-
this.#deps.appendMessage({
|
|
107251
|
-
channelId: this.#deps.channelId,
|
|
107252
|
-
messageId: `review-decision-${this.#deps.taskId}-${Date.now()}`,
|
|
107253
|
-
participantId: this.#deps.humanParticipantId,
|
|
107254
|
-
senderKind: "human",
|
|
107255
|
-
content: [{ type: "review_decision", decision, feedback, reviewer }],
|
|
107256
|
-
timestamp: Date.now(),
|
|
107257
|
-
isSynthetic: false
|
|
107258
|
-
}).catch((err3) => {
|
|
107259
|
-
this.#deps.log({
|
|
107260
|
-
event: "plan_decision_append_failed",
|
|
107261
|
-
taskId: this.#deps.taskId,
|
|
107262
|
-
decision,
|
|
107263
|
-
error: err3 instanceof Error ? err3.message : String(err3)
|
|
107264
|
-
});
|
|
107265
|
-
});
|
|
107266
|
-
this.#broadcastPlanReview(decision, feedback, reviewer);
|
|
107267
|
-
}
|
|
107268
|
-
#broadcastPlanReview(decision, feedback, reviewer) {
|
|
107269
|
-
this.#deps.getSendControlMessage()?.({
|
|
107270
|
-
type: "plan_review",
|
|
107271
|
-
taskId: this.#deps.taskId,
|
|
107272
|
-
decision,
|
|
107273
|
-
feedback,
|
|
107274
|
-
reviewer
|
|
107275
|
-
});
|
|
107276
|
-
}
|
|
107277
107626
|
/**
|
|
107278
|
-
*
|
|
107279
|
-
*
|
|
107627
|
+
* Build the narrow Context for the review-resolution free functions. State
|
|
107628
|
+
* (`#approvalReceived`, `#planReviewState`) is read broadly across this class,
|
|
107629
|
+
* so it stays here and is mutated through narrow setters — see
|
|
107630
|
+
* `plan-review-resolution.ts`.
|
|
107280
107631
|
*/
|
|
107281
|
-
|
|
107282
|
-
|
|
107283
|
-
this.#deps.taskId
|
|
107284
|
-
);
|
|
107285
|
-
const storeComments = allUndelivered.filter(
|
|
107286
|
-
(a) => a.annotationType === "plan-text"
|
|
107287
|
-
).map((a) => ({
|
|
107288
|
-
commentId: a.commentId,
|
|
107289
|
-
anchorText: a.anchorText,
|
|
107290
|
-
body: a.body,
|
|
107291
|
-
authorName: a.authorName
|
|
107292
|
-
}));
|
|
107293
|
-
return mergeCommentSources(storeComments, browserComments);
|
|
107294
|
-
}
|
|
107295
|
-
/** Resolve the best agent version from file-backed store. */
|
|
107296
|
-
async #resolveLastAgentVersion() {
|
|
107297
|
-
const versions = await this.#deps.annotationStore.getPlanVersions(this.#deps.taskId);
|
|
107298
|
-
return findLastAgentVersion(versions);
|
|
107299
|
-
}
|
|
107300
|
-
async #preparePlanForResolution(toolUseId, browserComments) {
|
|
107301
|
-
const currentContent = this.#deps.planRepo.getContent(this.#deps.taskId);
|
|
107302
|
-
const mergedComments = await this.#gatherMergedComments(browserComments);
|
|
107303
|
-
this.#deps.log({
|
|
107304
|
-
event: "plan_review_context",
|
|
107632
|
+
#reviewResolutionContext() {
|
|
107633
|
+
return {
|
|
107305
107634
|
taskId: this.#deps.taskId,
|
|
107306
|
-
|
|
107307
|
-
|
|
107308
|
-
|
|
107309
|
-
|
|
107310
|
-
|
|
107311
|
-
|
|
107312
|
-
|
|
107313
|
-
|
|
107314
|
-
|
|
107315
|
-
|
|
107316
|
-
|
|
107317
|
-
|
|
107318
|
-
|
|
107319
|
-
|
|
107320
|
-
|
|
107321
|
-
|
|
107322
|
-
this.#deps.getSendControlMessage()?.({
|
|
107323
|
-
type: "annotation_version_added",
|
|
107324
|
-
taskId: this.#deps.taskId,
|
|
107325
|
-
version: humanVersion
|
|
107326
|
-
});
|
|
107327
|
-
}
|
|
107328
|
-
}
|
|
107329
|
-
if (mergedComments.length > 0) {
|
|
107330
|
-
await this.#deliverPlanAnnotations(mergedComments);
|
|
107331
|
-
}
|
|
107332
|
-
}
|
|
107333
|
-
/**
|
|
107334
|
-
* Deliver review comments and plan edit diff. Plan-anchored comments are
|
|
107335
|
-
* no longer pushed as a top-level `<review-comment surface="plan">`
|
|
107336
|
-
* synthetic — the `<plan>` resource projection now embeds them inline as
|
|
107337
|
-
* `<comment>` children. Instead we mark the comments delivered and notify
|
|
107338
|
-
* the resource resolver so it re-projects.
|
|
107339
|
-
*
|
|
107340
|
-
* The `<plan-update>` patch (showing human edits over the agent's
|
|
107341
|
-
* baseline) is still emitted synchronously here so the agent sees the
|
|
107342
|
-
* diff in the same turn the decision lands. Resource-level lineage
|
|
107343
|
-
* patches will continue to surface ongoing CRDT mutations independently.
|
|
107344
|
-
*/
|
|
107345
|
-
async #deliverPlanAnnotations(mergedComments) {
|
|
107346
|
-
try {
|
|
107347
|
-
const lastAgentVersion = await this.#resolveLastAgentVersion();
|
|
107348
|
-
const currentContent = this.#deps.planRepo.getContent(this.#deps.taskId);
|
|
107349
|
-
const parts = [];
|
|
107350
|
-
if (lastAgentVersion?.markdown && lastAgentVersion.markdown !== currentContent) {
|
|
107351
|
-
const diff = computeUnifiedDiff(lastAgentVersion.markdown, currentContent);
|
|
107352
|
-
if (diff) {
|
|
107353
|
-
const participant = this.#deps.humanParticipantId;
|
|
107354
|
-
parts.push(
|
|
107355
|
-
`<${XML_TAGS.PLAN_UPDATE} participant="${escapeXmlAttr(participant)}">
|
|
107356
|
-
--- original (agent)
|
|
107357
|
-
+++ reviewed (${escapeXmlAttr(participant)})
|
|
107358
|
-
${diff}
|
|
107359
|
-
</${XML_TAGS.PLAN_UPDATE}>`
|
|
107360
|
-
);
|
|
107361
|
-
}
|
|
107362
|
-
}
|
|
107363
|
-
if (parts.length > 0) {
|
|
107364
|
-
this.#deps.pushSyntheticMessageAndWake([{ type: "text", text: parts.join("\n\n") }]);
|
|
107635
|
+
channelId: this.#deps.channelId,
|
|
107636
|
+
humanParticipantId: this.#deps.humanParticipantId,
|
|
107637
|
+
planRepo: this.#deps.planRepo,
|
|
107638
|
+
annotationStore: this.#deps.annotationStore,
|
|
107639
|
+
log: this.#deps.log,
|
|
107640
|
+
getSendControlMessage: this.#deps.getSendControlMessage,
|
|
107641
|
+
pushSyntheticMessageAndWake: this.#deps.pushSyntheticMessageAndWake,
|
|
107642
|
+
appendMessage: this.#deps.appendMessage,
|
|
107643
|
+
getHumanParticipantName: this.#deps.getHumanParticipantName,
|
|
107644
|
+
notifyPlanChange: this.#deps.notifyPlanChange,
|
|
107645
|
+
onPlanReviewEnded: this.#deps.onPlanReviewEnded,
|
|
107646
|
+
setApprovalReceived: (value) => {
|
|
107647
|
+
this.#approvalReceived = value;
|
|
107648
|
+
},
|
|
107649
|
+
setPlanReviewState: (state) => {
|
|
107650
|
+
this.#planReviewState = state;
|
|
107365
107651
|
}
|
|
107366
|
-
|
|
107367
|
-
await this.#deps.annotationStore.markDelivered(this.#deps.taskId, deliveredIds);
|
|
107368
|
-
this.#deps.notifyPlanChange();
|
|
107369
|
-
this.#deps.log({
|
|
107370
|
-
event: "plan_annotations_delivered",
|
|
107371
|
-
taskId: this.#deps.taskId,
|
|
107372
|
-
commentCount: mergedComments.length,
|
|
107373
|
-
hasPlanDiff: parts.length > 0
|
|
107374
|
-
});
|
|
107375
|
-
} catch (err3) {
|
|
107376
|
-
const msg = err3 instanceof Error ? err3.message : String(err3);
|
|
107377
|
-
this.#deps.log({
|
|
107378
|
-
event: "plan_annotations_delivery_failed",
|
|
107379
|
-
taskId: this.#deps.taskId,
|
|
107380
|
-
error: msg
|
|
107381
|
-
});
|
|
107382
|
-
}
|
|
107652
|
+
};
|
|
107383
107653
|
}
|
|
107384
107654
|
};
|
|
107385
107655
|
|
|
@@ -107716,7 +107986,7 @@ function tryBeginBridge(ctx) {
|
|
|
107716
107986
|
function endBridge(ctx) {
|
|
107717
107987
|
ctx.setBridgeInFlight(false);
|
|
107718
107988
|
}
|
|
107719
|
-
async function compactSelfForHandoff(ctx,
|
|
107989
|
+
async function compactSelfForHandoff(ctx, startSilenceMs = COMPACTION_SILENCE_TIMEOUT_MS) {
|
|
107720
107990
|
const triggered = await ctx.forceCompact();
|
|
107721
107991
|
if (!triggered) {
|
|
107722
107992
|
ctx.log({
|
|
@@ -107725,14 +107995,23 @@ async function compactSelfForHandoff(ctx, timeoutMs = COMPACT_TIMEOUT_MS) {
|
|
|
107725
107995
|
});
|
|
107726
107996
|
return false;
|
|
107727
107997
|
}
|
|
107728
|
-
const
|
|
107998
|
+
const startDeadline = Date.now() + startSilenceMs;
|
|
107729
107999
|
const pollIntervalMs = 250;
|
|
107730
108000
|
let leftIdleOnce = false;
|
|
107731
|
-
while (
|
|
108001
|
+
while (true) {
|
|
107732
108002
|
await new Promise((resolve10) => setTimeout(resolve10, pollIntervalMs));
|
|
107733
108003
|
const kind = ctx.getCompactionStateKind();
|
|
107734
108004
|
if (!leftIdleOnce) {
|
|
107735
|
-
if (kind !== "idle")
|
|
108005
|
+
if (kind !== "idle") {
|
|
108006
|
+
leftIdleOnce = true;
|
|
108007
|
+
} else if (Date.now() >= startDeadline) {
|
|
108008
|
+
ctx.log({
|
|
108009
|
+
event: "compact_self_for_handoff_start_silence_timeout",
|
|
108010
|
+
taskId: ctx.taskId,
|
|
108011
|
+
startSilenceMs
|
|
108012
|
+
});
|
|
108013
|
+
return false;
|
|
108014
|
+
}
|
|
107736
108015
|
continue;
|
|
107737
108016
|
}
|
|
107738
108017
|
if (kind === "idle") return true;
|
|
@@ -107741,13 +108020,6 @@ async function compactSelfForHandoff(ctx, timeoutMs = COMPACT_TIMEOUT_MS) {
|
|
|
107741
108020
|
return false;
|
|
107742
108021
|
}
|
|
107743
108022
|
}
|
|
107744
|
-
ctx.log({
|
|
107745
|
-
event: "compact_self_for_handoff_timeout",
|
|
107746
|
-
taskId: ctx.taskId,
|
|
107747
|
-
timeoutMs,
|
|
107748
|
-
leftIdleOnce
|
|
107749
|
-
});
|
|
107750
|
-
return false;
|
|
107751
108023
|
}
|
|
107752
108024
|
function consumePendingNeutralHandoff(ctx, destProvider) {
|
|
107753
108025
|
const pending = ctx.getPendingNeutralHandoff();
|
|
@@ -109417,7 +109689,7 @@ var RewindCheckpointHandler = class {
|
|
|
109417
109689
|
|
|
109418
109690
|
// src/services/task/side-thread-registry.ts
|
|
109419
109691
|
import { mkdir as mkdir33, readFile as readFile46, rename as rename20, writeFile as writeFile26 } from "fs/promises";
|
|
109420
|
-
import { dirname as dirname34, join as
|
|
109692
|
+
import { dirname as dirname34, join as join72 } from "path";
|
|
109421
109693
|
var ThreadFileSchema = external_exports.object({
|
|
109422
109694
|
threads: external_exports.record(external_exports.string(), ThreadMetadataSchema)
|
|
109423
109695
|
});
|
|
@@ -109793,7 +110065,7 @@ var SideThreadRegistry = class {
|
|
|
109793
110065
|
}
|
|
109794
110066
|
/** Persistence */
|
|
109795
110067
|
#filePath() {
|
|
109796
|
-
return
|
|
110068
|
+
return join72(this.#deps.dataDir, "threads", `${this.#deps.taskId}.json`);
|
|
109797
110069
|
}
|
|
109798
110070
|
async #ensureLoaded() {
|
|
109799
110071
|
if (this.#loadedFromDisk.has(this.#deps.taskId)) return;
|
|
@@ -110046,15 +110318,15 @@ function buildHandlers(deps) {
|
|
|
110046
110318
|
deps.dispatchToSelf({ kind: "flash_timer_elapsed" });
|
|
110047
110319
|
});
|
|
110048
110320
|
} else {
|
|
110049
|
-
deps.
|
|
110050
|
-
deps.dispatchToSelf({ kind: "
|
|
110321
|
+
deps.setCompactSilenceTimer(durationMs, () => {
|
|
110322
|
+
deps.dispatchToSelf({ kind: "compact_silence_timeout", now: Date.now() });
|
|
110051
110323
|
});
|
|
110052
110324
|
}
|
|
110053
110325
|
},
|
|
110054
110326
|
clearTimer: (timerKind) => {
|
|
110055
110327
|
if (timerKind === "pre-warn") deps.clearPreWarnTimer();
|
|
110056
110328
|
else if (timerKind === "flash") deps.clearFlashTimer();
|
|
110057
|
-
else deps.
|
|
110329
|
+
else deps.clearCompactSilenceTimer();
|
|
110058
110330
|
},
|
|
110059
110331
|
callProfileForceCompact: (instructions) => {
|
|
110060
110332
|
const opts = instructions !== void 0 ? { instructions } : void 0;
|
|
@@ -110096,23 +110368,10 @@ function enrichTelemetryPayload(event, deps) {
|
|
|
110096
110368
|
switch (event.kind) {
|
|
110097
110369
|
case "compaction_started":
|
|
110098
110370
|
return base2;
|
|
110099
|
-
case "compaction_completed":
|
|
110100
|
-
|
|
110101
|
-
const enriched = { ...base2, durationMs: event.durationMs };
|
|
110102
|
-
if (event.preTokens !== void 0) enriched.preTokens = event.preTokens;
|
|
110103
|
-
if (event.postTokens !== void 0) enriched.postTokens = event.postTokens;
|
|
110104
|
-
if (event.tokensReclaimed !== void 0) enriched.tokensReclaimed = event.tokensReclaimed;
|
|
110105
|
-
if (priorMessageCount !== void 0) enriched.priorMessageCount = priorMessageCount;
|
|
110106
|
-
if (postMessageCount !== void 0) enriched.postMessageCount = postMessageCount;
|
|
110107
|
-
return enriched;
|
|
110108
|
-
}
|
|
110371
|
+
case "compaction_completed":
|
|
110372
|
+
return enrichCompletedPayload(base2, event, deps);
|
|
110109
110373
|
case "compaction_failed":
|
|
110110
|
-
return
|
|
110111
|
-
...base2,
|
|
110112
|
-
durationMs: event.durationMs,
|
|
110113
|
-
reason: event.reason,
|
|
110114
|
-
errorMessage: truncateError(event.error)
|
|
110115
|
-
};
|
|
110374
|
+
return enrichFailedPayload(base2, event);
|
|
110116
110375
|
case "compaction_subprocess_died":
|
|
110117
110376
|
return {
|
|
110118
110377
|
...base2,
|
|
@@ -110123,6 +110382,26 @@ function enrichTelemetryPayload(event, deps) {
|
|
|
110123
110382
|
return assertNever7(event);
|
|
110124
110383
|
}
|
|
110125
110384
|
}
|
|
110385
|
+
function enrichCompletedPayload(base2, event, deps) {
|
|
110386
|
+
const { priorMessageCount, postMessageCount } = deps.getMessageCounts();
|
|
110387
|
+
const enriched = { ...base2, durationMs: event.durationMs };
|
|
110388
|
+
if (event.preTokens !== void 0) enriched.preTokens = event.preTokens;
|
|
110389
|
+
if (event.postTokens !== void 0) enriched.postTokens = event.postTokens;
|
|
110390
|
+
if (event.tokensReclaimed !== void 0) enriched.tokensReclaimed = event.tokensReclaimed;
|
|
110391
|
+
if (priorMessageCount !== void 0) enriched.priorMessageCount = priorMessageCount;
|
|
110392
|
+
if (postMessageCount !== void 0) enriched.postMessageCount = postMessageCount;
|
|
110393
|
+
return enriched;
|
|
110394
|
+
}
|
|
110395
|
+
function enrichFailedPayload(base2, event) {
|
|
110396
|
+
const enriched = {
|
|
110397
|
+
...base2,
|
|
110398
|
+
durationMs: event.durationMs,
|
|
110399
|
+
reason: event.reason,
|
|
110400
|
+
errorMessage: truncateError(event.error)
|
|
110401
|
+
};
|
|
110402
|
+
if (event.silenceMs !== void 0) enriched.silenceMs = event.silenceMs;
|
|
110403
|
+
return enriched;
|
|
110404
|
+
}
|
|
110126
110405
|
function mapSmStatusToWire(status) {
|
|
110127
110406
|
switch (status) {
|
|
110128
110407
|
case "pre-warn":
|
|
@@ -110241,7 +110520,7 @@ async function buildCompactionSnapshot(deps, instructions) {
|
|
|
110241
110520
|
};
|
|
110242
110521
|
}
|
|
110243
110522
|
function buildCompactionShellHandlers(ctx) {
|
|
110244
|
-
let
|
|
110523
|
+
let compactSilenceHandle = null;
|
|
110245
110524
|
const shellDeps = {
|
|
110246
110525
|
/** Late-bound: every effect invocation reads the current subprocess. */
|
|
110247
110526
|
get subprocess() {
|
|
@@ -110317,19 +110596,19 @@ function buildCompactionShellHandlers(ctx) {
|
|
|
110317
110596
|
},
|
|
110318
110597
|
clearFlashTimer: () => {
|
|
110319
110598
|
},
|
|
110320
|
-
|
|
110321
|
-
if (
|
|
110322
|
-
clearTimeout(
|
|
110599
|
+
setCompactSilenceTimer: (durationMs, onElapsed) => {
|
|
110600
|
+
if (compactSilenceHandle !== null) {
|
|
110601
|
+
clearTimeout(compactSilenceHandle);
|
|
110323
110602
|
}
|
|
110324
|
-
|
|
110325
|
-
|
|
110603
|
+
compactSilenceHandle = setTimeout(() => {
|
|
110604
|
+
compactSilenceHandle = null;
|
|
110326
110605
|
onElapsed();
|
|
110327
110606
|
}, durationMs);
|
|
110328
110607
|
},
|
|
110329
|
-
|
|
110330
|
-
if (
|
|
110331
|
-
clearTimeout(
|
|
110332
|
-
|
|
110608
|
+
clearCompactSilenceTimer: () => {
|
|
110609
|
+
if (compactSilenceHandle !== null) {
|
|
110610
|
+
clearTimeout(compactSilenceHandle);
|
|
110611
|
+
compactSilenceHandle = null;
|
|
110333
110612
|
}
|
|
110334
110613
|
},
|
|
110335
110614
|
dispatchToSelf: (event) => {
|
|
@@ -110353,8 +110632,7 @@ function buildCompactionShellHandlers(ctx) {
|
|
|
110353
110632
|
},
|
|
110354
110633
|
/**
|
|
110355
110634
|
* Live-read every dispatch so a cross-runtime switch between dispatches
|
|
110356
|
-
* picks up the destination's capability.
|
|
110357
|
-
* arming the compact-timeout watchdog.
|
|
110635
|
+
* picks up the destination's capability.
|
|
110358
110636
|
*/
|
|
110359
110637
|
get runtimeFailureReporting() {
|
|
110360
110638
|
return ctx.getRuntimeFailureReporting();
|
|
@@ -110545,6 +110823,7 @@ function decideByMessageCount(params) {
|
|
|
110545
110823
|
return { kind: "noop" };
|
|
110546
110824
|
}
|
|
110547
110825
|
function handleMainThreadEvent(ctx, event) {
|
|
110826
|
+
maybeDispatchCompactionActivity(ctx, event);
|
|
110548
110827
|
switch (event.type) {
|
|
110549
110828
|
case "init_received":
|
|
110550
110829
|
ctx.onInitReceived(event.metadata);
|
|
@@ -110675,12 +110954,60 @@ function handleMainThreadEvent(ctx, event) {
|
|
|
110675
110954
|
break;
|
|
110676
110955
|
case "stream_activity":
|
|
110677
110956
|
break;
|
|
110957
|
+
case "tool_execution_started":
|
|
110958
|
+
case "tool_execution_settled":
|
|
110959
|
+
break;
|
|
110678
110960
|
default: {
|
|
110679
110961
|
const _exhaustive = event;
|
|
110680
110962
|
throw new Error(`Unhandled subprocess event: ${JSON.stringify(_exhaustive)}`);
|
|
110681
110963
|
}
|
|
110682
110964
|
}
|
|
110683
110965
|
}
|
|
110966
|
+
function maybeDispatchCompactionActivity(ctx, event) {
|
|
110967
|
+
if (ctx.getCompactionStateKind() !== "compacting") return;
|
|
110968
|
+
if (!isCompactionActivityEvent(event)) return;
|
|
110969
|
+
ctx.dispatchCompactionEvent({ kind: "compact_activity", now: Date.now() });
|
|
110970
|
+
}
|
|
110971
|
+
function isCompactionActivityEvent(event) {
|
|
110972
|
+
switch (event.type) {
|
|
110973
|
+
case "assistant_message":
|
|
110974
|
+
case "background_agent_completed":
|
|
110975
|
+
case "background_agent_result":
|
|
110976
|
+
case "background_agent_started":
|
|
110977
|
+
case "codex_token_snapshot":
|
|
110978
|
+
case "cumulative_usage_snapshot":
|
|
110979
|
+
case "informational_notice":
|
|
110980
|
+
case "memory_recall":
|
|
110981
|
+
case "mcp_mid_thread_reload_needed":
|
|
110982
|
+
case "plan_content_ready":
|
|
110983
|
+
case "step_progress":
|
|
110984
|
+
case "stream_activity":
|
|
110985
|
+
case "stream_delta":
|
|
110986
|
+
case "subagent_progress":
|
|
110987
|
+
case "tool_execution_settled":
|
|
110988
|
+
case "tool_execution_started":
|
|
110989
|
+
case "tool_result_echo":
|
|
110990
|
+
case "tool_use_started":
|
|
110991
|
+
case "tool_use_summary":
|
|
110992
|
+
case "turn_complete":
|
|
110993
|
+
case "user_message_echo":
|
|
110994
|
+
case "api_usage_snapshot":
|
|
110995
|
+
return true;
|
|
110996
|
+
case "auth_not_logged_in":
|
|
110997
|
+
case "billing_error":
|
|
110998
|
+
case "compaction_completed":
|
|
110999
|
+
case "compaction_failed":
|
|
111000
|
+
case "compaction_started":
|
|
111001
|
+
case "init_received":
|
|
111002
|
+
case "rate_limit":
|
|
111003
|
+
case "rate_limit_error":
|
|
111004
|
+
case "sdk_error":
|
|
111005
|
+
case "subprocess_died":
|
|
111006
|
+
return false;
|
|
111007
|
+
default:
|
|
111008
|
+
return assertNever(event);
|
|
111009
|
+
}
|
|
111010
|
+
}
|
|
110684
111011
|
function handleAssistantMessage(ctx, event) {
|
|
110685
111012
|
ctx.log({
|
|
110686
111013
|
event: "debug_assistant_message",
|
|
@@ -110804,6 +111131,9 @@ function handleTurnComplete(ctx, event) {
|
|
|
110804
111131
|
});
|
|
110805
111132
|
}
|
|
110806
111133
|
}
|
|
111134
|
+
function isCodexMissingBearer401(runtimeId, error) {
|
|
111135
|
+
return runtimeId === "codex" && /missing\s+bearer/i.test(error) && error.includes("401");
|
|
111136
|
+
}
|
|
110807
111137
|
function handleSdkError(ctx, event) {
|
|
110808
111138
|
ctx.collabQueueTurnComplete();
|
|
110809
111139
|
ctx.pushManagerTurnEnd();
|
|
@@ -110820,6 +111150,13 @@ function handleSdkError(ctx, event) {
|
|
|
110820
111150
|
now: Date.now(),
|
|
110821
111151
|
error: event.error
|
|
110822
111152
|
});
|
|
111153
|
+
if (isCodexMissingBearer401(ctx.getRuntimeId(), event.error)) {
|
|
111154
|
+
ctx.onAuthNotLoggedIn(ctx.getRuntimeId());
|
|
111155
|
+
telemeter("codex_compact_auth_failure", {
|
|
111156
|
+
taskId: ctx.taskId,
|
|
111157
|
+
runtimeId: ctx.getRuntimeId()
|
|
111158
|
+
});
|
|
111159
|
+
}
|
|
110823
111160
|
}
|
|
110824
111161
|
}
|
|
110825
111162
|
function handleBillingOrRateLimitError(ctx, kind) {
|
|
@@ -111709,7 +112046,9 @@ var CompactionController = class {
|
|
|
111709
112046
|
this.#dispatchCompactionEventAndPersist(event);
|
|
111710
112047
|
}
|
|
111711
112048
|
#dispatchCompactionEventAndPersist(event) {
|
|
112049
|
+
const previousState = this.#compactionState;
|
|
111712
112050
|
const result = transition(this.#compactionState, event);
|
|
112051
|
+
if (result.newState === previousState && result.effects.length === 0) return;
|
|
111713
112052
|
this.#compactionState = result.newState;
|
|
111714
112053
|
this.#deps.persistCompactionRuntimeState(
|
|
111715
112054
|
result.newState.kind === "idle" ? null : result.newState
|
|
@@ -112628,7 +112967,7 @@ async function resolveTaskListPrepend(ctx) {
|
|
|
112628
112967
|
// src/services/task/cc-task-file-store.ts
|
|
112629
112968
|
import { readdir as readdir20, readFile as readFile47 } from "fs/promises";
|
|
112630
112969
|
import { homedir as homedir10 } from "os";
|
|
112631
|
-
import { basename as basename12, dirname as dirname35, join as
|
|
112970
|
+
import { basename as basename12, dirname as dirname35, join as join73 } from "path";
|
|
112632
112971
|
var VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
|
|
112633
112972
|
var IDLE_DETACH_MS = 5 * 60 * 1e3;
|
|
112634
112973
|
var IDLE_SWEEP_INTERVAL_MS = 6e4;
|
|
@@ -112644,7 +112983,7 @@ function isCCTaskFile(value) {
|
|
|
112644
112983
|
}
|
|
112645
112984
|
function createCCTaskFileWatcher(listId, log) {
|
|
112646
112985
|
const sanitizedId = sanitize(listId);
|
|
112647
|
-
const dir =
|
|
112986
|
+
const dir = join73(homedir10(), ".claude", "tasks", sanitizedId);
|
|
112648
112987
|
const targetDirName = basename12(dir);
|
|
112649
112988
|
const parentDir = dirname35(dir);
|
|
112650
112989
|
const watchState = {
|
|
@@ -112680,7 +113019,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
112680
113019
|
return tasks;
|
|
112681
113020
|
}
|
|
112682
113021
|
async function readTask(taskId) {
|
|
112683
|
-
const filePath =
|
|
113022
|
+
const filePath = join73(dir, `${taskId}.json`);
|
|
112684
113023
|
try {
|
|
112685
113024
|
const raw = await readFile47(filePath, "utf-8");
|
|
112686
113025
|
const parsed = JSON.parse(raw);
|
|
@@ -112747,7 +113086,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
112747
113086
|
watchState.parentSub = null;
|
|
112748
113087
|
return;
|
|
112749
113088
|
}
|
|
112750
|
-
if (ev.path.endsWith(targetDirName) || ev.path ===
|
|
113089
|
+
if (ev.path.endsWith(targetDirName) || ev.path === join73(parentDir, targetDirName)) {
|
|
112751
113090
|
void ensureDirWatcher();
|
|
112752
113091
|
break;
|
|
112753
113092
|
}
|
|
@@ -112863,7 +113202,7 @@ function createCCTaskFileWatcher(listId, log) {
|
|
|
112863
113202
|
// src/services/task/cc-task-file-writer.ts
|
|
112864
113203
|
import { createHash as createHash10 } from "crypto";
|
|
112865
113204
|
import { mkdir as mkdir34, readdir as readdir21, rename as rename21, unlink as unlink15, writeFile as writeFile27 } from "fs/promises";
|
|
112866
|
-
import { join as
|
|
113205
|
+
import { join as join74 } from "path";
|
|
112867
113206
|
function contentHash2(data) {
|
|
112868
113207
|
return createHash10("sha256").update(data).digest("hex").slice(0, 16);
|
|
112869
113208
|
}
|
|
@@ -112879,7 +113218,7 @@ async function writeTasks(dir, tasks, hashes) {
|
|
|
112879
113218
|
const json = JSON.stringify(structuredTaskToCCFile(task), null, 2);
|
|
112880
113219
|
const hash = contentHash2(json);
|
|
112881
113220
|
if (hashes.get(id) === hash) continue;
|
|
112882
|
-
await atomicWriteFile2(
|
|
113221
|
+
await atomicWriteFile2(join74(dir, `${id}.json`), json);
|
|
112883
113222
|
hashes.set(id, hash);
|
|
112884
113223
|
}
|
|
112885
113224
|
return targetIds;
|
|
@@ -112895,7 +113234,7 @@ async function removeStaleFiles(dir, targetIds, hashes) {
|
|
|
112895
113234
|
if (!entry.endsWith(".json")) continue;
|
|
112896
113235
|
const id = entry.slice(0, -".json".length);
|
|
112897
113236
|
if (!targetIds.has(id) && hashes.has(id)) {
|
|
112898
|
-
await unlink15(
|
|
113237
|
+
await unlink15(join74(dir, entry)).catch(() => {
|
|
112899
113238
|
});
|
|
112900
113239
|
hashes.delete(id);
|
|
112901
113240
|
}
|
|
@@ -113497,10 +113836,10 @@ var StructuredTaskTracker = class {
|
|
|
113497
113836
|
import { unwatchFile, watchFile } from "fs";
|
|
113498
113837
|
import { open as open2 } from "fs/promises";
|
|
113499
113838
|
import { homedir as homedir11 } from "os";
|
|
113500
|
-
import { join as
|
|
113839
|
+
import { join as join75 } from "path";
|
|
113501
113840
|
function computeTranscriptPath(cwd, sessionId, taskId) {
|
|
113502
113841
|
const cwdSlug = cwd.replace(/[^a-zA-Z0-9]/g, "-").slice(0, 200);
|
|
113503
|
-
return
|
|
113842
|
+
return join75(
|
|
113504
113843
|
homedir11(),
|
|
113505
113844
|
".claude",
|
|
113506
113845
|
"projects",
|
|
@@ -114210,14 +114549,59 @@ function trackPlanFileCreation(content, onPlanFile) {
|
|
|
114210
114549
|
}
|
|
114211
114550
|
|
|
114212
114551
|
// src/services/token-counter.ts
|
|
114213
|
-
import {
|
|
114552
|
+
import { spawn as spawn13 } from "child_process";
|
|
114214
114553
|
import { createHash as createHash11 } from "crypto";
|
|
114215
114554
|
import { unlink as unlink16, writeFile as writeFile28 } from "fs/promises";
|
|
114216
114555
|
import { createRequire as createRequire4 } from "module";
|
|
114217
114556
|
import { tmpdir } from "os";
|
|
114218
|
-
import { join as
|
|
114219
|
-
|
|
114220
|
-
|
|
114557
|
+
import { join as join76 } from "path";
|
|
114558
|
+
function spawnCollect(bin, args, opts) {
|
|
114559
|
+
return new Promise((resolve10, reject) => {
|
|
114560
|
+
const child = spawn13(bin, args, {
|
|
114561
|
+
cwd: opts.cwd,
|
|
114562
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
114563
|
+
env: opts.env
|
|
114564
|
+
});
|
|
114565
|
+
let stdout = "";
|
|
114566
|
+
let stderr = "";
|
|
114567
|
+
let settled = false;
|
|
114568
|
+
child.stdout?.on("data", (d) => {
|
|
114569
|
+
stdout += d.toString("utf-8");
|
|
114570
|
+
});
|
|
114571
|
+
child.stderr?.on("data", (d) => {
|
|
114572
|
+
stderr += d.toString("utf-8");
|
|
114573
|
+
});
|
|
114574
|
+
const timer = setTimeout(() => {
|
|
114575
|
+
if (settled) return;
|
|
114576
|
+
settled = true;
|
|
114577
|
+
child.kill("SIGTERM");
|
|
114578
|
+
reject(
|
|
114579
|
+
new Error(`Token count timed out after ${opts.timeout}ms. stderr: ${stderr.slice(0, 200)}`)
|
|
114580
|
+
);
|
|
114581
|
+
}, opts.timeout);
|
|
114582
|
+
child.on("close", (code) => {
|
|
114583
|
+
clearTimeout(timer);
|
|
114584
|
+
if (settled) return;
|
|
114585
|
+
settled = true;
|
|
114586
|
+
if (code === 0) {
|
|
114587
|
+
resolve10(stdout);
|
|
114588
|
+
} else {
|
|
114589
|
+
reject(
|
|
114590
|
+
new Error(
|
|
114591
|
+
`Command exited ${code ?? "null"}: ${bin} ${args.slice(0, 3).join(" ")}... stderr: ${stderr.trim().slice(0, 300)}`
|
|
114592
|
+
)
|
|
114593
|
+
);
|
|
114594
|
+
}
|
|
114595
|
+
});
|
|
114596
|
+
child.on("error", (err3) => {
|
|
114597
|
+
clearTimeout(timer);
|
|
114598
|
+
if (settled) return;
|
|
114599
|
+
settled = true;
|
|
114600
|
+
reject(err3);
|
|
114601
|
+
});
|
|
114602
|
+
});
|
|
114603
|
+
}
|
|
114604
|
+
var _spawnImpl = null;
|
|
114221
114605
|
function resolveDaemonNodeModules(log) {
|
|
114222
114606
|
try {
|
|
114223
114607
|
const thisDir = new URL(".", import.meta.url);
|
|
@@ -114228,7 +114612,7 @@ function resolveDaemonNodeModules(log) {
|
|
|
114228
114612
|
} catch {
|
|
114229
114613
|
}
|
|
114230
114614
|
try {
|
|
114231
|
-
const cwdRequire = createRequire4(
|
|
114615
|
+
const cwdRequire = createRequire4(join76(process.cwd(), "package.json"));
|
|
114232
114616
|
const sdkPath = cwdRequire.resolve("@modelcontextprotocol/sdk/server/mcp.js");
|
|
114233
114617
|
const idx = sdkPath.lastIndexOf("node_modules");
|
|
114234
114618
|
if (idx >= 0) return sdkPath.slice(0, idx + "node_modules".length);
|
|
@@ -114238,7 +114622,7 @@ function resolveDaemonNodeModules(log) {
|
|
|
114238
114622
|
error: err3 instanceof Error ? err3.message : String(err3)
|
|
114239
114623
|
});
|
|
114240
114624
|
}
|
|
114241
|
-
return
|
|
114625
|
+
return join76(process.cwd(), "node_modules");
|
|
114242
114626
|
}
|
|
114243
114627
|
function planTokenCount(systemPrompt, metadata, cwd, model, mcpServers) {
|
|
114244
114628
|
return {
|
|
@@ -114263,6 +114647,8 @@ function hashPlan(plan) {
|
|
|
114263
114647
|
}
|
|
114264
114648
|
var MAX_CACHE_SIZE = 50;
|
|
114265
114649
|
var CACHE_TTL_MS = 15 * 60 * 1e3;
|
|
114650
|
+
var FAILURE_BACKOFF_MS = 5 * 60 * 1e3;
|
|
114651
|
+
var failureBackoffUntilMs = 0;
|
|
114266
114652
|
var activeCount = 0;
|
|
114267
114653
|
var cache = /* @__PURE__ */ new Map();
|
|
114268
114654
|
function evictOldest() {
|
|
@@ -114435,7 +114821,7 @@ async function serializeMcpConfig(servers, allToolNames, daemonNodeModules, rese
|
|
|
114435
114821
|
if (isReservedMcpServerName(serverName, reservedServerNames)) continue;
|
|
114436
114822
|
const script = buildStubServerScript(serverName, tools);
|
|
114437
114823
|
if (!script) continue;
|
|
114438
|
-
const stubPath =
|
|
114824
|
+
const stubPath = join76(tmpdir(), `shipyard-mcp-stub-${serverName}-${Date.now()}.cjs`);
|
|
114439
114825
|
await writeFile28(stubPath, script, "utf-8");
|
|
114440
114826
|
stubPaths.push(stubPath);
|
|
114441
114827
|
mcpServers[serverName] = {
|
|
@@ -114461,11 +114847,37 @@ async function prepareTokenCountArgs(plan, log, reservedServerNames = SDK_RESERV
|
|
|
114461
114847
|
reservedServerNames
|
|
114462
114848
|
);
|
|
114463
114849
|
if (!serialized.config) return { args, mcpConfigPath: null, stubPaths: serialized.stubPaths };
|
|
114464
|
-
const mcpConfigPath =
|
|
114850
|
+
const mcpConfigPath = join76(tmpdir(), `shipyard-mcp-${Date.now()}.json`);
|
|
114465
114851
|
await writeFile28(mcpConfigPath, JSON.stringify(serialized.config), "utf-8");
|
|
114466
114852
|
args.push("--mcp-config", mcpConfigPath);
|
|
114467
114853
|
return { args, mcpConfigPath, stubPaths: serialized.stubPaths };
|
|
114468
114854
|
}
|
|
114855
|
+
async function runWithRetry(bin, plan, log, cleanupPaths) {
|
|
114856
|
+
const invokeSpawn = _spawnImpl ?? spawnCollect;
|
|
114857
|
+
const runAttempt = async (reservedServerNames) => {
|
|
114858
|
+
const attemptPrepared = await prepareTokenCountArgs(plan, log, reservedServerNames);
|
|
114859
|
+
cleanupPaths.push(...attemptPrepared.stubPaths);
|
|
114860
|
+
if (attemptPrepared.mcpConfigPath) cleanupPaths.push(attemptPrepared.mcpConfigPath);
|
|
114861
|
+
const stdout = await invokeSpawn(bin, attemptPrepared.args, {
|
|
114862
|
+
cwd: plan.cwd,
|
|
114863
|
+
timeout: 6e4,
|
|
114864
|
+
env: process.env
|
|
114865
|
+
});
|
|
114866
|
+
return { stdout, stubCount: attemptPrepared.stubPaths.length };
|
|
114867
|
+
};
|
|
114868
|
+
try {
|
|
114869
|
+
return await runAttempt(SDK_RESERVED_MCP_SERVER_NAMES);
|
|
114870
|
+
} catch (err3) {
|
|
114871
|
+
const reservedServerName = parseReservedMcpServerName(
|
|
114872
|
+
err3 instanceof Error ? err3.message : String(err3)
|
|
114873
|
+
);
|
|
114874
|
+
if (!reservedServerName) throw err3;
|
|
114875
|
+
log({ event: "token_count_reserved_mcp_retry", serverName: reservedServerName });
|
|
114876
|
+
const retryNames = new Set(SDK_RESERVED_MCP_SERVER_NAMES);
|
|
114877
|
+
retryNames.add(reservedServerName);
|
|
114878
|
+
return await runAttempt(retryNames);
|
|
114879
|
+
}
|
|
114880
|
+
}
|
|
114469
114881
|
async function executeTokenCount(plan, log) {
|
|
114470
114882
|
const key = hashPlan(plan);
|
|
114471
114883
|
const cached = getCached(key);
|
|
@@ -114473,51 +114885,39 @@ async function executeTokenCount(plan, log) {
|
|
|
114473
114885
|
log({ event: "token_count_cache_hit", cacheSize: cache.size });
|
|
114474
114886
|
return cached;
|
|
114475
114887
|
}
|
|
114888
|
+
const now = Date.now();
|
|
114889
|
+
if (now < failureBackoffUntilMs) {
|
|
114890
|
+
log({ event: "token_count_skipped_backoff", backoffRemainingMs: failureBackoffUntilMs - now });
|
|
114891
|
+
return null;
|
|
114892
|
+
}
|
|
114476
114893
|
if (activeCount >= 1) {
|
|
114477
114894
|
log({ event: "token_count_skipped_concurrent", activeCount });
|
|
114478
114895
|
return null;
|
|
114479
114896
|
}
|
|
114897
|
+
const claudeBin = resolveClaudeBinaryPath(log);
|
|
114898
|
+
if (!claudeBin) return null;
|
|
114480
114899
|
const cleanupPaths = [];
|
|
114481
114900
|
activeCount++;
|
|
114482
114901
|
try {
|
|
114483
114902
|
const t0 = Date.now();
|
|
114484
|
-
log({
|
|
114485
|
-
|
|
114486
|
-
|
|
114487
|
-
|
|
114488
|
-
|
|
114489
|
-
|
|
114490
|
-
|
|
114491
|
-
const { stdout } = await execFileAsync4(claudeBin, attemptPrepared.args, {
|
|
114492
|
-
cwd: plan.cwd,
|
|
114493
|
-
encoding: "utf-8",
|
|
114494
|
-
timeout: 6e4,
|
|
114495
|
-
env: process.env
|
|
114496
|
-
});
|
|
114497
|
-
return { stdout, stubCount: attemptPrepared.stubPaths.length };
|
|
114498
|
-
};
|
|
114499
|
-
let attempt;
|
|
114500
|
-
try {
|
|
114501
|
-
attempt = await runAttempt(SDK_RESERVED_MCP_SERVER_NAMES);
|
|
114502
|
-
} catch (err3) {
|
|
114503
|
-
const reservedServerName = parseReservedMcpServerName(
|
|
114504
|
-
err3 instanceof Error ? err3.message : String(err3)
|
|
114505
|
-
);
|
|
114506
|
-
if (!reservedServerName) throw err3;
|
|
114507
|
-
log({ event: "token_count_reserved_mcp_retry", serverName: reservedServerName });
|
|
114508
|
-
const retryReservedServerNames = new Set(SDK_RESERVED_MCP_SERVER_NAMES);
|
|
114509
|
-
retryReservedServerNames.add(reservedServerName);
|
|
114510
|
-
attempt = await runAttempt(retryReservedServerNames);
|
|
114511
|
-
}
|
|
114903
|
+
log({
|
|
114904
|
+
event: "token_count_started",
|
|
114905
|
+
cwd: plan.cwd,
|
|
114906
|
+
cacheSize: cache.size,
|
|
114907
|
+
binaryPath: claudeBin
|
|
114908
|
+
});
|
|
114909
|
+
const attempt = await runWithRetry(claudeBin, plan, log, cleanupPaths);
|
|
114512
114910
|
const parsed = JSON.parse(attempt.stdout);
|
|
114513
114911
|
const resultText = parsed.result ?? "";
|
|
114514
114912
|
if (!resultText) {
|
|
114913
|
+
failureBackoffUntilMs = Date.now() + FAILURE_BACKOFF_MS;
|
|
114515
114914
|
log({ event: "token_count_empty_result" });
|
|
114516
114915
|
return null;
|
|
114517
114916
|
}
|
|
114518
114917
|
const result = parseContextOutput(resultText, plan.shipyardSystemPrompt);
|
|
114519
114918
|
evictOldest();
|
|
114520
114919
|
cache.set(key, { result, expiry: Date.now() + CACHE_TTL_MS });
|
|
114920
|
+
failureBackoffUntilMs = 0;
|
|
114521
114921
|
log({
|
|
114522
114922
|
event: "token_count_complete",
|
|
114523
114923
|
durationMs: Date.now() - t0,
|
|
@@ -114533,7 +114933,13 @@ async function executeTokenCount(plan, log) {
|
|
|
114533
114933
|
return result;
|
|
114534
114934
|
} catch (err3) {
|
|
114535
114935
|
const errorMessage5 = err3 instanceof Error ? err3.message : String(err3);
|
|
114536
|
-
|
|
114936
|
+
failureBackoffUntilMs = Date.now() + FAILURE_BACKOFF_MS;
|
|
114937
|
+
log({
|
|
114938
|
+
event: "token_count_failed",
|
|
114939
|
+
error: errorMessage5,
|
|
114940
|
+
binaryPath: claudeBin,
|
|
114941
|
+
backoffUntilMs: failureBackoffUntilMs
|
|
114942
|
+
});
|
|
114537
114943
|
return null;
|
|
114538
114944
|
} finally {
|
|
114539
114945
|
activeCount--;
|
|
@@ -114812,8 +115218,8 @@ var Task = class {
|
|
|
114812
115218
|
* The wire `handoff_status.mode` the daemon actually ran for the most recent
|
|
114813
115219
|
* cross-runtime switch (Wave 2b). Stamped by `applyCrossRuntimeSwitch` from
|
|
114814
115220
|
* the orchestration layer's real decision (compact-then-bridge that actually
|
|
114815
|
-
* compacted, direct when
|
|
114816
|
-
* text-only source). Read at the destination spawn site so the broadcast
|
|
115221
|
+
* compacted, direct when source compaction could not start or failed,
|
|
115222
|
+
* cursor-source-lossy for a text-only source). Read at the destination spawn site so the broadcast
|
|
114817
115223
|
* `handoff_status` reflects ground truth instead of the browser's same-
|
|
114818
115224
|
* threshold prediction. null until the first switch lands.
|
|
114819
115225
|
*/
|
|
@@ -115339,7 +115745,8 @@ var Task = class {
|
|
|
115339
115745
|
* to avoid an extra `??` in this already-at-budget constructor.
|
|
115340
115746
|
*/
|
|
115341
115747
|
stallTimeoutMs: deps.stallTimeoutMs,
|
|
115342
|
-
firstStepStallTimeoutMs: deps.firstStepStallTimeoutMs
|
|
115748
|
+
firstStepStallTimeoutMs: deps.firstStepStallTimeoutMs,
|
|
115749
|
+
toolExecutionStallTimeoutMs: deps.toolExecutionStallTimeoutMs
|
|
115343
115750
|
},
|
|
115344
115751
|
this.#buildMainThreadCallbacks(deps)
|
|
115345
115752
|
);
|
|
@@ -115595,12 +116002,15 @@ var Task = class {
|
|
|
115595
116002
|
* OUT of idle before treating return-to-idle as success: `forceCompact()`
|
|
115596
116003
|
* returns sync but the SM only enters `compacting` LATER on the
|
|
115597
116004
|
* subprocess's `compaction_started`. Without the latch, poll #1 sees
|
|
115598
|
-
* idle and returns true before any compaction.
|
|
115599
|
-
*
|
|
115600
|
-
*
|
|
116005
|
+
* idle and returns true before any compaction. Once compaction starts, this
|
|
116006
|
+
* waits for the SM to settle rather than enforcing a total wall-clock
|
|
116007
|
+
* deadline; the SM's activity-reset silence watchdog handles runtime
|
|
116008
|
+
* liveness. Returns false on failure, no subprocess, or silence before the
|
|
116009
|
+
* compaction starts — caller falls through to direct bridging and the
|
|
116010
|
+
* destination's native compactor handles overflow.
|
|
115601
116011
|
*/
|
|
115602
|
-
async compactSelfForHandoff(
|
|
115603
|
-
return compactSelfForHandoff(this.#crossRuntimeCtx(),
|
|
116012
|
+
async compactSelfForHandoff(startSilenceMs = COMPACTION_SILENCE_TIMEOUT_MS) {
|
|
116013
|
+
return compactSelfForHandoff(this.#crossRuntimeCtx(), startSilenceMs);
|
|
115604
116014
|
}
|
|
115605
116015
|
/** Current working directory. May be undefined during early bootstrap before EnterWorktree. */
|
|
115606
116016
|
get cwd() {
|
|
@@ -117599,6 +118009,23 @@ function handlePlanContinueInTask(tasks, log, taskId, toolUseId, decision, feedb
|
|
|
117599
118009
|
task.orchestrator.handlePlanContinue(toolUseId, decision, feedback, opts);
|
|
117600
118010
|
}
|
|
117601
118011
|
|
|
118012
|
+
// src/services/task/manager/task-manager-stall-budgets.ts
|
|
118013
|
+
function resolveStallBudgets(agentSystem) {
|
|
118014
|
+
if (!isKnownAgentProvider(agentSystem)) {
|
|
118015
|
+
return {
|
|
118016
|
+
stallTimeoutMs: null,
|
|
118017
|
+
firstStepStallTimeoutMs: null,
|
|
118018
|
+
toolExecutionStallTimeoutMs: null
|
|
118019
|
+
};
|
|
118020
|
+
}
|
|
118021
|
+
const profile = profileRegistry.get(agentSystem);
|
|
118022
|
+
return {
|
|
118023
|
+
stallTimeoutMs: profile.stallTimeoutMs ?? null,
|
|
118024
|
+
firstStepStallTimeoutMs: profile.firstStepStallTimeoutMs ?? null,
|
|
118025
|
+
toolExecutionStallTimeoutMs: profile.toolExecutionStallTimeoutMs
|
|
118026
|
+
};
|
|
118027
|
+
}
|
|
118028
|
+
|
|
117602
118029
|
// src/services/task/manager/task-manager-threads.ts
|
|
117603
118030
|
function createThreadInTask(tasks, userId, params) {
|
|
117604
118031
|
const task = tasks.get(params.taskId);
|
|
@@ -119715,8 +120142,7 @@ var TaskManager = class {
|
|
|
119715
120142
|
initialOriginalCwd: opts?.initialOriginalCwd,
|
|
119716
120143
|
taskStateStore: this.#deps.taskStateStore,
|
|
119717
120144
|
ownerUserId: this.#deps.userId,
|
|
119718
|
-
|
|
119719
|
-
firstStepStallTimeoutMs: isKnownAgentProvider(opts.agentSystem) ? profileRegistry.get(opts.agentSystem).firstStepStallTimeoutMs ?? null : null
|
|
120145
|
+
...resolveStallBudgets(opts.agentSystem)
|
|
119720
120146
|
});
|
|
119721
120147
|
}
|
|
119722
120148
|
};
|
|
@@ -119730,11 +120156,11 @@ function pathExistsForLog2(path5) {
|
|
|
119730
120156
|
}
|
|
119731
120157
|
|
|
119732
120158
|
// src/services/task/manager/task-state-store.ts
|
|
119733
|
-
import { join as
|
|
120159
|
+
import { join as join78 } from "path";
|
|
119734
120160
|
|
|
119735
120161
|
// src/services/storage/directory-record-store.ts
|
|
119736
120162
|
import { mkdir as mkdir35, readdir as readdir22, readFile as readFile48, rename as rename22, stat as stat17, unlink as unlink17 } from "fs/promises";
|
|
119737
|
-
import { join as
|
|
120163
|
+
import { join as join77 } from "path";
|
|
119738
120164
|
var SCHEMA_VERSION_FILENAME = "__schema-version";
|
|
119739
120165
|
var RECORD_SUFFIX = ".json";
|
|
119740
120166
|
var SAFE_ID_RE = /^[A-Za-z0-9_-][A-Za-z0-9_.-]*$/;
|
|
@@ -119754,7 +120180,7 @@ function buildDirectoryRecordStore(opts) {
|
|
|
119754
120180
|
let writeQueue = Promise.resolve();
|
|
119755
120181
|
function recordPath(id) {
|
|
119756
120182
|
assertSafeRecordId(id);
|
|
119757
|
-
return
|
|
120183
|
+
return join77(dataDir, `${id}${RECORD_SUFFIX}`);
|
|
119758
120184
|
}
|
|
119759
120185
|
function notify(event) {
|
|
119760
120186
|
for (const listener of listeners) {
|
|
@@ -119769,7 +120195,7 @@ function buildDirectoryRecordStore(opts) {
|
|
|
119769
120195
|
}
|
|
119770
120196
|
async function readSchemaVersion() {
|
|
119771
120197
|
try {
|
|
119772
|
-
const raw = await readFile48(
|
|
120198
|
+
const raw = await readFile48(join77(dataDir, SCHEMA_VERSION_FILENAME), "utf-8");
|
|
119773
120199
|
const parsed = Number.parseInt(raw.trim(), 10);
|
|
119774
120200
|
return Number.isFinite(parsed) ? parsed : null;
|
|
119775
120201
|
} catch (err3) {
|
|
@@ -119778,7 +120204,7 @@ function buildDirectoryRecordStore(opts) {
|
|
|
119778
120204
|
}
|
|
119779
120205
|
}
|
|
119780
120206
|
async function writeSchemaVersion(version) {
|
|
119781
|
-
const path5 =
|
|
120207
|
+
const path5 = join77(dataDir, SCHEMA_VERSION_FILENAME);
|
|
119782
120208
|
await atomicWriteFile(path5, String(version));
|
|
119783
120209
|
}
|
|
119784
120210
|
async function legacyFileExists() {
|
|
@@ -119883,7 +120309,7 @@ function buildDirectoryRecordStore(opts) {
|
|
|
119883
120309
|
for (const entry of entries) {
|
|
119884
120310
|
if (!entry.endsWith(RECORD_SUFFIX)) continue;
|
|
119885
120311
|
const id = entry.slice(0, -RECORD_SUFFIX.length);
|
|
119886
|
-
const record = await loadOneRecord(
|
|
120312
|
+
const record = await loadOneRecord(join77(dataDir, entry), entry);
|
|
119887
120313
|
if (record !== null) records.set(id, record);
|
|
119888
120314
|
}
|
|
119889
120315
|
return records;
|
|
@@ -120184,8 +120610,8 @@ function computePaginatedPage(all, capturedVersion, opts) {
|
|
|
120184
120610
|
}
|
|
120185
120611
|
function buildTaskStateStore(dataDir) {
|
|
120186
120612
|
const store = buildDirectoryRecordStore({
|
|
120187
|
-
dataDir:
|
|
120188
|
-
legacyFilePath:
|
|
120613
|
+
dataDir: join78(dataDir, "tasks"),
|
|
120614
|
+
legacyFilePath: join78(dataDir, "tasks.json"),
|
|
120189
120615
|
recordSchema: TaskRecordSchema,
|
|
120190
120616
|
currentVersion: TASK_STORE_VERSION,
|
|
120191
120617
|
migrate(raw) {
|
|
@@ -121269,7 +121695,7 @@ async function createDaemon(deps) {
|
|
|
121269
121695
|
const annotationStore = buildAnnotationStore(deps.dataDir);
|
|
121270
121696
|
const deliverableStore = buildDeliverableStore(deps.dataDir);
|
|
121271
121697
|
const resourceRegistry = createResourceRegistry();
|
|
121272
|
-
const assetStore = buildAssetStore(
|
|
121698
|
+
const assetStore = buildAssetStore(join79(deps.dataDir, "assets"));
|
|
121273
121699
|
const shipyardResolver = createShipyardResolver();
|
|
121274
121700
|
shipyardResolver.addSubResolver("asset", createAssetResolver(assetStore));
|
|
121275
121701
|
const pluginResourceResolver = new PluginResourceResolver(deps.log);
|
|
@@ -121278,10 +121704,10 @@ async function createDaemon(deps) {
|
|
|
121278
121704
|
const capabilitiesRef = { current: null };
|
|
121279
121705
|
const rateLimitStoreRef = { current: null };
|
|
121280
121706
|
const gitCheckpoint = createGitCheckpointService();
|
|
121281
|
-
const userSettingsStore = buildUserSettingsStore(
|
|
121282
|
-
const projectsStore = buildProjectsStore(
|
|
121707
|
+
const userSettingsStore = buildUserSettingsStore(join79(deps.dataDir, "user-settings.json"));
|
|
121708
|
+
const projectsStore = buildProjectsStore(join79(deps.dataDir, "projects.json"));
|
|
121283
121709
|
const credentialsVaultStore = buildCredentialsVaultStore(
|
|
121284
|
-
|
|
121710
|
+
join79(deps.dataDir, "credentials-vault.json")
|
|
121285
121711
|
);
|
|
121286
121712
|
const cursorVaultKeyRef = { current: null };
|
|
121287
121713
|
let resolveCursorVaultKey;
|
|
@@ -121727,7 +122153,7 @@ async function createDaemon(deps) {
|
|
|
121727
122153
|
taskId: args.taskId,
|
|
121728
122154
|
agentSystem: args.agentSystem,
|
|
121729
122155
|
environmentKey: args.cwd,
|
|
121730
|
-
pluginsDir:
|
|
122156
|
+
pluginsDir: join79(deps.shipyardHome, "plugins"),
|
|
121731
122157
|
vizWatcher: getOrCreateVizWatcher(args.taskId).watcher,
|
|
121732
122158
|
mode: "task",
|
|
121733
122159
|
previewProxy: deps.previewProxy,
|
|
@@ -121820,7 +122246,7 @@ async function createDaemon(deps) {
|
|
|
121820
122246
|
/** Claude in-process harness — Codex uses the HTTP path with 'codex' above. */
|
|
121821
122247
|
agentSystem: AGENT_SYSTEM_CLAUDE_CODE,
|
|
121822
122248
|
environmentKey: args.cwd,
|
|
121823
|
-
pluginsDir:
|
|
122249
|
+
pluginsDir: join79(deps.shipyardHome, "plugins"),
|
|
121824
122250
|
vizWatcher: args.vizWatcher,
|
|
121825
122251
|
mode: args.mode ?? "task",
|
|
121826
122252
|
previewProxy: deps.previewProxy,
|
|
@@ -121907,6 +122333,7 @@ async function createDaemon(deps) {
|
|
|
121907
122333
|
deps.log
|
|
121908
122334
|
);
|
|
121909
122335
|
subprocess.installHarnessTaskIdSetter(setHarnessTaskId);
|
|
122336
|
+
subprocess.installHarnessEpoch(epoch);
|
|
121910
122337
|
return subprocess;
|
|
121911
122338
|
}
|
|
121912
122339
|
const lastUsageLimitBroadcastByTask = /* @__PURE__ */ new Map();
|
|
@@ -122146,7 +122573,7 @@ async function createDaemon(deps) {
|
|
|
122146
122573
|
if (!cwd) return null;
|
|
122147
122574
|
try {
|
|
122148
122575
|
const { gitExecSafe: gitExecSafe2 } = await import("./git-pool-V73Q53NX.js");
|
|
122149
|
-
const { getRepoDefaultBranch: getRepoDefaultBranch2 } = await import("./git-repo-
|
|
122576
|
+
const { getRepoDefaultBranch: getRepoDefaultBranch2 } = await import("./git-repo-CTZJS3ER.js");
|
|
122150
122577
|
const branch = await gitExecSafe2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? "";
|
|
122151
122578
|
if (!branch || branch === "HEAD") return null;
|
|
122152
122579
|
const pr = branchPrStateCache.get(taskId)?.currentBranchPR;
|
|
@@ -122834,7 +123261,7 @@ async function createDaemon(deps) {
|
|
|
122834
123261
|
const stallProfilerConfig = getStallProfilerConfig();
|
|
122835
123262
|
const stallProfiler = new StallProfiler({
|
|
122836
123263
|
log: deps.log,
|
|
122837
|
-
outDir:
|
|
123264
|
+
outDir: join79(deps.dataDir, "stall-profiles"),
|
|
122838
123265
|
thresholdMs: stallProfilerConfig.thresholdMs,
|
|
122839
123266
|
captureMs: stallProfilerConfig.captureMs,
|
|
122840
123267
|
rateLimitMs: stallProfilerConfig.rateLimitMs
|
|
@@ -123434,10 +123861,10 @@ async function createDaemon(deps) {
|
|
|
123434
123861
|
import { existsSync as existsSync12 } from "fs";
|
|
123435
123862
|
|
|
123436
123863
|
// src/services/bootstrap/self-update.ts
|
|
123437
|
-
import { execFile as
|
|
123864
|
+
import { execFile as execFile11, spawn as spawn14 } from "child_process";
|
|
123438
123865
|
import { createHash as createHash12 } from "crypto";
|
|
123439
123866
|
import { chmod as chmod3, mkdir as mkdir37, readFile as readFile49, rename as rename23, unlink as unlink18, writeFile as writeFile29 } from "fs/promises";
|
|
123440
|
-
import { join as
|
|
123867
|
+
import { join as join80 } from "path";
|
|
123441
123868
|
|
|
123442
123869
|
// src/services/bootstrap/self-update-installer-scripts.ts
|
|
123443
123870
|
function buildPosixInstallerScript(params) {
|
|
@@ -123810,7 +124237,7 @@ function parseNpmViewOutput(stdout) {
|
|
|
123810
124237
|
}
|
|
123811
124238
|
async function defaultResolveVersion(pkg, tag) {
|
|
123812
124239
|
return new Promise((resolve10, reject) => {
|
|
123813
|
-
|
|
124240
|
+
execFile11(
|
|
123814
124241
|
"npm",
|
|
123815
124242
|
["view", `${pkg}@${tag}`, "version", "dist.tarball", "dist.shasum", "--json"],
|
|
123816
124243
|
{ timeout: NPM_VIEW_TIMEOUT_MS, encoding: "utf-8" },
|
|
@@ -123839,7 +124266,7 @@ async function downloadTarball(url, destPath, fetchFn) {
|
|
|
123839
124266
|
throw new Error(`download failed: HTTP ${response.status} ${response.statusText}`);
|
|
123840
124267
|
}
|
|
123841
124268
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
123842
|
-
await mkdir37(
|
|
124269
|
+
await mkdir37(join80(destPath, ".."), { recursive: true });
|
|
123843
124270
|
try {
|
|
123844
124271
|
await writeFile29(tmpPath, bytes);
|
|
123845
124272
|
await rename23(tmpPath, destPath);
|
|
@@ -123869,7 +124296,7 @@ async function verifyChecksum(path5, expectedHash) {
|
|
|
123869
124296
|
}
|
|
123870
124297
|
}
|
|
123871
124298
|
async function stageInstallerScript(scriptPath, params) {
|
|
123872
|
-
await mkdir37(
|
|
124299
|
+
await mkdir37(join80(scriptPath, ".."), { recursive: true });
|
|
123873
124300
|
const body = process.platform === "win32" ? buildWindowsInstallerScript(params) : buildPosixInstallerScript(params);
|
|
123874
124301
|
await writeFile29(scriptPath, body);
|
|
123875
124302
|
await chmod3(scriptPath, 493);
|
|
@@ -123919,16 +124346,16 @@ function buildInstallerParams(state, deps) {
|
|
|
123919
124346
|
targetVersion: state.resolved.version,
|
|
123920
124347
|
previousVersion: deps.currentVersion,
|
|
123921
124348
|
parentPid: deps.pid,
|
|
123922
|
-
statusFilePath:
|
|
123923
|
-
pidFilePath:
|
|
123924
|
-
logPath:
|
|
123925
|
-
snapshotPath:
|
|
124349
|
+
statusFilePath: join80(deps.shipyardHome, "update-status.json"),
|
|
124350
|
+
pidFilePath: join80(deps.shipyardHome, "daemon.pid"),
|
|
124351
|
+
logPath: join80(deps.shipyardHome, "updates", `install-${deps.pid}.log`),
|
|
124352
|
+
snapshotPath: join80(deps.shipyardHome, "updates", `rollback-${deps.currentVersion}`),
|
|
123926
124353
|
npmBin: "npm"
|
|
123927
124354
|
};
|
|
123928
124355
|
}
|
|
123929
124356
|
function tarballPathFor(shipyardHome, version, shasum) {
|
|
123930
124357
|
const shaPrefix = shasum.slice(0, 12);
|
|
123931
|
-
return
|
|
124358
|
+
return join80(shipyardHome, "updates", `${version}-${shaPrefix}.tgz`);
|
|
123932
124359
|
}
|
|
123933
124360
|
async function runResolveStep(step, state, deps) {
|
|
123934
124361
|
const resolver = deps.resolveVersion ?? defaultResolveVersion;
|
|
@@ -123960,7 +124387,7 @@ async function runStageScriptStep(step, state, deps) {
|
|
|
123960
124387
|
);
|
|
123961
124388
|
await stageInstallerScript(step.scriptPath, params);
|
|
123962
124389
|
}
|
|
123963
|
-
var defaultSpawnCommand = (cmd, args, opts) =>
|
|
124390
|
+
var defaultSpawnCommand = (cmd, args, opts) => spawn14(cmd, [...args], opts ?? {});
|
|
123964
124391
|
function runSpawnStep(step, state, deps) {
|
|
123965
124392
|
const spawnFn = deps.spawnCommand ?? defaultSpawnCommand;
|
|
123966
124393
|
spawnInstaller(step.scriptPath, step.logPath, spawnFn);
|
|
@@ -124150,26 +124577,27 @@ var WARN_EVENTS = /* @__PURE__ */ new Set([
|
|
|
124150
124577
|
"webrtc_offer_handshake_failed",
|
|
124151
124578
|
"webrtc_initiate_handshake_failed",
|
|
124152
124579
|
/**
|
|
124153
|
-
* At-least-once delivery
|
|
124154
|
-
*
|
|
124155
|
-
*
|
|
124156
|
-
*
|
|
124157
|
-
*
|
|
124158
|
-
*
|
|
124159
|
-
*
|
|
124160
|
-
*
|
|
124580
|
+
* At-least-once delivery health summary. Replaces per-event `alo_resend`
|
|
124581
|
+
* warn noise (411K lines / 25-day corpus). One summary per stream per 30s
|
|
124582
|
+
* with pending count, resend count, drop count, and oldest message age.
|
|
124583
|
+
* `alo_dead_letter` and the orphan reaper are stronger failure signals
|
|
124584
|
+
* (entry permanently dropped / channel wedged) and remain at warn.
|
|
124585
|
+
*
|
|
124586
|
+
* `alo_resend` per-event lines are demoted to debug (see DEBUG_EVENTS
|
|
124587
|
+
* below); `alo_resend_exhausted` is an explicit error emitted by the shell
|
|
124588
|
+
* when an entry exceeds maxDeliver — the shell carries `{level:'error'}` so
|
|
124589
|
+
* classifyLogLevel honours it without needing it in WARN_EVENTS.
|
|
124161
124590
|
*/
|
|
124162
|
-
"
|
|
124591
|
+
"alo_health",
|
|
124163
124592
|
"alo_dead_letter",
|
|
124164
124593
|
"alo_shell_orphan_reaped",
|
|
124165
124594
|
/**
|
|
124166
|
-
* Memory/health heartbeats
|
|
124167
|
-
*
|
|
124168
|
-
*
|
|
124169
|
-
*
|
|
124170
|
-
*
|
|
124171
|
-
*
|
|
124172
|
-
* the D1 telemetry path (metricsCollector.capture) is already level-independent.
|
|
124595
|
+
* Memory/health heartbeats promoted to WARN for observability: a 50GB-RAM incident
|
|
124596
|
+
* had ZERO daemon memory samples in its logs (sessions died sub-10s, and even
|
|
124597
|
+
* healthy post-incident days were warn-only under the old default). Keeping them at
|
|
124598
|
+
* WARN ensures RSS/heap curves appear even in environments where LOG_LEVEL is
|
|
124599
|
+
* explicitly set to warn; the D1 telemetry path (metricsCollector.capture) is
|
|
124600
|
+
* already level-independent and always fires regardless.
|
|
124173
124601
|
*/
|
|
124174
124602
|
"health_snapshot",
|
|
124175
124603
|
"browser_metrics_sample"
|
|
@@ -124208,11 +124636,17 @@ var DEBUG_EVENTS = /* @__PURE__ */ new Set([
|
|
|
124208
124636
|
* occasionally carries an error will get silently demoted to debug.
|
|
124209
124637
|
* Only demote events that are pure happy-path counters with no error
|
|
124210
124638
|
* field. `msg_received` is intentionally EXCLUDED because it can carry
|
|
124211
|
-
* a parse error
|
|
124212
|
-
*
|
|
124639
|
+
* a parse error.
|
|
124640
|
+
*
|
|
124641
|
+
* `alo_resend` per-event lines are demoted here (#4511). The shell
|
|
124642
|
+
* carries `{level:'debug'}` on every event; this belt-and-suspenders
|
|
124643
|
+
* entry ensures classifyLogLevel agrees even if the level field is ever
|
|
124644
|
+
* removed. The `alo_health` periodic summary in WARN_EVENTS is the
|
|
124645
|
+
* intended production-visible signal for retry pressure.
|
|
124213
124646
|
*/
|
|
124214
124647
|
"channel_send",
|
|
124215
124648
|
"alo_ack_received",
|
|
124649
|
+
"alo_resend",
|
|
124216
124650
|
"dc_buffered_sample",
|
|
124217
124651
|
"control_message_handled"
|
|
124218
124652
|
]);
|
|
@@ -124295,7 +124729,7 @@ function routeSignalingMessage(peerManager, signalingHandle, log) {
|
|
|
124295
124729
|
}
|
|
124296
124730
|
|
|
124297
124731
|
// src/services/lsp/lsp-bridge.ts
|
|
124298
|
-
import { spawn as
|
|
124732
|
+
import { spawn as spawn15 } from "child_process";
|
|
124299
124733
|
|
|
124300
124734
|
// src/services/lsp/lsp-server-supervisor.ts
|
|
124301
124735
|
var SHUTDOWN_GRACE_MS = 5e3;
|
|
@@ -124571,7 +125005,7 @@ function handleLSPChannel(cwd, send, log, languageId) {
|
|
|
124571
125005
|
serverId: server.serverId
|
|
124572
125006
|
}),
|
|
124573
125007
|
log,
|
|
124574
|
-
spawn:
|
|
125008
|
+
spawn: spawn15,
|
|
124575
125009
|
now: () => Date.now(),
|
|
124576
125010
|
setTimeoutFn: (fn, ms) => setTimeout(fn, ms),
|
|
124577
125011
|
clearTimeoutFn: (timer) => {
|
|
@@ -125003,7 +125437,7 @@ function buildLocalDirectChannelCallbacks(deps) {
|
|
|
125003
125437
|
|
|
125004
125438
|
// src/services/skills-cache/codex-skills-cache.ts
|
|
125005
125439
|
import { mkdir as mkdir38, readFile as readFile50, rename as rename24, writeFile as writeFile30 } from "fs/promises";
|
|
125006
|
-
import { dirname as dirname37, join as
|
|
125440
|
+
import { dirname as dirname37, join as join81 } from "path";
|
|
125007
125441
|
var CACHE_FILENAME = "codex-skills-cache.json";
|
|
125008
125442
|
var SkillInfoCacheEntrySchema = external_exports.object({
|
|
125009
125443
|
name: external_exports.string(),
|
|
@@ -125020,7 +125454,7 @@ var CodexSkillsCacheFileSchema = external_exports.object({
|
|
|
125020
125454
|
skills: external_exports.array(SkillInfoCacheEntrySchema)
|
|
125021
125455
|
});
|
|
125022
125456
|
function defaultCodexSkillsCachePath(shipyardHome) {
|
|
125023
|
-
return
|
|
125457
|
+
return join81(shipyardHome, "state", CACHE_FILENAME);
|
|
125024
125458
|
}
|
|
125025
125459
|
async function loadCodexSkillsCache(path5) {
|
|
125026
125460
|
let raw;
|
|
@@ -125084,11 +125518,11 @@ async function saveCodexSkillsCache(path5, cache2) {
|
|
|
125084
125518
|
// src/services/skills-mirror/manager.ts
|
|
125085
125519
|
import { lstat, mkdir as mkdir40, readlink, stat as stat18, symlink, unlink as unlink20 } from "fs/promises";
|
|
125086
125520
|
import { homedir as homedir13 } from "os";
|
|
125087
|
-
import { dirname as dirname39, join as
|
|
125521
|
+
import { dirname as dirname39, join as join83 } from "path";
|
|
125088
125522
|
|
|
125089
125523
|
// src/services/skills-mirror/manifest.ts
|
|
125090
125524
|
import { mkdir as mkdir39, readFile as readFile51, rename as rename25, unlink as unlink19, writeFile as writeFile31 } from "fs/promises";
|
|
125091
|
-
import { dirname as dirname38, join as
|
|
125525
|
+
import { dirname as dirname38, join as join82 } from "path";
|
|
125092
125526
|
var MANIFEST_VERSION = 1;
|
|
125093
125527
|
var ManifestEntrySchema = external_exports.object({
|
|
125094
125528
|
/** Display name of the skill (e.g. `qa`, or `plugin:name` when namespaced). */
|
|
@@ -125108,7 +125542,7 @@ function emptyManifest() {
|
|
|
125108
125542
|
return { version: MANIFEST_VERSION, entries: [] };
|
|
125109
125543
|
}
|
|
125110
125544
|
function defaultManifestPath(shipyardHome) {
|
|
125111
|
-
return
|
|
125545
|
+
return join82(shipyardHome, "state", "skill-mirror-manifest.json");
|
|
125112
125546
|
}
|
|
125113
125547
|
async function loadManifest(path5) {
|
|
125114
125548
|
try {
|
|
@@ -125139,15 +125573,15 @@ async function deleteManifest(path5) {
|
|
|
125139
125573
|
function defaultPaths() {
|
|
125140
125574
|
const home = homedir13();
|
|
125141
125575
|
return {
|
|
125142
|
-
claudeSkillsDir:
|
|
125143
|
-
codexSkillsDir:
|
|
125576
|
+
claudeSkillsDir: join83(home, ".claude", "skills"),
|
|
125577
|
+
codexSkillsDir: join83(home, ".agents", "skills"),
|
|
125144
125578
|
manifestPath: defaultManifestPath(getShipyardHome())
|
|
125145
125579
|
};
|
|
125146
125580
|
}
|
|
125147
125581
|
function plannedSymlinkPath(skill, paths) {
|
|
125148
125582
|
const linkName = skill.namespace ? `${skill.namespace}:${skill.name}` : skill.name;
|
|
125149
|
-
if (skill.sourceAgent === "claude-code") return
|
|
125150
|
-
if (skill.sourceAgent === "codex") return
|
|
125583
|
+
if (skill.sourceAgent === "claude-code") return join83(paths.codexSkillsDir, linkName);
|
|
125584
|
+
if (skill.sourceAgent === "codex") return join83(paths.claudeSkillsDir, linkName);
|
|
125151
125585
|
return null;
|
|
125152
125586
|
}
|
|
125153
125587
|
function isPermissionError(err3) {
|
|
@@ -125360,7 +125794,7 @@ async function ensureMirror(skills, config, paths = defaultPaths()) {
|
|
|
125360
125794
|
|
|
125361
125795
|
// src/services/skills-mirror/prefs.ts
|
|
125362
125796
|
import { mkdir as mkdir41, readFile as readFile52, rename as rename26, writeFile as writeFile32 } from "fs/promises";
|
|
125363
|
-
import { dirname as dirname40, join as
|
|
125797
|
+
import { dirname as dirname40, join as join84 } from "path";
|
|
125364
125798
|
var SkillsMirrorPrefsSchema = external_exports.object({
|
|
125365
125799
|
enabled: external_exports.boolean().default(true)
|
|
125366
125800
|
});
|
|
@@ -125369,7 +125803,7 @@ function defaultSkillsMirrorPrefs() {
|
|
|
125369
125803
|
return { enabled: true };
|
|
125370
125804
|
}
|
|
125371
125805
|
function prefsPath(dataDir) {
|
|
125372
|
-
return
|
|
125806
|
+
return join84(dataDir, FILENAME);
|
|
125373
125807
|
}
|
|
125374
125808
|
async function loadSkillsMirrorPrefs(dataDir) {
|
|
125375
125809
|
try {
|
|
@@ -125418,7 +125852,7 @@ async function reconcileSkillsMirror(args) {
|
|
|
125418
125852
|
// src/services/storage/daemon-settings-store.ts
|
|
125419
125853
|
import { createHash as createHash13 } from "crypto";
|
|
125420
125854
|
import { mkdir as mkdir42, readFile as readFile53 } from "fs/promises";
|
|
125421
|
-
import { join as
|
|
125855
|
+
import { join as join85 } from "path";
|
|
125422
125856
|
var ProjectSettingsSchema = external_exports.object({
|
|
125423
125857
|
disabledMcpServers: external_exports.array(external_exports.string()).optional(),
|
|
125424
125858
|
previewMigrationV36Done: external_exports.boolean().default(false)
|
|
@@ -125427,9 +125861,9 @@ function hashProjectPath(projectPath) {
|
|
|
125427
125861
|
return createHash13("sha256").update(projectPath).digest("hex").slice(0, 16);
|
|
125428
125862
|
}
|
|
125429
125863
|
function buildDaemonSettingsStore(dataDir) {
|
|
125430
|
-
const settingsDir =
|
|
125864
|
+
const settingsDir = join85(dataDir, "settings");
|
|
125431
125865
|
function settingsPath(projectPath) {
|
|
125432
|
-
return
|
|
125866
|
+
return join85(settingsDir, `${hashProjectPath(projectPath)}.json`);
|
|
125433
125867
|
}
|
|
125434
125868
|
async function ensureDir() {
|
|
125435
125869
|
await mkdir42(settingsDir, { recursive: true });
|
|
@@ -125454,12 +125888,12 @@ function buildDaemonSettingsStore(dataDir) {
|
|
|
125454
125888
|
|
|
125455
125889
|
// src/services/storage/plugin-config-store.ts
|
|
125456
125890
|
import { mkdir as mkdir43, readFile as readFile54 } from "fs/promises";
|
|
125457
|
-
import { join as
|
|
125891
|
+
import { join as join86 } from "path";
|
|
125458
125892
|
function buildPluginConfigStore(pluginsDir) {
|
|
125459
125893
|
const cache2 = /* @__PURE__ */ new Map();
|
|
125460
125894
|
const writeQueues = /* @__PURE__ */ new Map();
|
|
125461
125895
|
function configPath(pluginId) {
|
|
125462
|
-
return
|
|
125896
|
+
return join86(pluginsDir, pluginId, "config.json");
|
|
125463
125897
|
}
|
|
125464
125898
|
async function readConfigFromDisk(pluginId) {
|
|
125465
125899
|
const fp = configPath(pluginId);
|
|
@@ -125513,7 +125947,7 @@ function buildPluginConfigStore(pluginsDir) {
|
|
|
125513
125947
|
const fresh = await readConfigFromDisk(pluginId);
|
|
125514
125948
|
fresh[key] = value;
|
|
125515
125949
|
cache2.set(pluginId, fresh);
|
|
125516
|
-
await mkdir43(
|
|
125950
|
+
await mkdir43(join86(pluginsDir, pluginId), { recursive: true });
|
|
125517
125951
|
await atomicWriteFile(configPath(pluginId), JSON.stringify(fresh, null, 2));
|
|
125518
125952
|
})
|
|
125519
125953
|
);
|
|
@@ -125759,7 +126193,7 @@ async function serve(options = {}) {
|
|
|
125759
126193
|
}
|
|
125760
126194
|
async function runServeBody(options, captureRefs) {
|
|
125761
126195
|
const shipyardHome = options.shipyardHome ?? getShipyardHome();
|
|
125762
|
-
const dataDir =
|
|
126196
|
+
const dataDir = join87(shipyardHome, options.isDev ? "data-dev" : "data");
|
|
125763
126197
|
const log = createChildLogger({ mode: "serve" });
|
|
125764
126198
|
log.info(
|
|
125765
126199
|
{ event: "daemon_start", version: getDaemonVersion(), pid: process.pid },
|
|
@@ -125767,7 +126201,7 @@ async function runServeBody(options, captureRefs) {
|
|
|
125767
126201
|
);
|
|
125768
126202
|
const { workspaceRoot, unscopedWorkspace, unscopedReason } = await resolveWorkspaceScope();
|
|
125769
126203
|
registerBuiltinPlugins();
|
|
125770
|
-
const pluginConfigStore = buildPluginConfigStore(
|
|
126204
|
+
const pluginConfigStore = buildPluginConfigStore(join87(dataDir, "plugins"));
|
|
125771
126205
|
await mkdir44(dataDir, { recursive: true });
|
|
125772
126206
|
log.info(
|
|
125773
126207
|
{
|
|
@@ -125860,9 +126294,9 @@ async function runServeBody(options, captureRefs) {
|
|
|
125860
126294
|
await bootstrapPhase("pid_sweep_orphans", () => pidTracker.sweepOrphans(logAdapter));
|
|
125861
126295
|
await bootstrapPhase(
|
|
125862
126296
|
"legacy_epoch_prune",
|
|
125863
|
-
() => pruneOldEpochData(
|
|
126297
|
+
() => pruneOldEpochData(join87(dataDir, "loro"), LEGACY_EPOCH, logAdapter)
|
|
125864
126298
|
);
|
|
125865
|
-
const storage = new FileStorageAdapter(
|
|
126299
|
+
const storage = new FileStorageAdapter(join87(dataDir, "loro"));
|
|
125866
126300
|
const personalWebrtcAdapter = new WebRtcDataChannelAdapter();
|
|
125867
126301
|
const peerRoleRegistry = createPeerRoleRegistry();
|
|
125868
126302
|
const presencePoolRef = { pool: {} };
|
|
@@ -125970,14 +126404,14 @@ async function runServeBody(options, captureRefs) {
|
|
|
125970
126404
|
current: []
|
|
125971
126405
|
};
|
|
125972
126406
|
const terminalPtys = /* @__PURE__ */ new Map();
|
|
125973
|
-
const terminalLogsDir =
|
|
126407
|
+
const terminalLogsDir = join87(shipyardHome, "data", "terminals");
|
|
125974
126408
|
await mkdir44(terminalLogsDir, { recursive: true, mode: 448 });
|
|
125975
126409
|
const publishedArtifactStore = createPublishedArtifactStore({
|
|
125976
|
-
rootDir:
|
|
126410
|
+
rootDir: join87(dataDir, "published"),
|
|
125977
126411
|
log: logAdapter
|
|
125978
126412
|
});
|
|
125979
126413
|
const previewStateStore = createPreviewStateStore({
|
|
125980
|
-
rootDir:
|
|
126414
|
+
rootDir: join87(dataDir, "preview-state"),
|
|
125981
126415
|
logger: log
|
|
125982
126416
|
});
|
|
125983
126417
|
let codexSkillsApplyScopedPatchRef = null;
|
|
@@ -126056,7 +126490,7 @@ async function runServeBody(options, captureRefs) {
|
|
|
126056
126490
|
daemon.healthMetrics.start();
|
|
126057
126491
|
publishedArtifactStore.setSendControlMessage((msg) => daemon.taskManager.broadcastControl(msg));
|
|
126058
126492
|
previewStateStore.setSendControlMessage((msg) => daemon.taskManager.broadcastControl(msg));
|
|
126059
|
-
const pluginsDir =
|
|
126493
|
+
const pluginsDir = join87(shipyardHome, "plugins");
|
|
126060
126494
|
await mkdir44(pluginsDir, { recursive: true });
|
|
126061
126495
|
let loadedPlugins = [];
|
|
126062
126496
|
const loadedPluginsRef = {
|
|
@@ -126458,7 +126892,9 @@ async function runServeBody(options, captureRefs) {
|
|
|
126458
126892
|
}, delay2);
|
|
126459
126893
|
capabilityBackstopTimer.unref();
|
|
126460
126894
|
};
|
|
126461
|
-
|
|
126895
|
+
void daemon.capabilityDetectionReady.then(() => {
|
|
126896
|
+
if (!disposed) scheduleCapabilityBackstop();
|
|
126897
|
+
});
|
|
126462
126898
|
lifecycle.onShutdown(async () => {
|
|
126463
126899
|
disposed = true;
|
|
126464
126900
|
if (capabilityBackstopTimer) clearTimeout(capabilityBackstopTimer);
|
|
@@ -126581,4 +127017,4 @@ export {
|
|
|
126581
127017
|
decideWorkspaceScope,
|
|
126582
127018
|
serve
|
|
126583
127019
|
};
|
|
126584
|
-
//# sourceMappingURL=serve-
|
|
127020
|
+
//# sourceMappingURL=serve-P3U2C5YH.js.map
|