open-walnut 0.4.1 → 0.4.2
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/README.md +2 -5
- package/dist/cli-fast.js +69 -46
- package/dist/cli.js +704 -274
- package/dist/daemon-binaries/acp-worker.js.version +1 -1
- package/dist/web/server.js +704 -274
- package/dist/web/static/assets/{OfficePreview-C8OMbtiq.js → OfficePreview-CEHO0xkd.js} +2 -2
- package/dist/web/static/assets/{docx-preview-hDgtrOpv.js → docx-preview-CuZwjAMS.js} +1 -1
- package/dist/web/static/assets/{index-CWIydpPr.js → index-0MyC-yGG.js} +1 -1
- package/dist/web/static/assets/{index-BAb6UVet.js → index-BBjg7m-_.js} +1 -1
- package/dist/web/static/assets/{index-D1uUk0AQ.js → index-BFF_G72i.js} +289 -289
- package/dist/web/static/assets/{index-CPHXL-wy.js → index-BRLxNhyy.js} +1 -1
- package/dist/web/static/assets/{index-VcSnwuHG.js → index-Bqg4_WEG.js} +1 -1
- package/dist/web/static/assets/{index-bfZj1PAc.js → index-C2YGUYGS.js} +1 -1
- package/dist/web/static/assets/{index-ph8Hxff-.js → index-D1dpOEzW.js} +1 -1
- package/dist/web/static/assets/{index-nRBC-NJN.js → index-D9qPPm7h.js} +1 -1
- package/dist/web/static/assets/{index-D81YKipC.js → index-DAOPV34I.js} +1 -1
- package/dist/web/static/assets/{index-CBHRNnCk.js → index-DLjK8dGE.js} +1 -1
- package/dist/web/static/assets/{index-KY36bfpA.js → index-DOXm2Aye.js} +1 -1
- package/dist/web/static/assets/{index-EowLf_Xf.js → index-DR-LSqAg.js} +1 -1
- package/dist/web/static/assets/{index-gv-TJwHT.js → index-DXtp_ZWh.js} +1 -1
- package/dist/web/static/assets/index-DeYCGmOR.css +32 -0
- package/dist/web/static/assets/{index-B2-N8jTH.js → index-DrWzb338.js} +1 -1
- package/dist/web/static/assets/{index-Caj09uf7.js → index-DtRwkh8J.js} +1 -1
- package/dist/web/static/assets/{index-C5-vT4kT.js → index-ccs5s80D.js} +1 -1
- package/dist/web/static/assets/{index-DtRt21Zi.js → index-mWEwJcqw.js} +1 -1
- package/dist/web/static/assets/{index-B35fidy6.js → index-unnPilnK.js} +1 -1
- package/dist/web/static/assets/{jszip.min-DyrlNEg4.js → jszip.min-DYFwy4PE.js} +1 -1
- package/dist/web/static/assets/{pptx-preview.es-DjND6y6O.js → pptx-preview.es-eu2nX9JC.js} +1 -1
- package/dist/web/static/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/static/assets/index-Btn4AoIy.css +0 -32
package/dist/cli.js
CHANGED
|
@@ -8815,24 +8815,25 @@ function reapSession(sid, code, reason) {
|
|
|
8815
8815
|
let jsonlAgeMs = null;
|
|
8816
8816
|
try { jsonlAgeMs = Date.now() - fs.statSync(session.jsonlPath).mtimeMs; } catch {}
|
|
8817
8817
|
const cleanExit = isTurnCompleteExit(session.jsonlPath);
|
|
8818
|
-
//
|
|
8819
|
-
//
|
|
8820
|
-
// before
|
|
8821
|
-
// it (usually orphan-poll ESRCH \u2192 code -1). Verified
|
|
8822
|
-
// line was a control_response (the reply to a
|
|
8823
|
-
// a routine shape), so isTurnCompleteExit said
|
|
8824
|
-
// the projection painted a red "Error" over a
|
|
8825
|
-
//
|
|
8826
|
-
// information the tail does not contain,
|
|
8827
|
-
//
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8818
|
+
// A DELIBERATE stop is not a failure, and the JSONL tail cannot tell us it
|
|
8819
|
+
// happened. Both writers (our idle scanner, and any stop RPC via cmdStop)
|
|
8820
|
+
// stamp intentionalStopAt before signalling; the death then lands here through
|
|
8821
|
+
// whichever path notices it (usually orphan-poll ESRCH \u2192 code -1). Verified
|
|
8822
|
+
// production case: the last line was a control_response (the reply to a
|
|
8823
|
+
// Walnut-issued control request \u2014 a routine shape), so isTurnCompleteExit said
|
|
8824
|
+
// false, exitCode stayed -1, and the projection painted a red "Error" over a
|
|
8825
|
+
// healthy, fully resumable session that was stopped on purpose. Checked FIRST
|
|
8826
|
+
// and unconditionally: the flag carries information the tail does not contain,
|
|
8827
|
+
// which is why isTurnCompleteExit is left alone.
|
|
8828
|
+
// Keep in sync with daemon-core.ts reapSession.
|
|
8829
|
+
const intentionalStop = session.intentionalStopAt != null;
|
|
8830
|
+
if (intentionalStop && code !== 0) {
|
|
8831
|
+
logMsg('info', 'reapSession: intentional stop, normalizing exit code', {
|
|
8831
8832
|
sid, pid: session.pid, originalCode: code, originalReason: reason,
|
|
8832
|
-
jsonlAgeMs, cleanExit,
|
|
8833
|
+
jsonlAgeMs, cleanExit, intentionalStopAt: session.intentionalStopAt,
|
|
8833
8834
|
});
|
|
8834
8835
|
code = 0;
|
|
8835
|
-
reason = reason + '+intentional-
|
|
8836
|
+
reason = reason + '+intentional-stop';
|
|
8836
8837
|
} else if (code !== 0 && cleanExit) {
|
|
8837
8838
|
logMsg('info', 'reapSession: turn-complete detected, normalizing exit code', {
|
|
8838
8839
|
sid, pid: session.pid, originalCode: code, originalReason: reason, jsonlAgeMs,
|
|
@@ -12486,6 +12487,15 @@ function cmdStop(ws, id, cmd) {
|
|
|
12486
12487
|
const pid = session.pid;
|
|
12487
12488
|
logMsg('info', 'cmdStop: stopping session (process group kill)', { sid, pid });
|
|
12488
12489
|
|
|
12490
|
+
// Nobody sends stop by accident, so record the intent BEFORE the first signal:
|
|
12491
|
+
// the death lands asynchronously (orphan poll \u2192 ESRCH \u2192 code -1) and by then
|
|
12492
|
+
// nothing remembers this was asked for. This path is how the SERVER's idle
|
|
12493
|
+
// reaper kills (mgr.kill() \u2192 stop RPC), not the daemon's own scan, so it needs
|
|
12494
|
+
// its own stamp: a LOCAL session auto-stopped after 2h idle surfaced as
|
|
12495
|
+
// "Session ended unexpectedly and no cause was recorded" (2026-09-03).
|
|
12496
|
+
// Keep in sync with daemon-standalone.ts cmdStop.
|
|
12497
|
+
session.intentionalStopAt = Date.now();
|
|
12498
|
+
|
|
12489
12499
|
// An explicit stop is a human decision \u2014 never undone by a pending auto-retry
|
|
12490
12500
|
// respawning the CLI minutes later. Cancel the timer AND clear the streak so
|
|
12491
12501
|
// an in-flight result line can't re-arm one either.
|
|
@@ -12522,7 +12532,7 @@ function cmdStop(ws, id, cmd) {
|
|
|
12522
12532
|
|
|
12523
12533
|
// \u2500\u2500 Status \u2500\u2500
|
|
12524
12534
|
function cmdStatus(ws, id, cmd) {
|
|
12525
|
-
const { sid } = cmd;
|
|
12535
|
+
const { sid, includeArgs } = cmd;
|
|
12526
12536
|
if (!sid) return sendError(ws, id, 'status: missing sid');
|
|
12527
12537
|
|
|
12528
12538
|
const session = sessions.get(sid);
|
|
@@ -12553,6 +12563,11 @@ function cmdStatus(ws, id, cmd) {
|
|
|
12553
12563
|
exitCode: session.exitCode,
|
|
12554
12564
|
exitReason: session.exitReason,
|
|
12555
12565
|
pendingCtrl: session.pendingCtrl,
|
|
12566
|
+
// Opt-in: the argv the live process was launched with (a fork copies it to
|
|
12567
|
+
// reproduce the parent's prompt-cache prefix). Off by default because the
|
|
12568
|
+
// health monitor polls status for every session and the argv carries the
|
|
12569
|
+
// multi-KB --append-system-prompt.
|
|
12570
|
+
...(includeArgs && session.args && session.args.length > 0 ? { args: session.args } : {}),
|
|
12556
12571
|
});
|
|
12557
12572
|
}
|
|
12558
12573
|
|
|
@@ -14605,7 +14620,7 @@ function scanIdleSessions() {
|
|
|
14605
14620
|
// end" for any session whose last line isn't a type:result \u2014 and the
|
|
14606
14621
|
// projection then shows a red Error for our own routine reclamation.
|
|
14607
14622
|
// Keep in sync with daemon-standalone.ts scanIdleSessions.
|
|
14608
|
-
session.
|
|
14623
|
+
session.intentionalStopAt = now;
|
|
14609
14624
|
killSessionProcessGroup(pid, sid);
|
|
14610
14625
|
}
|
|
14611
14626
|
}
|
|
@@ -15289,19 +15304,19 @@ function createDaemonCore(deps) {
|
|
|
15289
15304
|
jsonlAgeMs = clock() - fs121.statSync(session2.jsonlPath).mtimeMs;
|
|
15290
15305
|
} catch {
|
|
15291
15306
|
}
|
|
15292
|
-
const
|
|
15293
|
-
if (
|
|
15294
|
-
logger2("info", "reapSession: intentional
|
|
15307
|
+
const intentionalStop = session2.intentionalStopAt != null;
|
|
15308
|
+
if (intentionalStop && code !== 0) {
|
|
15309
|
+
logger2("info", "reapSession: intentional stop, normalizing exit code", {
|
|
15295
15310
|
sid,
|
|
15296
15311
|
pid: session2.pid,
|
|
15297
15312
|
originalCode: code,
|
|
15298
15313
|
originalReason: reason,
|
|
15299
15314
|
jsonlAgeMs,
|
|
15300
15315
|
cleanExit,
|
|
15301
|
-
|
|
15316
|
+
intentionalStopAt: session2.intentionalStopAt
|
|
15302
15317
|
});
|
|
15303
15318
|
code = 0;
|
|
15304
|
-
reason = reason + "+intentional-
|
|
15319
|
+
reason = reason + "+intentional-stop";
|
|
15305
15320
|
} else if (cleanExit && code !== 0) {
|
|
15306
15321
|
logger2("info", "reapSession: turn-complete detected, normalizing exit code", {
|
|
15307
15322
|
sid,
|
|
@@ -16092,6 +16107,7 @@ var init_daemon_core = __esm({
|
|
|
16092
16107
|
var claude_cli_detect_exports = {};
|
|
16093
16108
|
__export(claude_cli_detect_exports, {
|
|
16094
16109
|
_CLAUDE_CREDENTIALS_HOME: () => _CLAUDE_CREDENTIALS_HOME,
|
|
16110
|
+
describeClaudeCliAuth: () => describeClaudeCliAuth,
|
|
16095
16111
|
detectClaudeCli: () => detectClaudeCli,
|
|
16096
16112
|
hasClaudeSubscriptionAuth: () => hasClaudeSubscriptionAuth,
|
|
16097
16113
|
isClaudeCliInstalled: () => isClaudeCliInstalled,
|
|
@@ -16152,9 +16168,32 @@ function detectClaudeCli() {
|
|
|
16152
16168
|
return {
|
|
16153
16169
|
installed: installed2,
|
|
16154
16170
|
subscriptionAuth,
|
|
16155
|
-
subscriptionReady: installed2 && subscriptionAuth
|
|
16171
|
+
subscriptionReady: installed2 && subscriptionAuth,
|
|
16172
|
+
ready: installed2,
|
|
16173
|
+
auth: installed2 ? describeClaudeCliAuth() : { mode: "unknown", label: "not installed" }
|
|
16156
16174
|
};
|
|
16157
16175
|
}
|
|
16176
|
+
function describeClaudeCliAuth(env3 = process.env, settingsEnv = readClaudeSettingsEnv()) {
|
|
16177
|
+
const merged = { ...env3, ...settingsEnv };
|
|
16178
|
+
const truthy = (v3) => !!v3 && v3 !== "0" && v3.toLowerCase() !== "false";
|
|
16179
|
+
if (truthy(merged.CLAUDE_CODE_USE_BEDROCK)) {
|
|
16180
|
+
const region = merged.AWS_REGION || merged.AWS_DEFAULT_REGION;
|
|
16181
|
+
return { mode: "bedrock", label: region ? `Bedrock (${region})` : "Bedrock" };
|
|
16182
|
+
}
|
|
16183
|
+
if (truthy(merged.CLAUDE_CODE_USE_VERTEX)) return { mode: "vertex", label: "Vertex AI" };
|
|
16184
|
+
if (merged.ANTHROPIC_API_KEY || merged.ANTHROPIC_AUTH_TOKEN) return { mode: "api-key", label: "an Anthropic API key" };
|
|
16185
|
+
if (hasClaudeSubscriptionAuth()) return { mode: "subscription", label: "your Claude subscription" };
|
|
16186
|
+
return { mode: "unknown", label: "its own login" };
|
|
16187
|
+
}
|
|
16188
|
+
function readClaudeSettingsEnv() {
|
|
16189
|
+
try {
|
|
16190
|
+
const raw = fs16.readFileSync(path17.join(os4.homedir(), ".claude", "settings.json"), "utf8");
|
|
16191
|
+
const env3 = JSON.parse(raw).env ?? {};
|
|
16192
|
+
return Object.fromEntries(Object.entries(env3).filter(([, v3]) => typeof v3 === "string"));
|
|
16193
|
+
} catch {
|
|
16194
|
+
return {};
|
|
16195
|
+
}
|
|
16196
|
+
}
|
|
16158
16197
|
var KEYCHAIN_OAUTH_SERVICE, _CLAUDE_CREDENTIALS_HOME;
|
|
16159
16198
|
var init_claude_cli_detect = __esm({
|
|
16160
16199
|
"src/core/claude-cli-detect.ts"() {
|
|
@@ -23524,7 +23563,7 @@ __export(credential_resolver_exports, {
|
|
|
23524
23563
|
DEFAULT_REGION: () => DEFAULT_REGION,
|
|
23525
23564
|
hasResolvableCredential: () => hasResolvableCredential,
|
|
23526
23565
|
readClaudeCredentialExport: () => readClaudeCredentialExport,
|
|
23527
|
-
readClaudeSettingsEnv: () =>
|
|
23566
|
+
readClaudeSettingsEnv: () => readClaudeSettingsEnv2,
|
|
23528
23567
|
resolveCredentialFrom: () => resolveCredentialFrom,
|
|
23529
23568
|
resolveCredentials: () => resolveCredentials,
|
|
23530
23569
|
traceCredentialResolution: () => traceCredentialResolution
|
|
@@ -23679,7 +23718,7 @@ function traceCredentialResolution(inputs) {
|
|
|
23679
23718
|
region: { value: winner.region ?? DEFAULT_REGION, source: regionSource }
|
|
23680
23719
|
};
|
|
23681
23720
|
}
|
|
23682
|
-
function
|
|
23721
|
+
function readClaudeSettingsEnv2(settingsFile = CLAUDE_SETTINGS_FILE) {
|
|
23683
23722
|
try {
|
|
23684
23723
|
const raw = fs30.readFileSync(settingsFile, "utf-8");
|
|
23685
23724
|
const parsed = JSON.parse(raw);
|
|
@@ -23719,7 +23758,7 @@ function probeAwsFiles() {
|
|
|
23719
23758
|
function resolveCredentials(config) {
|
|
23720
23759
|
const result = resolveCredentialFrom({
|
|
23721
23760
|
config,
|
|
23722
|
-
claudeEnv:
|
|
23761
|
+
claudeEnv: readClaudeSettingsEnv2(),
|
|
23723
23762
|
processEnv: process.env,
|
|
23724
23763
|
awsFiles: probeAwsFiles(),
|
|
23725
23764
|
claudeCredentialExport: readClaudeCredentialExport()
|
|
@@ -66501,10 +66540,9 @@ function buildArgs(opts) {
|
|
|
66501
66540
|
// Input stays default (text): we write the whole prompt to stdin and close.
|
|
66502
66541
|
// (stream-json INPUT would force us into a JSON envelope for no gain here.)
|
|
66503
66542
|
"--tools",
|
|
66504
|
-
""
|
|
66543
|
+
""
|
|
66505
66544
|
// disable ALL built-in tools (deny-all)
|
|
66506
|
-
|
|
66507
|
-
FORCE_SUBSCRIPTION_SETTINGS
|
|
66545
|
+
// No --settings override: the user's settings.json (and so their auth) applies as-is.
|
|
66508
66546
|
];
|
|
66509
66547
|
let systemText = systemToText(opts.system).trim();
|
|
66510
66548
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -66533,7 +66571,7 @@ function systemToText(system) {
|
|
|
66533
66571
|
}
|
|
66534
66572
|
function buildSpawnEnv(base = process.env) {
|
|
66535
66573
|
const env3 = { ...base };
|
|
66536
|
-
for (const key2 of
|
|
66574
|
+
for (const key2 of ENV_TO_STRIP) {
|
|
66537
66575
|
delete env3[key2];
|
|
66538
66576
|
}
|
|
66539
66577
|
env3.CLAUDE_CODE_ENTRYPOINT = WALNUT_UTILITY_ENTRYPOINT;
|
|
@@ -66595,7 +66633,7 @@ function normalizeCliModel(model) {
|
|
|
66595
66633
|
}
|
|
66596
66634
|
return void 0;
|
|
66597
66635
|
}
|
|
66598
|
-
var
|
|
66636
|
+
var ENV_TO_STRIP, MAX_CONCURRENT_CLI, CLI_TURN_TIMEOUT_MS, SESSION_REUSE_TTL_MS, MAX_TRACKED_SESSIONS, ClaudeCliAdapter;
|
|
66599
66637
|
var init_adapter_claude_cli = __esm({
|
|
66600
66638
|
"src/agent/providers/adapter-claude-cli.ts"() {
|
|
66601
66639
|
"use strict";
|
|
@@ -66605,24 +66643,11 @@ var init_adapter_claude_cli = __esm({
|
|
|
66605
66643
|
init_logging();
|
|
66606
66644
|
init_claude_cli_detect();
|
|
66607
66645
|
init_claude_cli_protocol();
|
|
66608
|
-
|
|
66609
|
-
|
|
66610
|
-
|
|
66611
|
-
|
|
66612
|
-
|
|
66613
|
-
"AWS_ACCESS_KEY_ID",
|
|
66614
|
-
"AWS_SECRET_ACCESS_KEY",
|
|
66615
|
-
"AWS_SESSION_TOKEN",
|
|
66616
|
-
"AWS_PROFILE",
|
|
66617
|
-
"CLAUDE_CODE_USE_BEDROCK",
|
|
66618
|
-
"CLAUDE_CODE_USE_VERTEX",
|
|
66619
|
-
// Nested-session marker — inline-subagent strips this too so the child doesn't
|
|
66620
|
-
// think it's running inside another Claude session.
|
|
66621
|
-
"CLAUDECODE"
|
|
66622
|
-
];
|
|
66623
|
-
FORCE_SUBSCRIPTION_SETTINGS = JSON.stringify({
|
|
66624
|
-
env: { CLAUDE_CODE_USE_BEDROCK: "", CLAUDE_CODE_USE_VERTEX: "" }
|
|
66625
|
-
});
|
|
66646
|
+
ENV_TO_STRIP = ["CLAUDECODE"];
|
|
66647
|
+
MAX_CONCURRENT_CLI = (() => {
|
|
66648
|
+
const n6 = Number(process.env.WALNUT_CLAUDE_CLI_CONCURRENCY);
|
|
66649
|
+
return Number.isFinite(n6) && n6 > 0 ? Math.floor(n6) : 3;
|
|
66650
|
+
})();
|
|
66626
66651
|
CLI_TURN_TIMEOUT_MS = 12e4;
|
|
66627
66652
|
SESSION_REUSE_TTL_MS = 30 * 60 * 1e3;
|
|
66628
66653
|
MAX_TRACKED_SESSIONS = 50;
|
|
@@ -66630,6 +66655,25 @@ var init_adapter_claude_cli = __esm({
|
|
|
66630
66655
|
protocol = "claude-cli";
|
|
66631
66656
|
/** conversationKey → CLI session for --resume chaining. In-process only. */
|
|
66632
66657
|
sessions = /* @__PURE__ */ new Map();
|
|
66658
|
+
/** Process gate: MAX_CONCURRENT_CLI turns at once, the rest queue in order. */
|
|
66659
|
+
inFlight = 0;
|
|
66660
|
+
waiters = [];
|
|
66661
|
+
async withSlot(fn) {
|
|
66662
|
+
if (this.inFlight >= MAX_CONCURRENT_CLI) {
|
|
66663
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
66664
|
+
}
|
|
66665
|
+
this.inFlight++;
|
|
66666
|
+
try {
|
|
66667
|
+
return await fn();
|
|
66668
|
+
} finally {
|
|
66669
|
+
this.inFlight--;
|
|
66670
|
+
this.waiters.shift()?.();
|
|
66671
|
+
}
|
|
66672
|
+
}
|
|
66673
|
+
/** Test hook: how many turns are running / waiting right now. */
|
|
66674
|
+
_gateStateForTesting() {
|
|
66675
|
+
return { inFlight: this.inFlight, waiting: this.waiters.length };
|
|
66676
|
+
}
|
|
66633
66677
|
resetClient() {
|
|
66634
66678
|
this.sessions.clear();
|
|
66635
66679
|
}
|
|
@@ -66707,7 +66751,7 @@ User: ${overridePrompt}` : "");
|
|
|
66707
66751
|
}
|
|
66708
66752
|
if (!prompt.trim()) prompt = "(continue)";
|
|
66709
66753
|
const args = [...buildArgs(opts), session2.flag, session2.id];
|
|
66710
|
-
return this.execCli(command6, args, env3, prompt, opts.signal, onTextDelta).then((result) => {
|
|
66754
|
+
return this.withSlot(() => this.execCli(command6, args, env3, prompt, opts.signal, onTextDelta)).then((result) => {
|
|
66711
66755
|
this.sessions.set(key2, {
|
|
66712
66756
|
sessionId: session2.id,
|
|
66713
66757
|
seenMessages: opts.messages.length,
|
|
@@ -66726,7 +66770,7 @@ User: ${overridePrompt}` : "");
|
|
|
66726
66770
|
User: ${overridePrompt}` : "");
|
|
66727
66771
|
const freshId = randomUUID3();
|
|
66728
66772
|
const freshArgs = [...buildArgs(opts), "--session-id", freshId];
|
|
66729
|
-
return this.execCli(command6, freshArgs, env3, freshPrompt || "(continue)", opts.signal, onTextDelta).then((result) => {
|
|
66773
|
+
return this.withSlot(() => this.execCli(command6, freshArgs, env3, freshPrompt || "(continue)", opts.signal, onTextDelta)).then((result) => {
|
|
66730
66774
|
this.sessions.set(key2, {
|
|
66731
66775
|
sessionId: freshId,
|
|
66732
66776
|
seenMessages: opts.messages.length,
|
|
@@ -66810,7 +66854,7 @@ User: ${overridePrompt}` : "");
|
|
|
66810
66854
|
}
|
|
66811
66855
|
const resultText = finalResult?.result || accumulatedText;
|
|
66812
66856
|
if (resultText && resultText.length < 300 && /^\s*failed to authenticate|^\s*api error: 4\d\d/i.test(resultText)) {
|
|
66813
|
-
reject(new Error(`claude-cli
|
|
66857
|
+
reject(new Error(`claude-cli could not authenticate \u2014 run \`claude\` once in a terminal to sign in (or check its Bedrock settings). CLI said: ${resultText.slice(0, 200)}`));
|
|
66814
66858
|
return;
|
|
66815
66859
|
}
|
|
66816
66860
|
if (code !== 0 && !accumulatedText && !finalResult) {
|
|
@@ -67010,6 +67054,50 @@ var init_registry2 = __esm({
|
|
|
67010
67054
|
}
|
|
67011
67055
|
});
|
|
67012
67056
|
|
|
67057
|
+
// src/agent/providers/default-provider.ts
|
|
67058
|
+
var default_provider_exports = {};
|
|
67059
|
+
__export(default_provider_exports, {
|
|
67060
|
+
CLAUDE_CLI_PROVIDER: () => CLAUDE_CLI_PROVIDER,
|
|
67061
|
+
LEGACY_DEFAULT_PROVIDER: () => LEGACY_DEFAULT_PROVIDER,
|
|
67062
|
+
_resetDefaultProviderCacheForTesting: () => _resetDefaultProviderCacheForTesting,
|
|
67063
|
+
hasExplicitBedrockConfig: () => hasExplicitBedrockConfig,
|
|
67064
|
+
mainProviderIsImplicit: () => mainProviderIsImplicit,
|
|
67065
|
+
resolveMainProviderName: () => resolveMainProviderName
|
|
67066
|
+
});
|
|
67067
|
+
function claudeCliInstalledCached() {
|
|
67068
|
+
const now = Date.now();
|
|
67069
|
+
if (!cliProbe || now - cliProbe.at > CLI_PROBE_TTL_MS) {
|
|
67070
|
+
cliProbe = { at: now, installed: isClaudeCliInstalled() };
|
|
67071
|
+
}
|
|
67072
|
+
return cliProbe.installed;
|
|
67073
|
+
}
|
|
67074
|
+
function _resetDefaultProviderCacheForTesting() {
|
|
67075
|
+
cliProbe = void 0;
|
|
67076
|
+
}
|
|
67077
|
+
function hasExplicitBedrockConfig(config) {
|
|
67078
|
+
const b8 = config.providers?.bedrock;
|
|
67079
|
+
if (b8 && (b8.bearer_token || b8.aws_access_key_id || b8.aws_profile || b8.aws_credential_export)) return true;
|
|
67080
|
+
return !!config.provider?.bedrock_bearer_token;
|
|
67081
|
+
}
|
|
67082
|
+
function resolveMainProviderName(config, claudeInstalled = claudeCliInstalledCached()) {
|
|
67083
|
+
if (config.agent?.main_provider) return config.agent.main_provider;
|
|
67084
|
+
if (claudeInstalled) return CLAUDE_CLI_PROVIDER;
|
|
67085
|
+
return LEGACY_DEFAULT_PROVIDER;
|
|
67086
|
+
}
|
|
67087
|
+
function mainProviderIsImplicit(config) {
|
|
67088
|
+
return !config.agent?.main_provider;
|
|
67089
|
+
}
|
|
67090
|
+
var LEGACY_DEFAULT_PROVIDER, CLAUDE_CLI_PROVIDER, CLI_PROBE_TTL_MS, cliProbe;
|
|
67091
|
+
var init_default_provider = __esm({
|
|
67092
|
+
"src/agent/providers/default-provider.ts"() {
|
|
67093
|
+
"use strict";
|
|
67094
|
+
init_claude_cli_detect();
|
|
67095
|
+
LEGACY_DEFAULT_PROVIDER = "bedrock";
|
|
67096
|
+
CLAUDE_CLI_PROVIDER = "claude_cli";
|
|
67097
|
+
CLI_PROBE_TTL_MS = 6e4;
|
|
67098
|
+
}
|
|
67099
|
+
});
|
|
67100
|
+
|
|
67013
67101
|
// src/agent/model.ts
|
|
67014
67102
|
var model_exports = {};
|
|
67015
67103
|
__export(model_exports, {
|
|
@@ -67044,7 +67132,7 @@ function getContextThreshold(model, percent) {
|
|
|
67044
67132
|
}
|
|
67045
67133
|
async function resolveForCall(config) {
|
|
67046
67134
|
const fullConfig = await getConfig();
|
|
67047
|
-
const providerName = config?.provider ?? fullConfig
|
|
67135
|
+
const providerName = config?.provider ?? resolveMainProviderName(fullConfig);
|
|
67048
67136
|
const hasExplicitProviders = fullConfig.providers && Object.keys(fullConfig.providers).length > 0;
|
|
67049
67137
|
let providers = hasExplicitProviders ? buildProviderMap(fullConfig.providers, fullConfig) : synthesizeFromLegacy(fullConfig);
|
|
67050
67138
|
const resolved = resolveProvider(providerName, providers);
|
|
@@ -67114,6 +67202,7 @@ var init_model = __esm({
|
|
|
67114
67202
|
"use strict";
|
|
67115
67203
|
init_config_manager();
|
|
67116
67204
|
init_registry2();
|
|
67205
|
+
init_default_provider();
|
|
67117
67206
|
init_defaults();
|
|
67118
67207
|
init_model_catalog();
|
|
67119
67208
|
}
|
|
@@ -68554,7 +68643,7 @@ __export(cheap_model_exports, {
|
|
|
68554
68643
|
});
|
|
68555
68644
|
function fastModelFor(config) {
|
|
68556
68645
|
if (config.agent?.fast_model) return config.agent.fast_model;
|
|
68557
|
-
const providerName = config
|
|
68646
|
+
const providerName = resolveMainProviderName(config);
|
|
68558
68647
|
return MODEL_CATALOG[providerName]?.find(
|
|
68559
68648
|
(model) => model.id.toLowerCase().includes("haiku")
|
|
68560
68649
|
)?.id;
|
|
@@ -68566,6 +68655,7 @@ var init_cheap_model = __esm({
|
|
|
68566
68655
|
"src/core/cheap-model.ts"() {
|
|
68567
68656
|
"use strict";
|
|
68568
68657
|
init_model_catalog();
|
|
68658
|
+
init_default_provider();
|
|
68569
68659
|
}
|
|
68570
68660
|
});
|
|
68571
68661
|
|
|
@@ -70385,7 +70475,20 @@ async function applySnapshot(sessionId, snapshot2, source) {
|
|
|
70385
70475
|
if (projected !== "error") {
|
|
70386
70476
|
updates.errorMessage = void 0;
|
|
70387
70477
|
updates.errorKind = void 0;
|
|
70388
|
-
|
|
70478
|
+
const stashedStop = projected === "stopped" ? takeSuppressedErrorReason(sessionId) : null;
|
|
70479
|
+
if (stashedStop?.message) {
|
|
70480
|
+
updates.errorMessage = stashedStop.message;
|
|
70481
|
+
if (stashedStop.kind) updates.errorKind = stashedStop.kind;
|
|
70482
|
+
log.session.info("snapshot projection adopted suppressed stop reason", {
|
|
70483
|
+
sessionId,
|
|
70484
|
+
suppressedReason: stashedStop.reason,
|
|
70485
|
+
kind: stashedStop.kind ?? null,
|
|
70486
|
+
projected,
|
|
70487
|
+
source
|
|
70488
|
+
});
|
|
70489
|
+
} else {
|
|
70490
|
+
clearSuppressedErrorReason(sessionId);
|
|
70491
|
+
}
|
|
70389
70492
|
} else {
|
|
70390
70493
|
let droppedDisproven = false;
|
|
70391
70494
|
if (assertsHostUnreachable(record)) {
|
|
@@ -74459,6 +74562,26 @@ var init_transcript_chain = __esm({
|
|
|
74459
74562
|
}
|
|
74460
74563
|
});
|
|
74461
74564
|
|
|
74565
|
+
// src/core/sessions/side-thread-warmup.ts
|
|
74566
|
+
function isCacheWarmupText(text) {
|
|
74567
|
+
return text.startsWith(CACHE_WARMUP_TAG);
|
|
74568
|
+
}
|
|
74569
|
+
function markWarmupTurnPending(sessionId) {
|
|
74570
|
+
pendingWarmupTurns.add(sessionId);
|
|
74571
|
+
}
|
|
74572
|
+
function consumeWarmupTurn(sessionId) {
|
|
74573
|
+
return pendingWarmupTurns.delete(sessionId);
|
|
74574
|
+
}
|
|
74575
|
+
var CACHE_WARMUP_TAG, CACHE_WARMUP_MESSAGE, pendingWarmupTurns;
|
|
74576
|
+
var init_side_thread_warmup = __esm({
|
|
74577
|
+
"src/core/sessions/side-thread-warmup.ts"() {
|
|
74578
|
+
"use strict";
|
|
74579
|
+
CACHE_WARMUP_TAG = "<walnut-cache-warmup>";
|
|
74580
|
+
CACHE_WARMUP_MESSAGE = `${CACHE_WARMUP_TAG}This is a cache warm-up from the tool hosting this side thread, not a user message. Reply with exactly one word: Ready. Use no tools and add nothing else.</walnut-cache-warmup>`;
|
|
74581
|
+
pendingWarmupTurns = /* @__PURE__ */ new Set();
|
|
74582
|
+
}
|
|
74583
|
+
});
|
|
74584
|
+
|
|
74462
74585
|
// src/core/history-disk-cache.ts
|
|
74463
74586
|
var history_disk_cache_exports = {};
|
|
74464
74587
|
__export(history_disk_cache_exports, {
|
|
@@ -76619,6 +76742,7 @@ __export(session_lifecycle_exports, {
|
|
|
76619
76742
|
retrySession: () => retrySession,
|
|
76620
76743
|
terminateSession: () => terminateSession
|
|
76621
76744
|
});
|
|
76745
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
76622
76746
|
async function readProviderSessionHistory(sessionId, record, nativeHost, skipSubagents = true, opts) {
|
|
76623
76747
|
if (record && engineCaps(record.engine).historySource === "acp-journal") {
|
|
76624
76748
|
const { readAcpSessionHistoryState: readAcpSessionHistoryState2 } = await Promise.resolve().then(() => (init_acp_session_history(), acp_session_history_exports));
|
|
@@ -76729,6 +76853,33 @@ async function changeSessionMode(existingRecord, sessionId, mode, updates = {})
|
|
|
76729
76853
|
});
|
|
76730
76854
|
return updated;
|
|
76731
76855
|
}
|
|
76856
|
+
function normalizePinQuote(value) {
|
|
76857
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
76858
|
+
throw new SessionControlError("pinned_messages[].quote must be an object", 400);
|
|
76859
|
+
}
|
|
76860
|
+
const quote = value;
|
|
76861
|
+
const exact = quote.exact;
|
|
76862
|
+
if (typeof exact !== "string" || !exact.trim() || exact.length > MAX_PIN_QUOTE_CHARS) {
|
|
76863
|
+
throw new SessionControlError(
|
|
76864
|
+
`pinned_messages[].quote.exact must be a non-empty string (max ${MAX_PIN_QUOTE_CHARS} chars)`,
|
|
76865
|
+
400
|
|
76866
|
+
);
|
|
76867
|
+
}
|
|
76868
|
+
for (const side of ["prefix", "suffix"]) {
|
|
76869
|
+
const context = quote[side];
|
|
76870
|
+
if (context !== void 0 && context !== null && (typeof context !== "string" || context.length > MAX_PIN_QUOTE_CONTEXT_CHARS)) {
|
|
76871
|
+
throw new SessionControlError(
|
|
76872
|
+
`pinned_messages[].quote.${side} must be a string (max ${MAX_PIN_QUOTE_CONTEXT_CHARS} chars)`,
|
|
76873
|
+
400
|
|
76874
|
+
);
|
|
76875
|
+
}
|
|
76876
|
+
}
|
|
76877
|
+
return {
|
|
76878
|
+
exact,
|
|
76879
|
+
...typeof quote.prefix === "string" && quote.prefix ? { prefix: quote.prefix } : {},
|
|
76880
|
+
...typeof quote.suffix === "string" && quote.suffix ? { suffix: quote.suffix } : {}
|
|
76881
|
+
};
|
|
76882
|
+
}
|
|
76732
76883
|
function normalizePinnedMessages(value) {
|
|
76733
76884
|
if (!Array.isArray(value)) {
|
|
76734
76885
|
throw new SessionControlError("pinned_messages must be an array", 400);
|
|
@@ -76737,7 +76888,9 @@ function normalizePinnedMessages(value) {
|
|
|
76737
76888
|
throw new SessionControlError(`pinned_messages holds at most ${MAX_PINNED_MESSAGES} entries`, 400);
|
|
76738
76889
|
}
|
|
76739
76890
|
const out = [];
|
|
76740
|
-
const
|
|
76891
|
+
const seenWholeMessage = /* @__PURE__ */ new Set();
|
|
76892
|
+
const seenQuoteId = /* @__PURE__ */ new Set();
|
|
76893
|
+
const seenQuoteShape = /* @__PURE__ */ new Set();
|
|
76741
76894
|
for (const raw of value) {
|
|
76742
76895
|
if (!raw || typeof raw !== "object") {
|
|
76743
76896
|
throw new SessionControlError("each pinned_messages entry must be an object", 400);
|
|
@@ -76747,13 +76900,40 @@ function normalizePinnedMessages(value) {
|
|
|
76747
76900
|
if (typeof msgId !== "string" || !msgId.trim() || msgId.length > MAX_PIN_ID_CHARS) {
|
|
76748
76901
|
throw new SessionControlError("pinned_messages[].msgId must be a non-empty string", 400);
|
|
76749
76902
|
}
|
|
76750
|
-
|
|
76751
|
-
|
|
76903
|
+
const rawId = entry.id;
|
|
76904
|
+
if (rawId !== void 0 && rawId !== null && (typeof rawId !== "string" || !rawId.trim() || rawId.length > MAX_PIN_ENTRY_ID_CHARS)) {
|
|
76905
|
+
throw new SessionControlError(
|
|
76906
|
+
`pinned_messages[].id must be a non-empty string (max ${MAX_PIN_ENTRY_ID_CHARS} chars)`,
|
|
76907
|
+
400
|
|
76908
|
+
);
|
|
76909
|
+
}
|
|
76910
|
+
const id = typeof rawId === "string" ? rawId : void 0;
|
|
76911
|
+
const quote = entry.quote === void 0 || entry.quote === null ? void 0 : normalizePinQuote(entry.quote);
|
|
76912
|
+
let pinId = id;
|
|
76913
|
+
if (quote) {
|
|
76914
|
+
if (id && seenQuoteId.has(id)) continue;
|
|
76915
|
+
const shape = [msgId, quote.exact, quote.prefix ?? "", quote.suffix ?? ""].join("\0");
|
|
76916
|
+
if (seenQuoteShape.has(shape)) continue;
|
|
76917
|
+
pinId = id ?? randomUUID4();
|
|
76918
|
+
seenQuoteId.add(pinId);
|
|
76919
|
+
seenQuoteShape.add(shape);
|
|
76920
|
+
} else {
|
|
76921
|
+
if (seenWholeMessage.has(msgId)) continue;
|
|
76922
|
+
seenWholeMessage.add(msgId);
|
|
76923
|
+
}
|
|
76752
76924
|
const role = entry.role === "user" || entry.role === "assistant" || entry.role === "system" ? entry.role : "assistant";
|
|
76753
76925
|
const label = typeof entry.label === "string" ? entry.label.trim().slice(0, MAX_PIN_LABEL_CHARS) : "";
|
|
76754
76926
|
const timestamp = typeof entry.timestamp === "string" ? entry.timestamp : void 0;
|
|
76755
76927
|
const pinnedAt = typeof entry.pinnedAt === "string" ? entry.pinnedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
76756
|
-
out.push({
|
|
76928
|
+
out.push({
|
|
76929
|
+
msgId,
|
|
76930
|
+
label,
|
|
76931
|
+
role,
|
|
76932
|
+
...timestamp ? { timestamp } : {},
|
|
76933
|
+
pinnedAt,
|
|
76934
|
+
...pinId ? { id: pinId } : {},
|
|
76935
|
+
...quote ? { quote } : {}
|
|
76936
|
+
});
|
|
76757
76937
|
}
|
|
76758
76938
|
return out;
|
|
76759
76939
|
}
|
|
@@ -77412,7 +77592,7 @@ async function readSessionRichHistory(sessionId, tail) {
|
|
|
77412
77592
|
...historyWindowed ? { windowed: true } : {}
|
|
77413
77593
|
};
|
|
77414
77594
|
}
|
|
77415
|
-
var CLAUDE_SESSION_MODES, MAX_PINNED_MESSAGES, MAX_PIN_LABEL_CHARS, MAX_PIN_ID_CHARS, RECHECK_RPC_TIMEOUT_MS, RECHECK_TIMED_OUT, GIT_BASES, MAX_FORK_DEPTH, HISTORY_STARTUP_GRACE_MS;
|
|
77595
|
+
var CLAUDE_SESSION_MODES, MAX_PINNED_MESSAGES, MAX_PIN_LABEL_CHARS, MAX_PIN_ID_CHARS, MAX_PIN_ENTRY_ID_CHARS, MAX_PIN_QUOTE_CHARS, MAX_PIN_QUOTE_CONTEXT_CHARS, RECHECK_RPC_TIMEOUT_MS, RECHECK_TIMED_OUT, GIT_BASES, MAX_FORK_DEPTH, HISTORY_STARTUP_GRACE_MS;
|
|
77416
77596
|
var init_session_lifecycle = __esm({
|
|
77417
77597
|
"src/core/sessions/session-lifecycle.ts"() {
|
|
77418
77598
|
"use strict";
|
|
@@ -77426,6 +77606,9 @@ var init_session_lifecycle = __esm({
|
|
|
77426
77606
|
MAX_PINNED_MESSAGES = 200;
|
|
77427
77607
|
MAX_PIN_LABEL_CHARS = 300;
|
|
77428
77608
|
MAX_PIN_ID_CHARS = 200;
|
|
77609
|
+
MAX_PIN_ENTRY_ID_CHARS = 64;
|
|
77610
|
+
MAX_PIN_QUOTE_CHARS = 2e3;
|
|
77611
|
+
MAX_PIN_QUOTE_CONTEXT_CHARS = 64;
|
|
77429
77612
|
RECHECK_RPC_TIMEOUT_MS = 5e3;
|
|
77430
77613
|
RECHECK_TIMED_OUT = /* @__PURE__ */ Symbol("recheck-timeout");
|
|
77431
77614
|
GIT_BASES = /* @__PURE__ */ new Set(["uncommitted", "previous", "remote"]);
|
|
@@ -77728,7 +77911,7 @@ __export(compact_inject_exports, {
|
|
|
77728
77911
|
extractSessionMeta: () => extractSessionMeta,
|
|
77729
77912
|
injectCompactBoundary: () => injectCompactBoundary
|
|
77730
77913
|
});
|
|
77731
|
-
import { randomUUID as
|
|
77914
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
77732
77915
|
import fsp26 from "fs/promises";
|
|
77733
77916
|
async function extractSessionMeta(jsonlPath) {
|
|
77734
77917
|
const content = await fsp26.readFile(jsonlPath, "utf-8");
|
|
@@ -77780,8 +77963,8 @@ async function injectCompactBoundary(jsonlPath, summary, meta) {
|
|
|
77780
77963
|
log.session.error("Could not find last message UUID in JSONL", { jsonlPath });
|
|
77781
77964
|
return null;
|
|
77782
77965
|
}
|
|
77783
|
-
const boundaryUuid =
|
|
77784
|
-
const summaryUuid =
|
|
77966
|
+
const boundaryUuid = randomUUID5();
|
|
77967
|
+
const summaryUuid = randomUUID5();
|
|
77785
77968
|
const now = /* @__PURE__ */ new Date();
|
|
77786
77969
|
const boundaryTs = now.toISOString();
|
|
77787
77970
|
const summaryTs = new Date(now.getTime() + 1e3).toISOString();
|
|
@@ -88565,6 +88748,82 @@ var init_task = __esm({
|
|
|
88565
88748
|
}
|
|
88566
88749
|
});
|
|
88567
88750
|
|
|
88751
|
+
// src/core/sessions/spawn-prefix.ts
|
|
88752
|
+
var spawn_prefix_exports = {};
|
|
88753
|
+
__export(spawn_prefix_exports, {
|
|
88754
|
+
MAX_SPAWN_PROMPT_BYTES: () => MAX_SPAWN_PROMPT_BYTES,
|
|
88755
|
+
parseSpawnPrefixFromArgs: () => parseSpawnPrefixFromArgs,
|
|
88756
|
+
readParentSpawnPrefix: () => readParentSpawnPrefix,
|
|
88757
|
+
spawnPrefixFromRecord: () => spawnPrefixFromRecord
|
|
88758
|
+
});
|
|
88759
|
+
function flagValue(args, flag) {
|
|
88760
|
+
const i8 = args.indexOf(flag);
|
|
88761
|
+
if (i8 < 0 || i8 + 1 >= args.length) return void 0;
|
|
88762
|
+
const v3 = args[i8 + 1];
|
|
88763
|
+
return v3 !== void 0 && !v3.startsWith("--") ? v3 : void 0;
|
|
88764
|
+
}
|
|
88765
|
+
function parseSpawnPrefixFromArgs(args) {
|
|
88766
|
+
const prompt = flagValue(args, "--append-system-prompt");
|
|
88767
|
+
return {
|
|
88768
|
+
appendSystemPrompt: prompt !== void 0 && Buffer.byteLength(prompt, "utf8") <= MAX_SPAWN_PROMPT_BYTES ? prompt : null,
|
|
88769
|
+
...flagValue(args, "--model") ? { model: flagValue(args, "--model") } : {},
|
|
88770
|
+
...EFFORTS.includes(flagValue(args, "--effort") ?? "") ? { effort: flagValue(args, "--effort") } : {},
|
|
88771
|
+
...flagValue(args, "--permission-mode") ? { permissionMode: flagValue(args, "--permission-mode") } : {},
|
|
88772
|
+
source: "live-process"
|
|
88773
|
+
};
|
|
88774
|
+
}
|
|
88775
|
+
function spawnPrefixFromRecord(record) {
|
|
88776
|
+
const stored = record.appliedAppendSystemPrompt;
|
|
88777
|
+
if (stored === void 0 || stored === null) return { appendSystemPrompt: null, source: "unknown" };
|
|
88778
|
+
const usable = stored.length > 0 && Buffer.byteLength(stored, "utf8") <= MAX_SPAWN_PROMPT_BYTES;
|
|
88779
|
+
return { appendSystemPrompt: usable ? stored : null, source: "record" };
|
|
88780
|
+
}
|
|
88781
|
+
async function defaultLiveArgsReader(hostKey2, sessionId) {
|
|
88782
|
+
const { probeDaemonSessionArgs: probeDaemonSessionArgs2 } = await Promise.resolve().then(() => (init_daemon_connection(), daemon_connection_exports));
|
|
88783
|
+
return probeDaemonSessionArgs2(hostKey2, sessionId);
|
|
88784
|
+
}
|
|
88785
|
+
async function readParentSpawnPrefix(parent, deps = {}) {
|
|
88786
|
+
const readLiveArgs = deps.readLiveArgs ?? defaultLiveArgsReader;
|
|
88787
|
+
const hostKey2 = parent.host || "__local__";
|
|
88788
|
+
let live = null;
|
|
88789
|
+
try {
|
|
88790
|
+
live = await readLiveArgs(hostKey2, parent.claudeSessionId);
|
|
88791
|
+
} catch (err2) {
|
|
88792
|
+
log.session.debug("spawn prefix: live argv read threw", {
|
|
88793
|
+
sessionId: parent.claudeSessionId,
|
|
88794
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
88795
|
+
});
|
|
88796
|
+
}
|
|
88797
|
+
if (live && live.length > 0) {
|
|
88798
|
+
const prefix = parseSpawnPrefixFromArgs(live);
|
|
88799
|
+
const remembered = parent.appliedAppendSystemPrompt ?? void 0;
|
|
88800
|
+
const actual = prefix.appendSystemPrompt ?? "";
|
|
88801
|
+
if (remembered !== actual) {
|
|
88802
|
+
const persist4 = deps.persist ?? (async (sid, prompt) => {
|
|
88803
|
+
const { updateSessionRecord: updateSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
88804
|
+
return updateSessionRecord2(sid, { appliedAppendSystemPrompt: prompt });
|
|
88805
|
+
});
|
|
88806
|
+
void persist4(parent.claudeSessionId, actual).catch((err2) => {
|
|
88807
|
+
log.session.warn("spawn prefix: backfill of applied prompt failed", {
|
|
88808
|
+
sessionId: parent.claudeSessionId,
|
|
88809
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
88810
|
+
});
|
|
88811
|
+
});
|
|
88812
|
+
}
|
|
88813
|
+
return prefix;
|
|
88814
|
+
}
|
|
88815
|
+
return spawnPrefixFromRecord(parent);
|
|
88816
|
+
}
|
|
88817
|
+
var EFFORTS, MAX_SPAWN_PROMPT_BYTES;
|
|
88818
|
+
var init_spawn_prefix = __esm({
|
|
88819
|
+
"src/core/sessions/spawn-prefix.ts"() {
|
|
88820
|
+
"use strict";
|
|
88821
|
+
init_logging();
|
|
88822
|
+
EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
88823
|
+
MAX_SPAWN_PROMPT_BYTES = 65536;
|
|
88824
|
+
}
|
|
88825
|
+
});
|
|
88826
|
+
|
|
88568
88827
|
// src/core/sessions/side-thread-fork.ts
|
|
88569
88828
|
var side_thread_fork_exports = {};
|
|
88570
88829
|
__export(side_thread_fork_exports, {
|
|
@@ -88621,6 +88880,10 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
88621
88880
|
const sessionId = crypto16.randomUUID();
|
|
88622
88881
|
const lane = sideThreadLaneKey(parentSid, threadId);
|
|
88623
88882
|
const mode = parent.mode !== "default" ? parent.mode : void 0;
|
|
88883
|
+
const { readParentSpawnPrefix: readParentSpawnPrefix2 } = await Promise.resolve().then(() => (init_spawn_prefix(), spawn_prefix_exports));
|
|
88884
|
+
const prefix = await readParentSpawnPrefix2(parent);
|
|
88885
|
+
const cliModel = prefix.model ?? parent.cliModel;
|
|
88886
|
+
const effort = prefix.effort ?? parent.effort;
|
|
88624
88887
|
await createSessionRecord(sessionId, "", parent.project ?? "", parent.cwd, {
|
|
88625
88888
|
title,
|
|
88626
88889
|
...mode ? { mode } : {},
|
|
@@ -88628,8 +88891,10 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
88628
88891
|
// The thread inherits the parent's launch bundle verbatim, re-applied from
|
|
88629
88892
|
// the record on every cold resume.
|
|
88630
88893
|
...parent.profile ? { profile: parent.profile } : {},
|
|
88631
|
-
...
|
|
88632
|
-
...
|
|
88894
|
+
...effort ? { effort } : {},
|
|
88895
|
+
...cliModel ? { cliModel } : {},
|
|
88896
|
+
// '' = the parent runs without an append prompt, and so does the thread.
|
|
88897
|
+
appliedAppendSystemPrompt: prefix.appendSystemPrompt ?? "",
|
|
88633
88898
|
lane,
|
|
88634
88899
|
forkedFromSessionId: resumeFrom,
|
|
88635
88900
|
initialProcessStatus: "idle",
|
|
@@ -88645,11 +88910,12 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
88645
88910
|
...mode ? { mode } : {},
|
|
88646
88911
|
...parent.host ? { host: parent.host } : {},
|
|
88647
88912
|
...parent.profile ? { profile: parent.profile } : {},
|
|
88648
|
-
...
|
|
88913
|
+
...effort ? { effort } : {},
|
|
88649
88914
|
// Must be `cliModel` (the parent's verbatim --model arg, [1m] marker
|
|
88650
88915
|
// included): the CLI does NOT inherit a model across --fork-session, and
|
|
88651
88916
|
// the reported `model` has lost the marker.
|
|
88652
|
-
...
|
|
88917
|
+
...cliModel ? { model: cliModel } : {},
|
|
88918
|
+
appendSystemPromptExact: prefix.appendSystemPrompt,
|
|
88653
88919
|
lane,
|
|
88654
88920
|
forkedFromSessionId: resumeFrom
|
|
88655
88921
|
}, ["session-runner"], { source: "side-thread-fork" });
|
|
@@ -88658,7 +88924,9 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
88658
88924
|
threadId,
|
|
88659
88925
|
sessionId,
|
|
88660
88926
|
resumeFrom,
|
|
88661
|
-
initOnly: !opts?.message
|
|
88927
|
+
initOnly: !opts?.message,
|
|
88928
|
+
prefixSource: prefix.source,
|
|
88929
|
+
prefixPromptLength: prefix.appendSystemPrompt?.length ?? 0
|
|
88662
88930
|
});
|
|
88663
88931
|
return { sessionId, resumeFromSessionId: resumeFrom };
|
|
88664
88932
|
}
|
|
@@ -100914,14 +101182,16 @@ function isLive(r8) {
|
|
|
100914
101182
|
function isRevivable(r8) {
|
|
100915
101183
|
return !!r8.outputFile || r8.consumedOffset !== void 0;
|
|
100916
101184
|
}
|
|
100917
|
-
var STANDBY_TTL_MS, MAX_LIVE_THREADS, IDLE_SWEEP_INTERVAL_MS, THREAD_IDLE_MS, SideThreadManager, sideThreadManager;
|
|
101185
|
+
var STANDBY_TTL_MS, WARMED_STANDBY_TTL_MS, MAX_LIVE_THREADS, IDLE_SWEEP_INTERVAL_MS, THREAD_IDLE_MS, SideThreadManager, sideThreadManager;
|
|
100918
101186
|
var init_side_thread_manager = __esm({
|
|
100919
101187
|
"src/core/sessions/side-thread-manager.ts"() {
|
|
100920
101188
|
"use strict";
|
|
100921
101189
|
init_logging();
|
|
100922
101190
|
init_side_thread_fork();
|
|
100923
101191
|
init_session_controls();
|
|
101192
|
+
init_side_thread_warmup();
|
|
100924
101193
|
STANDBY_TTL_MS = 12e4;
|
|
101194
|
+
WARMED_STANDBY_TTL_MS = 15 * 6e4;
|
|
100925
101195
|
MAX_LIVE_THREADS = 3;
|
|
100926
101196
|
IDLE_SWEEP_INTERVAL_MS = 5 * 6e4;
|
|
100927
101197
|
THREAD_IDLE_MS = 30 * 6e4;
|
|
@@ -100932,6 +101202,8 @@ var init_side_thread_manager = __esm({
|
|
|
100932
101202
|
* In-memory only: a server restart loses it, but the boot sweep archives
|
|
100933
101203
|
* every standby anyway, so a record can never outlive its map entry. */
|
|
100934
101204
|
standbyParentOffsets = /* @__PURE__ */ new Map();
|
|
101205
|
+
/** parentSid → standby sid that already ran its cache warm-up turn. */
|
|
101206
|
+
warmedStandbys = /* @__PURE__ */ new Map();
|
|
100935
101207
|
/** One in-flight standby/create op per parent — two concurrent asks must not
|
|
100936
101208
|
* both consume the same standby (they would collide on its lane rename). */
|
|
100937
101209
|
inFlight = /* @__PURE__ */ new Map();
|
|
@@ -100963,6 +101235,7 @@ var init_side_thread_manager = __esm({
|
|
|
100963
101235
|
for (const timer3 of this.standbyTimers.values()) clearTimeout(timer3);
|
|
100964
101236
|
this.standbyTimers.clear();
|
|
100965
101237
|
this.standbyParentOffsets.clear();
|
|
101238
|
+
this.warmedStandbys.clear();
|
|
100966
101239
|
this.inFlight.clear();
|
|
100967
101240
|
}
|
|
100968
101241
|
// ── Standby ────────────────────────────────────────────────────────────────
|
|
@@ -100978,10 +101251,12 @@ var init_side_thread_manager = __esm({
|
|
|
100978
101251
|
const existing = await this.findStandby(parentSid);
|
|
100979
101252
|
if (existing) {
|
|
100980
101253
|
if (await this.isStandbyUsable(parentSid, existing)) {
|
|
100981
|
-
this.
|
|
101254
|
+
const warmed = this.warmedStandbys.get(parentSid) === existing.claudeSessionId;
|
|
101255
|
+
this.armStandbyTtl(parentSid, existing.claudeSessionId, warmed ? WARMED_STANDBY_TTL_MS : STANDBY_TTL_MS);
|
|
100982
101256
|
return existing.claudeSessionId;
|
|
100983
101257
|
}
|
|
100984
101258
|
void this.retireSession(existing.claudeSessionId, "side_thread_standby_stale");
|
|
101259
|
+
this.warmedStandbys.delete(parentSid);
|
|
100985
101260
|
}
|
|
100986
101261
|
const parentOffset = parent.consumedOffset;
|
|
100987
101262
|
const { sessionId } = await forkSideThreadSession(parentSid, STANDBY_THREAD_ID);
|
|
@@ -101007,7 +101282,7 @@ var init_side_thread_manager = __esm({
|
|
|
101007
101282
|
if (!parent) return false;
|
|
101008
101283
|
return parent.consumedOffset === this.standbyParentOffsets.get(parentSid);
|
|
101009
101284
|
}
|
|
101010
|
-
armStandbyTtl(parentSid, sessionId) {
|
|
101285
|
+
armStandbyTtl(parentSid, sessionId, ttlMs = STANDBY_TTL_MS) {
|
|
101011
101286
|
const prev = this.standbyTimers.get(parentSid);
|
|
101012
101287
|
if (prev) clearTimeout(prev);
|
|
101013
101288
|
const timer3 = setTimeout(() => {
|
|
@@ -101017,13 +101292,41 @@ var init_side_thread_manager = __esm({
|
|
|
101017
101292
|
const record = await getSessionByClaudeId2(sessionId).catch(() => null);
|
|
101018
101293
|
if (!record || parseSideLaneKey(record.lane)?.threadId !== STANDBY_THREAD_ID) return;
|
|
101019
101294
|
this.standbyParentOffsets.delete(parentSid);
|
|
101295
|
+
this.warmedStandbys.delete(parentSid);
|
|
101020
101296
|
await this.retireSession(sessionId, "side_thread_standby_ttl");
|
|
101021
101297
|
}).catch(() => {
|
|
101022
101298
|
});
|
|
101023
|
-
},
|
|
101299
|
+
}, ttlMs);
|
|
101024
101300
|
timer3.unref?.();
|
|
101025
101301
|
this.standbyTimers.set(parentSid, timer3);
|
|
101026
101302
|
}
|
|
101303
|
+
/**
|
|
101304
|
+
* Run the standby's cache warm-up turn (see side-thread-warmup.ts). Called
|
|
101305
|
+
* when the user starts TYPING a new question: the fork's first API call pays
|
|
101306
|
+
* the full prefix write no matter what, so paying it while they type turns
|
|
101307
|
+
* the actual question into a cached follow-up. Idempotent per standby; a
|
|
101308
|
+
* missing or stale standby is a no-op (the ask path will fork fresh anyway).
|
|
101309
|
+
*/
|
|
101310
|
+
async warmStandby(parentSid) {
|
|
101311
|
+
return this.serialize(parentSid, async () => {
|
|
101312
|
+
const standby = await this.findStandby(parentSid);
|
|
101313
|
+
if (!standby) return { warmed: false, reason: "no_standby" };
|
|
101314
|
+
if (!await this.isStandbyUsable(parentSid, standby)) return { warmed: false, reason: "stale" };
|
|
101315
|
+
if (this.warmedStandbys.get(parentSid) === standby.claudeSessionId) {
|
|
101316
|
+
return { warmed: true, reason: "already_warm" };
|
|
101317
|
+
}
|
|
101318
|
+
const { sendMessageToSession: sendMessageToSession2 } = await Promise.resolve().then(() => (init_session_message_queue(), session_message_queue_exports));
|
|
101319
|
+
markWarmupTurnPending(standby.claudeSessionId);
|
|
101320
|
+
await sendMessageToSession2(standby.claudeSessionId, CACHE_WARMUP_MESSAGE, { source: "side-thread-warmup" });
|
|
101321
|
+
this.warmedStandbys.set(parentSid, standby.claudeSessionId);
|
|
101322
|
+
this.armStandbyTtl(parentSid, standby.claudeSessionId, WARMED_STANDBY_TTL_MS);
|
|
101323
|
+
log.session.info("side thread: standby cache warm-up sent", {
|
|
101324
|
+
parentSid,
|
|
101325
|
+
standbySid: standby.claudeSessionId
|
|
101326
|
+
});
|
|
101327
|
+
return { warmed: true };
|
|
101328
|
+
});
|
|
101329
|
+
}
|
|
101027
101330
|
// ── Threads ────────────────────────────────────────────────────────────────
|
|
101028
101331
|
/**
|
|
101029
101332
|
* Open a side thread on `parentSid` and ask `question` in it.
|
|
@@ -101034,6 +101337,10 @@ var init_side_thread_manager = __esm({
|
|
|
101034
101337
|
* question as its spawn's first turn instead — a send issued right after
|
|
101035
101338
|
* SESSION_START can lose that race and cold-`--resume` an id the CLI has never
|
|
101036
101339
|
* seen (see personal-ai-lane.ts LaneSession.created).
|
|
101340
|
+
*
|
|
101341
|
+
* `imageContext` (attachment paths for the CLI to Read) rides the MESSAGE only.
|
|
101342
|
+
* The stored entry keeps the plain question, so the chip label, the injected
|
|
101343
|
+
* transcript and a promoted task never carry the machine preamble.
|
|
101037
101344
|
*/
|
|
101038
101345
|
async createThread(parentSid, input) {
|
|
101039
101346
|
const question = input.question?.trim();
|
|
@@ -101041,8 +101348,11 @@ var init_side_thread_manager = __esm({
|
|
|
101041
101348
|
return this.serialize(parentSid, async () => {
|
|
101042
101349
|
const threadId = mintSideThreadId();
|
|
101043
101350
|
const title = input.title?.trim() || void 0;
|
|
101351
|
+
const message = input.imageContext ? `${input.imageContext}
|
|
101352
|
+
|
|
101353
|
+
${question}` : question;
|
|
101044
101354
|
const consumed = await this.consumeStandby(parentSid, threadId, title);
|
|
101045
|
-
const threadSessionId = consumed ?? (await forkSideThreadSession(parentSid, threadId, { message
|
|
101355
|
+
const threadSessionId = consumed ?? (await forkSideThreadSession(parentSid, threadId, { message, ...title ? { title } : {} })).sessionId;
|
|
101046
101356
|
let entry;
|
|
101047
101357
|
try {
|
|
101048
101358
|
const { addSideThread: addSideThread2 } = await Promise.resolve().then(() => (init_side_questions(), side_questions_exports));
|
|
@@ -101054,7 +101364,7 @@ var init_side_thread_manager = __esm({
|
|
|
101054
101364
|
});
|
|
101055
101365
|
if (consumed) {
|
|
101056
101366
|
const { sendMessageToSession: sendMessageToSession2 } = await Promise.resolve().then(() => (init_session_message_queue(), session_message_queue_exports));
|
|
101057
|
-
await sendMessageToSession2(threadSessionId,
|
|
101367
|
+
await sendMessageToSession2(threadSessionId, message, { source: "side-thread" });
|
|
101058
101368
|
}
|
|
101059
101369
|
} catch (err2) {
|
|
101060
101370
|
void this.retireSession(threadSessionId, "side_thread_create_failed");
|
|
@@ -101089,6 +101399,7 @@ var init_side_thread_manager = __esm({
|
|
|
101089
101399
|
if (!await this.isStandbyUsable(parentSid, standby)) {
|
|
101090
101400
|
void this.retireSession(standby.claudeSessionId, "side_thread_standby_stale");
|
|
101091
101401
|
this.standbyParentOffsets.delete(parentSid);
|
|
101402
|
+
this.warmedStandbys.delete(parentSid);
|
|
101092
101403
|
return null;
|
|
101093
101404
|
}
|
|
101094
101405
|
const timer3 = this.standbyTimers.get(parentSid);
|
|
@@ -101097,6 +101408,7 @@ var init_side_thread_manager = __esm({
|
|
|
101097
101408
|
this.standbyTimers.delete(parentSid);
|
|
101098
101409
|
}
|
|
101099
101410
|
this.standbyParentOffsets.delete(parentSid);
|
|
101411
|
+
this.warmedStandbys.delete(parentSid);
|
|
101100
101412
|
const lane = sideThreadLaneKey(parentSid, threadId);
|
|
101101
101413
|
const { updateSessionRecord: updateSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
101102
101414
|
await updateSessionRecord2(standby.claudeSessionId, {
|
|
@@ -101283,16 +101595,30 @@ async function promoteSideThread(parentSid, threadId, opts) {
|
|
|
101283
101595
|
const threadRecord = await getSessionByClaudeId2(sessionId);
|
|
101284
101596
|
if (!threadRecord) throw new SessionControlError("Side thread session not found", 404);
|
|
101285
101597
|
const parentRecord = await getSessionByClaudeId2(parentSid);
|
|
101286
|
-
const
|
|
101287
|
-
const
|
|
101598
|
+
const siblingOfTaskId = parentRecord?.taskId?.trim() || void 0;
|
|
101599
|
+
const threadLabel = opts?.title?.trim() || entry.title?.trim() || void 0;
|
|
101288
101600
|
const { addTask: addTask2, linkSession: linkSession2, linkSessionSlot: linkSessionSlot2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
101289
|
-
|
|
101290
|
-
|
|
101291
|
-
|
|
101292
|
-
|
|
101293
|
-
|
|
101294
|
-
|
|
101295
|
-
|
|
101601
|
+
let groupId;
|
|
101602
|
+
let task;
|
|
101603
|
+
if (siblingOfTaskId) {
|
|
101604
|
+
const created = await createForkSiblingTask(siblingOfTaskId, {
|
|
101605
|
+
// A known label short-circuits the background refine; without one the
|
|
101606
|
+
// thread's first question is what gets summarized (same as a fork's message).
|
|
101607
|
+
titlePrefix: threadLabel,
|
|
101608
|
+
prompt: entry.question,
|
|
101609
|
+
description: entry.question,
|
|
101610
|
+
source: "side-thread-promote"
|
|
101611
|
+
});
|
|
101612
|
+
task = created.task;
|
|
101613
|
+
groupId = created.groupId;
|
|
101614
|
+
} else {
|
|
101615
|
+
({ task } = await addTask2({
|
|
101616
|
+
title: threadLabel || entry.question,
|
|
101617
|
+
description: entry.question,
|
|
101618
|
+
// A person clicked "promote" — same board default as any hand-made task.
|
|
101619
|
+
pinned: true
|
|
101620
|
+
}));
|
|
101621
|
+
}
|
|
101296
101622
|
const stamped = await markThreadPromoted2(parentSid, threadId, task.id);
|
|
101297
101623
|
if (!stamped) {
|
|
101298
101624
|
log.session.warn("side thread promote: entry vanished before stamp \u2014 promote proceeds, 409 guard is unarmed", {
|
|
@@ -101306,7 +101632,7 @@ async function promoteSideThread(parentSid, threadId, opts) {
|
|
|
101306
101632
|
project: task.project ?? "",
|
|
101307
101633
|
// Clearing the lane un-hides the session (see the file header).
|
|
101308
101634
|
lane: void 0,
|
|
101309
|
-
title
|
|
101635
|
+
title: task.title
|
|
101310
101636
|
});
|
|
101311
101637
|
try {
|
|
101312
101638
|
const { sessionRunner: sessionRunner2 } = await Promise.resolve().then(() => (init_claude_code_session(), claude_code_session_exports));
|
|
@@ -101325,10 +101651,18 @@ async function promoteSideThread(parentSid, threadId, opts) {
|
|
|
101325
101651
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
101326
101652
|
});
|
|
101327
101653
|
}
|
|
101328
|
-
log.session.info("side thread: promoted to task", {
|
|
101654
|
+
log.session.info("side thread: promoted to task", {
|
|
101655
|
+
parentSid,
|
|
101656
|
+
threadId,
|
|
101657
|
+
sessionId,
|
|
101658
|
+
taskId: task.id,
|
|
101659
|
+
siblingOfTaskId,
|
|
101660
|
+
groupId
|
|
101661
|
+
});
|
|
101329
101662
|
return {
|
|
101330
101663
|
taskId: task.id,
|
|
101331
|
-
...
|
|
101664
|
+
...siblingOfTaskId ? { siblingOfTaskId } : {},
|
|
101665
|
+
...groupId ? { groupId } : {},
|
|
101332
101666
|
sessionId
|
|
101333
101667
|
};
|
|
101334
101668
|
}
|
|
@@ -101343,7 +101677,7 @@ var init_side_thread_promote = __esm({
|
|
|
101343
101677
|
|
|
101344
101678
|
// src/web/routes/sessions.ts
|
|
101345
101679
|
import { Router as Router10 } from "express";
|
|
101346
|
-
import { randomUUID as
|
|
101680
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
101347
101681
|
import os19 from "os";
|
|
101348
101682
|
import path91 from "path";
|
|
101349
101683
|
function condenseStaleReason(msg) {
|
|
@@ -101689,7 +102023,7 @@ var init_sessions2 = __esm({
|
|
|
101689
102023
|
}
|
|
101690
102024
|
const isNativeEngine = !isAcpEngine(engine);
|
|
101691
102025
|
const clientSessionId = typeof req.body.sessionId === "string" && UUID_RE2.test(req.body.sessionId) ? req.body.sessionId : void 0;
|
|
101692
|
-
const preassignedSessionId = isNativeEngine ? clientSessionId ??
|
|
102026
|
+
const preassignedSessionId = isNativeEngine ? clientSessionId ?? randomUUID6() : void 0;
|
|
101693
102027
|
const updatedTask = await quickStartSession({
|
|
101694
102028
|
message: sessionMessage,
|
|
101695
102029
|
messagePrefix,
|
|
@@ -102664,18 +102998,38 @@ var init_sessions2 = __esm({
|
|
|
102664
102998
|
});
|
|
102665
102999
|
}
|
|
102666
103000
|
});
|
|
103001
|
+
sessionsRouter.post("/:sessionId/side-threads/standby/warm", async (req, res) => {
|
|
103002
|
+
if (refuseSideThreadsOnReplica(res)) return;
|
|
103003
|
+
const sessionId = String(req.params.sessionId);
|
|
103004
|
+
try {
|
|
103005
|
+
const { sideThreadManager: sideThreadManager2 } = await Promise.resolve().then(() => (init_side_thread_manager(), side_thread_manager_exports));
|
|
103006
|
+
res.json(await sideThreadManager2.warmStandby(sessionId));
|
|
103007
|
+
} catch (err2) {
|
|
103008
|
+
log.web.warn("side thread standby warm-up failed", {
|
|
103009
|
+
sessionId,
|
|
103010
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
103011
|
+
});
|
|
103012
|
+
res.json({ warmed: false, reason: "error" });
|
|
103013
|
+
}
|
|
103014
|
+
});
|
|
102667
103015
|
sessionsRouter.post("/:sessionId/side-threads", async (req, res, next) => {
|
|
102668
103016
|
if (refuseSideThreadsOnReplica(res)) return;
|
|
102669
103017
|
try {
|
|
102670
|
-
const { question, title } = req.body ?? {};
|
|
103018
|
+
const { question, title, images } = req.body ?? {};
|
|
102671
103019
|
if (!question || typeof question !== "string" || !question.trim()) {
|
|
102672
103020
|
res.status(400).json({ error: "question (non-empty string) is required" });
|
|
102673
103021
|
return;
|
|
102674
103022
|
}
|
|
103023
|
+
let imageContext = "";
|
|
103024
|
+
if (images && images.length > 0) {
|
|
103025
|
+
const processed = await processAndSaveImages(images);
|
|
103026
|
+
if (processed) imageContext = buildSessionImageContext(processed.savedImages);
|
|
103027
|
+
}
|
|
102675
103028
|
const { sideThreadManager: sideThreadManager2 } = await Promise.resolve().then(() => (init_side_thread_manager(), side_thread_manager_exports));
|
|
102676
103029
|
const thread = await sideThreadManager2.createThread(String(req.params.sessionId), {
|
|
102677
103030
|
question,
|
|
102678
|
-
...typeof title === "string" && title.trim() ? { title } : {}
|
|
103031
|
+
...typeof title === "string" && title.trim() ? { title } : {},
|
|
103032
|
+
...imageContext ? { imageContext } : {}
|
|
102679
103033
|
});
|
|
102680
103034
|
res.json({ thread });
|
|
102681
103035
|
} catch (err2) {
|
|
@@ -102694,7 +103048,8 @@ var init_sessions2 = __esm({
|
|
|
102694
103048
|
);
|
|
102695
103049
|
res.json({
|
|
102696
103050
|
taskId: result.taskId,
|
|
102697
|
-
...result.
|
|
103051
|
+
...result.siblingOfTaskId ? { siblingOfTaskId: result.siblingOfTaskId } : {},
|
|
103052
|
+
...result.groupId ? { groupId: result.groupId } : {},
|
|
102698
103053
|
sessionId: result.sessionId
|
|
102699
103054
|
});
|
|
102700
103055
|
} catch (err2) {
|
|
@@ -103389,7 +103744,7 @@ var micro_claude_exports = {};
|
|
|
103389
103744
|
__export(micro_claude_exports, {
|
|
103390
103745
|
runMicroClaude: () => runMicroClaude
|
|
103391
103746
|
});
|
|
103392
|
-
import { randomUUID as
|
|
103747
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
103393
103748
|
async function runMicroClaude(opts) {
|
|
103394
103749
|
if (opts.warm) {
|
|
103395
103750
|
const run10 = await runWarmMicroClaude({
|
|
@@ -103398,7 +103753,7 @@ async function runMicroClaude(opts) {
|
|
|
103398
103753
|
tools: opts.tools ?? [],
|
|
103399
103754
|
prompt: opts.prompt,
|
|
103400
103755
|
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
103401
|
-
toolUseId: opts.toolUseId ?? `micro-claude-${
|
|
103756
|
+
toolUseId: opts.toolUseId ?? `micro-claude-${randomUUID7()}`,
|
|
103402
103757
|
...opts.onBlock ? { onBlock: opts.onBlock } : {}
|
|
103403
103758
|
});
|
|
103404
103759
|
return { response: run10.response, costUsd: run10.costUsd, durationMs: run10.durationMs };
|
|
@@ -103408,7 +103763,7 @@ async function runMicroClaude(opts) {
|
|
|
103408
103763
|
systemPrompt: opts.system,
|
|
103409
103764
|
model: opts.model ?? DEFAULT_MODEL2,
|
|
103410
103765
|
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
103411
|
-
toolUseId: opts.toolUseId ?? `micro-claude-${
|
|
103766
|
+
toolUseId: opts.toolUseId ?? `micro-claude-${randomUUID7()}`,
|
|
103412
103767
|
slim: true,
|
|
103413
103768
|
thinking: opts.thinking ?? false,
|
|
103414
103769
|
...opts.tools && opts.tools.length > 0 ? { tools: opts.tools } : {},
|
|
@@ -104233,7 +104588,7 @@ __export(micro_agent_exports, {
|
|
|
104233
104588
|
async function resolveTierModel(tier) {
|
|
104234
104589
|
const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
104235
104590
|
const config = await getConfig2();
|
|
104236
|
-
const provider = config
|
|
104591
|
+
const provider = resolveMainProviderName(config);
|
|
104237
104592
|
const { MODEL_CATALOG: MODEL_CATALOG3 } = await Promise.resolve().then(() => (init_model_catalog(), model_catalog_exports));
|
|
104238
104593
|
const entry = MODEL_CATALOG3[provider]?.find((m6) => {
|
|
104239
104594
|
const id = m6.id.toLowerCase();
|
|
@@ -104302,6 +104657,7 @@ var DEFAULT_TIER, DEFAULT_MAX_TOKENS2, DEFAULT_MAX_ROUNDS, DEFAULT_TIMEOUT_MS3;
|
|
|
104302
104657
|
var init_micro_agent = __esm({
|
|
104303
104658
|
"src/agent/micro-agent.ts"() {
|
|
104304
104659
|
"use strict";
|
|
104660
|
+
init_default_provider();
|
|
104305
104661
|
DEFAULT_TIER = "sonnet";
|
|
104306
104662
|
DEFAULT_MAX_TOKENS2 = 2e3;
|
|
104307
104663
|
DEFAULT_MAX_ROUNDS = 3;
|
|
@@ -104317,7 +104673,7 @@ __export(task_search_agent_exports, {
|
|
|
104317
104673
|
prewarmAgentSearchChild: () => prewarmAgentSearchChild,
|
|
104318
104674
|
runTaskSearchAgent: () => runTaskSearchAgent
|
|
104319
104675
|
});
|
|
104320
|
-
import { randomUUID as
|
|
104676
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
104321
104677
|
function useCliEngine() {
|
|
104322
104678
|
return process.env.WALNUT_AGENT_SEARCH_ENGINE !== "inprocess";
|
|
104323
104679
|
}
|
|
@@ -104397,7 +104753,7 @@ async function claudeCliEngine(userPrompt, options) {
|
|
|
104397
104753
|
model: options.model,
|
|
104398
104754
|
timeoutMs: options.timeoutMs,
|
|
104399
104755
|
tools: ["Bash"],
|
|
104400
|
-
toolUseId: `task-search-${
|
|
104756
|
+
toolUseId: `task-search-${randomUUID8()}`,
|
|
104401
104757
|
warm: true,
|
|
104402
104758
|
onBlock
|
|
104403
104759
|
});
|
|
@@ -105660,10 +106016,11 @@ async function buildProvidersPayload() {
|
|
|
105660
106016
|
providers[name] = {
|
|
105661
106017
|
api: prov.api,
|
|
105662
106018
|
base_url: prov.base_url,
|
|
105663
|
-
status: caps.
|
|
106019
|
+
status: caps.installed ? "ready" : "no_key",
|
|
105664
106020
|
auto_detected: !explicitNames.has(name),
|
|
105665
106021
|
models: getModelsForProvider(name, prov.models),
|
|
105666
|
-
credential_source: caps.
|
|
106022
|
+
credential_source: caps.installed ? `cli_${caps.auth.mode}` : "cli_not_installed",
|
|
106023
|
+
credential_detail: caps.installed ? caps.auth.label : void 0
|
|
105667
106024
|
};
|
|
105668
106025
|
continue;
|
|
105669
106026
|
}
|
|
@@ -105722,10 +106079,11 @@ async function buildProvidersPayload() {
|
|
|
105722
106079
|
const caps = detectClaudeCli2();
|
|
105723
106080
|
providers[name] = {
|
|
105724
106081
|
api: template.api,
|
|
105725
|
-
status: caps.
|
|
106082
|
+
status: caps.installed ? "ready" : "no_key",
|
|
105726
106083
|
auto_detected: false,
|
|
105727
106084
|
models: getModelsForProvider(name),
|
|
105728
|
-
credential_source: caps.
|
|
106085
|
+
credential_source: caps.installed ? `cli_${caps.auth.mode}` : "cli_not_installed",
|
|
106086
|
+
credential_detail: caps.installed ? caps.auth.label : void 0
|
|
105729
106087
|
};
|
|
105730
106088
|
continue;
|
|
105731
106089
|
}
|
|
@@ -105881,7 +106239,7 @@ var init_config11 = __esm({
|
|
|
105881
106239
|
const {
|
|
105882
106240
|
traceCredentialResolution: traceCredentialResolution2,
|
|
105883
106241
|
resolveCredentials: resolveCredentials2,
|
|
105884
|
-
readClaudeSettingsEnv:
|
|
106242
|
+
readClaudeSettingsEnv: readClaudeSettingsEnv3,
|
|
105885
106243
|
readClaudeCredentialExport: readClaudeCredentialExport2
|
|
105886
106244
|
} = await Promise.resolve().then(() => (init_credential_resolver(), credential_resolver_exports));
|
|
105887
106245
|
const fs121 = await import("fs");
|
|
@@ -105897,7 +106255,7 @@ var init_config11 = __esm({
|
|
|
105897
106255
|
};
|
|
105898
106256
|
const trace = traceCredentialResolution2({
|
|
105899
106257
|
config,
|
|
105900
|
-
claudeEnv:
|
|
106258
|
+
claudeEnv: readClaudeSettingsEnv3(),
|
|
105901
106259
|
processEnv: process.env,
|
|
105902
106260
|
awsFiles: {
|
|
105903
106261
|
credentials: exists3(path193.join(home, ".aws", "credentials")),
|
|
@@ -133332,7 +133690,7 @@ __export(session_requests_exports, {
|
|
|
133332
133690
|
settleNotified: () => settleNotified,
|
|
133333
133691
|
settleReplied: () => settleReplied
|
|
133334
133692
|
});
|
|
133335
|
-
import { createHash as createHash18, randomUUID as
|
|
133693
|
+
import { createHash as createHash18, randomUUID as randomUUID9 } from "crypto";
|
|
133336
133694
|
import path137 from "path";
|
|
133337
133695
|
function oneLine(text, max = 160) {
|
|
133338
133696
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
@@ -133356,7 +133714,7 @@ function clampReplyTimeoutSecs(secs) {
|
|
|
133356
133714
|
}
|
|
133357
133715
|
async function createSessionRequest(input) {
|
|
133358
133716
|
const request3 = {
|
|
133359
|
-
id: `rq-${
|
|
133717
|
+
id: `rq-${randomUUID9().replace(/-/g, "").slice(0, 12)}`,
|
|
133360
133718
|
fromSessionId: input.fromSessionId,
|
|
133361
133719
|
...input.toSessionId ? { toSessionId: input.toSessionId } : {},
|
|
133362
133720
|
...input.toTaskId ? { toTaskId: input.toTaskId } : {},
|
|
@@ -133789,7 +134147,7 @@ __export(task_start_exports, {
|
|
|
133789
134147
|
SessionExistsError: () => SessionExistsError,
|
|
133790
134148
|
startSessionForTask: () => startSessionForTask
|
|
133791
134149
|
});
|
|
133792
|
-
import { randomUUID as
|
|
134150
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
133793
134151
|
async function startSessionForTask(params) {
|
|
133794
134152
|
const { taskIdPrefix, source } = params;
|
|
133795
134153
|
let task;
|
|
@@ -133840,7 +134198,7 @@ async function startSessionForTask(params) {
|
|
|
133840
134198
|
}
|
|
133841
134199
|
let message = params.message?.trim() || `Working on task: ${task.title}`;
|
|
133842
134200
|
const engine = normalizeEngine(params.engine);
|
|
133843
|
-
const preassignedSessionId = engineCaps(engine).idProvisioning === "provider-issued" ? void 0 :
|
|
134201
|
+
const preassignedSessionId = engineCaps(engine).idProvisioning === "provider-issued" ? void 0 : randomUUID10();
|
|
133844
134202
|
let requestId;
|
|
133845
134203
|
if (params.expectReply) {
|
|
133846
134204
|
const { resolveCaller: resolveCaller2 } = await Promise.resolve().then(() => (init_session_send_core(), session_send_core_exports));
|
|
@@ -149338,7 +149696,7 @@ async function generateLedgerDesc(task, timeoutMs = 15e3) {
|
|
|
149338
149696
|
try {
|
|
149339
149697
|
const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
149340
149698
|
const config = await getConfig2();
|
|
149341
|
-
const providerName = config
|
|
149699
|
+
const providerName = resolveMainProviderName(config);
|
|
149342
149700
|
const model = cheapModelFor(providerName);
|
|
149343
149701
|
const detail = (task.description ?? "").trim().slice(0, 800);
|
|
149344
149702
|
const project = (task.project ?? "").trim();
|
|
@@ -149397,6 +149755,7 @@ var init_task_ledger_desc = __esm({
|
|
|
149397
149755
|
"use strict";
|
|
149398
149756
|
init_model();
|
|
149399
149757
|
init_model_catalog();
|
|
149758
|
+
init_default_provider();
|
|
149400
149759
|
init_logging();
|
|
149401
149760
|
init_task_manager();
|
|
149402
149761
|
MAX_DESC_LEN = 120;
|
|
@@ -149420,6 +149779,10 @@ function diffStreamedVsPersisted(streamedTextMsgIds, persistedMsgIds) {
|
|
|
149420
149779
|
}
|
|
149421
149780
|
function armStreamConvergenceCheck(sessionId, streamedTextMsgIds) {
|
|
149422
149781
|
if (streamedTextMsgIds.length === 0) return;
|
|
149782
|
+
if (consumeWarmupTurn(sessionId)) {
|
|
149783
|
+
log.obs.debug("stream-convergence: skipped hidden warm-up turn", { sessionId });
|
|
149784
|
+
return;
|
|
149785
|
+
}
|
|
149423
149786
|
const timer3 = setTimeout(() => {
|
|
149424
149787
|
void runCheck(sessionId, streamedTextMsgIds).catch((err2) => {
|
|
149425
149788
|
log.obs.warn("stream-convergence check failed", {
|
|
@@ -149485,6 +149848,7 @@ var init_stream_convergence = __esm({
|
|
|
149485
149848
|
"src/core/observability/stream-convergence.ts"() {
|
|
149486
149849
|
"use strict";
|
|
149487
149850
|
init_logging();
|
|
149851
|
+
init_side_thread_warmup();
|
|
149488
149852
|
CHECK_DELAY_MS = 15e3;
|
|
149489
149853
|
INCIDENT_DEDUPE_MS = 30 * 6e4;
|
|
149490
149854
|
lastIncidentAt = /* @__PURE__ */ new Map();
|
|
@@ -149735,12 +150099,17 @@ function getSystemHealth() {
|
|
|
149735
150099
|
}
|
|
149736
150100
|
async function refreshSystemHealth() {
|
|
149737
150101
|
systemHealth.claudeCliAvailable = checkClaudeCliAvailable();
|
|
149738
|
-
|
|
150102
|
+
applyCredentialHealth(await resolveCredentialHealth());
|
|
150103
|
+
broadcastEvent("system:health", systemHealth);
|
|
150104
|
+
return systemHealth;
|
|
150105
|
+
}
|
|
150106
|
+
function applyCredentialHealth(cred) {
|
|
149739
150107
|
systemHealth.hasReadyProvider = cred.hasReadyProvider;
|
|
149740
150108
|
systemHealth.credentialSource = cred.source;
|
|
149741
150109
|
systemHealth.credentialDetail = cred.detail;
|
|
149742
|
-
|
|
149743
|
-
|
|
150110
|
+
systemHealth.mainProvider = cred.mainProvider;
|
|
150111
|
+
systemHealth.mainProviderImplicit = cred.mainProviderImplicit;
|
|
150112
|
+
systemHealth.claudeCliAuth = cred.claudeCliAuth;
|
|
149744
150113
|
}
|
|
149745
150114
|
function checkClaudeCliAvailable() {
|
|
149746
150115
|
return isClaudeCliInstalled();
|
|
@@ -149750,27 +150119,36 @@ async function resolveCredentialHealth() {
|
|
|
149750
150119
|
const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
149751
150120
|
const { resolveCredentials: resolveCredentials2 } = await Promise.resolve().then(() => (init_credential_resolver(), credential_resolver_exports));
|
|
149752
150121
|
const { buildProviderMap: buildProviderMap2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports));
|
|
150122
|
+
const { resolveMainProviderName: resolveMainProviderName2, mainProviderIsImplicit: mainProviderIsImplicit2 } = await Promise.resolve().then(() => (init_default_provider(), default_provider_exports));
|
|
150123
|
+
const { detectClaudeCli: detectClaudeCli2 } = await Promise.resolve().then(() => (init_claude_cli_detect(), claude_cli_detect_exports));
|
|
149753
150124
|
const config = await getConfig2();
|
|
150125
|
+
const cli = detectClaudeCli2();
|
|
150126
|
+
const mainProvider = resolveMainProviderName2(config, cli.installed);
|
|
150127
|
+
const base = {
|
|
150128
|
+
mainProvider,
|
|
150129
|
+
mainProviderImplicit: mainProviderIsImplicit2(config),
|
|
150130
|
+
claudeCliAuth: cli.installed ? cli.auth.label : void 0
|
|
150131
|
+
};
|
|
150132
|
+
if (mainProvider === "claude_cli") {
|
|
150133
|
+
return cli.installed ? { ...base, hasReadyProvider: true, source: "config", detail: `claude-cli (${cli.auth.label})` } : { ...base, hasReadyProvider: false, source: "none" };
|
|
150134
|
+
}
|
|
149754
150135
|
const cred = resolveCredentials2(config);
|
|
149755
150136
|
if (cred.source !== "none") {
|
|
149756
|
-
return { hasReadyProvider: true, source: cred.source, detail: cred.detail };
|
|
150137
|
+
return { ...base, hasReadyProvider: true, source: cred.source, detail: cred.detail };
|
|
149757
150138
|
}
|
|
149758
150139
|
const providers = buildProviderMap2(config.providers, config);
|
|
149759
150140
|
for (const [, prov] of Object.entries(providers)) {
|
|
149760
150141
|
if (prov.api === "bedrock" || prov.api === "ollama") continue;
|
|
149761
150142
|
if (prov.api === "claude-cli") {
|
|
149762
|
-
|
|
149763
|
-
if (detectClaudeCli2().subscriptionReady) {
|
|
149764
|
-
return { hasReadyProvider: true, source: "config", detail: "claude-cli (subscription)" };
|
|
149765
|
-
}
|
|
150143
|
+
if (cli.installed) return { ...base, hasReadyProvider: true, source: "config", detail: `claude-cli (${cli.auth.label})` };
|
|
149766
150144
|
continue;
|
|
149767
150145
|
}
|
|
149768
150146
|
const implemented = prov.api === "anthropic-messages" || prov.api === "openai-chat" || prov.api === "google-generative-ai";
|
|
149769
150147
|
if (implemented && (prov.api_key || prov.bearer_token)) {
|
|
149770
|
-
return { hasReadyProvider: true, source: "config", detail: prov.api };
|
|
150148
|
+
return { ...base, hasReadyProvider: true, source: "config", detail: prov.api };
|
|
149771
150149
|
}
|
|
149772
150150
|
}
|
|
149773
|
-
return { hasReadyProvider: false, source: "none" };
|
|
150151
|
+
return { ...base, hasReadyProvider: false, source: "none" };
|
|
149774
150152
|
} catch {
|
|
149775
150153
|
return { hasReadyProvider: false, source: "none" };
|
|
149776
150154
|
}
|
|
@@ -149809,11 +150187,11 @@ async function startServer(options = {}) {
|
|
|
149809
150187
|
}
|
|
149810
150188
|
{
|
|
149811
150189
|
const cred = await resolveCredentialHealth();
|
|
149812
|
-
|
|
149813
|
-
systemHealth.credentialSource = cred.source;
|
|
149814
|
-
systemHealth.credentialDetail = cred.detail;
|
|
150190
|
+
applyCredentialHealth(cred);
|
|
149815
150191
|
if (!systemHealth.hasReadyProvider) {
|
|
149816
150192
|
log.web.warn("No AI provider configured \u2014 configure one in Settings");
|
|
150193
|
+
} else if (cred.mainProvider === "claude_cli") {
|
|
150194
|
+
log.web.info("AI provider: Claude Code", { auth: cred.claudeCliAuth, implicit: cred.mainProviderImplicit });
|
|
149817
150195
|
} else if (cred.source && cred.source !== "config") {
|
|
149818
150196
|
log.web.info("Auto-detected Bedrock credential", { source: cred.source, detail: cred.detail });
|
|
149819
150197
|
}
|
|
@@ -151093,10 +151471,9 @@ async function startServer(options = {}) {
|
|
|
151093
151471
|
const prev = systemHealth.hasReadyProvider;
|
|
151094
151472
|
const prevSource = systemHealth.credentialSource;
|
|
151095
151473
|
const cred = await resolveCredentialHealth();
|
|
151096
|
-
|
|
151097
|
-
|
|
151098
|
-
|
|
151099
|
-
if (cred.hasReadyProvider !== prev || cred.source !== prevSource) {
|
|
151474
|
+
const prevProvider = systemHealth.mainProvider;
|
|
151475
|
+
applyCredentialHealth(cred);
|
|
151476
|
+
if (cred.hasReadyProvider !== prev || cred.source !== prevSource || cred.mainProvider !== prevProvider) {
|
|
151100
151477
|
broadcastEvent("system:health", systemHealth);
|
|
151101
151478
|
}
|
|
151102
151479
|
} catch {
|
|
@@ -158523,11 +158900,12 @@ __export(session_controls_exports, {
|
|
|
158523
158900
|
applySessionModelChange: () => applySessionModelChange,
|
|
158524
158901
|
computeModelOptions: () => computeModelOptions,
|
|
158525
158902
|
controlErrorKind: () => controlErrorKind,
|
|
158903
|
+
createForkSiblingTask: () => createForkSiblingTask,
|
|
158526
158904
|
forkSessionToTask: () => forkSessionToTask,
|
|
158527
158905
|
handleSessionControlRelay: () => handleSessionControlRelay,
|
|
158528
158906
|
modelReadBackMatches: () => modelReadBackMatches
|
|
158529
158907
|
});
|
|
158530
|
-
import { randomUUID as
|
|
158908
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
158531
158909
|
function controlErrorKind(status2) {
|
|
158532
158910
|
if (status2 === 404) return "not_found";
|
|
158533
158911
|
if (status2 === 405) return "method_not_allowed";
|
|
@@ -158700,6 +159078,124 @@ async function computeModelOptions(sessionId) {
|
|
|
158700
159078
|
currentEffort: liveEffort ?? record.effectiveEffort ?? record.effort ?? null
|
|
158701
159079
|
};
|
|
158702
159080
|
}
|
|
159081
|
+
async function createForkSiblingTask(sourceTaskId, opts) {
|
|
159082
|
+
const {
|
|
159083
|
+
getTask: getTask2,
|
|
159084
|
+
addTask: addTask2,
|
|
159085
|
+
updateTask: updateTask2,
|
|
159086
|
+
groupTasks: groupTasks2,
|
|
159087
|
+
addToGroup: addToGroup2,
|
|
159088
|
+
renameGroup: renameGroup2
|
|
159089
|
+
} = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
159090
|
+
let sourceTask;
|
|
159091
|
+
try {
|
|
159092
|
+
sourceTask = await getTask2(sourceTaskId);
|
|
159093
|
+
} catch {
|
|
159094
|
+
throw new SessionControlError(`Source task "${sourceTaskId}" not found`, 404);
|
|
159095
|
+
}
|
|
159096
|
+
const sourceBaseTitle = sourceTask.title.replace(/^Fork of\s+/i, "").replace(/\s+-\s+fork of\s+.*$/i, "").trim() || sourceTask.title;
|
|
159097
|
+
const explicitTitle = opts.childTitle || (opts.titlePrefix ? `${opts.titlePrefix} - fork of ${sourceBaseTitle}` : void 0);
|
|
159098
|
+
const autoTitle = !explicitTitle;
|
|
159099
|
+
const newTitle = explicitTitle || `Fork of ${sourceBaseTitle}`;
|
|
159100
|
+
let inheritedTier;
|
|
159101
|
+
if (sourceTask.pinned && sourceTask.focus_tier) {
|
|
159102
|
+
const { getCustomTiers: getCustomTiers2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
159103
|
+
const known = ["focus", "backlog", "wait"].includes(sourceTask.focus_tier) || (await getCustomTiers2()).some((t3) => t3.id === sourceTask.focus_tier);
|
|
159104
|
+
if (known) inheritedTier = sourceTask.focus_tier;
|
|
159105
|
+
else {
|
|
159106
|
+
log.session.warn("fork: source tier is not registered, filing the fork in Satellite", {
|
|
159107
|
+
sourceTaskId: sourceTask.id,
|
|
159108
|
+
tier: sourceTask.focus_tier
|
|
159109
|
+
});
|
|
159110
|
+
}
|
|
159111
|
+
}
|
|
159112
|
+
const { task: newFork } = await addTask2({
|
|
159113
|
+
title: newTitle,
|
|
159114
|
+
project: sourceTask.project || "",
|
|
159115
|
+
source: sourceTask.source,
|
|
159116
|
+
...opts.description ? { description: opts.description } : {},
|
|
159117
|
+
// A fork is a person splitting off live work, so it joins the board like
|
|
159118
|
+
// any other hand-made task (Satellite = pinned, no stored tier).
|
|
159119
|
+
pinned: true,
|
|
159120
|
+
...inheritedTier ? { focus_tier: inheritedTier } : {},
|
|
159121
|
+
// Forking an Ask Walnut conversation yields another Ask Walnut
|
|
159122
|
+
// conversation: without the flag the fork loses the amber marker AND the
|
|
159123
|
+
// persona drift repair (both key on task.walnut_agent, never on project).
|
|
159124
|
+
...sourceTask.walnut_agent ? { walnut_agent: true } : {}
|
|
159125
|
+
});
|
|
159126
|
+
let forkGroupId;
|
|
159127
|
+
try {
|
|
159128
|
+
if (sourceTask.group_id) {
|
|
159129
|
+
const r8 = await addToGroup2(sourceTask.group_id, [newFork.id]);
|
|
159130
|
+
forkGroupId = r8.group_id;
|
|
159131
|
+
} else {
|
|
159132
|
+
const r8 = await groupTasks2([sourceTask.id, newFork.id], sourceTask.title);
|
|
159133
|
+
forkGroupId = r8.group_id;
|
|
159134
|
+
}
|
|
159135
|
+
} catch (err2) {
|
|
159136
|
+
log.session.warn("fork: failed to group source + fork", {
|
|
159137
|
+
sourceTaskId: sourceTask.id,
|
|
159138
|
+
forkTaskId: newFork.id,
|
|
159139
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
159140
|
+
});
|
|
159141
|
+
}
|
|
159142
|
+
let task;
|
|
159143
|
+
try {
|
|
159144
|
+
task = await getTask2(newFork.id);
|
|
159145
|
+
} catch {
|
|
159146
|
+
task = newFork;
|
|
159147
|
+
}
|
|
159148
|
+
bus.emit(EventNames.TASK_CREATED, { task }, ["web-ui", "main-agent"], { source: opts.source });
|
|
159149
|
+
if (autoTitle && opts.prompt?.trim()) {
|
|
159150
|
+
const forkId = newFork.id;
|
|
159151
|
+
const sourceTitle = sourceBaseTitle;
|
|
159152
|
+
const placeholderTitle = newTitle;
|
|
159153
|
+
const prompt = opts.prompt;
|
|
159154
|
+
const titleSource = `${opts.source}-title`;
|
|
159155
|
+
void (async () => {
|
|
159156
|
+
try {
|
|
159157
|
+
const { summarizeForkPrompt: summarizeForkPrompt2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
159158
|
+
const label = await summarizeForkPrompt2(prompt);
|
|
159159
|
+
if (!label) return;
|
|
159160
|
+
const current5 = await getTask2(forkId);
|
|
159161
|
+
if (current5.title !== placeholderTitle) {
|
|
159162
|
+
log.session.info("fork title refine skipped \u2014 title changed since fork", { taskId: forkId });
|
|
159163
|
+
return;
|
|
159164
|
+
}
|
|
159165
|
+
const refinedTitle = `${label} - fork of ${sourceTitle}`;
|
|
159166
|
+
const { task: updated } = await updateTask2(forkId, { title: refinedTitle }, { source: titleSource });
|
|
159167
|
+
bus.emit(EventNames.TASK_UPDATED, { task: updated }, ["web-ui", "main-agent"], { source: titleSource });
|
|
159168
|
+
log.session.info("fork title refined", { taskId: forkId, title: refinedTitle });
|
|
159169
|
+
} catch (err2) {
|
|
159170
|
+
log.session.warn("fork title refine failed", {
|
|
159171
|
+
taskId: forkId,
|
|
159172
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
159173
|
+
});
|
|
159174
|
+
}
|
|
159175
|
+
})();
|
|
159176
|
+
}
|
|
159177
|
+
if (forkGroupId && !sourceTask.group_id) {
|
|
159178
|
+
const gid = forkGroupId;
|
|
159179
|
+
const seedTitles = [sourceTask.title, newTitle];
|
|
159180
|
+
const groupSource = opts.source;
|
|
159181
|
+
void (async () => {
|
|
159182
|
+
try {
|
|
159183
|
+
const { summarizeGroupLabel: summarizeGroupLabel2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
159184
|
+
const label = await summarizeGroupLabel2(seedTitles);
|
|
159185
|
+
if (!label) return;
|
|
159186
|
+
await renameGroup2(gid, label);
|
|
159187
|
+
bus.emit(EventNames.TASK_GROUPS_CHANGED, { group_id: gid, label }, ["web-ui", "main-agent"], { source: groupSource });
|
|
159188
|
+
log.session.info("fork group label refined", { groupId: gid, label });
|
|
159189
|
+
} catch (err2) {
|
|
159190
|
+
log.session.warn("fork group label refine failed", {
|
|
159191
|
+
groupId: gid,
|
|
159192
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
159193
|
+
});
|
|
159194
|
+
}
|
|
159195
|
+
})();
|
|
159196
|
+
}
|
|
159197
|
+
return { task, ...forkGroupId ? { groupId: forkGroupId } : {} };
|
|
159198
|
+
}
|
|
158703
159199
|
async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
158704
159200
|
const { task_id, create_child_task, child_title, message, title, model, imageContext } = input;
|
|
158705
159201
|
if (!task_id && !create_child_task) {
|
|
@@ -158709,14 +159205,7 @@ async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
|
158709
159205
|
throw new SessionControlError("task_id and create_child_task are mutually exclusive", 400);
|
|
158710
159206
|
}
|
|
158711
159207
|
const { getSessionByClaudeId: getSessionByClaudeId2, getSessionsForTask: getSessionsForTask2, createSessionRecord: createSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
158712
|
-
const {
|
|
158713
|
-
getTask: getTask2,
|
|
158714
|
-
addTask: addTask2,
|
|
158715
|
-
updateTask: updateTask2,
|
|
158716
|
-
groupTasks: groupTasks2,
|
|
158717
|
-
addToGroup: addToGroup2,
|
|
158718
|
-
renameGroup: renameGroup2
|
|
158719
|
-
} = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
159208
|
+
const { getTask: getTask2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
158720
159209
|
const sourceRecord = await getSessionByClaudeId2(sourceSessionId);
|
|
158721
159210
|
if (!sourceRecord) throw new SessionControlError("Source session not found", 404);
|
|
158722
159211
|
if (!engineCaps(sourceRecord.engine).fork) {
|
|
@@ -158733,108 +159222,13 @@ async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
|
158733
159222
|
if (!sourceRecord.taskId) {
|
|
158734
159223
|
throw new SessionControlError("Source session has no task \u2014 cannot create fork task", 400);
|
|
158735
159224
|
}
|
|
158736
|
-
|
|
158737
|
-
|
|
158738
|
-
|
|
158739
|
-
|
|
158740
|
-
|
|
158741
|
-
|
|
158742
|
-
const sourceBaseTitle = sourceTask.title.replace(/^Fork of\s+/i, "").replace(/\s+-\s+fork of\s+.*$/i, "").trim() || sourceTask.title;
|
|
158743
|
-
const autoTitle = !child_title;
|
|
158744
|
-
const newTitle = child_title || `Fork of ${sourceBaseTitle}`;
|
|
158745
|
-
let inheritedTier;
|
|
158746
|
-
if (sourceTask.pinned && sourceTask.focus_tier) {
|
|
158747
|
-
const { getCustomTiers: getCustomTiers2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
158748
|
-
const known = ["focus", "backlog", "wait"].includes(sourceTask.focus_tier) || (await getCustomTiers2()).some((t3) => t3.id === sourceTask.focus_tier);
|
|
158749
|
-
if (known) inheritedTier = sourceTask.focus_tier;
|
|
158750
|
-
else {
|
|
158751
|
-
log.session.warn("fork: source tier is not registered, filing the fork in Satellite", {
|
|
158752
|
-
sourceTaskId: sourceTask.id,
|
|
158753
|
-
tier: sourceTask.focus_tier
|
|
158754
|
-
});
|
|
158755
|
-
}
|
|
158756
|
-
}
|
|
158757
|
-
const { task: newFork } = await addTask2({
|
|
158758
|
-
title: newTitle,
|
|
158759
|
-
project: sourceTask.project || "",
|
|
158760
|
-
source: sourceTask.source,
|
|
158761
|
-
// A fork is a person splitting off live work, so it joins the board like
|
|
158762
|
-
// any other hand-made task (Satellite = pinned, no stored tier).
|
|
158763
|
-
pinned: true,
|
|
158764
|
-
...inheritedTier ? { focus_tier: inheritedTier } : {},
|
|
158765
|
-
// Forking an Ask Walnut conversation yields another Ask Walnut
|
|
158766
|
-
// conversation: without the flag the fork loses the amber marker AND the
|
|
158767
|
-
// persona drift repair (both key on task.walnut_agent, never on project).
|
|
158768
|
-
...sourceTask.walnut_agent ? { walnut_agent: true } : {}
|
|
158769
|
-
});
|
|
158770
|
-
let forkGroupId;
|
|
158771
|
-
try {
|
|
158772
|
-
if (sourceTask.group_id) {
|
|
158773
|
-
const r8 = await addToGroup2(sourceTask.group_id, [newFork.id]);
|
|
158774
|
-
forkGroupId = r8.group_id;
|
|
158775
|
-
} else {
|
|
158776
|
-
const r8 = await groupTasks2([sourceTask.id, newFork.id], sourceTask.title);
|
|
158777
|
-
forkGroupId = r8.group_id;
|
|
158778
|
-
}
|
|
158779
|
-
} catch (err2) {
|
|
158780
|
-
log.session.warn("fork: failed to group source + fork", {
|
|
158781
|
-
sourceTaskId: sourceTask.id,
|
|
158782
|
-
forkTaskId: newFork.id,
|
|
158783
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
158784
|
-
});
|
|
158785
|
-
}
|
|
158786
|
-
try {
|
|
158787
|
-
task = await getTask2(newFork.id);
|
|
158788
|
-
} catch {
|
|
158789
|
-
task = newFork;
|
|
158790
|
-
}
|
|
158791
|
-
bus.emit(EventNames.TASK_CREATED, { task }, ["web-ui", "main-agent"], { source: "fork" });
|
|
159225
|
+
const created = await createForkSiblingTask(sourceRecord.taskId, {
|
|
159226
|
+
childTitle: child_title,
|
|
159227
|
+
prompt: message,
|
|
159228
|
+
source: "fork"
|
|
159229
|
+
});
|
|
159230
|
+
task = created.task;
|
|
158792
159231
|
childTaskCreated = true;
|
|
158793
|
-
if (autoTitle && message?.trim()) {
|
|
158794
|
-
const forkId = newFork.id;
|
|
158795
|
-
const sourceTitle = sourceBaseTitle;
|
|
158796
|
-
const placeholderTitle = newTitle;
|
|
158797
|
-
void (async () => {
|
|
158798
|
-
try {
|
|
158799
|
-
const { summarizeForkPrompt: summarizeForkPrompt2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
158800
|
-
const label = await summarizeForkPrompt2(message);
|
|
158801
|
-
if (!label) return;
|
|
158802
|
-
const current5 = await getTask2(forkId);
|
|
158803
|
-
if (current5.title !== placeholderTitle) {
|
|
158804
|
-
log.session.info("fork title refine skipped \u2014 title changed since fork", { taskId: forkId });
|
|
158805
|
-
return;
|
|
158806
|
-
}
|
|
158807
|
-
const refinedTitle = `${label} - fork of ${sourceTitle}`;
|
|
158808
|
-
const { task: updated } = await updateTask2(forkId, { title: refinedTitle }, { source: "fork-title" });
|
|
158809
|
-
bus.emit(EventNames.TASK_UPDATED, { task: updated }, ["web-ui", "main-agent"], { source: "fork-title" });
|
|
158810
|
-
log.session.info("fork title refined", { taskId: forkId, title: refinedTitle });
|
|
158811
|
-
} catch (err2) {
|
|
158812
|
-
log.session.warn("fork title refine failed", {
|
|
158813
|
-
taskId: forkId,
|
|
158814
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
158815
|
-
});
|
|
158816
|
-
}
|
|
158817
|
-
})();
|
|
158818
|
-
}
|
|
158819
|
-
if (forkGroupId && !sourceTask.group_id) {
|
|
158820
|
-
const gid = forkGroupId;
|
|
158821
|
-
const seedTitles = [sourceTask.title, newTitle];
|
|
158822
|
-
void (async () => {
|
|
158823
|
-
try {
|
|
158824
|
-
const { summarizeGroupLabel: summarizeGroupLabel2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
158825
|
-
const label = await summarizeGroupLabel2(seedTitles);
|
|
158826
|
-
if (!label) return;
|
|
158827
|
-
await renameGroup2(gid, label);
|
|
158828
|
-
bus.emit(EventNames.TASK_GROUPS_CHANGED, { group_id: gid, label }, ["web-ui", "main-agent"], { source: "fork" });
|
|
158829
|
-
log.session.info("fork group label refined", { groupId: gid, label });
|
|
158830
|
-
} catch (err2) {
|
|
158831
|
-
log.session.warn("fork group label refine failed", {
|
|
158832
|
-
groupId: gid,
|
|
158833
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
158834
|
-
});
|
|
158835
|
-
}
|
|
158836
|
-
})();
|
|
158837
|
-
}
|
|
158838
159232
|
} else {
|
|
158839
159233
|
try {
|
|
158840
159234
|
task = await getTask2(task_id);
|
|
@@ -158856,7 +159250,7 @@ async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
|
158856
159250
|
const FORK_FOCUS_PREFIX = "This is a forked session. Focus on the NEW request below \u2014 treat it as your primary task. Do not resume or continue the parent session's previous work unless the user explicitly asks you to.\n\n";
|
|
158857
159251
|
const forkMessage = `${FORK_FOCUS_PREFIX}${imageContext ?? ""}New request:
|
|
158858
159252
|
${userRequest}`;
|
|
158859
|
-
const forkSessionId =
|
|
159253
|
+
const forkSessionId = randomUUID11();
|
|
158860
159254
|
const forkTitle = title ?? `Fork of ${sourceRecord.title ?? sourceSessionId.slice(0, 16)}`;
|
|
158861
159255
|
try {
|
|
158862
159256
|
await createSessionRecord2(forkSessionId, task.id, task.project ?? "", sourceRecord.cwd, {
|
|
@@ -159579,7 +159973,7 @@ __export(session_rewind_exports, {
|
|
|
159579
159973
|
probeRewindViaDaemon: () => probeRewindViaDaemon,
|
|
159580
159974
|
rewindSessionToMessage: () => rewindSessionToMessage
|
|
159581
159975
|
});
|
|
159582
|
-
import { randomUUID as
|
|
159976
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
159583
159977
|
function cutAncestorHistoryAtRewindPoint(ancestorMessages, rewoundAtMessageUuid) {
|
|
159584
159978
|
if (!rewoundAtMessageUuid) {
|
|
159585
159979
|
return { messages: ancestorMessages, found: false, dropped: 0 };
|
|
@@ -159780,7 +160174,7 @@ async function rewindSessionToMessage(sessionId, input, source = "web-api") {
|
|
|
159780
160174
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
159781
160175
|
});
|
|
159782
160176
|
}
|
|
159783
|
-
const rewoundId =
|
|
160177
|
+
const rewoundId = randomUUID12();
|
|
159784
160178
|
const baseTitle = (record.title ?? sessionId.slice(0, 16)).replace(/\s*\(rewound\)$/i, "");
|
|
159785
160179
|
const title = `${baseTitle} (rewound)`.slice(0, 200);
|
|
159786
160180
|
const { createSessionRecord: createSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
@@ -160500,6 +160894,7 @@ function parseSessionMessages(content, opts) {
|
|
|
160500
160894
|
}
|
|
160501
160895
|
}
|
|
160502
160896
|
let lastPlanContent = null;
|
|
160897
|
+
let dropWarmupReply = false;
|
|
160503
160898
|
const resultParentIds = [];
|
|
160504
160899
|
const result = [];
|
|
160505
160900
|
for (const [mapMsgId, msg] of messageMap) {
|
|
@@ -160577,10 +160972,18 @@ ${imgPath}` : imgPath;
|
|
|
160577
160972
|
let text = textParts.join("\n").trim();
|
|
160578
160973
|
if (!text && tools2.length === 0 && !thinking) continue;
|
|
160579
160974
|
if (msg.role === "user" && text.startsWith("<")) {
|
|
160975
|
+
if (isCacheWarmupText(text)) {
|
|
160976
|
+
dropWarmupReply = true;
|
|
160977
|
+
continue;
|
|
160978
|
+
}
|
|
160580
160979
|
const transformed = transformInjectedUserText(text);
|
|
160581
160980
|
if (transformed === null) continue;
|
|
160582
160981
|
if (transformed !== void 0) text = transformed;
|
|
160583
160982
|
}
|
|
160983
|
+
if (msg.role === "assistant" && dropWarmupReply) {
|
|
160984
|
+
dropWarmupReply = false;
|
|
160985
|
+
if (tools2.length === 0) continue;
|
|
160986
|
+
}
|
|
160584
160987
|
if (msg.role === "user") text = stripOutputModeWrappers(text);
|
|
160585
160988
|
if (msg.role === "assistant" && !text && tools2.length === 0) continue;
|
|
160586
160989
|
result.push({
|
|
@@ -161531,6 +161934,7 @@ var init_session_history = __esm({
|
|
|
161531
161934
|
init_session_io();
|
|
161532
161935
|
init_constants();
|
|
161533
161936
|
init_remote_image_mirror();
|
|
161937
|
+
init_side_thread_warmup();
|
|
161534
161938
|
init_session_file_reader();
|
|
161535
161939
|
LOCAL_HOME = os36.homedir();
|
|
161536
161940
|
TAIL_SEGMENT_ROLL_BYTES = 4 * 1024 * 1024;
|
|
@@ -161678,7 +162082,7 @@ var init_context_window = __esm({
|
|
|
161678
162082
|
});
|
|
161679
162083
|
|
|
161680
162084
|
// src/providers/turn-ledger.ts
|
|
161681
|
-
import { randomUUID as
|
|
162085
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
161682
162086
|
function openTurn(sessionId) {
|
|
161683
162087
|
const existing = openTurns.get(sessionId);
|
|
161684
162088
|
if (existing && !existing.settled) {
|
|
@@ -161688,7 +162092,7 @@ function openTurn(sessionId) {
|
|
|
161688
162092
|
});
|
|
161689
162093
|
return { turnId: existing.turnId, promise: existing.promise };
|
|
161690
162094
|
}
|
|
161691
|
-
const turnId =
|
|
162095
|
+
const turnId = randomUUID13();
|
|
161692
162096
|
let resolve;
|
|
161693
162097
|
let reject;
|
|
161694
162098
|
const promise = new Promise((res, rej) => {
|
|
@@ -168436,11 +168840,39 @@ var init_claude_code_session = __esm({
|
|
|
168436
168840
|
session2.pendingDescription = message.slice(0, 500);
|
|
168437
168841
|
let appendSystemPrompt;
|
|
168438
168842
|
const isFork = !!data.forkedFromSessionId;
|
|
168439
|
-
|
|
168440
|
-
|
|
168441
|
-
|
|
168442
|
-
|
|
168443
|
-
|
|
168843
|
+
const { parseSideLaneKey: parseSideLaneKey2 } = await Promise.resolve().then(() => (init_side_thread_fork(), side_thread_fork_exports));
|
|
168844
|
+
const sideIds = parseSideLaneKey2(data.lane);
|
|
168845
|
+
if (sideIds) {
|
|
168846
|
+
let exact = data.appendSystemPromptExact;
|
|
168847
|
+
let source = "live-or-record";
|
|
168848
|
+
if (exact === void 0) {
|
|
168849
|
+
try {
|
|
168850
|
+
const { getSessionByClaudeId: getSessionByClaudeId2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
168851
|
+
const { spawnPrefixFromRecord: spawnPrefixFromRecord2 } = await Promise.resolve().then(() => (init_spawn_prefix(), spawn_prefix_exports));
|
|
168852
|
+
const parentRecord = await getSessionByClaudeId2(sideIds.parentSid);
|
|
168853
|
+
const prefix = parentRecord ? spawnPrefixFromRecord2(parentRecord) : null;
|
|
168854
|
+
exact = prefix?.appendSystemPrompt ?? null;
|
|
168855
|
+
source = prefix?.source ?? "no-parent-record";
|
|
168856
|
+
} catch (err2) {
|
|
168857
|
+
exact = null;
|
|
168858
|
+
source = "lookup-failed";
|
|
168859
|
+
log.session.warn("side thread: parent prompt lookup failed \u2014 spawning without an append prompt", {
|
|
168860
|
+
parentSid: sideIds.parentSid,
|
|
168861
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
168862
|
+
});
|
|
168863
|
+
}
|
|
168864
|
+
}
|
|
168865
|
+
appendSystemPrompt = exact ?? void 0;
|
|
168866
|
+
log.session.info("side thread: prefix prompt resolved", {
|
|
168867
|
+
parentSid: sideIds.parentSid,
|
|
168868
|
+
source,
|
|
168869
|
+
promptLength: appendSystemPrompt?.length ?? 0
|
|
168870
|
+
});
|
|
168871
|
+
} else {
|
|
168872
|
+
if (data.appendSystemPrompt && !isFork) {
|
|
168873
|
+
appendSystemPrompt = data.appendSystemPrompt;
|
|
168874
|
+
log.session.info("using caller-provided system prompt", { taskId: taskId2, promptLength: data.appendSystemPrompt.length });
|
|
168875
|
+
}
|
|
168444
168876
|
try {
|
|
168445
168877
|
const { buildSessionContext: buildSessionContext2 } = await Promise.resolve().then(() => (init_session_context(), session_context_exports));
|
|
168446
168878
|
const ctx = await buildSessionContext2(taskId2 ?? "", cwd, data.host);
|
|
@@ -168458,32 +168890,6 @@ ${ctx.systemPrompt}` : ctx.systemPrompt;
|
|
|
168458
168890
|
log.session.warn("failed to build session context", { taskId: taskId2, error: err2 instanceof Error ? err2.message : String(err2) });
|
|
168459
168891
|
}
|
|
168460
168892
|
}
|
|
168461
|
-
{
|
|
168462
|
-
const { parseSideLaneKey: parseSideLaneKey2 } = await Promise.resolve().then(() => (init_side_thread_fork(), side_thread_fork_exports));
|
|
168463
|
-
const sideIds = parseSideLaneKey2(data.lane);
|
|
168464
|
-
if (sideIds) {
|
|
168465
|
-
try {
|
|
168466
|
-
const { getSessionByClaudeId: getSessionByClaudeId2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
168467
|
-
const parentRecord = await getSessionByClaudeId2(sideIds.parentSid);
|
|
168468
|
-
const inherited = parentRecord?.appliedAppendSystemPrompt;
|
|
168469
|
-
if (inherited && Buffer.byteLength(inherited, "utf8") <= 65536) {
|
|
168470
|
-
appendSystemPrompt = inherited;
|
|
168471
|
-
log.session.info("side thread: inherited parent system prompt verbatim", {
|
|
168472
|
-
parentSid: sideIds.parentSid,
|
|
168473
|
-
promptLength: appendSystemPrompt.length
|
|
168474
|
-
});
|
|
168475
|
-
} else {
|
|
168476
|
-
log.session.info("side thread: parent has no stored system prompt \u2014 fresh build (cache prefix will diverge)", {
|
|
168477
|
-
parentSid: sideIds.parentSid
|
|
168478
|
-
});
|
|
168479
|
-
}
|
|
168480
|
-
} catch (err2) {
|
|
168481
|
-
log.session.warn("side thread: parent prompt lookup failed \u2014 fresh build", {
|
|
168482
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
168483
|
-
});
|
|
168484
|
-
}
|
|
168485
|
-
}
|
|
168486
|
-
}
|
|
168487
168893
|
if (data.preassignedSessionId && appendSystemPrompt && Buffer.byteLength(appendSystemPrompt, "utf8") <= 65536) {
|
|
168488
168894
|
const promptToStore = appendSystemPrompt;
|
|
168489
168895
|
const sidToStore = data.preassignedSessionId;
|
|
@@ -177482,7 +177888,7 @@ __export(mobile_launch_exports, {
|
|
|
177482
177888
|
performMobileLaunch: () => performMobileLaunch,
|
|
177483
177889
|
validateMobileLaunchBody: () => validateMobileLaunchBody
|
|
177484
177890
|
});
|
|
177485
|
-
import { randomUUID as
|
|
177891
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
177486
177892
|
function launchErrorCode(status2) {
|
|
177487
177893
|
if (status2 === 404) return "not_found";
|
|
177488
177894
|
if (status2 === 409) return "conflict";
|
|
@@ -177604,7 +178010,7 @@ async function performMobileLaunch(input, source) {
|
|
|
177604
178010
|
);
|
|
177605
178011
|
}
|
|
177606
178012
|
}
|
|
177607
|
-
const preassignedSessionId = engineCaps(input.engine).idProvisioning === "provider-issued" ? void 0 :
|
|
178013
|
+
const preassignedSessionId = engineCaps(input.engine).idProvisioning === "provider-issued" ? void 0 : randomUUID14();
|
|
177608
178014
|
const task = await quickStartSession({
|
|
177609
178015
|
message: input.message,
|
|
177610
178016
|
cwd: input.cwd,
|
|
@@ -178031,6 +178437,7 @@ __export(daemon_connection_exports, {
|
|
|
178031
178437
|
isDaemonConnected: () => isDaemonConnected,
|
|
178032
178438
|
isPooledConnection: () => isPooledConnection,
|
|
178033
178439
|
probeDaemonSession: () => probeDaemonSession,
|
|
178440
|
+
probeDaemonSessionArgs: () => probeDaemonSessionArgs,
|
|
178034
178441
|
pushDaemonHooksToAllHosts: () => pushDaemonHooksToAllHosts,
|
|
178035
178442
|
setOnDaemonHostConnected: () => setOnDaemonHostConnected,
|
|
178036
178443
|
setOnDaemonStatusChange: () => setOnDaemonStatusChange,
|
|
@@ -178207,6 +178614,28 @@ async function probeDaemonSession(hostKey2, sessionId) {
|
|
|
178207
178614
|
return null;
|
|
178208
178615
|
}
|
|
178209
178616
|
}
|
|
178617
|
+
async function probeDaemonSessionArgs(hostKey2, sessionId, deadlineMs = 2e3) {
|
|
178618
|
+
const conn = getConnectedDaemonConnection(hostKey2);
|
|
178619
|
+
if (!conn) return null;
|
|
178620
|
+
try {
|
|
178621
|
+
const result = await Promise.race([
|
|
178622
|
+
conn.send("status", { sid: sessionId, includeArgs: true }),
|
|
178623
|
+
new Promise((_, reject) => setTimeout(
|
|
178624
|
+
() => reject(new Error(`status probe exceeded ${deadlineMs}ms`)),
|
|
178625
|
+
deadlineMs
|
|
178626
|
+
).unref?.())
|
|
178627
|
+
]);
|
|
178628
|
+
if (!result.ok || !result.alive || !Array.isArray(result.args)) return null;
|
|
178629
|
+
return result.args.filter((a8) => typeof a8 === "string");
|
|
178630
|
+
} catch (err2) {
|
|
178631
|
+
log.session.debug("probeDaemonSessionArgs: status probe failed", {
|
|
178632
|
+
hostKey: hostKey2,
|
|
178633
|
+
sessionId,
|
|
178634
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
178635
|
+
});
|
|
178636
|
+
return null;
|
|
178637
|
+
}
|
|
178638
|
+
}
|
|
178210
178639
|
var execFileAsync23, MOBILE_ENQUEUE_LEDGER_MAX, recentMobileEnqueues, DaemonConnection, onPoolStatusChange, onHostConnected, hostConnectedListeners, connectionPool, connectingPromises, failureCache, FAILURE_CACHE_TTL_MS;
|
|
178211
178640
|
var init_daemon_connection = __esm({
|
|
178212
178641
|
"src/providers/daemon-connection.ts"() {
|
|
@@ -182774,6 +183203,7 @@ async function createSessionRecord(claudeSessionId, taskId2, project, cwd, extra
|
|
|
182774
183203
|
...extra?.effort ? { effort: extra.effort } : {},
|
|
182775
183204
|
...extra?.profile ? { profile: extra.profile } : {},
|
|
182776
183205
|
...extra?.lane ? { lane: extra.lane } : {},
|
|
183206
|
+
...extra?.appliedAppendSystemPrompt !== void 0 ? { appliedAppendSystemPrompt: extra.appliedAppendSystemPrompt } : {},
|
|
182777
183207
|
...extra?.engine ? { engine: extra.engine } : {},
|
|
182778
183208
|
...extra?.acpRuntimeId ? { acpRuntimeId: extra.acpRuntimeId } : {},
|
|
182779
183209
|
...extra?.acpJournalPath ? { acpJournalPath: extra.acpJournalPath } : {},
|
|
@@ -182928,7 +183358,7 @@ function applyUpdateToSession(session2, updates, logLabel) {
|
|
|
182928
183358
|
};
|
|
182929
183359
|
if (count2 === 1) log.session.info("legacy status write suppressed", payload);
|
|
182930
183360
|
else log.session.debug("legacy status write suppressed", payload);
|
|
182931
|
-
if (updates.process_status === "error") {
|
|
183361
|
+
if (updates.process_status === "error" || updates.process_status === "stopped") {
|
|
182932
183362
|
noteSuppressedErrorReason(session2.claudeSessionId, {
|
|
182933
183363
|
reason: typeof reason === "string" ? reason : "unknown",
|
|
182934
183364
|
message: typeof updates.errorMessage === "string" ? updates.errorMessage : void 0,
|