@code-yeongyu/senpi-agent-core 2026.9.29 → 2026.10.1-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -20
- package/dist/agent-loop.d.ts +27 -3
- package/dist/agent-loop.js +192 -56
- package/dist/agent.d.ts +21 -7
- package/dist/agent.js +38 -18
- package/dist/harness/execution/tools.d.ts +1 -1
- package/dist/harness/execution/tools.js +0 -2
- package/dist/harness/messages.js +1 -0
- package/dist/harness/pico3/bash.d.ts +10 -0
- package/dist/harness/pico3/bash.js +25 -0
- package/dist/harness/pico3/bounded.d.ts +21 -0
- package/dist/harness/pico3/bounded.js +95 -0
- package/dist/harness/pico3/chord.d.ts +41 -0
- package/dist/harness/pico3/chord.js +147 -0
- package/dist/harness/pico3/context.d.ts +16 -0
- package/dist/harness/pico3/context.js +88 -0
- package/dist/harness/pico3/harness.d.ts +198 -0
- package/dist/harness/pico3/harness.js +645 -0
- package/dist/harness/pico3/hooks.d.ts +10 -0
- package/dist/harness/pico3/hooks.js +33 -0
- package/dist/harness/pico3/index.d.ts +19 -0
- package/dist/harness/pico3/index.js +15 -0
- package/dist/harness/pico3/jsonl.d.ts +58 -0
- package/dist/harness/pico3/jsonl.js +322 -0
- package/dist/harness/pico3/kinds/collapse.d.ts +49 -0
- package/dist/harness/pico3/kinds/collapse.js +191 -0
- package/dist/harness/pico3/kinds/entries.d.ts +78 -0
- package/dist/harness/pico3/kinds/entries.js +13 -0
- package/dist/harness/pico3/kinds/frames.d.ts +9 -0
- package/dist/harness/pico3/kinds/frames.js +75 -0
- package/dist/harness/pico3/kinds/generation.d.ts +94 -0
- package/dist/harness/pico3/kinds/generation.js +504 -0
- package/dist/harness/pico3/kinds/job.d.ts +44 -0
- package/dist/harness/pico3/kinds/job.js +143 -0
- package/dist/harness/pico3/kinds/plugin.d.ts +15 -0
- package/dist/harness/pico3/kinds/plugin.js +35 -0
- package/dist/harness/pico3/kinds/post-tools.d.ts +22 -0
- package/dist/harness/pico3/kinds/post-tools.js +145 -0
- package/dist/harness/pico3/kinds/task-api.d.ts +3 -0
- package/dist/harness/pico3/kinds/task-api.js +45 -0
- package/dist/harness/pico3/kinds/tool.d.ts +43 -0
- package/dist/harness/pico3/kinds/tool.js +373 -0
- package/dist/harness/pico3/legacy-tracker.d.ts +11 -0
- package/dist/harness/pico3/legacy-tracker.js +39 -0
- package/dist/harness/pico3/membrane.d.ts +23 -0
- package/dist/harness/pico3/membrane.js +140 -0
- package/dist/harness/pico3/memory.d.ts +53 -0
- package/dist/harness/pico3/memory.js +265 -0
- package/dist/harness/pico3/scheduler.d.ts +49 -0
- package/dist/harness/pico3/scheduler.js +437 -0
- package/dist/harness/pico3/session.d.ts +264 -0
- package/dist/harness/pico3/session.js +1328 -0
- package/dist/harness/pico3/system.d.ts +126 -0
- package/dist/harness/pico3/system.js +244 -0
- package/dist/harness/pico3/types.d.ts +973 -0
- package/dist/harness/pico3/types.js +94 -0
- package/dist/harness/pico3/view.d.ts +34 -0
- package/dist/harness/pico3/view.js +404 -0
- package/dist/harness/runtime/drive/tool-placement.js +2 -27
- package/dist/harness/telemetry.d.ts +36 -36
- package/dist/harness/tools/image.js +1 -1
- package/dist/harness/utils/read-folders/index.d.ts +2 -2
- package/dist/harness/utils/read-folders/index.js +2 -2
- package/dist/harness/utils/read-folders/tree-sitter/grammar-assets.js +1 -1
- package/dist/proxy.d.ts +2 -2
- package/dist/types.d.ts +111 -34
- package/package.json +10 -5
- package/dist/agent-loop.d.ts.map +0 -1
- package/dist/agent-loop.js.map +0 -1
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js.map +0 -1
- package/dist/assistant-terminal-state.d.ts.map +0 -1
- package/dist/assistant-terminal-state.js.map +0 -1
- package/dist/empty-assistant-recovery.d.ts.map +0 -1
- package/dist/empty-assistant-recovery.js.map +0 -1
- package/dist/harness/agent-harness.d.ts.map +0 -1
- package/dist/harness/agent-harness.js.map +0 -1
- package/dist/harness/compaction/branch-summarization.d.ts.map +0 -1
- package/dist/harness/compaction/branch-summarization.js.map +0 -1
- package/dist/harness/compaction/compaction.d.ts.map +0 -1
- package/dist/harness/compaction/compaction.js.map +0 -1
- package/dist/harness/compaction/utils.d.ts.map +0 -1
- package/dist/harness/compaction/utils.js.map +0 -1
- package/dist/harness/config.d.ts.map +0 -1
- package/dist/harness/config.js.map +0 -1
- package/dist/harness/context.d.ts.map +0 -1
- package/dist/harness/context.js.map +0 -1
- package/dist/harness/env/nodejs.d.ts.map +0 -1
- package/dist/harness/env/nodejs.js.map +0 -1
- package/dist/harness/events.d.ts.map +0 -1
- package/dist/harness/events.js.map +0 -1
- package/dist/harness/execution/assistant.d.ts.map +0 -1
- package/dist/harness/execution/assistant.js.map +0 -1
- package/dist/harness/execution/effect-gate.d.ts.map +0 -1
- package/dist/harness/execution/effect-gate.js.map +0 -1
- package/dist/harness/execution/tools.d.ts.map +0 -1
- package/dist/harness/execution/tools.js.map +0 -1
- package/dist/harness/hooks.d.ts.map +0 -1
- package/dist/harness/hooks.js.map +0 -1
- package/dist/harness/messages.d.ts.map +0 -1
- package/dist/harness/messages.js.map +0 -1
- package/dist/harness/prompt-templates.d.ts.map +0 -1
- package/dist/harness/prompt-templates.js.map +0 -1
- package/dist/harness/result.d.ts.map +0 -1
- package/dist/harness/result.js.map +0 -1
- package/dist/harness/runtime/drive/boundary.d.ts.map +0 -1
- package/dist/harness/runtime/drive/boundary.js.map +0 -1
- package/dist/harness/runtime/drive/checkpoint.d.ts.map +0 -1
- package/dist/harness/runtime/drive/checkpoint.js.map +0 -1
- package/dist/harness/runtime/drive/deferred.d.ts.map +0 -1
- package/dist/harness/runtime/drive/deferred.js.map +0 -1
- package/dist/harness/runtime/drive/generation.d.ts.map +0 -1
- package/dist/harness/runtime/drive/generation.js.map +0 -1
- package/dist/harness/runtime/drive/reconcile.d.ts.map +0 -1
- package/dist/harness/runtime/drive/reconcile.js.map +0 -1
- package/dist/harness/runtime/drive/recovery.d.ts.map +0 -1
- package/dist/harness/runtime/drive/recovery.js.map +0 -1
- package/dist/harness/runtime/drive/response.d.ts.map +0 -1
- package/dist/harness/runtime/drive/response.js.map +0 -1
- package/dist/harness/runtime/drive/retry.d.ts.map +0 -1
- package/dist/harness/runtime/drive/retry.js.map +0 -1
- package/dist/harness/runtime/drive/structural.d.ts.map +0 -1
- package/dist/harness/runtime/drive/structural.js.map +0 -1
- package/dist/harness/runtime/drive/terminal.d.ts.map +0 -1
- package/dist/harness/runtime/drive/terminal.js.map +0 -1
- package/dist/harness/runtime/drive/tool-placement.d.ts.map +0 -1
- package/dist/harness/runtime/drive/tool-placement.js.map +0 -1
- package/dist/harness/runtime/drive/tools.d.ts.map +0 -1
- package/dist/harness/runtime/drive/tools.js.map +0 -1
- package/dist/harness/runtime/drive.d.ts.map +0 -1
- package/dist/harness/runtime/drive.js.map +0 -1
- package/dist/harness/runtime/harness.d.ts.map +0 -1
- package/dist/harness/runtime/harness.js.map +0 -1
- package/dist/harness/runtime/index.d.ts.map +0 -1
- package/dist/harness/runtime/index.js.map +0 -1
- package/dist/harness/runtime/lane.d.ts.map +0 -1
- package/dist/harness/runtime/lane.js.map +0 -1
- package/dist/harness/runtime/progress.d.ts.map +0 -1
- package/dist/harness/runtime/progress.js.map +0 -1
- package/dist/harness/runtime/reducer.d.ts.map +0 -1
- package/dist/harness/runtime/reducer.js.map +0 -1
- package/dist/harness/runtime/restore.d.ts.map +0 -1
- package/dist/harness/runtime/restore.js.map +0 -1
- package/dist/harness/runtime/transcript.d.ts.map +0 -1
- package/dist/harness/runtime/transcript.js.map +0 -1
- package/dist/harness/runtime/types.d.ts.map +0 -1
- package/dist/harness/runtime/types.js.map +0 -1
- package/dist/harness/session/commit.d.ts.map +0 -1
- package/dist/harness/session/commit.js.map +0 -1
- package/dist/harness/session/context.d.ts.map +0 -1
- package/dist/harness/session/context.js.map +0 -1
- package/dist/harness/session/fork-policy.d.ts.map +0 -1
- package/dist/harness/session/fork-policy.js.map +0 -1
- package/dist/harness/session/fork.d.ts.map +0 -1
- package/dist/harness/session/fork.js.map +0 -1
- package/dist/harness/session/in-memory-storage-state.d.ts.map +0 -1
- package/dist/harness/session/in-memory-storage-state.js.map +0 -1
- package/dist/harness/session/index.d.ts.map +0 -1
- package/dist/harness/session/index.js.map +0 -1
- package/dist/harness/session/jsonl/codec.d.ts.map +0 -1
- package/dist/harness/session/jsonl/codec.js.map +0 -1
- package/dist/harness/session/jsonl/fork.d.ts.map +0 -1
- package/dist/harness/session/jsonl/fork.js.map +0 -1
- package/dist/harness/session/jsonl/index.d.ts.map +0 -1
- package/dist/harness/session/jsonl/index.js.map +0 -1
- package/dist/harness/session/jsonl/io.d.ts.map +0 -1
- package/dist/harness/session/jsonl/io.js.map +0 -1
- package/dist/harness/session/jsonl/legacy-v3.d.ts.map +0 -1
- package/dist/harness/session/jsonl/legacy-v3.js.map +0 -1
- package/dist/harness/session/jsonl/repo.d.ts.map +0 -1
- package/dist/harness/session/jsonl/repo.js.map +0 -1
- package/dist/harness/session/jsonl/storage.d.ts.map +0 -1
- package/dist/harness/session/jsonl/storage.js.map +0 -1
- package/dist/harness/session/jsonl/types.d.ts.map +0 -1
- package/dist/harness/session/jsonl/types.js.map +0 -1
- package/dist/harness/session/memory.d.ts.map +0 -1
- package/dist/harness/session/memory.js.map +0 -1
- package/dist/harness/session/mutation-line.d.ts.map +0 -1
- package/dist/harness/session/mutation-line.js.map +0 -1
- package/dist/harness/session/session.d.ts.map +0 -1
- package/dist/harness/session/session.js.map +0 -1
- package/dist/harness/session/testing/benchmark/datasets.d.ts.map +0 -1
- package/dist/harness/session/testing/benchmark/datasets.js.map +0 -1
- package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +0 -1
- package/dist/harness/session/testing/benchmark/session-repo.js.map +0 -1
- package/dist/harness/session/testing/benchmark/storage.d.ts.map +0 -1
- package/dist/harness/session/testing/benchmark/storage.js.map +0 -1
- package/dist/harness/session/testing/conformance/session-repo.d.ts.map +0 -1
- package/dist/harness/session/testing/conformance/session-repo.js.map +0 -1
- package/dist/harness/session/testing/conformance/storage.d.ts.map +0 -1
- package/dist/harness/session/testing/conformance/storage.js.map +0 -1
- package/dist/harness/session/testing/gating-storage.d.ts.map +0 -1
- package/dist/harness/session/testing/gating-storage.js.map +0 -1
- package/dist/harness/session/testing/index.d.ts.map +0 -1
- package/dist/harness/session/testing/index.js.map +0 -1
- package/dist/harness/session/testing/instrumented-storage.d.ts.map +0 -1
- package/dist/harness/session/testing/instrumented-storage.js.map +0 -1
- package/dist/harness/session/testing/storage-decorator.d.ts.map +0 -1
- package/dist/harness/session/testing/storage-decorator.js.map +0 -1
- package/dist/harness/session/testing/types.d.ts.map +0 -1
- package/dist/harness/session/testing/types.js.map +0 -1
- package/dist/harness/session/types.d.ts.map +0 -1
- package/dist/harness/session/types.js.map +0 -1
- package/dist/harness/session/values.d.ts.map +0 -1
- package/dist/harness/session/values.js.map +0 -1
- package/dist/harness/skills.d.ts.map +0 -1
- package/dist/harness/skills.js.map +0 -1
- package/dist/harness/system-prompt.d.ts.map +0 -1
- package/dist/harness/system-prompt.js.map +0 -1
- package/dist/harness/telemetry.d.ts.map +0 -1
- package/dist/harness/telemetry.js.map +0 -1
- package/dist/harness/tools/bash.d.ts.map +0 -1
- package/dist/harness/tools/bash.js.map +0 -1
- package/dist/harness/tools/edit-diff.d.ts.map +0 -1
- package/dist/harness/tools/edit-diff.js.map +0 -1
- package/dist/harness/tools/edit.d.ts.map +0 -1
- package/dist/harness/tools/edit.js.map +0 -1
- package/dist/harness/tools/file-mutation-queue.d.ts.map +0 -1
- package/dist/harness/tools/file-mutation-queue.js.map +0 -1
- package/dist/harness/tools/image.d.ts.map +0 -1
- package/dist/harness/tools/image.js.map +0 -1
- package/dist/harness/tools/index.d.ts.map +0 -1
- package/dist/harness/tools/index.js.map +0 -1
- package/dist/harness/tools/path-utils.d.ts.map +0 -1
- package/dist/harness/tools/path-utils.js.map +0 -1
- package/dist/harness/tools/post-mutate.d.ts.map +0 -1
- package/dist/harness/tools/post-mutate.js.map +0 -1
- package/dist/harness/tools/read.d.ts.map +0 -1
- package/dist/harness/tools/read.js.map +0 -1
- package/dist/harness/tools/tool-context.d.ts.map +0 -1
- package/dist/harness/tools/tool-context.js.map +0 -1
- package/dist/harness/tools/write.d.ts.map +0 -1
- package/dist/harness/tools/write.js.map +0 -1
- package/dist/harness/types.d.ts.map +0 -1
- package/dist/harness/types.js.map +0 -1
- package/dist/harness/utils/adaptive-publisher.d.ts.map +0 -1
- package/dist/harness/utils/adaptive-publisher.js.map +0 -1
- package/dist/harness/utils/output-capture.d.ts.map +0 -1
- package/dist/harness/utils/output-capture.js.map +0 -1
- package/dist/harness/utils/read-folders/brace-scanner.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/brace-scanner.js.map +0 -1
- package/dist/harness/utils/read-folders/compose.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/compose.js.map +0 -1
- package/dist/harness/utils/read-folders/header-protection.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/header-protection.js.map +0 -1
- package/dist/harness/utils/read-folders/index.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/index.js.map +0 -1
- package/dist/harness/utils/read-folders/lexical-context.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/lexical-context.js.map +0 -1
- package/dist/harness/utils/read-folders/lexical-spans.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/lexical-spans.js.map +0 -1
- package/dist/harness/utils/read-folders/prepare.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/prepare.js.map +0 -1
- package/dist/harness/utils/read-folders/tree-sitter/engine.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/tree-sitter/engine.js.map +0 -1
- package/dist/harness/utils/read-folders/tree-sitter/grammar-assets.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/tree-sitter/grammar-assets.js.map +0 -1
- package/dist/harness/utils/read-folders/tree-sitter/syntax.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/tree-sitter/syntax.js.map +0 -1
- package/dist/harness/utils/read-folders/types.d.ts.map +0 -1
- package/dist/harness/utils/read-folders/types.js.map +0 -1
- package/dist/harness/utils/segmented-read-view.d.ts.map +0 -1
- package/dist/harness/utils/segmented-read-view.js.map +0 -1
- package/dist/harness/utils/shell-output.d.ts.map +0 -1
- package/dist/harness/utils/shell-output.js.map +0 -1
- package/dist/harness/utils/truncate.d.ts.map +0 -1
- package/dist/harness/utils/truncate.js.map +0 -1
- package/dist/harness/utils/usage.d.ts.map +0 -1
- package/dist/harness/utils/usage.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/proxy.d.ts.map +0 -1
- package/dist/proxy.js.map +0 -1
- package/dist/search/index.d.ts.map +0 -1
- package/dist/search/index.js.map +0 -1
- package/dist/stream-fn.d.ts.map +0 -1
- package/dist/stream-fn.js.map +0 -1
- package/dist/tool-arguments.d.ts.map +0 -1
- package/dist/tool-arguments.js.map +0 -1
- package/dist/tool-name-alias.d.ts.map +0 -1
- package/dist/tool-name-alias.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
package/dist/agent.js
CHANGED
|
@@ -3,7 +3,10 @@ import { buildProviderContext as buildProviderContextFromAgentContext, runAgentL
|
|
|
3
3
|
import { ProviderRetryWatchdogAbortError } from "./assistant-terminal-state.js";
|
|
4
4
|
import { getDefaultStreamFn } from "./stream-fn.js";
|
|
5
5
|
function defaultConvertToLlm(messages) {
|
|
6
|
-
return messages.filter((message) => message.role === "
|
|
6
|
+
return messages.filter((message) => message.role === "system" ||
|
|
7
|
+
message.role === "user" ||
|
|
8
|
+
message.role === "assistant" ||
|
|
9
|
+
message.role === "toolResult");
|
|
7
10
|
}
|
|
8
11
|
const EMPTY_USAGE = {
|
|
9
12
|
input: 0,
|
|
@@ -68,18 +71,16 @@ class PendingMessageQueue {
|
|
|
68
71
|
getClearGeneration() {
|
|
69
72
|
return this.clearGeneration;
|
|
70
73
|
}
|
|
71
|
-
|
|
72
|
-
if (this.mode === "all")
|
|
73
|
-
|
|
74
|
-
this.messages = [];
|
|
75
|
-
return drained;
|
|
76
|
-
}
|
|
74
|
+
peek() {
|
|
75
|
+
if (this.mode === "all")
|
|
76
|
+
return this.messages.slice();
|
|
77
77
|
const first = this.messages[0];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
return first ? [first] : [];
|
|
79
|
+
}
|
|
80
|
+
drain() {
|
|
81
|
+
const drained = this.peek();
|
|
82
|
+
this.messages = this.messages.slice(drained.length);
|
|
83
|
+
return drained;
|
|
83
84
|
}
|
|
84
85
|
prepend(messages) {
|
|
85
86
|
this.messages = [...messages, ...this.messages];
|
|
@@ -110,9 +111,11 @@ export class Agent {
|
|
|
110
111
|
this.getApiKey = runtimeOptions.getApiKey;
|
|
111
112
|
this.onPayload = runtimeOptions.onPayload;
|
|
112
113
|
this.onResponse = runtimeOptions.onResponse;
|
|
114
|
+
this.onProviderStreamEvent = runtimeOptions.onProviderStreamEvent;
|
|
113
115
|
this.beforeToolCall = runtimeOptions.beforeToolCall;
|
|
114
116
|
this.afterToolCall = runtimeOptions.afterToolCall;
|
|
115
|
-
this.
|
|
117
|
+
this.finishTurn = runtimeOptions.finishTurn;
|
|
118
|
+
this.prepareRequest = runtimeOptions.prepareRequest;
|
|
116
119
|
this.prepareNextTurn = runtimeOptions.prepareNextTurn;
|
|
117
120
|
this.prepareNextTurnWithContext = runtimeOptions.prepareNextTurnWithContext;
|
|
118
121
|
this.steeringQueue = new PendingMessageQueue(runtimeOptions.steeringMode ?? "one-at-a-time");
|
|
@@ -190,6 +193,11 @@ export class Agent {
|
|
|
190
193
|
hasQueuedMessages() {
|
|
191
194
|
return this.steeringQueue.hasItems() || this.followUpQueue.hasItems();
|
|
192
195
|
}
|
|
196
|
+
/** Preview the messages selected for the next turn without consuming them. */
|
|
197
|
+
peekQueuedMessages() {
|
|
198
|
+
const steering = this.steeringQueue.peek();
|
|
199
|
+
return steering.length > 0 ? steering : this.followUpQueue.peek();
|
|
200
|
+
}
|
|
193
201
|
/** Active abort signal for the current run, if any. */
|
|
194
202
|
get signal() {
|
|
195
203
|
return this.activeRun?.abortController.signal;
|
|
@@ -217,7 +225,10 @@ export class Agent {
|
|
|
217
225
|
waitForIdle() {
|
|
218
226
|
return this.activeRun?.promise ?? Promise.resolve();
|
|
219
227
|
}
|
|
220
|
-
/**
|
|
228
|
+
/**
|
|
229
|
+
* Clear transcript state, runtime state, and queued messages. Fork: the prompt/tool baseline is agent state
|
|
230
|
+
* (`systemPrompt`, `tools`), so transcript system messages are conversation content and are cleared too.
|
|
231
|
+
*/
|
|
221
232
|
reset() {
|
|
222
233
|
if (this.activeRun) {
|
|
223
234
|
throw new Error("Agent is already processing. Wait for completion before resetting.");
|
|
@@ -280,7 +291,7 @@ export class Agent {
|
|
|
280
291
|
throw new Error("Agent is already processing. Wait for completion before continuing.");
|
|
281
292
|
}
|
|
282
293
|
const lastMessage = this._state.messages[this._state.messages.length - 1];
|
|
283
|
-
if (!lastMessage) {
|
|
294
|
+
if (!lastMessage || this._state.messages.every((message) => message.role === "system")) {
|
|
284
295
|
throw new Error("No messages to continue from");
|
|
285
296
|
}
|
|
286
297
|
if (lastMessage.role === "assistant") {
|
|
@@ -344,7 +355,7 @@ export class Agent {
|
|
|
344
355
|
let skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;
|
|
345
356
|
let steeringQueueGeneration = this.steeringQueue.getClearGeneration();
|
|
346
357
|
let followUpQueueGeneration = this.followUpQueue.getClearGeneration();
|
|
347
|
-
const
|
|
358
|
+
const finishTurn = this.finishTurn;
|
|
348
359
|
const reasoning = this._state.reasoningBaseline ?? this._state.thinkingLevel;
|
|
349
360
|
return {
|
|
350
361
|
model: this._state.model,
|
|
@@ -353,6 +364,7 @@ export class Agent {
|
|
|
353
364
|
sessionId: this.sessionId,
|
|
354
365
|
onPayload: this.onPayload,
|
|
355
366
|
onResponse: this.onResponse,
|
|
367
|
+
onProviderStreamEvent: this.onProviderStreamEvent,
|
|
356
368
|
transport: this.transport,
|
|
357
369
|
thinkingBudgets: this.thinkingBudgets,
|
|
358
370
|
timeoutMs: this.timeoutMs,
|
|
@@ -367,9 +379,17 @@ export class Agent {
|
|
|
367
379
|
resolveUnknownToolCall: this.resolveUnknownToolCall,
|
|
368
380
|
beforeToolCall: this.beforeToolCall,
|
|
369
381
|
afterToolCall: this.afterToolCall,
|
|
370
|
-
|
|
371
|
-
|
|
382
|
+
// `{ action: "end" }` ends the run without polling queues, so the fork's post-run queue drain is
|
|
383
|
+
// suppressed for this run as well: queued input stays for its owner.
|
|
384
|
+
finishTurn: finishTurn
|
|
385
|
+
? async (turn, signal) => {
|
|
386
|
+
const decision = (await finishTurn(turn, signal)) ?? undefined;
|
|
387
|
+
if (decision?.action === "end")
|
|
388
|
+
this.suppressQueuedMessageDrain();
|
|
389
|
+
return decision;
|
|
390
|
+
}
|
|
372
391
|
: undefined,
|
|
392
|
+
prepareRequest: this.prepareRequest,
|
|
373
393
|
prepareNextTurn: this.prepareNextTurnWithContext || this.prepareNextTurn
|
|
374
394
|
? async (context) => {
|
|
375
395
|
if (this.prepareNextTurnWithContext) {
|
|
@@ -61,7 +61,7 @@ export declare function executeToolCall<TContext extends object | undefined>(cal
|
|
|
61
61
|
/** Apply an after-tool patch field by field. */
|
|
62
62
|
export declare function finalizeToolCall<TContext extends object | undefined>(call: ClearedToolCall<TContext>, executed: ExecutedToolCall, patch: AfterToolPatch | undefined): FinalizedToolCall;
|
|
63
63
|
/** Reconstruct the canonical tool result represented by a staged transcript message. */
|
|
64
|
-
export declare function toolResultFromMessage(message: ToolResultMessage
|
|
64
|
+
export declare function toolResultFromMessage(message: ToolResultMessage, terminate: boolean): AgentToolResult;
|
|
65
65
|
/** Convert finalized tool output to the provider-facing transcript message. */
|
|
66
66
|
export declare function createToolResultMessage(call: FinalizedToolCall): ToolResultMessage;
|
|
67
67
|
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -100,7 +100,6 @@ export function toolResultFromMessage(message, terminate) {
|
|
|
100
100
|
content: message.content,
|
|
101
101
|
details: message.details,
|
|
102
102
|
...(message.usage === undefined ? {} : { usage: message.usage }),
|
|
103
|
-
...(message.addedToolNames === undefined ? {} : { addedToolNames: message.addedToolNames }),
|
|
104
103
|
...(terminate ? { terminate: true } : {}),
|
|
105
104
|
};
|
|
106
105
|
}
|
|
@@ -113,7 +112,6 @@ export function createToolResultMessage(call) {
|
|
|
113
112
|
content: call.result.content ?? [],
|
|
114
113
|
...(call.result.details === undefined ? {} : { details: call.result.details }),
|
|
115
114
|
...(call.result.usage === undefined ? {} : { usage: call.result.usage }),
|
|
116
|
-
...(call.result.addedToolNames?.length ? { addedToolNames: call.result.addedToolNames } : {}),
|
|
117
115
|
isError: call.isError,
|
|
118
116
|
timestamp: Date.now(),
|
|
119
117
|
};
|
package/dist/harness/messages.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import type { ToolDeclaration } from "./types.ts";
|
|
3
|
+
declare const parameters: Type.TObject<{
|
|
4
|
+
command: Type.TString;
|
|
5
|
+
cwd: Type.TOptional<Type.TString>;
|
|
6
|
+
}>;
|
|
7
|
+
/** Run a shell command. Output is piped to the kernel; bounds come from `output`. */
|
|
8
|
+
export declare function bashTool(output?: ToolDeclaration["output"]): ToolDeclaration<typeof parameters>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=bash.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
const parameters = Type.Object({ command: Type.String(), cwd: Type.Optional(Type.String()) });
|
|
4
|
+
/** Run a shell command. Output is piped to the kernel; bounds come from `output`. */
|
|
5
|
+
export function bashTool(output = {}) {
|
|
6
|
+
return {
|
|
7
|
+
name: "bash",
|
|
8
|
+
description: "Run a shell command",
|
|
9
|
+
parameters,
|
|
10
|
+
replay: "unsafe",
|
|
11
|
+
output,
|
|
12
|
+
async execute({ command, cwd }, api, ctx) {
|
|
13
|
+
const started = Date.now();
|
|
14
|
+
const child = spawn("bash", ["-c", command], { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
15
|
+
const onAbort = () => child.kill("SIGKILL");
|
|
16
|
+
ctx.abortSignal?.addEventListener("abort", onAbort);
|
|
17
|
+
child.stdout.on("data", (c) => api.stream(c));
|
|
18
|
+
child.stderr.on("data", (c) => api.stream(c));
|
|
19
|
+
const { code, signal } = await new Promise((r) => child.on("close", (code, signal) => r({ code, signal })));
|
|
20
|
+
ctx.abortSignal?.removeEventListener("abort", onAbort);
|
|
21
|
+
return { isError: code !== 0, details: { exitCode: code, signal, ms: Date.now() - started } };
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=bash.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded byte collector. It retains a prefix or suffix constrained by both a byte
|
|
3
|
+
* budget and a newline budget, and accounts for every discarded byte/newline.
|
|
4
|
+
*/
|
|
5
|
+
export declare class Bounded {
|
|
6
|
+
private bytes;
|
|
7
|
+
private readonly maxBytes;
|
|
8
|
+
private readonly maxLines;
|
|
9
|
+
private readonly retain;
|
|
10
|
+
droppedBytes: number;
|
|
11
|
+
droppedLines: number;
|
|
12
|
+
total: number;
|
|
13
|
+
constructor(maxBytes: number, maxLines: number, retain: "head" | "tail");
|
|
14
|
+
push(chunk: Uint8Array): void;
|
|
15
|
+
private pushHead;
|
|
16
|
+
private pushTail;
|
|
17
|
+
private drop;
|
|
18
|
+
get dropped(): number;
|
|
19
|
+
text(): string;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=bounded.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded byte collector. It retains a prefix or suffix constrained by both a byte
|
|
3
|
+
* budget and a newline budget, and accounts for every discarded byte/newline.
|
|
4
|
+
*/
|
|
5
|
+
export class Bounded {
|
|
6
|
+
constructor(maxBytes, maxLines, retain) {
|
|
7
|
+
this.bytes = new Uint8Array();
|
|
8
|
+
this.droppedBytes = 0;
|
|
9
|
+
this.droppedLines = 0;
|
|
10
|
+
this.total = 0;
|
|
11
|
+
this.maxBytes = Math.max(0, maxBytes);
|
|
12
|
+
this.maxLines = Math.max(0, maxLines);
|
|
13
|
+
this.retain = retain;
|
|
14
|
+
}
|
|
15
|
+
push(chunk) {
|
|
16
|
+
this.total += chunk.length;
|
|
17
|
+
if (chunk.length === 0)
|
|
18
|
+
return;
|
|
19
|
+
if (this.maxBytes === 0 || this.maxLines === 0) {
|
|
20
|
+
this.drop(chunk);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (this.retain === "head") {
|
|
24
|
+
this.pushHead(chunk);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.pushTail(chunk);
|
|
28
|
+
}
|
|
29
|
+
pushHead(chunk) {
|
|
30
|
+
const remainingBytes = this.maxBytes - this.bytes.length;
|
|
31
|
+
const remainingLines = this.maxLines - countNewlines(this.bytes);
|
|
32
|
+
if (remainingBytes <= 0 || remainingLines <= 0) {
|
|
33
|
+
this.drop(chunk);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let take = Math.min(chunk.length, remainingBytes);
|
|
37
|
+
let lines = 0;
|
|
38
|
+
for (let i = 0; i < take; i++) {
|
|
39
|
+
if (chunk[i] !== 0x0a)
|
|
40
|
+
continue;
|
|
41
|
+
lines++;
|
|
42
|
+
if (lines === remainingLines) {
|
|
43
|
+
take = i + 1;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
this.bytes = concat(this.bytes, chunk.subarray(0, take));
|
|
48
|
+
this.drop(chunk.subarray(take));
|
|
49
|
+
}
|
|
50
|
+
pushTail(chunk) {
|
|
51
|
+
const incomingStart = tailStart(chunk, this.maxBytes, this.maxLines);
|
|
52
|
+
this.drop(chunk.subarray(0, incomingStart));
|
|
53
|
+
const combined = concat(this.bytes, chunk.subarray(incomingStart));
|
|
54
|
+
const start = tailStart(combined, this.maxBytes, this.maxLines);
|
|
55
|
+
this.drop(combined.subarray(0, start));
|
|
56
|
+
this.bytes = combined.slice(start);
|
|
57
|
+
}
|
|
58
|
+
drop(bytes) {
|
|
59
|
+
this.droppedBytes += bytes.length;
|
|
60
|
+
this.droppedLines += countNewlines(bytes);
|
|
61
|
+
}
|
|
62
|
+
get dropped() {
|
|
63
|
+
return this.droppedBytes;
|
|
64
|
+
}
|
|
65
|
+
text() {
|
|
66
|
+
return new TextDecoder().decode(this.bytes);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function tailStart(bytes, maxBytes, maxLines) {
|
|
70
|
+
let start = Math.max(0, bytes.length - maxBytes);
|
|
71
|
+
let excessLines = countNewlines(bytes.subarray(start)) - maxLines;
|
|
72
|
+
for (; start < bytes.length && excessLines > 0; start++) {
|
|
73
|
+
if (bytes[start] === 0x0a)
|
|
74
|
+
excessLines--;
|
|
75
|
+
}
|
|
76
|
+
return start;
|
|
77
|
+
}
|
|
78
|
+
function concat(a, b) {
|
|
79
|
+
if (a.length === 0)
|
|
80
|
+
return b.slice();
|
|
81
|
+
if (b.length === 0)
|
|
82
|
+
return a;
|
|
83
|
+
const out = new Uint8Array(a.length + b.length);
|
|
84
|
+
out.set(a);
|
|
85
|
+
out.set(b, a.length);
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
function countNewlines(bytes) {
|
|
89
|
+
let count = 0;
|
|
90
|
+
for (const byte of bytes)
|
|
91
|
+
if (byte === 0x0a)
|
|
92
|
+
count++;
|
|
93
|
+
return count;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=bounded.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Context, type MutableReplicatedState, type ReplicatedState } from "@earendil-works/chord";
|
|
2
|
+
import type { ConversationHandle, Harness } from "./harness.ts";
|
|
3
|
+
import type { ConversationSpec, ConversationView, Entry, EntryScan, Id, JsonObject, NewEntry, SendInput, ViewEvent } from "./types.ts";
|
|
4
|
+
export type PublishedConversationView = ConversationView & {
|
|
5
|
+
commit: {
|
|
6
|
+
events: ViewEvent[];
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export interface PicoConversationService {
|
|
10
|
+
readonly view: ReplicatedState<PublishedConversationView>;
|
|
11
|
+
send(input: SendInput, ctx: Context): Promise<Id>;
|
|
12
|
+
write(entry: NewEntry, ctx: Context): Promise<Id>;
|
|
13
|
+
inputAbort(id: Id, ctx: Context): Promise<"aborted" | "already_placed" | "not_found">;
|
|
14
|
+
configSet(patch: JsonObject, ctx: Context): Promise<void>;
|
|
15
|
+
abort(ctx: Context): Promise<void>;
|
|
16
|
+
reset(handoff: string | null, ctx: Context): Promise<void>;
|
|
17
|
+
collapse(instructions: string | null, ctx: Context): Promise<Id>;
|
|
18
|
+
fork(at: Id | "start", spec: Omit<ConversationSpec, "parent">, ctx: Context): Promise<Id>;
|
|
19
|
+
entries(scan: EntryScan, ctx: Context): Promise<Entry[]>;
|
|
20
|
+
}
|
|
21
|
+
/** Local registration/control plane and remote keyed conversation contract used by Chord facets. */
|
|
22
|
+
export declare const PicoHarnessService: import("@earendil-works/chord").Service<Harness<[]>>;
|
|
23
|
+
export declare const PicoConversationService: import("@earendil-works/chord").Service<PicoConversationService>;
|
|
24
|
+
export declare function createPicoConversationService<Cfg extends object>(harness: Pick<Harness, "abortInput" | "entries">, conversation: ConversationHandle<Cfg>, view: ReplicatedState<PublishedConversationView>): PicoConversationService;
|
|
25
|
+
export interface ChordViewBridge {
|
|
26
|
+
readonly view: MutableReplicatedState<PublishedConversationView>;
|
|
27
|
+
readonly closed: boolean;
|
|
28
|
+
close(): void;
|
|
29
|
+
}
|
|
30
|
+
export interface ChordViewBridgeOptions {
|
|
31
|
+
capacity?: number;
|
|
32
|
+
/** Called after the raw watch is closed. The owner should close and respawn its keyed service instance. */
|
|
33
|
+
onFailure?: (error: Error) => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Adapt Pico's commit-granular envelopes to one Chord publication per commit.
|
|
37
|
+
* The raw listener only enqueues. Applying ops and publishing happen together,
|
|
38
|
+
* off the Session line, with no await between them.
|
|
39
|
+
*/
|
|
40
|
+
export declare function attachChordView(conversation: Pick<ConversationHandle, "watch">, createState: (initial: PublishedConversationView) => MutableReplicatedState<PublishedConversationView>, ctx: Context, opts?: ChordViewBridgeOptions): Promise<ChordViewBridge>;
|
|
41
|
+
//# sourceMappingURL=chord.d.ts.map
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { defineService, } from "@earendil-works/chord";
|
|
2
|
+
import { WATCH_CAPACITY } from "./view.js";
|
|
3
|
+
/** Local registration/control plane and remote keyed conversation contract used by Chord facets. */
|
|
4
|
+
export const PicoHarnessService = defineService("pi.harness", { local: true });
|
|
5
|
+
export const PicoConversationService = defineService("pi.conversation");
|
|
6
|
+
export function createPicoConversationService(harness, conversation, view) {
|
|
7
|
+
return {
|
|
8
|
+
view,
|
|
9
|
+
send: (input, ctx) => conversation.send(input, ctx).then((handle) => handle.id),
|
|
10
|
+
write: (entry, ctx) => conversation.write(entry, ctx),
|
|
11
|
+
inputAbort: (id, ctx) => harness.abortInput(id, ctx, conversation.id),
|
|
12
|
+
configSet: (patch, ctx) => conversation.config.set(patch, ctx),
|
|
13
|
+
abort: (ctx) => conversation.abort(ctx),
|
|
14
|
+
reset: (handoff, ctx) => conversation.reset(handoff ?? undefined, ctx),
|
|
15
|
+
collapse: (instructions, ctx) => conversation.collapse(instructions ?? undefined, ctx),
|
|
16
|
+
fork: (at, spec, ctx) => conversation.fork(at, spec, ctx).then((child) => child.id),
|
|
17
|
+
entries: (scan, ctx) => harness.entries({ ...scan, conversationId: conversation.id }, ctx),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Adapt Pico's commit-granular envelopes to one Chord publication per commit.
|
|
22
|
+
* The raw listener only enqueues. Applying ops and publishing happen together,
|
|
23
|
+
* off the Session line, with no await between them.
|
|
24
|
+
*/
|
|
25
|
+
export async function attachChordView(conversation, createState, ctx, opts = {}) {
|
|
26
|
+
const watch = await conversation.watch(ctx);
|
|
27
|
+
const view = createState({ ...structuredClone(watch.view), commit: { events: [] } });
|
|
28
|
+
return bridgeWatch(watch, view, ctx, opts);
|
|
29
|
+
}
|
|
30
|
+
function bridgeWatch(watch, view, ctx, opts) {
|
|
31
|
+
const capacity = opts.capacity ?? WATCH_CAPACITY;
|
|
32
|
+
if (!Number.isSafeInteger(capacity) || capacity < 1)
|
|
33
|
+
throw new RangeError("Chord view queue capacity must be positive");
|
|
34
|
+
const queue = [];
|
|
35
|
+
let scheduled = false;
|
|
36
|
+
let closed = false;
|
|
37
|
+
const fail = (cause) => {
|
|
38
|
+
if (closed)
|
|
39
|
+
return;
|
|
40
|
+
closed = true;
|
|
41
|
+
queue.length = 0;
|
|
42
|
+
watch.stop();
|
|
43
|
+
try {
|
|
44
|
+
opts.onFailure?.(cause instanceof Error ? cause : new Error(String(cause)));
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
};
|
|
48
|
+
const drain = () => {
|
|
49
|
+
scheduled = false;
|
|
50
|
+
while (!closed && queue.length > 0) {
|
|
51
|
+
const envelope = queue.shift();
|
|
52
|
+
try {
|
|
53
|
+
view.change(ctx, (draft) => {
|
|
54
|
+
applyTracked(draft, envelope.ops);
|
|
55
|
+
draft.commit.events = envelope.events;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
fail(error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
watch.start((envelope) => {
|
|
64
|
+
if (closed)
|
|
65
|
+
return;
|
|
66
|
+
if (queue.length >= capacity) {
|
|
67
|
+
fail(new Error(`Pico-to-Chord view queue exceeded ${capacity} envelopes`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
queue.push(envelope);
|
|
71
|
+
if (scheduled)
|
|
72
|
+
return;
|
|
73
|
+
scheduled = true;
|
|
74
|
+
queueMicrotask(drain);
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
view,
|
|
78
|
+
get closed() {
|
|
79
|
+
return closed;
|
|
80
|
+
},
|
|
81
|
+
close() {
|
|
82
|
+
if (closed)
|
|
83
|
+
return;
|
|
84
|
+
closed = true;
|
|
85
|
+
queue.length = 0;
|
|
86
|
+
watch.stop();
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function applyTracked(root, ops) {
|
|
91
|
+
for (const op of ops) {
|
|
92
|
+
if (op[0] === "r")
|
|
93
|
+
throw new Error("live Pico envelope unexpectedly replaced the view root");
|
|
94
|
+
const path = op[1];
|
|
95
|
+
if (op[0] === "p") {
|
|
96
|
+
const target = resolve(root, path);
|
|
97
|
+
if (!Array.isArray(target))
|
|
98
|
+
throw new Error(`Pico splice path is not an array: ${path.join(".")}`);
|
|
99
|
+
target.splice(op[2], op[3], ...op[4]);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (op[0] === "m") {
|
|
103
|
+
const target = resolve(root, path);
|
|
104
|
+
if (!Array.isArray(target) || target.length !== op[2].length) {
|
|
105
|
+
throw new Error(`Pico permutation path is not a matching array: ${path.join(".")}`);
|
|
106
|
+
}
|
|
107
|
+
const array = target;
|
|
108
|
+
const previous = array.slice();
|
|
109
|
+
const rank = new Map();
|
|
110
|
+
for (let index = 0; index < op[2].length; index++) {
|
|
111
|
+
const value = previous[op[2][index]];
|
|
112
|
+
if (!rank.has(value))
|
|
113
|
+
rank.set(value, index);
|
|
114
|
+
}
|
|
115
|
+
array.sort((left, right) => rank.get(left) - rank.get(right));
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const parent = resolve(root, path.slice(0, -1));
|
|
119
|
+
if (parent === null || typeof parent !== "object")
|
|
120
|
+
throw new Error(`Pico operation parent is not an object: ${path.join(".")}`);
|
|
121
|
+
const key = path[path.length - 1];
|
|
122
|
+
if (op[0] === "d") {
|
|
123
|
+
if (Array.isArray(parent))
|
|
124
|
+
parent.splice(key, 1);
|
|
125
|
+
else
|
|
126
|
+
delete parent[key];
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const record = parent;
|
|
130
|
+
if (op[0] === "s")
|
|
131
|
+
record[key] = op[2];
|
|
132
|
+
else if (op[0] === "a")
|
|
133
|
+
record[key] = `${String(record[key])}${op[2]}`;
|
|
134
|
+
else
|
|
135
|
+
record[key] = String(record[key]).slice(op[2]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function resolve(root, path) {
|
|
139
|
+
let value = root;
|
|
140
|
+
for (const segment of path) {
|
|
141
|
+
if (value === null || typeof value !== "object")
|
|
142
|
+
return undefined;
|
|
143
|
+
value = value[segment];
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=chord.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from "@earendil-works/chord";
|
|
2
|
+
import type { ContextView, Id, Storage } from "./types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* pico §1.3, verbatim:
|
|
5
|
+
* H = newest fork-visible entry at or before T with a head
|
|
6
|
+
* from = H ? H.head : transcript start
|
|
7
|
+
* range = fork-visible entries from `from` through T
|
|
8
|
+
* edits = per target, newest edit in range wins
|
|
9
|
+
* entries = H ? [H, ...range without any head entries] : range
|
|
10
|
+
* model = concat(entries.map(e => edits[e.id] ? apply : e.model)), then reorder tool results
|
|
11
|
+
*
|
|
12
|
+
* Display-only entries (aborted/error assistants, pi.usage, model-less plugin entries) have no
|
|
13
|
+
* `model` and contribute nothing. Nothing here inspects an error string.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deriveContext(storage: Storage, conversationId: Id, at: Id | undefined, ctx: Context): Promise<ContextView>;
|
|
16
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pico §1.3, verbatim:
|
|
3
|
+
* H = newest fork-visible entry at or before T with a head
|
|
4
|
+
* from = H ? H.head : transcript start
|
|
5
|
+
* range = fork-visible entries from `from` through T
|
|
6
|
+
* edits = per target, newest edit in range wins
|
|
7
|
+
* entries = H ? [H, ...range without any head entries] : range
|
|
8
|
+
* model = concat(entries.map(e => edits[e.id] ? apply : e.model)), then reorder tool results
|
|
9
|
+
*
|
|
10
|
+
* Display-only entries (aborted/error assistants, pi.usage, model-less plugin entries) have no
|
|
11
|
+
* `model` and contribute nothing. Nothing here inspects an error string.
|
|
12
|
+
*/
|
|
13
|
+
export async function deriveContext(storage, conversationId, at, ctx) {
|
|
14
|
+
const [head] = await storage.scanEntries({ conversationId, withHead: true, ...(at === undefined ? {} : { before: at + 1 }), limit: 1 }, ctx);
|
|
15
|
+
const from = head?.head;
|
|
16
|
+
// Walk newest-first until we pass `from`.
|
|
17
|
+
const range = [];
|
|
18
|
+
let before = at === undefined ? undefined : at + 1;
|
|
19
|
+
for (;;) {
|
|
20
|
+
const page = await storage.scanEntries({ conversationId, ...(before === undefined ? {} : { before }), limit: 256 }, ctx);
|
|
21
|
+
let done = page.length < 256;
|
|
22
|
+
for (const e of page) {
|
|
23
|
+
if (from !== undefined && e.id < from) {
|
|
24
|
+
done = true;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
range.push(e);
|
|
28
|
+
}
|
|
29
|
+
if (done)
|
|
30
|
+
break;
|
|
31
|
+
before = page[page.length - 1].id;
|
|
32
|
+
}
|
|
33
|
+
range.reverse();
|
|
34
|
+
const edits = new Map();
|
|
35
|
+
for (const e of range)
|
|
36
|
+
for (const edit of e.edits ?? [])
|
|
37
|
+
edits.set(edit.target, edit); // later wins by iteration order
|
|
38
|
+
const entries = head === undefined ? range : [head, ...range.filter((e) => e.head === undefined)];
|
|
39
|
+
const messages = [];
|
|
40
|
+
for (const e of entries) {
|
|
41
|
+
const edit = edits.get(e.id);
|
|
42
|
+
if (edit?.action === "omit")
|
|
43
|
+
continue;
|
|
44
|
+
if (edit?.action === "replace") {
|
|
45
|
+
messages.push(...(edit.messages ?? []));
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (e.model)
|
|
49
|
+
messages.push(...e.model);
|
|
50
|
+
}
|
|
51
|
+
return { head, entries, messages: reorderToolResults(messages) };
|
|
52
|
+
}
|
|
53
|
+
function reorderToolResults(messages) {
|
|
54
|
+
const out = [];
|
|
55
|
+
for (let i = 0; i < messages.length; i++) {
|
|
56
|
+
const m = messages[i];
|
|
57
|
+
out.push(m);
|
|
58
|
+
if (m.role !== "assistant")
|
|
59
|
+
continue;
|
|
60
|
+
const calls = m.content.filter((c) => c.type === "toolCall");
|
|
61
|
+
if (calls.length === 0)
|
|
62
|
+
continue;
|
|
63
|
+
const results = new Map();
|
|
64
|
+
let j = i + 1;
|
|
65
|
+
while (j < messages.length && messages[j].role === "toolResult") {
|
|
66
|
+
const r = messages[j];
|
|
67
|
+
results.set(r.toolCallId, r);
|
|
68
|
+
j++;
|
|
69
|
+
}
|
|
70
|
+
for (const call of calls) {
|
|
71
|
+
out.push(results.get(call.id) ??
|
|
72
|
+
{
|
|
73
|
+
role: "toolResult",
|
|
74
|
+
toolCallId: call.id,
|
|
75
|
+
toolName: call.name,
|
|
76
|
+
content: [
|
|
77
|
+
{ type: "text", text: "Tool result unavailable: history ends before this call completed." },
|
|
78
|
+
],
|
|
79
|
+
isError: true,
|
|
80
|
+
details: { reason: "missing_after_fork" },
|
|
81
|
+
timestamp: m.timestamp,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
i = j - 1;
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=context.js.map
|