@botbotgo/agent-harness 0.0.251 → 0.0.252
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 -14
- package/README.zh.md +11 -12
- package/dist/api.d.ts +13 -6
- package/dist/api.js +70 -6
- package/dist/config/agents/direct.yaml +3 -3
- package/dist/config/agents/orchestra.yaml +3 -3
- package/dist/config/catalogs/stores.yaml +3 -9
- package/dist/config/runtime/workspace.yaml +1 -2
- package/dist/contracts/runtime.d.ts +9 -14
- package/dist/flow/build-flow-graph.js +198 -67
- package/dist/flow/export-mermaid.js +314 -4
- package/dist/flow/export-sequence-mermaid.js +149 -2
- package/dist/flow/types.d.ts +11 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.d.ts +3 -2
- package/dist/persistence/file-store.js +34 -8
- package/dist/persistence/sqlite-store.d.ts +2 -2
- package/dist/persistence/sqlite-store.js +64 -11
- package/dist/persistence/types.d.ts +3 -3
- package/dist/protocol/a2a/http.js +2 -4
- package/dist/resource/isolation.js +30 -2
- package/dist/runtime/harness/events/streaming.js +8 -8
- package/dist/runtime/harness/run/inspection.d.ts +2 -0
- package/dist/runtime/harness/run/inspection.js +91 -46
- package/dist/runtime/harness/run/stream-run.d.ts +2 -2
- package/dist/runtime/harness/run/stream-run.js +34 -23
- package/dist/runtime/harness/run/surface-semantics.d.ts +14 -0
- package/dist/runtime/harness/run/surface-semantics.js +106 -0
- package/dist/runtime/harness/run/thread-records.js +2 -34
- package/dist/runtime/harness/system/store.d.ts +6 -4
- package/dist/runtime/harness/system/store.js +76 -42
- package/dist/runtime/harness.js +5 -7
- package/dist/runtime/maintenance/checkpoint-maintenance.js +4 -119
- package/dist/runtime/maintenance/index.d.ts +0 -1
- package/dist/runtime/maintenance/index.js +0 -1
- package/dist/runtime/support/runtime-factories.js +2 -42
- package/dist/upstream-events.js +14 -0
- package/package.json +1 -3
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +0 -9
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +0 -39
- package/dist/runtime/support/sqlite-drivers.d.ts +0 -12
- package/dist/runtime/support/sqlite-drivers.js +0 -24
|
@@ -2,7 +2,7 @@ import { AGENT_INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError } from ".
|
|
|
2
2
|
import { renderRuntimeFailure, renderToolFailure } from "../../support/harness-support.js";
|
|
3
3
|
import { getBindingPrimaryModel } from "../../support/compiled-binding.js";
|
|
4
4
|
import { createContentBlocksItem, createToolResultKey, } from "../events/streaming.js";
|
|
5
|
-
import {
|
|
5
|
+
import { projectRuntimeSurfaceFromSingleUpstreamEvent } from "./inspection.js";
|
|
6
6
|
import { formatAgentName } from "../../../utils/agent-display.js";
|
|
7
7
|
function normalizeStreamChunk(chunk) {
|
|
8
8
|
if (typeof chunk === "string") {
|
|
@@ -73,44 +73,55 @@ export async function* streamHarnessRun(options) {
|
|
|
73
73
|
const normalizedChunk = normalizeStreamChunk(rawChunk);
|
|
74
74
|
if (normalizedChunk.kind === "upstream-event") {
|
|
75
75
|
upstreamEventOrdinal += 1;
|
|
76
|
+
const projectionBinding = options.getBinding(currentAgentId) ?? options.binding;
|
|
76
77
|
const surfaceProjection = projectRuntimeSurfaceFromSingleUpstreamEvent({
|
|
77
78
|
event: {
|
|
78
79
|
agentId: currentAgentId,
|
|
79
80
|
agentName: currentAgentName,
|
|
80
81
|
event: normalizedChunk.event,
|
|
81
82
|
},
|
|
82
|
-
binding:
|
|
83
|
+
binding: projectionBinding,
|
|
83
84
|
currentAgentId,
|
|
84
85
|
currentAgentName,
|
|
85
|
-
sourceEventId: `upstream:${upstreamEventOrdinal}`,
|
|
86
|
-
});
|
|
87
|
-
const inspection = consumeRunInspectionUpstreamEvent({
|
|
88
|
-
event: normalizedChunk.event,
|
|
89
|
-
currentAgentId,
|
|
90
86
|
delegationChain,
|
|
91
|
-
|
|
87
|
+
sourceEventId: `upstream:${upstreamEventOrdinal}`,
|
|
92
88
|
});
|
|
93
|
-
currentAgentId =
|
|
94
|
-
currentAgentName =
|
|
95
|
-
delegationChain =
|
|
89
|
+
currentAgentId = surfaceProjection.currentAgentId;
|
|
90
|
+
currentAgentName = surfaceProjection.currentAgentName;
|
|
91
|
+
delegationChain = surfaceProjection.delegationChain;
|
|
96
92
|
await options.updateRunInspection(options.threadId, options.runId, {
|
|
97
93
|
currentAgentId,
|
|
98
94
|
delegationChain,
|
|
99
|
-
|
|
95
|
+
});
|
|
96
|
+
if (surfaceProjection.items.length === 0) {
|
|
97
|
+
await options.appendRunTraceItem(options.threadId, options.runId, {
|
|
100
98
|
agentId: currentAgentId,
|
|
101
99
|
agentName: currentAgentName,
|
|
102
100
|
event: normalizedChunk.event,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
101
|
+
});
|
|
102
|
+
yield {
|
|
103
|
+
type: "upstream-event",
|
|
104
|
+
threadId: options.threadId,
|
|
105
|
+
runId: options.runId,
|
|
106
|
+
event: normalizedChunk.event,
|
|
107
|
+
};
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
for (const surfaceItem of surfaceProjection.items) {
|
|
111
|
+
await options.appendRunTraceItem(options.threadId, options.runId, {
|
|
112
|
+
agentId: surfaceItem.agentId ?? currentAgentId,
|
|
113
|
+
agentName: surfaceItem.agentName ?? currentAgentName,
|
|
114
|
+
surfaceItem,
|
|
115
|
+
event: normalizedChunk.event,
|
|
116
|
+
});
|
|
117
|
+
yield {
|
|
118
|
+
type: "upstream-event",
|
|
119
|
+
threadId: options.threadId,
|
|
120
|
+
runId: options.runId,
|
|
121
|
+
surfaceItem,
|
|
122
|
+
event: normalizedChunk.event,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
114
125
|
continue;
|
|
115
126
|
}
|
|
116
127
|
nonUpstreamStreamActivityObserved = true;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CompiledAgentBinding, RuntimeSurfaceKind } from "../../../contracts/types.js";
|
|
2
|
+
export declare function normalizeSurfaceToken(value: string): string;
|
|
3
|
+
export declare function buildSurfaceId(kind: RuntimeSurfaceKind, value: string): string;
|
|
4
|
+
export declare function stripStepPrefix(label: string): string;
|
|
5
|
+
export declare function resolveSurfaceDisplayName(input: {
|
|
6
|
+
kind: RuntimeSurfaceKind;
|
|
7
|
+
step: string;
|
|
8
|
+
binding?: CompiledAgentBinding;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function resolveSurfaceAction(input: {
|
|
11
|
+
kind: RuntimeSurfaceKind;
|
|
12
|
+
step?: string;
|
|
13
|
+
event?: unknown;
|
|
14
|
+
}): string;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readSkillMetadata } from "../../support/skill-metadata.js";
|
|
2
|
+
import { formatAgentName } from "../../../utils/agent-display.js";
|
|
3
|
+
import { getBindingMemorySources, getBindingSkills, } from "../../support/compiled-binding.js";
|
|
4
|
+
function asObject(value) {
|
|
5
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
6
|
+
}
|
|
7
|
+
function readStringArray(value) {
|
|
8
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
9
|
+
}
|
|
10
|
+
function normalizeLabel(value) {
|
|
11
|
+
return value.replace(/\s+/g, " ").trim();
|
|
12
|
+
}
|
|
13
|
+
export function normalizeSurfaceToken(value) {
|
|
14
|
+
return value
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
17
|
+
.replace(/^-+|-+$/g, "")
|
|
18
|
+
.slice(0, 80) || "item";
|
|
19
|
+
}
|
|
20
|
+
export function buildSurfaceId(kind, value) {
|
|
21
|
+
void kind;
|
|
22
|
+
return normalizeSurfaceToken(value);
|
|
23
|
+
}
|
|
24
|
+
export function stripStepPrefix(label) {
|
|
25
|
+
return normalizeLabel(label)
|
|
26
|
+
.replace(/^Calling LLM\s+/i, "")
|
|
27
|
+
.replace(/^Completed LLM\s+/i, "")
|
|
28
|
+
.replace(/^Calling tool\s+/i, "")
|
|
29
|
+
.replace(/^Completed tool\s+/i, "")
|
|
30
|
+
.replace(/^Tool\s+/i, "")
|
|
31
|
+
.replace(/\s+failed$/i, "")
|
|
32
|
+
.replace(/^Calling skill\s+/i, "")
|
|
33
|
+
.replace(/^Completed skill\s+/i, "")
|
|
34
|
+
.replace(/^Accessing memory\s+/i, "")
|
|
35
|
+
.replace(/^Completed memory\s+/i, "");
|
|
36
|
+
}
|
|
37
|
+
function isGenericMemoryMiddlewareName(value) {
|
|
38
|
+
return /^(memorymiddleware\.before[_\s]?agent|memory middleware before agent)$/i.test(normalizeLabel(value));
|
|
39
|
+
}
|
|
40
|
+
function canonicalMemoryName(memorySource) {
|
|
41
|
+
const normalized = normalizeLabel(memorySource.replace(/^memory\//i, ""));
|
|
42
|
+
const segments = normalized.split(/[\\/]/).filter(Boolean);
|
|
43
|
+
return (segments.at(-1) ?? normalized).toLowerCase() || "memory";
|
|
44
|
+
}
|
|
45
|
+
function isGenericSkillMiddlewareName(value) {
|
|
46
|
+
return /^(skillsmiddleware\.before[_\s]?agent|skills middleware before agent)$/i.test(normalizeLabel(value));
|
|
47
|
+
}
|
|
48
|
+
export function resolveSurfaceDisplayName(input) {
|
|
49
|
+
const baseName = stripStepPrefix(input.step) || formatAgentName(input.kind);
|
|
50
|
+
if (input.kind === "memory" && input.binding && isGenericMemoryMiddlewareName(baseName)) {
|
|
51
|
+
const memorySources = getBindingMemorySources(input.binding).filter((name) => name.trim().length > 0);
|
|
52
|
+
return memorySources.length === 1 ? canonicalMemoryName(memorySources[0]) : baseName;
|
|
53
|
+
}
|
|
54
|
+
if (input.kind !== "skill" || !input.binding || !isGenericSkillMiddlewareName(baseName)) {
|
|
55
|
+
return baseName;
|
|
56
|
+
}
|
|
57
|
+
const skillNames = getBindingSkills(input.binding)
|
|
58
|
+
.map((skillPath) => readSkillMetadata(skillPath).name)
|
|
59
|
+
.filter((name) => typeof name === "string" && name.trim().length > 0);
|
|
60
|
+
return skillNames.length === 1 ? skillNames[0] : baseName;
|
|
61
|
+
}
|
|
62
|
+
function normalizeActionHint(value) {
|
|
63
|
+
return value
|
|
64
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
65
|
+
.replace(/[.:/]+/g, " ")
|
|
66
|
+
.replace(/[_-]+/g, " ")
|
|
67
|
+
.replace(/\s+/g, " ")
|
|
68
|
+
.trim()
|
|
69
|
+
.toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
function resolveMemoryAction(event, step) {
|
|
72
|
+
const typed = asObject(event);
|
|
73
|
+
const hints = [
|
|
74
|
+
typeof typed?.event === "string" ? typed.event : "",
|
|
75
|
+
typeof typed?.name === "string" ? typed.name : "",
|
|
76
|
+
typeof typed?.run_type === "string" ? typed.run_type : "",
|
|
77
|
+
...readStringArray(typed?.tags),
|
|
78
|
+
...readStringArray(typed?.ns),
|
|
79
|
+
step,
|
|
80
|
+
].map(normalizeActionHint);
|
|
81
|
+
if (hints.some((value) => /\b(store|memorize|sync|formation|ingestion|archive|write)\b/.test(value))) {
|
|
82
|
+
return "memorize";
|
|
83
|
+
}
|
|
84
|
+
if (hints.some((value) => /\b(recall|retrieve|memorymiddleware|before agent|checkpoint|context)\b/.test(value)
|
|
85
|
+
|| value.includes("accessing memory")
|
|
86
|
+
|| value.includes("access memory"))) {
|
|
87
|
+
return "recall";
|
|
88
|
+
}
|
|
89
|
+
return "access";
|
|
90
|
+
}
|
|
91
|
+
export function resolveSurfaceAction(input) {
|
|
92
|
+
switch (input.kind) {
|
|
93
|
+
case "agent":
|
|
94
|
+
return "handoff";
|
|
95
|
+
case "llm":
|
|
96
|
+
return "call";
|
|
97
|
+
case "tool":
|
|
98
|
+
return "execute";
|
|
99
|
+
case "skill":
|
|
100
|
+
return "apply";
|
|
101
|
+
case "memory":
|
|
102
|
+
return resolveMemoryAction(input.event, input.step ?? "");
|
|
103
|
+
default:
|
|
104
|
+
return "run";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,41 +1,13 @@
|
|
|
1
1
|
import { isTerminalRunState, toInspectableApprovalRecord } from "./helpers.js";
|
|
2
2
|
import { projectRuntimeTimeline } from "../events/timeline.js";
|
|
3
|
-
import { createUpstreamTimelineReducer } from "../../../upstream-events.js";
|
|
4
|
-
import { projectRuntimeSurfaceFromUpstreamEvents } from "./inspection.js";
|
|
5
|
-
function unwrapPersistedUpstreamEvent(event) {
|
|
6
|
-
if (typeof event !== "object" || event === null || Array.isArray(event)) {
|
|
7
|
-
return event;
|
|
8
|
-
}
|
|
9
|
-
const typed = event;
|
|
10
|
-
if (Object.prototype.hasOwnProperty.call(typed, "event")
|
|
11
|
-
&& typeof typed.event === "object"
|
|
12
|
-
&& typed.event !== null
|
|
13
|
-
&& !Array.isArray(typed.event)) {
|
|
14
|
-
return typed.event;
|
|
15
|
-
}
|
|
16
|
-
return event;
|
|
17
|
-
}
|
|
18
3
|
function selectLatestPendingApproval(approvals) {
|
|
19
4
|
return approvals
|
|
20
5
|
.filter((approval) => approval.status === "pending")
|
|
21
6
|
.sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
|
|
22
7
|
}
|
|
23
|
-
function buildRunInspectionProjection(upstreamEvents) {
|
|
24
|
-
const reducer = createUpstreamTimelineReducer();
|
|
25
|
-
const history = upstreamEvents.flatMap((event) => reducer.consume(unwrapPersistedUpstreamEvent(event)));
|
|
26
|
-
return {
|
|
27
|
-
upstreamEvents,
|
|
28
|
-
history,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
8
|
export async function buildRequestInspectionRecord(persistence, request) {
|
|
32
9
|
const inspection = await persistence.getRunInspection(request.threadId, request.runId);
|
|
33
10
|
const runtimeEvents = await persistence.listRunEvents(request.threadId, request.runId);
|
|
34
|
-
const { upstreamEvents, history } = buildRunInspectionProjection(inspection.upstreamEvents);
|
|
35
|
-
const runtimeSurface = projectRuntimeSurfaceFromUpstreamEvents({
|
|
36
|
-
upstreamEvents,
|
|
37
|
-
initialAgentId: request.agentId ?? inspection.currentAgentId ?? "agent",
|
|
38
|
-
});
|
|
39
11
|
return {
|
|
40
12
|
requestId: request.runId,
|
|
41
13
|
sessionId: request.threadId,
|
|
@@ -54,9 +26,7 @@ export async function buildRequestInspectionRecord(persistence, request) {
|
|
|
54
26
|
currentAgentId: request.currentAgentId,
|
|
55
27
|
delegationChain: request.delegationChain,
|
|
56
28
|
runtimeSnapshot: request.runtimeSnapshot,
|
|
57
|
-
|
|
58
|
-
history,
|
|
59
|
-
runtimeSurface,
|
|
29
|
+
traceItems: inspection.traceItems,
|
|
60
30
|
runtimeTimeline: projectRuntimeTimeline(runtimeEvents, {
|
|
61
31
|
threadId: request.threadId,
|
|
62
32
|
runId: request.runId,
|
|
@@ -82,9 +52,7 @@ function toRunRecord(request) {
|
|
|
82
52
|
currentAgentId: request.currentAgentId,
|
|
83
53
|
delegationChain: request.delegationChain,
|
|
84
54
|
runtimeSnapshot: request.runtimeSnapshot,
|
|
85
|
-
|
|
86
|
-
history: request.history,
|
|
87
|
-
runtimeSurface: request.runtimeSurface,
|
|
55
|
+
traceItems: request.traceItems,
|
|
88
56
|
runtimeTimeline: request.runtimeTimeline,
|
|
89
57
|
};
|
|
90
58
|
}
|
|
@@ -51,8 +51,13 @@ export declare class FileBackedStore {
|
|
|
51
51
|
}
|
|
52
52
|
export declare class SqliteBackedStore {
|
|
53
53
|
readonly filePath: string;
|
|
54
|
-
private
|
|
54
|
+
private client;
|
|
55
|
+
private initialized;
|
|
56
|
+
private initialization;
|
|
55
57
|
constructor(filePath: string);
|
|
58
|
+
private getClient;
|
|
59
|
+
private ensureInitialized;
|
|
60
|
+
private selectAll;
|
|
56
61
|
batch(operations: Array<Record<string, unknown>>): Promise<readonly unknown[]>;
|
|
57
62
|
get(namespace: string[], key: string): Promise<{
|
|
58
63
|
value: unknown;
|
|
@@ -61,7 +66,6 @@ export declare class SqliteBackedStore {
|
|
|
61
66
|
createdAt: Date;
|
|
62
67
|
updatedAt: Date;
|
|
63
68
|
} | null>;
|
|
64
|
-
private getSync;
|
|
65
69
|
search(namespacePrefix: string[]): Promise<Array<{
|
|
66
70
|
value: unknown;
|
|
67
71
|
key: string;
|
|
@@ -71,9 +75,7 @@ export declare class SqliteBackedStore {
|
|
|
71
75
|
score?: number;
|
|
72
76
|
}>>;
|
|
73
77
|
put(namespace: string[], key: string, value: Record<string, any>, _index?: false | string[]): Promise<void>;
|
|
74
|
-
private putSync;
|
|
75
78
|
delete(namespace: string[], key: string): Promise<void>;
|
|
76
|
-
private deleteSync;
|
|
77
79
|
listNamespaces(): Promise<string[][]>;
|
|
78
80
|
}
|
|
79
81
|
export declare function createInMemoryStore(): StoreLike;
|
|
@@ -2,7 +2,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { mkdirSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { InMemoryStore } from "@langchain/langgraph";
|
|
5
|
-
import {
|
|
5
|
+
import { createClient } from "@libsql/client";
|
|
6
6
|
const NAMESPACE_SEPARATOR = "\u001f";
|
|
7
7
|
function encodeValue(value) {
|
|
8
8
|
if (value instanceof Date) {
|
|
@@ -148,83 +148,117 @@ export class FileBackedStore {
|
|
|
148
148
|
}
|
|
149
149
|
export class SqliteBackedStore {
|
|
150
150
|
filePath;
|
|
151
|
-
|
|
151
|
+
client = null;
|
|
152
|
+
initialized = false;
|
|
153
|
+
initialization = null;
|
|
152
154
|
constructor(filePath) {
|
|
153
155
|
this.filePath = filePath;
|
|
154
156
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
this.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
157
|
+
}
|
|
158
|
+
async getClient() {
|
|
159
|
+
if (!this.client) {
|
|
160
|
+
this.client = createClient({ url: `file:${this.filePath}` });
|
|
161
|
+
}
|
|
162
|
+
return this.client;
|
|
163
|
+
}
|
|
164
|
+
async ensureInitialized() {
|
|
165
|
+
if (this.initialized) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (this.initialization) {
|
|
169
|
+
await this.initialization;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.initialization = (async () => {
|
|
173
|
+
const client = await this.getClient();
|
|
174
|
+
await client.execute("PRAGMA journal_mode=WAL");
|
|
175
|
+
await client.execute("PRAGMA foreign_keys=ON");
|
|
176
|
+
await client.execute("PRAGMA busy_timeout=5000");
|
|
177
|
+
await client.execute(`
|
|
178
|
+
CREATE TABLE IF NOT EXISTS store_entries (
|
|
179
|
+
namespace TEXT NOT NULL,
|
|
180
|
+
namespace_json TEXT NOT NULL,
|
|
181
|
+
key TEXT NOT NULL,
|
|
182
|
+
value_json TEXT NOT NULL,
|
|
183
|
+
created_at TEXT NOT NULL,
|
|
184
|
+
updated_at TEXT NOT NULL,
|
|
185
|
+
PRIMARY KEY (namespace, key)
|
|
186
|
+
)
|
|
187
|
+
`);
|
|
188
|
+
await client.execute("CREATE INDEX IF NOT EXISTS idx_store_entries_namespace ON store_entries(namespace)");
|
|
189
|
+
this.initialized = true;
|
|
190
|
+
this.initialization = null;
|
|
191
|
+
})();
|
|
192
|
+
await this.initialization;
|
|
193
|
+
}
|
|
194
|
+
async selectAll(sql, args = []) {
|
|
195
|
+
await this.ensureInitialized();
|
|
196
|
+
const client = await this.getClient();
|
|
197
|
+
const result = await client.execute({ sql, args });
|
|
198
|
+
return result.rows;
|
|
170
199
|
}
|
|
171
200
|
async batch(operations) {
|
|
172
|
-
const results =
|
|
201
|
+
const results = [];
|
|
202
|
+
for (const operation of operations) {
|
|
173
203
|
const namespace = Array.isArray(operation.namespace) ? operation.namespace.filter((item) => typeof item === "string") : [];
|
|
174
204
|
const key = typeof operation.key === "string" ? operation.key : "";
|
|
175
205
|
if ("value" in operation) {
|
|
176
|
-
this.
|
|
177
|
-
|
|
206
|
+
await this.put(namespace, key, operation.value);
|
|
207
|
+
results.push(undefined);
|
|
208
|
+
continue;
|
|
178
209
|
}
|
|
179
210
|
if (operation.delete === true) {
|
|
180
|
-
this.
|
|
181
|
-
|
|
211
|
+
await this.delete(namespace, key);
|
|
212
|
+
results.push(undefined);
|
|
213
|
+
continue;
|
|
182
214
|
}
|
|
183
|
-
|
|
184
|
-
}
|
|
215
|
+
results.push(await this.get(namespace, key));
|
|
216
|
+
}
|
|
185
217
|
return results;
|
|
186
218
|
}
|
|
187
219
|
async get(namespace, key) {
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
getSync(namespace, key) {
|
|
191
|
-
const row = this.db.prepare(`SELECT namespace, namespace_json, key, value_json, created_at, updated_at
|
|
220
|
+
const rows = await this.selectAll(`SELECT namespace, namespace_json, key, value_json, created_at, updated_at
|
|
192
221
|
FROM store_entries
|
|
193
|
-
WHERE namespace = ? AND key =
|
|
222
|
+
WHERE namespace = ? AND key = ?`, [serializeNamespace(namespace), key]);
|
|
223
|
+
const row = rows[0];
|
|
194
224
|
return row ? parseRow(row) : null;
|
|
195
225
|
}
|
|
196
226
|
async search(namespacePrefix) {
|
|
197
227
|
const prefix = serializeNamespace(namespacePrefix);
|
|
198
|
-
const rows = this.
|
|
228
|
+
const rows = await this.selectAll(`SELECT namespace, namespace_json, key, value_json, created_at, updated_at
|
|
199
229
|
FROM store_entries
|
|
200
230
|
WHERE namespace = ? OR namespace LIKE ?
|
|
201
|
-
ORDER BY namespace ASC, key ASC
|
|
231
|
+
ORDER BY namespace ASC, key ASC`, [prefix, `${prefix}${NAMESPACE_SEPARATOR}%`]);
|
|
202
232
|
return rows.map((row) => parseRow(row));
|
|
203
233
|
}
|
|
204
234
|
async put(namespace, key, value, _index) {
|
|
205
|
-
this.
|
|
206
|
-
|
|
207
|
-
putSync(namespace, key, value) {
|
|
235
|
+
await this.ensureInitialized();
|
|
236
|
+
const client = await this.getClient();
|
|
208
237
|
const now = new Date().toISOString();
|
|
209
238
|
const serializedNamespace = serializeNamespace(namespace);
|
|
210
239
|
const encodedValue = JSON.stringify(encodeValue(value));
|
|
211
|
-
|
|
240
|
+
await client.execute({
|
|
241
|
+
sql: `INSERT INTO store_entries (namespace, namespace_json, key, value_json, created_at, updated_at)
|
|
212
242
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
213
243
|
ON CONFLICT(namespace, key) DO UPDATE SET
|
|
214
244
|
namespace_json = excluded.namespace_json,
|
|
215
245
|
value_json = excluded.value_json,
|
|
216
|
-
updated_at = excluded.updated_at
|
|
246
|
+
updated_at = excluded.updated_at`,
|
|
247
|
+
args: [serializedNamespace, JSON.stringify(namespace), key, encodedValue, now, now],
|
|
248
|
+
});
|
|
217
249
|
}
|
|
218
250
|
async delete(namespace, key) {
|
|
219
|
-
this.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
251
|
+
await this.ensureInitialized();
|
|
252
|
+
const client = await this.getClient();
|
|
253
|
+
await client.execute({
|
|
254
|
+
sql: `DELETE FROM store_entries WHERE namespace = ? AND key = ?`,
|
|
255
|
+
args: [serializeNamespace(namespace), key],
|
|
256
|
+
});
|
|
223
257
|
}
|
|
224
258
|
async listNamespaces() {
|
|
225
|
-
const rows = this.
|
|
259
|
+
const rows = await this.selectAll(`SELECT DISTINCT namespace_json
|
|
226
260
|
FROM store_entries
|
|
227
|
-
ORDER BY namespace ASC`)
|
|
261
|
+
ORDER BY namespace ASC`);
|
|
228
262
|
return rows.map((row) => JSON.parse(row.namespace_json));
|
|
229
263
|
}
|
|
230
264
|
}
|
package/dist/runtime/harness.js
CHANGED
|
@@ -618,8 +618,7 @@ export class AgentHarnessRuntime {
|
|
|
618
618
|
currentAgentId: request.currentAgentId,
|
|
619
619
|
delegationChain: request.delegationChain,
|
|
620
620
|
runtimeSnapshot: request.runtimeSnapshot,
|
|
621
|
-
|
|
622
|
-
history: request.history,
|
|
621
|
+
traceItems: request.traceItems,
|
|
623
622
|
runtimeTimeline: request.runtimeTimeline,
|
|
624
623
|
}
|
|
625
624
|
: null;
|
|
@@ -687,8 +686,7 @@ export class AgentHarnessRuntime {
|
|
|
687
686
|
currentAgentId: request.currentAgentId,
|
|
688
687
|
delegationChain: request.delegationChain,
|
|
689
688
|
runtimeSnapshot: request.runtimeSnapshot,
|
|
690
|
-
|
|
691
|
-
history: request.history,
|
|
689
|
+
traceItems: request.traceItems,
|
|
692
690
|
runtimeTimeline: request.runtimeTimeline,
|
|
693
691
|
})),
|
|
694
692
|
pendingDecision: session.pendingDecision,
|
|
@@ -1662,6 +1660,7 @@ export class AgentHarnessRuntime {
|
|
|
1662
1660
|
isNewThread,
|
|
1663
1661
|
runCreatedEventPromise,
|
|
1664
1662
|
releaseRunSlotPromise,
|
|
1663
|
+
getBinding: (agentId) => getWorkspaceBinding(this.workspace, agentId),
|
|
1665
1664
|
loadPriorHistory: (threadId, runId) => this.loadPriorHistory(threadId, runId),
|
|
1666
1665
|
stream: (binding, message, threadId, priorHistory, streamOptions) => this.runtimeAdapter.stream(binding, message, threadId, priorHistory, streamOptions),
|
|
1667
1666
|
invokeWithHistory: (binding, input, threadId, runId) => this.invokeWithHistory(binding, input, threadId, runId),
|
|
@@ -1671,6 +1670,7 @@ export class AgentHarnessRuntime {
|
|
|
1671
1670
|
appendAssistantMessage: (threadId, runId, content) => appendLifecycleAssistantMessage(this.persistence, threadId, runId, content),
|
|
1672
1671
|
clearRunRequest: (threadId, runId) => this.persistence.clearRunRequest(threadId, runId),
|
|
1673
1672
|
updateRunInspection: (threadId, runId, patch) => this.persistence.updateRunInspection(threadId, runId, patch),
|
|
1673
|
+
appendRunTraceItem: (threadId, runId, item) => this.persistence.appendRunTraceItem(threadId, runId, item),
|
|
1674
1674
|
emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
|
|
1675
1675
|
});
|
|
1676
1676
|
for await (const item of stream) {
|
|
@@ -1837,9 +1837,7 @@ function toSessionRecord(record) {
|
|
|
1837
1837
|
function toRequestRecord(record) {
|
|
1838
1838
|
return {
|
|
1839
1839
|
...toRequestSummary(record),
|
|
1840
|
-
|
|
1841
|
-
history: record.history,
|
|
1842
|
-
runtimeSurface: record.runtimeSurface,
|
|
1840
|
+
traceItems: record.traceItems,
|
|
1843
1841
|
runtimeTimeline: record.runtimeTimeline,
|
|
1844
1842
|
};
|
|
1845
1843
|
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
|
|
3
|
-
import { listProtectedCheckpointThreadIds } from "../../persistence/sqlite-store.js";
|
|
4
2
|
import { fileExists } from "../../utils/fs.js";
|
|
5
3
|
import { getRuntimeDefaults } from "../../workspace/support/workspace-ref-utils.js";
|
|
6
|
-
import { ManagedSqliteSaver } from "./sqlite-maintained-checkpoint-saver.js";
|
|
7
4
|
function asObject(value) {
|
|
8
5
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
9
6
|
}
|
|
@@ -72,128 +69,16 @@ export function discoverCheckpointMaintenanceTargets(workspace) {
|
|
|
72
69
|
}
|
|
73
70
|
return Array.from(deduped.values());
|
|
74
71
|
}
|
|
75
|
-
function backfillCheckpointMetadata(db, nowMs = Date.now()) {
|
|
76
|
-
db.exec(`
|
|
77
|
-
CREATE TABLE IF NOT EXISTS checkpoint_maintenance_meta (
|
|
78
|
-
thread_id TEXT NOT NULL,
|
|
79
|
-
checkpoint_ns TEXT NOT NULL DEFAULT '',
|
|
80
|
-
checkpoint_id TEXT NOT NULL,
|
|
81
|
-
created_at_ms INTEGER NOT NULL,
|
|
82
|
-
PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
|
|
83
|
-
);`);
|
|
84
|
-
db.prepare(`INSERT OR IGNORE INTO checkpoint_maintenance_meta
|
|
85
|
-
(thread_id, checkpoint_ns, checkpoint_id, created_at_ms)
|
|
86
|
-
SELECT thread_id, checkpoint_ns, checkpoint_id, ?
|
|
87
|
-
FROM checkpoints`).run(nowMs);
|
|
88
|
-
}
|
|
89
|
-
function deleteCheckpointRows(db, rows) {
|
|
90
|
-
if (rows.length === 0) {
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
const deleteCheckpoint = db.prepare(`DELETE FROM checkpoints
|
|
94
|
-
WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
|
|
95
|
-
const deleteWrites = db.prepare(`DELETE FROM writes
|
|
96
|
-
WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
|
|
97
|
-
const deleteMeta = db.prepare(`DELETE FROM checkpoint_maintenance_meta
|
|
98
|
-
WHERE thread_id = ? AND checkpoint_ns = ? AND checkpoint_id = ?`);
|
|
99
|
-
db.transaction((items) => {
|
|
100
|
-
for (const row of items) {
|
|
101
|
-
deleteCheckpoint.run(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
|
|
102
|
-
deleteWrites.run(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
|
|
103
|
-
deleteMeta.run(row.thread_id, row.checkpoint_ns, row.checkpoint_id);
|
|
104
|
-
}
|
|
105
|
-
})(rows);
|
|
106
|
-
return rows.length;
|
|
107
|
-
}
|
|
108
|
-
function selectOldestRows(db, limit) {
|
|
109
|
-
return db
|
|
110
|
-
.prepare(`SELECT
|
|
111
|
-
meta.thread_id,
|
|
112
|
-
meta.checkpoint_ns,
|
|
113
|
-
meta.checkpoint_id,
|
|
114
|
-
meta.created_at_ms,
|
|
115
|
-
COALESCE(LENGTH(cp.checkpoint), 0) + COALESCE(LENGTH(cp.metadata), 0) + COALESCE(SUM(LENGTH(w.value)), 0) AS size_bytes
|
|
116
|
-
FROM checkpoint_maintenance_meta AS meta
|
|
117
|
-
JOIN checkpoints AS cp
|
|
118
|
-
ON cp.thread_id = meta.thread_id
|
|
119
|
-
AND cp.checkpoint_ns = meta.checkpoint_ns
|
|
120
|
-
AND cp.checkpoint_id = meta.checkpoint_id
|
|
121
|
-
LEFT JOIN writes AS w
|
|
122
|
-
ON w.thread_id = meta.thread_id
|
|
123
|
-
AND w.checkpoint_ns = meta.checkpoint_ns
|
|
124
|
-
AND w.checkpoint_id = meta.checkpoint_id
|
|
125
|
-
GROUP BY meta.thread_id, meta.checkpoint_ns, meta.checkpoint_id, meta.created_at_ms, cp.checkpoint, cp.metadata
|
|
126
|
-
ORDER BY meta.created_at_ms ASC, meta.checkpoint_id ASC
|
|
127
|
-
LIMIT ?`)
|
|
128
|
-
.all(limit);
|
|
129
|
-
}
|
|
130
|
-
function totalCheckpointBytes(db) {
|
|
131
|
-
const checkpointsBytes = db
|
|
132
|
-
.prepare(`SELECT COALESCE(SUM(LENGTH(checkpoint) + LENGTH(metadata)), 0) AS total FROM checkpoints`)
|
|
133
|
-
.get();
|
|
134
|
-
const writesBytes = db
|
|
135
|
-
.prepare(`SELECT COALESCE(SUM(LENGTH(value)), 0) AS total FROM writes`)
|
|
136
|
-
.get();
|
|
137
|
-
return Number(checkpointsBytes.total ?? 0) + Number(writesBytes.total ?? 0);
|
|
138
|
-
}
|
|
139
72
|
export function maintainSqliteCheckpoints(dbPath, config, nowMs = Date.now()) {
|
|
140
73
|
return maintainSqliteCheckpointsInternal(dbPath, config, nowMs);
|
|
141
74
|
}
|
|
142
|
-
async function maintainSqliteCheckpointsInternal(dbPath, config,
|
|
75
|
+
async function maintainSqliteCheckpointsInternal(dbPath, config, _nowMs) {
|
|
143
76
|
if (!(await fileExists(dbPath))) {
|
|
144
77
|
return { deletedCount: 0 };
|
|
145
78
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
saver.prepareMaintenance();
|
|
150
|
-
backfillCheckpointMetadata(db, nowMs);
|
|
151
|
-
const protectedThreadIds = await listProtectedCheckpointThreadIds(path.join(path.dirname(dbPath), "runtime.sqlite"));
|
|
152
|
-
let deletedCount = 0;
|
|
153
|
-
if (config.policies.maxAgeSeconds !== undefined) {
|
|
154
|
-
const cutoffMs = nowMs - config.policies.maxAgeSeconds * 1000;
|
|
155
|
-
const expired = db
|
|
156
|
-
.prepare(`SELECT
|
|
157
|
-
meta.thread_id,
|
|
158
|
-
meta.checkpoint_ns,
|
|
159
|
-
meta.checkpoint_id,
|
|
160
|
-
meta.created_at_ms,
|
|
161
|
-
0 AS size_bytes
|
|
162
|
-
FROM checkpoint_maintenance_meta AS meta
|
|
163
|
-
WHERE meta.created_at_ms <= ?
|
|
164
|
-
ORDER BY meta.created_at_ms ASC, meta.checkpoint_id ASC
|
|
165
|
-
LIMIT ?`)
|
|
166
|
-
.all(cutoffMs, config.sqlite.sweepBatchSize * 4);
|
|
167
|
-
deletedCount += deleteCheckpointRows(db, expired.filter((row) => !protectedThreadIds.has(row.thread_id)).slice(0, config.sqlite.sweepBatchSize));
|
|
168
|
-
}
|
|
169
|
-
if (config.policies.maxBytes !== undefined) {
|
|
170
|
-
let currentBytes = totalCheckpointBytes(db);
|
|
171
|
-
while (currentBytes > config.policies.maxBytes) {
|
|
172
|
-
const oldest = selectOldestRows(db, config.sqlite.sweepBatchSize * 4).filter((row) => !protectedThreadIds.has(row.thread_id));
|
|
173
|
-
if (oldest.length === 0) {
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
let reclaimed = 0;
|
|
177
|
-
const toDelete = [];
|
|
178
|
-
for (const row of oldest) {
|
|
179
|
-
toDelete.push(row);
|
|
180
|
-
reclaimed += Number(row.size_bytes ?? 0);
|
|
181
|
-
if (currentBytes - reclaimed <= config.policies.maxBytes) {
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
deletedCount += deleteCheckpointRows(db, toDelete);
|
|
186
|
-
currentBytes = totalCheckpointBytes(db);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (deletedCount > 0 && config.sqlite.vacuum) {
|
|
190
|
-
db.exec("VACUUM");
|
|
191
|
-
}
|
|
192
|
-
return { deletedCount };
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
db.close();
|
|
196
|
-
}
|
|
79
|
+
void config;
|
|
80
|
+
void path.dirname(dbPath);
|
|
81
|
+
throw new Error("Checkpoint maintenance for SqliteSaver is not supported in this runtime right now");
|
|
197
82
|
}
|
|
198
83
|
export class CheckpointMaintenanceLoop {
|
|
199
84
|
targets;
|