@chanlerdev/scorel 0.0.5 → 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 +8 -1
- package/dist/index.js +306 -137
- package/dist/index.js.map +3 -3
- package/docs/CHANGELOG.md +32 -0
- package/docs/ROADMAP.md +6 -0
- package/docs/SHIP.md +1 -0
- package/docs/spec/events.md +17 -1
- package/docs/spec/session.md +12 -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,6 +1746,7 @@ 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
|
+
import { Type } from "@mariozechner/pi-ai";
|
|
1749
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"() {
|
|
@@ -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,6 +1931,13 @@ 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.",
|
|
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
|
+
}),
|
|
1917
1941
|
execute: async (toolCallId, args, signal) => {
|
|
1918
1942
|
const input = parseBashArgs(args);
|
|
1919
1943
|
const commandCwd = input.cwd ? resolveWorkspacePath(input.cwd) : root;
|
|
@@ -1978,6 +2002,12 @@ String: ${input.old_string}`
|
|
|
1978
2002
|
defineTool({
|
|
1979
2003
|
name: "Glob",
|
|
1980
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
|
+
}),
|
|
1981
2011
|
execute: async (_toolCallId, args, signal) => {
|
|
1982
2012
|
const input = parseGlobArgs(args);
|
|
1983
2013
|
const limit = input.head_limit ?? DEFAULT_SEARCH_LIMIT;
|
|
@@ -1998,6 +2028,22 @@ String: ${input.old_string}`
|
|
|
1998
2028
|
defineTool({
|
|
1999
2029
|
name: "Grep",
|
|
2000
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
|
+
}),
|
|
2001
2047
|
execute: async (_toolCallId, args, signal) => {
|
|
2002
2048
|
const input = parseGrepArgs(args);
|
|
2003
2049
|
const mode = input.output_mode ?? "files";
|
|
@@ -2051,6 +2097,15 @@ ${filenames.join("\n")}`,
|
|
|
2051
2097
|
defineTool({
|
|
2052
2098
|
name: "TodoWrite",
|
|
2053
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
|
+
}),
|
|
2054
2109
|
execute: async (_toolCallId, args) => {
|
|
2055
2110
|
const input = parseTodoWriteArgs(args);
|
|
2056
2111
|
const oldTodos = state.todos;
|
|
@@ -2682,17 +2737,58 @@ ${value}` };
|
|
|
2682
2737
|
});
|
|
2683
2738
|
|
|
2684
2739
|
// packages/core/src/tools/index.ts
|
|
2685
|
-
|
|
2740
|
+
import { Type as Type2 } from "@mariozechner/pi-ai";
|
|
2741
|
+
var defineTool, createSnipTool, parseSnipToolInput, isRecord4;
|
|
2686
2742
|
var init_tools = __esm({
|
|
2687
2743
|
"packages/core/src/tools/index.ts"() {
|
|
2688
2744
|
"use strict";
|
|
2689
2745
|
init_coding_tools();
|
|
2690
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);
|
|
2691
2786
|
}
|
|
2692
2787
|
});
|
|
2693
2788
|
|
|
2694
2789
|
// packages/core/src/channel/index.ts
|
|
2695
|
-
|
|
2790
|
+
import { Type as Type3 } from "@mariozechner/pi-ai";
|
|
2791
|
+
var createSendChannelMessageTool, parseSendChannelMessageInput, parseAttachments, optionalString2, isRecord5;
|
|
2696
2792
|
var init_channel = __esm({
|
|
2697
2793
|
"packages/core/src/channel/index.ts"() {
|
|
2698
2794
|
"use strict";
|
|
@@ -2700,6 +2796,18 @@ var init_channel = __esm({
|
|
|
2700
2796
|
createSendChannelMessageTool = (options) => defineTool({
|
|
2701
2797
|
name: "SendChannelMessage",
|
|
2702
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
|
+
}),
|
|
2703
2811
|
execute: async (_toolCallId, args) => {
|
|
2704
2812
|
const input = parseSendChannelMessageInput(args);
|
|
2705
2813
|
const result = await options.sendCurrent(input);
|
|
@@ -2710,7 +2818,7 @@ var init_channel = __esm({
|
|
|
2710
2818
|
}
|
|
2711
2819
|
});
|
|
2712
2820
|
parseSendChannelMessageInput = (value) => {
|
|
2713
|
-
if (!
|
|
2821
|
+
if (!isRecord5(value)) {
|
|
2714
2822
|
throw new Error("SendChannelMessage args must be an object");
|
|
2715
2823
|
}
|
|
2716
2824
|
const text = typeof value.text === "string" && value.text.trim().length > 0 ? value.text : void 0;
|
|
@@ -2739,7 +2847,7 @@ var init_channel = __esm({
|
|
|
2739
2847
|
throw new Error("SendChannelMessage.attachments must be an array");
|
|
2740
2848
|
}
|
|
2741
2849
|
return value.map((item, index) => {
|
|
2742
|
-
if (!
|
|
2850
|
+
if (!isRecord5(item)) {
|
|
2743
2851
|
throw new Error(`SendChannelMessage.attachments.${index} must be an object`);
|
|
2744
2852
|
}
|
|
2745
2853
|
if (item.type !== "image" && item.type !== "file") {
|
|
@@ -2768,14 +2876,14 @@ var init_channel = __esm({
|
|
|
2768
2876
|
}
|
|
2769
2877
|
return value;
|
|
2770
2878
|
};
|
|
2771
|
-
|
|
2879
|
+
isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2772
2880
|
}
|
|
2773
2881
|
});
|
|
2774
2882
|
|
|
2775
2883
|
// packages/core/src/extensions/index.ts
|
|
2776
2884
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2777
2885
|
import { dirname as dirname4, resolve as resolve2 } from "node:path";
|
|
2778
|
-
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths,
|
|
2886
|
+
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths, isRecord6;
|
|
2779
2887
|
var init_extensions = __esm({
|
|
2780
2888
|
"packages/core/src/extensions/index.ts"() {
|
|
2781
2889
|
"use strict";
|
|
@@ -2788,7 +2896,7 @@ var init_extensions = __esm({
|
|
|
2788
2896
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
2789
2897
|
throw new Error(`Invalid extension manifest JSON at ${manifestPath}: ${message}`);
|
|
2790
2898
|
}
|
|
2791
|
-
if (!
|
|
2899
|
+
if (!isRecord6(value)) {
|
|
2792
2900
|
throw new Error(`Extension manifest at ${manifestPath} must be an object`);
|
|
2793
2901
|
}
|
|
2794
2902
|
const rootDir = dirname4(resolve2(manifestPath));
|
|
@@ -2844,7 +2952,7 @@ var init_extensions = __esm({
|
|
|
2844
2952
|
}
|
|
2845
2953
|
return value.map((item, index) => requireRelativePath(item, `${name}.${index}`, manifestPath));
|
|
2846
2954
|
};
|
|
2847
|
-
|
|
2955
|
+
isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2848
2956
|
}
|
|
2849
2957
|
});
|
|
2850
2958
|
|
|
@@ -2988,7 +3096,8 @@ var init_instructions = __esm({
|
|
|
2988
3096
|
import { appendFile, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "node:fs/promises";
|
|
2989
3097
|
import { homedir as homedir3 } from "node:os";
|
|
2990
3098
|
import { join as join6 } from "node:path";
|
|
2991
|
-
|
|
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;
|
|
2992
3101
|
var init_memory = __esm({
|
|
2993
3102
|
"packages/core/src/memory/index.ts"() {
|
|
2994
3103
|
"use strict";
|
|
@@ -3077,6 +3186,14 @@ var init_memory = __esm({
|
|
|
3077
3186
|
"Use this once near the end of a completed user turn when there is progress, a decision, or a follow-up worth preserving.",
|
|
3078
3187
|
"Do not include secrets, raw logs, speculation, or facts that should be re-read from the repository."
|
|
3079
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
|
+
}),
|
|
3080
3197
|
execute: async (_toolCallId, args) => {
|
|
3081
3198
|
const input = parseAppendDailyInput(args);
|
|
3082
3199
|
validateAppendDailyInput(input);
|
|
@@ -3220,7 +3337,7 @@ var init_memory = __esm({
|
|
|
3220
3337
|
normalizeMarkdownFile = (value) => `${value.trimEnd()}
|
|
3221
3338
|
`;
|
|
3222
3339
|
parseAppendDailyInput = (value) => {
|
|
3223
|
-
if (!
|
|
3340
|
+
if (!isRecord7(value)) {
|
|
3224
3341
|
throw new Error("AppendDaily args must be an object");
|
|
3225
3342
|
}
|
|
3226
3343
|
const summary = requireString3(value.summary, "summary");
|
|
@@ -3286,12 +3403,12 @@ var init_memory = __esm({
|
|
|
3286
3403
|
optionalNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3287
3404
|
optionalString3 = (value) => typeof value === "string" && value.trim() ? value : void 0;
|
|
3288
3405
|
parseLastFailure = (value) => {
|
|
3289
|
-
if (!
|
|
3406
|
+
if (!isRecord7(value)) return void 0;
|
|
3290
3407
|
const at = optionalNumber2(value.at);
|
|
3291
3408
|
const message = optionalString3(value.message);
|
|
3292
3409
|
return at !== void 0 && message ? { at, message } : void 0;
|
|
3293
3410
|
};
|
|
3294
|
-
|
|
3411
|
+
isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3295
3412
|
safeProjectId = (projectId) => {
|
|
3296
3413
|
if (!/^[A-Za-z0-9_-]+$/.test(projectId)) {
|
|
3297
3414
|
throw new Error("projectId must contain only letters, numbers, underscores, or hyphens");
|
|
@@ -3304,11 +3421,10 @@ var init_memory = __esm({
|
|
|
3304
3421
|
|
|
3305
3422
|
// packages/core/src/provider/pi-ai.ts
|
|
3306
3423
|
import {
|
|
3307
|
-
Type,
|
|
3308
3424
|
getModels,
|
|
3309
3425
|
streamSimple
|
|
3310
3426
|
} from "@mariozechner/pi-ai";
|
|
3311
|
-
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;
|
|
3312
3428
|
var init_pi_ai = __esm({
|
|
3313
3429
|
"packages/core/src/provider/pi-ai.ts"() {
|
|
3314
3430
|
"use strict";
|
|
@@ -3446,89 +3562,8 @@ var init_pi_ai = __esm({
|
|
|
3446
3562
|
toPiTool = (tool) => ({
|
|
3447
3563
|
name: tool.name,
|
|
3448
3564
|
description: tool.description,
|
|
3449
|
-
parameters:
|
|
3565
|
+
parameters: tool.parameters
|
|
3450
3566
|
});
|
|
3451
|
-
toolParameters = (name) => {
|
|
3452
|
-
switch (name) {
|
|
3453
|
-
case "Read":
|
|
3454
|
-
return Type.Object({
|
|
3455
|
-
file_path: Type.String(),
|
|
3456
|
-
offset: Type.Optional(Type.Number()),
|
|
3457
|
-
limit: Type.Optional(Type.Number()),
|
|
3458
|
-
full: Type.Optional(Type.Boolean())
|
|
3459
|
-
});
|
|
3460
|
-
case "Write":
|
|
3461
|
-
return Type.Object({
|
|
3462
|
-
file_path: Type.String(),
|
|
3463
|
-
content: Type.String()
|
|
3464
|
-
});
|
|
3465
|
-
case "Edit":
|
|
3466
|
-
return Type.Object({
|
|
3467
|
-
file_path: Type.String(),
|
|
3468
|
-
old_string: Type.String(),
|
|
3469
|
-
new_string: Type.String(),
|
|
3470
|
-
replace_all: Type.Optional(Type.Boolean())
|
|
3471
|
-
});
|
|
3472
|
-
case "Bash":
|
|
3473
|
-
return Type.Object({
|
|
3474
|
-
command: Type.String(),
|
|
3475
|
-
cwd: Type.Optional(Type.String()),
|
|
3476
|
-
timeout: Type.Optional(Type.Number()),
|
|
3477
|
-
description: Type.Optional(Type.String()),
|
|
3478
|
-
maxOutputBytes: Type.Optional(Type.Number())
|
|
3479
|
-
});
|
|
3480
|
-
case "Glob":
|
|
3481
|
-
return Type.Object({
|
|
3482
|
-
pattern: Type.String(),
|
|
3483
|
-
path: Type.Optional(Type.String()),
|
|
3484
|
-
head_limit: Type.Optional(Type.Number()),
|
|
3485
|
-
offset: Type.Optional(Type.Number())
|
|
3486
|
-
});
|
|
3487
|
-
case "Grep":
|
|
3488
|
-
return Type.Object({
|
|
3489
|
-
pattern: Type.String(),
|
|
3490
|
-
path: Type.Optional(Type.String()),
|
|
3491
|
-
glob: Type.Optional(Type.String()),
|
|
3492
|
-
output_mode: Type.Optional(Type.Union([Type.Literal("files"), Type.Literal("content"), Type.Literal("count")])),
|
|
3493
|
-
"-B": Type.Optional(Type.Number()),
|
|
3494
|
-
"-A": Type.Optional(Type.Number()),
|
|
3495
|
-
"-C": Type.Optional(Type.Number()),
|
|
3496
|
-
context: Type.Optional(Type.Number()),
|
|
3497
|
-
"-n": Type.Optional(Type.Boolean()),
|
|
3498
|
-
"-i": Type.Optional(Type.Boolean()),
|
|
3499
|
-
type: Type.Optional(Type.String()),
|
|
3500
|
-
head_limit: Type.Optional(Type.Number()),
|
|
3501
|
-
offset: Type.Optional(Type.Number()),
|
|
3502
|
-
multiline: Type.Optional(Type.Boolean())
|
|
3503
|
-
});
|
|
3504
|
-
case "TodoWrite":
|
|
3505
|
-
return Type.Object({
|
|
3506
|
-
todos: Type.Array(
|
|
3507
|
-
Type.Object({
|
|
3508
|
-
content: Type.String(),
|
|
3509
|
-
status: Type.Union([Type.Literal("pending"), Type.Literal("in_progress"), Type.Literal("completed")]),
|
|
3510
|
-
activeForm: Type.Optional(Type.String())
|
|
3511
|
-
})
|
|
3512
|
-
)
|
|
3513
|
-
});
|
|
3514
|
-
case "AppendDaily":
|
|
3515
|
-
return Type.Object({
|
|
3516
|
-
summary: Type.String(),
|
|
3517
|
-
completed: Type.Optional(Type.Array(Type.String())),
|
|
3518
|
-
decisions: Type.Optional(Type.Array(Type.String())),
|
|
3519
|
-
followUps: Type.Optional(Type.Array(Type.String())),
|
|
3520
|
-
memoryCandidates: Type.Optional(Type.Array(Type.String())),
|
|
3521
|
-
evidence: Type.Optional(Type.Array(Type.String()))
|
|
3522
|
-
});
|
|
3523
|
-
case "Skill":
|
|
3524
|
-
return Type.Object({
|
|
3525
|
-
name: Type.String(),
|
|
3526
|
-
args: Type.Optional(Type.String())
|
|
3527
|
-
});
|
|
3528
|
-
default:
|
|
3529
|
-
return Type.Object({});
|
|
3530
|
-
}
|
|
3531
|
-
};
|
|
3532
3567
|
textContent = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
3533
3568
|
toolResultText = (result) => {
|
|
3534
3569
|
if (typeof result === "object" && result !== null && "content" in result) {
|
|
@@ -3776,22 +3811,23 @@ var init_runtime = __esm({
|
|
|
3776
3811
|
});
|
|
3777
3812
|
|
|
3778
3813
|
// packages/core/src/session/index.ts
|
|
3814
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3779
3815
|
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile8, writeFile as writeFile4 } from "node:fs/promises";
|
|
3780
3816
|
import { dirname as dirname6, join as join7 } from "node:path";
|
|
3781
3817
|
function assertTreeEvent(value) {
|
|
3782
|
-
if (!
|
|
3818
|
+
if (!isRecord8(value)) {
|
|
3783
3819
|
throw new SessionStoreError("invalid_event", "Event must be an object");
|
|
3784
3820
|
}
|
|
3785
3821
|
if (value.type === "session_header") {
|
|
3786
3822
|
throw new SessionStoreError("invalid_event", "Session header must be stored as the JSONL header line");
|
|
3787
3823
|
}
|
|
3788
|
-
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") {
|
|
3789
3825
|
throw new SessionStoreError("invalid_event", "Unsupported session event type");
|
|
3790
3826
|
}
|
|
3791
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") {
|
|
3792
3828
|
throw new SessionStoreError("invalid_event", "Event is missing required base fields");
|
|
3793
3829
|
}
|
|
3794
|
-
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)) {
|
|
3795
3831
|
throw new SessionStoreError("invalid_event", "Message event is missing message payload");
|
|
3796
3832
|
}
|
|
3797
3833
|
if (value.type === "session_title_updated" && !isSessionTitleUpdated(value)) {
|
|
@@ -3806,6 +3842,9 @@ function assertTreeEvent(value) {
|
|
|
3806
3842
|
if (value.type === "compact" && !isCompactEvent(value)) {
|
|
3807
3843
|
throw new SessionStoreError("invalid_event", "compact is missing summary payload");
|
|
3808
3844
|
}
|
|
3845
|
+
if (value.type === "context_control" && !isContextControlEvent(value)) {
|
|
3846
|
+
throw new SessionStoreError("invalid_event", "context_control is missing hide_user_turn payload");
|
|
3847
|
+
}
|
|
3809
3848
|
if (value.type === "queue_update" && !isQueueUpdate(value)) {
|
|
3810
3849
|
throw new SessionStoreError("invalid_event", "queue_update is missing queue payload");
|
|
3811
3850
|
}
|
|
@@ -3816,11 +3855,12 @@ function assertTreeEvent(value) {
|
|
|
3816
3855
|
throw new SessionStoreError("invalid_event", "skill_index_delta is missing delta payload");
|
|
3817
3856
|
}
|
|
3818
3857
|
}
|
|
3819
|
-
var SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, sessionArtifactsDirPath, 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;
|
|
3820
3859
|
var init_session = __esm({
|
|
3821
3860
|
"packages/core/src/session/index.ts"() {
|
|
3822
3861
|
"use strict";
|
|
3823
3862
|
init_src();
|
|
3863
|
+
snipUserMessageAlias = (eventId) => `u_${createHash2("sha256").update(eventId).digest("hex").slice(0, 8)}`;
|
|
3824
3864
|
SessionStoreError = class extends Error {
|
|
3825
3865
|
code;
|
|
3826
3866
|
line;
|
|
@@ -3844,7 +3884,8 @@ var init_session = __esm({
|
|
|
3844
3884
|
steer: []
|
|
3845
3885
|
},
|
|
3846
3886
|
skillIndexInitialized: false,
|
|
3847
|
-
skillIndex: {}
|
|
3887
|
+
skillIndex: {},
|
|
3888
|
+
hiddenUserTurnSpans: []
|
|
3848
3889
|
};
|
|
3849
3890
|
get rootId() {
|
|
3850
3891
|
return this.#rootId;
|
|
@@ -3964,6 +4005,16 @@ var init_session = __esm({
|
|
|
3964
4005
|
delete next[removed.name];
|
|
3965
4006
|
}
|
|
3966
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
|
+
];
|
|
3967
4018
|
}
|
|
3968
4019
|
}
|
|
3969
4020
|
};
|
|
@@ -4025,7 +4076,11 @@ var init_session = __esm({
|
|
|
4025
4076
|
};
|
|
4026
4077
|
buildContext = (tree, leafId) => {
|
|
4027
4078
|
const path = tree.getPath(leafId);
|
|
4079
|
+
const hiddenIds = hiddenContextEventIds(tree, path, leafId);
|
|
4028
4080
|
return path.reduce((messages, id, index) => {
|
|
4081
|
+
if (hiddenIds.has(id)) {
|
|
4082
|
+
return messages;
|
|
4083
|
+
}
|
|
4029
4084
|
const event = tree.get(id)?.event;
|
|
4030
4085
|
if (!event) {
|
|
4031
4086
|
return messages;
|
|
@@ -4038,7 +4093,7 @@ var init_session = __esm({
|
|
|
4038
4093
|
appendHarnessItemToContext(messages, event);
|
|
4039
4094
|
}
|
|
4040
4095
|
if (event.type === "compact") {
|
|
4041
|
-
const retained = retainedMessagesBeforeCompact(tree, path.slice(0, index), event.retainedEventCount);
|
|
4096
|
+
const retained = retainedMessagesBeforeCompact(tree, path.slice(0, index), event.retainedEventCount, hiddenIds);
|
|
4042
4097
|
messages.length = 0;
|
|
4043
4098
|
messages.push(compactSummaryMessage(event));
|
|
4044
4099
|
messages.push(...retained);
|
|
@@ -4046,7 +4101,29 @@ var init_session = __esm({
|
|
|
4046
4101
|
return messages;
|
|
4047
4102
|
}, []);
|
|
4048
4103
|
};
|
|
4049
|
-
|
|
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()) => {
|
|
4050
4127
|
if (retainedEventCount <= 0) {
|
|
4051
4128
|
return [];
|
|
4052
4129
|
}
|
|
@@ -4061,6 +4138,9 @@ var init_session = __esm({
|
|
|
4061
4138
|
}
|
|
4062
4139
|
const retained = [];
|
|
4063
4140
|
for (const id of pathBeforeCompact.slice(start)) {
|
|
4141
|
+
if (hiddenIds.has(id)) {
|
|
4142
|
+
continue;
|
|
4143
|
+
}
|
|
4064
4144
|
const event = tree.get(id)?.event;
|
|
4065
4145
|
if (!event) {
|
|
4066
4146
|
continue;
|
|
@@ -4085,13 +4165,13 @@ var init_session = __esm({
|
|
|
4085
4165
|
}
|
|
4086
4166
|
};
|
|
4087
4167
|
parseHeader = (value) => {
|
|
4088
|
-
if (!
|
|
4168
|
+
if (!isRecord8(value)) {
|
|
4089
4169
|
throw new SessionStoreError("invalid_header", "Session header must be an object");
|
|
4090
4170
|
}
|
|
4091
4171
|
if (value.version !== 1 || typeof value.sessionId !== "string" || typeof value.deviceId !== "string") {
|
|
4092
4172
|
throw new SessionStoreError("invalid_header", "Session header is missing required identity fields");
|
|
4093
4173
|
}
|
|
4094
|
-
if (typeof value.createdAt !== "number" || !
|
|
4174
|
+
if (typeof value.createdAt !== "number" || !isRecord8(value.meta)) {
|
|
4095
4175
|
throw new SessionStoreError("invalid_header", "Session header is missing createdAt or meta");
|
|
4096
4176
|
}
|
|
4097
4177
|
if (typeof value.meta.projectId !== "string" || value.meta.projectId.length === 0) {
|
|
@@ -4105,7 +4185,7 @@ var init_session = __esm({
|
|
|
4105
4185
|
return value;
|
|
4106
4186
|
};
|
|
4107
4187
|
validateSessionMatch = (header, value) => {
|
|
4108
|
-
if (!
|
|
4188
|
+
if (!isRecord8(value) || typeof value.sessionId !== "string") {
|
|
4109
4189
|
throw new SessionStoreError("invalid_header", "Event must be an object with a sessionId");
|
|
4110
4190
|
}
|
|
4111
4191
|
if (value.sessionId !== header.sessionId) {
|
|
@@ -4114,24 +4194,25 @@ var init_session = __esm({
|
|
|
4114
4194
|
};
|
|
4115
4195
|
isConversationEvent = (event) => event.type === "user_message" || event.type === "assistant_message" || event.type === "tool_result" || event.type === "harness_item" || event.type === "compact";
|
|
4116
4196
|
isInstructionSnapshot = (value) => {
|
|
4117
|
-
if (!
|
|
4197
|
+
if (!isRecord8(value) || value.version !== 1 || typeof value.cwd !== "string" || !Array.isArray(value.sections)) {
|
|
4118
4198
|
return false;
|
|
4119
4199
|
}
|
|
4120
4200
|
return value.sections.every(
|
|
4121
|
-
(section2) =>
|
|
4201
|
+
(section2) => isRecord8(section2) && typeof section2.kind === "string" && typeof section2.frozenAt === "number" && typeof section2.renderedBlock === "string"
|
|
4122
4202
|
);
|
|
4123
4203
|
};
|
|
4124
|
-
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");
|
|
4125
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");
|
|
4126
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(
|
|
4127
|
-
(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"
|
|
4128
4209
|
);
|
|
4129
|
-
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");
|
|
4130
4211
|
isSkillIndexSnapshot = (value) => (value.anchorEventId === null || typeof value.anchorEventId === "string") && Array.isArray(value.entries) && value.entries.every(isSkillIndexEntry);
|
|
4131
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(
|
|
4132
|
-
(item) =>
|
|
4213
|
+
(item) => isRecord8(item) && typeof item.name === "string" && typeof item.previousPath === "string"
|
|
4133
4214
|
);
|
|
4134
|
-
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";
|
|
4135
4216
|
appendHarnessItemToContext = (messages, event) => {
|
|
4136
4217
|
const reminder = renderSystemReminder(event.item.content);
|
|
4137
4218
|
const last = messages.at(-1);
|
|
@@ -4168,7 +4249,7 @@ ${reminder}` }]
|
|
|
4168
4249
|
}
|
|
4169
4250
|
return false;
|
|
4170
4251
|
};
|
|
4171
|
-
isToolResultWithContent = (value) =>
|
|
4252
|
+
isToolResultWithContent = (value) => isRecord8(value) && Array.isArray(value.content);
|
|
4172
4253
|
renderSystemReminder = (content) => `<system-reminder>
|
|
4173
4254
|
${content}
|
|
4174
4255
|
</system-reminder>`;
|
|
@@ -4192,10 +4273,10 @@ ${content}
|
|
|
4192
4273
|
cloneMessage = (message) => ({
|
|
4193
4274
|
...message,
|
|
4194
4275
|
content: message.content.map((block) => {
|
|
4195
|
-
if (block.type !== "tool_result" || !
|
|
4276
|
+
if (block.type !== "tool_result" || !isRecord8(block.result)) {
|
|
4196
4277
|
return { ...block };
|
|
4197
4278
|
}
|
|
4198
|
-
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) } : {};
|
|
4199
4280
|
return {
|
|
4200
4281
|
...block,
|
|
4201
4282
|
result: {
|
|
@@ -4205,16 +4286,17 @@ ${content}
|
|
|
4205
4286
|
}),
|
|
4206
4287
|
...message.meta ? { meta: { ...message.meta } } : {}
|
|
4207
4288
|
});
|
|
4208
|
-
|
|
4289
|
+
isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4209
4290
|
}
|
|
4210
4291
|
});
|
|
4211
4292
|
|
|
4212
4293
|
// packages/core/src/skills/index.ts
|
|
4213
|
-
import { createHash as
|
|
4294
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
4214
4295
|
import { existsSync as existsSync2 } from "node:fs";
|
|
4215
4296
|
import { readdir as readdir5, readFile as readFile9, stat as stat4 } from "node:fs/promises";
|
|
4216
4297
|
import { homedir as homedir4 } from "node:os";
|
|
4217
4298
|
import { dirname as dirname7, join as join8, resolve as resolve4 } from "node:path";
|
|
4299
|
+
import { Type as Type5 } from "@mariozechner/pi-ai";
|
|
4218
4300
|
var scanSkillIndex, diffSkillIndex, hasSkillIndexDelta, renderSkillListing, renderSkillDelta, createSkillTool, projectSkillRoots, readSkillEntry, parseSkillMetadata, firstParagraph, parseSkillArgs, findGitRoot2, isNodeErrorCode4;
|
|
4219
4301
|
var init_skills = __esm({
|
|
4220
4302
|
"packages/core/src/skills/index.ts"() {
|
|
@@ -4305,6 +4387,10 @@ var init_skills = __esm({
|
|
|
4305
4387
|
createSkillTool = (options) => defineTool({
|
|
4306
4388
|
name: "Skill",
|
|
4307
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
|
+
}),
|
|
4308
4394
|
execute: async (_toolCallId, args) => {
|
|
4309
4395
|
const input = parseSkillArgs(args);
|
|
4310
4396
|
const entry = options.getEntry(input.name);
|
|
@@ -4369,7 +4455,7 @@ var init_skills = __esm({
|
|
|
4369
4455
|
...parsed.displayName ? { displayName: parsed.displayName } : {},
|
|
4370
4456
|
mtimeMs: fileStat.mtimeMs,
|
|
4371
4457
|
size: fileStat.size,
|
|
4372
|
-
contentHash:
|
|
4458
|
+
contentHash: createHash3("sha256").update(content).digest("hex"),
|
|
4373
4459
|
priority: options.priority
|
|
4374
4460
|
};
|
|
4375
4461
|
};
|
|
@@ -4785,7 +4871,7 @@ import { basename as basename3, dirname as dirname8, join as join10, resolve as
|
|
|
4785
4871
|
import { pathToFileURL } from "node:url";
|
|
4786
4872
|
import { promisify as promisify2 } from "node:util";
|
|
4787
4873
|
import { WebSocketServer } from "ws";
|
|
4788
|
-
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, parseQueuedModelSelection, 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;
|
|
4789
4875
|
var init_src4 = __esm({
|
|
4790
4876
|
"packages/daemon/src/index.ts"() {
|
|
4791
4877
|
"use strict";
|
|
@@ -5502,7 +5588,7 @@ var init_src4 = __esm({
|
|
|
5502
5588
|
ts: this.#now(),
|
|
5503
5589
|
message: {
|
|
5504
5590
|
role: "user",
|
|
5505
|
-
content: input.content,
|
|
5591
|
+
content: [...input.content, snipUserMessageIdBlock(userEventId)],
|
|
5506
5592
|
...input.source === "follow_up" ? { meta: { source: "follow_up", queueItemId: input.queueItemId } } : {}
|
|
5507
5593
|
}
|
|
5508
5594
|
});
|
|
@@ -5522,6 +5608,7 @@ var init_src4 = __esm({
|
|
|
5522
5608
|
finalAssistantEventId: firstAssistantEventId
|
|
5523
5609
|
};
|
|
5524
5610
|
lane.channelContext = input.channelContext;
|
|
5611
|
+
lane.snipClientId = clientId;
|
|
5525
5612
|
try {
|
|
5526
5613
|
for await (const rawEvent of lane.runtime.executeTurn(
|
|
5527
5614
|
buildContext(lane.session.tree, userEvent.id),
|
|
@@ -5537,6 +5624,7 @@ var init_src4 = __esm({
|
|
|
5537
5624
|
}
|
|
5538
5625
|
} finally {
|
|
5539
5626
|
lane.channelContext = void 0;
|
|
5627
|
+
lane.snipClientId = void 0;
|
|
5540
5628
|
lane.runtime.unregisterTool("SendChannelMessage");
|
|
5541
5629
|
}
|
|
5542
5630
|
const result = { userEventId, assistantEventId: state.finalAssistantEventId };
|
|
@@ -6786,6 +6874,80 @@ var init_src4 = __esm({
|
|
|
6786
6874
|
listNames: () => Object.keys(lane.session.tree.controlState.skillIndex).sort()
|
|
6787
6875
|
})
|
|
6788
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);
|
|
6789
6951
|
}
|
|
6790
6952
|
async #selectChatRuntime(lane, modelSelection) {
|
|
6791
6953
|
if (!modelSelection) {
|
|
@@ -7548,7 +7710,14 @@ var init_src4 = __esm({
|
|
|
7548
7710
|
};
|
|
7549
7711
|
countContentBlocks = (message, type) => message.content.filter((block) => block.type === type).length;
|
|
7550
7712
|
normalizeContent = (content) => typeof content === "string" ? [{ type: "text", text: content }] : content;
|
|
7551
|
-
|
|
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();
|
|
7552
7721
|
assistantText = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
|
|
7553
7722
|
messageText = (message) => {
|
|
7554
7723
|
const text = message.content.map((block) => {
|
|
@@ -7577,7 +7746,7 @@ var init_src4 = __esm({
|
|
|
7577
7746
|
return void 0;
|
|
7578
7747
|
}
|
|
7579
7748
|
const parsed = JSON.parse(text);
|
|
7580
|
-
if (!
|
|
7749
|
+
if (!isRecord9(parsed)) {
|
|
7581
7750
|
return void 0;
|
|
7582
7751
|
}
|
|
7583
7752
|
return {
|
|
@@ -7665,7 +7834,7 @@ var init_src4 = __esm({
|
|
|
7665
7834
|
}
|
|
7666
7835
|
};
|
|
7667
7836
|
parseRuntimeStats = (value) => {
|
|
7668
|
-
if (!
|
|
7837
|
+
if (!isRecord9(value) || !isRecord9(value.rtk)) {
|
|
7669
7838
|
return emptyRuntimeStats();
|
|
7670
7839
|
}
|
|
7671
7840
|
return {
|
|
@@ -7679,13 +7848,13 @@ var init_src4 = __esm({
|
|
|
7679
7848
|
};
|
|
7680
7849
|
};
|
|
7681
7850
|
parseRuntimeStatsBuckets = (value) => {
|
|
7682
|
-
if (!
|
|
7851
|
+
if (!isRecord9(value)) {
|
|
7683
7852
|
return {};
|
|
7684
7853
|
}
|
|
7685
7854
|
return Object.fromEntries(
|
|
7686
7855
|
Object.entries(value).map(([key, bucket]) => [
|
|
7687
7856
|
key,
|
|
7688
|
-
|
|
7857
|
+
isRecord9(bucket) ? {
|
|
7689
7858
|
outputTokens: nonNegativeInteger2(bucket.outputTokens),
|
|
7690
7859
|
savedTokens: nonNegativeInteger2(bucket.savedTokens)
|
|
7691
7860
|
} : { outputTokens: 0, savedTokens: 0 }
|
|
@@ -7703,11 +7872,11 @@ var init_src4 = __esm({
|
|
|
7703
7872
|
return bucket;
|
|
7704
7873
|
};
|
|
7705
7874
|
rtkSavingsFromToolResult = (result) => {
|
|
7706
|
-
if (!
|
|
7875
|
+
if (!isRecord9(result) || !isRecord9(result.details)) {
|
|
7707
7876
|
return void 0;
|
|
7708
7877
|
}
|
|
7709
7878
|
const rtk = result.details.rtk;
|
|
7710
|
-
if (!
|
|
7879
|
+
if (!isRecord9(rtk) || rtk.applied !== true) {
|
|
7711
7880
|
return void 0;
|
|
7712
7881
|
}
|
|
7713
7882
|
const outputTokens = nonNegativeInteger2(rtk.estimatedOutputTokens);
|
|
@@ -7752,7 +7921,7 @@ var init_src4 = __esm({
|
|
|
7752
7921
|
...context.data ? { data: context.data } : {}
|
|
7753
7922
|
});
|
|
7754
7923
|
parseQueuedChannelContext = (value) => {
|
|
7755
|
-
if (!
|
|
7924
|
+
if (!isRecord9(value)) {
|
|
7756
7925
|
return void 0;
|
|
7757
7926
|
}
|
|
7758
7927
|
if (typeof value.channel !== "string" || typeof value.externalConversationId !== "string") {
|
|
@@ -7764,11 +7933,11 @@ var init_src4 = __esm({
|
|
|
7764
7933
|
...typeof value.conversationType === "string" ? { conversationType: value.conversationType } : {},
|
|
7765
7934
|
...typeof value.senderDisplayName === "string" ? { senderDisplayName: value.senderDisplayName } : {},
|
|
7766
7935
|
...typeof value.mentionedBot === "boolean" ? { mentionedBot: value.mentionedBot } : {},
|
|
7767
|
-
...
|
|
7936
|
+
...isRecord9(value.data) ? { data: value.data } : {}
|
|
7768
7937
|
});
|
|
7769
7938
|
};
|
|
7770
7939
|
parseQueuedModelSelection = (value) => {
|
|
7771
|
-
if (!
|
|
7940
|
+
if (!isRecord9(value)) {
|
|
7772
7941
|
return void 0;
|
|
7773
7942
|
}
|
|
7774
7943
|
const selection = {};
|
|
@@ -7810,7 +7979,7 @@ var init_src4 = __esm({
|
|
|
7810
7979
|
};
|
|
7811
7980
|
isSteerMessage = (text) => /^\/(?:steer|interrupt)\b/i.test(text.trim());
|
|
7812
7981
|
stripImCommandPrefix = (text) => text.trim().replace(/^\/(?:steer|interrupt)\s*/i, "").trim() || text;
|
|
7813
|
-
|
|
7982
|
+
isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7814
7983
|
parseMemoryUpdate = (raw) => {
|
|
7815
7984
|
const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
7816
7985
|
if (!text) {
|
|
@@ -8036,7 +8205,7 @@ var init_daemon_cli = __esm({
|
|
|
8036
8205
|
DEFAULT_PORT = 7777;
|
|
8037
8206
|
STOP_POLL_INTERVAL_MS = 200;
|
|
8038
8207
|
STOP_GRACE_MS = 5e3;
|
|
8039
|
-
START_READY_TIMEOUT_MS =
|
|
8208
|
+
START_READY_TIMEOUT_MS = 3e4;
|
|
8040
8209
|
AUTO_STARTED_IDLE_SHUTDOWN_MS = 15 * 60 * 1e3;
|
|
8041
8210
|
FOREGROUND_IDLE_SHUTDOWN_MS = 0;
|
|
8042
8211
|
defaultStateDir2 = () => join13(homedir6(), ".scorel");
|
|
@@ -9202,7 +9371,7 @@ var init_up_cli = __esm({
|
|
|
9202
9371
|
init_daemon_cli();
|
|
9203
9372
|
DEFAULT_DAEMON_PORT = 7777;
|
|
9204
9373
|
DEFAULT_WEBUI_PORT = 3e3;
|
|
9205
|
-
DEFAULT_DAEMON_READY_TIMEOUT_MS =
|
|
9374
|
+
DEFAULT_DAEMON_READY_TIMEOUT_MS = 3e4;
|
|
9206
9375
|
defaultStateDir3 = () => join16(homedir8(), ".scorel");
|
|
9207
9376
|
defaultAttachSigint = (listener) => {
|
|
9208
9377
|
process.on("SIGINT", listener);
|
|
@@ -9557,7 +9726,7 @@ __export(index_exports, {
|
|
|
9557
9726
|
runChat: () => runChat,
|
|
9558
9727
|
runCli: () => runCli
|
|
9559
9728
|
});
|
|
9560
|
-
import { createHash as
|
|
9729
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9561
9730
|
import { appendFile as appendFile4, mkdir as mkdir8, readFile as readFile14, realpath as realpath3, readdir as readdir7, writeFile as writeFile8 } from "node:fs/promises";
|
|
9562
9731
|
import { createInterface } from "node:readline/promises";
|
|
9563
9732
|
import { homedir as homedir9 } from "node:os";
|
|
@@ -9879,11 +10048,11 @@ var init_index = __esm({
|
|
|
9879
10048
|
};
|
|
9880
10049
|
};
|
|
9881
10050
|
attachCacheFilePath = (stateDir, scope, sessionId) => {
|
|
9882
|
-
const scopeKey =
|
|
10051
|
+
const scopeKey = createHash4("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
9883
10052
|
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.json`);
|
|
9884
10053
|
};
|
|
9885
10054
|
attachDiagnosticsFilePath = (stateDir, scope, sessionId) => {
|
|
9886
|
-
const scopeKey =
|
|
10055
|
+
const scopeKey = createHash4("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
9887
10056
|
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.log`);
|
|
9888
10057
|
};
|
|
9889
10058
|
findAttachDiagnosticsFilePath = async (stateDir, sessionId, _remoteUrl) => {
|