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/web/server.js
CHANGED
|
@@ -10199,24 +10199,25 @@ function reapSession(sid, code, reason) {
|
|
|
10199
10199
|
let jsonlAgeMs = null;
|
|
10200
10200
|
try { jsonlAgeMs = Date.now() - fs.statSync(session.jsonlPath).mtimeMs; } catch {}
|
|
10201
10201
|
const cleanExit = isTurnCompleteExit(session.jsonlPath);
|
|
10202
|
-
//
|
|
10203
|
-
//
|
|
10204
|
-
// before
|
|
10205
|
-
// it (usually orphan-poll ESRCH \u2192 code -1). Verified
|
|
10206
|
-
// line was a control_response (the reply to a
|
|
10207
|
-
// a routine shape), so isTurnCompleteExit said
|
|
10208
|
-
// the projection painted a red "Error" over a
|
|
10209
|
-
//
|
|
10210
|
-
// information the tail does not contain,
|
|
10211
|
-
//
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10202
|
+
// A DELIBERATE stop is not a failure, and the JSONL tail cannot tell us it
|
|
10203
|
+
// happened. Both writers (our idle scanner, and any stop RPC via cmdStop)
|
|
10204
|
+
// stamp intentionalStopAt before signalling; the death then lands here through
|
|
10205
|
+
// whichever path notices it (usually orphan-poll ESRCH \u2192 code -1). Verified
|
|
10206
|
+
// production case: the last line was a control_response (the reply to a
|
|
10207
|
+
// Walnut-issued control request \u2014 a routine shape), so isTurnCompleteExit said
|
|
10208
|
+
// false, exitCode stayed -1, and the projection painted a red "Error" over a
|
|
10209
|
+
// healthy, fully resumable session that was stopped on purpose. Checked FIRST
|
|
10210
|
+
// and unconditionally: the flag carries information the tail does not contain,
|
|
10211
|
+
// which is why isTurnCompleteExit is left alone.
|
|
10212
|
+
// Keep in sync with daemon-core.ts reapSession.
|
|
10213
|
+
const intentionalStop = session.intentionalStopAt != null;
|
|
10214
|
+
if (intentionalStop && code !== 0) {
|
|
10215
|
+
logMsg('info', 'reapSession: intentional stop, normalizing exit code', {
|
|
10215
10216
|
sid, pid: session.pid, originalCode: code, originalReason: reason,
|
|
10216
|
-
jsonlAgeMs, cleanExit,
|
|
10217
|
+
jsonlAgeMs, cleanExit, intentionalStopAt: session.intentionalStopAt,
|
|
10217
10218
|
});
|
|
10218
10219
|
code = 0;
|
|
10219
|
-
reason = reason + '+intentional-
|
|
10220
|
+
reason = reason + '+intentional-stop';
|
|
10220
10221
|
} else if (code !== 0 && cleanExit) {
|
|
10221
10222
|
logMsg('info', 'reapSession: turn-complete detected, normalizing exit code', {
|
|
10222
10223
|
sid, pid: session.pid, originalCode: code, originalReason: reason, jsonlAgeMs,
|
|
@@ -13870,6 +13871,15 @@ function cmdStop(ws, id, cmd) {
|
|
|
13870
13871
|
const pid = session.pid;
|
|
13871
13872
|
logMsg('info', 'cmdStop: stopping session (process group kill)', { sid, pid });
|
|
13872
13873
|
|
|
13874
|
+
// Nobody sends stop by accident, so record the intent BEFORE the first signal:
|
|
13875
|
+
// the death lands asynchronously (orphan poll \u2192 ESRCH \u2192 code -1) and by then
|
|
13876
|
+
// nothing remembers this was asked for. This path is how the SERVER's idle
|
|
13877
|
+
// reaper kills (mgr.kill() \u2192 stop RPC), not the daemon's own scan, so it needs
|
|
13878
|
+
// its own stamp: a LOCAL session auto-stopped after 2h idle surfaced as
|
|
13879
|
+
// "Session ended unexpectedly and no cause was recorded" (2026-09-03).
|
|
13880
|
+
// Keep in sync with daemon-standalone.ts cmdStop.
|
|
13881
|
+
session.intentionalStopAt = Date.now();
|
|
13882
|
+
|
|
13873
13883
|
// An explicit stop is a human decision \u2014 never undone by a pending auto-retry
|
|
13874
13884
|
// respawning the CLI minutes later. Cancel the timer AND clear the streak so
|
|
13875
13885
|
// an in-flight result line can't re-arm one either.
|
|
@@ -13906,7 +13916,7 @@ function cmdStop(ws, id, cmd) {
|
|
|
13906
13916
|
|
|
13907
13917
|
// \u2500\u2500 Status \u2500\u2500
|
|
13908
13918
|
function cmdStatus(ws, id, cmd) {
|
|
13909
|
-
const { sid } = cmd;
|
|
13919
|
+
const { sid, includeArgs } = cmd;
|
|
13910
13920
|
if (!sid) return sendError(ws, id, 'status: missing sid');
|
|
13911
13921
|
|
|
13912
13922
|
const session = sessions.get(sid);
|
|
@@ -13937,6 +13947,11 @@ function cmdStatus(ws, id, cmd) {
|
|
|
13937
13947
|
exitCode: session.exitCode,
|
|
13938
13948
|
exitReason: session.exitReason,
|
|
13939
13949
|
pendingCtrl: session.pendingCtrl,
|
|
13950
|
+
// Opt-in: the argv the live process was launched with (a fork copies it to
|
|
13951
|
+
// reproduce the parent's prompt-cache prefix). Off by default because the
|
|
13952
|
+
// health monitor polls status for every session and the argv carries the
|
|
13953
|
+
// multi-KB --append-system-prompt.
|
|
13954
|
+
...(includeArgs && session.args && session.args.length > 0 ? { args: session.args } : {}),
|
|
13940
13955
|
});
|
|
13941
13956
|
}
|
|
13942
13957
|
|
|
@@ -15989,7 +16004,7 @@ function scanIdleSessions() {
|
|
|
15989
16004
|
// end" for any session whose last line isn't a type:result \u2014 and the
|
|
15990
16005
|
// projection then shows a red Error for our own routine reclamation.
|
|
15991
16006
|
// Keep in sync with daemon-standalone.ts scanIdleSessions.
|
|
15992
|
-
session.
|
|
16007
|
+
session.intentionalStopAt = now;
|
|
15993
16008
|
killSessionProcessGroup(pid, sid);
|
|
15994
16009
|
}
|
|
15995
16010
|
}
|
|
@@ -16673,19 +16688,19 @@ function createDaemonCore(deps) {
|
|
|
16673
16688
|
jsonlAgeMs = clock() - fs114.statSync(session2.jsonlPath).mtimeMs;
|
|
16674
16689
|
} catch {
|
|
16675
16690
|
}
|
|
16676
|
-
const
|
|
16677
|
-
if (
|
|
16678
|
-
logger2("info", "reapSession: intentional
|
|
16691
|
+
const intentionalStop = session2.intentionalStopAt != null;
|
|
16692
|
+
if (intentionalStop && code !== 0) {
|
|
16693
|
+
logger2("info", "reapSession: intentional stop, normalizing exit code", {
|
|
16679
16694
|
sid,
|
|
16680
16695
|
pid: session2.pid,
|
|
16681
16696
|
originalCode: code,
|
|
16682
16697
|
originalReason: reason,
|
|
16683
16698
|
jsonlAgeMs,
|
|
16684
16699
|
cleanExit,
|
|
16685
|
-
|
|
16700
|
+
intentionalStopAt: session2.intentionalStopAt
|
|
16686
16701
|
});
|
|
16687
16702
|
code = 0;
|
|
16688
|
-
reason = reason + "+intentional-
|
|
16703
|
+
reason = reason + "+intentional-stop";
|
|
16689
16704
|
} else if (cleanExit && code !== 0) {
|
|
16690
16705
|
logger2("info", "reapSession: turn-complete detected, normalizing exit code", {
|
|
16691
16706
|
sid,
|
|
@@ -17476,6 +17491,7 @@ var init_daemon_core = __esm({
|
|
|
17476
17491
|
var claude_cli_detect_exports = {};
|
|
17477
17492
|
__export(claude_cli_detect_exports, {
|
|
17478
17493
|
_CLAUDE_CREDENTIALS_HOME: () => _CLAUDE_CREDENTIALS_HOME,
|
|
17494
|
+
describeClaudeCliAuth: () => describeClaudeCliAuth,
|
|
17479
17495
|
detectClaudeCli: () => detectClaudeCli,
|
|
17480
17496
|
hasClaudeSubscriptionAuth: () => hasClaudeSubscriptionAuth,
|
|
17481
17497
|
isClaudeCliInstalled: () => isClaudeCliInstalled,
|
|
@@ -17536,9 +17552,32 @@ function detectClaudeCli() {
|
|
|
17536
17552
|
return {
|
|
17537
17553
|
installed: installed2,
|
|
17538
17554
|
subscriptionAuth,
|
|
17539
|
-
subscriptionReady: installed2 && subscriptionAuth
|
|
17555
|
+
subscriptionReady: installed2 && subscriptionAuth,
|
|
17556
|
+
ready: installed2,
|
|
17557
|
+
auth: installed2 ? describeClaudeCliAuth() : { mode: "unknown", label: "not installed" }
|
|
17540
17558
|
};
|
|
17541
17559
|
}
|
|
17560
|
+
function describeClaudeCliAuth(env3 = process.env, settingsEnv = readClaudeSettingsEnv()) {
|
|
17561
|
+
const merged = { ...env3, ...settingsEnv };
|
|
17562
|
+
const truthy = (v3) => !!v3 && v3 !== "0" && v3.toLowerCase() !== "false";
|
|
17563
|
+
if (truthy(merged.CLAUDE_CODE_USE_BEDROCK)) {
|
|
17564
|
+
const region = merged.AWS_REGION || merged.AWS_DEFAULT_REGION;
|
|
17565
|
+
return { mode: "bedrock", label: region ? `Bedrock (${region})` : "Bedrock" };
|
|
17566
|
+
}
|
|
17567
|
+
if (truthy(merged.CLAUDE_CODE_USE_VERTEX)) return { mode: "vertex", label: "Vertex AI" };
|
|
17568
|
+
if (merged.ANTHROPIC_API_KEY || merged.ANTHROPIC_AUTH_TOKEN) return { mode: "api-key", label: "an Anthropic API key" };
|
|
17569
|
+
if (hasClaudeSubscriptionAuth()) return { mode: "subscription", label: "your Claude subscription" };
|
|
17570
|
+
return { mode: "unknown", label: "its own login" };
|
|
17571
|
+
}
|
|
17572
|
+
function readClaudeSettingsEnv() {
|
|
17573
|
+
try {
|
|
17574
|
+
const raw = fs17.readFileSync(path18.join(os4.homedir(), ".claude", "settings.json"), "utf8");
|
|
17575
|
+
const env3 = JSON.parse(raw).env ?? {};
|
|
17576
|
+
return Object.fromEntries(Object.entries(env3).filter(([, v3]) => typeof v3 === "string"));
|
|
17577
|
+
} catch {
|
|
17578
|
+
return {};
|
|
17579
|
+
}
|
|
17580
|
+
}
|
|
17542
17581
|
var KEYCHAIN_OAUTH_SERVICE, _CLAUDE_CREDENTIALS_HOME;
|
|
17543
17582
|
var init_claude_cli_detect = __esm({
|
|
17544
17583
|
"src/core/claude-cli-detect.ts"() {
|
|
@@ -24728,7 +24767,7 @@ __export(credential_resolver_exports, {
|
|
|
24728
24767
|
DEFAULT_REGION: () => DEFAULT_REGION,
|
|
24729
24768
|
hasResolvableCredential: () => hasResolvableCredential,
|
|
24730
24769
|
readClaudeCredentialExport: () => readClaudeCredentialExport,
|
|
24731
|
-
readClaudeSettingsEnv: () =>
|
|
24770
|
+
readClaudeSettingsEnv: () => readClaudeSettingsEnv2,
|
|
24732
24771
|
resolveCredentialFrom: () => resolveCredentialFrom,
|
|
24733
24772
|
resolveCredentials: () => resolveCredentials,
|
|
24734
24773
|
traceCredentialResolution: () => traceCredentialResolution
|
|
@@ -24883,7 +24922,7 @@ function traceCredentialResolution(inputs) {
|
|
|
24883
24922
|
region: { value: winner.region ?? DEFAULT_REGION, source: regionSource }
|
|
24884
24923
|
};
|
|
24885
24924
|
}
|
|
24886
|
-
function
|
|
24925
|
+
function readClaudeSettingsEnv2(settingsFile = CLAUDE_SETTINGS_FILE) {
|
|
24887
24926
|
try {
|
|
24888
24927
|
const raw = fs31.readFileSync(settingsFile, "utf-8");
|
|
24889
24928
|
const parsed = JSON.parse(raw);
|
|
@@ -24923,7 +24962,7 @@ function probeAwsFiles() {
|
|
|
24923
24962
|
function resolveCredentials(config) {
|
|
24924
24963
|
const result = resolveCredentialFrom({
|
|
24925
24964
|
config,
|
|
24926
|
-
claudeEnv:
|
|
24965
|
+
claudeEnv: readClaudeSettingsEnv2(),
|
|
24927
24966
|
processEnv: process.env,
|
|
24928
24967
|
awsFiles: probeAwsFiles(),
|
|
24929
24968
|
claudeCredentialExport: readClaudeCredentialExport()
|
|
@@ -67705,10 +67744,9 @@ function buildArgs(opts) {
|
|
|
67705
67744
|
// Input stays default (text): we write the whole prompt to stdin and close.
|
|
67706
67745
|
// (stream-json INPUT would force us into a JSON envelope for no gain here.)
|
|
67707
67746
|
"--tools",
|
|
67708
|
-
""
|
|
67747
|
+
""
|
|
67709
67748
|
// disable ALL built-in tools (deny-all)
|
|
67710
|
-
|
|
67711
|
-
FORCE_SUBSCRIPTION_SETTINGS
|
|
67749
|
+
// No --settings override: the user's settings.json (and so their auth) applies as-is.
|
|
67712
67750
|
];
|
|
67713
67751
|
let systemText = systemToText(opts.system).trim();
|
|
67714
67752
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -67737,7 +67775,7 @@ function systemToText(system) {
|
|
|
67737
67775
|
}
|
|
67738
67776
|
function buildSpawnEnv(base = process.env) {
|
|
67739
67777
|
const env3 = { ...base };
|
|
67740
|
-
for (const key2 of
|
|
67778
|
+
for (const key2 of ENV_TO_STRIP) {
|
|
67741
67779
|
delete env3[key2];
|
|
67742
67780
|
}
|
|
67743
67781
|
env3.CLAUDE_CODE_ENTRYPOINT = WALNUT_UTILITY_ENTRYPOINT;
|
|
@@ -67799,7 +67837,7 @@ function normalizeCliModel(model) {
|
|
|
67799
67837
|
}
|
|
67800
67838
|
return void 0;
|
|
67801
67839
|
}
|
|
67802
|
-
var
|
|
67840
|
+
var ENV_TO_STRIP, MAX_CONCURRENT_CLI, CLI_TURN_TIMEOUT_MS, SESSION_REUSE_TTL_MS, MAX_TRACKED_SESSIONS, ClaudeCliAdapter;
|
|
67803
67841
|
var init_adapter_claude_cli = __esm({
|
|
67804
67842
|
"src/agent/providers/adapter-claude-cli.ts"() {
|
|
67805
67843
|
"use strict";
|
|
@@ -67809,24 +67847,11 @@ var init_adapter_claude_cli = __esm({
|
|
|
67809
67847
|
init_logging();
|
|
67810
67848
|
init_claude_cli_detect();
|
|
67811
67849
|
init_claude_cli_protocol();
|
|
67812
|
-
|
|
67813
|
-
|
|
67814
|
-
|
|
67815
|
-
|
|
67816
|
-
|
|
67817
|
-
"AWS_ACCESS_KEY_ID",
|
|
67818
|
-
"AWS_SECRET_ACCESS_KEY",
|
|
67819
|
-
"AWS_SESSION_TOKEN",
|
|
67820
|
-
"AWS_PROFILE",
|
|
67821
|
-
"CLAUDE_CODE_USE_BEDROCK",
|
|
67822
|
-
"CLAUDE_CODE_USE_VERTEX",
|
|
67823
|
-
// Nested-session marker — inline-subagent strips this too so the child doesn't
|
|
67824
|
-
// think it's running inside another Claude session.
|
|
67825
|
-
"CLAUDECODE"
|
|
67826
|
-
];
|
|
67827
|
-
FORCE_SUBSCRIPTION_SETTINGS = JSON.stringify({
|
|
67828
|
-
env: { CLAUDE_CODE_USE_BEDROCK: "", CLAUDE_CODE_USE_VERTEX: "" }
|
|
67829
|
-
});
|
|
67850
|
+
ENV_TO_STRIP = ["CLAUDECODE"];
|
|
67851
|
+
MAX_CONCURRENT_CLI = (() => {
|
|
67852
|
+
const n6 = Number(process.env.WALNUT_CLAUDE_CLI_CONCURRENCY);
|
|
67853
|
+
return Number.isFinite(n6) && n6 > 0 ? Math.floor(n6) : 3;
|
|
67854
|
+
})();
|
|
67830
67855
|
CLI_TURN_TIMEOUT_MS = 12e4;
|
|
67831
67856
|
SESSION_REUSE_TTL_MS = 30 * 60 * 1e3;
|
|
67832
67857
|
MAX_TRACKED_SESSIONS = 50;
|
|
@@ -67834,6 +67859,25 @@ var init_adapter_claude_cli = __esm({
|
|
|
67834
67859
|
protocol = "claude-cli";
|
|
67835
67860
|
/** conversationKey → CLI session for --resume chaining. In-process only. */
|
|
67836
67861
|
sessions = /* @__PURE__ */ new Map();
|
|
67862
|
+
/** Process gate: MAX_CONCURRENT_CLI turns at once, the rest queue in order. */
|
|
67863
|
+
inFlight = 0;
|
|
67864
|
+
waiters = [];
|
|
67865
|
+
async withSlot(fn) {
|
|
67866
|
+
if (this.inFlight >= MAX_CONCURRENT_CLI) {
|
|
67867
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
67868
|
+
}
|
|
67869
|
+
this.inFlight++;
|
|
67870
|
+
try {
|
|
67871
|
+
return await fn();
|
|
67872
|
+
} finally {
|
|
67873
|
+
this.inFlight--;
|
|
67874
|
+
this.waiters.shift()?.();
|
|
67875
|
+
}
|
|
67876
|
+
}
|
|
67877
|
+
/** Test hook: how many turns are running / waiting right now. */
|
|
67878
|
+
_gateStateForTesting() {
|
|
67879
|
+
return { inFlight: this.inFlight, waiting: this.waiters.length };
|
|
67880
|
+
}
|
|
67837
67881
|
resetClient() {
|
|
67838
67882
|
this.sessions.clear();
|
|
67839
67883
|
}
|
|
@@ -67911,7 +67955,7 @@ User: ${overridePrompt}` : "");
|
|
|
67911
67955
|
}
|
|
67912
67956
|
if (!prompt.trim()) prompt = "(continue)";
|
|
67913
67957
|
const args = [...buildArgs(opts), session2.flag, session2.id];
|
|
67914
|
-
return this.execCli(command6, args, env3, prompt, opts.signal, onTextDelta).then((result) => {
|
|
67958
|
+
return this.withSlot(() => this.execCli(command6, args, env3, prompt, opts.signal, onTextDelta)).then((result) => {
|
|
67915
67959
|
this.sessions.set(key2, {
|
|
67916
67960
|
sessionId: session2.id,
|
|
67917
67961
|
seenMessages: opts.messages.length,
|
|
@@ -67930,7 +67974,7 @@ User: ${overridePrompt}` : "");
|
|
|
67930
67974
|
User: ${overridePrompt}` : "");
|
|
67931
67975
|
const freshId = randomUUID3();
|
|
67932
67976
|
const freshArgs = [...buildArgs(opts), "--session-id", freshId];
|
|
67933
|
-
return this.execCli(command6, freshArgs, env3, freshPrompt || "(continue)", opts.signal, onTextDelta).then((result) => {
|
|
67977
|
+
return this.withSlot(() => this.execCli(command6, freshArgs, env3, freshPrompt || "(continue)", opts.signal, onTextDelta)).then((result) => {
|
|
67934
67978
|
this.sessions.set(key2, {
|
|
67935
67979
|
sessionId: freshId,
|
|
67936
67980
|
seenMessages: opts.messages.length,
|
|
@@ -68014,7 +68058,7 @@ User: ${overridePrompt}` : "");
|
|
|
68014
68058
|
}
|
|
68015
68059
|
const resultText = finalResult?.result || accumulatedText;
|
|
68016
68060
|
if (resultText && resultText.length < 300 && /^\s*failed to authenticate|^\s*api error: 4\d\d/i.test(resultText)) {
|
|
68017
|
-
reject(new Error(`claude-cli
|
|
68061
|
+
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)}`));
|
|
68018
68062
|
return;
|
|
68019
68063
|
}
|
|
68020
68064
|
if (code !== 0 && !accumulatedText && !finalResult) {
|
|
@@ -68214,6 +68258,50 @@ var init_registry = __esm({
|
|
|
68214
68258
|
}
|
|
68215
68259
|
});
|
|
68216
68260
|
|
|
68261
|
+
// src/agent/providers/default-provider.ts
|
|
68262
|
+
var default_provider_exports = {};
|
|
68263
|
+
__export(default_provider_exports, {
|
|
68264
|
+
CLAUDE_CLI_PROVIDER: () => CLAUDE_CLI_PROVIDER,
|
|
68265
|
+
LEGACY_DEFAULT_PROVIDER: () => LEGACY_DEFAULT_PROVIDER,
|
|
68266
|
+
_resetDefaultProviderCacheForTesting: () => _resetDefaultProviderCacheForTesting,
|
|
68267
|
+
hasExplicitBedrockConfig: () => hasExplicitBedrockConfig,
|
|
68268
|
+
mainProviderIsImplicit: () => mainProviderIsImplicit,
|
|
68269
|
+
resolveMainProviderName: () => resolveMainProviderName
|
|
68270
|
+
});
|
|
68271
|
+
function claudeCliInstalledCached() {
|
|
68272
|
+
const now = Date.now();
|
|
68273
|
+
if (!cliProbe || now - cliProbe.at > CLI_PROBE_TTL_MS) {
|
|
68274
|
+
cliProbe = { at: now, installed: isClaudeCliInstalled() };
|
|
68275
|
+
}
|
|
68276
|
+
return cliProbe.installed;
|
|
68277
|
+
}
|
|
68278
|
+
function _resetDefaultProviderCacheForTesting() {
|
|
68279
|
+
cliProbe = void 0;
|
|
68280
|
+
}
|
|
68281
|
+
function hasExplicitBedrockConfig(config) {
|
|
68282
|
+
const b8 = config.providers?.bedrock;
|
|
68283
|
+
if (b8 && (b8.bearer_token || b8.aws_access_key_id || b8.aws_profile || b8.aws_credential_export)) return true;
|
|
68284
|
+
return !!config.provider?.bedrock_bearer_token;
|
|
68285
|
+
}
|
|
68286
|
+
function resolveMainProviderName(config, claudeInstalled = claudeCliInstalledCached()) {
|
|
68287
|
+
if (config.agent?.main_provider) return config.agent.main_provider;
|
|
68288
|
+
if (claudeInstalled) return CLAUDE_CLI_PROVIDER;
|
|
68289
|
+
return LEGACY_DEFAULT_PROVIDER;
|
|
68290
|
+
}
|
|
68291
|
+
function mainProviderIsImplicit(config) {
|
|
68292
|
+
return !config.agent?.main_provider;
|
|
68293
|
+
}
|
|
68294
|
+
var LEGACY_DEFAULT_PROVIDER, CLAUDE_CLI_PROVIDER, CLI_PROBE_TTL_MS, cliProbe;
|
|
68295
|
+
var init_default_provider = __esm({
|
|
68296
|
+
"src/agent/providers/default-provider.ts"() {
|
|
68297
|
+
"use strict";
|
|
68298
|
+
init_claude_cli_detect();
|
|
68299
|
+
LEGACY_DEFAULT_PROVIDER = "bedrock";
|
|
68300
|
+
CLAUDE_CLI_PROVIDER = "claude_cli";
|
|
68301
|
+
CLI_PROBE_TTL_MS = 6e4;
|
|
68302
|
+
}
|
|
68303
|
+
});
|
|
68304
|
+
|
|
68217
68305
|
// src/agent/model.ts
|
|
68218
68306
|
var model_exports = {};
|
|
68219
68307
|
__export(model_exports, {
|
|
@@ -68248,7 +68336,7 @@ function getContextThreshold(model, percent) {
|
|
|
68248
68336
|
}
|
|
68249
68337
|
async function resolveForCall(config) {
|
|
68250
68338
|
const fullConfig = await getConfig();
|
|
68251
|
-
const providerName = config?.provider ?? fullConfig
|
|
68339
|
+
const providerName = config?.provider ?? resolveMainProviderName(fullConfig);
|
|
68252
68340
|
const hasExplicitProviders = fullConfig.providers && Object.keys(fullConfig.providers).length > 0;
|
|
68253
68341
|
let providers = hasExplicitProviders ? buildProviderMap(fullConfig.providers, fullConfig) : synthesizeFromLegacy(fullConfig);
|
|
68254
68342
|
const resolved = resolveProvider(providerName, providers);
|
|
@@ -68318,6 +68406,7 @@ var init_model = __esm({
|
|
|
68318
68406
|
"use strict";
|
|
68319
68407
|
init_config_manager();
|
|
68320
68408
|
init_registry();
|
|
68409
|
+
init_default_provider();
|
|
68321
68410
|
init_defaults();
|
|
68322
68411
|
init_model_catalog();
|
|
68323
68412
|
}
|
|
@@ -69758,7 +69847,7 @@ __export(cheap_model_exports, {
|
|
|
69758
69847
|
});
|
|
69759
69848
|
function fastModelFor(config) {
|
|
69760
69849
|
if (config.agent?.fast_model) return config.agent.fast_model;
|
|
69761
|
-
const providerName = config
|
|
69850
|
+
const providerName = resolveMainProviderName(config);
|
|
69762
69851
|
return MODEL_CATALOG[providerName]?.find(
|
|
69763
69852
|
(model) => model.id.toLowerCase().includes("haiku")
|
|
69764
69853
|
)?.id;
|
|
@@ -69770,6 +69859,7 @@ var init_cheap_model = __esm({
|
|
|
69770
69859
|
"src/core/cheap-model.ts"() {
|
|
69771
69860
|
"use strict";
|
|
69772
69861
|
init_model_catalog();
|
|
69862
|
+
init_default_provider();
|
|
69773
69863
|
}
|
|
69774
69864
|
});
|
|
69775
69865
|
|
|
@@ -71589,7 +71679,20 @@ async function applySnapshot(sessionId, snapshot2, source) {
|
|
|
71589
71679
|
if (projected !== "error") {
|
|
71590
71680
|
updates.errorMessage = void 0;
|
|
71591
71681
|
updates.errorKind = void 0;
|
|
71592
|
-
|
|
71682
|
+
const stashedStop = projected === "stopped" ? takeSuppressedErrorReason(sessionId) : null;
|
|
71683
|
+
if (stashedStop?.message) {
|
|
71684
|
+
updates.errorMessage = stashedStop.message;
|
|
71685
|
+
if (stashedStop.kind) updates.errorKind = stashedStop.kind;
|
|
71686
|
+
log.session.info("snapshot projection adopted suppressed stop reason", {
|
|
71687
|
+
sessionId,
|
|
71688
|
+
suppressedReason: stashedStop.reason,
|
|
71689
|
+
kind: stashedStop.kind ?? null,
|
|
71690
|
+
projected,
|
|
71691
|
+
source
|
|
71692
|
+
});
|
|
71693
|
+
} else {
|
|
71694
|
+
clearSuppressedErrorReason(sessionId);
|
|
71695
|
+
}
|
|
71593
71696
|
} else {
|
|
71594
71697
|
let droppedDisproven = false;
|
|
71595
71698
|
if (assertsHostUnreachable(record)) {
|
|
@@ -75663,6 +75766,26 @@ var init_transcript_chain = __esm({
|
|
|
75663
75766
|
}
|
|
75664
75767
|
});
|
|
75665
75768
|
|
|
75769
|
+
// src/core/sessions/side-thread-warmup.ts
|
|
75770
|
+
function isCacheWarmupText(text) {
|
|
75771
|
+
return text.startsWith(CACHE_WARMUP_TAG);
|
|
75772
|
+
}
|
|
75773
|
+
function markWarmupTurnPending(sessionId) {
|
|
75774
|
+
pendingWarmupTurns.add(sessionId);
|
|
75775
|
+
}
|
|
75776
|
+
function consumeWarmupTurn(sessionId) {
|
|
75777
|
+
return pendingWarmupTurns.delete(sessionId);
|
|
75778
|
+
}
|
|
75779
|
+
var CACHE_WARMUP_TAG, CACHE_WARMUP_MESSAGE, pendingWarmupTurns;
|
|
75780
|
+
var init_side_thread_warmup = __esm({
|
|
75781
|
+
"src/core/sessions/side-thread-warmup.ts"() {
|
|
75782
|
+
"use strict";
|
|
75783
|
+
CACHE_WARMUP_TAG = "<walnut-cache-warmup>";
|
|
75784
|
+
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>`;
|
|
75785
|
+
pendingWarmupTurns = /* @__PURE__ */ new Set();
|
|
75786
|
+
}
|
|
75787
|
+
});
|
|
75788
|
+
|
|
75666
75789
|
// src/core/history-disk-cache.ts
|
|
75667
75790
|
var history_disk_cache_exports = {};
|
|
75668
75791
|
__export(history_disk_cache_exports, {
|
|
@@ -77823,6 +77946,7 @@ __export(session_lifecycle_exports, {
|
|
|
77823
77946
|
retrySession: () => retrySession,
|
|
77824
77947
|
terminateSession: () => terminateSession
|
|
77825
77948
|
});
|
|
77949
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
77826
77950
|
async function readProviderSessionHistory(sessionId, record, nativeHost, skipSubagents = true, opts) {
|
|
77827
77951
|
if (record && engineCaps(record.engine).historySource === "acp-journal") {
|
|
77828
77952
|
const { readAcpSessionHistoryState: readAcpSessionHistoryState2 } = await Promise.resolve().then(() => (init_acp_session_history(), acp_session_history_exports));
|
|
@@ -77933,6 +78057,33 @@ async function changeSessionMode(existingRecord, sessionId, mode, updates = {})
|
|
|
77933
78057
|
});
|
|
77934
78058
|
return updated;
|
|
77935
78059
|
}
|
|
78060
|
+
function normalizePinQuote(value) {
|
|
78061
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
78062
|
+
throw new SessionControlError("pinned_messages[].quote must be an object", 400);
|
|
78063
|
+
}
|
|
78064
|
+
const quote = value;
|
|
78065
|
+
const exact = quote.exact;
|
|
78066
|
+
if (typeof exact !== "string" || !exact.trim() || exact.length > MAX_PIN_QUOTE_CHARS) {
|
|
78067
|
+
throw new SessionControlError(
|
|
78068
|
+
`pinned_messages[].quote.exact must be a non-empty string (max ${MAX_PIN_QUOTE_CHARS} chars)`,
|
|
78069
|
+
400
|
|
78070
|
+
);
|
|
78071
|
+
}
|
|
78072
|
+
for (const side of ["prefix", "suffix"]) {
|
|
78073
|
+
const context = quote[side];
|
|
78074
|
+
if (context !== void 0 && context !== null && (typeof context !== "string" || context.length > MAX_PIN_QUOTE_CONTEXT_CHARS)) {
|
|
78075
|
+
throw new SessionControlError(
|
|
78076
|
+
`pinned_messages[].quote.${side} must be a string (max ${MAX_PIN_QUOTE_CONTEXT_CHARS} chars)`,
|
|
78077
|
+
400
|
|
78078
|
+
);
|
|
78079
|
+
}
|
|
78080
|
+
}
|
|
78081
|
+
return {
|
|
78082
|
+
exact,
|
|
78083
|
+
...typeof quote.prefix === "string" && quote.prefix ? { prefix: quote.prefix } : {},
|
|
78084
|
+
...typeof quote.suffix === "string" && quote.suffix ? { suffix: quote.suffix } : {}
|
|
78085
|
+
};
|
|
78086
|
+
}
|
|
77936
78087
|
function normalizePinnedMessages(value) {
|
|
77937
78088
|
if (!Array.isArray(value)) {
|
|
77938
78089
|
throw new SessionControlError("pinned_messages must be an array", 400);
|
|
@@ -77941,7 +78092,9 @@ function normalizePinnedMessages(value) {
|
|
|
77941
78092
|
throw new SessionControlError(`pinned_messages holds at most ${MAX_PINNED_MESSAGES} entries`, 400);
|
|
77942
78093
|
}
|
|
77943
78094
|
const out = [];
|
|
77944
|
-
const
|
|
78095
|
+
const seenWholeMessage = /* @__PURE__ */ new Set();
|
|
78096
|
+
const seenQuoteId = /* @__PURE__ */ new Set();
|
|
78097
|
+
const seenQuoteShape = /* @__PURE__ */ new Set();
|
|
77945
78098
|
for (const raw of value) {
|
|
77946
78099
|
if (!raw || typeof raw !== "object") {
|
|
77947
78100
|
throw new SessionControlError("each pinned_messages entry must be an object", 400);
|
|
@@ -77951,13 +78104,40 @@ function normalizePinnedMessages(value) {
|
|
|
77951
78104
|
if (typeof msgId !== "string" || !msgId.trim() || msgId.length > MAX_PIN_ID_CHARS) {
|
|
77952
78105
|
throw new SessionControlError("pinned_messages[].msgId must be a non-empty string", 400);
|
|
77953
78106
|
}
|
|
77954
|
-
|
|
77955
|
-
|
|
78107
|
+
const rawId = entry.id;
|
|
78108
|
+
if (rawId !== void 0 && rawId !== null && (typeof rawId !== "string" || !rawId.trim() || rawId.length > MAX_PIN_ENTRY_ID_CHARS)) {
|
|
78109
|
+
throw new SessionControlError(
|
|
78110
|
+
`pinned_messages[].id must be a non-empty string (max ${MAX_PIN_ENTRY_ID_CHARS} chars)`,
|
|
78111
|
+
400
|
|
78112
|
+
);
|
|
78113
|
+
}
|
|
78114
|
+
const id = typeof rawId === "string" ? rawId : void 0;
|
|
78115
|
+
const quote = entry.quote === void 0 || entry.quote === null ? void 0 : normalizePinQuote(entry.quote);
|
|
78116
|
+
let pinId = id;
|
|
78117
|
+
if (quote) {
|
|
78118
|
+
if (id && seenQuoteId.has(id)) continue;
|
|
78119
|
+
const shape = [msgId, quote.exact, quote.prefix ?? "", quote.suffix ?? ""].join("\0");
|
|
78120
|
+
if (seenQuoteShape.has(shape)) continue;
|
|
78121
|
+
pinId = id ?? randomUUID4();
|
|
78122
|
+
seenQuoteId.add(pinId);
|
|
78123
|
+
seenQuoteShape.add(shape);
|
|
78124
|
+
} else {
|
|
78125
|
+
if (seenWholeMessage.has(msgId)) continue;
|
|
78126
|
+
seenWholeMessage.add(msgId);
|
|
78127
|
+
}
|
|
77956
78128
|
const role = entry.role === "user" || entry.role === "assistant" || entry.role === "system" ? entry.role : "assistant";
|
|
77957
78129
|
const label = typeof entry.label === "string" ? entry.label.trim().slice(0, MAX_PIN_LABEL_CHARS) : "";
|
|
77958
78130
|
const timestamp = typeof entry.timestamp === "string" ? entry.timestamp : void 0;
|
|
77959
78131
|
const pinnedAt = typeof entry.pinnedAt === "string" ? entry.pinnedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
77960
|
-
out.push({
|
|
78132
|
+
out.push({
|
|
78133
|
+
msgId,
|
|
78134
|
+
label,
|
|
78135
|
+
role,
|
|
78136
|
+
...timestamp ? { timestamp } : {},
|
|
78137
|
+
pinnedAt,
|
|
78138
|
+
...pinId ? { id: pinId } : {},
|
|
78139
|
+
...quote ? { quote } : {}
|
|
78140
|
+
});
|
|
77961
78141
|
}
|
|
77962
78142
|
return out;
|
|
77963
78143
|
}
|
|
@@ -78616,7 +78796,7 @@ async function readSessionRichHistory(sessionId, tail) {
|
|
|
78616
78796
|
...historyWindowed ? { windowed: true } : {}
|
|
78617
78797
|
};
|
|
78618
78798
|
}
|
|
78619
|
-
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;
|
|
78799
|
+
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;
|
|
78620
78800
|
var init_session_lifecycle = __esm({
|
|
78621
78801
|
"src/core/sessions/session-lifecycle.ts"() {
|
|
78622
78802
|
"use strict";
|
|
@@ -78630,6 +78810,9 @@ var init_session_lifecycle = __esm({
|
|
|
78630
78810
|
MAX_PINNED_MESSAGES = 200;
|
|
78631
78811
|
MAX_PIN_LABEL_CHARS = 300;
|
|
78632
78812
|
MAX_PIN_ID_CHARS = 200;
|
|
78813
|
+
MAX_PIN_ENTRY_ID_CHARS = 64;
|
|
78814
|
+
MAX_PIN_QUOTE_CHARS = 2e3;
|
|
78815
|
+
MAX_PIN_QUOTE_CONTEXT_CHARS = 64;
|
|
78633
78816
|
RECHECK_RPC_TIMEOUT_MS = 5e3;
|
|
78634
78817
|
RECHECK_TIMED_OUT = /* @__PURE__ */ Symbol("recheck-timeout");
|
|
78635
78818
|
GIT_BASES = /* @__PURE__ */ new Set(["uncommitted", "previous", "remote"]);
|
|
@@ -78932,7 +79115,7 @@ __export(compact_inject_exports, {
|
|
|
78932
79115
|
extractSessionMeta: () => extractSessionMeta,
|
|
78933
79116
|
injectCompactBoundary: () => injectCompactBoundary
|
|
78934
79117
|
});
|
|
78935
|
-
import { randomUUID as
|
|
79118
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
78936
79119
|
import fsp26 from "fs/promises";
|
|
78937
79120
|
async function extractSessionMeta(jsonlPath) {
|
|
78938
79121
|
const content = await fsp26.readFile(jsonlPath, "utf-8");
|
|
@@ -78984,8 +79167,8 @@ async function injectCompactBoundary(jsonlPath, summary, meta) {
|
|
|
78984
79167
|
log.session.error("Could not find last message UUID in JSONL", { jsonlPath });
|
|
78985
79168
|
return null;
|
|
78986
79169
|
}
|
|
78987
|
-
const boundaryUuid =
|
|
78988
|
-
const summaryUuid =
|
|
79170
|
+
const boundaryUuid = randomUUID5();
|
|
79171
|
+
const summaryUuid = randomUUID5();
|
|
78989
79172
|
const now = /* @__PURE__ */ new Date();
|
|
78990
79173
|
const boundaryTs = now.toISOString();
|
|
78991
79174
|
const summaryTs = new Date(now.getTime() + 1e3).toISOString();
|
|
@@ -89769,6 +89952,82 @@ var init_task = __esm({
|
|
|
89769
89952
|
}
|
|
89770
89953
|
});
|
|
89771
89954
|
|
|
89955
|
+
// src/core/sessions/spawn-prefix.ts
|
|
89956
|
+
var spawn_prefix_exports = {};
|
|
89957
|
+
__export(spawn_prefix_exports, {
|
|
89958
|
+
MAX_SPAWN_PROMPT_BYTES: () => MAX_SPAWN_PROMPT_BYTES,
|
|
89959
|
+
parseSpawnPrefixFromArgs: () => parseSpawnPrefixFromArgs,
|
|
89960
|
+
readParentSpawnPrefix: () => readParentSpawnPrefix,
|
|
89961
|
+
spawnPrefixFromRecord: () => spawnPrefixFromRecord
|
|
89962
|
+
});
|
|
89963
|
+
function flagValue(args, flag) {
|
|
89964
|
+
const i8 = args.indexOf(flag);
|
|
89965
|
+
if (i8 < 0 || i8 + 1 >= args.length) return void 0;
|
|
89966
|
+
const v3 = args[i8 + 1];
|
|
89967
|
+
return v3 !== void 0 && !v3.startsWith("--") ? v3 : void 0;
|
|
89968
|
+
}
|
|
89969
|
+
function parseSpawnPrefixFromArgs(args) {
|
|
89970
|
+
const prompt = flagValue(args, "--append-system-prompt");
|
|
89971
|
+
return {
|
|
89972
|
+
appendSystemPrompt: prompt !== void 0 && Buffer.byteLength(prompt, "utf8") <= MAX_SPAWN_PROMPT_BYTES ? prompt : null,
|
|
89973
|
+
...flagValue(args, "--model") ? { model: flagValue(args, "--model") } : {},
|
|
89974
|
+
...EFFORTS.includes(flagValue(args, "--effort") ?? "") ? { effort: flagValue(args, "--effort") } : {},
|
|
89975
|
+
...flagValue(args, "--permission-mode") ? { permissionMode: flagValue(args, "--permission-mode") } : {},
|
|
89976
|
+
source: "live-process"
|
|
89977
|
+
};
|
|
89978
|
+
}
|
|
89979
|
+
function spawnPrefixFromRecord(record) {
|
|
89980
|
+
const stored = record.appliedAppendSystemPrompt;
|
|
89981
|
+
if (stored === void 0 || stored === null) return { appendSystemPrompt: null, source: "unknown" };
|
|
89982
|
+
const usable = stored.length > 0 && Buffer.byteLength(stored, "utf8") <= MAX_SPAWN_PROMPT_BYTES;
|
|
89983
|
+
return { appendSystemPrompt: usable ? stored : null, source: "record" };
|
|
89984
|
+
}
|
|
89985
|
+
async function defaultLiveArgsReader(hostKey2, sessionId) {
|
|
89986
|
+
const { probeDaemonSessionArgs: probeDaemonSessionArgs2 } = await Promise.resolve().then(() => (init_daemon_connection(), daemon_connection_exports));
|
|
89987
|
+
return probeDaemonSessionArgs2(hostKey2, sessionId);
|
|
89988
|
+
}
|
|
89989
|
+
async function readParentSpawnPrefix(parent, deps = {}) {
|
|
89990
|
+
const readLiveArgs = deps.readLiveArgs ?? defaultLiveArgsReader;
|
|
89991
|
+
const hostKey2 = parent.host || "__local__";
|
|
89992
|
+
let live = null;
|
|
89993
|
+
try {
|
|
89994
|
+
live = await readLiveArgs(hostKey2, parent.claudeSessionId);
|
|
89995
|
+
} catch (err2) {
|
|
89996
|
+
log.session.debug("spawn prefix: live argv read threw", {
|
|
89997
|
+
sessionId: parent.claudeSessionId,
|
|
89998
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
89999
|
+
});
|
|
90000
|
+
}
|
|
90001
|
+
if (live && live.length > 0) {
|
|
90002
|
+
const prefix = parseSpawnPrefixFromArgs(live);
|
|
90003
|
+
const remembered = parent.appliedAppendSystemPrompt ?? void 0;
|
|
90004
|
+
const actual = prefix.appendSystemPrompt ?? "";
|
|
90005
|
+
if (remembered !== actual) {
|
|
90006
|
+
const persist4 = deps.persist ?? (async (sid, prompt) => {
|
|
90007
|
+
const { updateSessionRecord: updateSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
90008
|
+
return updateSessionRecord2(sid, { appliedAppendSystemPrompt: prompt });
|
|
90009
|
+
});
|
|
90010
|
+
void persist4(parent.claudeSessionId, actual).catch((err2) => {
|
|
90011
|
+
log.session.warn("spawn prefix: backfill of applied prompt failed", {
|
|
90012
|
+
sessionId: parent.claudeSessionId,
|
|
90013
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
90014
|
+
});
|
|
90015
|
+
});
|
|
90016
|
+
}
|
|
90017
|
+
return prefix;
|
|
90018
|
+
}
|
|
90019
|
+
return spawnPrefixFromRecord(parent);
|
|
90020
|
+
}
|
|
90021
|
+
var EFFORTS, MAX_SPAWN_PROMPT_BYTES;
|
|
90022
|
+
var init_spawn_prefix = __esm({
|
|
90023
|
+
"src/core/sessions/spawn-prefix.ts"() {
|
|
90024
|
+
"use strict";
|
|
90025
|
+
init_logging();
|
|
90026
|
+
EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
90027
|
+
MAX_SPAWN_PROMPT_BYTES = 65536;
|
|
90028
|
+
}
|
|
90029
|
+
});
|
|
90030
|
+
|
|
89772
90031
|
// src/core/sessions/side-thread-fork.ts
|
|
89773
90032
|
var side_thread_fork_exports = {};
|
|
89774
90033
|
__export(side_thread_fork_exports, {
|
|
@@ -89825,6 +90084,10 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
89825
90084
|
const sessionId = crypto18.randomUUID();
|
|
89826
90085
|
const lane = sideThreadLaneKey(parentSid, threadId);
|
|
89827
90086
|
const mode = parent.mode !== "default" ? parent.mode : void 0;
|
|
90087
|
+
const { readParentSpawnPrefix: readParentSpawnPrefix2 } = await Promise.resolve().then(() => (init_spawn_prefix(), spawn_prefix_exports));
|
|
90088
|
+
const prefix = await readParentSpawnPrefix2(parent);
|
|
90089
|
+
const cliModel = prefix.model ?? parent.cliModel;
|
|
90090
|
+
const effort = prefix.effort ?? parent.effort;
|
|
89828
90091
|
await createSessionRecord(sessionId, "", parent.project ?? "", parent.cwd, {
|
|
89829
90092
|
title,
|
|
89830
90093
|
...mode ? { mode } : {},
|
|
@@ -89832,8 +90095,10 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
89832
90095
|
// The thread inherits the parent's launch bundle verbatim, re-applied from
|
|
89833
90096
|
// the record on every cold resume.
|
|
89834
90097
|
...parent.profile ? { profile: parent.profile } : {},
|
|
89835
|
-
...
|
|
89836
|
-
...
|
|
90098
|
+
...effort ? { effort } : {},
|
|
90099
|
+
...cliModel ? { cliModel } : {},
|
|
90100
|
+
// '' = the parent runs without an append prompt, and so does the thread.
|
|
90101
|
+
appliedAppendSystemPrompt: prefix.appendSystemPrompt ?? "",
|
|
89837
90102
|
lane,
|
|
89838
90103
|
forkedFromSessionId: resumeFrom,
|
|
89839
90104
|
initialProcessStatus: "idle",
|
|
@@ -89849,11 +90114,12 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
89849
90114
|
...mode ? { mode } : {},
|
|
89850
90115
|
...parent.host ? { host: parent.host } : {},
|
|
89851
90116
|
...parent.profile ? { profile: parent.profile } : {},
|
|
89852
|
-
...
|
|
90117
|
+
...effort ? { effort } : {},
|
|
89853
90118
|
// Must be `cliModel` (the parent's verbatim --model arg, [1m] marker
|
|
89854
90119
|
// included): the CLI does NOT inherit a model across --fork-session, and
|
|
89855
90120
|
// the reported `model` has lost the marker.
|
|
89856
|
-
...
|
|
90121
|
+
...cliModel ? { model: cliModel } : {},
|
|
90122
|
+
appendSystemPromptExact: prefix.appendSystemPrompt,
|
|
89857
90123
|
lane,
|
|
89858
90124
|
forkedFromSessionId: resumeFrom
|
|
89859
90125
|
}, ["session-runner"], { source: "side-thread-fork" });
|
|
@@ -89862,7 +90128,9 @@ async function forkSideThreadSession(parentSid, threadId, opts) {
|
|
|
89862
90128
|
threadId,
|
|
89863
90129
|
sessionId,
|
|
89864
90130
|
resumeFrom,
|
|
89865
|
-
initOnly: !opts?.message
|
|
90131
|
+
initOnly: !opts?.message,
|
|
90132
|
+
prefixSource: prefix.source,
|
|
90133
|
+
prefixPromptLength: prefix.appendSystemPrompt?.length ?? 0
|
|
89866
90134
|
});
|
|
89867
90135
|
return { sessionId, resumeFromSessionId: resumeFrom };
|
|
89868
90136
|
}
|
|
@@ -115734,11 +116002,12 @@ __export(session_controls_exports, {
|
|
|
115734
116002
|
applySessionModelChange: () => applySessionModelChange,
|
|
115735
116003
|
computeModelOptions: () => computeModelOptions,
|
|
115736
116004
|
controlErrorKind: () => controlErrorKind,
|
|
116005
|
+
createForkSiblingTask: () => createForkSiblingTask,
|
|
115737
116006
|
forkSessionToTask: () => forkSessionToTask,
|
|
115738
116007
|
handleSessionControlRelay: () => handleSessionControlRelay,
|
|
115739
116008
|
modelReadBackMatches: () => modelReadBackMatches
|
|
115740
116009
|
});
|
|
115741
|
-
import { randomUUID as
|
|
116010
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
115742
116011
|
function controlErrorKind(status2) {
|
|
115743
116012
|
if (status2 === 404) return "not_found";
|
|
115744
116013
|
if (status2 === 405) return "method_not_allowed";
|
|
@@ -115911,6 +116180,124 @@ async function computeModelOptions(sessionId) {
|
|
|
115911
116180
|
currentEffort: liveEffort ?? record.effectiveEffort ?? record.effort ?? null
|
|
115912
116181
|
};
|
|
115913
116182
|
}
|
|
116183
|
+
async function createForkSiblingTask(sourceTaskId, opts) {
|
|
116184
|
+
const {
|
|
116185
|
+
getTask: getTask2,
|
|
116186
|
+
addTask: addTask2,
|
|
116187
|
+
updateTask: updateTask2,
|
|
116188
|
+
groupTasks: groupTasks2,
|
|
116189
|
+
addToGroup: addToGroup2,
|
|
116190
|
+
renameGroup: renameGroup2
|
|
116191
|
+
} = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
116192
|
+
let sourceTask;
|
|
116193
|
+
try {
|
|
116194
|
+
sourceTask = await getTask2(sourceTaskId);
|
|
116195
|
+
} catch {
|
|
116196
|
+
throw new SessionControlError(`Source task "${sourceTaskId}" not found`, 404);
|
|
116197
|
+
}
|
|
116198
|
+
const sourceBaseTitle = sourceTask.title.replace(/^Fork of\s+/i, "").replace(/\s+-\s+fork of\s+.*$/i, "").trim() || sourceTask.title;
|
|
116199
|
+
const explicitTitle = opts.childTitle || (opts.titlePrefix ? `${opts.titlePrefix} - fork of ${sourceBaseTitle}` : void 0);
|
|
116200
|
+
const autoTitle = !explicitTitle;
|
|
116201
|
+
const newTitle = explicitTitle || `Fork of ${sourceBaseTitle}`;
|
|
116202
|
+
let inheritedTier;
|
|
116203
|
+
if (sourceTask.pinned && sourceTask.focus_tier) {
|
|
116204
|
+
const { getCustomTiers: getCustomTiers2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
116205
|
+
const known = ["focus", "backlog", "wait"].includes(sourceTask.focus_tier) || (await getCustomTiers2()).some((t3) => t3.id === sourceTask.focus_tier);
|
|
116206
|
+
if (known) inheritedTier = sourceTask.focus_tier;
|
|
116207
|
+
else {
|
|
116208
|
+
log.session.warn("fork: source tier is not registered, filing the fork in Satellite", {
|
|
116209
|
+
sourceTaskId: sourceTask.id,
|
|
116210
|
+
tier: sourceTask.focus_tier
|
|
116211
|
+
});
|
|
116212
|
+
}
|
|
116213
|
+
}
|
|
116214
|
+
const { task: newFork } = await addTask2({
|
|
116215
|
+
title: newTitle,
|
|
116216
|
+
project: sourceTask.project || "",
|
|
116217
|
+
source: sourceTask.source,
|
|
116218
|
+
...opts.description ? { description: opts.description } : {},
|
|
116219
|
+
// A fork is a person splitting off live work, so it joins the board like
|
|
116220
|
+
// any other hand-made task (Satellite = pinned, no stored tier).
|
|
116221
|
+
pinned: true,
|
|
116222
|
+
...inheritedTier ? { focus_tier: inheritedTier } : {},
|
|
116223
|
+
// Forking an Ask Walnut conversation yields another Ask Walnut
|
|
116224
|
+
// conversation: without the flag the fork loses the amber marker AND the
|
|
116225
|
+
// persona drift repair (both key on task.walnut_agent, never on project).
|
|
116226
|
+
...sourceTask.walnut_agent ? { walnut_agent: true } : {}
|
|
116227
|
+
});
|
|
116228
|
+
let forkGroupId;
|
|
116229
|
+
try {
|
|
116230
|
+
if (sourceTask.group_id) {
|
|
116231
|
+
const r8 = await addToGroup2(sourceTask.group_id, [newFork.id]);
|
|
116232
|
+
forkGroupId = r8.group_id;
|
|
116233
|
+
} else {
|
|
116234
|
+
const r8 = await groupTasks2([sourceTask.id, newFork.id], sourceTask.title);
|
|
116235
|
+
forkGroupId = r8.group_id;
|
|
116236
|
+
}
|
|
116237
|
+
} catch (err2) {
|
|
116238
|
+
log.session.warn("fork: failed to group source + fork", {
|
|
116239
|
+
sourceTaskId: sourceTask.id,
|
|
116240
|
+
forkTaskId: newFork.id,
|
|
116241
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
116242
|
+
});
|
|
116243
|
+
}
|
|
116244
|
+
let task;
|
|
116245
|
+
try {
|
|
116246
|
+
task = await getTask2(newFork.id);
|
|
116247
|
+
} catch {
|
|
116248
|
+
task = newFork;
|
|
116249
|
+
}
|
|
116250
|
+
bus.emit(EventNames.TASK_CREATED, { task }, ["web-ui", "main-agent"], { source: opts.source });
|
|
116251
|
+
if (autoTitle && opts.prompt?.trim()) {
|
|
116252
|
+
const forkId = newFork.id;
|
|
116253
|
+
const sourceTitle = sourceBaseTitle;
|
|
116254
|
+
const placeholderTitle = newTitle;
|
|
116255
|
+
const prompt = opts.prompt;
|
|
116256
|
+
const titleSource = `${opts.source}-title`;
|
|
116257
|
+
void (async () => {
|
|
116258
|
+
try {
|
|
116259
|
+
const { summarizeForkPrompt: summarizeForkPrompt2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
116260
|
+
const label = await summarizeForkPrompt2(prompt);
|
|
116261
|
+
if (!label) return;
|
|
116262
|
+
const current5 = await getTask2(forkId);
|
|
116263
|
+
if (current5.title !== placeholderTitle) {
|
|
116264
|
+
log.session.info("fork title refine skipped \u2014 title changed since fork", { taskId: forkId });
|
|
116265
|
+
return;
|
|
116266
|
+
}
|
|
116267
|
+
const refinedTitle = `${label} - fork of ${sourceTitle}`;
|
|
116268
|
+
const { task: updated } = await updateTask2(forkId, { title: refinedTitle }, { source: titleSource });
|
|
116269
|
+
bus.emit(EventNames.TASK_UPDATED, { task: updated }, ["web-ui", "main-agent"], { source: titleSource });
|
|
116270
|
+
log.session.info("fork title refined", { taskId: forkId, title: refinedTitle });
|
|
116271
|
+
} catch (err2) {
|
|
116272
|
+
log.session.warn("fork title refine failed", {
|
|
116273
|
+
taskId: forkId,
|
|
116274
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
116275
|
+
});
|
|
116276
|
+
}
|
|
116277
|
+
})();
|
|
116278
|
+
}
|
|
116279
|
+
if (forkGroupId && !sourceTask.group_id) {
|
|
116280
|
+
const gid = forkGroupId;
|
|
116281
|
+
const seedTitles = [sourceTask.title, newTitle];
|
|
116282
|
+
const groupSource = opts.source;
|
|
116283
|
+
void (async () => {
|
|
116284
|
+
try {
|
|
116285
|
+
const { summarizeGroupLabel: summarizeGroupLabel2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
116286
|
+
const label = await summarizeGroupLabel2(seedTitles);
|
|
116287
|
+
if (!label) return;
|
|
116288
|
+
await renameGroup2(gid, label);
|
|
116289
|
+
bus.emit(EventNames.TASK_GROUPS_CHANGED, { group_id: gid, label }, ["web-ui", "main-agent"], { source: groupSource });
|
|
116290
|
+
log.session.info("fork group label refined", { groupId: gid, label });
|
|
116291
|
+
} catch (err2) {
|
|
116292
|
+
log.session.warn("fork group label refine failed", {
|
|
116293
|
+
groupId: gid,
|
|
116294
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
116295
|
+
});
|
|
116296
|
+
}
|
|
116297
|
+
})();
|
|
116298
|
+
}
|
|
116299
|
+
return { task, ...forkGroupId ? { groupId: forkGroupId } : {} };
|
|
116300
|
+
}
|
|
115914
116301
|
async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
115915
116302
|
const { task_id, create_child_task, child_title, message, title, model, imageContext } = input;
|
|
115916
116303
|
if (!task_id && !create_child_task) {
|
|
@@ -115920,14 +116307,7 @@ async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
|
115920
116307
|
throw new SessionControlError("task_id and create_child_task are mutually exclusive", 400);
|
|
115921
116308
|
}
|
|
115922
116309
|
const { getSessionByClaudeId: getSessionByClaudeId2, getSessionsForTask: getSessionsForTask2, createSessionRecord: createSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
115923
|
-
const {
|
|
115924
|
-
getTask: getTask2,
|
|
115925
|
-
addTask: addTask2,
|
|
115926
|
-
updateTask: updateTask2,
|
|
115927
|
-
groupTasks: groupTasks2,
|
|
115928
|
-
addToGroup: addToGroup2,
|
|
115929
|
-
renameGroup: renameGroup2
|
|
115930
|
-
} = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
116310
|
+
const { getTask: getTask2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
115931
116311
|
const sourceRecord = await getSessionByClaudeId2(sourceSessionId);
|
|
115932
116312
|
if (!sourceRecord) throw new SessionControlError("Source session not found", 404);
|
|
115933
116313
|
if (!engineCaps(sourceRecord.engine).fork) {
|
|
@@ -115944,108 +116324,13 @@ async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
|
115944
116324
|
if (!sourceRecord.taskId) {
|
|
115945
116325
|
throw new SessionControlError("Source session has no task \u2014 cannot create fork task", 400);
|
|
115946
116326
|
}
|
|
115947
|
-
|
|
115948
|
-
|
|
115949
|
-
|
|
115950
|
-
|
|
115951
|
-
|
|
115952
|
-
|
|
115953
|
-
const sourceBaseTitle = sourceTask.title.replace(/^Fork of\s+/i, "").replace(/\s+-\s+fork of\s+.*$/i, "").trim() || sourceTask.title;
|
|
115954
|
-
const autoTitle = !child_title;
|
|
115955
|
-
const newTitle = child_title || `Fork of ${sourceBaseTitle}`;
|
|
115956
|
-
let inheritedTier;
|
|
115957
|
-
if (sourceTask.pinned && sourceTask.focus_tier) {
|
|
115958
|
-
const { getCustomTiers: getCustomTiers2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
115959
|
-
const known = ["focus", "backlog", "wait"].includes(sourceTask.focus_tier) || (await getCustomTiers2()).some((t3) => t3.id === sourceTask.focus_tier);
|
|
115960
|
-
if (known) inheritedTier = sourceTask.focus_tier;
|
|
115961
|
-
else {
|
|
115962
|
-
log.session.warn("fork: source tier is not registered, filing the fork in Satellite", {
|
|
115963
|
-
sourceTaskId: sourceTask.id,
|
|
115964
|
-
tier: sourceTask.focus_tier
|
|
115965
|
-
});
|
|
115966
|
-
}
|
|
115967
|
-
}
|
|
115968
|
-
const { task: newFork } = await addTask2({
|
|
115969
|
-
title: newTitle,
|
|
115970
|
-
project: sourceTask.project || "",
|
|
115971
|
-
source: sourceTask.source,
|
|
115972
|
-
// A fork is a person splitting off live work, so it joins the board like
|
|
115973
|
-
// any other hand-made task (Satellite = pinned, no stored tier).
|
|
115974
|
-
pinned: true,
|
|
115975
|
-
...inheritedTier ? { focus_tier: inheritedTier } : {},
|
|
115976
|
-
// Forking an Ask Walnut conversation yields another Ask Walnut
|
|
115977
|
-
// conversation: without the flag the fork loses the amber marker AND the
|
|
115978
|
-
// persona drift repair (both key on task.walnut_agent, never on project).
|
|
115979
|
-
...sourceTask.walnut_agent ? { walnut_agent: true } : {}
|
|
115980
|
-
});
|
|
115981
|
-
let forkGroupId;
|
|
115982
|
-
try {
|
|
115983
|
-
if (sourceTask.group_id) {
|
|
115984
|
-
const r8 = await addToGroup2(sourceTask.group_id, [newFork.id]);
|
|
115985
|
-
forkGroupId = r8.group_id;
|
|
115986
|
-
} else {
|
|
115987
|
-
const r8 = await groupTasks2([sourceTask.id, newFork.id], sourceTask.title);
|
|
115988
|
-
forkGroupId = r8.group_id;
|
|
115989
|
-
}
|
|
115990
|
-
} catch (err2) {
|
|
115991
|
-
log.session.warn("fork: failed to group source + fork", {
|
|
115992
|
-
sourceTaskId: sourceTask.id,
|
|
115993
|
-
forkTaskId: newFork.id,
|
|
115994
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
115995
|
-
});
|
|
115996
|
-
}
|
|
115997
|
-
try {
|
|
115998
|
-
task = await getTask2(newFork.id);
|
|
115999
|
-
} catch {
|
|
116000
|
-
task = newFork;
|
|
116001
|
-
}
|
|
116002
|
-
bus.emit(EventNames.TASK_CREATED, { task }, ["web-ui", "main-agent"], { source: "fork" });
|
|
116327
|
+
const created = await createForkSiblingTask(sourceRecord.taskId, {
|
|
116328
|
+
childTitle: child_title,
|
|
116329
|
+
prompt: message,
|
|
116330
|
+
source: "fork"
|
|
116331
|
+
});
|
|
116332
|
+
task = created.task;
|
|
116003
116333
|
childTaskCreated = true;
|
|
116004
|
-
if (autoTitle && message?.trim()) {
|
|
116005
|
-
const forkId = newFork.id;
|
|
116006
|
-
const sourceTitle = sourceBaseTitle;
|
|
116007
|
-
const placeholderTitle = newTitle;
|
|
116008
|
-
void (async () => {
|
|
116009
|
-
try {
|
|
116010
|
-
const { summarizeForkPrompt: summarizeForkPrompt2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
116011
|
-
const label = await summarizeForkPrompt2(message);
|
|
116012
|
-
if (!label) return;
|
|
116013
|
-
const current5 = await getTask2(forkId);
|
|
116014
|
-
if (current5.title !== placeholderTitle) {
|
|
116015
|
-
log.session.info("fork title refine skipped \u2014 title changed since fork", { taskId: forkId });
|
|
116016
|
-
return;
|
|
116017
|
-
}
|
|
116018
|
-
const refinedTitle = `${label} - fork of ${sourceTitle}`;
|
|
116019
|
-
const { task: updated } = await updateTask2(forkId, { title: refinedTitle }, { source: "fork-title" });
|
|
116020
|
-
bus.emit(EventNames.TASK_UPDATED, { task: updated }, ["web-ui", "main-agent"], { source: "fork-title" });
|
|
116021
|
-
log.session.info("fork title refined", { taskId: forkId, title: refinedTitle });
|
|
116022
|
-
} catch (err2) {
|
|
116023
|
-
log.session.warn("fork title refine failed", {
|
|
116024
|
-
taskId: forkId,
|
|
116025
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
116026
|
-
});
|
|
116027
|
-
}
|
|
116028
|
-
})();
|
|
116029
|
-
}
|
|
116030
|
-
if (forkGroupId && !sourceTask.group_id) {
|
|
116031
|
-
const gid = forkGroupId;
|
|
116032
|
-
const seedTitles = [sourceTask.title, newTitle];
|
|
116033
|
-
void (async () => {
|
|
116034
|
-
try {
|
|
116035
|
-
const { summarizeGroupLabel: summarizeGroupLabel2 } = await Promise.resolve().then(() => (init_fork_title(), fork_title_exports));
|
|
116036
|
-
const label = await summarizeGroupLabel2(seedTitles);
|
|
116037
|
-
if (!label) return;
|
|
116038
|
-
await renameGroup2(gid, label);
|
|
116039
|
-
bus.emit(EventNames.TASK_GROUPS_CHANGED, { group_id: gid, label }, ["web-ui", "main-agent"], { source: "fork" });
|
|
116040
|
-
log.session.info("fork group label refined", { groupId: gid, label });
|
|
116041
|
-
} catch (err2) {
|
|
116042
|
-
log.session.warn("fork group label refine failed", {
|
|
116043
|
-
groupId: gid,
|
|
116044
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
116045
|
-
});
|
|
116046
|
-
}
|
|
116047
|
-
})();
|
|
116048
|
-
}
|
|
116049
116334
|
} else {
|
|
116050
116335
|
try {
|
|
116051
116336
|
task = await getTask2(task_id);
|
|
@@ -116067,7 +116352,7 @@ async function forkSessionToTask(sourceSessionId, input, source = "web-api") {
|
|
|
116067
116352
|
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";
|
|
116068
116353
|
const forkMessage = `${FORK_FOCUS_PREFIX}${imageContext ?? ""}New request:
|
|
116069
116354
|
${userRequest}`;
|
|
116070
|
-
const forkSessionId =
|
|
116355
|
+
const forkSessionId = randomUUID6();
|
|
116071
116356
|
const forkTitle = title ?? `Fork of ${sourceRecord.title ?? sourceSessionId.slice(0, 16)}`;
|
|
116072
116357
|
try {
|
|
116073
116358
|
await createSessionRecord2(forkSessionId, task.id, task.project ?? "", sourceRecord.cwd, {
|
|
@@ -116790,7 +117075,7 @@ __export(session_rewind_exports, {
|
|
|
116790
117075
|
probeRewindViaDaemon: () => probeRewindViaDaemon,
|
|
116791
117076
|
rewindSessionToMessage: () => rewindSessionToMessage
|
|
116792
117077
|
});
|
|
116793
|
-
import { randomUUID as
|
|
117078
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
116794
117079
|
function cutAncestorHistoryAtRewindPoint(ancestorMessages, rewoundAtMessageUuid) {
|
|
116795
117080
|
if (!rewoundAtMessageUuid) {
|
|
116796
117081
|
return { messages: ancestorMessages, found: false, dropped: 0 };
|
|
@@ -116991,7 +117276,7 @@ async function rewindSessionToMessage(sessionId, input, source = "web-api") {
|
|
|
116991
117276
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
116992
117277
|
});
|
|
116993
117278
|
}
|
|
116994
|
-
const rewoundId =
|
|
117279
|
+
const rewoundId = randomUUID7();
|
|
116995
117280
|
const baseTitle = (record.title ?? sessionId.slice(0, 16)).replace(/\s*\(rewound\)$/i, "");
|
|
116996
117281
|
const title = `${baseTitle} (rewound)`.slice(0, 200);
|
|
116997
117282
|
const { createSessionRecord: createSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
@@ -117711,6 +117996,7 @@ function parseSessionMessages(content, opts) {
|
|
|
117711
117996
|
}
|
|
117712
117997
|
}
|
|
117713
117998
|
let lastPlanContent = null;
|
|
117999
|
+
let dropWarmupReply = false;
|
|
117714
118000
|
const resultParentIds = [];
|
|
117715
118001
|
const result = [];
|
|
117716
118002
|
for (const [mapMsgId, msg] of messageMap) {
|
|
@@ -117788,10 +118074,18 @@ ${imgPath}` : imgPath;
|
|
|
117788
118074
|
let text = textParts.join("\n").trim();
|
|
117789
118075
|
if (!text && tools2.length === 0 && !thinking) continue;
|
|
117790
118076
|
if (msg.role === "user" && text.startsWith("<")) {
|
|
118077
|
+
if (isCacheWarmupText(text)) {
|
|
118078
|
+
dropWarmupReply = true;
|
|
118079
|
+
continue;
|
|
118080
|
+
}
|
|
117791
118081
|
const transformed = transformInjectedUserText(text);
|
|
117792
118082
|
if (transformed === null) continue;
|
|
117793
118083
|
if (transformed !== void 0) text = transformed;
|
|
117794
118084
|
}
|
|
118085
|
+
if (msg.role === "assistant" && dropWarmupReply) {
|
|
118086
|
+
dropWarmupReply = false;
|
|
118087
|
+
if (tools2.length === 0) continue;
|
|
118088
|
+
}
|
|
117795
118089
|
if (msg.role === "user") text = stripOutputModeWrappers(text);
|
|
117796
118090
|
if (msg.role === "assistant" && !text && tools2.length === 0) continue;
|
|
117797
118091
|
result.push({
|
|
@@ -118742,6 +119036,7 @@ var init_session_history = __esm({
|
|
|
118742
119036
|
init_session_io();
|
|
118743
119037
|
init_constants();
|
|
118744
119038
|
init_remote_image_mirror();
|
|
119039
|
+
init_side_thread_warmup();
|
|
118745
119040
|
init_session_file_reader();
|
|
118746
119041
|
LOCAL_HOME = os19.homedir();
|
|
118747
119042
|
TAIL_SEGMENT_ROLL_BYTES = 4 * 1024 * 1024;
|
|
@@ -119034,7 +119329,7 @@ var init_context_window = __esm({
|
|
|
119034
119329
|
});
|
|
119035
119330
|
|
|
119036
119331
|
// src/providers/turn-ledger.ts
|
|
119037
|
-
import { randomUUID as
|
|
119332
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
119038
119333
|
function openTurn(sessionId) {
|
|
119039
119334
|
const existing = openTurns.get(sessionId);
|
|
119040
119335
|
if (existing && !existing.settled) {
|
|
@@ -119044,7 +119339,7 @@ function openTurn(sessionId) {
|
|
|
119044
119339
|
});
|
|
119045
119340
|
return { turnId: existing.turnId, promise: existing.promise };
|
|
119046
119341
|
}
|
|
119047
|
-
const turnId =
|
|
119342
|
+
const turnId = randomUUID8();
|
|
119048
119343
|
let resolve;
|
|
119049
119344
|
let reject;
|
|
119050
119345
|
const promise = new Promise((res, rej) => {
|
|
@@ -127038,11 +127333,39 @@ var init_claude_code_session = __esm({
|
|
|
127038
127333
|
session2.pendingDescription = message.slice(0, 500);
|
|
127039
127334
|
let appendSystemPrompt;
|
|
127040
127335
|
const isFork = !!data.forkedFromSessionId;
|
|
127041
|
-
|
|
127042
|
-
|
|
127043
|
-
|
|
127044
|
-
|
|
127045
|
-
|
|
127336
|
+
const { parseSideLaneKey: parseSideLaneKey2 } = await Promise.resolve().then(() => (init_side_thread_fork(), side_thread_fork_exports));
|
|
127337
|
+
const sideIds = parseSideLaneKey2(data.lane);
|
|
127338
|
+
if (sideIds) {
|
|
127339
|
+
let exact = data.appendSystemPromptExact;
|
|
127340
|
+
let source = "live-or-record";
|
|
127341
|
+
if (exact === void 0) {
|
|
127342
|
+
try {
|
|
127343
|
+
const { getSessionByClaudeId: getSessionByClaudeId2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
127344
|
+
const { spawnPrefixFromRecord: spawnPrefixFromRecord2 } = await Promise.resolve().then(() => (init_spawn_prefix(), spawn_prefix_exports));
|
|
127345
|
+
const parentRecord = await getSessionByClaudeId2(sideIds.parentSid);
|
|
127346
|
+
const prefix = parentRecord ? spawnPrefixFromRecord2(parentRecord) : null;
|
|
127347
|
+
exact = prefix?.appendSystemPrompt ?? null;
|
|
127348
|
+
source = prefix?.source ?? "no-parent-record";
|
|
127349
|
+
} catch (err2) {
|
|
127350
|
+
exact = null;
|
|
127351
|
+
source = "lookup-failed";
|
|
127352
|
+
log.session.warn("side thread: parent prompt lookup failed \u2014 spawning without an append prompt", {
|
|
127353
|
+
parentSid: sideIds.parentSid,
|
|
127354
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
127355
|
+
});
|
|
127356
|
+
}
|
|
127357
|
+
}
|
|
127358
|
+
appendSystemPrompt = exact ?? void 0;
|
|
127359
|
+
log.session.info("side thread: prefix prompt resolved", {
|
|
127360
|
+
parentSid: sideIds.parentSid,
|
|
127361
|
+
source,
|
|
127362
|
+
promptLength: appendSystemPrompt?.length ?? 0
|
|
127363
|
+
});
|
|
127364
|
+
} else {
|
|
127365
|
+
if (data.appendSystemPrompt && !isFork) {
|
|
127366
|
+
appendSystemPrompt = data.appendSystemPrompt;
|
|
127367
|
+
log.session.info("using caller-provided system prompt", { taskId: taskId2, promptLength: data.appendSystemPrompt.length });
|
|
127368
|
+
}
|
|
127046
127369
|
try {
|
|
127047
127370
|
const { buildSessionContext: buildSessionContext2 } = await Promise.resolve().then(() => (init_session_context(), session_context_exports));
|
|
127048
127371
|
const ctx = await buildSessionContext2(taskId2 ?? "", cwd, data.host);
|
|
@@ -127060,32 +127383,6 @@ ${ctx.systemPrompt}` : ctx.systemPrompt;
|
|
|
127060
127383
|
log.session.warn("failed to build session context", { taskId: taskId2, error: err2 instanceof Error ? err2.message : String(err2) });
|
|
127061
127384
|
}
|
|
127062
127385
|
}
|
|
127063
|
-
{
|
|
127064
|
-
const { parseSideLaneKey: parseSideLaneKey2 } = await Promise.resolve().then(() => (init_side_thread_fork(), side_thread_fork_exports));
|
|
127065
|
-
const sideIds = parseSideLaneKey2(data.lane);
|
|
127066
|
-
if (sideIds) {
|
|
127067
|
-
try {
|
|
127068
|
-
const { getSessionByClaudeId: getSessionByClaudeId2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
127069
|
-
const parentRecord = await getSessionByClaudeId2(sideIds.parentSid);
|
|
127070
|
-
const inherited = parentRecord?.appliedAppendSystemPrompt;
|
|
127071
|
-
if (inherited && Buffer.byteLength(inherited, "utf8") <= 65536) {
|
|
127072
|
-
appendSystemPrompt = inherited;
|
|
127073
|
-
log.session.info("side thread: inherited parent system prompt verbatim", {
|
|
127074
|
-
parentSid: sideIds.parentSid,
|
|
127075
|
-
promptLength: appendSystemPrompt.length
|
|
127076
|
-
});
|
|
127077
|
-
} else {
|
|
127078
|
-
log.session.info("side thread: parent has no stored system prompt \u2014 fresh build (cache prefix will diverge)", {
|
|
127079
|
-
parentSid: sideIds.parentSid
|
|
127080
|
-
});
|
|
127081
|
-
}
|
|
127082
|
-
} catch (err2) {
|
|
127083
|
-
log.session.warn("side thread: parent prompt lookup failed \u2014 fresh build", {
|
|
127084
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
127085
|
-
});
|
|
127086
|
-
}
|
|
127087
|
-
}
|
|
127088
|
-
}
|
|
127089
127386
|
if (data.preassignedSessionId && appendSystemPrompt && Buffer.byteLength(appendSystemPrompt, "utf8") <= 65536) {
|
|
127090
127387
|
const promptToStore = appendSystemPrompt;
|
|
127091
127388
|
const sidToStore = data.preassignedSessionId;
|
|
@@ -128285,7 +128582,7 @@ __export(session_requests_exports, {
|
|
|
128285
128582
|
settleNotified: () => settleNotified,
|
|
128286
128583
|
settleReplied: () => settleReplied
|
|
128287
128584
|
});
|
|
128288
|
-
import { createHash as createHash14, randomUUID as
|
|
128585
|
+
import { createHash as createHash14, randomUUID as randomUUID9 } from "crypto";
|
|
128289
128586
|
import path119 from "path";
|
|
128290
128587
|
function oneLine2(text, max = 160) {
|
|
128291
128588
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
@@ -128309,7 +128606,7 @@ function clampReplyTimeoutSecs(secs) {
|
|
|
128309
128606
|
}
|
|
128310
128607
|
async function createSessionRequest(input) {
|
|
128311
128608
|
const request2 = {
|
|
128312
|
-
id: `rq-${
|
|
128609
|
+
id: `rq-${randomUUID9().replace(/-/g, "").slice(0, 12)}`,
|
|
128313
128610
|
fromSessionId: input.fromSessionId,
|
|
128314
128611
|
...input.toSessionId ? { toSessionId: input.toSessionId } : {},
|
|
128315
128612
|
...input.toTaskId ? { toTaskId: input.toTaskId } : {},
|
|
@@ -137268,7 +137565,7 @@ __export(mobile_launch_exports, {
|
|
|
137268
137565
|
performMobileLaunch: () => performMobileLaunch,
|
|
137269
137566
|
validateMobileLaunchBody: () => validateMobileLaunchBody
|
|
137270
137567
|
});
|
|
137271
|
-
import { randomUUID as
|
|
137568
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
137272
137569
|
function launchErrorCode(status2) {
|
|
137273
137570
|
if (status2 === 404) return "not_found";
|
|
137274
137571
|
if (status2 === 409) return "conflict";
|
|
@@ -137390,7 +137687,7 @@ async function performMobileLaunch(input, source) {
|
|
|
137390
137687
|
);
|
|
137391
137688
|
}
|
|
137392
137689
|
}
|
|
137393
|
-
const preassignedSessionId = engineCaps(input.engine).idProvisioning === "provider-issued" ? void 0 :
|
|
137690
|
+
const preassignedSessionId = engineCaps(input.engine).idProvisioning === "provider-issued" ? void 0 : randomUUID10();
|
|
137394
137691
|
const task = await quickStartSession({
|
|
137395
137692
|
message: input.message,
|
|
137396
137693
|
cwd: input.cwd,
|
|
@@ -138189,6 +138486,7 @@ __export(daemon_connection_exports, {
|
|
|
138189
138486
|
isDaemonConnected: () => isDaemonConnected,
|
|
138190
138487
|
isPooledConnection: () => isPooledConnection,
|
|
138191
138488
|
probeDaemonSession: () => probeDaemonSession,
|
|
138489
|
+
probeDaemonSessionArgs: () => probeDaemonSessionArgs,
|
|
138192
138490
|
pushDaemonHooksToAllHosts: () => pushDaemonHooksToAllHosts,
|
|
138193
138491
|
setOnDaemonHostConnected: () => setOnDaemonHostConnected,
|
|
138194
138492
|
setOnDaemonStatusChange: () => setOnDaemonStatusChange,
|
|
@@ -138365,6 +138663,28 @@ async function probeDaemonSession(hostKey2, sessionId) {
|
|
|
138365
138663
|
return null;
|
|
138366
138664
|
}
|
|
138367
138665
|
}
|
|
138666
|
+
async function probeDaemonSessionArgs(hostKey2, sessionId, deadlineMs = 2e3) {
|
|
138667
|
+
const conn = getConnectedDaemonConnection(hostKey2);
|
|
138668
|
+
if (!conn) return null;
|
|
138669
|
+
try {
|
|
138670
|
+
const result = await Promise.race([
|
|
138671
|
+
conn.send("status", { sid: sessionId, includeArgs: true }),
|
|
138672
|
+
new Promise((_, reject) => setTimeout(
|
|
138673
|
+
() => reject(new Error(`status probe exceeded ${deadlineMs}ms`)),
|
|
138674
|
+
deadlineMs
|
|
138675
|
+
).unref?.())
|
|
138676
|
+
]);
|
|
138677
|
+
if (!result.ok || !result.alive || !Array.isArray(result.args)) return null;
|
|
138678
|
+
return result.args.filter((a8) => typeof a8 === "string");
|
|
138679
|
+
} catch (err2) {
|
|
138680
|
+
log.session.debug("probeDaemonSessionArgs: status probe failed", {
|
|
138681
|
+
hostKey: hostKey2,
|
|
138682
|
+
sessionId,
|
|
138683
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
138684
|
+
});
|
|
138685
|
+
return null;
|
|
138686
|
+
}
|
|
138687
|
+
}
|
|
138368
138688
|
var execFileAsync11, MOBILE_ENQUEUE_LEDGER_MAX, recentMobileEnqueues, DaemonConnection, onPoolStatusChange, onHostConnected, hostConnectedListeners, connectionPool, connectingPromises, failureCache, FAILURE_CACHE_TTL_MS;
|
|
138369
138689
|
var init_daemon_connection = __esm({
|
|
138370
138690
|
"src/providers/daemon-connection.ts"() {
|
|
@@ -143710,6 +144030,7 @@ async function createSessionRecord(claudeSessionId, taskId2, project, cwd, extra
|
|
|
143710
144030
|
...extra?.effort ? { effort: extra.effort } : {},
|
|
143711
144031
|
...extra?.profile ? { profile: extra.profile } : {},
|
|
143712
144032
|
...extra?.lane ? { lane: extra.lane } : {},
|
|
144033
|
+
...extra?.appliedAppendSystemPrompt !== void 0 ? { appliedAppendSystemPrompt: extra.appliedAppendSystemPrompt } : {},
|
|
143713
144034
|
...extra?.engine ? { engine: extra.engine } : {},
|
|
143714
144035
|
...extra?.acpRuntimeId ? { acpRuntimeId: extra.acpRuntimeId } : {},
|
|
143715
144036
|
...extra?.acpJournalPath ? { acpJournalPath: extra.acpJournalPath } : {},
|
|
@@ -143864,7 +144185,7 @@ function applyUpdateToSession(session2, updates, logLabel) {
|
|
|
143864
144185
|
};
|
|
143865
144186
|
if (count2 === 1) log.session.info("legacy status write suppressed", payload);
|
|
143866
144187
|
else log.session.debug("legacy status write suppressed", payload);
|
|
143867
|
-
if (updates.process_status === "error") {
|
|
144188
|
+
if (updates.process_status === "error" || updates.process_status === "stopped") {
|
|
143868
144189
|
noteSuppressedErrorReason(session2.claudeSessionId, {
|
|
143869
144190
|
reason: typeof reason === "string" ? reason : "unknown",
|
|
143870
144191
|
message: typeof updates.errorMessage === "string" ? updates.errorMessage : void 0,
|
|
@@ -151813,14 +152134,16 @@ function isLive(r8) {
|
|
|
151813
152134
|
function isRevivable(r8) {
|
|
151814
152135
|
return !!r8.outputFile || r8.consumedOffset !== void 0;
|
|
151815
152136
|
}
|
|
151816
|
-
var STANDBY_TTL_MS, MAX_LIVE_THREADS, IDLE_SWEEP_INTERVAL_MS, THREAD_IDLE_MS, SideThreadManager, sideThreadManager;
|
|
152137
|
+
var STANDBY_TTL_MS, WARMED_STANDBY_TTL_MS, MAX_LIVE_THREADS, IDLE_SWEEP_INTERVAL_MS, THREAD_IDLE_MS, SideThreadManager, sideThreadManager;
|
|
151817
152138
|
var init_side_thread_manager = __esm({
|
|
151818
152139
|
"src/core/sessions/side-thread-manager.ts"() {
|
|
151819
152140
|
"use strict";
|
|
151820
152141
|
init_logging();
|
|
151821
152142
|
init_side_thread_fork();
|
|
151822
152143
|
init_session_controls();
|
|
152144
|
+
init_side_thread_warmup();
|
|
151823
152145
|
STANDBY_TTL_MS = 12e4;
|
|
152146
|
+
WARMED_STANDBY_TTL_MS = 15 * 6e4;
|
|
151824
152147
|
MAX_LIVE_THREADS = 3;
|
|
151825
152148
|
IDLE_SWEEP_INTERVAL_MS = 5 * 6e4;
|
|
151826
152149
|
THREAD_IDLE_MS = 30 * 6e4;
|
|
@@ -151831,6 +152154,8 @@ var init_side_thread_manager = __esm({
|
|
|
151831
152154
|
* In-memory only: a server restart loses it, but the boot sweep archives
|
|
151832
152155
|
* every standby anyway, so a record can never outlive its map entry. */
|
|
151833
152156
|
standbyParentOffsets = /* @__PURE__ */ new Map();
|
|
152157
|
+
/** parentSid → standby sid that already ran its cache warm-up turn. */
|
|
152158
|
+
warmedStandbys = /* @__PURE__ */ new Map();
|
|
151834
152159
|
/** One in-flight standby/create op per parent — two concurrent asks must not
|
|
151835
152160
|
* both consume the same standby (they would collide on its lane rename). */
|
|
151836
152161
|
inFlight = /* @__PURE__ */ new Map();
|
|
@@ -151862,6 +152187,7 @@ var init_side_thread_manager = __esm({
|
|
|
151862
152187
|
for (const timer3 of this.standbyTimers.values()) clearTimeout(timer3);
|
|
151863
152188
|
this.standbyTimers.clear();
|
|
151864
152189
|
this.standbyParentOffsets.clear();
|
|
152190
|
+
this.warmedStandbys.clear();
|
|
151865
152191
|
this.inFlight.clear();
|
|
151866
152192
|
}
|
|
151867
152193
|
// ── Standby ────────────────────────────────────────────────────────────────
|
|
@@ -151877,10 +152203,12 @@ var init_side_thread_manager = __esm({
|
|
|
151877
152203
|
const existing = await this.findStandby(parentSid);
|
|
151878
152204
|
if (existing) {
|
|
151879
152205
|
if (await this.isStandbyUsable(parentSid, existing)) {
|
|
151880
|
-
this.
|
|
152206
|
+
const warmed = this.warmedStandbys.get(parentSid) === existing.claudeSessionId;
|
|
152207
|
+
this.armStandbyTtl(parentSid, existing.claudeSessionId, warmed ? WARMED_STANDBY_TTL_MS : STANDBY_TTL_MS);
|
|
151881
152208
|
return existing.claudeSessionId;
|
|
151882
152209
|
}
|
|
151883
152210
|
void this.retireSession(existing.claudeSessionId, "side_thread_standby_stale");
|
|
152211
|
+
this.warmedStandbys.delete(parentSid);
|
|
151884
152212
|
}
|
|
151885
152213
|
const parentOffset = parent.consumedOffset;
|
|
151886
152214
|
const { sessionId } = await forkSideThreadSession(parentSid, STANDBY_THREAD_ID);
|
|
@@ -151906,7 +152234,7 @@ var init_side_thread_manager = __esm({
|
|
|
151906
152234
|
if (!parent) return false;
|
|
151907
152235
|
return parent.consumedOffset === this.standbyParentOffsets.get(parentSid);
|
|
151908
152236
|
}
|
|
151909
|
-
armStandbyTtl(parentSid, sessionId) {
|
|
152237
|
+
armStandbyTtl(parentSid, sessionId, ttlMs = STANDBY_TTL_MS) {
|
|
151910
152238
|
const prev = this.standbyTimers.get(parentSid);
|
|
151911
152239
|
if (prev) clearTimeout(prev);
|
|
151912
152240
|
const timer3 = setTimeout(() => {
|
|
@@ -151916,13 +152244,41 @@ var init_side_thread_manager = __esm({
|
|
|
151916
152244
|
const record = await getSessionByClaudeId2(sessionId).catch(() => null);
|
|
151917
152245
|
if (!record || parseSideLaneKey(record.lane)?.threadId !== STANDBY_THREAD_ID) return;
|
|
151918
152246
|
this.standbyParentOffsets.delete(parentSid);
|
|
152247
|
+
this.warmedStandbys.delete(parentSid);
|
|
151919
152248
|
await this.retireSession(sessionId, "side_thread_standby_ttl");
|
|
151920
152249
|
}).catch(() => {
|
|
151921
152250
|
});
|
|
151922
|
-
},
|
|
152251
|
+
}, ttlMs);
|
|
151923
152252
|
timer3.unref?.();
|
|
151924
152253
|
this.standbyTimers.set(parentSid, timer3);
|
|
151925
152254
|
}
|
|
152255
|
+
/**
|
|
152256
|
+
* Run the standby's cache warm-up turn (see side-thread-warmup.ts). Called
|
|
152257
|
+
* when the user starts TYPING a new question: the fork's first API call pays
|
|
152258
|
+
* the full prefix write no matter what, so paying it while they type turns
|
|
152259
|
+
* the actual question into a cached follow-up. Idempotent per standby; a
|
|
152260
|
+
* missing or stale standby is a no-op (the ask path will fork fresh anyway).
|
|
152261
|
+
*/
|
|
152262
|
+
async warmStandby(parentSid) {
|
|
152263
|
+
return this.serialize(parentSid, async () => {
|
|
152264
|
+
const standby = await this.findStandby(parentSid);
|
|
152265
|
+
if (!standby) return { warmed: false, reason: "no_standby" };
|
|
152266
|
+
if (!await this.isStandbyUsable(parentSid, standby)) return { warmed: false, reason: "stale" };
|
|
152267
|
+
if (this.warmedStandbys.get(parentSid) === standby.claudeSessionId) {
|
|
152268
|
+
return { warmed: true, reason: "already_warm" };
|
|
152269
|
+
}
|
|
152270
|
+
const { sendMessageToSession: sendMessageToSession2 } = await Promise.resolve().then(() => (init_session_message_queue(), session_message_queue_exports));
|
|
152271
|
+
markWarmupTurnPending(standby.claudeSessionId);
|
|
152272
|
+
await sendMessageToSession2(standby.claudeSessionId, CACHE_WARMUP_MESSAGE, { source: "side-thread-warmup" });
|
|
152273
|
+
this.warmedStandbys.set(parentSid, standby.claudeSessionId);
|
|
152274
|
+
this.armStandbyTtl(parentSid, standby.claudeSessionId, WARMED_STANDBY_TTL_MS);
|
|
152275
|
+
log.session.info("side thread: standby cache warm-up sent", {
|
|
152276
|
+
parentSid,
|
|
152277
|
+
standbySid: standby.claudeSessionId
|
|
152278
|
+
});
|
|
152279
|
+
return { warmed: true };
|
|
152280
|
+
});
|
|
152281
|
+
}
|
|
151926
152282
|
// ── Threads ────────────────────────────────────────────────────────────────
|
|
151927
152283
|
/**
|
|
151928
152284
|
* Open a side thread on `parentSid` and ask `question` in it.
|
|
@@ -151933,6 +152289,10 @@ var init_side_thread_manager = __esm({
|
|
|
151933
152289
|
* question as its spawn's first turn instead — a send issued right after
|
|
151934
152290
|
* SESSION_START can lose that race and cold-`--resume` an id the CLI has never
|
|
151935
152291
|
* seen (see personal-ai-lane.ts LaneSession.created).
|
|
152292
|
+
*
|
|
152293
|
+
* `imageContext` (attachment paths for the CLI to Read) rides the MESSAGE only.
|
|
152294
|
+
* The stored entry keeps the plain question, so the chip label, the injected
|
|
152295
|
+
* transcript and a promoted task never carry the machine preamble.
|
|
151936
152296
|
*/
|
|
151937
152297
|
async createThread(parentSid, input) {
|
|
151938
152298
|
const question = input.question?.trim();
|
|
@@ -151940,8 +152300,11 @@ var init_side_thread_manager = __esm({
|
|
|
151940
152300
|
return this.serialize(parentSid, async () => {
|
|
151941
152301
|
const threadId = mintSideThreadId();
|
|
151942
152302
|
const title = input.title?.trim() || void 0;
|
|
152303
|
+
const message = input.imageContext ? `${input.imageContext}
|
|
152304
|
+
|
|
152305
|
+
${question}` : question;
|
|
151943
152306
|
const consumed = await this.consumeStandby(parentSid, threadId, title);
|
|
151944
|
-
const threadSessionId = consumed ?? (await forkSideThreadSession(parentSid, threadId, { message
|
|
152307
|
+
const threadSessionId = consumed ?? (await forkSideThreadSession(parentSid, threadId, { message, ...title ? { title } : {} })).sessionId;
|
|
151945
152308
|
let entry;
|
|
151946
152309
|
try {
|
|
151947
152310
|
const { addSideThread: addSideThread2 } = await Promise.resolve().then(() => (init_side_questions(), side_questions_exports));
|
|
@@ -151953,7 +152316,7 @@ var init_side_thread_manager = __esm({
|
|
|
151953
152316
|
});
|
|
151954
152317
|
if (consumed) {
|
|
151955
152318
|
const { sendMessageToSession: sendMessageToSession2 } = await Promise.resolve().then(() => (init_session_message_queue(), session_message_queue_exports));
|
|
151956
|
-
await sendMessageToSession2(threadSessionId,
|
|
152319
|
+
await sendMessageToSession2(threadSessionId, message, { source: "side-thread" });
|
|
151957
152320
|
}
|
|
151958
152321
|
} catch (err2) {
|
|
151959
152322
|
void this.retireSession(threadSessionId, "side_thread_create_failed");
|
|
@@ -151988,6 +152351,7 @@ var init_side_thread_manager = __esm({
|
|
|
151988
152351
|
if (!await this.isStandbyUsable(parentSid, standby)) {
|
|
151989
152352
|
void this.retireSession(standby.claudeSessionId, "side_thread_standby_stale");
|
|
151990
152353
|
this.standbyParentOffsets.delete(parentSid);
|
|
152354
|
+
this.warmedStandbys.delete(parentSid);
|
|
151991
152355
|
return null;
|
|
151992
152356
|
}
|
|
151993
152357
|
const timer3 = this.standbyTimers.get(parentSid);
|
|
@@ -151996,6 +152360,7 @@ var init_side_thread_manager = __esm({
|
|
|
151996
152360
|
this.standbyTimers.delete(parentSid);
|
|
151997
152361
|
}
|
|
151998
152362
|
this.standbyParentOffsets.delete(parentSid);
|
|
152363
|
+
this.warmedStandbys.delete(parentSid);
|
|
151999
152364
|
const lane = sideThreadLaneKey(parentSid, threadId);
|
|
152000
152365
|
const { updateSessionRecord: updateSessionRecord2 } = await Promise.resolve().then(() => (init_session_tracker(), session_tracker_exports));
|
|
152001
152366
|
await updateSessionRecord2(standby.claudeSessionId, {
|
|
@@ -152182,16 +152547,30 @@ async function promoteSideThread(parentSid, threadId, opts) {
|
|
|
152182
152547
|
const threadRecord = await getSessionByClaudeId2(sessionId);
|
|
152183
152548
|
if (!threadRecord) throw new SessionControlError("Side thread session not found", 404);
|
|
152184
152549
|
const parentRecord = await getSessionByClaudeId2(parentSid);
|
|
152185
|
-
const
|
|
152186
|
-
const
|
|
152550
|
+
const siblingOfTaskId = parentRecord?.taskId?.trim() || void 0;
|
|
152551
|
+
const threadLabel = opts?.title?.trim() || entry.title?.trim() || void 0;
|
|
152187
152552
|
const { addTask: addTask2, linkSession: linkSession2, linkSessionSlot: linkSessionSlot2 } = await Promise.resolve().then(() => (init_task_manager(), task_manager_exports));
|
|
152188
|
-
|
|
152189
|
-
|
|
152190
|
-
|
|
152191
|
-
|
|
152192
|
-
|
|
152193
|
-
|
|
152194
|
-
|
|
152553
|
+
let groupId;
|
|
152554
|
+
let task;
|
|
152555
|
+
if (siblingOfTaskId) {
|
|
152556
|
+
const created = await createForkSiblingTask(siblingOfTaskId, {
|
|
152557
|
+
// A known label short-circuits the background refine; without one the
|
|
152558
|
+
// thread's first question is what gets summarized (same as a fork's message).
|
|
152559
|
+
titlePrefix: threadLabel,
|
|
152560
|
+
prompt: entry.question,
|
|
152561
|
+
description: entry.question,
|
|
152562
|
+
source: "side-thread-promote"
|
|
152563
|
+
});
|
|
152564
|
+
task = created.task;
|
|
152565
|
+
groupId = created.groupId;
|
|
152566
|
+
} else {
|
|
152567
|
+
({ task } = await addTask2({
|
|
152568
|
+
title: threadLabel || entry.question,
|
|
152569
|
+
description: entry.question,
|
|
152570
|
+
// A person clicked "promote" — same board default as any hand-made task.
|
|
152571
|
+
pinned: true
|
|
152572
|
+
}));
|
|
152573
|
+
}
|
|
152195
152574
|
const stamped = await markThreadPromoted2(parentSid, threadId, task.id);
|
|
152196
152575
|
if (!stamped) {
|
|
152197
152576
|
log.session.warn("side thread promote: entry vanished before stamp \u2014 promote proceeds, 409 guard is unarmed", {
|
|
@@ -152205,7 +152584,7 @@ async function promoteSideThread(parentSid, threadId, opts) {
|
|
|
152205
152584
|
project: task.project ?? "",
|
|
152206
152585
|
// Clearing the lane un-hides the session (see the file header).
|
|
152207
152586
|
lane: void 0,
|
|
152208
|
-
title
|
|
152587
|
+
title: task.title
|
|
152209
152588
|
});
|
|
152210
152589
|
try {
|
|
152211
152590
|
const { sessionRunner: sessionRunner2 } = await Promise.resolve().then(() => (init_claude_code_session(), claude_code_session_exports));
|
|
@@ -152224,10 +152603,18 @@ async function promoteSideThread(parentSid, threadId, opts) {
|
|
|
152224
152603
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
152225
152604
|
});
|
|
152226
152605
|
}
|
|
152227
|
-
log.session.info("side thread: promoted to task", {
|
|
152606
|
+
log.session.info("side thread: promoted to task", {
|
|
152607
|
+
parentSid,
|
|
152608
|
+
threadId,
|
|
152609
|
+
sessionId,
|
|
152610
|
+
taskId: task.id,
|
|
152611
|
+
siblingOfTaskId,
|
|
152612
|
+
groupId
|
|
152613
|
+
});
|
|
152228
152614
|
return {
|
|
152229
152615
|
taskId: task.id,
|
|
152230
|
-
...
|
|
152616
|
+
...siblingOfTaskId ? { siblingOfTaskId } : {},
|
|
152617
|
+
...groupId ? { groupId } : {},
|
|
152231
152618
|
sessionId
|
|
152232
152619
|
};
|
|
152233
152620
|
}
|
|
@@ -152242,7 +152629,7 @@ var init_side_thread_promote = __esm({
|
|
|
152242
152629
|
|
|
152243
152630
|
// src/web/routes/sessions.ts
|
|
152244
152631
|
import { Router as Router16 } from "express";
|
|
152245
|
-
import { randomUUID as
|
|
152632
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
152246
152633
|
import os27 from "os";
|
|
152247
152634
|
import path142 from "path";
|
|
152248
152635
|
function condenseStaleReason(msg) {
|
|
@@ -152588,7 +152975,7 @@ var init_sessions = __esm({
|
|
|
152588
152975
|
}
|
|
152589
152976
|
const isNativeEngine = !isAcpEngine(engine);
|
|
152590
152977
|
const clientSessionId = typeof req.body.sessionId === "string" && UUID_RE3.test(req.body.sessionId) ? req.body.sessionId : void 0;
|
|
152591
|
-
const preassignedSessionId = isNativeEngine ? clientSessionId ??
|
|
152978
|
+
const preassignedSessionId = isNativeEngine ? clientSessionId ?? randomUUID11() : void 0;
|
|
152592
152979
|
const updatedTask = await quickStartSession({
|
|
152593
152980
|
message: sessionMessage,
|
|
152594
152981
|
messagePrefix,
|
|
@@ -153563,18 +153950,38 @@ var init_sessions = __esm({
|
|
|
153563
153950
|
});
|
|
153564
153951
|
}
|
|
153565
153952
|
});
|
|
153953
|
+
sessionsRouter.post("/:sessionId/side-threads/standby/warm", async (req, res) => {
|
|
153954
|
+
if (refuseSideThreadsOnReplica(res)) return;
|
|
153955
|
+
const sessionId = String(req.params.sessionId);
|
|
153956
|
+
try {
|
|
153957
|
+
const { sideThreadManager: sideThreadManager2 } = await Promise.resolve().then(() => (init_side_thread_manager(), side_thread_manager_exports));
|
|
153958
|
+
res.json(await sideThreadManager2.warmStandby(sessionId));
|
|
153959
|
+
} catch (err2) {
|
|
153960
|
+
log.web.warn("side thread standby warm-up failed", {
|
|
153961
|
+
sessionId,
|
|
153962
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
153963
|
+
});
|
|
153964
|
+
res.json({ warmed: false, reason: "error" });
|
|
153965
|
+
}
|
|
153966
|
+
});
|
|
153566
153967
|
sessionsRouter.post("/:sessionId/side-threads", async (req, res, next) => {
|
|
153567
153968
|
if (refuseSideThreadsOnReplica(res)) return;
|
|
153568
153969
|
try {
|
|
153569
|
-
const { question, title } = req.body ?? {};
|
|
153970
|
+
const { question, title, images } = req.body ?? {};
|
|
153570
153971
|
if (!question || typeof question !== "string" || !question.trim()) {
|
|
153571
153972
|
res.status(400).json({ error: "question (non-empty string) is required" });
|
|
153572
153973
|
return;
|
|
153573
153974
|
}
|
|
153975
|
+
let imageContext = "";
|
|
153976
|
+
if (images && images.length > 0) {
|
|
153977
|
+
const processed = await processAndSaveImages(images);
|
|
153978
|
+
if (processed) imageContext = buildSessionImageContext(processed.savedImages);
|
|
153979
|
+
}
|
|
153574
153980
|
const { sideThreadManager: sideThreadManager2 } = await Promise.resolve().then(() => (init_side_thread_manager(), side_thread_manager_exports));
|
|
153575
153981
|
const thread = await sideThreadManager2.createThread(String(req.params.sessionId), {
|
|
153576
153982
|
question,
|
|
153577
|
-
...typeof title === "string" && title.trim() ? { title } : {}
|
|
153983
|
+
...typeof title === "string" && title.trim() ? { title } : {},
|
|
153984
|
+
...imageContext ? { imageContext } : {}
|
|
153578
153985
|
});
|
|
153579
153986
|
res.json({ thread });
|
|
153580
153987
|
} catch (err2) {
|
|
@@ -153593,7 +154000,8 @@ var init_sessions = __esm({
|
|
|
153593
154000
|
);
|
|
153594
154001
|
res.json({
|
|
153595
154002
|
taskId: result.taskId,
|
|
153596
|
-
...result.
|
|
154003
|
+
...result.siblingOfTaskId ? { siblingOfTaskId: result.siblingOfTaskId } : {},
|
|
154004
|
+
...result.groupId ? { groupId: result.groupId } : {},
|
|
153597
154005
|
sessionId: result.sessionId
|
|
153598
154006
|
});
|
|
153599
154007
|
} catch (err2) {
|
|
@@ -154288,7 +154696,7 @@ var micro_claude_exports = {};
|
|
|
154288
154696
|
__export(micro_claude_exports, {
|
|
154289
154697
|
runMicroClaude: () => runMicroClaude
|
|
154290
154698
|
});
|
|
154291
|
-
import { randomUUID as
|
|
154699
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
154292
154700
|
async function runMicroClaude(opts) {
|
|
154293
154701
|
if (opts.warm) {
|
|
154294
154702
|
const run10 = await runWarmMicroClaude({
|
|
@@ -154297,7 +154705,7 @@ async function runMicroClaude(opts) {
|
|
|
154297
154705
|
tools: opts.tools ?? [],
|
|
154298
154706
|
prompt: opts.prompt,
|
|
154299
154707
|
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
154300
|
-
toolUseId: opts.toolUseId ?? `micro-claude-${
|
|
154708
|
+
toolUseId: opts.toolUseId ?? `micro-claude-${randomUUID12()}`,
|
|
154301
154709
|
...opts.onBlock ? { onBlock: opts.onBlock } : {}
|
|
154302
154710
|
});
|
|
154303
154711
|
return { response: run10.response, costUsd: run10.costUsd, durationMs: run10.durationMs };
|
|
@@ -154307,7 +154715,7 @@ async function runMicroClaude(opts) {
|
|
|
154307
154715
|
systemPrompt: opts.system,
|
|
154308
154716
|
model: opts.model ?? DEFAULT_MODEL2,
|
|
154309
154717
|
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2,
|
|
154310
|
-
toolUseId: opts.toolUseId ?? `micro-claude-${
|
|
154718
|
+
toolUseId: opts.toolUseId ?? `micro-claude-${randomUUID12()}`,
|
|
154311
154719
|
slim: true,
|
|
154312
154720
|
thinking: opts.thinking ?? false,
|
|
154313
154721
|
...opts.tools && opts.tools.length > 0 ? { tools: opts.tools } : {},
|
|
@@ -154337,7 +154745,7 @@ __export(micro_agent_exports, {
|
|
|
154337
154745
|
async function resolveTierModel(tier) {
|
|
154338
154746
|
const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
154339
154747
|
const config = await getConfig2();
|
|
154340
|
-
const provider = config
|
|
154748
|
+
const provider = resolveMainProviderName(config);
|
|
154341
154749
|
const { MODEL_CATALOG: MODEL_CATALOG3 } = await Promise.resolve().then(() => (init_model_catalog(), model_catalog_exports));
|
|
154342
154750
|
const entry = MODEL_CATALOG3[provider]?.find((m6) => {
|
|
154343
154751
|
const id = m6.id.toLowerCase();
|
|
@@ -154406,6 +154814,7 @@ var DEFAULT_TIER, DEFAULT_MAX_TOKENS2, DEFAULT_MAX_ROUNDS, DEFAULT_TIMEOUT_MS3;
|
|
|
154406
154814
|
var init_micro_agent = __esm({
|
|
154407
154815
|
"src/agent/micro-agent.ts"() {
|
|
154408
154816
|
"use strict";
|
|
154817
|
+
init_default_provider();
|
|
154409
154818
|
DEFAULT_TIER = "sonnet";
|
|
154410
154819
|
DEFAULT_MAX_TOKENS2 = 2e3;
|
|
154411
154820
|
DEFAULT_MAX_ROUNDS = 3;
|
|
@@ -154421,7 +154830,7 @@ __export(task_search_agent_exports, {
|
|
|
154421
154830
|
prewarmAgentSearchChild: () => prewarmAgentSearchChild,
|
|
154422
154831
|
runTaskSearchAgent: () => runTaskSearchAgent
|
|
154423
154832
|
});
|
|
154424
|
-
import { randomUUID as
|
|
154833
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
154425
154834
|
function useCliEngine() {
|
|
154426
154835
|
return process.env.WALNUT_AGENT_SEARCH_ENGINE !== "inprocess";
|
|
154427
154836
|
}
|
|
@@ -154501,7 +154910,7 @@ async function claudeCliEngine(userPrompt, options) {
|
|
|
154501
154910
|
model: options.model,
|
|
154502
154911
|
timeoutMs: options.timeoutMs,
|
|
154503
154912
|
tools: ["Bash"],
|
|
154504
|
-
toolUseId: `task-search-${
|
|
154913
|
+
toolUseId: `task-search-${randomUUID13()}`,
|
|
154505
154914
|
warm: true,
|
|
154506
154915
|
onBlock
|
|
154507
154916
|
});
|
|
@@ -155579,10 +155988,11 @@ async function buildProvidersPayload() {
|
|
|
155579
155988
|
providers[name] = {
|
|
155580
155989
|
api: prov.api,
|
|
155581
155990
|
base_url: prov.base_url,
|
|
155582
|
-
status: caps.
|
|
155991
|
+
status: caps.installed ? "ready" : "no_key",
|
|
155583
155992
|
auto_detected: !explicitNames.has(name),
|
|
155584
155993
|
models: getModelsForProvider(name, prov.models),
|
|
155585
|
-
credential_source: caps.
|
|
155994
|
+
credential_source: caps.installed ? `cli_${caps.auth.mode}` : "cli_not_installed",
|
|
155995
|
+
credential_detail: caps.installed ? caps.auth.label : void 0
|
|
155586
155996
|
};
|
|
155587
155997
|
continue;
|
|
155588
155998
|
}
|
|
@@ -155641,10 +156051,11 @@ async function buildProvidersPayload() {
|
|
|
155641
156051
|
const caps = detectClaudeCli2();
|
|
155642
156052
|
providers[name] = {
|
|
155643
156053
|
api: template.api,
|
|
155644
|
-
status: caps.
|
|
156054
|
+
status: caps.installed ? "ready" : "no_key",
|
|
155645
156055
|
auto_detected: false,
|
|
155646
156056
|
models: getModelsForProvider(name),
|
|
155647
|
-
credential_source: caps.
|
|
156057
|
+
credential_source: caps.installed ? `cli_${caps.auth.mode}` : "cli_not_installed",
|
|
156058
|
+
credential_detail: caps.installed ? caps.auth.label : void 0
|
|
155648
156059
|
};
|
|
155649
156060
|
continue;
|
|
155650
156061
|
}
|
|
@@ -155800,7 +156211,7 @@ var init_config11 = __esm({
|
|
|
155800
156211
|
const {
|
|
155801
156212
|
traceCredentialResolution: traceCredentialResolution2,
|
|
155802
156213
|
resolveCredentials: resolveCredentials2,
|
|
155803
|
-
readClaudeSettingsEnv:
|
|
156214
|
+
readClaudeSettingsEnv: readClaudeSettingsEnv3,
|
|
155804
156215
|
readClaudeCredentialExport: readClaudeCredentialExport2
|
|
155805
156216
|
} = await Promise.resolve().then(() => (init_credential_resolver(), credential_resolver_exports));
|
|
155806
156217
|
const fs114 = await import("fs");
|
|
@@ -155816,7 +156227,7 @@ var init_config11 = __esm({
|
|
|
155816
156227
|
};
|
|
155817
156228
|
const trace = traceCredentialResolution2({
|
|
155818
156229
|
config,
|
|
155819
|
-
claudeEnv:
|
|
156230
|
+
claudeEnv: readClaudeSettingsEnv3(),
|
|
155820
156231
|
processEnv: process.env,
|
|
155821
156232
|
awsFiles: {
|
|
155822
156233
|
credentials: exists3(path185.join(home, ".aws", "credentials")),
|
|
@@ -173146,7 +173557,7 @@ __export(task_start_exports, {
|
|
|
173146
173557
|
SessionExistsError: () => SessionExistsError,
|
|
173147
173558
|
startSessionForTask: () => startSessionForTask
|
|
173148
173559
|
});
|
|
173149
|
-
import { randomUUID as
|
|
173560
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
173150
173561
|
async function startSessionForTask(params) {
|
|
173151
173562
|
const { taskIdPrefix, source } = params;
|
|
173152
173563
|
let task;
|
|
@@ -173197,7 +173608,7 @@ async function startSessionForTask(params) {
|
|
|
173197
173608
|
}
|
|
173198
173609
|
let message = params.message?.trim() || `Working on task: ${task.title}`;
|
|
173199
173610
|
const engine = normalizeEngine(params.engine);
|
|
173200
|
-
const preassignedSessionId = engineCaps(engine).idProvisioning === "provider-issued" ? void 0 :
|
|
173611
|
+
const preassignedSessionId = engineCaps(engine).idProvisioning === "provider-issued" ? void 0 : randomUUID14();
|
|
173201
173612
|
let requestId;
|
|
173202
173613
|
if (params.expectReply) {
|
|
173203
173614
|
const { resolveCaller: resolveCaller2 } = await Promise.resolve().then(() => (init_session_send_core(), session_send_core_exports));
|
|
@@ -184525,7 +184936,7 @@ async function generateLedgerDesc(task, timeoutMs = 15e3) {
|
|
|
184525
184936
|
try {
|
|
184526
184937
|
const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
184527
184938
|
const config = await getConfig2();
|
|
184528
|
-
const providerName = config
|
|
184939
|
+
const providerName = resolveMainProviderName(config);
|
|
184529
184940
|
const model = cheapModelFor(providerName);
|
|
184530
184941
|
const detail = (task.description ?? "").trim().slice(0, 800);
|
|
184531
184942
|
const project = (task.project ?? "").trim();
|
|
@@ -184584,6 +184995,7 @@ var init_task_ledger_desc = __esm({
|
|
|
184584
184995
|
"use strict";
|
|
184585
184996
|
init_model();
|
|
184586
184997
|
init_model_catalog();
|
|
184998
|
+
init_default_provider();
|
|
184587
184999
|
init_logging();
|
|
184588
185000
|
init_task_manager();
|
|
184589
185001
|
MAX_DESC_LEN = 120;
|
|
@@ -184607,6 +185019,10 @@ function diffStreamedVsPersisted(streamedTextMsgIds, persistedMsgIds) {
|
|
|
184607
185019
|
}
|
|
184608
185020
|
function armStreamConvergenceCheck(sessionId, streamedTextMsgIds) {
|
|
184609
185021
|
if (streamedTextMsgIds.length === 0) return;
|
|
185022
|
+
if (consumeWarmupTurn(sessionId)) {
|
|
185023
|
+
log.obs.debug("stream-convergence: skipped hidden warm-up turn", { sessionId });
|
|
185024
|
+
return;
|
|
185025
|
+
}
|
|
184610
185026
|
const timer3 = setTimeout(() => {
|
|
184611
185027
|
void runCheck(sessionId, streamedTextMsgIds).catch((err2) => {
|
|
184612
185028
|
log.obs.warn("stream-convergence check failed", {
|
|
@@ -184672,6 +185088,7 @@ var init_stream_convergence = __esm({
|
|
|
184672
185088
|
"src/core/observability/stream-convergence.ts"() {
|
|
184673
185089
|
"use strict";
|
|
184674
185090
|
init_logging();
|
|
185091
|
+
init_side_thread_warmup();
|
|
184675
185092
|
CHECK_DELAY_MS = 15e3;
|
|
184676
185093
|
INCIDENT_DEDUPE_MS = 30 * 6e4;
|
|
184677
185094
|
lastIncidentAt = /* @__PURE__ */ new Map();
|
|
@@ -184922,12 +185339,17 @@ function getSystemHealth() {
|
|
|
184922
185339
|
}
|
|
184923
185340
|
async function refreshSystemHealth() {
|
|
184924
185341
|
systemHealth.claudeCliAvailable = checkClaudeCliAvailable();
|
|
184925
|
-
|
|
185342
|
+
applyCredentialHealth(await resolveCredentialHealth());
|
|
185343
|
+
broadcastEvent("system:health", systemHealth);
|
|
185344
|
+
return systemHealth;
|
|
185345
|
+
}
|
|
185346
|
+
function applyCredentialHealth(cred) {
|
|
184926
185347
|
systemHealth.hasReadyProvider = cred.hasReadyProvider;
|
|
184927
185348
|
systemHealth.credentialSource = cred.source;
|
|
184928
185349
|
systemHealth.credentialDetail = cred.detail;
|
|
184929
|
-
|
|
184930
|
-
|
|
185350
|
+
systemHealth.mainProvider = cred.mainProvider;
|
|
185351
|
+
systemHealth.mainProviderImplicit = cred.mainProviderImplicit;
|
|
185352
|
+
systemHealth.claudeCliAuth = cred.claudeCliAuth;
|
|
184931
185353
|
}
|
|
184932
185354
|
function checkClaudeCliAvailable() {
|
|
184933
185355
|
return isClaudeCliInstalled();
|
|
@@ -184937,27 +185359,36 @@ async function resolveCredentialHealth() {
|
|
|
184937
185359
|
const { getConfig: getConfig2 } = await Promise.resolve().then(() => (init_config_manager(), config_manager_exports));
|
|
184938
185360
|
const { resolveCredentials: resolveCredentials2 } = await Promise.resolve().then(() => (init_credential_resolver(), credential_resolver_exports));
|
|
184939
185361
|
const { buildProviderMap: buildProviderMap2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
185362
|
+
const { resolveMainProviderName: resolveMainProviderName2, mainProviderIsImplicit: mainProviderIsImplicit2 } = await Promise.resolve().then(() => (init_default_provider(), default_provider_exports));
|
|
185363
|
+
const { detectClaudeCli: detectClaudeCli2 } = await Promise.resolve().then(() => (init_claude_cli_detect(), claude_cli_detect_exports));
|
|
184940
185364
|
const config = await getConfig2();
|
|
185365
|
+
const cli = detectClaudeCli2();
|
|
185366
|
+
const mainProvider = resolveMainProviderName2(config, cli.installed);
|
|
185367
|
+
const base = {
|
|
185368
|
+
mainProvider,
|
|
185369
|
+
mainProviderImplicit: mainProviderIsImplicit2(config),
|
|
185370
|
+
claudeCliAuth: cli.installed ? cli.auth.label : void 0
|
|
185371
|
+
};
|
|
185372
|
+
if (mainProvider === "claude_cli") {
|
|
185373
|
+
return cli.installed ? { ...base, hasReadyProvider: true, source: "config", detail: `claude-cli (${cli.auth.label})` } : { ...base, hasReadyProvider: false, source: "none" };
|
|
185374
|
+
}
|
|
184941
185375
|
const cred = resolveCredentials2(config);
|
|
184942
185376
|
if (cred.source !== "none") {
|
|
184943
|
-
return { hasReadyProvider: true, source: cred.source, detail: cred.detail };
|
|
185377
|
+
return { ...base, hasReadyProvider: true, source: cred.source, detail: cred.detail };
|
|
184944
185378
|
}
|
|
184945
185379
|
const providers = buildProviderMap2(config.providers, config);
|
|
184946
185380
|
for (const [, prov] of Object.entries(providers)) {
|
|
184947
185381
|
if (prov.api === "bedrock" || prov.api === "ollama") continue;
|
|
184948
185382
|
if (prov.api === "claude-cli") {
|
|
184949
|
-
|
|
184950
|
-
if (detectClaudeCli2().subscriptionReady) {
|
|
184951
|
-
return { hasReadyProvider: true, source: "config", detail: "claude-cli (subscription)" };
|
|
184952
|
-
}
|
|
185383
|
+
if (cli.installed) return { ...base, hasReadyProvider: true, source: "config", detail: `claude-cli (${cli.auth.label})` };
|
|
184953
185384
|
continue;
|
|
184954
185385
|
}
|
|
184955
185386
|
const implemented = prov.api === "anthropic-messages" || prov.api === "openai-chat" || prov.api === "google-generative-ai";
|
|
184956
185387
|
if (implemented && (prov.api_key || prov.bearer_token)) {
|
|
184957
|
-
return { hasReadyProvider: true, source: "config", detail: prov.api };
|
|
185388
|
+
return { ...base, hasReadyProvider: true, source: "config", detail: prov.api };
|
|
184958
185389
|
}
|
|
184959
185390
|
}
|
|
184960
|
-
return { hasReadyProvider: false, source: "none" };
|
|
185391
|
+
return { ...base, hasReadyProvider: false, source: "none" };
|
|
184961
185392
|
} catch {
|
|
184962
185393
|
return { hasReadyProvider: false, source: "none" };
|
|
184963
185394
|
}
|
|
@@ -184996,11 +185427,11 @@ async function startServer(options = {}) {
|
|
|
184996
185427
|
}
|
|
184997
185428
|
{
|
|
184998
185429
|
const cred = await resolveCredentialHealth();
|
|
184999
|
-
|
|
185000
|
-
systemHealth.credentialSource = cred.source;
|
|
185001
|
-
systemHealth.credentialDetail = cred.detail;
|
|
185430
|
+
applyCredentialHealth(cred);
|
|
185002
185431
|
if (!systemHealth.hasReadyProvider) {
|
|
185003
185432
|
log.web.warn("No AI provider configured \u2014 configure one in Settings");
|
|
185433
|
+
} else if (cred.mainProvider === "claude_cli") {
|
|
185434
|
+
log.web.info("AI provider: Claude Code", { auth: cred.claudeCliAuth, implicit: cred.mainProviderImplicit });
|
|
185004
185435
|
} else if (cred.source && cred.source !== "config") {
|
|
185005
185436
|
log.web.info("Auto-detected Bedrock credential", { source: cred.source, detail: cred.detail });
|
|
185006
185437
|
}
|
|
@@ -186280,10 +186711,9 @@ async function startServer(options = {}) {
|
|
|
186280
186711
|
const prev = systemHealth.hasReadyProvider;
|
|
186281
186712
|
const prevSource = systemHealth.credentialSource;
|
|
186282
186713
|
const cred = await resolveCredentialHealth();
|
|
186283
|
-
|
|
186284
|
-
|
|
186285
|
-
|
|
186286
|
-
if (cred.hasReadyProvider !== prev || cred.source !== prevSource) {
|
|
186714
|
+
const prevProvider = systemHealth.mainProvider;
|
|
186715
|
+
applyCredentialHealth(cred);
|
|
186716
|
+
if (cred.hasReadyProvider !== prev || cred.source !== prevSource || cred.mainProvider !== prevProvider) {
|
|
186287
186717
|
broadcastEvent("system:health", systemHealth);
|
|
186288
186718
|
}
|
|
186289
186719
|
} catch {
|