@chanlerdev/scorel 0.0.4 → 0.0.6
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 +37 -1
- package/dist/index.js +676 -198
- package/dist/index.js.map +4 -4
- package/docs/CHANGELOG.md +73 -0
- package/docs/ROADMAP.md +11 -0
- package/docs/SHIP.md +10 -5
- package/docs/spec/events.md +17 -1
- package/docs/spec/session.md +12 -0
- package/docs/spec/ship/S0064-gui-product-intent-and-boundary.md +1 -1
- package/docs/spec/ship/S0073-provider-model-profile-contract.md +8 -1
- package/docs/spec/ship/S0103-daemon-lifecycle-and-settings-resilience.md +61 -0
- package/docs/spec/ship/S0104-tool-result-artifacts.md +64 -0
- package/docs/spec/ship/S0105-cli-update-and-gui-release.md +128 -0
- package/docs/spec/ship/S0106-snip-context-control.md +113 -0
- package/docs/spec/ship/S0107-system-reminder-unification.md +112 -0
- package/docs/spec/ship/S0108-gui-bundled-cli-runtime.md +108 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1746,7 +1746,8 @@ import { mkdir as mkdir2, readFile as readFile4, rename as rename2, rm, stat as
|
|
|
1746
1746
|
import { userInfo } from "node:os";
|
|
1747
1747
|
import { basename as basename2, dirname as dirname3, extname, isAbsolute, relative, resolve } from "node:path";
|
|
1748
1748
|
import { promisify } from "node:util";
|
|
1749
|
-
|
|
1749
|
+
import { Type } from "@mariozechner/pi-ai";
|
|
1750
|
+
var execFileAsync, DEFAULT_SEARCH_LIMIT, DEFAULT_GREP_LIMIT, DEFAULT_READ_LIMIT, DEFAULT_CONTEXT_WINDOW, READ_TOKEN_BUDGET_RATIO, FULL_READ_TOKEN_BUDGET_RATIO, createCodingTools, parseReadArgs, parseWriteArgs, parseEditArgs, parseBashArgs, parseGlobArgs, parseGrepArgs, parseTodoWriteArgs, parseTodoItem, expectRecord, expectPath, expectString, optionalString, optionalNumber, optionalBoolean, snapshotFile, sameSnapshot, exists, isWithin, linesOf, IMAGE_EXTENSIONS, DOCUMENT_EXTENSIONS, BINARY_EXTENSIONS, assertReadableFileKind, assertTextBuffer, selectCompleteLinesWithinBudget, estimateTokens, renderReadLines, readTokenBudget, completeRanges, hasCompleteCoverage, mergeRanges, countOccurrences, atomicWriteFile, bashResult, renderFullBashResult, writeBashArtifact, safeArtifactSegment, projectBashStreams, projectOutputStream, resolveDefaultShell, resolveRtkCommand, rtkRewriteResult, executableRewriteCommand, readRtkGain, rtkSavedTokenDelta, withRtkSavings, nonNegativeInteger, isRecord3, shellQuote, shellCommandArgs, userShell, truncate, sliceBytes, textResult, byteLength, isTimeoutError, isExecError, runRipgrep, splitOutput, vcsExcludes, grepArgs, splitGlobPatterns, paginate, toWorkspaceRelative, relativizeGrepLine, relativizeCountLine, sortPathsByMtime, formatPaginatedText, formatLimitSuffix, parseCountLines;
|
|
1750
1751
|
var init_coding_tools = __esm({
|
|
1751
1752
|
"packages/core/src/tools/coding-tools.ts"() {
|
|
1752
1753
|
"use strict";
|
|
@@ -1799,6 +1800,12 @@ var init_coding_tools = __esm({
|
|
|
1799
1800
|
defineTool({
|
|
1800
1801
|
name: "Read",
|
|
1801
1802
|
description: "Read a text file from the workspace. Long reads are truncated by complete lines; accumulated coverage unlocks Write/Edit.",
|
|
1803
|
+
parameters: Type.Object({
|
|
1804
|
+
file_path: Type.String(),
|
|
1805
|
+
offset: Type.Optional(Type.Number()),
|
|
1806
|
+
limit: Type.Optional(Type.Number()),
|
|
1807
|
+
full: Type.Optional(Type.Boolean())
|
|
1808
|
+
}),
|
|
1802
1809
|
execute: async (_toolCallId, args) => {
|
|
1803
1810
|
const input = parseReadArgs(args);
|
|
1804
1811
|
if (input.full && (input.offset !== void 0 || input.limit !== void 0)) {
|
|
@@ -1858,6 +1865,10 @@ var init_coding_tools = __esm({
|
|
|
1858
1865
|
defineTool({
|
|
1859
1866
|
name: "Write",
|
|
1860
1867
|
description: "Create a new file or fully overwrite an existing file. Existing files require complete read coverage of the current file.",
|
|
1868
|
+
parameters: Type.Object({
|
|
1869
|
+
file_path: Type.String(),
|
|
1870
|
+
content: Type.String()
|
|
1871
|
+
}),
|
|
1861
1872
|
execute: async (_toolCallId, args) => {
|
|
1862
1873
|
const input = parseWriteArgs(args);
|
|
1863
1874
|
const path = resolveWorkspacePath(input.path);
|
|
@@ -1879,6 +1890,12 @@ var init_coding_tools = __esm({
|
|
|
1879
1890
|
defineTool({
|
|
1880
1891
|
name: "Edit",
|
|
1881
1892
|
description: "Perform an exact string replacement in an existing file. Requires complete read coverage and a unique old_string unless replace_all is true.",
|
|
1893
|
+
parameters: Type.Object({
|
|
1894
|
+
file_path: Type.String(),
|
|
1895
|
+
old_string: Type.String(),
|
|
1896
|
+
new_string: Type.String(),
|
|
1897
|
+
replace_all: Type.Optional(Type.Boolean())
|
|
1898
|
+
}),
|
|
1882
1899
|
execute: async (_toolCallId, args) => {
|
|
1883
1900
|
const input = parseEditArgs(args);
|
|
1884
1901
|
const path = resolveWorkspacePath(input.path);
|
|
@@ -1914,7 +1931,14 @@ String: ${input.old_string}`
|
|
|
1914
1931
|
defineTool({
|
|
1915
1932
|
name: "Bash",
|
|
1916
1933
|
description: "Execute a shell command in the workspace with timeout and output truncation.",
|
|
1917
|
-
|
|
1934
|
+
parameters: Type.Object({
|
|
1935
|
+
command: Type.String(),
|
|
1936
|
+
cwd: Type.Optional(Type.String()),
|
|
1937
|
+
timeout: Type.Optional(Type.Number()),
|
|
1938
|
+
description: Type.Optional(Type.String()),
|
|
1939
|
+
maxOutputBytes: Type.Optional(Type.Number())
|
|
1940
|
+
}),
|
|
1941
|
+
execute: async (toolCallId, args, signal) => {
|
|
1918
1942
|
const input = parseBashArgs(args);
|
|
1919
1943
|
const commandCwd = input.cwd ? resolveWorkspacePath(input.cwd) : root;
|
|
1920
1944
|
const timeoutMs = Math.min(input.timeoutMs ?? defaultTimeoutMs, maxTimeoutMs);
|
|
@@ -1940,12 +1964,14 @@ String: ${input.old_string}`
|
|
|
1940
1964
|
maxBuffer: Math.max(outputLimit * 4, 1024 * 1024)
|
|
1941
1965
|
});
|
|
1942
1966
|
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1943
|
-
return bashResult({
|
|
1967
|
+
return await bashResult({
|
|
1944
1968
|
exitCode: 0,
|
|
1945
1969
|
stdout: result.stdout,
|
|
1946
1970
|
stderr: result.stderr,
|
|
1947
1971
|
cwd: commandCwd,
|
|
1948
1972
|
outputLimit,
|
|
1973
|
+
artifactDir: options.toolResultArtifacts?.dir,
|
|
1974
|
+
toolCallId,
|
|
1949
1975
|
shell: defaultShell,
|
|
1950
1976
|
command,
|
|
1951
1977
|
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
@@ -1956,12 +1982,14 @@ String: ${input.old_string}`
|
|
|
1956
1982
|
}
|
|
1957
1983
|
if (isExecError(cause)) {
|
|
1958
1984
|
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1959
|
-
return bashResult({
|
|
1985
|
+
return await bashResult({
|
|
1960
1986
|
exitCode: typeof cause.code === "number" ? cause.code : 1,
|
|
1961
1987
|
stdout: String(cause.stdout ?? ""),
|
|
1962
1988
|
stderr: String(cause.stderr ?? cause.message),
|
|
1963
1989
|
cwd: commandCwd,
|
|
1964
1990
|
outputLimit,
|
|
1991
|
+
artifactDir: options.toolResultArtifacts?.dir,
|
|
1992
|
+
toolCallId,
|
|
1965
1993
|
shell: defaultShell,
|
|
1966
1994
|
command,
|
|
1967
1995
|
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
@@ -1974,6 +2002,12 @@ String: ${input.old_string}`
|
|
|
1974
2002
|
defineTool({
|
|
1975
2003
|
name: "Glob",
|
|
1976
2004
|
description: "Find files by glob pattern using ripgrep file discovery.",
|
|
2005
|
+
parameters: Type.Object({
|
|
2006
|
+
pattern: Type.String(),
|
|
2007
|
+
path: Type.Optional(Type.String()),
|
|
2008
|
+
head_limit: Type.Optional(Type.Number()),
|
|
2009
|
+
offset: Type.Optional(Type.Number())
|
|
2010
|
+
}),
|
|
1977
2011
|
execute: async (_toolCallId, args, signal) => {
|
|
1978
2012
|
const input = parseGlobArgs(args);
|
|
1979
2013
|
const limit = input.head_limit ?? DEFAULT_SEARCH_LIMIT;
|
|
@@ -1994,6 +2028,22 @@ String: ${input.old_string}`
|
|
|
1994
2028
|
defineTool({
|
|
1995
2029
|
name: "Grep",
|
|
1996
2030
|
description: 'Search file contents with ripgrep. Default output_mode is "files" for matching paths; use "content" for matching lines or "count" for match counts.',
|
|
2031
|
+
parameters: Type.Object({
|
|
2032
|
+
pattern: Type.String(),
|
|
2033
|
+
path: Type.Optional(Type.String()),
|
|
2034
|
+
glob: Type.Optional(Type.String()),
|
|
2035
|
+
output_mode: Type.Optional(Type.Union([Type.Literal("files"), Type.Literal("content"), Type.Literal("count")])),
|
|
2036
|
+
"-B": Type.Optional(Type.Number()),
|
|
2037
|
+
"-A": Type.Optional(Type.Number()),
|
|
2038
|
+
"-C": Type.Optional(Type.Number()),
|
|
2039
|
+
context: Type.Optional(Type.Number()),
|
|
2040
|
+
"-n": Type.Optional(Type.Boolean()),
|
|
2041
|
+
"-i": Type.Optional(Type.Boolean()),
|
|
2042
|
+
type: Type.Optional(Type.String()),
|
|
2043
|
+
head_limit: Type.Optional(Type.Number()),
|
|
2044
|
+
offset: Type.Optional(Type.Number()),
|
|
2045
|
+
multiline: Type.Optional(Type.Boolean())
|
|
2046
|
+
}),
|
|
1997
2047
|
execute: async (_toolCallId, args, signal) => {
|
|
1998
2048
|
const input = parseGrepArgs(args);
|
|
1999
2049
|
const mode = input.output_mode ?? "files";
|
|
@@ -2047,6 +2097,15 @@ ${filenames.join("\n")}`,
|
|
|
2047
2097
|
defineTool({
|
|
2048
2098
|
name: "TodoWrite",
|
|
2049
2099
|
description: "Replace the current session todo list with a complete updated list.",
|
|
2100
|
+
parameters: Type.Object({
|
|
2101
|
+
todos: Type.Array(
|
|
2102
|
+
Type.Object({
|
|
2103
|
+
content: Type.String(),
|
|
2104
|
+
status: Type.Union([Type.Literal("pending"), Type.Literal("in_progress"), Type.Literal("completed")]),
|
|
2105
|
+
activeForm: Type.Optional(Type.String())
|
|
2106
|
+
})
|
|
2107
|
+
)
|
|
2108
|
+
}),
|
|
2050
2109
|
execute: async (_toolCallId, args) => {
|
|
2051
2110
|
const input = parseTodoWriteArgs(args);
|
|
2052
2111
|
const oldTodos = state.todos;
|
|
@@ -2329,17 +2388,41 @@ ${filenames.join("\n")}`,
|
|
|
2329
2388
|
throw cause;
|
|
2330
2389
|
}
|
|
2331
2390
|
};
|
|
2332
|
-
bashResult = (input) => {
|
|
2333
|
-
const
|
|
2334
|
-
const
|
|
2335
|
-
|
|
2391
|
+
bashResult = async (input) => {
|
|
2392
|
+
const stdoutBytes = Buffer.byteLength(input.stdout);
|
|
2393
|
+
const stderrBytes = Buffer.byteLength(input.stderr);
|
|
2394
|
+
const fullResult = renderFullBashResult(input);
|
|
2395
|
+
const resultBytes = Buffer.byteLength(fullResult);
|
|
2396
|
+
const shouldArchive = Boolean(input.artifactDir) && resultBytes > input.outputLimit;
|
|
2397
|
+
const artifactPath = shouldArchive && input.artifactDir ? await writeBashArtifact(input.artifactDir, input.toolCallId, fullResult) : void 0;
|
|
2398
|
+
const projection = artifactPath ? projectBashStreams(input.stdout, input.stderr, input.outputLimit) : void 0;
|
|
2399
|
+
const stdout = projection?.stdout ?? truncate(input.stdout, input.outputLimit, "stdout");
|
|
2400
|
+
const stderr = projection?.stderr ?? truncate(input.stderr, input.outputLimit, "stderr");
|
|
2401
|
+
const text = artifactPath ? [
|
|
2402
|
+
`exitCode: ${input.exitCode}`,
|
|
2403
|
+
`cwd: ${input.cwd}`,
|
|
2404
|
+
`artifact: ${artifactPath}`,
|
|
2405
|
+
`resultBytes: ${resultBytes}`,
|
|
2406
|
+
`stdoutBytes: ${stdoutBytes}`,
|
|
2407
|
+
`stderrBytes: ${stderrBytes}`,
|
|
2408
|
+
...projection?.lines ?? []
|
|
2409
|
+
].join("\n") : `exitCode: ${input.exitCode}
|
|
2336
2410
|
cwd: ${input.cwd}
|
|
2337
2411
|
stdout:
|
|
2338
2412
|
${stdout}
|
|
2339
2413
|
stderr:
|
|
2340
|
-
${stderr}
|
|
2414
|
+
${stderr}`;
|
|
2415
|
+
return textResult(text, {
|
|
2341
2416
|
exitCode: input.exitCode,
|
|
2342
2417
|
cwd: input.cwd,
|
|
2418
|
+
...artifactPath ? {
|
|
2419
|
+
artifact: {
|
|
2420
|
+
path: artifactPath,
|
|
2421
|
+
resultBytes,
|
|
2422
|
+
stdoutBytes,
|
|
2423
|
+
stderrBytes
|
|
2424
|
+
}
|
|
2425
|
+
} : {},
|
|
2343
2426
|
...input.shell ? { shell: input.shell } : {},
|
|
2344
2427
|
...input.command ? { command: input.command } : {},
|
|
2345
2428
|
...input.rtk ? {
|
|
@@ -2351,6 +2434,57 @@ ${stderr}`)
|
|
|
2351
2434
|
} : {}
|
|
2352
2435
|
});
|
|
2353
2436
|
};
|
|
2437
|
+
renderFullBashResult = (input) => `exitCode: ${input.exitCode}
|
|
2438
|
+
cwd: ${input.cwd}
|
|
2439
|
+
stdout:
|
|
2440
|
+
${input.stdout}
|
|
2441
|
+
stderr:
|
|
2442
|
+
${input.stderr}`;
|
|
2443
|
+
writeBashArtifact = async (artifactDir, toolCallId, content) => {
|
|
2444
|
+
const directory = resolve(artifactDir, safeArtifactSegment(toolCallId));
|
|
2445
|
+
await mkdir2(directory, { recursive: true });
|
|
2446
|
+
const path = resolve(directory, "result.txt");
|
|
2447
|
+
await writeFile2(path, content, { encoding: "utf8", mode: 384 });
|
|
2448
|
+
return path;
|
|
2449
|
+
};
|
|
2450
|
+
safeArtifactSegment = (value) => value.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120) || "tool_call";
|
|
2451
|
+
projectBashStreams = (stdout, stderr, maxBytes) => {
|
|
2452
|
+
const streams = [
|
|
2453
|
+
{ label: "stdout", value: stdout },
|
|
2454
|
+
{ label: "stderr", value: stderr }
|
|
2455
|
+
].filter((stream) => Buffer.byteLength(stream.value) > 0);
|
|
2456
|
+
if (streams.length === 0) {
|
|
2457
|
+
return { lines: ["stdout:", "", "stderr:", ""], stdout: "", stderr: "" };
|
|
2458
|
+
}
|
|
2459
|
+
const perStreamBudget = Math.max(1, Math.floor(maxBytes / streams.length));
|
|
2460
|
+
const projected = streams.map((stream) => projectOutputStream(stream.value, perStreamBudget, stream.label));
|
|
2461
|
+
const stdoutText = projected.find((stream) => stream.label === "stdout")?.text ?? "stdout:\n";
|
|
2462
|
+
const stderrText = projected.find((stream) => stream.label === "stderr")?.text ?? "stderr:\n";
|
|
2463
|
+
return {
|
|
2464
|
+
lines: projected.map((stream) => stream.text),
|
|
2465
|
+
stdout: stdoutText,
|
|
2466
|
+
stderr: stderrText
|
|
2467
|
+
};
|
|
2468
|
+
};
|
|
2469
|
+
projectOutputStream = (value, maxBytes, label) => {
|
|
2470
|
+
const bytes = Buffer.byteLength(value);
|
|
2471
|
+
if (bytes <= maxBytes) {
|
|
2472
|
+
return { label, text: `${label}:
|
|
2473
|
+
${value}` };
|
|
2474
|
+
}
|
|
2475
|
+
const headBytes = Math.max(1, Math.floor(maxBytes / 2));
|
|
2476
|
+
const tailBytes = Math.max(1, maxBytes - headBytes);
|
|
2477
|
+
return {
|
|
2478
|
+
label,
|
|
2479
|
+
text: [
|
|
2480
|
+
`${label} head:`,
|
|
2481
|
+
sliceBytes(value, 0, headBytes),
|
|
2482
|
+
`${label} tail:`,
|
|
2483
|
+
sliceBytes(value, Math.max(0, bytes - tailBytes), bytes),
|
|
2484
|
+
`[${label} archived: ${bytes} bytes; projection budget ${maxBytes} bytes]`
|
|
2485
|
+
].join("\n")
|
|
2486
|
+
};
|
|
2487
|
+
};
|
|
2354
2488
|
resolveDefaultShell = (input) => {
|
|
2355
2489
|
const shell = input || process.env.SHELL || userShell() || "/bin/sh";
|
|
2356
2490
|
return shell.trim() || "/bin/sh";
|
|
@@ -2434,10 +2568,11 @@ ${stderr}`)
|
|
|
2434
2568
|
if (bytes <= maxBytes) {
|
|
2435
2569
|
return value;
|
|
2436
2570
|
}
|
|
2437
|
-
const truncated =
|
|
2571
|
+
const truncated = sliceBytes(value, 0, maxBytes);
|
|
2438
2572
|
return `${truncated}
|
|
2439
2573
|
[${label} truncated: ${bytes} bytes > ${maxBytes} bytes]`;
|
|
2440
2574
|
};
|
|
2575
|
+
sliceBytes = (value, start, end) => Buffer.from(value).subarray(start, end).toString("utf8");
|
|
2441
2576
|
textResult = (text, details) => ({
|
|
2442
2577
|
content: [{ type: "text", text }],
|
|
2443
2578
|
details
|
|
@@ -2602,17 +2737,58 @@ ${stderr}`)
|
|
|
2602
2737
|
});
|
|
2603
2738
|
|
|
2604
2739
|
// packages/core/src/tools/index.ts
|
|
2605
|
-
|
|
2740
|
+
import { Type as Type2 } from "@mariozechner/pi-ai";
|
|
2741
|
+
var defineTool, createSnipTool, parseSnipToolInput, isRecord4;
|
|
2606
2742
|
var init_tools = __esm({
|
|
2607
2743
|
"packages/core/src/tools/index.ts"() {
|
|
2608
2744
|
"use strict";
|
|
2609
2745
|
init_coding_tools();
|
|
2610
2746
|
defineTool = (tool) => tool;
|
|
2747
|
+
createSnipTool = (options) => defineTool({
|
|
2748
|
+
name: "snip",
|
|
2749
|
+
description: [
|
|
2750
|
+
"Hide a completed user turn from future model context.",
|
|
2751
|
+
"Use only when an earlier user turn is obsolete or noisy.",
|
|
2752
|
+
"The session JSONL evidence is preserved; the hidden turn disappears from the next context build.",
|
|
2753
|
+
"Input: { userMessageId: string, reason?: string }."
|
|
2754
|
+
].join(" "),
|
|
2755
|
+
parameters: Type2.Object({
|
|
2756
|
+
userMessageId: Type2.String(),
|
|
2757
|
+
reason: Type2.Optional(Type2.String())
|
|
2758
|
+
}),
|
|
2759
|
+
execute: async (_toolCallId, args) => {
|
|
2760
|
+
const input = parseSnipToolInput(args);
|
|
2761
|
+
const result = await options.snip(input);
|
|
2762
|
+
return {
|
|
2763
|
+
content: [{
|
|
2764
|
+
type: "text",
|
|
2765
|
+
text: [
|
|
2766
|
+
`Snipped user turn ${result.anchorUserEventId}.`,
|
|
2767
|
+
`Hidden through ${result.throughEventId}.`,
|
|
2768
|
+
`${result.hiddenEventCount} event(s) will be omitted from future model context.`,
|
|
2769
|
+
"Original session JSONL remains unchanged."
|
|
2770
|
+
].join(" ")
|
|
2771
|
+
}],
|
|
2772
|
+
details: result
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
});
|
|
2776
|
+
parseSnipToolInput = (args) => {
|
|
2777
|
+
if (!isRecord4(args) || typeof args.userMessageId !== "string") {
|
|
2778
|
+
throw new Error("snip requires { userMessageId: string }");
|
|
2779
|
+
}
|
|
2780
|
+
return {
|
|
2781
|
+
userMessageId: args.userMessageId,
|
|
2782
|
+
...typeof args.reason === "string" && args.reason.trim() ? { reason: args.reason.trim() } : {}
|
|
2783
|
+
};
|
|
2784
|
+
};
|
|
2785
|
+
isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2611
2786
|
}
|
|
2612
2787
|
});
|
|
2613
2788
|
|
|
2614
2789
|
// packages/core/src/channel/index.ts
|
|
2615
|
-
|
|
2790
|
+
import { Type as Type3 } from "@mariozechner/pi-ai";
|
|
2791
|
+
var createSendChannelMessageTool, parseSendChannelMessageInput, parseAttachments, optionalString2, isRecord5;
|
|
2616
2792
|
var init_channel = __esm({
|
|
2617
2793
|
"packages/core/src/channel/index.ts"() {
|
|
2618
2794
|
"use strict";
|
|
@@ -2620,6 +2796,18 @@ var init_channel = __esm({
|
|
|
2620
2796
|
createSendChannelMessageTool = (options) => defineTool({
|
|
2621
2797
|
name: "SendChannelMessage",
|
|
2622
2798
|
description: "Send a text reply to the current IM channel conversation. Do not provide raw platform user ids or group ids.",
|
|
2799
|
+
parameters: Type3.Object({
|
|
2800
|
+
text: Type3.Optional(Type3.String()),
|
|
2801
|
+
attachments: Type3.Optional(Type3.Array(Type3.Object({
|
|
2802
|
+
type: Type3.Union([Type3.Literal("image"), Type3.Literal("file")]),
|
|
2803
|
+
path: Type3.Optional(Type3.String()),
|
|
2804
|
+
url: Type3.Optional(Type3.String()),
|
|
2805
|
+
mimeType: Type3.Optional(Type3.String()),
|
|
2806
|
+
caption: Type3.Optional(Type3.String())
|
|
2807
|
+
}))),
|
|
2808
|
+
channel: Type3.Optional(Type3.String()),
|
|
2809
|
+
target: Type3.Optional(Type3.Literal("current"))
|
|
2810
|
+
}),
|
|
2623
2811
|
execute: async (_toolCallId, args) => {
|
|
2624
2812
|
const input = parseSendChannelMessageInput(args);
|
|
2625
2813
|
const result = await options.sendCurrent(input);
|
|
@@ -2630,7 +2818,7 @@ var init_channel = __esm({
|
|
|
2630
2818
|
}
|
|
2631
2819
|
});
|
|
2632
2820
|
parseSendChannelMessageInput = (value) => {
|
|
2633
|
-
if (!
|
|
2821
|
+
if (!isRecord5(value)) {
|
|
2634
2822
|
throw new Error("SendChannelMessage args must be an object");
|
|
2635
2823
|
}
|
|
2636
2824
|
const text = typeof value.text === "string" && value.text.trim().length > 0 ? value.text : void 0;
|
|
@@ -2659,7 +2847,7 @@ var init_channel = __esm({
|
|
|
2659
2847
|
throw new Error("SendChannelMessage.attachments must be an array");
|
|
2660
2848
|
}
|
|
2661
2849
|
return value.map((item, index) => {
|
|
2662
|
-
if (!
|
|
2850
|
+
if (!isRecord5(item)) {
|
|
2663
2851
|
throw new Error(`SendChannelMessage.attachments.${index} must be an object`);
|
|
2664
2852
|
}
|
|
2665
2853
|
if (item.type !== "image" && item.type !== "file") {
|
|
@@ -2688,14 +2876,14 @@ var init_channel = __esm({
|
|
|
2688
2876
|
}
|
|
2689
2877
|
return value;
|
|
2690
2878
|
};
|
|
2691
|
-
|
|
2879
|
+
isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2692
2880
|
}
|
|
2693
2881
|
});
|
|
2694
2882
|
|
|
2695
2883
|
// packages/core/src/extensions/index.ts
|
|
2696
2884
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2697
2885
|
import { dirname as dirname4, resolve as resolve2 } from "node:path";
|
|
2698
|
-
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths,
|
|
2886
|
+
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths, isRecord6;
|
|
2699
2887
|
var init_extensions = __esm({
|
|
2700
2888
|
"packages/core/src/extensions/index.ts"() {
|
|
2701
2889
|
"use strict";
|
|
@@ -2708,7 +2896,7 @@ var init_extensions = __esm({
|
|
|
2708
2896
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
2709
2897
|
throw new Error(`Invalid extension manifest JSON at ${manifestPath}: ${message}`);
|
|
2710
2898
|
}
|
|
2711
|
-
if (!
|
|
2899
|
+
if (!isRecord6(value)) {
|
|
2712
2900
|
throw new Error(`Extension manifest at ${manifestPath} must be an object`);
|
|
2713
2901
|
}
|
|
2714
2902
|
const rootDir = dirname4(resolve2(manifestPath));
|
|
@@ -2764,7 +2952,7 @@ var init_extensions = __esm({
|
|
|
2764
2952
|
}
|
|
2765
2953
|
return value.map((item, index) => requireRelativePath(item, `${name}.${index}`, manifestPath));
|
|
2766
2954
|
};
|
|
2767
|
-
|
|
2955
|
+
isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2768
2956
|
}
|
|
2769
2957
|
});
|
|
2770
2958
|
|
|
@@ -2908,7 +3096,8 @@ var init_instructions = __esm({
|
|
|
2908
3096
|
import { appendFile, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "node:fs/promises";
|
|
2909
3097
|
import { homedir as homedir3 } from "node:os";
|
|
2910
3098
|
import { join as join6 } from "node:path";
|
|
2911
|
-
|
|
3099
|
+
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
3100
|
+
var memoryDate, scorelMemoryPaths, scorelSessionMemoryPaths, buildMemoryContext, renderMemoryHarness, appendDailyEntry, createAppendDailyTool, renderDailyEntry, readMemoryDreamState, writeMemoryDreamState, readSessionMemory, writeSessionMemory, renderSessionMemory, ensureMemoryFiles, ensureFile, readOptional, trimForContext, compactLine, renderList, renderBullets, normalizeMarkdownFile, parseAppendDailyInput, validateAppendDailyInput, isLowSignalSummary, containsNormalizedDailyEntry, normalizeDailyText, requireString3, optionalStringArray, optionalNumber2, optionalString3, parseLastFailure, isRecord7, safeProjectId, isNodeErrorCode3;
|
|
2912
3101
|
var init_memory = __esm({
|
|
2913
3102
|
"packages/core/src/memory/index.ts"() {
|
|
2914
3103
|
"use strict";
|
|
@@ -2997,6 +3186,14 @@ var init_memory = __esm({
|
|
|
2997
3186
|
"Use this once near the end of a completed user turn when there is progress, a decision, or a follow-up worth preserving.",
|
|
2998
3187
|
"Do not include secrets, raw logs, speculation, or facts that should be re-read from the repository."
|
|
2999
3188
|
].join(" "),
|
|
3189
|
+
parameters: Type4.Object({
|
|
3190
|
+
summary: Type4.String(),
|
|
3191
|
+
completed: Type4.Optional(Type4.Array(Type4.String())),
|
|
3192
|
+
decisions: Type4.Optional(Type4.Array(Type4.String())),
|
|
3193
|
+
followUps: Type4.Optional(Type4.Array(Type4.String())),
|
|
3194
|
+
memoryCandidates: Type4.Optional(Type4.Array(Type4.String())),
|
|
3195
|
+
evidence: Type4.Optional(Type4.Array(Type4.String()))
|
|
3196
|
+
}),
|
|
3000
3197
|
execute: async (_toolCallId, args) => {
|
|
3001
3198
|
const input = parseAppendDailyInput(args);
|
|
3002
3199
|
validateAppendDailyInput(input);
|
|
@@ -3140,7 +3337,7 @@ var init_memory = __esm({
|
|
|
3140
3337
|
normalizeMarkdownFile = (value) => `${value.trimEnd()}
|
|
3141
3338
|
`;
|
|
3142
3339
|
parseAppendDailyInput = (value) => {
|
|
3143
|
-
if (!
|
|
3340
|
+
if (!isRecord7(value)) {
|
|
3144
3341
|
throw new Error("AppendDaily args must be an object");
|
|
3145
3342
|
}
|
|
3146
3343
|
const summary = requireString3(value.summary, "summary");
|
|
@@ -3206,12 +3403,12 @@ var init_memory = __esm({
|
|
|
3206
3403
|
optionalNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3207
3404
|
optionalString3 = (value) => typeof value === "string" && value.trim() ? value : void 0;
|
|
3208
3405
|
parseLastFailure = (value) => {
|
|
3209
|
-
if (!
|
|
3406
|
+
if (!isRecord7(value)) return void 0;
|
|
3210
3407
|
const at = optionalNumber2(value.at);
|
|
3211
3408
|
const message = optionalString3(value.message);
|
|
3212
3409
|
return at !== void 0 && message ? { at, message } : void 0;
|
|
3213
3410
|
};
|
|
3214
|
-
|
|
3411
|
+
isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3215
3412
|
safeProjectId = (projectId) => {
|
|
3216
3413
|
if (!/^[A-Za-z0-9_-]+$/.test(projectId)) {
|
|
3217
3414
|
throw new Error("projectId must contain only letters, numbers, underscores, or hyphens");
|
|
@@ -3224,11 +3421,10 @@ var init_memory = __esm({
|
|
|
3224
3421
|
|
|
3225
3422
|
// packages/core/src/provider/pi-ai.ts
|
|
3226
3423
|
import {
|
|
3227
|
-
Type,
|
|
3228
3424
|
getModels,
|
|
3229
3425
|
streamSimple
|
|
3230
3426
|
} from "@mariozechner/pi-ai";
|
|
3231
|
-
var DEFAULT_CUSTOM_MODEL_CONTEXT_WINDOW, DEFAULT_CUSTOM_MODEL_MAX_TOKENS, createPiAiProvider, resolvePiAiModel, toPiContext, toPiMessage, toPiAssistantBlock, fromPiAssistant, fromPiContentBlock, toPiTool,
|
|
3427
|
+
var DEFAULT_CUSTOM_MODEL_CONTEXT_WINDOW, DEFAULT_CUSTOM_MODEL_MAX_TOKENS, createPiAiProvider, resolvePiAiModel, toPiContext, toPiMessage, toPiAssistantBlock, fromPiAssistant, fromPiContentBlock, toPiTool, textContent, toolResultText, stringMeta, toPiStopReason, fromPiStopReason, fromPiUsage;
|
|
3232
3428
|
var init_pi_ai = __esm({
|
|
3233
3429
|
"packages/core/src/provider/pi-ai.ts"() {
|
|
3234
3430
|
"use strict";
|
|
@@ -3366,89 +3562,8 @@ var init_pi_ai = __esm({
|
|
|
3366
3562
|
toPiTool = (tool) => ({
|
|
3367
3563
|
name: tool.name,
|
|
3368
3564
|
description: tool.description,
|
|
3369
|
-
parameters:
|
|
3565
|
+
parameters: tool.parameters
|
|
3370
3566
|
});
|
|
3371
|
-
toolParameters = (name) => {
|
|
3372
|
-
switch (name) {
|
|
3373
|
-
case "Read":
|
|
3374
|
-
return Type.Object({
|
|
3375
|
-
file_path: Type.String(),
|
|
3376
|
-
offset: Type.Optional(Type.Number()),
|
|
3377
|
-
limit: Type.Optional(Type.Number()),
|
|
3378
|
-
full: Type.Optional(Type.Boolean())
|
|
3379
|
-
});
|
|
3380
|
-
case "Write":
|
|
3381
|
-
return Type.Object({
|
|
3382
|
-
file_path: Type.String(),
|
|
3383
|
-
content: Type.String()
|
|
3384
|
-
});
|
|
3385
|
-
case "Edit":
|
|
3386
|
-
return Type.Object({
|
|
3387
|
-
file_path: Type.String(),
|
|
3388
|
-
old_string: Type.String(),
|
|
3389
|
-
new_string: Type.String(),
|
|
3390
|
-
replace_all: Type.Optional(Type.Boolean())
|
|
3391
|
-
});
|
|
3392
|
-
case "Bash":
|
|
3393
|
-
return Type.Object({
|
|
3394
|
-
command: Type.String(),
|
|
3395
|
-
cwd: Type.Optional(Type.String()),
|
|
3396
|
-
timeout: Type.Optional(Type.Number()),
|
|
3397
|
-
description: Type.Optional(Type.String()),
|
|
3398
|
-
maxOutputBytes: Type.Optional(Type.Number())
|
|
3399
|
-
});
|
|
3400
|
-
case "Glob":
|
|
3401
|
-
return Type.Object({
|
|
3402
|
-
pattern: Type.String(),
|
|
3403
|
-
path: Type.Optional(Type.String()),
|
|
3404
|
-
head_limit: Type.Optional(Type.Number()),
|
|
3405
|
-
offset: Type.Optional(Type.Number())
|
|
3406
|
-
});
|
|
3407
|
-
case "Grep":
|
|
3408
|
-
return Type.Object({
|
|
3409
|
-
pattern: Type.String(),
|
|
3410
|
-
path: Type.Optional(Type.String()),
|
|
3411
|
-
glob: Type.Optional(Type.String()),
|
|
3412
|
-
output_mode: Type.Optional(Type.Union([Type.Literal("files"), Type.Literal("content"), Type.Literal("count")])),
|
|
3413
|
-
"-B": Type.Optional(Type.Number()),
|
|
3414
|
-
"-A": Type.Optional(Type.Number()),
|
|
3415
|
-
"-C": Type.Optional(Type.Number()),
|
|
3416
|
-
context: Type.Optional(Type.Number()),
|
|
3417
|
-
"-n": Type.Optional(Type.Boolean()),
|
|
3418
|
-
"-i": Type.Optional(Type.Boolean()),
|
|
3419
|
-
type: Type.Optional(Type.String()),
|
|
3420
|
-
head_limit: Type.Optional(Type.Number()),
|
|
3421
|
-
offset: Type.Optional(Type.Number()),
|
|
3422
|
-
multiline: Type.Optional(Type.Boolean())
|
|
3423
|
-
});
|
|
3424
|
-
case "TodoWrite":
|
|
3425
|
-
return Type.Object({
|
|
3426
|
-
todos: Type.Array(
|
|
3427
|
-
Type.Object({
|
|
3428
|
-
content: Type.String(),
|
|
3429
|
-
status: Type.Union([Type.Literal("pending"), Type.Literal("in_progress"), Type.Literal("completed")]),
|
|
3430
|
-
activeForm: Type.Optional(Type.String())
|
|
3431
|
-
})
|
|
3432
|
-
)
|
|
3433
|
-
});
|
|
3434
|
-
case "AppendDaily":
|
|
3435
|
-
return Type.Object({
|
|
3436
|
-
summary: Type.String(),
|
|
3437
|
-
completed: Type.Optional(Type.Array(Type.String())),
|
|
3438
|
-
decisions: Type.Optional(Type.Array(Type.String())),
|
|
3439
|
-
followUps: Type.Optional(Type.Array(Type.String())),
|
|
3440
|
-
memoryCandidates: Type.Optional(Type.Array(Type.String())),
|
|
3441
|
-
evidence: Type.Optional(Type.Array(Type.String()))
|
|
3442
|
-
});
|
|
3443
|
-
case "Skill":
|
|
3444
|
-
return Type.Object({
|
|
3445
|
-
name: Type.String(),
|
|
3446
|
-
args: Type.Optional(Type.String())
|
|
3447
|
-
});
|
|
3448
|
-
default:
|
|
3449
|
-
return Type.Object({});
|
|
3450
|
-
}
|
|
3451
|
-
};
|
|
3452
3567
|
textContent = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
3453
3568
|
toolResultText = (result) => {
|
|
3454
3569
|
if (typeof result === "object" && result !== null && "content" in result) {
|
|
@@ -3696,22 +3811,23 @@ var init_runtime = __esm({
|
|
|
3696
3811
|
});
|
|
3697
3812
|
|
|
3698
3813
|
// packages/core/src/session/index.ts
|
|
3814
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3699
3815
|
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile8, writeFile as writeFile4 } from "node:fs/promises";
|
|
3700
3816
|
import { dirname as dirname6, join as join7 } from "node:path";
|
|
3701
3817
|
function assertTreeEvent(value) {
|
|
3702
|
-
if (!
|
|
3818
|
+
if (!isRecord8(value)) {
|
|
3703
3819
|
throw new SessionStoreError("invalid_event", "Event must be an object");
|
|
3704
3820
|
}
|
|
3705
3821
|
if (value.type === "session_header") {
|
|
3706
3822
|
throw new SessionStoreError("invalid_event", "Session header must be stored as the JSONL header line");
|
|
3707
3823
|
}
|
|
3708
|
-
if (value.type !== "user_message" && value.type !== "assistant_message" && value.type !== "tool_result" && value.type !== "session_title_updated" && value.type !== "instruction_snapshot" && value.type !== "harness_item" && value.type !== "compact" && value.type !== "queue_update" && value.type !== "skill_index_snapshot" && value.type !== "skill_index_delta") {
|
|
3824
|
+
if (value.type !== "user_message" && value.type !== "assistant_message" && value.type !== "tool_result" && value.type !== "session_title_updated" && value.type !== "instruction_snapshot" && value.type !== "harness_item" && value.type !== "compact" && value.type !== "context_control" && value.type !== "queue_update" && value.type !== "skill_index_snapshot" && value.type !== "skill_index_delta") {
|
|
3709
3825
|
throw new SessionStoreError("invalid_event", "Unsupported session event type");
|
|
3710
3826
|
}
|
|
3711
3827
|
if (typeof value.id !== "string" || value.parentId !== null && typeof value.parentId !== "string" || typeof value.seq !== "number" || typeof value.clientId !== "string" || typeof value.ts !== "number") {
|
|
3712
3828
|
throw new SessionStoreError("invalid_event", "Event is missing required base fields");
|
|
3713
3829
|
}
|
|
3714
|
-
if ((value.type === "user_message" || value.type === "assistant_message" || value.type === "tool_result") && !
|
|
3830
|
+
if ((value.type === "user_message" || value.type === "assistant_message" || value.type === "tool_result") && !isRecord8(value.message)) {
|
|
3715
3831
|
throw new SessionStoreError("invalid_event", "Message event is missing message payload");
|
|
3716
3832
|
}
|
|
3717
3833
|
if (value.type === "session_title_updated" && !isSessionTitleUpdated(value)) {
|
|
@@ -3726,6 +3842,9 @@ function assertTreeEvent(value) {
|
|
|
3726
3842
|
if (value.type === "compact" && !isCompactEvent(value)) {
|
|
3727
3843
|
throw new SessionStoreError("invalid_event", "compact is missing summary payload");
|
|
3728
3844
|
}
|
|
3845
|
+
if (value.type === "context_control" && !isContextControlEvent(value)) {
|
|
3846
|
+
throw new SessionStoreError("invalid_event", "context_control is missing hide_user_turn payload");
|
|
3847
|
+
}
|
|
3729
3848
|
if (value.type === "queue_update" && !isQueueUpdate(value)) {
|
|
3730
3849
|
throw new SessionStoreError("invalid_event", "queue_update is missing queue payload");
|
|
3731
3850
|
}
|
|
@@ -3736,11 +3855,12 @@ function assertTreeEvent(value) {
|
|
|
3736
3855
|
throw new SessionStoreError("invalid_event", "skill_index_delta is missing delta payload");
|
|
3737
3856
|
}
|
|
3738
3857
|
}
|
|
3739
|
-
var SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, createSession, loadSession, buildContext, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, appendReminderToToolResult, isToolResultWithContent, renderSystemReminder, compactSummaryMessage, cloneMessage,
|
|
3858
|
+
var snipUserMessageAlias, SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, sessionArtifactsDirPath, createSession, loadSession, buildContext, hiddenContextEventIds, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isContextControlEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, appendReminderToToolResult, isToolResultWithContent, renderSystemReminder, compactSummaryMessage, cloneMessage, isRecord8;
|
|
3740
3859
|
var init_session = __esm({
|
|
3741
3860
|
"packages/core/src/session/index.ts"() {
|
|
3742
3861
|
"use strict";
|
|
3743
3862
|
init_src();
|
|
3863
|
+
snipUserMessageAlias = (eventId) => `u_${createHash2("sha256").update(eventId).digest("hex").slice(0, 8)}`;
|
|
3744
3864
|
SessionStoreError = class extends Error {
|
|
3745
3865
|
code;
|
|
3746
3866
|
line;
|
|
@@ -3764,7 +3884,8 @@ var init_session = __esm({
|
|
|
3764
3884
|
steer: []
|
|
3765
3885
|
},
|
|
3766
3886
|
skillIndexInitialized: false,
|
|
3767
|
-
skillIndex: {}
|
|
3887
|
+
skillIndex: {},
|
|
3888
|
+
hiddenUserTurnSpans: []
|
|
3768
3889
|
};
|
|
3769
3890
|
get rootId() {
|
|
3770
3891
|
return this.#rootId;
|
|
@@ -3884,6 +4005,16 @@ var init_session = __esm({
|
|
|
3884
4005
|
delete next[removed.name];
|
|
3885
4006
|
}
|
|
3886
4007
|
this.controlState.skillIndex = next;
|
|
4008
|
+
} else if (event.type === "context_control") {
|
|
4009
|
+
this.controlState.hiddenUserTurnSpans = [
|
|
4010
|
+
...this.controlState.hiddenUserTurnSpans.filter(
|
|
4011
|
+
(span) => span.anchorUserEventId !== event.anchorUserEventId
|
|
4012
|
+
),
|
|
4013
|
+
{
|
|
4014
|
+
anchorUserEventId: event.anchorUserEventId,
|
|
4015
|
+
throughEventId: event.throughEventId
|
|
4016
|
+
}
|
|
4017
|
+
];
|
|
3887
4018
|
}
|
|
3888
4019
|
}
|
|
3889
4020
|
};
|
|
@@ -3917,6 +4048,7 @@ var init_session = __esm({
|
|
|
3917
4048
|
};
|
|
3918
4049
|
sessionFilePath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.jsonl`);
|
|
3919
4050
|
sessionLogFilePath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.log`);
|
|
4051
|
+
sessionArtifactsDirPath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.artifacts`);
|
|
3920
4052
|
createSession = async ({ sessionsDir, header }) => {
|
|
3921
4053
|
const validHeader = parseHeader(header);
|
|
3922
4054
|
await mkdir4(sessionsDir, { recursive: true });
|
|
@@ -3944,7 +4076,11 @@ var init_session = __esm({
|
|
|
3944
4076
|
};
|
|
3945
4077
|
buildContext = (tree, leafId) => {
|
|
3946
4078
|
const path = tree.getPath(leafId);
|
|
4079
|
+
const hiddenIds = hiddenContextEventIds(tree, path, leafId);
|
|
3947
4080
|
return path.reduce((messages, id, index) => {
|
|
4081
|
+
if (hiddenIds.has(id)) {
|
|
4082
|
+
return messages;
|
|
4083
|
+
}
|
|
3948
4084
|
const event = tree.get(id)?.event;
|
|
3949
4085
|
if (!event) {
|
|
3950
4086
|
return messages;
|
|
@@ -3957,7 +4093,7 @@ var init_session = __esm({
|
|
|
3957
4093
|
appendHarnessItemToContext(messages, event);
|
|
3958
4094
|
}
|
|
3959
4095
|
if (event.type === "compact") {
|
|
3960
|
-
const retained = retainedMessagesBeforeCompact(tree, path.slice(0, index), event.retainedEventCount);
|
|
4096
|
+
const retained = retainedMessagesBeforeCompact(tree, path.slice(0, index), event.retainedEventCount, hiddenIds);
|
|
3961
4097
|
messages.length = 0;
|
|
3962
4098
|
messages.push(compactSummaryMessage(event));
|
|
3963
4099
|
messages.push(...retained);
|
|
@@ -3965,7 +4101,29 @@ var init_session = __esm({
|
|
|
3965
4101
|
return messages;
|
|
3966
4102
|
}, []);
|
|
3967
4103
|
};
|
|
3968
|
-
|
|
4104
|
+
hiddenContextEventIds = (tree, path, leafId) => {
|
|
4105
|
+
const leaf = tree.get(leafId)?.event;
|
|
4106
|
+
if (!leaf) {
|
|
4107
|
+
return /* @__PURE__ */ new Set();
|
|
4108
|
+
}
|
|
4109
|
+
const pathIndexes = new Map(path.map((id, index) => [id, index]));
|
|
4110
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
4111
|
+
for (const event of tree) {
|
|
4112
|
+
if (event.type !== "context_control" || Number(event.seq) > Number(leaf.seq)) {
|
|
4113
|
+
continue;
|
|
4114
|
+
}
|
|
4115
|
+
const start = pathIndexes.get(event.anchorUserEventId);
|
|
4116
|
+
const end = pathIndexes.get(event.throughEventId);
|
|
4117
|
+
if (start === void 0 || end === void 0 || end < start) {
|
|
4118
|
+
continue;
|
|
4119
|
+
}
|
|
4120
|
+
for (const id of path.slice(start, end + 1)) {
|
|
4121
|
+
hidden.add(id);
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
return hidden;
|
|
4125
|
+
};
|
|
4126
|
+
retainedMessagesBeforeCompact = (tree, pathBeforeCompact, retainedEventCount, hiddenIds = /* @__PURE__ */ new Set()) => {
|
|
3969
4127
|
if (retainedEventCount <= 0) {
|
|
3970
4128
|
return [];
|
|
3971
4129
|
}
|
|
@@ -3980,6 +4138,9 @@ var init_session = __esm({
|
|
|
3980
4138
|
}
|
|
3981
4139
|
const retained = [];
|
|
3982
4140
|
for (const id of pathBeforeCompact.slice(start)) {
|
|
4141
|
+
if (hiddenIds.has(id)) {
|
|
4142
|
+
continue;
|
|
4143
|
+
}
|
|
3983
4144
|
const event = tree.get(id)?.event;
|
|
3984
4145
|
if (!event) {
|
|
3985
4146
|
continue;
|
|
@@ -4004,13 +4165,13 @@ var init_session = __esm({
|
|
|
4004
4165
|
}
|
|
4005
4166
|
};
|
|
4006
4167
|
parseHeader = (value) => {
|
|
4007
|
-
if (!
|
|
4168
|
+
if (!isRecord8(value)) {
|
|
4008
4169
|
throw new SessionStoreError("invalid_header", "Session header must be an object");
|
|
4009
4170
|
}
|
|
4010
4171
|
if (value.version !== 1 || typeof value.sessionId !== "string" || typeof value.deviceId !== "string") {
|
|
4011
4172
|
throw new SessionStoreError("invalid_header", "Session header is missing required identity fields");
|
|
4012
4173
|
}
|
|
4013
|
-
if (typeof value.createdAt !== "number" || !
|
|
4174
|
+
if (typeof value.createdAt !== "number" || !isRecord8(value.meta)) {
|
|
4014
4175
|
throw new SessionStoreError("invalid_header", "Session header is missing createdAt or meta");
|
|
4015
4176
|
}
|
|
4016
4177
|
if (typeof value.meta.projectId !== "string" || value.meta.projectId.length === 0) {
|
|
@@ -4024,7 +4185,7 @@ var init_session = __esm({
|
|
|
4024
4185
|
return value;
|
|
4025
4186
|
};
|
|
4026
4187
|
validateSessionMatch = (header, value) => {
|
|
4027
|
-
if (!
|
|
4188
|
+
if (!isRecord8(value) || typeof value.sessionId !== "string") {
|
|
4028
4189
|
throw new SessionStoreError("invalid_header", "Event must be an object with a sessionId");
|
|
4029
4190
|
}
|
|
4030
4191
|
if (value.sessionId !== header.sessionId) {
|
|
@@ -4033,24 +4194,25 @@ var init_session = __esm({
|
|
|
4033
4194
|
};
|
|
4034
4195
|
isConversationEvent = (event) => event.type === "user_message" || event.type === "assistant_message" || event.type === "tool_result" || event.type === "harness_item" || event.type === "compact";
|
|
4035
4196
|
isInstructionSnapshot = (value) => {
|
|
4036
|
-
if (!
|
|
4197
|
+
if (!isRecord8(value) || value.version !== 1 || typeof value.cwd !== "string" || !Array.isArray(value.sections)) {
|
|
4037
4198
|
return false;
|
|
4038
4199
|
}
|
|
4039
4200
|
return value.sections.every(
|
|
4040
|
-
(section2) =>
|
|
4201
|
+
(section2) => isRecord8(section2) && typeof section2.kind === "string" && typeof section2.frozenAt === "number" && typeof section2.renderedBlock === "string"
|
|
4041
4202
|
);
|
|
4042
4203
|
};
|
|
4043
|
-
isHarnessItem = (value) =>
|
|
4204
|
+
isHarnessItem = (value) => isRecord8(value) && typeof value.kind === "string" && typeof value.origin === "string" && typeof value.content === "string" && (value.visibility === "display" || value.visibility === "hidden" || value.visibility === "compact");
|
|
4044
4205
|
isCompactEvent = (value) => typeof value.summary === "string" && typeof value.compactedThrough === "string" && typeof value.tokensBefore === "number" && typeof value.tokensAfter === "number" && typeof value.retainedEventCount === "number";
|
|
4206
|
+
isContextControlEvent = (value) => value.operation === "hide_user_turn" && typeof value.anchorUserEventId === "string" && typeof value.throughEventId === "string" && (value.actor === "agent" || value.actor === "user" || value.actor === "system") && (value.reason === void 0 || typeof value.reason === "string");
|
|
4045
4207
|
isQueueUpdate = (value) => (value.queue === "follow_up" || value.queue === "steer") && value.operation === "rewrite" && Array.isArray(value.items) && (value.anchorEventId === null || typeof value.anchorEventId === "string") && value.items.every(
|
|
4046
|
-
(item) =>
|
|
4208
|
+
(item) => isRecord8(item) && typeof item.id === "string" && Array.isArray(item.content) && typeof item.createdAt === "number" && typeof item.updatedAt === "number" && typeof item.clientId === "string"
|
|
4047
4209
|
);
|
|
4048
|
-
isSessionTitleUpdated = (value) => typeof value.title === "string" && value.title.length > 0 && (value.source === "model" || value.source === "user") && (value.derivedFrom === void 0 ||
|
|
4210
|
+
isSessionTitleUpdated = (value) => typeof value.title === "string" && value.title.length > 0 && (value.source === "model" || value.source === "user") && (value.derivedFrom === void 0 || isRecord8(value.derivedFrom) && typeof value.derivedFrom.eventId === "string" && typeof value.derivedFrom.seq === "number");
|
|
4049
4211
|
isSkillIndexSnapshot = (value) => (value.anchorEventId === null || typeof value.anchorEventId === "string") && Array.isArray(value.entries) && value.entries.every(isSkillIndexEntry);
|
|
4050
4212
|
isSkillIndexDelta = (value) => (value.anchorEventId === null || typeof value.anchorEventId === "string") && Array.isArray(value.added) && Array.isArray(value.changed) && Array.isArray(value.removed) && value.added.every(isSkillIndexEntry) && value.changed.every(isSkillIndexEntry) && value.removed.every(
|
|
4051
|
-
(item) =>
|
|
4213
|
+
(item) => isRecord8(item) && typeof item.name === "string" && typeof item.previousPath === "string"
|
|
4052
4214
|
);
|
|
4053
|
-
isSkillIndexEntry = (value) =>
|
|
4215
|
+
isSkillIndexEntry = (value) => isRecord8(value) && typeof value.name === "string" && typeof value.path === "string" && (value.scope === "user" || value.scope === "project" || value.scope === "extension") && typeof value.description === "string" && typeof value.mtimeMs === "number" && typeof value.size === "number" && typeof value.contentHash === "string" && typeof value.priority === "number";
|
|
4054
4216
|
appendHarnessItemToContext = (messages, event) => {
|
|
4055
4217
|
const reminder = renderSystemReminder(event.item.content);
|
|
4056
4218
|
const last = messages.at(-1);
|
|
@@ -4087,7 +4249,7 @@ ${reminder}` }]
|
|
|
4087
4249
|
}
|
|
4088
4250
|
return false;
|
|
4089
4251
|
};
|
|
4090
|
-
isToolResultWithContent = (value) =>
|
|
4252
|
+
isToolResultWithContent = (value) => isRecord8(value) && Array.isArray(value.content);
|
|
4091
4253
|
renderSystemReminder = (content) => `<system-reminder>
|
|
4092
4254
|
${content}
|
|
4093
4255
|
</system-reminder>`;
|
|
@@ -4111,10 +4273,10 @@ ${content}
|
|
|
4111
4273
|
cloneMessage = (message) => ({
|
|
4112
4274
|
...message,
|
|
4113
4275
|
content: message.content.map((block) => {
|
|
4114
|
-
if (block.type !== "tool_result" || !
|
|
4276
|
+
if (block.type !== "tool_result" || !isRecord8(block.result)) {
|
|
4115
4277
|
return { ...block };
|
|
4116
4278
|
}
|
|
4117
|
-
const content = Array.isArray(block.result.content) ? { content: block.result.content.map((item) =>
|
|
4279
|
+
const content = Array.isArray(block.result.content) ? { content: block.result.content.map((item) => isRecord8(item) ? { ...item } : item) } : {};
|
|
4118
4280
|
return {
|
|
4119
4281
|
...block,
|
|
4120
4282
|
result: {
|
|
@@ -4124,16 +4286,17 @@ ${content}
|
|
|
4124
4286
|
}),
|
|
4125
4287
|
...message.meta ? { meta: { ...message.meta } } : {}
|
|
4126
4288
|
});
|
|
4127
|
-
|
|
4289
|
+
isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4128
4290
|
}
|
|
4129
4291
|
});
|
|
4130
4292
|
|
|
4131
4293
|
// packages/core/src/skills/index.ts
|
|
4132
|
-
import { createHash as
|
|
4294
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
4133
4295
|
import { existsSync as existsSync2 } from "node:fs";
|
|
4134
4296
|
import { readdir as readdir5, readFile as readFile9, stat as stat4 } from "node:fs/promises";
|
|
4135
4297
|
import { homedir as homedir4 } from "node:os";
|
|
4136
4298
|
import { dirname as dirname7, join as join8, resolve as resolve4 } from "node:path";
|
|
4299
|
+
import { Type as Type5 } from "@mariozechner/pi-ai";
|
|
4137
4300
|
var scanSkillIndex, diffSkillIndex, hasSkillIndexDelta, renderSkillListing, renderSkillDelta, createSkillTool, projectSkillRoots, readSkillEntry, parseSkillMetadata, firstParagraph, parseSkillArgs, findGitRoot2, isNodeErrorCode4;
|
|
4138
4301
|
var init_skills = __esm({
|
|
4139
4302
|
"packages/core/src/skills/index.ts"() {
|
|
@@ -4224,6 +4387,10 @@ var init_skills = __esm({
|
|
|
4224
4387
|
createSkillTool = (options) => defineTool({
|
|
4225
4388
|
name: "Skill",
|
|
4226
4389
|
description: "Load the full SKILL.md instructions for an available session-indexed skill by name.",
|
|
4390
|
+
parameters: Type5.Object({
|
|
4391
|
+
name: Type5.String(),
|
|
4392
|
+
args: Type5.Optional(Type5.String())
|
|
4393
|
+
}),
|
|
4227
4394
|
execute: async (_toolCallId, args) => {
|
|
4228
4395
|
const input = parseSkillArgs(args);
|
|
4229
4396
|
const entry = options.getEntry(input.name);
|
|
@@ -4288,7 +4455,7 @@ var init_skills = __esm({
|
|
|
4288
4455
|
...parsed.displayName ? { displayName: parsed.displayName } : {},
|
|
4289
4456
|
mtimeMs: fileStat.mtimeMs,
|
|
4290
4457
|
size: fileStat.size,
|
|
4291
|
-
contentHash:
|
|
4458
|
+
contentHash: createHash3("sha256").update(content).digest("hex"),
|
|
4292
4459
|
priority: options.priority
|
|
4293
4460
|
};
|
|
4294
4461
|
};
|
|
@@ -4704,7 +4871,7 @@ import { basename as basename3, dirname as dirname8, join as join10, resolve as
|
|
|
4704
4871
|
import { pathToFileURL } from "node:url";
|
|
4705
4872
|
import { promisify as promisify2 } from "node:util";
|
|
4706
4873
|
import { WebSocketServer } from "ws";
|
|
4707
|
-
var daemonPackageName, SESSION_MEMORY_COMPACT_WAIT_MS, AUTO_COMPACT_RETAINED_EVENTS, execFileAsync2, localDaemonStateFile, createLocalDaemonState, readLocalDaemonState, removeLocalDaemonState, markDaemonStopped, daemonStateLiveness, defaultIsPidAlive, startRemoteDaemonWebSocketServer, startScorelHostWebSocketServer, closeWebSocketServer, createRealRuntime, ScorelHost, isMissingConfigError, createEmbeddedTransport, isNodeErrorCode5, wireErrorCode, hasContinuousCoverage, countContentBlocks, normalizeContent, inputText, assistantText, messageText, estimateScorelMessagesTokens, estimateTextTokens, compactLine2, parseSessionMemoryJson, stringArray, disabledMemorySettings, detectRtk, ensureRtkAvailable, emptyRuntimeStats, readRuntimeStats, writeRuntimeStats, parseRuntimeStats, parseRuntimeStatsBuckets, addRtkSavings, addRuntimeStatsBucket, rtkSavingsFromToolResult, nonNegativeInteger2, resolveDefaultShell2, shellCommandArgs2, userShell2, runtimeChannelContextFromWire, parseQueuedChannelContext, imBindingKey, defaultBuiltinExtensionsDir, runtimeModuleDir, findBuiltinExtensionsDir, isSteerMessage, stripImCommandPrefix,
|
|
4874
|
+
var daemonPackageName, SESSION_MEMORY_COMPACT_WAIT_MS, AUTO_COMPACT_RETAINED_EVENTS, execFileAsync2, localDaemonStateFile, createLocalDaemonState, readLocalDaemonState, removeLocalDaemonState, markDaemonStopped, daemonStateLiveness, defaultIsPidAlive, startRemoteDaemonWebSocketServer, startScorelHostWebSocketServer, closeWebSocketServer, createRealRuntime, ScorelHost, isMissingConfigError, createEmbeddedTransport, isNodeErrorCode5, wireErrorCode, hasContinuousCoverage, countContentBlocks, normalizeContent, snipUserMessageIdBlock, inputText, assistantText, messageText, estimateScorelMessagesTokens, estimateTextTokens, compactLine2, parseSessionMemoryJson, stringArray, disabledMemorySettings, detectRtk, ensureRtkAvailable, emptyRuntimeStats, readRuntimeStats, writeRuntimeStats, parseRuntimeStats, parseRuntimeStatsBuckets, addRtkSavings, addRuntimeStatsBucket, rtkSavingsFromToolResult, nonNegativeInteger2, resolveDefaultShell2, shellCommandArgs2, userShell2, runtimeChannelContextFromWire, parseQueuedChannelContext, parseQueuedModelSelection, imBindingKey, defaultBuiltinExtensionsDir, runtimeModuleDir, findBuiltinExtensionsDir, isSteerMessage, stripImCommandPrefix, isRecord9, parseMemoryUpdate, normalizeMarkdownFile2, sanitizeSessionTitle, shortStack, formatDiagnosticLine, formatDiagnosticValue;
|
|
4708
4875
|
var init_src4 = __esm({
|
|
4709
4876
|
"packages/daemon/src/index.ts"() {
|
|
4710
4877
|
"use strict";
|
|
@@ -4945,6 +5112,7 @@ var init_src4 = __esm({
|
|
|
4945
5112
|
for (const tool of createCodingTools({
|
|
4946
5113
|
cwd: options.cwd,
|
|
4947
5114
|
contextWindow: model.contextWindow,
|
|
5115
|
+
...options.sessionsDir && options.sessionId ? { toolResultArtifacts: { dir: sessionArtifactsDirPath(options.sessionsDir, options.sessionId) } } : {},
|
|
4948
5116
|
tokenSaving: {
|
|
4949
5117
|
rtk: {
|
|
4950
5118
|
enabled: options.config.runtime.tokenSavingRtk,
|
|
@@ -4985,6 +5153,7 @@ var init_src4 = __esm({
|
|
|
4985
5153
|
#registry;
|
|
4986
5154
|
#runtimeStatsQueue = Promise.resolve();
|
|
4987
5155
|
#idleShutdownTimer;
|
|
5156
|
+
#lastActiveWorkAt;
|
|
4988
5157
|
#started = false;
|
|
4989
5158
|
constructor(options) {
|
|
4990
5159
|
this.#sessionsDir = options.sessionsDir;
|
|
@@ -5003,6 +5172,7 @@ var init_src4 = __esm({
|
|
|
5003
5172
|
this.#onIdleShutdown = options.onIdleShutdown;
|
|
5004
5173
|
this.#now = options.now ?? Date.now;
|
|
5005
5174
|
this.#createId = options.createId ?? (() => crypto.randomUUID());
|
|
5175
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5006
5176
|
this.#registry = new ProjectRegistry({
|
|
5007
5177
|
sessionsDir: this.#sessionsDir,
|
|
5008
5178
|
projectsPath: options.projectsPath,
|
|
@@ -5067,6 +5237,13 @@ var init_src4 = __esm({
|
|
|
5067
5237
|
releaseSessionEventBuffer(sessionId) {
|
|
5068
5238
|
this.#events.delete(sessionId);
|
|
5069
5239
|
}
|
|
5240
|
+
activityStatus() {
|
|
5241
|
+
const activeWork = this.#hasActiveWork();
|
|
5242
|
+
if (activeWork) {
|
|
5243
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5244
|
+
}
|
|
5245
|
+
return { activeWork, lastActiveWorkAt: this.#lastActiveWorkAt };
|
|
5246
|
+
}
|
|
5070
5247
|
async handleMessage(connection, message) {
|
|
5071
5248
|
this.#assertStarted();
|
|
5072
5249
|
try {
|
|
@@ -5355,6 +5532,7 @@ var init_src4 = __esm({
|
|
|
5355
5532
|
content: normalizeContent(request.content),
|
|
5356
5533
|
parentId: request.options?.parentId,
|
|
5357
5534
|
source: "user",
|
|
5535
|
+
modelSelection: request.options?.modelSelection,
|
|
5358
5536
|
channelContext: request.options?.channelContext ? runtimeChannelContextFromWire(request.options.channelContext) : void 0,
|
|
5359
5537
|
onComplete: (result) => this.#respond(connection, request, { ...result, status: "completed" })
|
|
5360
5538
|
});
|
|
@@ -5380,11 +5558,14 @@ var init_src4 = __esm({
|
|
|
5380
5558
|
});
|
|
5381
5559
|
}
|
|
5382
5560
|
async #runUserTurn(lane, clientId, input) {
|
|
5561
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5383
5562
|
const sessionId = lane.session.header.sessionId;
|
|
5563
|
+
await this.#selectChatRuntime(lane, input.modelSelection);
|
|
5384
5564
|
await this.#appendDiagnostic(sessionId, "send_message_started", {
|
|
5385
5565
|
clientId,
|
|
5386
5566
|
activeLeafId: lane.session.activeLeafId,
|
|
5387
|
-
source: input.source
|
|
5567
|
+
source: input.source,
|
|
5568
|
+
selectedModelId: lane.selectedModel?.modelId
|
|
5388
5569
|
});
|
|
5389
5570
|
const instructionSnapshot = await this.#ensureInstructionSnapshot(lane, clientId);
|
|
5390
5571
|
await this.#syncSkillIndex(lane, clientId);
|
|
@@ -5407,7 +5588,7 @@ var init_src4 = __esm({
|
|
|
5407
5588
|
ts: this.#now(),
|
|
5408
5589
|
message: {
|
|
5409
5590
|
role: "user",
|
|
5410
|
-
content: input.content,
|
|
5591
|
+
content: [...input.content, snipUserMessageIdBlock(userEventId)],
|
|
5411
5592
|
...input.source === "follow_up" ? { meta: { source: "follow_up", queueItemId: input.queueItemId } } : {}
|
|
5412
5593
|
}
|
|
5413
5594
|
});
|
|
@@ -5427,6 +5608,7 @@ var init_src4 = __esm({
|
|
|
5427
5608
|
finalAssistantEventId: firstAssistantEventId
|
|
5428
5609
|
};
|
|
5429
5610
|
lane.channelContext = input.channelContext;
|
|
5611
|
+
lane.snipClientId = clientId;
|
|
5430
5612
|
try {
|
|
5431
5613
|
for await (const rawEvent of lane.runtime.executeTurn(
|
|
5432
5614
|
buildContext(lane.session.tree, userEvent.id),
|
|
@@ -5442,6 +5624,7 @@ var init_src4 = __esm({
|
|
|
5442
5624
|
}
|
|
5443
5625
|
} finally {
|
|
5444
5626
|
lane.channelContext = void 0;
|
|
5627
|
+
lane.snipClientId = void 0;
|
|
5445
5628
|
lane.runtime.unregisterTool("SendChannelMessage");
|
|
5446
5629
|
}
|
|
5447
5630
|
const result = { userEventId, assistantEventId: state.finalAssistantEventId };
|
|
@@ -5588,7 +5771,7 @@ var init_src4 = __esm({
|
|
|
5588
5771
|
createdAt: now,
|
|
5589
5772
|
updatedAt: now,
|
|
5590
5773
|
clientId: connection.clientId,
|
|
5591
|
-
...request.options?.channelContext ? { data: { channelContext: request.options.channelContext } } : {}
|
|
5774
|
+
...request.options?.channelContext || request.options?.modelSelection ? { data: { channelContext: request.options.channelContext, modelSelection: request.options.modelSelection } } : {}
|
|
5592
5775
|
};
|
|
5593
5776
|
lane.followUpWaiters.set(item.id, { connection, request });
|
|
5594
5777
|
await this.#appendQueueRewrite(lane, "follow_up", [...lane.session.tree.controlState.queues.follow_up, item], {
|
|
@@ -5641,6 +5824,7 @@ var init_src4 = __esm({
|
|
|
5641
5824
|
parentId: lane.session.activeLeafId,
|
|
5642
5825
|
source: "follow_up",
|
|
5643
5826
|
queueItemId: item.id,
|
|
5827
|
+
modelSelection: parseQueuedModelSelection(item.data?.modelSelection),
|
|
5644
5828
|
channelContext: parseQueuedChannelContext(item.data?.channelContext),
|
|
5645
5829
|
onComplete: waiter ? (result) => this.#respond(waiter.connection, waiter.request, { ...result, status: "completed" }) : void 0
|
|
5646
5830
|
});
|
|
@@ -6623,6 +6807,7 @@ var init_src4 = __esm({
|
|
|
6623
6807
|
session: loaded,
|
|
6624
6808
|
project,
|
|
6625
6809
|
runtime,
|
|
6810
|
+
...selectedModel ? { selectedModel } : {},
|
|
6626
6811
|
queue: Promise.resolve(),
|
|
6627
6812
|
appendQueue: Promise.resolve(),
|
|
6628
6813
|
followUpWaiters: /* @__PURE__ */ new Map()
|
|
@@ -6674,6 +6859,7 @@ var init_src4 = __esm({
|
|
|
6674
6859
|
session,
|
|
6675
6860
|
project,
|
|
6676
6861
|
runtime,
|
|
6862
|
+
...selectedModel ? { selectedModel } : {},
|
|
6677
6863
|
queue: Promise.resolve(),
|
|
6678
6864
|
appendQueue: Promise.resolve(),
|
|
6679
6865
|
followUpWaiters: /* @__PURE__ */ new Map()
|
|
@@ -6688,6 +6874,106 @@ var init_src4 = __esm({
|
|
|
6688
6874
|
listNames: () => Object.keys(lane.session.tree.controlState.skillIndex).sort()
|
|
6689
6875
|
})
|
|
6690
6876
|
);
|
|
6877
|
+
lane.runtime.registerTool(
|
|
6878
|
+
createSnipTool({
|
|
6879
|
+
snip: async (input) => this.#snipUserTurn(lane, input.userMessageId, input.reason)
|
|
6880
|
+
})
|
|
6881
|
+
);
|
|
6882
|
+
}
|
|
6883
|
+
async #snipUserTurn(lane, userMessageId, reason) {
|
|
6884
|
+
const leafId = lane.session.activeLeafId;
|
|
6885
|
+
if (!leafId) {
|
|
6886
|
+
throw new Error("snip requires an active conversation");
|
|
6887
|
+
}
|
|
6888
|
+
const path = lane.session.tree.getPath(leafId);
|
|
6889
|
+
const anchorUserEventId = this.#resolveSnipUserMessageId(lane, path, userMessageId);
|
|
6890
|
+
const anchorIndex = path.findIndex((id) => id === anchorUserEventId);
|
|
6891
|
+
if (anchorIndex < 0) {
|
|
6892
|
+
throw new Error(`snip target is not on the active conversation path: ${anchorUserEventId}`);
|
|
6893
|
+
}
|
|
6894
|
+
const anchor = lane.session.tree.get(anchorUserEventId)?.event;
|
|
6895
|
+
if (anchor?.type !== "user_message") {
|
|
6896
|
+
throw new Error(`snip target must be a user_message: ${anchorUserEventId}`);
|
|
6897
|
+
}
|
|
6898
|
+
const nextUserIndex = path.findIndex(
|
|
6899
|
+
(id, index) => index > anchorIndex && lane.session.tree.get(id)?.event.type === "user_message"
|
|
6900
|
+
);
|
|
6901
|
+
if (nextUserIndex < 0) {
|
|
6902
|
+
throw new Error("snip cannot hide the current user turn before the next user message exists");
|
|
6903
|
+
}
|
|
6904
|
+
const throughEventId = path[nextUserIndex - 1];
|
|
6905
|
+
if (!throughEventId || throughEventId === anchorUserEventId) {
|
|
6906
|
+
throw new Error(`snip target has no completed turn content: ${anchorUserEventId}`);
|
|
6907
|
+
}
|
|
6908
|
+
const clientId = lane.snipClientId;
|
|
6909
|
+
if (!clientId) {
|
|
6910
|
+
throw new Error("snip is only available while a user turn is running");
|
|
6911
|
+
}
|
|
6912
|
+
await this.#appendPersistent(lane, {
|
|
6913
|
+
type: "context_control",
|
|
6914
|
+
id: asEventId(this.#createId()),
|
|
6915
|
+
parentId: null,
|
|
6916
|
+
sessionId: lane.session.header.sessionId,
|
|
6917
|
+
clientId,
|
|
6918
|
+
ts: this.#now(),
|
|
6919
|
+
operation: "hide_user_turn",
|
|
6920
|
+
anchorUserEventId,
|
|
6921
|
+
throughEventId,
|
|
6922
|
+
actor: "agent",
|
|
6923
|
+
...reason ? { reason } : {}
|
|
6924
|
+
});
|
|
6925
|
+
await this.#appendDiagnostic(lane.session.header.sessionId, "context_snipped", {
|
|
6926
|
+
anchorUserEventId,
|
|
6927
|
+
throughEventId,
|
|
6928
|
+
hiddenEventCount: nextUserIndex - anchorIndex
|
|
6929
|
+
});
|
|
6930
|
+
return {
|
|
6931
|
+
anchorUserEventId,
|
|
6932
|
+
throughEventId,
|
|
6933
|
+
hiddenEventCount: nextUserIndex - anchorIndex
|
|
6934
|
+
};
|
|
6935
|
+
}
|
|
6936
|
+
#resolveSnipUserMessageId(lane, path, userMessageId) {
|
|
6937
|
+
if (path.includes(userMessageId)) {
|
|
6938
|
+
return userMessageId;
|
|
6939
|
+
}
|
|
6940
|
+
const matches = path.filter((id) => {
|
|
6941
|
+
const event = lane.session.tree.get(id)?.event;
|
|
6942
|
+
return event?.type === "user_message" && snipUserMessageAlias(id) === userMessageId;
|
|
6943
|
+
});
|
|
6944
|
+
if (matches.length === 1) {
|
|
6945
|
+
return matches[0];
|
|
6946
|
+
}
|
|
6947
|
+
if (matches.length > 1) {
|
|
6948
|
+
throw new Error(`snip target short id is ambiguous: ${userMessageId}`);
|
|
6949
|
+
}
|
|
6950
|
+
return asEventId(userMessageId);
|
|
6951
|
+
}
|
|
6952
|
+
async #selectChatRuntime(lane, modelSelection) {
|
|
6953
|
+
if (!modelSelection) {
|
|
6954
|
+
return;
|
|
6955
|
+
}
|
|
6956
|
+
const selectedModel = await this.#selectedModelFromMeta(
|
|
6957
|
+
{ projectId: lane.project.projectId, modelSelection },
|
|
6958
|
+
lane.project
|
|
6959
|
+
);
|
|
6960
|
+
if (!selectedModel || lane.selectedModel?.modelId === selectedModel.modelId) {
|
|
6961
|
+
return;
|
|
6962
|
+
}
|
|
6963
|
+
lane.runtime = await this.#createRuntime({
|
|
6964
|
+
sessionId: lane.session.header.sessionId,
|
|
6965
|
+
project: lane.project,
|
|
6966
|
+
selectedModel,
|
|
6967
|
+
purpose: "chat"
|
|
6968
|
+
});
|
|
6969
|
+
lane.selectedModel = selectedModel;
|
|
6970
|
+
this.#registerLaneTools(lane);
|
|
6971
|
+
await this.#appendDiagnostic(lane.session.header.sessionId, "chat_model_selected", {
|
|
6972
|
+
projectId: lane.project.projectId,
|
|
6973
|
+
workDir: lane.project.workDir,
|
|
6974
|
+
selectedModelId: selectedModel.modelId,
|
|
6975
|
+
role: selectedModel.role
|
|
6976
|
+
});
|
|
6691
6977
|
}
|
|
6692
6978
|
#syncChannelTool(lane, channelContext) {
|
|
6693
6979
|
if (!channelContext) {
|
|
@@ -7249,9 +7535,10 @@ var init_src4 = __esm({
|
|
|
7249
7535
|
}
|
|
7250
7536
|
const persistedSelection = "selectedModel" in meta ? meta.selectedModel : void 0;
|
|
7251
7537
|
const requestedSelection = "modelSelection" in meta ? meta.modelSelection : void 0;
|
|
7538
|
+
const selectionInput = persistedSelection ? config.models[persistedSelection.modelId] ? { modelId: persistedSelection.modelId, role: persistedSelection.role } : persistedSelection.role ? { role: persistedSelection.role } : void 0 : requestedSelection;
|
|
7252
7539
|
const selection = resolveModelSelection(
|
|
7253
7540
|
config,
|
|
7254
|
-
|
|
7541
|
+
selectionInput
|
|
7255
7542
|
);
|
|
7256
7543
|
const model = resolvePiAiModel(selection.config);
|
|
7257
7544
|
return {
|
|
@@ -7423,7 +7710,14 @@ var init_src4 = __esm({
|
|
|
7423
7710
|
};
|
|
7424
7711
|
countContentBlocks = (message, type) => message.content.filter((block) => block.type === type).length;
|
|
7425
7712
|
normalizeContent = (content) => typeof content === "string" ? [{ type: "text", text: content }] : content;
|
|
7426
|
-
|
|
7713
|
+
snipUserMessageIdBlock = (userEventId) => ({
|
|
7714
|
+
type: "text",
|
|
7715
|
+
text: `<system-reminder>
|
|
7716
|
+
snip.userMessageId: ${snipUserMessageAlias(userEventId)}
|
|
7717
|
+
</system-reminder>`,
|
|
7718
|
+
visibility: "model"
|
|
7719
|
+
});
|
|
7720
|
+
inputText = (message) => message.content.flatMap((block) => block.type === "text" && block.visibility !== "model" ? [block.text] : []).join("\n").trim();
|
|
7427
7721
|
assistantText = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
|
|
7428
7722
|
messageText = (message) => {
|
|
7429
7723
|
const text = message.content.map((block) => {
|
|
@@ -7452,7 +7746,7 @@ var init_src4 = __esm({
|
|
|
7452
7746
|
return void 0;
|
|
7453
7747
|
}
|
|
7454
7748
|
const parsed = JSON.parse(text);
|
|
7455
|
-
if (!
|
|
7749
|
+
if (!isRecord9(parsed)) {
|
|
7456
7750
|
return void 0;
|
|
7457
7751
|
}
|
|
7458
7752
|
return {
|
|
@@ -7540,7 +7834,7 @@ var init_src4 = __esm({
|
|
|
7540
7834
|
}
|
|
7541
7835
|
};
|
|
7542
7836
|
parseRuntimeStats = (value) => {
|
|
7543
|
-
if (!
|
|
7837
|
+
if (!isRecord9(value) || !isRecord9(value.rtk)) {
|
|
7544
7838
|
return emptyRuntimeStats();
|
|
7545
7839
|
}
|
|
7546
7840
|
return {
|
|
@@ -7554,13 +7848,13 @@ var init_src4 = __esm({
|
|
|
7554
7848
|
};
|
|
7555
7849
|
};
|
|
7556
7850
|
parseRuntimeStatsBuckets = (value) => {
|
|
7557
|
-
if (!
|
|
7851
|
+
if (!isRecord9(value)) {
|
|
7558
7852
|
return {};
|
|
7559
7853
|
}
|
|
7560
7854
|
return Object.fromEntries(
|
|
7561
7855
|
Object.entries(value).map(([key, bucket]) => [
|
|
7562
7856
|
key,
|
|
7563
|
-
|
|
7857
|
+
isRecord9(bucket) ? {
|
|
7564
7858
|
outputTokens: nonNegativeInteger2(bucket.outputTokens),
|
|
7565
7859
|
savedTokens: nonNegativeInteger2(bucket.savedTokens)
|
|
7566
7860
|
} : { outputTokens: 0, savedTokens: 0 }
|
|
@@ -7578,11 +7872,11 @@ var init_src4 = __esm({
|
|
|
7578
7872
|
return bucket;
|
|
7579
7873
|
};
|
|
7580
7874
|
rtkSavingsFromToolResult = (result) => {
|
|
7581
|
-
if (!
|
|
7875
|
+
if (!isRecord9(result) || !isRecord9(result.details)) {
|
|
7582
7876
|
return void 0;
|
|
7583
7877
|
}
|
|
7584
7878
|
const rtk = result.details.rtk;
|
|
7585
|
-
if (!
|
|
7879
|
+
if (!isRecord9(rtk) || rtk.applied !== true) {
|
|
7586
7880
|
return void 0;
|
|
7587
7881
|
}
|
|
7588
7882
|
const outputTokens = nonNegativeInteger2(rtk.estimatedOutputTokens);
|
|
@@ -7627,7 +7921,7 @@ var init_src4 = __esm({
|
|
|
7627
7921
|
...context.data ? { data: context.data } : {}
|
|
7628
7922
|
});
|
|
7629
7923
|
parseQueuedChannelContext = (value) => {
|
|
7630
|
-
if (!
|
|
7924
|
+
if (!isRecord9(value)) {
|
|
7631
7925
|
return void 0;
|
|
7632
7926
|
}
|
|
7633
7927
|
if (typeof value.channel !== "string" || typeof value.externalConversationId !== "string") {
|
|
@@ -7639,9 +7933,22 @@ var init_src4 = __esm({
|
|
|
7639
7933
|
...typeof value.conversationType === "string" ? { conversationType: value.conversationType } : {},
|
|
7640
7934
|
...typeof value.senderDisplayName === "string" ? { senderDisplayName: value.senderDisplayName } : {},
|
|
7641
7935
|
...typeof value.mentionedBot === "boolean" ? { mentionedBot: value.mentionedBot } : {},
|
|
7642
|
-
...
|
|
7936
|
+
...isRecord9(value.data) ? { data: value.data } : {}
|
|
7643
7937
|
});
|
|
7644
7938
|
};
|
|
7939
|
+
parseQueuedModelSelection = (value) => {
|
|
7940
|
+
if (!isRecord9(value)) {
|
|
7941
|
+
return void 0;
|
|
7942
|
+
}
|
|
7943
|
+
const selection = {};
|
|
7944
|
+
if (typeof value.modelId === "string") {
|
|
7945
|
+
selection.modelId = value.modelId;
|
|
7946
|
+
}
|
|
7947
|
+
if (value.role === "primary" || value.role === "standard" || value.role === "auxiliary") {
|
|
7948
|
+
selection.role = value.role;
|
|
7949
|
+
}
|
|
7950
|
+
return selection.modelId || selection.role ? selection : void 0;
|
|
7951
|
+
};
|
|
7645
7952
|
imBindingKey = (extensionId, externalConversationId) => `${extensionId}:${externalConversationId}`;
|
|
7646
7953
|
defaultBuiltinExtensionsDir = () => findBuiltinExtensionsDir([
|
|
7647
7954
|
runtimeModuleDir(),
|
|
@@ -7672,7 +7979,7 @@ var init_src4 = __esm({
|
|
|
7672
7979
|
};
|
|
7673
7980
|
isSteerMessage = (text) => /^\/(?:steer|interrupt)\b/i.test(text.trim());
|
|
7674
7981
|
stripImCommandPrefix = (text) => text.trim().replace(/^\/(?:steer|interrupt)\s*/i, "").trim() || text;
|
|
7675
|
-
|
|
7982
|
+
isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7676
7983
|
parseMemoryUpdate = (raw) => {
|
|
7677
7984
|
const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
7678
7985
|
if (!text) {
|
|
@@ -7777,25 +8084,131 @@ var init_relay_cli = __esm({
|
|
|
7777
8084
|
}
|
|
7778
8085
|
});
|
|
7779
8086
|
|
|
8087
|
+
// apps/cli/src/update-cli.ts
|
|
8088
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
8089
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
8090
|
+
import { dirname as dirname9, join as join12 } from "node:path";
|
|
8091
|
+
import { fileURLToPath } from "node:url";
|
|
8092
|
+
import { promisify as promisify3 } from "node:util";
|
|
8093
|
+
var SCOREL_PACKAGE_NAME, AUTO_UPDATE_INTERVAL_MS, ACTIVE_WORK_STALE_MS, execFileAsync3, compareSemver, shouldRunAutoUpdate, createNpmPackageUpdater, readInstalledScorelVersion, runCliUpdate, writeUpdateUsage, parseSemver;
|
|
8094
|
+
var init_update_cli = __esm({
|
|
8095
|
+
"apps/cli/src/update-cli.ts"() {
|
|
8096
|
+
"use strict";
|
|
8097
|
+
SCOREL_PACKAGE_NAME = "@chanlerdev/scorel";
|
|
8098
|
+
AUTO_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
8099
|
+
ACTIVE_WORK_STALE_MS = 3 * 60 * 60 * 1e3;
|
|
8100
|
+
execFileAsync3 = promisify3(execFileCallback);
|
|
8101
|
+
compareSemver = (a, b) => {
|
|
8102
|
+
const left = parseSemver(a);
|
|
8103
|
+
const right = parseSemver(b);
|
|
8104
|
+
for (let index = 0; index < 3; index += 1) {
|
|
8105
|
+
const delta = left[index] - right[index];
|
|
8106
|
+
if (delta !== 0) return delta;
|
|
8107
|
+
}
|
|
8108
|
+
return 0;
|
|
8109
|
+
};
|
|
8110
|
+
shouldRunAutoUpdate = (activity) => !activity.activeWork || activity.now - activity.lastActiveWorkAt >= ACTIVE_WORK_STALE_MS;
|
|
8111
|
+
createNpmPackageUpdater = (options) => {
|
|
8112
|
+
const packageName = options.packageName ?? SCOREL_PACKAGE_NAME;
|
|
8113
|
+
const execFile3 = options.execFile ?? ((command, argv) => execFileAsync3(command, argv));
|
|
8114
|
+
return {
|
|
8115
|
+
async checkLatest() {
|
|
8116
|
+
const result = await execFile3("npm", ["view", packageName, "version"]);
|
|
8117
|
+
const latest = result.stdout.trim();
|
|
8118
|
+
if (!latest) {
|
|
8119
|
+
throw new Error(`npm did not return a latest version for ${packageName}`);
|
|
8120
|
+
}
|
|
8121
|
+
parseSemver(latest);
|
|
8122
|
+
return latest;
|
|
8123
|
+
},
|
|
8124
|
+
async update() {
|
|
8125
|
+
const latestVersion = await this.checkLatest();
|
|
8126
|
+
if (compareSemver(options.currentVersion, latestVersion) >= 0) {
|
|
8127
|
+
return { status: "current", currentVersion: options.currentVersion, latestVersion };
|
|
8128
|
+
}
|
|
8129
|
+
await execFile3("npm", ["install", "-g", `${packageName}@${latestVersion}`]);
|
|
8130
|
+
return { status: "updated", currentVersion: options.currentVersion, latestVersion };
|
|
8131
|
+
}
|
|
8132
|
+
};
|
|
8133
|
+
};
|
|
8134
|
+
readInstalledScorelVersion = async () => {
|
|
8135
|
+
const here = dirname9(fileURLToPath(import.meta.url));
|
|
8136
|
+
for (const candidate of [
|
|
8137
|
+
join12(here, "..", "package.json"),
|
|
8138
|
+
join12(here, "..", "..", "package.json"),
|
|
8139
|
+
join12(process.cwd(), "package.json")
|
|
8140
|
+
]) {
|
|
8141
|
+
try {
|
|
8142
|
+
const parsed = JSON.parse(await readFile12(candidate, "utf8"));
|
|
8143
|
+
if (typeof parsed.version === "string" && (parsed.name === SCOREL_PACKAGE_NAME || parsed.name === "@scorel/app-cli")) {
|
|
8144
|
+
return parsed.version;
|
|
8145
|
+
}
|
|
8146
|
+
} catch {
|
|
8147
|
+
}
|
|
8148
|
+
}
|
|
8149
|
+
return "0.0.0";
|
|
8150
|
+
};
|
|
8151
|
+
runCliUpdate = async (argv, io, options = {}) => {
|
|
8152
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
8153
|
+
writeUpdateUsage(io.output);
|
|
8154
|
+
return 0;
|
|
8155
|
+
}
|
|
8156
|
+
if (argv.length > 0) {
|
|
8157
|
+
writeUpdateUsage(io.error);
|
|
8158
|
+
return 1;
|
|
8159
|
+
}
|
|
8160
|
+
const currentVersion = options.currentVersion ?? await readInstalledScorelVersion();
|
|
8161
|
+
const updater = options.updater ?? createNpmPackageUpdater({ currentVersion });
|
|
8162
|
+
try {
|
|
8163
|
+
const result = await updater.update();
|
|
8164
|
+
if (result.status === "current") {
|
|
8165
|
+
io.output.write(`scorel is current (${result.currentVersion})
|
|
8166
|
+
`);
|
|
8167
|
+
} else {
|
|
8168
|
+
io.output.write(`updated scorel ${result.currentVersion} -> ${result.latestVersion}
|
|
8169
|
+
`);
|
|
8170
|
+
}
|
|
8171
|
+
return 0;
|
|
8172
|
+
} catch (cause) {
|
|
8173
|
+
io.error.write(`scorel update error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
8174
|
+
`);
|
|
8175
|
+
return 1;
|
|
8176
|
+
}
|
|
8177
|
+
};
|
|
8178
|
+
writeUpdateUsage = (output) => {
|
|
8179
|
+
output.write("Usage: scorel update\n scorel upgrade\n");
|
|
8180
|
+
};
|
|
8181
|
+
parseSemver = (version) => {
|
|
8182
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version);
|
|
8183
|
+
if (!match) {
|
|
8184
|
+
throw new Error(`Invalid semver version: ${version}`);
|
|
8185
|
+
}
|
|
8186
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
8187
|
+
};
|
|
8188
|
+
}
|
|
8189
|
+
});
|
|
8190
|
+
|
|
7780
8191
|
// apps/cli/src/daemon-cli.ts
|
|
7781
8192
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
7782
8193
|
import { spawn } from "node:child_process";
|
|
7783
8194
|
import { homedir as homedir6 } from "node:os";
|
|
7784
|
-
import { dirname as
|
|
7785
|
-
import { fileURLToPath } from "node:url";
|
|
7786
|
-
var DEFAULT_HOST, DEFAULT_PORT, STOP_POLL_INTERVAL_MS, STOP_GRACE_MS, START_READY_TIMEOUT_MS,
|
|
8195
|
+
import { dirname as dirname10, join as join13 } from "node:path";
|
|
8196
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8197
|
+
var DEFAULT_HOST, DEFAULT_PORT, STOP_POLL_INTERVAL_MS, STOP_GRACE_MS, START_READY_TIMEOUT_MS, AUTO_STARTED_IDLE_SHUTDOWN_MS, FOREGROUND_IDLE_SHUTDOWN_MS, defaultStateDir2, isLoopbackHost, formatTimestamp, runCliDaemon, runStartCommand, runServeCommand, startAutoUpdateLoop, stopRunningDaemon, runStatusCommand, runStopCommand, runResetCommand, formatStatusLine, parseServeFlags, parseStatusFlags, requireValue2, sleep, waitForDaemonReady, detachBackgroundDaemon, nodeEntrypointArgs, writeDaemonUsage;
|
|
7787
8198
|
var init_daemon_cli = __esm({
|
|
7788
8199
|
"apps/cli/src/daemon-cli.ts"() {
|
|
7789
8200
|
"use strict";
|
|
7790
8201
|
init_src4();
|
|
7791
8202
|
init_relay_cli();
|
|
8203
|
+
init_update_cli();
|
|
7792
8204
|
DEFAULT_HOST = "127.0.0.1";
|
|
7793
8205
|
DEFAULT_PORT = 7777;
|
|
7794
8206
|
STOP_POLL_INTERVAL_MS = 200;
|
|
7795
8207
|
STOP_GRACE_MS = 5e3;
|
|
7796
|
-
START_READY_TIMEOUT_MS =
|
|
7797
|
-
|
|
7798
|
-
|
|
8208
|
+
START_READY_TIMEOUT_MS = 3e4;
|
|
8209
|
+
AUTO_STARTED_IDLE_SHUTDOWN_MS = 15 * 60 * 1e3;
|
|
8210
|
+
FOREGROUND_IDLE_SHUTDOWN_MS = 0;
|
|
8211
|
+
defaultStateDir2 = () => join13(homedir6(), ".scorel");
|
|
7799
8212
|
isLoopbackHost = (host) => host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
7800
8213
|
formatTimestamp = (epochMs) => new Date(epochMs).toISOString();
|
|
7801
8214
|
runCliDaemon = async (argv, options) => {
|
|
@@ -7824,7 +8237,7 @@ var init_daemon_cli = __esm({
|
|
|
7824
8237
|
runStartCommand = async (argv, options) => {
|
|
7825
8238
|
let flags;
|
|
7826
8239
|
try {
|
|
7827
|
-
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env);
|
|
8240
|
+
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env, FOREGROUND_IDLE_SHUTDOWN_MS);
|
|
7828
8241
|
} catch (cause) {
|
|
7829
8242
|
options.error.write(`scorel daemon start error: ${cause.message}
|
|
7830
8243
|
`);
|
|
@@ -7837,7 +8250,7 @@ var init_daemon_cli = __esm({
|
|
|
7837
8250
|
`);
|
|
7838
8251
|
return 0;
|
|
7839
8252
|
}
|
|
7840
|
-
const cliEntrypoint = options.cliEntrypoint ??
|
|
8253
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath2(import.meta.url).replace(/daemon-cli\.ts$/, "index.ts");
|
|
7841
8254
|
const child = (options.spawn ?? spawn)(process.execPath, [
|
|
7842
8255
|
...nodeEntrypointArgs(cliEntrypoint),
|
|
7843
8256
|
"host",
|
|
@@ -7854,7 +8267,7 @@ var init_daemon_cli = __esm({
|
|
|
7854
8267
|
...flags.relayUrl ? ["--relay", flags.relayUrl] : ["--no-relay"],
|
|
7855
8268
|
...flags.replace ? ["--replace"] : []
|
|
7856
8269
|
], {
|
|
7857
|
-
cwd:
|
|
8270
|
+
cwd: dirname10(cliEntrypoint),
|
|
7858
8271
|
env: { ...process.env, ...options.env ?? {} },
|
|
7859
8272
|
detached: true,
|
|
7860
8273
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7881,7 +8294,7 @@ var init_daemon_cli = __esm({
|
|
|
7881
8294
|
runServeCommand = async (argv, options) => {
|
|
7882
8295
|
let flags;
|
|
7883
8296
|
try {
|
|
7884
|
-
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env);
|
|
8297
|
+
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env, FOREGROUND_IDLE_SHUTDOWN_MS);
|
|
7885
8298
|
} catch (cause) {
|
|
7886
8299
|
options.error.write(`scorel daemon serve error: ${cause.message}
|
|
7887
8300
|
`);
|
|
@@ -7914,9 +8327,10 @@ Use --replace to stop it and start a new one.
|
|
|
7914
8327
|
stopRequested = true;
|
|
7915
8328
|
resolveStopWaiter?.();
|
|
7916
8329
|
};
|
|
8330
|
+
const sessionsDir = options.sessionsDir ?? scorelSessionsDir(homedir6());
|
|
7917
8331
|
const daemon = new ScorelHost({
|
|
7918
|
-
sessionsDir
|
|
7919
|
-
projectsPath:
|
|
8332
|
+
sessionsDir,
|
|
8333
|
+
projectsPath: join13(options.stateDir, "projects.json"),
|
|
7920
8334
|
deviceId: identity.deviceId,
|
|
7921
8335
|
deviceDisplayName: identity.displayName,
|
|
7922
8336
|
idleShutdownMs: flags.idleShutdownMs,
|
|
@@ -7924,15 +8338,25 @@ Use --replace to stop it and start a new one.
|
|
|
7924
8338
|
scorelHomeDir: options.stateDir,
|
|
7925
8339
|
loadConfig: async ({ project }) => loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
7926
8340
|
loadConfigProfile: async ({ project }) => loadScorelConfigProfile({ cwd: project.workDir, ...configScope }),
|
|
7927
|
-
createRuntime: async ({ project, selectedModel, purpose }) => createRealRuntime({
|
|
8341
|
+
createRuntime: async ({ sessionId, project, selectedModel, purpose }) => createRealRuntime({
|
|
7928
8342
|
cwd: project.workDir,
|
|
7929
8343
|
config: await loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
8344
|
+
sessionsDir,
|
|
8345
|
+
sessionId,
|
|
7930
8346
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
7931
8347
|
includeTools: purpose === "chat"
|
|
7932
8348
|
})
|
|
7933
8349
|
});
|
|
7934
8350
|
await daemon.start();
|
|
7935
8351
|
await daemon.registerProject(flags.cwd);
|
|
8352
|
+
const autoUpdater = await startAutoUpdateLoop({
|
|
8353
|
+
host: daemon,
|
|
8354
|
+
requestStop,
|
|
8355
|
+
output: options.output,
|
|
8356
|
+
error: options.error,
|
|
8357
|
+
updater: options.packageUpdater,
|
|
8358
|
+
intervalMs: options.autoUpdateIntervalMs ?? AUTO_UPDATE_INTERVAL_MS
|
|
8359
|
+
});
|
|
7936
8360
|
const server = await startScorelHostWebSocketServer({
|
|
7937
8361
|
hostService: daemon,
|
|
7938
8362
|
host: flags.host,
|
|
@@ -7978,6 +8402,7 @@ Use --replace to stop it and start a new one.
|
|
|
7978
8402
|
}
|
|
7979
8403
|
const shutdown = async () => {
|
|
7980
8404
|
try {
|
|
8405
|
+
autoUpdater.stop();
|
|
7981
8406
|
relayClient?.close();
|
|
7982
8407
|
await server.close();
|
|
7983
8408
|
} finally {
|
|
@@ -8028,6 +8453,42 @@ Use --replace to stop it and start a new one.
|
|
|
8028
8453
|
`);
|
|
8029
8454
|
return 0;
|
|
8030
8455
|
};
|
|
8456
|
+
startAutoUpdateLoop = async (options) => {
|
|
8457
|
+
const updater = options.updater ?? createNpmPackageUpdater({ currentVersion: await readInstalledScorelVersion() });
|
|
8458
|
+
let timer;
|
|
8459
|
+
let running = false;
|
|
8460
|
+
const tick = async () => {
|
|
8461
|
+
if (running) return;
|
|
8462
|
+
running = true;
|
|
8463
|
+
try {
|
|
8464
|
+
const activity = options.host.activityStatus();
|
|
8465
|
+
if (!shouldRunAutoUpdate({ ...activity, now: Date.now() })) {
|
|
8466
|
+
return;
|
|
8467
|
+
}
|
|
8468
|
+
const result = await updater.update();
|
|
8469
|
+
if (result.status === "updated") {
|
|
8470
|
+
options.output.write(`scorel auto-updated ${result.currentVersion} -> ${result.latestVersion}; restarting host
|
|
8471
|
+
`);
|
|
8472
|
+
options.requestStop("auto-update");
|
|
8473
|
+
}
|
|
8474
|
+
} catch (cause) {
|
|
8475
|
+
options.error.write(`scorel auto-update error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
8476
|
+
`);
|
|
8477
|
+
} finally {
|
|
8478
|
+
running = false;
|
|
8479
|
+
}
|
|
8480
|
+
};
|
|
8481
|
+
timer = setInterval(() => void tick(), options.intervalMs);
|
|
8482
|
+
timer.unref?.();
|
|
8483
|
+
return {
|
|
8484
|
+
stop() {
|
|
8485
|
+
if (timer) {
|
|
8486
|
+
clearInterval(timer);
|
|
8487
|
+
timer = void 0;
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
};
|
|
8491
|
+
};
|
|
8031
8492
|
stopRunningDaemon = async (state, options) => {
|
|
8032
8493
|
try {
|
|
8033
8494
|
process.kill(state.pid, "SIGTERM");
|
|
@@ -8131,14 +8592,14 @@ Use --replace to stop it and start a new one.
|
|
|
8131
8592
|
const stoppedAt = state.stoppedAt !== null ? formatTimestamp(state.stoppedAt) : "unknown";
|
|
8132
8593
|
return `stopped url=${state.wsUrl} last-pid=${state.pid} stoppedAt=${stoppedAt} liveness=${liveness}`;
|
|
8133
8594
|
};
|
|
8134
|
-
parseServeFlags = (argv, defaultCwd, env) => {
|
|
8595
|
+
parseServeFlags = (argv, defaultCwd, env, defaultIdleShutdownMs) => {
|
|
8135
8596
|
let host = DEFAULT_HOST;
|
|
8136
8597
|
let port = DEFAULT_PORT;
|
|
8137
8598
|
let cwd = defaultCwd;
|
|
8138
8599
|
let token;
|
|
8139
8600
|
let relayUrl = resolveDefaultRelayUrl(env);
|
|
8140
8601
|
let replace = false;
|
|
8141
|
-
let idleShutdownMs =
|
|
8602
|
+
let idleShutdownMs = defaultIdleShutdownMs;
|
|
8142
8603
|
for (let index = 0; index < argv.length; index += 1) {
|
|
8143
8604
|
const arg = argv[index];
|
|
8144
8605
|
if (arg === "--host") {
|
|
@@ -8475,8 +8936,8 @@ var init_routing = __esm({
|
|
|
8475
8936
|
});
|
|
8476
8937
|
|
|
8477
8938
|
// apps/relay/src/store.ts
|
|
8478
|
-
import { mkdir as mkdir7, readFile as
|
|
8479
|
-
import { join as
|
|
8939
|
+
import { mkdir as mkdir7, readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
|
|
8940
|
+
import { join as join14 } from "node:path";
|
|
8480
8941
|
var FileRelayStore, emptyStoreFile;
|
|
8481
8942
|
var init_store = __esm({
|
|
8482
8943
|
"apps/relay/src/store.ts"() {
|
|
@@ -8486,7 +8947,7 @@ var init_store = __esm({
|
|
|
8486
8947
|
#now;
|
|
8487
8948
|
#queue = Promise.resolve();
|
|
8488
8949
|
constructor(options) {
|
|
8489
|
-
this.#filePath =
|
|
8950
|
+
this.#filePath = join14(options.dataDir, "relay-store.json");
|
|
8490
8951
|
this.#now = options.now ?? Date.now;
|
|
8491
8952
|
}
|
|
8492
8953
|
async upsertDevice(record) {
|
|
@@ -8529,7 +8990,7 @@ var init_store = __esm({
|
|
|
8529
8990
|
this.#queue = this.#queue.then(async () => {
|
|
8530
8991
|
const file = await this.#read();
|
|
8531
8992
|
mutator(file);
|
|
8532
|
-
await mkdir7(
|
|
8993
|
+
await mkdir7(join14(this.#filePath, ".."), { recursive: true });
|
|
8533
8994
|
await writeFile7(this.#filePath, `${JSON.stringify(file, null, 2)}
|
|
8534
8995
|
`);
|
|
8535
8996
|
});
|
|
@@ -8537,7 +8998,7 @@ var init_store = __esm({
|
|
|
8537
8998
|
}
|
|
8538
8999
|
async #read() {
|
|
8539
9000
|
try {
|
|
8540
|
-
const raw = JSON.parse(await
|
|
9001
|
+
const raw = JSON.parse(await readFile13(this.#filePath, "utf8"));
|
|
8541
9002
|
if (raw.version !== 1 || !Array.isArray(raw.devices) || !Array.isArray(raw.clients) || !Array.isArray(raw.bindings)) {
|
|
8542
9003
|
return emptyStoreFile();
|
|
8543
9004
|
}
|
|
@@ -8790,7 +9251,7 @@ var init_library = __esm({
|
|
|
8790
9251
|
|
|
8791
9252
|
// apps/cli/src/relay-server-cli.ts
|
|
8792
9253
|
import { homedir as homedir7 } from "node:os";
|
|
8793
|
-
import { join as
|
|
9254
|
+
import { join as join15 } from "node:path";
|
|
8794
9255
|
var DEFAULT_HOST2, DEFAULT_PORT2, runCliRelay, runRelayServe, parseRelayServeFlags, waitForStop, requireValue3, writeRelayUsage;
|
|
8795
9256
|
var init_relay_server_cli = __esm({
|
|
8796
9257
|
"apps/cli/src/relay-server-cli.ts"() {
|
|
@@ -8842,7 +9303,7 @@ var init_relay_server_cli = __esm({
|
|
|
8842
9303
|
parseRelayServeFlags = (argv) => {
|
|
8843
9304
|
let host = DEFAULT_HOST2;
|
|
8844
9305
|
let port = DEFAULT_PORT2;
|
|
8845
|
-
let dataDir =
|
|
9306
|
+
let dataDir = join15(homedir7(), ".scorel", "relay");
|
|
8846
9307
|
for (let index = 0; index < argv.length; index += 1) {
|
|
8847
9308
|
const arg = argv[index];
|
|
8848
9309
|
if (arg === "--host") {
|
|
@@ -8900,17 +9361,18 @@ var init_relay_server_cli = __esm({
|
|
|
8900
9361
|
// apps/cli/src/up-cli.ts
|
|
8901
9362
|
import { spawn as spawn2 } from "node:child_process";
|
|
8902
9363
|
import { homedir as homedir8 } from "node:os";
|
|
8903
|
-
import { dirname as
|
|
8904
|
-
import { fileURLToPath as
|
|
9364
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
9365
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
8905
9366
|
var DEFAULT_DAEMON_PORT, DEFAULT_WEBUI_PORT, DEFAULT_DAEMON_READY_TIMEOUT_MS, defaultStateDir3, defaultAttachSigint, runCliUp, parseUpFlags, requireValue4, waitForDaemonReady2, pipeWithPrefix, detachBackgroundDaemon2, nodeEntrypointArgs2, pipeStreamLines, once;
|
|
8906
9367
|
var init_up_cli = __esm({
|
|
8907
9368
|
"apps/cli/src/up-cli.ts"() {
|
|
8908
9369
|
"use strict";
|
|
8909
9370
|
init_src4();
|
|
9371
|
+
init_daemon_cli();
|
|
8910
9372
|
DEFAULT_DAEMON_PORT = 7777;
|
|
8911
9373
|
DEFAULT_WEBUI_PORT = 3e3;
|
|
8912
|
-
DEFAULT_DAEMON_READY_TIMEOUT_MS =
|
|
8913
|
-
defaultStateDir3 = () =>
|
|
9374
|
+
DEFAULT_DAEMON_READY_TIMEOUT_MS = 3e4;
|
|
9375
|
+
defaultStateDir3 = () => join16(homedir8(), ".scorel");
|
|
8914
9376
|
defaultAttachSigint = (listener) => {
|
|
8915
9377
|
process.on("SIGINT", listener);
|
|
8916
9378
|
return () => process.off("SIGINT", listener);
|
|
@@ -8925,7 +9387,7 @@ var init_up_cli = __esm({
|
|
|
8925
9387
|
return 1;
|
|
8926
9388
|
}
|
|
8927
9389
|
const stateDir = options.stateDir ?? defaultStateDir3();
|
|
8928
|
-
const cliEntrypoint = options.cliEntrypoint ??
|
|
9390
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath3(import.meta.url).replace(/up-cli\.ts$/, "index.ts");
|
|
8929
9391
|
const spawnFn = options.spawn ?? spawn2;
|
|
8930
9392
|
const readState = options.readState ?? ((dir) => readLocalDaemonState({ stateDir: dir }));
|
|
8931
9393
|
const attachSigint = options.attachSigint ?? defaultAttachSigint;
|
|
@@ -8944,10 +9406,12 @@ var init_up_cli = __esm({
|
|
|
8944
9406
|
String(flags.daemonPort),
|
|
8945
9407
|
"--cwd",
|
|
8946
9408
|
flags.cwd,
|
|
9409
|
+
"--idle-timeout-ms",
|
|
9410
|
+
String(AUTO_STARTED_IDLE_SHUTDOWN_MS),
|
|
8947
9411
|
"--no-relay"
|
|
8948
9412
|
];
|
|
8949
9413
|
daemonChild = spawnFn(process.execPath, daemonArgs, {
|
|
8950
|
-
cwd:
|
|
9414
|
+
cwd: dirname11(cliEntrypoint),
|
|
8951
9415
|
env: { ...process.env },
|
|
8952
9416
|
detached: true,
|
|
8953
9417
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8977,7 +9441,7 @@ var init_up_cli = __esm({
|
|
|
8977
9441
|
String(flags.webuiPort)
|
|
8978
9442
|
];
|
|
8979
9443
|
const webuiChild = spawnFn(process.execPath, webuiArgs, {
|
|
8980
|
-
cwd:
|
|
9444
|
+
cwd: dirname11(cliEntrypoint),
|
|
8981
9445
|
env: { ...process.env },
|
|
8982
9446
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8983
9447
|
});
|
|
@@ -9142,8 +9606,8 @@ var init_up_cli = __esm({
|
|
|
9142
9606
|
// apps/cli/src/webui-cli.ts
|
|
9143
9607
|
import { spawn as spawn3 } from "node:child_process";
|
|
9144
9608
|
import { existsSync as existsSync4 } from "node:fs";
|
|
9145
|
-
import { dirname as
|
|
9146
|
-
import { fileURLToPath as
|
|
9609
|
+
import { dirname as dirname12, resolve as resolve6 } from "node:path";
|
|
9610
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9147
9611
|
var DEFAULT_PORT3, DEFAULT_HOST3, runCliWebUi, findWebuiAppDir, buildWebUiSpawnPlan, parseWebUiFlags, requireValue5, waitForChildExit;
|
|
9148
9612
|
var init_webui_cli = __esm({
|
|
9149
9613
|
"apps/cli/src/webui-cli.ts"() {
|
|
@@ -9174,7 +9638,7 @@ var init_webui_cli = __esm({
|
|
|
9174
9638
|
return await waitForChildExit(child, options);
|
|
9175
9639
|
};
|
|
9176
9640
|
findWebuiAppDir = () => {
|
|
9177
|
-
let cursor =
|
|
9641
|
+
let cursor = dirname12(fileURLToPath4(import.meta.url));
|
|
9178
9642
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
9179
9643
|
const candidate = resolve6(cursor, "apps/webui/package.json");
|
|
9180
9644
|
if (existsSync4(candidate)) {
|
|
@@ -9262,12 +9726,12 @@ __export(index_exports, {
|
|
|
9262
9726
|
runChat: () => runChat,
|
|
9263
9727
|
runCli: () => runCli
|
|
9264
9728
|
});
|
|
9265
|
-
import { createHash as
|
|
9266
|
-
import { appendFile as appendFile4, mkdir as mkdir8, readFile as
|
|
9729
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9730
|
+
import { appendFile as appendFile4, mkdir as mkdir8, readFile as readFile14, realpath as realpath3, readdir as readdir7, writeFile as writeFile8 } from "node:fs/promises";
|
|
9267
9731
|
import { createInterface } from "node:readline/promises";
|
|
9268
9732
|
import { homedir as homedir9 } from "node:os";
|
|
9269
|
-
import { fileURLToPath as
|
|
9270
|
-
import { basename as basename4, dirname as
|
|
9733
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
9734
|
+
import { basename as basename4, dirname as dirname13, join as join17 } from "node:path";
|
|
9271
9735
|
var cliAppName, cliClientDependency, cliDaemonDependency, defaultSessionsDir, defaultStateDir4, runCli, runProject, runLogs, runAttach, attachCacheScope, attachCacheFilePath, attachDiagnosticsFilePath, findAttachDiagnosticsFilePath, stateDirFromSessionsDir, AttachDiagnostics, readAttachCache, writeAttachCache, emptyAttachCacheSnapshot, mergePersistentEvents, highestSeq, highestCachedStreamSeq, updateAttachCacheSnapshot, removeCompletedTransients, isCachedTransientMessage, AsyncInputQueue, parseAttachOptions, parseLogsOptions, runChat, createSigintHandler, loadOrCreateSession, parseChatOptions, requireValue6, promptIfInteractive, writeUsage, writeProjectUsage, writeEventError, writeToolResult, redactDiagnosticFields, formatDiagnosticLine2, formatDiagnosticValue2, AttachEventRenderer, blocksToText, isCliEntrypoint;
|
|
9272
9736
|
var init_index = __esm({
|
|
9273
9737
|
async "apps/cli/src/index.ts"() {
|
|
@@ -9280,13 +9744,19 @@ var init_index = __esm({
|
|
|
9280
9744
|
init_relay_server_cli();
|
|
9281
9745
|
init_up_cli();
|
|
9282
9746
|
init_webui_cli();
|
|
9747
|
+
init_update_cli();
|
|
9283
9748
|
cliAppName = "@scorel/app-cli";
|
|
9284
9749
|
cliClientDependency = clientPackageName;
|
|
9285
9750
|
cliDaemonDependency = daemonPackageName;
|
|
9286
9751
|
defaultSessionsDir = () => scorelSessionsDir(homedir9());
|
|
9287
|
-
defaultStateDir4 = () =>
|
|
9752
|
+
defaultStateDir4 = () => join17(homedir9(), ".scorel");
|
|
9288
9753
|
runCli = async (argv, io = { input: process.stdin, output: process.stdout, error: process.stderr }, runOptions = {}) => {
|
|
9289
9754
|
const [command, ...rest] = argv;
|
|
9755
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
9756
|
+
io.output.write(`${await readInstalledScorelVersion()}
|
|
9757
|
+
`);
|
|
9758
|
+
return 0;
|
|
9759
|
+
}
|
|
9290
9760
|
if (!command || command === "chat") {
|
|
9291
9761
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
9292
9762
|
writeUsage(io.output);
|
|
@@ -9332,6 +9802,9 @@ var init_index = __esm({
|
|
|
9332
9802
|
error: io.error
|
|
9333
9803
|
});
|
|
9334
9804
|
}
|
|
9805
|
+
if (command === "update" || command === "upgrade") {
|
|
9806
|
+
return runCliUpdate(rest, { output: io.output, error: io.error });
|
|
9807
|
+
}
|
|
9335
9808
|
if (command === "attach") {
|
|
9336
9809
|
try {
|
|
9337
9810
|
return runAttach(parseAttachOptions(rest), {
|
|
@@ -9419,10 +9892,10 @@ var init_index = __esm({
|
|
|
9419
9892
|
}
|
|
9420
9893
|
};
|
|
9421
9894
|
runLogs = async (options, io) => {
|
|
9422
|
-
const filePath = options.attach ? await findAttachDiagnosticsFilePath(io.stateDir, options.sessionId, options.remoteUrl) :
|
|
9895
|
+
const filePath = options.attach ? await findAttachDiagnosticsFilePath(io.stateDir, options.sessionId, options.remoteUrl) : join17(io.sessionsDir, `${options.sessionId}.log`);
|
|
9423
9896
|
let content;
|
|
9424
9897
|
try {
|
|
9425
|
-
content = await
|
|
9898
|
+
content = await readFile14(filePath, "utf8");
|
|
9426
9899
|
} catch (cause) {
|
|
9427
9900
|
io.error.write(`scorel logs error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
9428
9901
|
`);
|
|
@@ -9575,32 +10048,32 @@ var init_index = __esm({
|
|
|
9575
10048
|
};
|
|
9576
10049
|
};
|
|
9577
10050
|
attachCacheFilePath = (stateDir, scope, sessionId) => {
|
|
9578
|
-
const scopeKey =
|
|
9579
|
-
return
|
|
10051
|
+
const scopeKey = createHash4("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
10052
|
+
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.json`);
|
|
9580
10053
|
};
|
|
9581
10054
|
attachDiagnosticsFilePath = (stateDir, scope, sessionId) => {
|
|
9582
|
-
const scopeKey =
|
|
9583
|
-
return
|
|
10055
|
+
const scopeKey = createHash4("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
10056
|
+
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.log`);
|
|
9584
10057
|
};
|
|
9585
10058
|
findAttachDiagnosticsFilePath = async (stateDir, sessionId, _remoteUrl) => {
|
|
9586
|
-
const root =
|
|
10059
|
+
const root = join17(stateDir, "attach-cache");
|
|
9587
10060
|
const scopes = await readdir7(root).catch(() => []);
|
|
9588
10061
|
for (const scope of scopes) {
|
|
9589
|
-
const candidate =
|
|
10062
|
+
const candidate = join17(root, scope, `${sessionId}.log`);
|
|
9590
10063
|
try {
|
|
9591
|
-
await
|
|
10064
|
+
await readFile14(candidate, "utf8");
|
|
9592
10065
|
return candidate;
|
|
9593
10066
|
} catch {
|
|
9594
10067
|
continue;
|
|
9595
10068
|
}
|
|
9596
10069
|
}
|
|
9597
|
-
return
|
|
10070
|
+
return join17(root, "__missing__", `${sessionId}.log`);
|
|
9598
10071
|
};
|
|
9599
10072
|
stateDirFromSessionsDir = (sessionsDir) => {
|
|
9600
10073
|
if (!sessionsDir) {
|
|
9601
10074
|
return defaultStateDir4();
|
|
9602
10075
|
}
|
|
9603
|
-
return basename4(sessionsDir) === "sessions" ?
|
|
10076
|
+
return basename4(sessionsDir) === "sessions" ? dirname13(sessionsDir) : sessionsDir;
|
|
9604
10077
|
};
|
|
9605
10078
|
AttachDiagnostics = class {
|
|
9606
10079
|
#stateDir;
|
|
@@ -9652,14 +10125,14 @@ var init_index = __esm({
|
|
|
9652
10125
|
}
|
|
9653
10126
|
const filePath = attachDiagnosticsFilePath(this.#stateDir, this.#scope, this.#sessionId);
|
|
9654
10127
|
this.#writes.push(
|
|
9655
|
-
mkdir8(
|
|
10128
|
+
mkdir8(dirname13(filePath), { recursive: true }).then(() => appendFile4(filePath, `${line}
|
|
9656
10129
|
`, "utf8"))
|
|
9657
10130
|
);
|
|
9658
10131
|
}
|
|
9659
10132
|
};
|
|
9660
10133
|
readAttachCache = async (stateDir, scope, sessionId) => {
|
|
9661
10134
|
try {
|
|
9662
|
-
const raw = JSON.parse(await
|
|
10135
|
+
const raw = JSON.parse(await readFile14(attachCacheFilePath(stateDir, scope, sessionId), "utf8"));
|
|
9663
10136
|
if (raw.version !== 1 || raw.sessionId !== String(sessionId) || raw.scope.kind !== scope.kind || raw.scope.locator !== scope.locator || !Array.isArray(raw.events)) {
|
|
9664
10137
|
return emptyAttachCacheSnapshot();
|
|
9665
10138
|
}
|
|
@@ -9682,7 +10155,7 @@ var init_index = __esm({
|
|
|
9682
10155
|
const filePath = attachCacheFilePath(stateDir, scope, sessionId);
|
|
9683
10156
|
const uniqueEvents = mergePersistentEvents(snapshot.events);
|
|
9684
10157
|
const transients = removeCompletedTransients(snapshot.transients, uniqueEvents);
|
|
9685
|
-
await mkdir8(
|
|
10158
|
+
await mkdir8(dirname13(filePath), { recursive: true });
|
|
9686
10159
|
await writeFile8(
|
|
9687
10160
|
filePath,
|
|
9688
10161
|
`${JSON.stringify({ version: 1, scope, sessionId: String(sessionId), events: uniqueEvents, transients }, null, 2)}
|
|
@@ -9824,14 +10297,16 @@ var init_index = __esm({
|
|
|
9824
10297
|
const loadProjectConfigProfile = async (project2) => options.config ?? await loadScorelConfigProfile({ cwd: project2.workDir, ...configScope });
|
|
9825
10298
|
const daemon = new ScorelHost({
|
|
9826
10299
|
sessionsDir: options.sessionsDir,
|
|
9827
|
-
projectsPath:
|
|
10300
|
+
projectsPath: join17(options.stateDir, "projects.json"),
|
|
9828
10301
|
deviceId: asDeviceId("device_local"),
|
|
9829
10302
|
scorelHomeDir: options.stateDir,
|
|
9830
10303
|
loadConfig: async ({ project: project2 }) => loadProjectConfig(project2),
|
|
9831
10304
|
loadConfigProfile: async ({ project: project2 }) => loadProjectConfigProfile(project2),
|
|
9832
|
-
createRuntime: async ({ project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
10305
|
+
createRuntime: async ({ sessionId, project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
9833
10306
|
cwd: project2.workDir,
|
|
9834
10307
|
config: await loadProjectConfig(project2),
|
|
10308
|
+
sessionsDir: options.sessionsDir,
|
|
10309
|
+
sessionId,
|
|
9835
10310
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
9836
10311
|
includeTools: purpose === "chat"
|
|
9837
10312
|
})
|
|
@@ -9971,6 +10446,9 @@ var init_index = __esm({
|
|
|
9971
10446
|
" scorel relay serve [--host <h>] [--port <p>] [--data-dir <dir>]",
|
|
9972
10447
|
" scorel webui [--port <p>] [--host <h>]",
|
|
9973
10448
|
" scorel up [--daemon-port <p>] [--webui-port <p>] [--cwd <d>]",
|
|
10449
|
+
" scorel update",
|
|
10450
|
+
" scorel upgrade",
|
|
10451
|
+
" scorel version",
|
|
9974
10452
|
" scorel logs [--attach] --session <id> [--remote <ws-url>] [--tail <n>]",
|
|
9975
10453
|
" scorel project list",
|
|
9976
10454
|
" scorel project add <dir>",
|
|
@@ -10102,7 +10580,7 @@ ${text}
|
|
|
10102
10580
|
if (!process.argv[1]) return false;
|
|
10103
10581
|
const [argvPath, modulePath] = await Promise.all([
|
|
10104
10582
|
realpath3(process.argv[1]).catch(() => process.argv[1]),
|
|
10105
|
-
realpath3(
|
|
10583
|
+
realpath3(fileURLToPath5(import.meta.url)).catch(() => fileURLToPath5(import.meta.url))
|
|
10106
10584
|
]);
|
|
10107
10585
|
return argvPath === modulePath;
|
|
10108
10586
|
};
|