@code-yeongyu/senpi-agent-core 2026.8.12 → 2026.8.13
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 +13 -7
- package/dist/agent-loop.js +5 -1
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +3 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +8 -0
- package/dist/agent.js.map +1 -1
- package/dist/harness/agent-harness.d.ts +452 -96
- package/dist/harness/agent-harness.d.ts.map +1 -1
- package/dist/harness/agent-harness.js +162 -1005
- package/dist/harness/agent-harness.js.map +1 -1
- package/dist/harness/compaction/branch-summarization.d.ts +13 -6
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/harness/compaction/branch-summarization.js +10 -14
- package/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/dist/harness/compaction/compaction.d.ts +14 -17
- package/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/dist/harness/compaction/compaction.js +23 -36
- package/dist/harness/compaction/compaction.js.map +1 -1
- package/dist/harness/env/nodejs.d.ts +1 -0
- package/dist/harness/env/nodejs.d.ts.map +1 -1
- package/dist/harness/env/nodejs.js +23 -7
- package/dist/harness/env/nodejs.js.map +1 -1
- package/dist/harness/messages.d.ts +3 -3
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js +3 -3
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/prompt-templates.d.ts.map +1 -1
- package/dist/harness/prompt-templates.js +4 -9
- package/dist/harness/prompt-templates.js.map +1 -1
- package/dist/harness/reducer.d.ts +100 -0
- package/dist/harness/reducer.d.ts.map +1 -0
- package/dist/harness/reducer.js +414 -0
- package/dist/harness/reducer.js.map +1 -0
- package/dist/harness/result.d.ts +40 -0
- package/dist/harness/result.d.ts.map +1 -0
- package/dist/harness/result.js +41 -0
- package/dist/harness/result.js.map +1 -0
- package/dist/harness/session/context.d.ts +22 -0
- package/dist/harness/session/context.d.ts.map +1 -0
- package/dist/harness/session/context.js +67 -0
- package/dist/harness/session/context.js.map +1 -0
- package/dist/harness/session/index.d.ts +7 -0
- package/dist/harness/session/index.d.ts.map +1 -0
- package/dist/harness/session/index.js +6 -0
- package/dist/harness/session/index.js.map +1 -0
- package/dist/harness/session/jsonl/codec.d.ts +8 -0
- package/dist/harness/session/jsonl/codec.d.ts.map +1 -0
- package/dist/harness/session/jsonl/codec.js +190 -0
- package/dist/harness/session/jsonl/codec.js.map +1 -0
- package/dist/harness/session/jsonl/errors.d.ts +5 -0
- package/dist/harness/session/jsonl/errors.d.ts.map +1 -0
- package/dist/harness/session/jsonl/errors.js +11 -0
- package/dist/harness/session/jsonl/errors.js.map +1 -0
- package/dist/harness/session/jsonl/repo.d.ts +23 -0
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -0
- package/dist/harness/session/jsonl/repo.js +129 -0
- package/dist/harness/session/jsonl/repo.js.map +1 -0
- package/dist/harness/session/jsonl/storage.d.ts +43 -0
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -0
- package/dist/harness/session/jsonl/storage.js +206 -0
- package/dist/harness/session/jsonl/storage.js.map +1 -0
- package/dist/harness/session/jsonl/types.d.ts +38 -0
- package/dist/harness/session/jsonl/types.d.ts.map +1 -0
- package/dist/harness/session/jsonl/types.js +2 -0
- package/dist/harness/session/jsonl/types.js.map +1 -0
- package/dist/harness/session/jsonl.d.ts +3 -0
- package/dist/harness/session/jsonl.d.ts.map +1 -0
- package/dist/harness/session/jsonl.js +2 -0
- package/dist/harness/session/jsonl.js.map +1 -0
- package/dist/harness/session/memory.d.ts +44 -0
- package/dist/harness/session/memory.d.ts.map +1 -0
- package/dist/harness/session/memory.js +142 -0
- package/dist/harness/session/memory.js.map +1 -0
- package/dist/harness/session/search.d.ts +19 -0
- package/dist/harness/session/search.d.ts.map +1 -0
- package/dist/harness/session/search.js +41 -0
- package/dist/harness/session/search.js.map +1 -0
- package/dist/harness/session/session.d.ts +43 -38
- package/dist/harness/session/session.d.ts.map +1 -1
- package/dist/harness/session/session.js +186 -304
- package/dist/harness/session/session.js.map +1 -1
- package/dist/harness/session/state.d.ts +65 -0
- package/dist/harness/session/state.d.ts.map +1 -0
- package/dist/harness/session/state.js +320 -0
- package/dist/harness/session/state.js.map +1 -0
- package/dist/harness/session/testing/conformance.d.ts +4 -0
- package/dist/harness/session/testing/conformance.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance.js +743 -0
- package/dist/harness/session/testing/conformance.js.map +1 -0
- package/dist/harness/session/testing/index.d.ts +3 -0
- package/dist/harness/session/testing/index.d.ts.map +1 -0
- package/dist/harness/session/testing/index.js +2 -0
- package/dist/harness/session/testing/index.js.map +1 -0
- package/dist/harness/session/testing/types.d.ts +14 -0
- package/dist/harness/session/testing/types.d.ts.map +1 -0
- package/dist/harness/session/testing/types.js +2 -0
- package/dist/harness/session/testing/types.js.map +1 -0
- package/dist/harness/session/types.d.ts +333 -0
- package/dist/harness/session/types.d.ts.map +1 -0
- package/dist/harness/session/types.js +9 -0
- package/dist/harness/session/types.js.map +1 -0
- package/dist/harness/skills.js +21 -19
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/telemetry.d.ts +1221 -0
- package/dist/harness/telemetry.d.ts.map +1 -0
- package/dist/harness/telemetry.js +508 -0
- package/dist/harness/telemetry.js.map +1 -0
- package/dist/harness/types.d.ts +8 -485
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +4 -16
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/utils/truncate.js.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +1 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +1 -0
- package/dist/proxy.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -2
- package/dist/harness/session/array-session-reader.d.ts +0 -3
- package/dist/harness/session/array-session-reader.d.ts.map +0 -1
- package/dist/harness/session/array-session-reader.js +0 -70
- package/dist/harness/session/array-session-reader.js.map +0 -1
- package/dist/harness/session/fork.d.ts +0 -4
- package/dist/harness/session/fork.d.ts.map +0 -1
- package/dist/harness/session/fork.js +0 -22
- package/dist/harness/session/fork.js.map +0 -1
- package/dist/harness/session/jsonl-store.d.ts +0 -13
- package/dist/harness/session/jsonl-store.d.ts.map +0 -1
- package/dist/harness/session/jsonl-store.js +0 -304
- package/dist/harness/session/jsonl-store.js.map +0 -1
- package/dist/harness/session/keyed-operation-queue.d.ts +0 -17
- package/dist/harness/session/keyed-operation-queue.d.ts.map +0 -1
- package/dist/harness/session/keyed-operation-queue.js +0 -60
- package/dist/harness/session/keyed-operation-queue.js.map +0 -1
- package/dist/harness/session/memory-store.d.ts +0 -6
- package/dist/harness/session/memory-store.d.ts.map +0 -1
- package/dist/harness/session/memory-store.js +0 -107
- package/dist/harness/session/memory-store.js.map +0 -1
- package/dist/harness/session/repository.d.ts +0 -28
- package/dist/harness/session/repository.d.ts.map +0 -1
- package/dist/harness/session/repository.js +0 -61
- package/dist/harness/session/repository.js.map +0 -1
- package/dist/harness/session/search-backend.d.ts +0 -8
- package/dist/harness/session/search-backend.d.ts.map +0 -1
- package/dist/harness/session/search-backend.js +0 -22
- package/dist/harness/session/search-backend.js.map +0 -1
|
@@ -1,1077 +1,234 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { collectEntriesForBranchSummary, generateBranchSummary } from "./compaction/branch-summarization.js";
|
|
4
|
-
import { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction } from "./compaction/compaction.js";
|
|
5
|
-
import { convertToLlm } from "./messages.js";
|
|
6
|
-
import { formatPromptTemplateInvocation } from "./prompt-templates.js";
|
|
7
|
-
import { formatSkillInvocation } from "./skills.js";
|
|
8
|
-
import { AgentHarnessError, BranchSummaryError, CompactionError, SessionError, toError } from "./types.js";
|
|
9
|
-
function createUserMessage(text, images) {
|
|
10
|
-
const content = [{ type: "text", text }];
|
|
11
|
-
if (images)
|
|
12
|
-
content.push(...images);
|
|
13
|
-
return { role: "user", content, timestamp: Date.now() };
|
|
1
|
+
import { TaggedError } from "./result.js";
|
|
2
|
+
export class LaneBusy extends TaggedError("LaneBusy") {
|
|
14
3
|
}
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
role: "assistant",
|
|
18
|
-
content: [{ type: "text", text: "" }],
|
|
19
|
-
api: model.api,
|
|
20
|
-
provider: model.provider,
|
|
21
|
-
model: model.id,
|
|
22
|
-
stopReason: aborted ? "aborted" : "error",
|
|
23
|
-
errorMessage: error instanceof Error ? error.message : String(error),
|
|
24
|
-
timestamp: Date.now(),
|
|
25
|
-
usage: {
|
|
26
|
-
input: 0,
|
|
27
|
-
output: 0,
|
|
28
|
-
cacheRead: 0,
|
|
29
|
-
cacheWrite: 0,
|
|
30
|
-
totalTokens: 0,
|
|
31
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
32
|
-
},
|
|
33
|
-
};
|
|
4
|
+
export class MissingIdentities extends TaggedError("MissingIdentities") {
|
|
34
5
|
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
...streamOptions,
|
|
38
|
-
headers: streamOptions?.headers ? { ...streamOptions.headers } : undefined,
|
|
39
|
-
metadata: streamOptions?.metadata ? { ...streamOptions.metadata } : undefined,
|
|
40
|
-
};
|
|
6
|
+
export class NoActiveRun extends TaggedError("NoActiveRun") {
|
|
41
7
|
}
|
|
42
|
-
|
|
43
|
-
const merged = {};
|
|
44
|
-
let hasHeaders = false;
|
|
45
|
-
for (const entry of headers) {
|
|
46
|
-
if (!entry)
|
|
47
|
-
continue;
|
|
48
|
-
Object.assign(merged, entry);
|
|
49
|
-
hasHeaders = true;
|
|
50
|
-
}
|
|
51
|
-
return hasHeaders ? merged : undefined;
|
|
8
|
+
export class NoActiveOperation extends TaggedError("NoActiveOperation") {
|
|
52
9
|
}
|
|
53
|
-
|
|
54
|
-
function hasOwn(object, key) {
|
|
55
|
-
return objectHasOwnProperty.call(object, key);
|
|
10
|
+
export class NothingToResume extends TaggedError("NothingToResume") {
|
|
56
11
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
12
|
+
export class InvalidMessage extends TaggedError("InvalidMessage") {
|
|
13
|
+
}
|
|
14
|
+
export class UnknownSkill extends TaggedError("UnknownSkill") {
|
|
15
|
+
}
|
|
16
|
+
export class UnknownTemplate extends TaggedError("UnknownTemplate") {
|
|
17
|
+
}
|
|
18
|
+
export class UnknownTarget extends TaggedError("UnknownTarget") {
|
|
19
|
+
}
|
|
20
|
+
export class UnknownQueueItem extends TaggedError("UnknownQueueItem") {
|
|
21
|
+
}
|
|
22
|
+
export class LaneExists extends TaggedError("LaneExists") {
|
|
66
23
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
result.maxRetryDelayMs = patch.maxRetryDelayMs;
|
|
79
|
-
if (hasOwn(patch, "cacheRetention"))
|
|
80
|
-
result.cacheRetention = patch.cacheRetention;
|
|
81
|
-
if (hasOwn(patch, "headers")) {
|
|
82
|
-
if (patch.headers === undefined) {
|
|
83
|
-
result.headers = undefined;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
const headers = { ...(result.headers ?? {}) };
|
|
87
|
-
for (const [key, value] of Object.entries(patch.headers)) {
|
|
88
|
-
if (value === undefined)
|
|
89
|
-
delete headers[key];
|
|
90
|
-
else
|
|
91
|
-
headers[key] = value;
|
|
92
|
-
}
|
|
93
|
-
result.headers = Object.keys(headers).length > 0 ? headers : undefined;
|
|
94
|
-
}
|
|
24
|
+
export class InvalidLane extends TaggedError("InvalidLane") {
|
|
25
|
+
}
|
|
26
|
+
export class NothingToCompact extends TaggedError("NothingToCompact") {
|
|
27
|
+
}
|
|
28
|
+
export class Closed extends TaggedError("Closed") {
|
|
29
|
+
}
|
|
30
|
+
export class HarnessFault extends Error {
|
|
31
|
+
constructor(message, cause) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "HarnessFault";
|
|
34
|
+
this.cause = cause;
|
|
95
35
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const metadata = { ...(result.metadata ?? {}) };
|
|
102
|
-
for (const [key, value] of Object.entries(patch.metadata)) {
|
|
103
|
-
if (value === undefined)
|
|
104
|
-
delete metadata[key];
|
|
105
|
-
else
|
|
106
|
-
metadata[key] = value;
|
|
107
|
-
}
|
|
108
|
-
result.metadata = Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
109
|
-
}
|
|
36
|
+
}
|
|
37
|
+
export class HarnessClosed extends Error {
|
|
38
|
+
constructor() {
|
|
39
|
+
super("AgentHarness was closed while the operation was active");
|
|
40
|
+
this.name = "HarnessClosed";
|
|
110
41
|
}
|
|
111
|
-
return result;
|
|
112
42
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return new AgentHarnessError("session", cause.message, cause);
|
|
120
|
-
if (cause instanceof CompactionError)
|
|
121
|
-
return new AgentHarnessError("compaction", cause.message, cause);
|
|
122
|
-
if (cause instanceof BranchSummaryError)
|
|
123
|
-
return new AgentHarnessError("branch_summary", cause.message, cause);
|
|
124
|
-
return new AgentHarnessError(fallbackCode, cause.message, cause);
|
|
43
|
+
export class HarnessNotImplemented extends Error {
|
|
44
|
+
constructor(operation) {
|
|
45
|
+
super(`AgentHarness.${operation} is not implemented yet`);
|
|
46
|
+
this.name = "HarnessNotImplemented";
|
|
47
|
+
this.operation = operation;
|
|
48
|
+
}
|
|
125
49
|
}
|
|
126
|
-
|
|
127
|
-
|
|
50
|
+
class UnavailableRegistry {
|
|
51
|
+
constructor(operation, isClosed) {
|
|
52
|
+
this.operation = operation;
|
|
53
|
+
this.isClosed = isClosed;
|
|
54
|
+
}
|
|
55
|
+
on(_name, _handler, _options) {
|
|
56
|
+
throw this.isClosed() ? new HarnessClosed() : new HarnessNotImplemented(this.operation);
|
|
57
|
+
}
|
|
128
58
|
}
|
|
129
59
|
export class AgentHarness {
|
|
130
60
|
constructor(options) {
|
|
131
|
-
this.
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
134
|
-
this.pendingSessionWrites = [];
|
|
135
|
-
this.tools = new Map();
|
|
136
|
-
this.steerQueue = [];
|
|
137
|
-
this.followUpQueue = [];
|
|
138
|
-
this.nextTurnQueue = [];
|
|
139
|
-
this.handlers = new Map();
|
|
61
|
+
this.name = "main";
|
|
62
|
+
this.closed = false;
|
|
63
|
+
this.durableSession = options.session;
|
|
140
64
|
this.session = options.session;
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
143
|
-
this.streamOptions = cloneStreamOptions(options.streamOptions);
|
|
144
|
-
this.retry = options.retry;
|
|
145
|
-
this.systemPrompt = options.systemPrompt;
|
|
146
|
-
this.toolContext = options.toolContext;
|
|
147
|
-
this.validateUniqueNames((options.tools ?? []).map((tool) => tool.name), "Duplicate tool name(s)");
|
|
148
|
-
for (const tool of options.tools ?? []) {
|
|
149
|
-
this.tools.set(tool.name, tool);
|
|
150
|
-
}
|
|
65
|
+
this.hooks = new UnavailableRegistry("hooks.on", () => this.closed);
|
|
66
|
+
this.events = new UnavailableRegistry("events.on", () => this.closed);
|
|
151
67
|
this.model = options.model;
|
|
152
68
|
this.thinkingLevel = options.thinkingLevel ?? "off";
|
|
153
|
-
this.activeToolNames = options.activeToolNames
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
this.steeringQueueMode = options.steeringMode ?? "one-at-a-time";
|
|
159
|
-
this.followUpQueueMode = options.followUpMode ?? "one-at-a-time";
|
|
160
|
-
}
|
|
161
|
-
assertNotShutDown() {
|
|
162
|
-
if (this.isShutdown)
|
|
163
|
-
throw new AgentHarnessError("invalid_state", "AgentHarness has been shut down");
|
|
164
|
-
}
|
|
165
|
-
getHandlers(type) {
|
|
166
|
-
return this.handlers.get(type);
|
|
167
|
-
}
|
|
168
|
-
async emitOwn(event, signal) {
|
|
169
|
-
for (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {
|
|
170
|
-
try {
|
|
171
|
-
await listener(event, signal);
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
throw normalizeHookError(error);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
async emitAny(event, signal) {
|
|
179
|
-
for (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {
|
|
180
|
-
try {
|
|
181
|
-
await listener(event, signal);
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
throw normalizeHookError(error);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
async emitHook(event) {
|
|
189
|
-
const handlers = this.getHandlers(event.type);
|
|
190
|
-
if (!handlers || handlers.size === 0)
|
|
191
|
-
return undefined;
|
|
192
|
-
let lastResult;
|
|
193
|
-
for (const handler of handlers) {
|
|
194
|
-
try {
|
|
195
|
-
const result = await handler(event);
|
|
196
|
-
if (result !== undefined) {
|
|
197
|
-
lastResult = result;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
throw normalizeHookError(error);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return lastResult;
|
|
205
|
-
}
|
|
206
|
-
retryCallbacks(operation) {
|
|
207
|
-
return {
|
|
208
|
-
onRetryScheduled: (attempt, maxAttempts, delayMs, errorMessage) => this.emitOwn({ type: "retry_scheduled", operation, attempt, maxAttempts, delayMs, errorMessage }),
|
|
209
|
-
onRetryAttemptStart: () => this.emitOwn({ type: "retry_attempt_start", operation }),
|
|
210
|
-
onRetryFinished: () => this.emitOwn({ type: "retry_finished", operation }),
|
|
69
|
+
this.activeToolNames = [...(options.activeToolNames ?? options.tools?.map((tool) => tool.name) ?? [])];
|
|
70
|
+
this.tools = [...(options.tools ?? [])];
|
|
71
|
+
this.resources = {
|
|
72
|
+
skills: options.resources?.skills ? [...options.resources.skills] : undefined,
|
|
73
|
+
promptTemplates: options.resources?.promptTemplates ? [...options.resources.promptTemplates] : undefined,
|
|
211
74
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
for (const handler of handlers) {
|
|
219
|
-
try {
|
|
220
|
-
const result = await handler({
|
|
221
|
-
type: "before_provider_request",
|
|
222
|
-
model,
|
|
223
|
-
sessionId,
|
|
224
|
-
streamOptions: cloneStreamOptions(current),
|
|
225
|
-
});
|
|
226
|
-
if (result?.streamOptions) {
|
|
227
|
-
current = applyStreamOptionsPatch(current, result.streamOptions);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
throw normalizeHookError(error);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return current;
|
|
235
|
-
}
|
|
236
|
-
async emitBeforeProviderPayload(model, payload) {
|
|
237
|
-
const handlers = this.getHandlers("before_provider_payload");
|
|
238
|
-
let current = payload;
|
|
239
|
-
if (!handlers || handlers.size === 0)
|
|
240
|
-
return current;
|
|
241
|
-
for (const handler of handlers) {
|
|
242
|
-
try {
|
|
243
|
-
const result = await handler({ type: "before_provider_payload", model, payload: current });
|
|
244
|
-
if (result !== undefined) {
|
|
245
|
-
current = result.payload;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
throw normalizeHookError(error);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return current;
|
|
253
|
-
}
|
|
254
|
-
async emitQueueUpdate() {
|
|
255
|
-
await this.emitOwn({
|
|
256
|
-
type: "queue_update",
|
|
257
|
-
steer: [...this.steerQueue],
|
|
258
|
-
followUp: [...this.followUpQueue],
|
|
259
|
-
nextTurn: [...this.nextTurnQueue],
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
startOperation() {
|
|
263
|
-
const abortController = new AbortController();
|
|
264
|
-
let finish = () => { };
|
|
265
|
-
this.activeAbortController = abortController;
|
|
266
|
-
void this.track("operation", () => new Promise((resolve) => {
|
|
267
|
-
finish = resolve;
|
|
268
|
-
}));
|
|
269
|
-
return {
|
|
270
|
-
signal: abortController.signal,
|
|
271
|
-
finish: () => {
|
|
272
|
-
this.activeAbortController = undefined;
|
|
273
|
-
finish();
|
|
274
|
-
},
|
|
75
|
+
this.streamOptions = { ...(options.streamOptions ?? {}) };
|
|
76
|
+
this.retryPolicy = options.retry ?? { enabled: false, maxRetries: 0, baseDelayMs: 1000 };
|
|
77
|
+
this.compactionSettings = options.compaction ?? {
|
|
78
|
+
enabled: true,
|
|
79
|
+
reserveTokens: 16384,
|
|
80
|
+
keepRecentTokens: 20000,
|
|
275
81
|
};
|
|
82
|
+
this.steeringMode = options.steeringMode ?? "one-at-a-time";
|
|
83
|
+
this.followUpMode = options.followUpMode ?? "one-at-a-time";
|
|
276
84
|
}
|
|
277
|
-
async
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
this.activeTasks.set(settled, kind);
|
|
283
|
-
try {
|
|
284
|
-
return await operation();
|
|
285
|
-
}
|
|
286
|
-
finally {
|
|
287
|
-
this.activeTasks.delete(settled);
|
|
288
|
-
settle();
|
|
289
|
-
}
|
|
85
|
+
static async create(options) {
|
|
86
|
+
const [record] = await options.session.findRecords({ limit: 1 });
|
|
87
|
+
if (record !== undefined)
|
|
88
|
+
throw new HarnessNotImplemented("create.restore");
|
|
89
|
+
return { harness: new AgentHarness(options), suspended: [] };
|
|
290
90
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const tasks = [...this.activeTasks].flatMap(([task, taskKind]) => kind === undefined || kind === taskKind ? [task] : []);
|
|
294
|
-
if (tasks.length === 0)
|
|
295
|
-
return;
|
|
296
|
-
await Promise.all(tasks);
|
|
297
|
-
}
|
|
91
|
+
unavailable(operation) {
|
|
92
|
+
return Promise.reject(this.closed ? new HarnessClosed() : new HarnessNotImplemented(operation));
|
|
298
93
|
}
|
|
299
|
-
async
|
|
300
|
-
|
|
301
|
-
return await this.toolContext();
|
|
302
|
-
}
|
|
303
|
-
return this.toolContext;
|
|
94
|
+
async getLeafId() {
|
|
95
|
+
return this.durableSession.getLeafId();
|
|
304
96
|
}
|
|
305
|
-
|
|
306
|
-
return
|
|
307
|
-
...tool,
|
|
308
|
-
execute: (toolCallId, params, signal, onUpdate) => tool.execute(toolCallId, params, signal, onUpdate, context),
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
async createTurnState() {
|
|
312
|
-
this.assertNotShutDown();
|
|
313
|
-
const context = await this.session.buildContext();
|
|
314
|
-
const resources = this.getResources();
|
|
315
|
-
const sessionMetadata = await this.session.getMetadata();
|
|
316
|
-
const toolContext = await this.resolveToolContext();
|
|
317
|
-
const tools = [...this.tools.values()];
|
|
318
|
-
const activeTools = this.activeToolNames
|
|
319
|
-
.map((name) => this.tools.get(name))
|
|
320
|
-
.filter((tool) => tool !== undefined);
|
|
321
|
-
let systemPrompt = "You are a helpful assistant.";
|
|
322
|
-
if (typeof this.systemPrompt === "string") {
|
|
323
|
-
systemPrompt = this.systemPrompt;
|
|
324
|
-
}
|
|
325
|
-
else if (this.systemPrompt) {
|
|
326
|
-
systemPrompt = await this.systemPrompt({
|
|
327
|
-
session: this.session,
|
|
328
|
-
model: this.model,
|
|
329
|
-
thinkingLevel: this.thinkingLevel,
|
|
330
|
-
activeTools,
|
|
331
|
-
resources,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
return {
|
|
335
|
-
messages: context.messages,
|
|
336
|
-
resources,
|
|
337
|
-
toolContext,
|
|
338
|
-
streamOptions: cloneStreamOptions(this.streamOptions),
|
|
339
|
-
sessionId: sessionMetadata.id,
|
|
340
|
-
systemPrompt,
|
|
341
|
-
model: this.model,
|
|
342
|
-
thinkingLevel: this.thinkingLevel,
|
|
343
|
-
tools,
|
|
344
|
-
activeTools,
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
createContext(turnState, systemPrompt) {
|
|
348
|
-
return {
|
|
349
|
-
systemPrompt: systemPrompt ?? turnState.systemPrompt,
|
|
350
|
-
messages: turnState.messages.slice(),
|
|
351
|
-
tools: turnState.activeTools.map((tool) => this.bindToolContext(tool, turnState.toolContext)),
|
|
352
|
-
};
|
|
97
|
+
async prompt(_input, _images) {
|
|
98
|
+
return this.unavailable("prompt");
|
|
353
99
|
}
|
|
354
|
-
|
|
355
|
-
return
|
|
356
|
-
const turnState = getTurnState();
|
|
357
|
-
const snapshotOptions = { ...turnState.streamOptions };
|
|
358
|
-
const requestOptions = await this.emitBeforeProviderRequest(model, turnState.sessionId, snapshotOptions);
|
|
359
|
-
return this.models.streamSimple(model, context, {
|
|
360
|
-
cacheRetention: requestOptions.cacheRetention,
|
|
361
|
-
headers: requestOptions.headers,
|
|
362
|
-
maxRetries: requestOptions.maxRetries,
|
|
363
|
-
maxRetryDelayMs: requestOptions.maxRetryDelayMs,
|
|
364
|
-
metadata: requestOptions.metadata,
|
|
365
|
-
onPayload: async (payload) => await this.emitBeforeProviderPayload(model, payload),
|
|
366
|
-
onResponse: async (response) => {
|
|
367
|
-
const headers = { ...response.headers };
|
|
368
|
-
await this.emitOwn({ type: "after_provider_response", status: response.status, headers }, streamOptions?.signal);
|
|
369
|
-
},
|
|
370
|
-
reasoning: streamOptions?.reasoning,
|
|
371
|
-
signal: streamOptions?.signal,
|
|
372
|
-
sessionId: turnState.sessionId,
|
|
373
|
-
timeoutMs: requestOptions.timeoutMs,
|
|
374
|
-
transport: requestOptions.transport,
|
|
375
|
-
});
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
async drainQueuedMessages(queue, mode) {
|
|
379
|
-
const messages = mode === "all" ? queue.splice(0) : queue.splice(0, 1);
|
|
380
|
-
if (messages.length === 0)
|
|
381
|
-
return messages;
|
|
382
|
-
try {
|
|
383
|
-
await this.emitQueueUpdate();
|
|
384
|
-
return messages;
|
|
385
|
-
}
|
|
386
|
-
catch (error) {
|
|
387
|
-
queue.unshift(...messages);
|
|
388
|
-
throw normalizeHookError(error);
|
|
389
|
-
}
|
|
100
|
+
async skill(_name, _additionalInstructions) {
|
|
101
|
+
return this.unavailable("skill");
|
|
390
102
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
let steeringQueueAtDrain = this.steerQueue;
|
|
394
|
-
let followUpQueueAtDrain = this.followUpQueue;
|
|
395
|
-
return {
|
|
396
|
-
model: turnState.model,
|
|
397
|
-
reasoning: turnState.thinkingLevel === "off" ? undefined : turnState.thinkingLevel,
|
|
398
|
-
convertToLlm,
|
|
399
|
-
transformContext: async (messages) => {
|
|
400
|
-
const result = await this.emitHook({ type: "context", messages: [...messages] });
|
|
401
|
-
return result?.messages ?? messages;
|
|
402
|
-
},
|
|
403
|
-
beforeToolCall: async ({ toolCall, args }) => {
|
|
404
|
-
const result = await this.emitHook({
|
|
405
|
-
type: "tool_call",
|
|
406
|
-
toolCallId: toolCall.id,
|
|
407
|
-
toolName: toolCall.name,
|
|
408
|
-
input: args,
|
|
409
|
-
});
|
|
410
|
-
return result ? { block: result.block, reason: result.reason } : undefined;
|
|
411
|
-
},
|
|
412
|
-
afterToolCall: async ({ toolCall, args, result, isError }) => {
|
|
413
|
-
const patch = await this.emitHook({
|
|
414
|
-
type: "tool_result",
|
|
415
|
-
toolCallId: toolCall.id,
|
|
416
|
-
toolName: toolCall.name,
|
|
417
|
-
input: args,
|
|
418
|
-
content: result.content,
|
|
419
|
-
details: result.details,
|
|
420
|
-
isError,
|
|
421
|
-
usage: result.usage,
|
|
422
|
-
});
|
|
423
|
-
return patch
|
|
424
|
-
? {
|
|
425
|
-
content: patch.content,
|
|
426
|
-
details: patch.details,
|
|
427
|
-
isError: patch.isError,
|
|
428
|
-
usage: patch.usage,
|
|
429
|
-
terminate: patch.terminate,
|
|
430
|
-
}
|
|
431
|
-
: undefined;
|
|
432
|
-
},
|
|
433
|
-
prepareNextTurn: async () => {
|
|
434
|
-
await this.flushPendingSessionWrites();
|
|
435
|
-
const nextTurnState = await this.createTurnState();
|
|
436
|
-
setTurnState(nextTurnState);
|
|
437
|
-
return {
|
|
438
|
-
context: this.createContext(nextTurnState),
|
|
439
|
-
model: nextTurnState.model,
|
|
440
|
-
thinkingLevel: nextTurnState.thinkingLevel,
|
|
441
|
-
};
|
|
442
|
-
},
|
|
443
|
-
getSteeringMessages: async () => {
|
|
444
|
-
steeringQueueAtDrain = this.steerQueue;
|
|
445
|
-
return this.drainQueuedMessages(steeringQueueAtDrain, this.steeringQueueMode);
|
|
446
|
-
},
|
|
447
|
-
getFollowUpMessages: async () => {
|
|
448
|
-
followUpQueueAtDrain = this.followUpQueue;
|
|
449
|
-
return this.drainQueuedMessages(followUpQueueAtDrain, this.followUpQueueMode);
|
|
450
|
-
},
|
|
451
|
-
restorePendingMessages: async (queue, messages) => {
|
|
452
|
-
const target = queue === "steering" ? this.steerQueue : this.followUpQueue;
|
|
453
|
-
const drainedQueue = queue === "steering" ? steeringQueueAtDrain : followUpQueueAtDrain;
|
|
454
|
-
if (target !== drainedQueue)
|
|
455
|
-
return;
|
|
456
|
-
for (let index = messages.length - 1; index >= 0; index--) {
|
|
457
|
-
const message = messages[index];
|
|
458
|
-
if (message?.role !== "user") {
|
|
459
|
-
throw new AgentHarnessError("invalid_state", "Only user messages can be restored to input queues");
|
|
460
|
-
}
|
|
461
|
-
target.unshift(message);
|
|
462
|
-
}
|
|
463
|
-
await this.emitQueueUpdate();
|
|
464
|
-
},
|
|
465
|
-
};
|
|
103
|
+
async promptFromTemplate(_name, _args) {
|
|
104
|
+
return this.unavailable("promptFromTemplate");
|
|
466
105
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
if (duplicates.length > 0)
|
|
470
|
-
throw new AgentHarnessError("invalid_argument", `${message}: ${duplicates.join(", ")}`);
|
|
106
|
+
async compact(_options) {
|
|
107
|
+
return this.unavailable("compact");
|
|
471
108
|
}
|
|
472
|
-
|
|
473
|
-
this.
|
|
474
|
-
const missing = toolNames.filter((name) => !tools.has(name));
|
|
475
|
-
if (missing.length > 0)
|
|
476
|
-
throw new AgentHarnessError("invalid_argument", `Unknown tool(s): ${missing.join(", ")}`);
|
|
109
|
+
async navigateTree(_targetId, _options) {
|
|
110
|
+
return this.unavailable("navigateTree");
|
|
477
111
|
}
|
|
478
|
-
async
|
|
479
|
-
|
|
480
|
-
const write = this.pendingSessionWrites[0];
|
|
481
|
-
if (write.type === "message") {
|
|
482
|
-
await this.session.appendMessage(write.message);
|
|
483
|
-
}
|
|
484
|
-
else if (write.type === "model_change") {
|
|
485
|
-
await this.session.appendModelChange(write.provider, write.modelId);
|
|
486
|
-
}
|
|
487
|
-
else if (write.type === "thinking_level_change") {
|
|
488
|
-
await this.session.appendThinkingLevelChange(write.thinkingLevel);
|
|
489
|
-
}
|
|
490
|
-
else if (write.type === "active_tools_change") {
|
|
491
|
-
await this.session.appendActiveToolsChange(write.activeToolNames);
|
|
492
|
-
}
|
|
493
|
-
else if (write.type === "custom") {
|
|
494
|
-
await this.session.appendCustomEntry(write.customType, write.data);
|
|
495
|
-
}
|
|
496
|
-
else if (write.type === "custom_message") {
|
|
497
|
-
await this.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details);
|
|
498
|
-
}
|
|
499
|
-
else if (write.type === "label") {
|
|
500
|
-
await this.session.appendLabel(write.targetId, write.label);
|
|
501
|
-
}
|
|
502
|
-
else if (write.type === "session_info") {
|
|
503
|
-
await this.session.appendSessionName(write.name ?? "");
|
|
504
|
-
}
|
|
505
|
-
else if (write.type === "leaf") {
|
|
506
|
-
await this.session.moveTo(write.targetId);
|
|
507
|
-
}
|
|
508
|
-
this.pendingSessionWrites.shift();
|
|
509
|
-
}
|
|
112
|
+
async resume() {
|
|
113
|
+
return this.unavailable("resume");
|
|
510
114
|
}
|
|
511
|
-
async
|
|
512
|
-
|
|
513
|
-
await this.session.appendMessage(event.message);
|
|
514
|
-
await this.emitAny(event, signal);
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
if (event.type === "turn_end") {
|
|
518
|
-
let eventError;
|
|
519
|
-
try {
|
|
520
|
-
await this.emitAny(event, signal);
|
|
521
|
-
}
|
|
522
|
-
catch (error) {
|
|
523
|
-
eventError = error;
|
|
524
|
-
}
|
|
525
|
-
const hadPendingMutations = this.pendingSessionWrites.length > 0;
|
|
526
|
-
await this.flushPendingSessionWrites();
|
|
527
|
-
if (eventError)
|
|
528
|
-
throw eventError;
|
|
529
|
-
await this.emitOwn({ type: "save_point", hadPendingMutations });
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
if (event.type === "agent_end") {
|
|
533
|
-
await this.flushPendingSessionWrites();
|
|
534
|
-
this.phase = "idle";
|
|
535
|
-
await this.emitAny(event, signal);
|
|
536
|
-
await this.emitOwn({ type: "settled", nextTurnCount: this.nextTurnQueue.length }, signal);
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
await this.emitAny(event, signal);
|
|
540
|
-
}
|
|
541
|
-
async emitRunFailure(model, error, aborted, signal) {
|
|
542
|
-
const failureMessage = createFailureMessage(model, error, aborted);
|
|
543
|
-
await this.handleAgentEvent({ type: "message_start", message: failureMessage }, signal);
|
|
544
|
-
await this.handleAgentEvent({ type: "message_end", message: failureMessage }, signal);
|
|
545
|
-
await this.handleAgentEvent({ type: "turn_end", message: failureMessage, toolResults: [] }, signal);
|
|
546
|
-
await this.handleAgentEvent({ type: "agent_end", messages: [failureMessage] }, signal);
|
|
547
|
-
return [failureMessage];
|
|
115
|
+
async abort() {
|
|
116
|
+
return this.unavailable("abort");
|
|
548
117
|
}
|
|
549
|
-
async
|
|
550
|
-
this.
|
|
551
|
-
let activeTurnState = turnState;
|
|
552
|
-
let messages = [createUserMessage(text, options?.images)];
|
|
553
|
-
if (this.nextTurnQueue.length > 0) {
|
|
554
|
-
const queuedMessages = this.nextTurnQueue.splice(0);
|
|
555
|
-
try {
|
|
556
|
-
await this.emitQueueUpdate();
|
|
557
|
-
}
|
|
558
|
-
catch (error) {
|
|
559
|
-
this.nextTurnQueue.unshift(...queuedMessages);
|
|
560
|
-
throw normalizeHookError(error);
|
|
561
|
-
}
|
|
562
|
-
messages = [...queuedMessages, messages[0]];
|
|
563
|
-
}
|
|
564
|
-
const beforeResult = await this.emitHook({
|
|
565
|
-
type: "before_agent_start",
|
|
566
|
-
prompt: text,
|
|
567
|
-
images: options?.images,
|
|
568
|
-
systemPrompt: turnState.systemPrompt,
|
|
569
|
-
resources: turnState.resources,
|
|
570
|
-
});
|
|
571
|
-
this.assertNotShutDown();
|
|
572
|
-
if (beforeResult?.messages)
|
|
573
|
-
messages = [...messages, ...beforeResult.messages];
|
|
574
|
-
const getTurnState = () => activeTurnState;
|
|
575
|
-
const setTurnState = (nextTurnState) => {
|
|
576
|
-
activeTurnState = nextTurnState;
|
|
577
|
-
};
|
|
578
|
-
const runResultPromise = (async () => {
|
|
579
|
-
try {
|
|
580
|
-
return await runAgentLoop(messages, this.createContext(turnState, beforeResult?.systemPrompt), this.createLoopConfig(getTurnState, setTurnState), (event) => this.handleAgentEvent(event, signal), signal, this.createStreamFn(getTurnState));
|
|
581
|
-
}
|
|
582
|
-
catch (error) {
|
|
583
|
-
try {
|
|
584
|
-
return await this.emitRunFailure(activeTurnState.model, error, signal.aborted, signal);
|
|
585
|
-
}
|
|
586
|
-
catch (failureError) {
|
|
587
|
-
const cause = new AggregateError([toError(error), toError(failureError)], "Agent run failed and failure reporting failed");
|
|
588
|
-
throw new AgentHarnessError("unknown", cause.message, cause);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
})();
|
|
592
|
-
try {
|
|
593
|
-
const newMessages = await runResultPromise;
|
|
594
|
-
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
595
|
-
const message = newMessages[i];
|
|
596
|
-
if (message.role === "assistant") {
|
|
597
|
-
return message;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
throw new AgentHarnessError("invalid_state", "AgentHarness prompt completed without an assistant message");
|
|
601
|
-
}
|
|
602
|
-
finally {
|
|
603
|
-
await this.flushPendingSessionWrites();
|
|
604
|
-
}
|
|
118
|
+
async steer(_input, _images) {
|
|
119
|
+
return this.unavailable("steer");
|
|
605
120
|
}
|
|
606
|
-
async
|
|
607
|
-
this.
|
|
608
|
-
if (this.phase !== "idle")
|
|
609
|
-
throw new AgentHarnessError("busy", "AgentHarness is busy");
|
|
610
|
-
this.phase = "turn";
|
|
611
|
-
const operation = this.startOperation();
|
|
612
|
-
try {
|
|
613
|
-
const turnState = await this.createTurnState();
|
|
614
|
-
return await this.executeTurn(turnState, text, operation.signal, options);
|
|
615
|
-
}
|
|
616
|
-
catch (error) {
|
|
617
|
-
this.phase = "idle";
|
|
618
|
-
throw normalizeHarnessError(error, "unknown");
|
|
619
|
-
}
|
|
620
|
-
finally {
|
|
621
|
-
operation.finish();
|
|
622
|
-
}
|
|
121
|
+
async followUp(_input, _images) {
|
|
122
|
+
return this.unavailable("followUp");
|
|
623
123
|
}
|
|
624
|
-
async
|
|
625
|
-
this.
|
|
626
|
-
if (this.phase !== "idle")
|
|
627
|
-
throw new AgentHarnessError("busy", "AgentHarness is busy");
|
|
628
|
-
this.phase = "turn";
|
|
629
|
-
const operation = this.startOperation();
|
|
630
|
-
try {
|
|
631
|
-
const turnState = await this.createTurnState();
|
|
632
|
-
const skill = (turnState.resources.skills ?? []).find((candidate) => candidate.name === name);
|
|
633
|
-
if (!skill)
|
|
634
|
-
throw new AgentHarnessError("invalid_argument", `Unknown skill: ${name}`);
|
|
635
|
-
return await this.executeTurn(turnState, formatSkillInvocation(skill, additionalInstructions), operation.signal);
|
|
636
|
-
}
|
|
637
|
-
catch (error) {
|
|
638
|
-
this.phase = "idle";
|
|
639
|
-
throw normalizeHarnessError(error, "unknown");
|
|
640
|
-
}
|
|
641
|
-
finally {
|
|
642
|
-
operation.finish();
|
|
643
|
-
}
|
|
124
|
+
async nextRun(_input, _images) {
|
|
125
|
+
return this.unavailable("nextRun");
|
|
644
126
|
}
|
|
645
|
-
async
|
|
646
|
-
this.
|
|
647
|
-
if (this.phase !== "idle")
|
|
648
|
-
throw new AgentHarnessError("busy", "AgentHarness is busy");
|
|
649
|
-
this.phase = "turn";
|
|
650
|
-
const operation = this.startOperation();
|
|
651
|
-
try {
|
|
652
|
-
const turnState = await this.createTurnState();
|
|
653
|
-
const template = (turnState.resources.promptTemplates ?? []).find((candidate) => candidate.name === name);
|
|
654
|
-
if (!template)
|
|
655
|
-
throw new AgentHarnessError("invalid_argument", `Unknown prompt template: ${name}`);
|
|
656
|
-
return await this.executeTurn(turnState, formatPromptTemplateInvocation(template, args), operation.signal);
|
|
657
|
-
}
|
|
658
|
-
catch (error) {
|
|
659
|
-
this.phase = "idle";
|
|
660
|
-
throw normalizeHarnessError(error, "unknown");
|
|
661
|
-
}
|
|
662
|
-
finally {
|
|
663
|
-
operation.finish();
|
|
664
|
-
}
|
|
127
|
+
async cancelQueued(_entryId) {
|
|
128
|
+
return this.unavailable("cancelQueued");
|
|
665
129
|
}
|
|
666
|
-
async
|
|
667
|
-
this.
|
|
668
|
-
if (this.phase === "idle")
|
|
669
|
-
throw new AgentHarnessError("invalid_state", "Cannot steer while idle");
|
|
670
|
-
this.steerQueue.push(createUserMessage(text, options?.images));
|
|
671
|
-
await this.emitQueueUpdate();
|
|
130
|
+
async recordUsage(_usage, _options) {
|
|
131
|
+
return this.unavailable("recordUsage");
|
|
672
132
|
}
|
|
673
|
-
async
|
|
674
|
-
this.
|
|
675
|
-
if (this.phase === "idle")
|
|
676
|
-
throw new AgentHarnessError("invalid_state", "Cannot follow up while idle");
|
|
677
|
-
this.followUpQueue.push(createUserMessage(text, options?.images));
|
|
678
|
-
await this.emitQueueUpdate();
|
|
133
|
+
async waitForIdle() {
|
|
134
|
+
return this.unavailable("waitForIdle");
|
|
679
135
|
}
|
|
680
|
-
async
|
|
681
|
-
this.
|
|
682
|
-
this.nextTurnQueue.push(createUserMessage(text, options?.images));
|
|
683
|
-
await this.emitQueueUpdate();
|
|
136
|
+
async runWhenIdle(_callback) {
|
|
137
|
+
return this.unavailable("runWhenIdle");
|
|
684
138
|
}
|
|
685
|
-
async
|
|
686
|
-
this.
|
|
687
|
-
return this.track("mutation", async () => {
|
|
688
|
-
try {
|
|
689
|
-
if (this.phase === "idle") {
|
|
690
|
-
await this.session.appendMessage(message);
|
|
691
|
-
}
|
|
692
|
-
else {
|
|
693
|
-
this.pendingSessionWrites.push({ type: "message", message });
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
catch (error) {
|
|
697
|
-
throw normalizeHarnessError(error, "session");
|
|
698
|
-
}
|
|
699
|
-
});
|
|
139
|
+
async peekAction() {
|
|
140
|
+
return this.unavailable("peekAction");
|
|
700
141
|
}
|
|
701
|
-
async
|
|
702
|
-
this.
|
|
703
|
-
if (this.phase !== "idle")
|
|
704
|
-
throw new AgentHarnessError("busy", "compact() requires idle harness");
|
|
705
|
-
this.phase = "compaction";
|
|
706
|
-
const operation = this.startOperation();
|
|
707
|
-
try {
|
|
708
|
-
const model = this.model;
|
|
709
|
-
if (!model)
|
|
710
|
-
throw new AgentHarnessError("invalid_state", "No model set for compaction");
|
|
711
|
-
const branchEntries = await this.session.getBranch();
|
|
712
|
-
const preparationResult = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);
|
|
713
|
-
if (!preparationResult.ok)
|
|
714
|
-
throw preparationResult.error;
|
|
715
|
-
const preparation = preparationResult.value;
|
|
716
|
-
if (!preparation)
|
|
717
|
-
throw new AgentHarnessError("compaction", "Nothing to compact");
|
|
718
|
-
const hookResult = await this.emitHook({
|
|
719
|
-
type: "session_before_compact",
|
|
720
|
-
preparation,
|
|
721
|
-
branchEntries,
|
|
722
|
-
customInstructions,
|
|
723
|
-
signal: operation.signal,
|
|
724
|
-
});
|
|
725
|
-
if (hookResult?.cancel)
|
|
726
|
-
throw new AgentHarnessError("compaction", "Compaction cancelled");
|
|
727
|
-
const provided = hookResult?.compaction;
|
|
728
|
-
const compactResult = provided
|
|
729
|
-
? { ok: true, value: provided }
|
|
730
|
-
: await compact(preparation, this.models, model, customInstructions, operation.signal, this.thinkingLevel, this.retry, this.retryCallbacks("compaction"));
|
|
731
|
-
if (!compactResult.ok)
|
|
732
|
-
throw compactResult.error;
|
|
733
|
-
const result = compactResult.value;
|
|
734
|
-
this.assertNotShutDown();
|
|
735
|
-
const entryId = await this.session.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, provided !== undefined, result.usage, result.retainedTail);
|
|
736
|
-
const entry = await this.session.getEntry(entryId);
|
|
737
|
-
if (entry?.type === "compaction") {
|
|
738
|
-
await this.emitOwn({ type: "session_compact", compactionEntry: entry, fromHook: provided !== undefined });
|
|
739
|
-
}
|
|
740
|
-
return result;
|
|
741
|
-
}
|
|
742
|
-
catch (error) {
|
|
743
|
-
throw normalizeHarnessError(error, "compaction");
|
|
744
|
-
}
|
|
745
|
-
finally {
|
|
746
|
-
this.phase = "idle";
|
|
747
|
-
operation.finish();
|
|
748
|
-
}
|
|
142
|
+
async executeAction() {
|
|
143
|
+
return this.unavailable("executeAction");
|
|
749
144
|
}
|
|
750
|
-
async
|
|
751
|
-
this.
|
|
752
|
-
if (this.phase !== "idle")
|
|
753
|
-
throw new AgentHarnessError("busy", "navigateTree() requires idle harness");
|
|
754
|
-
this.phase = "branch_summary";
|
|
755
|
-
const operation = this.startOperation();
|
|
756
|
-
try {
|
|
757
|
-
const oldLeafId = await this.session.getLeafId();
|
|
758
|
-
if (oldLeafId === targetId)
|
|
759
|
-
return { cancelled: false };
|
|
760
|
-
const targetEntry = await this.session.getEntry(targetId);
|
|
761
|
-
if (!targetEntry)
|
|
762
|
-
throw new AgentHarnessError("invalid_argument", `Entry ${targetId} not found`);
|
|
763
|
-
const { entries, commonAncestorId } = await collectEntriesForBranchSummary(this.session, oldLeafId, targetId);
|
|
764
|
-
const preparation = {
|
|
765
|
-
targetId,
|
|
766
|
-
oldLeafId,
|
|
767
|
-
commonAncestorId,
|
|
768
|
-
entriesToSummarize: entries,
|
|
769
|
-
userWantsSummary: options?.summarize ?? false,
|
|
770
|
-
customInstructions: options?.customInstructions,
|
|
771
|
-
replaceInstructions: options?.replaceInstructions,
|
|
772
|
-
label: options?.label,
|
|
773
|
-
};
|
|
774
|
-
const hookResult = await this.emitHook({
|
|
775
|
-
type: "session_before_tree",
|
|
776
|
-
preparation,
|
|
777
|
-
signal: operation.signal,
|
|
778
|
-
});
|
|
779
|
-
if (hookResult?.cancel)
|
|
780
|
-
return { cancelled: true };
|
|
781
|
-
let summaryEntry;
|
|
782
|
-
let summaryText = hookResult?.summary?.summary;
|
|
783
|
-
let summaryDetails = hookResult?.summary?.details;
|
|
784
|
-
let summaryUsage = hookResult?.summary?.usage;
|
|
785
|
-
if (!summaryText && options?.summarize && entries.length > 0) {
|
|
786
|
-
const model = this.model;
|
|
787
|
-
if (!model)
|
|
788
|
-
throw new AgentHarnessError("invalid_state", "No model set for branch summary");
|
|
789
|
-
const branchSummary = await generateBranchSummary(entries, {
|
|
790
|
-
models: this.models,
|
|
791
|
-
model,
|
|
792
|
-
signal: operation.signal,
|
|
793
|
-
customInstructions: hookResult?.customInstructions ?? options?.customInstructions,
|
|
794
|
-
replaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,
|
|
795
|
-
retry: this.retry,
|
|
796
|
-
callbacks: this.retryCallbacks("branch_summary"),
|
|
797
|
-
});
|
|
798
|
-
if (!branchSummary.ok) {
|
|
799
|
-
if (branchSummary.error.code === "aborted")
|
|
800
|
-
return { cancelled: true };
|
|
801
|
-
throw new AgentHarnessError("branch_summary", branchSummary.error.message, branchSummary.error);
|
|
802
|
-
}
|
|
803
|
-
summaryText = branchSummary.value.summary;
|
|
804
|
-
summaryUsage = branchSummary.value.usage;
|
|
805
|
-
summaryDetails = {
|
|
806
|
-
readFiles: branchSummary.value.readFiles,
|
|
807
|
-
modifiedFiles: branchSummary.value.modifiedFiles,
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
let editorText;
|
|
811
|
-
let newLeafId;
|
|
812
|
-
if (targetEntry.type === "message" && targetEntry.message.role === "user") {
|
|
813
|
-
newLeafId = targetEntry.parentId;
|
|
814
|
-
editorText = contentText(targetEntry.message.content, "");
|
|
815
|
-
}
|
|
816
|
-
else if (targetEntry.type === "custom_message") {
|
|
817
|
-
newLeafId = targetEntry.parentId;
|
|
818
|
-
editorText = contentText(targetEntry.content, "");
|
|
819
|
-
}
|
|
820
|
-
else {
|
|
821
|
-
newLeafId = targetId;
|
|
822
|
-
}
|
|
823
|
-
this.assertNotShutDown();
|
|
824
|
-
const summaryId = await this.session.moveTo(newLeafId, summaryText
|
|
825
|
-
? {
|
|
826
|
-
summary: summaryText,
|
|
827
|
-
details: summaryDetails,
|
|
828
|
-
usage: summaryUsage,
|
|
829
|
-
fromHook: hookResult?.summary !== undefined,
|
|
830
|
-
}
|
|
831
|
-
: undefined);
|
|
832
|
-
if (summaryId) {
|
|
833
|
-
const entry = await this.session.getEntry(summaryId);
|
|
834
|
-
if (entry?.type === "branch_summary")
|
|
835
|
-
summaryEntry = entry;
|
|
836
|
-
}
|
|
837
|
-
await this.emitOwn({
|
|
838
|
-
type: "session_tree",
|
|
839
|
-
newLeafId: await this.session.getLeafId(),
|
|
840
|
-
oldLeafId,
|
|
841
|
-
summaryEntry,
|
|
842
|
-
fromHook: hookResult?.summary !== undefined,
|
|
843
|
-
});
|
|
844
|
-
return { cancelled: false, editorText, summaryEntry };
|
|
845
|
-
}
|
|
846
|
-
catch (error) {
|
|
847
|
-
throw normalizeHarnessError(error, "branch_summary");
|
|
848
|
-
}
|
|
849
|
-
finally {
|
|
850
|
-
this.phase = "idle";
|
|
851
|
-
operation.finish();
|
|
852
|
-
}
|
|
145
|
+
async runToCompletion() {
|
|
146
|
+
return this.unavailable("runToCompletion");
|
|
853
147
|
}
|
|
854
|
-
getModel() {
|
|
148
|
+
async getModel() {
|
|
855
149
|
return this.model;
|
|
856
150
|
}
|
|
857
151
|
async setModel(model) {
|
|
858
|
-
this.
|
|
859
|
-
return this.track("mutation", async () => {
|
|
860
|
-
try {
|
|
861
|
-
const previousModel = this.model;
|
|
862
|
-
if (this.phase === "idle") {
|
|
863
|
-
await this.session.appendModelChange(model.provider, model.id);
|
|
864
|
-
}
|
|
865
|
-
else {
|
|
866
|
-
this.pendingSessionWrites.push({ type: "model_change", provider: model.provider, modelId: model.id });
|
|
867
|
-
}
|
|
868
|
-
this.model = model;
|
|
869
|
-
await this.emitOwn({ type: "model_update", model, previousModel, source: "set" });
|
|
870
|
-
}
|
|
871
|
-
catch (error) {
|
|
872
|
-
throw normalizeHarnessError(error, "session");
|
|
873
|
-
}
|
|
874
|
-
});
|
|
152
|
+
this.model = model;
|
|
875
153
|
}
|
|
876
|
-
getThinkingLevel() {
|
|
154
|
+
async getThinkingLevel() {
|
|
877
155
|
return this.thinkingLevel;
|
|
878
156
|
}
|
|
879
157
|
async setThinkingLevel(level) {
|
|
880
|
-
this.
|
|
881
|
-
return this.track("mutation", async () => {
|
|
882
|
-
try {
|
|
883
|
-
const previousLevel = this.thinkingLevel;
|
|
884
|
-
if (this.phase === "idle") {
|
|
885
|
-
await this.session.appendThinkingLevelChange(level);
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
this.pendingSessionWrites.push({ type: "thinking_level_change", thinkingLevel: level });
|
|
889
|
-
}
|
|
890
|
-
this.thinkingLevel = level;
|
|
891
|
-
await this.emitOwn({ type: "thinking_level_update", level, previousLevel });
|
|
892
|
-
}
|
|
893
|
-
catch (error) {
|
|
894
|
-
throw normalizeHarnessError(error, "session");
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
getTools() {
|
|
899
|
-
return [...this.tools.values()];
|
|
158
|
+
this.thinkingLevel = level;
|
|
900
159
|
}
|
|
901
|
-
async
|
|
902
|
-
this.
|
|
903
|
-
return this.track("mutation", () => this.applyTools(tools, activeToolNames));
|
|
160
|
+
async getActiveTools() {
|
|
161
|
+
return [...this.activeToolNames];
|
|
904
162
|
}
|
|
905
|
-
async
|
|
906
|
-
|
|
907
|
-
this.validateUniqueNames(tools.map((tool) => tool.name), "Duplicate tool name(s)");
|
|
908
|
-
const nextTools = new Map(tools.map((tool) => [tool.name, tool]));
|
|
909
|
-
const nextActiveToolNames = activeToolNames ? [...activeToolNames] : this.activeToolNames;
|
|
910
|
-
this.validateToolNames(nextActiveToolNames, nextTools);
|
|
911
|
-
const previousToolNames = [...this.tools.keys()];
|
|
912
|
-
const previousActiveToolNames = [...this.activeToolNames];
|
|
913
|
-
if (this.phase === "idle") {
|
|
914
|
-
await this.session.appendActiveToolsChange(nextActiveToolNames);
|
|
915
|
-
}
|
|
916
|
-
else {
|
|
917
|
-
this.pendingSessionWrites.push({ type: "active_tools_change", activeToolNames: [...nextActiveToolNames] });
|
|
918
|
-
}
|
|
919
|
-
this.tools = nextTools;
|
|
920
|
-
this.activeToolNames = [...nextActiveToolNames];
|
|
921
|
-
await this.emitOwn({
|
|
922
|
-
type: "tools_update",
|
|
923
|
-
toolNames: [...this.tools.keys()],
|
|
924
|
-
previousToolNames,
|
|
925
|
-
activeToolNames: [...this.activeToolNames],
|
|
926
|
-
previousActiveToolNames,
|
|
927
|
-
source: "set",
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
catch (error) {
|
|
931
|
-
throw normalizeHarnessError(error, "invalid_argument");
|
|
932
|
-
}
|
|
163
|
+
async setActiveTools(names) {
|
|
164
|
+
this.activeToolNames = [...names];
|
|
933
165
|
}
|
|
934
|
-
|
|
935
|
-
return this.
|
|
166
|
+
async watch() {
|
|
167
|
+
return this.unavailable("watch");
|
|
936
168
|
}
|
|
937
|
-
async
|
|
938
|
-
this.
|
|
939
|
-
return this.track("mutation", () => this.applyActiveTools(toolNames));
|
|
169
|
+
async lane(_name) {
|
|
170
|
+
return this.unavailable("lane");
|
|
940
171
|
}
|
|
941
|
-
async
|
|
942
|
-
|
|
943
|
-
this.validateToolNames(toolNames);
|
|
944
|
-
const previousToolNames = [...this.tools.keys()];
|
|
945
|
-
const previousActiveToolNames = [...this.activeToolNames];
|
|
946
|
-
if (this.phase === "idle") {
|
|
947
|
-
await this.session.appendActiveToolsChange(toolNames);
|
|
948
|
-
}
|
|
949
|
-
else {
|
|
950
|
-
this.pendingSessionWrites.push({ type: "active_tools_change", activeToolNames: [...toolNames] });
|
|
951
|
-
}
|
|
952
|
-
this.activeToolNames = [...toolNames];
|
|
953
|
-
await this.emitOwn({
|
|
954
|
-
type: "tools_update",
|
|
955
|
-
toolNames: [...this.tools.keys()],
|
|
956
|
-
previousToolNames,
|
|
957
|
-
activeToolNames: [...this.activeToolNames],
|
|
958
|
-
previousActiveToolNames,
|
|
959
|
-
source: "set",
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
catch (error) {
|
|
963
|
-
throw normalizeHarnessError(error, "invalid_argument");
|
|
964
|
-
}
|
|
172
|
+
async createLane(_name, _at) {
|
|
173
|
+
return this.unavailable("createLane");
|
|
965
174
|
}
|
|
966
|
-
|
|
967
|
-
return this.
|
|
175
|
+
async lanes() {
|
|
176
|
+
return this.unavailable("lanes");
|
|
968
177
|
}
|
|
969
|
-
async
|
|
970
|
-
this.
|
|
971
|
-
this.steeringQueueMode = mode;
|
|
178
|
+
async getTools() {
|
|
179
|
+
return [...this.tools];
|
|
972
180
|
}
|
|
973
|
-
|
|
974
|
-
|
|
181
|
+
async setTools(tools, activeNames) {
|
|
182
|
+
this.tools = [...tools];
|
|
183
|
+
this.activeToolNames = [...(activeNames ?? tools.map((tool) => tool.name))];
|
|
975
184
|
}
|
|
976
|
-
async
|
|
977
|
-
this.assertNotShutDown();
|
|
978
|
-
this.followUpQueueMode = mode;
|
|
979
|
-
}
|
|
980
|
-
getResources() {
|
|
185
|
+
async getResources() {
|
|
981
186
|
return {
|
|
982
|
-
skills: this.resources.skills
|
|
983
|
-
promptTemplates: this.resources.promptTemplates
|
|
187
|
+
skills: this.resources.skills ? [...this.resources.skills] : undefined,
|
|
188
|
+
promptTemplates: this.resources.promptTemplates ? [...this.resources.promptTemplates] : undefined,
|
|
984
189
|
};
|
|
985
190
|
}
|
|
986
191
|
async setResources(resources) {
|
|
987
|
-
this.assertNotShutDown();
|
|
988
|
-
const previousResources = this.getResources();
|
|
989
192
|
this.resources = {
|
|
990
|
-
skills: resources.skills
|
|
991
|
-
promptTemplates: resources.promptTemplates
|
|
193
|
+
skills: resources.skills ? [...resources.skills] : undefined,
|
|
194
|
+
promptTemplates: resources.promptTemplates ? [...resources.promptTemplates] : undefined,
|
|
992
195
|
};
|
|
993
|
-
await this.emitOwn({ type: "resources_update", resources: this.getResources(), previousResources });
|
|
994
196
|
}
|
|
995
|
-
getStreamOptions() {
|
|
996
|
-
return
|
|
197
|
+
async getStreamOptions() {
|
|
198
|
+
return { ...this.streamOptions };
|
|
997
199
|
}
|
|
998
|
-
async setStreamOptions(
|
|
999
|
-
this.
|
|
1000
|
-
this.streamOptions = cloneStreamOptions(streamOptions);
|
|
200
|
+
async setStreamOptions(options) {
|
|
201
|
+
this.streamOptions = { ...options };
|
|
1001
202
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
if (this.isShutdown)
|
|
1005
|
-
return;
|
|
1006
|
-
this.isShutdown = true;
|
|
1007
|
-
this.pendingSessionWrites = [];
|
|
1008
|
-
this.steerQueue = [];
|
|
1009
|
-
this.followUpQueue = [];
|
|
1010
|
-
this.nextTurnQueue = [];
|
|
1011
|
-
this.activeAbortController?.abort();
|
|
1012
|
-
this.shutdownPromise = this.waitForTasks();
|
|
203
|
+
async getRetryPolicy() {
|
|
204
|
+
return { ...this.retryPolicy };
|
|
1013
205
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
if (!this.shutdownPromise) {
|
|
1017
|
-
return Promise.reject(new AgentHarnessError("invalid_state", "Shutdown has not been requested"));
|
|
1018
|
-
}
|
|
1019
|
-
return this.shutdownPromise;
|
|
206
|
+
async setRetryPolicy(policy) {
|
|
207
|
+
this.retryPolicy = { ...policy };
|
|
1020
208
|
}
|
|
1021
|
-
async
|
|
1022
|
-
this.
|
|
1023
|
-
const clearedSteer = [...this.steerQueue];
|
|
1024
|
-
const clearedFollowUp = [...this.followUpQueue];
|
|
1025
|
-
this.steerQueue = [];
|
|
1026
|
-
this.followUpQueue = [];
|
|
1027
|
-
this.activeAbortController?.abort();
|
|
1028
|
-
const errors = [];
|
|
1029
|
-
try {
|
|
1030
|
-
await this.emitQueueUpdate();
|
|
1031
|
-
}
|
|
1032
|
-
catch (error) {
|
|
1033
|
-
errors.push(toError(error));
|
|
1034
|
-
}
|
|
1035
|
-
try {
|
|
1036
|
-
await this.waitForIdle();
|
|
1037
|
-
}
|
|
1038
|
-
catch (error) {
|
|
1039
|
-
errors.push(toError(error));
|
|
1040
|
-
}
|
|
1041
|
-
try {
|
|
1042
|
-
await this.emitOwn({ type: "abort", clearedSteer, clearedFollowUp });
|
|
1043
|
-
}
|
|
1044
|
-
catch (error) {
|
|
1045
|
-
errors.push(toError(error));
|
|
1046
|
-
}
|
|
1047
|
-
if (errors.length > 0) {
|
|
1048
|
-
const cause = errors.length === 1 ? errors[0] : new AggregateError(errors, "Abort completed with errors");
|
|
1049
|
-
throw normalizeHarnessError(cause, "hook");
|
|
1050
|
-
}
|
|
1051
|
-
return { clearedSteer, clearedFollowUp };
|
|
209
|
+
async getCompactionSettings() {
|
|
210
|
+
return { ...this.compactionSettings };
|
|
1052
211
|
}
|
|
1053
|
-
async
|
|
1054
|
-
|
|
212
|
+
async setCompactionSettings(settings) {
|
|
213
|
+
this.compactionSettings = { ...settings };
|
|
214
|
+
}
|
|
215
|
+
async getSteeringMode() {
|
|
216
|
+
return this.steeringMode;
|
|
217
|
+
}
|
|
218
|
+
async setSteeringMode(mode) {
|
|
219
|
+
this.steeringMode = mode;
|
|
220
|
+
}
|
|
221
|
+
async getFollowUpMode() {
|
|
222
|
+
return this.followUpMode;
|
|
223
|
+
}
|
|
224
|
+
async setFollowUpMode(mode) {
|
|
225
|
+
this.followUpMode = mode;
|
|
1055
226
|
}
|
|
1056
|
-
|
|
1057
|
-
this.
|
|
1058
|
-
let handlers = this.handlers.get(SUBSCRIBER_EVENT_TYPE);
|
|
1059
|
-
if (!handlers) {
|
|
1060
|
-
handlers = new Set();
|
|
1061
|
-
this.handlers.set(SUBSCRIBER_EVENT_TYPE, handlers);
|
|
1062
|
-
}
|
|
1063
|
-
handlers.add(listener);
|
|
1064
|
-
return () => handlers.delete(listener);
|
|
227
|
+
async watchSession() {
|
|
228
|
+
return this.unavailable("watchSession");
|
|
1065
229
|
}
|
|
1066
|
-
|
|
1067
|
-
this.
|
|
1068
|
-
let handlers = this.handlers.get(type);
|
|
1069
|
-
if (!handlers) {
|
|
1070
|
-
handlers = new Set();
|
|
1071
|
-
this.handlers.set(type, handlers);
|
|
1072
|
-
}
|
|
1073
|
-
handlers.add(handler);
|
|
1074
|
-
return () => handlers.delete(handler);
|
|
230
|
+
async close() {
|
|
231
|
+
this.closed = true;
|
|
1075
232
|
}
|
|
1076
233
|
}
|
|
1077
234
|
//# sourceMappingURL=agent-harness.js.map
|