@botbotgo/agent-harness 0.0.101 → 0.0.103

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.
Files changed (40) hide show
  1. package/dist/package-version.d.ts +1 -1
  2. package/dist/package-version.js +1 -1
  3. package/dist/persistence/sqlite-run-context-store.d.ts +22 -0
  4. package/dist/persistence/sqlite-run-context-store.js +64 -0
  5. package/dist/persistence/sqlite-run-queue-store.d.ts +41 -0
  6. package/dist/persistence/sqlite-run-queue-store.js +120 -0
  7. package/dist/persistence/sqlite-store.d.ts +2 -2
  8. package/dist/persistence/sqlite-store.js +31 -117
  9. package/dist/resource/mcp-tool-support.d.ts +21 -0
  10. package/dist/resource/mcp-tool-support.js +173 -0
  11. package/dist/resource/resource-impl.d.ts +1 -18
  12. package/dist/resource/resource-impl.js +3 -166
  13. package/dist/runtime/adapter/invoke-runtime.d.ts +22 -0
  14. package/dist/runtime/adapter/invoke-runtime.js +18 -0
  15. package/dist/runtime/adapter/middleware-assembly.d.ts +75 -0
  16. package/dist/runtime/adapter/middleware-assembly.js +175 -0
  17. package/dist/runtime/adapter/runtime-shell.d.ts +27 -0
  18. package/dist/runtime/adapter/runtime-shell.js +168 -0
  19. package/dist/runtime/adapter/stream-runtime.d.ts +46 -0
  20. package/dist/runtime/adapter/stream-runtime.js +93 -0
  21. package/dist/runtime/adapter/tool-resolution.d.ts +14 -0
  22. package/dist/runtime/adapter/tool-resolution.js +57 -0
  23. package/dist/runtime/agent-runtime-adapter.d.ts +1 -6
  24. package/dist/runtime/agent-runtime-adapter.js +140 -495
  25. package/dist/runtime/harness/run/run-operations.d.ts +50 -0
  26. package/dist/runtime/harness/run/run-operations.js +113 -0
  27. package/dist/runtime/harness/run/run-slot-acquisition.d.ts +64 -0
  28. package/dist/runtime/harness/run/run-slot-acquisition.js +157 -0
  29. package/dist/runtime/harness/run/startup-runtime.d.ts +37 -0
  30. package/dist/runtime/harness/run/startup-runtime.js +68 -0
  31. package/dist/runtime/harness/run/stream-run.d.ts +53 -0
  32. package/dist/runtime/harness/run/stream-run.js +304 -0
  33. package/dist/runtime/harness/run/thread-records.d.ts +21 -0
  34. package/dist/runtime/harness/run/thread-records.js +59 -0
  35. package/dist/runtime/harness.js +121 -639
  36. package/dist/workspace/object-loader.d.ts +1 -8
  37. package/dist/workspace/object-loader.js +3 -197
  38. package/dist/workspace/yaml-object-reader.d.ts +15 -0
  39. package/dist/workspace/yaml-object-reader.js +202 -0
  40. package/package.json +1 -1
@@ -0,0 +1,304 @@
1
+ import { AGENT_INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError } from "../../agent-runtime-adapter.js";
2
+ import { normalizeUpstreamRuntimeEvent } from "../../parsing/stream-event-parsing.js";
3
+ import { renderRuntimeFailure, renderToolFailure } from "../../support/harness-support.js";
4
+ import { createContentBlocksItem, createToolResultKey, emitOutputDeltaAndCreateItem, } from "../events/streaming.js";
5
+ function normalizeStreamChunk(chunk) {
6
+ if (typeof chunk === "string") {
7
+ if (chunk.startsWith(AGENT_INTERRUPT_SENTINEL_PREFIX)) {
8
+ return { kind: "interrupt", content: chunk.slice(AGENT_INTERRUPT_SENTINEL_PREFIX.length) };
9
+ }
10
+ return { kind: "content", content: chunk };
11
+ }
12
+ if (chunk.kind === "upstream-event") {
13
+ return { kind: "upstream-event", event: (chunk.event ?? {}) };
14
+ }
15
+ if (chunk.kind === "interrupt") {
16
+ return { kind: "interrupt", content: chunk.content };
17
+ }
18
+ if (chunk.kind === "reasoning") {
19
+ return { kind: "reasoning", content: chunk.content ?? "" };
20
+ }
21
+ if (chunk.kind === "step") {
22
+ return { kind: "step", content: chunk.content ?? "" };
23
+ }
24
+ if (chunk.kind === "tool-result") {
25
+ return {
26
+ kind: "tool-result",
27
+ toolName: chunk.toolName ?? "unknown_tool",
28
+ output: chunk.output,
29
+ isError: chunk.isError,
30
+ };
31
+ }
32
+ return { kind: "content", content: chunk.content ?? "" };
33
+ }
34
+ function isUpstreamRuntimeEvent(event) {
35
+ return event.format === "langgraph-v2" && "normalized" in event && "streamPart" in event;
36
+ }
37
+ export async function* streamHarnessRun(options) {
38
+ const priorHistoryPromise = Promise.resolve(options.isNewThread ? [] : undefined).then((historyHint) => historyHint ?? options.loadPriorHistory(options.threadId, options.runId));
39
+ yield { type: "event", event: await options.runCreatedEventPromise };
40
+ let releaseRunSlot = async () => undefined;
41
+ let emitted = false;
42
+ let streamActivityObserved = false;
43
+ const emitOutputDelta = (content) => emitOutputDeltaAndCreateItem(options.emit, options.threadId, options.runId, options.selectedAgentId, content);
44
+ try {
45
+ const [priorHistory, acquiredReleaseRunSlot] = await Promise.all([
46
+ priorHistoryPromise,
47
+ options.releaseRunSlotPromise,
48
+ ]).then(([loadedPriorHistory, resolvedReleaseRunSlot]) => [loadedPriorHistory, resolvedReleaseRunSlot]);
49
+ releaseRunSlot = acquiredReleaseRunSlot;
50
+ let assistantOutput = "";
51
+ const toolErrors = [];
52
+ let lastToolResultKey = null;
53
+ for await (const rawChunk of options.stream(options.binding, options.input, options.threadId, priorHistory, {
54
+ context: options.invocation.context,
55
+ state: options.invocation.state,
56
+ files: options.invocation.files,
57
+ runId: options.runId,
58
+ })) {
59
+ if (!rawChunk) {
60
+ continue;
61
+ }
62
+ streamActivityObserved = true;
63
+ const normalizedChunk = normalizeStreamChunk(rawChunk);
64
+ if (normalizedChunk.kind === "upstream-event") {
65
+ const rawEvent = normalizedChunk.event;
66
+ yield {
67
+ type: "upstream-event",
68
+ threadId: options.threadId,
69
+ runId: options.runId,
70
+ agentId: options.selectedAgentId,
71
+ event: isUpstreamRuntimeEvent(rawEvent)
72
+ ? rawEvent
73
+ : normalizeUpstreamRuntimeEvent(rawEvent.raw ?? rawEvent),
74
+ };
75
+ continue;
76
+ }
77
+ if (normalizedChunk.kind === "interrupt") {
78
+ const checkpointRef = `checkpoints/${options.threadId}/${options.runId}/cp-1`;
79
+ const waitingEvent = await options.setRunStateAndEmit(options.threadId, options.runId, 6, "waiting_for_approval", {
80
+ previousState: "running",
81
+ checkpointRef,
82
+ });
83
+ const approvalRequest = await options.requestApprovalAndEmit(options.threadId, options.runId, options.input, normalizedChunk.content, checkpointRef, 7);
84
+ yield { type: "event", event: waitingEvent };
85
+ yield { type: "event", event: approvalRequest.event };
86
+ yield {
87
+ type: "result",
88
+ result: {
89
+ threadId: options.threadId,
90
+ runId: options.runId,
91
+ agentId: options.selectedAgentId,
92
+ state: "waiting_for_approval",
93
+ output: assistantOutput,
94
+ finalMessageText: assistantOutput,
95
+ interruptContent: normalizedChunk.content,
96
+ approvalId: approvalRequest.approval.approvalId,
97
+ pendingActionId: approvalRequest.approval.pendingActionId,
98
+ },
99
+ };
100
+ return;
101
+ }
102
+ if (normalizedChunk.kind === "reasoning") {
103
+ await options.emit(options.threadId, options.runId, 3, "reasoning.delta", {
104
+ content: normalizedChunk.content,
105
+ });
106
+ yield {
107
+ type: "reasoning",
108
+ threadId: options.threadId,
109
+ runId: options.runId,
110
+ agentId: options.selectedAgentId,
111
+ content: normalizedChunk.content,
112
+ };
113
+ continue;
114
+ }
115
+ if (normalizedChunk.kind === "step") {
116
+ yield {
117
+ type: "step",
118
+ threadId: options.threadId,
119
+ runId: options.runId,
120
+ agentId: options.selectedAgentId,
121
+ content: normalizedChunk.content,
122
+ };
123
+ continue;
124
+ }
125
+ if (normalizedChunk.kind === "tool-result") {
126
+ const toolResultKey = createToolResultKey(normalizedChunk.toolName, normalizedChunk.output, normalizedChunk.isError);
127
+ if (toolResultKey === lastToolResultKey) {
128
+ continue;
129
+ }
130
+ lastToolResultKey = toolResultKey;
131
+ if (normalizedChunk.isError) {
132
+ toolErrors.push(renderToolFailure(normalizedChunk.toolName, normalizedChunk.output));
133
+ }
134
+ yield {
135
+ type: "tool-result",
136
+ threadId: options.threadId,
137
+ runId: options.runId,
138
+ agentId: options.selectedAgentId,
139
+ toolName: normalizedChunk.toolName,
140
+ output: normalizedChunk.output,
141
+ isError: normalizedChunk.isError,
142
+ };
143
+ continue;
144
+ }
145
+ emitted = true;
146
+ assistantOutput += normalizedChunk.content;
147
+ yield await emitOutputDelta(normalizedChunk.content);
148
+ }
149
+ if (!assistantOutput && toolErrors.length > 0) {
150
+ assistantOutput = toolErrors.join("\n\n");
151
+ emitted = true;
152
+ yield await emitOutputDelta(assistantOutput);
153
+ }
154
+ if (!assistantOutput) {
155
+ const actual = await options.invokeWithHistory(options.binding, options.input, options.threadId, options.runId);
156
+ if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
157
+ yield createContentBlocksItem(options.threadId, options.runId, options.selectedAgentId, actual.contentBlocks);
158
+ }
159
+ if (actual.output) {
160
+ assistantOutput = actual.output;
161
+ emitted = true;
162
+ yield await emitOutputDelta(actual.output);
163
+ }
164
+ }
165
+ await options.appendAssistantMessage(options.threadId, options.runId, assistantOutput);
166
+ yield {
167
+ type: "result",
168
+ result: {
169
+ threadId: options.threadId,
170
+ runId: options.runId,
171
+ agentId: options.selectedAgentId,
172
+ state: "completed",
173
+ output: assistantOutput,
174
+ finalMessageText: assistantOutput,
175
+ },
176
+ };
177
+ yield {
178
+ type: "event",
179
+ event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, "completed", {
180
+ previousState: "running",
181
+ }),
182
+ };
183
+ }
184
+ catch (error) {
185
+ if (emitted || streamActivityObserved) {
186
+ const runtimeFailure = renderRuntimeFailure(error);
187
+ yield {
188
+ type: "event",
189
+ event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, "failed", {
190
+ previousState: "running",
191
+ error: error instanceof Error ? error.message : String(error),
192
+ }),
193
+ };
194
+ yield {
195
+ type: "content",
196
+ threadId: options.threadId,
197
+ runId: options.runId,
198
+ agentId: options.selectedAgentId,
199
+ content: runtimeFailure,
200
+ };
201
+ yield {
202
+ type: "result",
203
+ result: {
204
+ threadId: options.threadId,
205
+ runId: options.runId,
206
+ agentId: options.selectedAgentId,
207
+ state: "failed",
208
+ output: runtimeFailure,
209
+ finalMessageText: runtimeFailure,
210
+ },
211
+ };
212
+ return;
213
+ }
214
+ if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
215
+ const runtimeFailure = renderRuntimeFailure(error);
216
+ yield {
217
+ type: "event",
218
+ event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, "failed", {
219
+ previousState: "running",
220
+ error: error.message,
221
+ }),
222
+ };
223
+ yield {
224
+ type: "content",
225
+ threadId: options.threadId,
226
+ runId: options.runId,
227
+ agentId: options.selectedAgentId,
228
+ content: runtimeFailure,
229
+ };
230
+ yield {
231
+ type: "result",
232
+ result: {
233
+ threadId: options.threadId,
234
+ runId: options.runId,
235
+ agentId: options.selectedAgentId,
236
+ state: "failed",
237
+ output: runtimeFailure,
238
+ finalMessageText: runtimeFailure,
239
+ },
240
+ };
241
+ return;
242
+ }
243
+ try {
244
+ const actual = await options.invokeWithHistory(options.binding, options.input, options.threadId, options.runId);
245
+ await options.appendAssistantMessage(options.threadId, options.runId, actual.output);
246
+ if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
247
+ yield createContentBlocksItem(options.threadId, options.runId, options.selectedAgentId, actual.contentBlocks);
248
+ }
249
+ if (actual.output) {
250
+ yield await emitOutputDelta(actual.output);
251
+ }
252
+ yield {
253
+ type: "result",
254
+ result: {
255
+ ...actual,
256
+ threadId: options.threadId,
257
+ runId: options.runId,
258
+ agentId: options.selectedAgentId,
259
+ },
260
+ };
261
+ yield {
262
+ type: "event",
263
+ event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, actual.state, {
264
+ previousState: "running",
265
+ }),
266
+ };
267
+ return;
268
+ }
269
+ catch (invokeError) {
270
+ await options.emitSyntheticFallback(options.threadId, options.runId, options.selectedAgentId, invokeError);
271
+ const runtimeFailure = renderRuntimeFailure(invokeError);
272
+ yield {
273
+ type: "event",
274
+ event: await options.setRunStateAndEmit(options.threadId, options.runId, 6, "failed", {
275
+ previousState: "running",
276
+ error: invokeError instanceof Error ? invokeError.message : String(invokeError),
277
+ }),
278
+ };
279
+ yield {
280
+ type: "content",
281
+ threadId: options.threadId,
282
+ runId: options.runId,
283
+ agentId: options.selectedAgentId,
284
+ content: runtimeFailure,
285
+ };
286
+ yield {
287
+ type: "result",
288
+ result: {
289
+ threadId: options.threadId,
290
+ runId: options.runId,
291
+ agentId: options.selectedAgentId,
292
+ state: "failed",
293
+ output: runtimeFailure,
294
+ finalMessageText: runtimeFailure,
295
+ },
296
+ };
297
+ return;
298
+ }
299
+ }
300
+ finally {
301
+ await options.clearRunRequest(options.threadId, options.runId);
302
+ await releaseRunSlot();
303
+ }
304
+ }
@@ -0,0 +1,21 @@
1
+ import type { ApprovalRecord, ThreadRecord, ThreadSummary } from "../../../contracts/types.js";
2
+ import type { RuntimePersistence } from "../../../persistence/types.js";
3
+ export declare function getThreadRecord(input: {
4
+ persistence: RuntimePersistence;
5
+ getSession: (threadId: string) => Promise<ThreadSummary | null>;
6
+ }, threadId: string): Promise<ThreadRecord | null>;
7
+ export declare function listPublicApprovals(input: {
8
+ persistence: RuntimePersistence;
9
+ }, filter?: {
10
+ status?: ApprovalRecord["status"];
11
+ threadId?: string;
12
+ runId?: string;
13
+ }): Promise<ApprovalRecord[]>;
14
+ export declare function getPublicApproval(input: {
15
+ persistence: RuntimePersistence;
16
+ }, approvalId: string): Promise<ApprovalRecord | null>;
17
+ export declare function deleteThreadRecord(input: {
18
+ getThread: (threadId: string) => Promise<ThreadRecord | null>;
19
+ deleteThread: (threadId: string) => Promise<boolean>;
20
+ deleteThreadCheckpoints: (threadId: string) => Promise<void>;
21
+ }, threadId: string): Promise<boolean>;
@@ -0,0 +1,59 @@
1
+ import { isTerminalRunState, toPublicApprovalRecord } from "./helpers.js";
2
+ export async function getThreadRecord(input, threadId) {
3
+ const [threadSummary, meta, messages, runs] = await Promise.all([
4
+ input.getSession(threadId),
5
+ input.persistence.getThreadMeta(threadId),
6
+ input.persistence.listThreadMessages(threadId, 200),
7
+ input.persistence.listThreadRuns(threadId),
8
+ ]);
9
+ if (!threadSummary || !meta) {
10
+ return null;
11
+ }
12
+ const latestRunId = threadSummary.latestRunId;
13
+ const latestApprovals = await input.persistence.getRunApprovals(threadId, latestRunId);
14
+ const pendingApproval = latestApprovals
15
+ .filter((approval) => approval.status === "pending")
16
+ .sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
17
+ return {
18
+ threadId,
19
+ entryAgentId: meta.entryAgentId,
20
+ currentState: threadSummary.status,
21
+ latestRunId,
22
+ createdAt: meta.createdAt,
23
+ updatedAt: threadSummary.updatedAt,
24
+ messages,
25
+ runs,
26
+ pendingDecision: pendingApproval
27
+ ? {
28
+ approvalId: pendingApproval.approvalId,
29
+ pendingActionId: pendingApproval.pendingActionId,
30
+ toolName: pendingApproval.toolName,
31
+ allowedDecisions: pendingApproval.allowedDecisions,
32
+ requestedAt: pendingApproval.requestedAt,
33
+ }
34
+ : undefined,
35
+ };
36
+ }
37
+ export async function listPublicApprovals(input, filter) {
38
+ const approvals = await input.persistence.listApprovals(filter);
39
+ return approvals.map((approval) => toPublicApprovalRecord(approval));
40
+ }
41
+ export async function getPublicApproval(input, approvalId) {
42
+ const approval = await input.persistence.getApproval(approvalId);
43
+ return approval ? toPublicApprovalRecord(approval) : null;
44
+ }
45
+ export async function deleteThreadRecord(input, threadId) {
46
+ const thread = await input.getThread(threadId);
47
+ if (!thread) {
48
+ return false;
49
+ }
50
+ const activeRun = thread.runs.find((run) => !isTerminalRunState(run.state));
51
+ if (activeRun) {
52
+ throw new Error(`Cannot delete thread ${threadId} while run ${activeRun.runId} is ${activeRun.state}`);
53
+ }
54
+ const deleted = await input.deleteThread(threadId);
55
+ if (deleted) {
56
+ await input.deleteThreadCheckpoints(threadId);
57
+ }
58
+ return deleted;
59
+ }