@kenkaiiii/ggcoder 5.74.2 → 5.75.0
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 +1 -1
- package/assets/skills/bulletproof/SKILL.md +1 -1
- package/assets/skills/clarify/SKILL.md +1 -1
- package/assets/skills/code-review/SKILL.md +1 -1
- package/assets/skills/compliance-guard/SKILL.md +1 -1
- package/assets/skills/durable/SKILL.md +1 -1
- package/assets/skills/evidence-led-ui/SKILL.md +1 -1
- package/assets/skills/lean/SKILL.md +1 -1
- package/assets/skills/refactoring/SKILL.md +1 -1
- package/assets/skills/root-cause/SKILL.md +1 -1
- package/assets/skills/shared-language/SKILL.md +1 -1
- package/assets/skills/tdd/SKILL.md +1 -1
- package/dist/app-sidecar/app-settings.d.ts +65 -0
- package/dist/app-sidecar/app-settings.js +147 -0
- package/dist/app-sidecar/app-settings.js.map +1 -0
- package/dist/app-sidecar/attachments.d.ts +11 -0
- package/dist/app-sidecar/attachments.js +47 -0
- package/dist/app-sidecar/attachments.js.map +1 -0
- package/dist/app-sidecar/auth-routes.d.ts +3 -0
- package/dist/app-sidecar/auth-routes.js +290 -0
- package/dist/app-sidecar/auth-routes.js.map +1 -0
- package/dist/app-sidecar/autopilot-runner.d.ts +61 -0
- package/dist/app-sidecar/autopilot-runner.js +367 -0
- package/dist/app-sidecar/autopilot-runner.js.map +1 -0
- package/dist/app-sidecar/file-search.d.ts +15 -0
- package/dist/app-sidecar/file-search.js +103 -0
- package/dist/app-sidecar/file-search.js.map +1 -0
- package/dist/app-sidecar/hf-pull-manager.d.ts +20 -0
- package/dist/app-sidecar/hf-pull-manager.js +224 -0
- package/dist/app-sidecar/hf-pull-manager.js.map +1 -0
- package/dist/app-sidecar/history-routes.d.ts +3 -0
- package/dist/app-sidecar/history-routes.js +375 -0
- package/dist/app-sidecar/history-routes.js.map +1 -0
- package/dist/app-sidecar/history.d.ts +67 -0
- package/dist/app-sidecar/history.js +23 -0
- package/dist/app-sidecar/history.js.map +1 -0
- package/dist/app-sidecar/http.d.ts +5 -0
- package/dist/app-sidecar/http.js +27 -0
- package/dist/app-sidecar/http.js.map +1 -0
- package/dist/app-sidecar/integration-routes.d.ts +3 -0
- package/dist/app-sidecar/integration-routes.js +286 -0
- package/dist/app-sidecar/integration-routes.js.map +1 -0
- package/dist/app-sidecar/json-mode.d.ts +10 -0
- package/dist/app-sidecar/json-mode.js +73 -0
- package/dist/app-sidecar/json-mode.js.map +1 -0
- package/dist/app-sidecar/keep-awake.d.ts +4 -0
- package/dist/app-sidecar/keep-awake.js +5 -0
- package/dist/app-sidecar/keep-awake.js.map +1 -0
- package/dist/app-sidecar/ken-context.d.ts +20 -0
- package/dist/app-sidecar/ken-context.js +55 -0
- package/dist/app-sidecar/ken-context.js.map +1 -0
- package/dist/app-sidecar/mcp-routes.d.ts +3 -0
- package/dist/app-sidecar/mcp-routes.js +185 -0
- package/dist/app-sidecar/mcp-routes.js.map +1 -0
- package/dist/app-sidecar/mcp-rows.d.ts +31 -0
- package/dist/app-sidecar/mcp-rows.js +77 -0
- package/dist/app-sidecar/mcp-rows.js.map +1 -0
- package/dist/app-sidecar/progress-manager.d.ts +10 -0
- package/dist/app-sidecar/progress-manager.js +116 -0
- package/dist/app-sidecar/progress-manager.js.map +1 -0
- package/dist/app-sidecar/prompt-routes.d.ts +3 -0
- package/dist/app-sidecar/prompt-routes.js +354 -0
- package/dist/app-sidecar/prompt-routes.js.map +1 -0
- package/dist/app-sidecar/route-context.d.ts +145 -0
- package/dist/app-sidecar/route-context.js +2 -0
- package/dist/app-sidecar/route-context.js.map +1 -0
- package/dist/app-sidecar/run-control-routes.d.ts +3 -0
- package/dist/app-sidecar/run-control-routes.js +430 -0
- package/dist/app-sidecar/run-control-routes.js.map +1 -0
- package/dist/app-sidecar/session-routes.d.ts +3 -0
- package/dist/app-sidecar/session-routes.js +115 -0
- package/dist/app-sidecar/session-routes.js.map +1 -0
- package/dist/app-sidecar/session-types.d.ts +39 -0
- package/dist/app-sidecar/session-types.js +5 -0
- package/dist/app-sidecar/session-types.js.map +1 -0
- package/dist/app-sidecar/task-routes.d.ts +3 -0
- package/dist/app-sidecar/task-routes.js +149 -0
- package/dist/app-sidecar/task-routes.js.map +1 -0
- package/dist/app-sidecar/usage.d.ts +19 -0
- package/dist/app-sidecar/usage.js +153 -0
- package/dist/app-sidecar/usage.js.map +1 -0
- package/dist/app-sidecar/workspace-routes.d.ts +3 -0
- package/dist/app-sidecar/workspace-routes.js +244 -0
- package/dist/app-sidecar/workspace-routes.js.map +1 -0
- package/dist/app-sidecar.js +482 -3695
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/core/acceptance-checks.js +3 -9
- package/dist/core/acceptance-checks.js.map +1 -1
- package/dist/core/agent-session/helpers.d.ts +26 -0
- package/dist/core/agent-session/helpers.js +47 -0
- package/dist/core/agent-session/helpers.js.map +1 -0
- package/dist/core/agent-session/mcp-tools.d.ts +68 -0
- package/dist/core/agent-session/mcp-tools.js +195 -0
- package/dist/core/agent-session/mcp-tools.js.map +1 -0
- package/dist/core/agent-session/run-loop-monitor.d.ts +63 -0
- package/dist/core/agent-session/run-loop-monitor.js +164 -0
- package/dist/core/agent-session/run-loop-monitor.js.map +1 -0
- package/dist/core/agent-session/transcript-markers.d.ts +57 -0
- package/dist/core/agent-session/transcript-markers.js +166 -0
- package/dist/core/agent-session/transcript-markers.js.map +1 -0
- package/dist/core/agent-session/verification-tracker.d.ts +46 -0
- package/dist/core/agent-session/verification-tracker.js +246 -0
- package/dist/core/agent-session/verification-tracker.js.map +1 -0
- package/dist/core/agent-session.d.ts +27 -87
- package/dist/core/agent-session.js +166 -709
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.js +3 -3
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/autopilot-cycle.d.ts +6 -2
- package/dist/core/autopilot-cycle.js +1 -1
- package/dist/core/autopilot-cycle.js.map +1 -1
- package/dist/core/bundled-agents.js +6 -6
- package/dist/core/bundled-agents.js.map +1 -1
- package/dist/core/checklist-git.d.ts +19 -0
- package/dist/core/checklist-git.js +54 -0
- package/dist/core/checklist-git.js.map +1 -0
- package/dist/core/checklist-items.d.ts +10 -1
- package/dist/core/checklist-items.js +22 -0
- package/dist/core/checklist-items.js.map +1 -1
- package/dist/core/checklist-prompt.d.ts +5 -1
- package/dist/core/checklist-prompt.js +16 -5
- package/dist/core/checklist-prompt.js.map +1 -1
- package/dist/core/checklist-snapshot.d.ts +5 -1
- package/dist/core/checklist-snapshot.js +19 -2
- package/dist/core/checklist-snapshot.js.map +1 -1
- package/dist/core/context-limits.js +1 -1
- package/dist/core/context-limits.js.map +1 -1
- package/dist/core/plan-handoff.d.ts +30 -0
- package/dist/core/plan-handoff.js +10 -0
- package/dist/core/plan-handoff.js.map +1 -0
- package/dist/core/plan-submission-gate.d.ts +28 -0
- package/dist/core/plan-submission-gate.js +27 -0
- package/dist/core/plan-submission-gate.js.map +1 -0
- package/dist/core/skills.js +6 -9
- package/dist/core/skills.js.map +1 -1
- package/dist/core/style-packs/index.js +2 -11
- package/dist/core/style-packs/index.js.map +1 -1
- package/dist/core/style-packs/packs.d.ts +3 -12
- package/dist/core/style-packs/packs.js +132 -173
- package/dist/core/style-packs/packs.js.map +1 -1
- package/dist/system-prompt.js +55 -78
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/ask-user.js +13 -74
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/bash.js +13 -78
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/checklist.d.ts +2 -10
- package/dist/tools/checklist.js +7 -39
- package/dist/tools/checklist.js.map +1 -1
- package/dist/tools/code-nav.js +7 -29
- package/dist/tools/code-nav.js.map +1 -1
- package/dist/tools/edit.d.ts +3 -9
- package/dist/tools/edit.js +66 -65
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/enter-plan.js +2 -7
- package/dist/tools/enter-plan.js.map +1 -1
- package/dist/tools/exit-plan.js +2 -3
- package/dist/tools/exit-plan.js.map +1 -1
- package/dist/tools/find.js +3 -4
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/grep.js +6 -18
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/ls.js +3 -3
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/prompt-hints.js +34 -47
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read.js +8 -20
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/search-code.js +4 -12
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/skill.js +13 -7
- package/dist/tools/skill.js.map +1 -1
- package/dist/tools/subagent-control.js +7 -22
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent.js +4 -7
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/task-output.js +4 -15
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/task-send.js +5 -17
- package/dist/tools/task-send.js.map +1 -1
- package/dist/tools/task-stop.js +2 -2
- package/dist/tools/task-stop.js.map +1 -1
- package/dist/tools/tool-search.js +2 -7
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-tiers.d.ts +17 -4
- package/dist/tools/tool-tiers.js +38 -15
- package/dist/tools/tool-tiers.js.map +1 -1
- package/dist/tools/web-fetch.js +7 -33
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/write.js +3 -4
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/components/Footer.js +1 -2
- package/dist/ui/components/Footer.js.map +1 -1
- package/package.json +7 -4
- package/dist/core/api-benchmark.d.ts +0 -63
- package/dist/core/api-benchmark.js +0 -381
- package/dist/core/api-benchmark.js.map +0 -1
- package/dist/core/bash-spawn-benchmark.d.ts +0 -1
- package/dist/core/bash-spawn-benchmark.js +0 -139
- package/dist/core/bash-spawn-benchmark.js.map +0 -1
- package/dist/core/cache-warm-benchmark.d.ts +0 -21
- package/dist/core/cache-warm-benchmark.js +0 -170
- package/dist/core/cache-warm-benchmark.js.map +0 -1
- package/dist/core/fast-apply-benchmark.d.ts +0 -25
- package/dist/core/fast-apply-benchmark.js +0 -284
- package/dist/core/fast-apply-benchmark.js.map +0 -1
- package/dist/core/hashline-edit-benchmark.d.ts +0 -88
- package/dist/core/hashline-edit-benchmark.js +0 -518
- package/dist/core/hashline-edit-benchmark.js.map +0 -1
- package/dist/core/semantic-search-benchmark.d.ts +0 -36
- package/dist/core/semantic-search-benchmark.js +0 -211
- package/dist/core/semantic-search-benchmark.js.map +0 -1
- package/dist/core/speed-benchmark.d.ts +0 -132
- package/dist/core/speed-benchmark.js +0 -410
- package/dist/core/speed-benchmark.js.map +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { log } from "../logger.js";
|
|
3
|
+
import { MCPClientManager } from "../mcp/index.js";
|
|
4
|
+
import { clampMcpToolDescription, DeferredToolCatalog } from "../mcp/deferred-catalog.js";
|
|
5
|
+
import { McpCatalogCache } from "../mcp/catalog-cache.js";
|
|
6
|
+
import { createToolSearchTool } from "../../tools/tool-search.js";
|
|
7
|
+
/**
|
|
8
|
+
* MCP tool wiring for one session: the client manager, the deferred
|
|
9
|
+
* `tool_search` catalog (shared with deferred built-ins), cached-catalog stubs
|
|
10
|
+
* and the live/cached reconcile maps.
|
|
11
|
+
*
|
|
12
|
+
* Never owns the tool array — it reads the session's CURRENT array through
|
|
13
|
+
* `getTools` (the session may filter-replace it) and only appends to it or
|
|
14
|
+
* swaps same-named entries in place, so the cached tool prefix stays stable.
|
|
15
|
+
*/
|
|
16
|
+
export class McpToolRegistry {
|
|
17
|
+
getTools;
|
|
18
|
+
getContextLimits;
|
|
19
|
+
manager;
|
|
20
|
+
/** Deferred MCP tools awaiting discovery via tool_search. */
|
|
21
|
+
catalog;
|
|
22
|
+
/** Live (connected) MCP tools by name — the reconcile target for cached stubs. */
|
|
23
|
+
liveTools = new Map();
|
|
24
|
+
/** Server name for each cached-only tool, so a stub knows what to wait on. */
|
|
25
|
+
cachedToolServers = new Map();
|
|
26
|
+
catalogCache = new McpCatalogCache();
|
|
27
|
+
constructor(getTools, getContextLimits) {
|
|
28
|
+
this.getTools = getTools;
|
|
29
|
+
this.getContextLimits = getContextLimits;
|
|
30
|
+
}
|
|
31
|
+
createManager(options) {
|
|
32
|
+
this.manager = new MCPClientManager({
|
|
33
|
+
catalogCache: this.catalogCache,
|
|
34
|
+
modernProtocol: options.modernProtocol,
|
|
35
|
+
onElicit: options.onElicit,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
ensureCatalog() {
|
|
39
|
+
this.catalog ??= new DeferredToolCatalog(this.getContextLimits());
|
|
40
|
+
return this.catalog;
|
|
41
|
+
}
|
|
42
|
+
/** Hold tools in the tool_search catalog and make sure tool_search exists. */
|
|
43
|
+
addDeferred(tools) {
|
|
44
|
+
this.ensureCatalog().add(tools);
|
|
45
|
+
this.ensureToolSearchTool();
|
|
46
|
+
}
|
|
47
|
+
/** Pull named entries out of the catalog (empty when no catalog exists). */
|
|
48
|
+
promote(names) {
|
|
49
|
+
return this.catalog?.promote(names) ?? [];
|
|
50
|
+
}
|
|
51
|
+
/** Forget every MCP tool before a provider-switch reconnect re-adds them. */
|
|
52
|
+
clearMcpTools() {
|
|
53
|
+
this.catalog?.removeWhere((name) => name.startsWith("mcp__"));
|
|
54
|
+
this.liveTools.clear();
|
|
55
|
+
this.cachedToolServers.clear();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Route freshly connected MCP tools: deferred into the tool_search catalog
|
|
59
|
+
* (default — keeps ~8k tokens of schema out of every cache-miss turn) or
|
|
60
|
+
* pushed eagerly when `defer` is false.
|
|
61
|
+
* Promotion pushes onto the live tools array the running agent loop
|
|
62
|
+
* re-reads every turn, so promoted tools are callable on the next step.
|
|
63
|
+
*/
|
|
64
|
+
addMcpTools(mcpTools, defer) {
|
|
65
|
+
if (mcpTools.length === 0)
|
|
66
|
+
return;
|
|
67
|
+
for (const tool of mcpTools) {
|
|
68
|
+
this.liveTools.set(tool.name, tool);
|
|
69
|
+
this.cachedToolServers.delete(tool.name);
|
|
70
|
+
}
|
|
71
|
+
if (!defer) {
|
|
72
|
+
// Eager path bypasses the catalog, so budget descriptions here too.
|
|
73
|
+
this.replaceOrPushTools(mcpTools.map((tool) => clampMcpToolDescription(tool, this.getContextLimits())));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// `add` is name-keyed, so live definitions replace cached stubs in place.
|
|
77
|
+
this.ensureCatalog().add(mcpTools);
|
|
78
|
+
// A stub the model already promoted lives in the tools array; swap it for
|
|
79
|
+
// the live tool so later calls dispatch directly instead of through the stub.
|
|
80
|
+
this.replaceLivePromotedTools(mcpTools);
|
|
81
|
+
this.ensureToolSearchTool();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Register `tool_search` once. Promotion of a cached-only entry waits for its
|
|
85
|
+
* server so the model is told immediately when that capability turns out to
|
|
86
|
+
* be unreachable, instead of promoting a tool that fails on first call.
|
|
87
|
+
*
|
|
88
|
+
* The catalog is created on demand rather than required up front: deferred
|
|
89
|
+
* built-in tools populate it with zero MCP servers connected, so gating
|
|
90
|
+
* registration on an existing catalog would leave those tools unreachable.
|
|
91
|
+
*/
|
|
92
|
+
ensureToolSearchTool() {
|
|
93
|
+
const catalog = this.ensureCatalog();
|
|
94
|
+
if (this.getTools().some((t) => t.name === "tool_search"))
|
|
95
|
+
return;
|
|
96
|
+
this.getTools().push(createToolSearchTool(catalog, (promoted) => {
|
|
97
|
+
this.getTools().push(...promoted);
|
|
98
|
+
}, async (toolName) => {
|
|
99
|
+
if (this.liveTools.has(toolName))
|
|
100
|
+
return undefined;
|
|
101
|
+
const serverName = this.cachedToolServers.get(toolName);
|
|
102
|
+
if (!serverName)
|
|
103
|
+
return undefined;
|
|
104
|
+
const outcome = (await this.manager?.whenConnected(serverName)) ?? {
|
|
105
|
+
ok: false,
|
|
106
|
+
error: "MCP is disabled for this session",
|
|
107
|
+
};
|
|
108
|
+
return outcome.ok
|
|
109
|
+
? { serverName, ok: true }
|
|
110
|
+
: { serverName, ok: false, error: outcome.error };
|
|
111
|
+
}, this.getContextLimits()));
|
|
112
|
+
}
|
|
113
|
+
/** Append tools, replacing any same-named entry (cached stub → live tool). */
|
|
114
|
+
replaceOrPushTools(tools) {
|
|
115
|
+
const live = this.getTools();
|
|
116
|
+
for (const tool of tools) {
|
|
117
|
+
const index = live.findIndex((t) => t.name === tool.name);
|
|
118
|
+
if (index >= 0)
|
|
119
|
+
live[index] = tool;
|
|
120
|
+
else
|
|
121
|
+
live.push(tool);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Swap already-promoted cached stubs for their live equivalents, in place. */
|
|
125
|
+
replaceLivePromotedTools(tools) {
|
|
126
|
+
const live = this.getTools();
|
|
127
|
+
for (const tool of tools) {
|
|
128
|
+
const index = live.findIndex((t) => t.name === tool.name);
|
|
129
|
+
if (index >= 0)
|
|
130
|
+
live[index] = tool;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Publish cached tool definitions into the deferred catalog so `tool_search`
|
|
135
|
+
* answers correctly on turn 1. A cached stub carries the real name, one-line
|
|
136
|
+
* description and input schema; calling it waits for the live connection and
|
|
137
|
+
* then dispatches against the real client, or returns a clear error when that
|
|
138
|
+
* server ultimately failed. Live tools replace stubs on connect.
|
|
139
|
+
*/
|
|
140
|
+
async seedFromCache(servers) {
|
|
141
|
+
let entries;
|
|
142
|
+
try {
|
|
143
|
+
entries = await this.catalogCache.entriesFor(servers);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const stubs = [];
|
|
149
|
+
for (const [serverName, entry] of entries) {
|
|
150
|
+
for (const cached of entry.tools) {
|
|
151
|
+
if (this.liveTools.has(cached.name))
|
|
152
|
+
continue;
|
|
153
|
+
this.cachedToolServers.set(cached.name, serverName);
|
|
154
|
+
stubs.push(this.buildCachedMcpTool(serverName, cached));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (stubs.length === 0)
|
|
158
|
+
return;
|
|
159
|
+
log("INFO", "mcp", "Seeded deferred tool catalog from cache", {
|
|
160
|
+
tools: String(stubs.length),
|
|
161
|
+
servers: String(entries.size),
|
|
162
|
+
});
|
|
163
|
+
// Catalog-only registration for cached stubs — never marks them live.
|
|
164
|
+
this.addDeferred(stubs);
|
|
165
|
+
}
|
|
166
|
+
buildCachedMcpTool(serverName, cached) {
|
|
167
|
+
return {
|
|
168
|
+
name: cached.name,
|
|
169
|
+
description: cached.description,
|
|
170
|
+
parameters: z.record(z.string(), z.unknown()),
|
|
171
|
+
rawInputSchema: cached.rawInputSchema,
|
|
172
|
+
execute: async (args, context) => {
|
|
173
|
+
const live = this.liveTools.get(cached.name);
|
|
174
|
+
if (live)
|
|
175
|
+
return live.execute(args, context);
|
|
176
|
+
const outcome = (await this.manager?.whenConnected(serverName)) ?? {
|
|
177
|
+
ok: false,
|
|
178
|
+
error: "MCP is disabled for this session",
|
|
179
|
+
};
|
|
180
|
+
if (!outcome.ok) {
|
|
181
|
+
return (`MCP tool ${cached.name} is unavailable: server "${serverName}" did not connect ` +
|
|
182
|
+
`(${outcome.error}). This tool was offered from a cached catalog. ` +
|
|
183
|
+
`Use a different approach or ask the user to check their MCP configuration.`);
|
|
184
|
+
}
|
|
185
|
+
const connected = this.liveTools.get(cached.name);
|
|
186
|
+
if (!connected) {
|
|
187
|
+
return (`MCP tool ${cached.name} no longer exists: server "${serverName}" connected but ` +
|
|
188
|
+
`does not expose it. The cached catalog entry was stale.`);
|
|
189
|
+
}
|
|
190
|
+
return connected.execute(args, context);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=mcp-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-tools.js","sourceRoot":"","sources":["../../../src/core/agent-session/mcp-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CycleDetector, ToolCallProgressTracker, type CycleDetection, type LoopBreakDecision } from "../loop-breaker.js";
|
|
2
|
+
import { type SemanticCallDigest, type SemanticLoopVerdict } from "../semantic-loop-check.js";
|
|
3
|
+
/** LLM-judged loop detection state for one run. */
|
|
4
|
+
export interface SemanticLoopRunState {
|
|
5
|
+
checksUsed: number;
|
|
6
|
+
lastCheckTurn: number;
|
|
7
|
+
pending: boolean;
|
|
8
|
+
verdict: SemanticLoopVerdict | null;
|
|
9
|
+
injected: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Per-run stuck-detection state (mirrors the TUI's useAgentLoop refs): tool
|
|
13
|
+
* stats, streamed text, progress/cycle trackers, the two-stage loop-breaker and
|
|
14
|
+
* the semantic loop judge's budget. Reset at the start of every run; observed
|
|
15
|
+
* from the event stream. Owns state and decisions only — logging, events and
|
|
16
|
+
* the judge call stay with the session.
|
|
17
|
+
*/
|
|
18
|
+
export declare class RunLoopMonitor {
|
|
19
|
+
stats: {
|
|
20
|
+
toolCalls: number;
|
|
21
|
+
toolFailures: number;
|
|
22
|
+
turns: number;
|
|
23
|
+
};
|
|
24
|
+
text: string;
|
|
25
|
+
consecutiveFailures: number;
|
|
26
|
+
repeatedNoProgressCalls: number;
|
|
27
|
+
readonly progressTracker: ToolCallProgressTracker;
|
|
28
|
+
readonly cycleDetector: CycleDetector;
|
|
29
|
+
cyclicPattern: CycleDetection | null;
|
|
30
|
+
/** 0 = none; 1 = first nudge sent; 2 = final stop-and-report injected. */
|
|
31
|
+
loopBreakInjected: 0 | 1 | 2;
|
|
32
|
+
/** Recent tool-call digests for the semantic loop judge — bounded ring. */
|
|
33
|
+
recentCalls: SemanticCallDigest[];
|
|
34
|
+
/** LLM-judged loop detection state. `verdict` holds a LOOP verdict awaiting
|
|
35
|
+
* injection at the next steering poll; judge failures fail open (no
|
|
36
|
+
* injection) and still consume budget + cooldown. Replaced (never mutated)
|
|
37
|
+
* on reset so a late judge from an earlier run cannot write into it. */
|
|
38
|
+
semanticLoop: SemanticLoopRunState;
|
|
39
|
+
/** Clean slate for a new run. */
|
|
40
|
+
reset(): void;
|
|
41
|
+
recordText(text: string): void;
|
|
42
|
+
recordToolEnd(name: string, args: Record<string, unknown> | undefined, result: string, isError: boolean): void;
|
|
43
|
+
recordTurn(turn: number): void;
|
|
44
|
+
/** Deterministic stuck verdict from the current signals. */
|
|
45
|
+
evaluateLoopBreak(): LoopBreakDecision;
|
|
46
|
+
/**
|
|
47
|
+
* Two-stage loop-breaker: stage 1 nudges; a FRESH detection after that
|
|
48
|
+
* escalates to stage 2. Returns the stage to inject (signals reset so stage 2
|
|
49
|
+
* only fires on new evidence), or null when nothing should be injected.
|
|
50
|
+
*/
|
|
51
|
+
takeLoopBreakStage(decision: LoopBreakDecision): 1 | 2 | null;
|
|
52
|
+
/** Consume a pending semantic LOOP verdict, exactly once per run. */
|
|
53
|
+
takeSemanticVerdict(): SemanticLoopVerdict | null;
|
|
54
|
+
shouldRunSemanticCheck(deterministicBreak: boolean): boolean;
|
|
55
|
+
/** Mark a judge call in flight; the returned run state is its stale-guard token. */
|
|
56
|
+
beginSemanticCheck(): SemanticLoopRunState;
|
|
57
|
+
/** Publish a judge verdict only if its run is still the current one. */
|
|
58
|
+
publishSemanticVerdict(runState: SemanticLoopRunState, verdict: SemanticLoopVerdict): void;
|
|
59
|
+
/** Settle a judge call: consume budget + cooldown, unless its run is stale. */
|
|
60
|
+
settleSemanticCheck(runState: SemanticLoopRunState): void;
|
|
61
|
+
/** Reasons to refuse a turn-budget extension; empty means grant. */
|
|
62
|
+
turnExtensionRefusals(): string[];
|
|
63
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { evaluateLoopBreak, CycleDetector, ToolCallProgressTracker, detectTextRepetition, } from "../loop-breaker.js";
|
|
2
|
+
import { MAX_SEMANTIC_LOOP_CALLS, shouldRunSemanticLoopCheck, } from "../semantic-loop-check.js";
|
|
3
|
+
import { TURN_EXTENSION_MAX_FAILURE_RATIO } from "./helpers.js";
|
|
4
|
+
function freshSemanticLoopState() {
|
|
5
|
+
return { checksUsed: 0, lastCheckTurn: 0, pending: false, verdict: null, injected: false };
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Per-run stuck-detection state (mirrors the TUI's useAgentLoop refs): tool
|
|
9
|
+
* stats, streamed text, progress/cycle trackers, the two-stage loop-breaker and
|
|
10
|
+
* the semantic loop judge's budget. Reset at the start of every run; observed
|
|
11
|
+
* from the event stream. Owns state and decisions only — logging, events and
|
|
12
|
+
* the judge call stay with the session.
|
|
13
|
+
*/
|
|
14
|
+
export class RunLoopMonitor {
|
|
15
|
+
stats = { toolCalls: 0, toolFailures: 0, turns: 0 };
|
|
16
|
+
text = "";
|
|
17
|
+
consecutiveFailures = 0;
|
|
18
|
+
repeatedNoProgressCalls = 0;
|
|
19
|
+
progressTracker = new ToolCallProgressTracker();
|
|
20
|
+
cycleDetector = new CycleDetector();
|
|
21
|
+
cyclicPattern = null;
|
|
22
|
+
/** 0 = none; 1 = first nudge sent; 2 = final stop-and-report injected. */
|
|
23
|
+
loopBreakInjected = 0;
|
|
24
|
+
/** Recent tool-call digests for the semantic loop judge — bounded ring. */
|
|
25
|
+
recentCalls = [];
|
|
26
|
+
/** LLM-judged loop detection state. `verdict` holds a LOOP verdict awaiting
|
|
27
|
+
* injection at the next steering poll; judge failures fail open (no
|
|
28
|
+
* injection) and still consume budget + cooldown. Replaced (never mutated)
|
|
29
|
+
* on reset so a late judge from an earlier run cannot write into it. */
|
|
30
|
+
semanticLoop = freshSemanticLoopState();
|
|
31
|
+
/** Clean slate for a new run. */
|
|
32
|
+
reset() {
|
|
33
|
+
this.stats = { toolCalls: 0, toolFailures: 0, turns: 0 };
|
|
34
|
+
this.text = "";
|
|
35
|
+
this.consecutiveFailures = 0;
|
|
36
|
+
this.repeatedNoProgressCalls = 0;
|
|
37
|
+
this.progressTracker.reset();
|
|
38
|
+
this.cycleDetector.reset();
|
|
39
|
+
this.cyclicPattern = null;
|
|
40
|
+
this.loopBreakInjected = 0;
|
|
41
|
+
this.recentCalls = [];
|
|
42
|
+
this.semanticLoop = freshSemanticLoopState();
|
|
43
|
+
}
|
|
44
|
+
recordText(text) {
|
|
45
|
+
this.text += text;
|
|
46
|
+
}
|
|
47
|
+
recordToolEnd(name, args, result, isError) {
|
|
48
|
+
this.stats.toolCalls += 1;
|
|
49
|
+
if (isError)
|
|
50
|
+
this.stats.toolFailures += 1;
|
|
51
|
+
this.consecutiveFailures = isError ? this.consecutiveFailures + 1 : 0;
|
|
52
|
+
this.repeatedNoProgressCalls = this.progressTracker.record(name, args, result, isError);
|
|
53
|
+
this.cyclicPattern = this.cycleDetector.record(name, args, result, isError);
|
|
54
|
+
// Semantic-loop judge input: a bounded digest of WHAT was attempted and
|
|
55
|
+
// HOW it came out. Args/results are sliced AT RECORD TIME — a write with
|
|
56
|
+
// a 50 KiB payload or a bash dump must never inflate the ring, and the
|
|
57
|
+
// judge needs shapes, not payloads.
|
|
58
|
+
this.recentCalls.push({
|
|
59
|
+
tool: name,
|
|
60
|
+
args: args === undefined ? "" : JSON.stringify(args).slice(0, 300),
|
|
61
|
+
ok: !isError,
|
|
62
|
+
result: result.slice(0, 400),
|
|
63
|
+
});
|
|
64
|
+
if (this.recentCalls.length > MAX_SEMANTIC_LOOP_CALLS) {
|
|
65
|
+
this.recentCalls.splice(0, this.recentCalls.length - MAX_SEMANTIC_LOOP_CALLS);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
recordTurn(turn) {
|
|
69
|
+
this.stats.turns = turn;
|
|
70
|
+
}
|
|
71
|
+
/** Deterministic stuck verdict from the current signals. */
|
|
72
|
+
evaluateLoopBreak() {
|
|
73
|
+
return evaluateLoopBreak({
|
|
74
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
75
|
+
repeatedNoProgressCalls: this.repeatedNoProgressCalls,
|
|
76
|
+
textRepetitionDetected: detectTextRepetition(this.text),
|
|
77
|
+
...(this.cyclicPattern ? { cyclicPattern: this.cyclicPattern } : {}),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Two-stage loop-breaker: stage 1 nudges; a FRESH detection after that
|
|
82
|
+
* escalates to stage 2. Returns the stage to inject (signals reset so stage 2
|
|
83
|
+
* only fires on new evidence), or null when nothing should be injected.
|
|
84
|
+
*/
|
|
85
|
+
takeLoopBreakStage(decision) {
|
|
86
|
+
if (this.loopBreakInjected >= 2)
|
|
87
|
+
return null;
|
|
88
|
+
if (!decision.shouldBreak)
|
|
89
|
+
return null;
|
|
90
|
+
const stage = this.loopBreakInjected === 0 ? 1 : 2;
|
|
91
|
+
this.loopBreakInjected = stage;
|
|
92
|
+
this.progressTracker.reset();
|
|
93
|
+
this.cycleDetector.reset();
|
|
94
|
+
this.cyclicPattern = null;
|
|
95
|
+
this.consecutiveFailures = 0;
|
|
96
|
+
this.repeatedNoProgressCalls = 0;
|
|
97
|
+
// The deterministic breaker owns this burst — a semantic verdict from
|
|
98
|
+
// the same burst must not double-correct on the next poll.
|
|
99
|
+
this.semanticLoop.verdict = null;
|
|
100
|
+
// Clear the text buffer too — otherwise a stage-1 text-repetition
|
|
101
|
+
// trigger still sees the same repeated tail on the next check and
|
|
102
|
+
// escalates to stage 2 on stale evidence.
|
|
103
|
+
this.text = "";
|
|
104
|
+
return stage;
|
|
105
|
+
}
|
|
106
|
+
/** Consume a pending semantic LOOP verdict, exactly once per run. */
|
|
107
|
+
takeSemanticVerdict() {
|
|
108
|
+
if (!this.semanticLoop.verdict || this.semanticLoop.injected)
|
|
109
|
+
return null;
|
|
110
|
+
const verdict = this.semanticLoop.verdict;
|
|
111
|
+
this.semanticLoop.injected = true;
|
|
112
|
+
this.semanticLoop.verdict = null;
|
|
113
|
+
// The judged burst has been addressed; a fresh burst must re-accumulate.
|
|
114
|
+
this.consecutiveFailures = 0;
|
|
115
|
+
return verdict;
|
|
116
|
+
}
|
|
117
|
+
shouldRunSemanticCheck(deterministicBreak) {
|
|
118
|
+
return shouldRunSemanticLoopCheck({
|
|
119
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
120
|
+
totalFailures: this.stats.toolFailures,
|
|
121
|
+
turns: this.stats.turns,
|
|
122
|
+
lastCheckTurn: this.semanticLoop.lastCheckTurn,
|
|
123
|
+
checksUsed: this.semanticLoop.checksUsed,
|
|
124
|
+
checkPending: this.semanticLoop.pending,
|
|
125
|
+
deterministicBreak,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** Mark a judge call in flight; the returned run state is its stale-guard token. */
|
|
129
|
+
beginSemanticCheck() {
|
|
130
|
+
const runState = this.semanticLoop;
|
|
131
|
+
runState.pending = true;
|
|
132
|
+
return runState;
|
|
133
|
+
}
|
|
134
|
+
/** Publish a judge verdict only if its run is still the current one. */
|
|
135
|
+
publishSemanticVerdict(runState, verdict) {
|
|
136
|
+
if (this.semanticLoop === runState)
|
|
137
|
+
runState.verdict = verdict;
|
|
138
|
+
}
|
|
139
|
+
/** Settle a judge call: consume budget + cooldown, unless its run is stale. */
|
|
140
|
+
settleSemanticCheck(runState) {
|
|
141
|
+
if (this.semanticLoop === runState) {
|
|
142
|
+
runState.pending = false;
|
|
143
|
+
runState.checksUsed += 1;
|
|
144
|
+
runState.lastCheckTurn = this.stats.turns;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Reasons to refuse a turn-budget extension; empty means grant. */
|
|
148
|
+
turnExtensionRefusals() {
|
|
149
|
+
const refusals = [];
|
|
150
|
+
const stuck = this.evaluateLoopBreak();
|
|
151
|
+
if (stuck.shouldBreak)
|
|
152
|
+
refusals.push(...stuck.reasons);
|
|
153
|
+
// Stage 2 means the loop-breaker already detected spinning twice and told
|
|
154
|
+
// the agent to stop and report. Do not overrule that with more turns.
|
|
155
|
+
if (this.loopBreakInjected >= 2)
|
|
156
|
+
refusals.push("loop-breaker already escalated");
|
|
157
|
+
const { toolCalls, toolFailures } = this.stats;
|
|
158
|
+
if (toolCalls > 0 && toolFailures / toolCalls > TURN_EXTENSION_MAX_FAILURE_RATIO) {
|
|
159
|
+
refusals.push(`${toolFailures}/${toolCalls} tool calls failed`);
|
|
160
|
+
}
|
|
161
|
+
return refusals;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=run-loop-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-loop-monitor.js","sourceRoot":"","sources":["../../../src/core/agent-session/run-loop-monitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type KenTurnPayload, type AutopilotMarkerPayload, type AppMarkerPayload, type SessionEntry, type SessionManager, type TurnMetricPayload } from "../session-manager.js";
|
|
2
|
+
import type { CompactionAnchorRemap } from "../compaction/compactor.js";
|
|
3
|
+
/**
|
|
4
|
+
* Display-only transcript history recorded against a session file: Ken Kai
|
|
5
|
+
* turns, autopilot verdicts, app markers and per-turn metrics. NEVER part of
|
|
6
|
+
* the LLM message history — persisted as `custom` entries (parentId null, so
|
|
7
|
+
* never on the message DAG) and reloaded on resume so the transcript renders
|
|
8
|
+
* the same rows the live run showed. Persistence is a no-op while the session
|
|
9
|
+
* has no file (transient sessions keep markers in memory only).
|
|
10
|
+
*/
|
|
11
|
+
export declare class TranscriptMarkers {
|
|
12
|
+
private readonly getSessionManager;
|
|
13
|
+
private readonly getSessionPath;
|
|
14
|
+
private kenTurns;
|
|
15
|
+
private autopilotMarkers;
|
|
16
|
+
private appMarkers;
|
|
17
|
+
private turnMetrics;
|
|
18
|
+
constructor(getSessionManager: () => SessionManager, getSessionPath: () => string);
|
|
19
|
+
getKenTurns(): KenTurnPayload[];
|
|
20
|
+
getAutopilotMarkers(): AutopilotMarkerPayload[];
|
|
21
|
+
getAppMarkers(): AppMarkerPayload[];
|
|
22
|
+
getTurnMetrics(): TurnMetricPayload[];
|
|
23
|
+
/** Summed cost when every recorded turn has a known price, else undefined. */
|
|
24
|
+
knownCostUsd(): number | undefined;
|
|
25
|
+
/** Drop everything — display-only history belongs to the OLD session. */
|
|
26
|
+
clear(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Restore from a loaded session file. The leaf is passed so each marker also
|
|
29
|
+
* carries its FILE-order position, the fallback used when a legacy anchor is
|
|
30
|
+
* out of range (see RecordedPosition).
|
|
31
|
+
*/
|
|
32
|
+
restore(entries: SessionEntry[], leafId: string | null | undefined): void;
|
|
33
|
+
/**
|
|
34
|
+
* Rebase every transcript anchor (Ken turns, autopilot verdicts, app markers)
|
|
35
|
+
* onto a freshly compacted message list.
|
|
36
|
+
*/
|
|
37
|
+
remapAnchors(remap: CompactionAnchorRemap | undefined): void;
|
|
38
|
+
recordKenTurn(question: string, reply: string, afterMessageCount: number): Promise<void>;
|
|
39
|
+
recordAutopilotMarker(phase: AutopilotMarkerPayload["phase"], extra: {
|
|
40
|
+
reason?: string;
|
|
41
|
+
body?: string;
|
|
42
|
+
} | undefined, afterMessageCount: number): Promise<void>;
|
|
43
|
+
recordAppMarker(kind: AppMarkerPayload["kind"], data: Record<string, unknown>, afterMessageCount: number): Promise<void>;
|
|
44
|
+
/** Keep a turn metric in memory; {@link persistTurnMetric} writes it. */
|
|
45
|
+
addTurnMetric(payload: TurnMetricPayload): void;
|
|
46
|
+
persistTurnMetric(payload: TurnMetricPayload): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Re-append everything to the current session file. Called after a
|
|
49
|
+
* continuation/compaction file is created so display-only history isn't lost
|
|
50
|
+
* when the session is rewritten (those rewrites only re-persist messages).
|
|
51
|
+
* Each marker keeps its original `afterMessageCount` anchor.
|
|
52
|
+
*/
|
|
53
|
+
rePersistAll(): Promise<void>;
|
|
54
|
+
private rePersistTurnMetrics;
|
|
55
|
+
private rePersist;
|
|
56
|
+
private append;
|
|
57
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { KEN_TURN_CUSTOM_KIND, AUTOPILOT_MARKER_CUSTOM_KIND, APP_MARKER_CUSTOM_KIND, } from "../session-manager.js";
|
|
3
|
+
import { remapAnchorForCompaction, stripRecordedPosition } from "../session-history.js";
|
|
4
|
+
/**
|
|
5
|
+
* Display-only transcript history recorded against a session file: Ken Kai
|
|
6
|
+
* turns, autopilot verdicts, app markers and per-turn metrics. NEVER part of
|
|
7
|
+
* the LLM message history — persisted as `custom` entries (parentId null, so
|
|
8
|
+
* never on the message DAG) and reloaded on resume so the transcript renders
|
|
9
|
+
* the same rows the live run showed. Persistence is a no-op while the session
|
|
10
|
+
* has no file (transient sessions keep markers in memory only).
|
|
11
|
+
*/
|
|
12
|
+
export class TranscriptMarkers {
|
|
13
|
+
getSessionManager;
|
|
14
|
+
getSessionPath;
|
|
15
|
+
// Ken Kai (mentor agent) turns. Each carries the non-system message count at
|
|
16
|
+
// record time so the webview can interleave them chronologically.
|
|
17
|
+
kenTurns = [];
|
|
18
|
+
// Autopilot Ken (auto-reviewer) verdict markers (prompted / done / human /
|
|
19
|
+
// capped), so a resumed session shows the identical Ken bubble the live run
|
|
20
|
+
// showed instead of dropping it or replaying a raw verdict.
|
|
21
|
+
autopilotMarkers = [];
|
|
22
|
+
// Generic app transcript markers (plan-mode banner, task header, error rows,
|
|
23
|
+
// user-bubble display hints).
|
|
24
|
+
appMarkers = [];
|
|
25
|
+
turnMetrics = [];
|
|
26
|
+
constructor(getSessionManager, getSessionPath) {
|
|
27
|
+
this.getSessionManager = getSessionManager;
|
|
28
|
+
this.getSessionPath = getSessionPath;
|
|
29
|
+
}
|
|
30
|
+
getKenTurns() {
|
|
31
|
+
return this.kenTurns;
|
|
32
|
+
}
|
|
33
|
+
getAutopilotMarkers() {
|
|
34
|
+
return this.autopilotMarkers;
|
|
35
|
+
}
|
|
36
|
+
getAppMarkers() {
|
|
37
|
+
return this.appMarkers;
|
|
38
|
+
}
|
|
39
|
+
getTurnMetrics() {
|
|
40
|
+
return this.turnMetrics.map((metric) => ({
|
|
41
|
+
...metric,
|
|
42
|
+
usage: { ...metric.usage },
|
|
43
|
+
timing: { ...metric.timing },
|
|
44
|
+
cost: { ...metric.cost },
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
/** Summed cost when every recorded turn has a known price, else undefined. */
|
|
48
|
+
knownCostUsd() {
|
|
49
|
+
return this.turnMetrics.length > 0 && this.turnMetrics.every((m) => m.cost.status === "known")
|
|
50
|
+
? this.turnMetrics.reduce((sum, m) => sum + (m.cost.status === "known" ? m.cost.usd : 0), 0)
|
|
51
|
+
: undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Drop everything — display-only history belongs to the OLD session. */
|
|
54
|
+
clear() {
|
|
55
|
+
this.kenTurns = [];
|
|
56
|
+
this.autopilotMarkers = [];
|
|
57
|
+
this.appMarkers = [];
|
|
58
|
+
this.turnMetrics = [];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Restore from a loaded session file. The leaf is passed so each marker also
|
|
62
|
+
* carries its FILE-order position, the fallback used when a legacy anchor is
|
|
63
|
+
* out of range (see RecordedPosition).
|
|
64
|
+
*/
|
|
65
|
+
restore(entries, leafId) {
|
|
66
|
+
const sessionManager = this.getSessionManager();
|
|
67
|
+
this.kenTurns = sessionManager.getKenTurns(entries, leafId);
|
|
68
|
+
this.autopilotMarkers = sessionManager.getAutopilotMarkers(entries, leafId);
|
|
69
|
+
this.appMarkers = sessionManager.getAppMarkers(entries, leafId);
|
|
70
|
+
this.turnMetrics = sessionManager.getTurnMetrics(entries);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Rebase every transcript anchor (Ken turns, autopilot verdicts, app markers)
|
|
74
|
+
* onto a freshly compacted message list.
|
|
75
|
+
*/
|
|
76
|
+
remapAnchors(remap) {
|
|
77
|
+
if (!remap)
|
|
78
|
+
return;
|
|
79
|
+
const move = (payload) => ({
|
|
80
|
+
...payload,
|
|
81
|
+
afterMessageCount: remapAnchorForCompaction(payload.afterMessageCount, remap),
|
|
82
|
+
});
|
|
83
|
+
this.kenTurns = this.kenTurns.map(move);
|
|
84
|
+
this.autopilotMarkers = this.autopilotMarkers.map(move);
|
|
85
|
+
this.appMarkers = this.appMarkers.map(move);
|
|
86
|
+
}
|
|
87
|
+
async recordKenTurn(question, reply, afterMessageCount) {
|
|
88
|
+
const payload = { version: 1, question, reply, afterMessageCount };
|
|
89
|
+
this.kenTurns.push(payload);
|
|
90
|
+
await this.append(KEN_TURN_CUSTOM_KIND, payload);
|
|
91
|
+
}
|
|
92
|
+
async recordAutopilotMarker(phase, extra, afterMessageCount) {
|
|
93
|
+
const payload = {
|
|
94
|
+
version: 1,
|
|
95
|
+
phase,
|
|
96
|
+
afterMessageCount,
|
|
97
|
+
...(extra?.reason !== undefined ? { reason: extra.reason } : {}),
|
|
98
|
+
...(extra?.body !== undefined ? { body: extra.body } : {}),
|
|
99
|
+
};
|
|
100
|
+
this.autopilotMarkers.push(payload);
|
|
101
|
+
await this.append(AUTOPILOT_MARKER_CUSTOM_KIND, payload);
|
|
102
|
+
}
|
|
103
|
+
async recordAppMarker(kind, data, afterMessageCount) {
|
|
104
|
+
const payload = { version: 1, kind, afterMessageCount, data };
|
|
105
|
+
this.appMarkers.push(payload);
|
|
106
|
+
await this.append(APP_MARKER_CUSTOM_KIND, payload);
|
|
107
|
+
}
|
|
108
|
+
/** Keep a turn metric in memory; {@link persistTurnMetric} writes it. */
|
|
109
|
+
addTurnMetric(payload) {
|
|
110
|
+
this.turnMetrics.push(payload);
|
|
111
|
+
}
|
|
112
|
+
async persistTurnMetric(payload) {
|
|
113
|
+
const sessionPath = this.getSessionPath();
|
|
114
|
+
if (sessionPath)
|
|
115
|
+
await this.getSessionManager().appendTurnMetric(sessionPath, payload);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Re-append everything to the current session file. Called after a
|
|
119
|
+
* continuation/compaction file is created so display-only history isn't lost
|
|
120
|
+
* when the session is rewritten (those rewrites only re-persist messages).
|
|
121
|
+
* Each marker keeps its original `afterMessageCount` anchor.
|
|
122
|
+
*/
|
|
123
|
+
async rePersistAll() {
|
|
124
|
+
await this.rePersistTurnMetrics();
|
|
125
|
+
await this.rePersist(KEN_TURN_CUSTOM_KIND, this.kenTurns);
|
|
126
|
+
await this.rePersist(AUTOPILOT_MARKER_CUSTOM_KIND, this.autopilotMarkers);
|
|
127
|
+
await this.rePersist(APP_MARKER_CUSTOM_KIND, this.appMarkers);
|
|
128
|
+
}
|
|
129
|
+
async rePersistTurnMetrics() {
|
|
130
|
+
if (!this.getSessionPath())
|
|
131
|
+
return;
|
|
132
|
+
for (const metric of this.turnMetrics) {
|
|
133
|
+
await this.getSessionManager().appendTurnMetric(this.getSessionPath(), metric);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async rePersist(kind, payloads) {
|
|
137
|
+
if (!this.getSessionPath())
|
|
138
|
+
return;
|
|
139
|
+
for (const payload of payloads) {
|
|
140
|
+
const entry = {
|
|
141
|
+
type: "custom",
|
|
142
|
+
kind,
|
|
143
|
+
id: crypto.randomUUID(),
|
|
144
|
+
parentId: null,
|
|
145
|
+
timestamp: new Date().toISOString(),
|
|
146
|
+
data: stripRecordedPosition(payload),
|
|
147
|
+
};
|
|
148
|
+
await this.getSessionManager().appendEntry(this.getSessionPath(), entry);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async append(kind, payload) {
|
|
152
|
+
const sessionPath = this.getSessionPath();
|
|
153
|
+
if (!sessionPath)
|
|
154
|
+
return;
|
|
155
|
+
const entry = {
|
|
156
|
+
type: "custom",
|
|
157
|
+
kind,
|
|
158
|
+
id: crypto.randomUUID(),
|
|
159
|
+
parentId: null,
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
data: payload,
|
|
162
|
+
};
|
|
163
|
+
await this.getSessionManager().appendEntry(sessionPath, entry);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=transcript-markers.js.map
|