@botbotgo/agent-harness 0.0.75 → 0.0.77
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/dist/api.d.ts +2 -1
- package/dist/api.js +3 -0
- package/dist/benchmark/checkpoint-resume-cost-benchmark.d.ts +33 -0
- package/dist/benchmark/checkpoint-resume-cost-benchmark.js +55 -0
- package/dist/benchmark/deepagent-local-model-benchmark.d.ts +27 -0
- package/dist/benchmark/deepagent-local-model-benchmark.js +35 -0
- package/dist/config/agents/direct.yaml +1 -1
- package/dist/config/agents/orchestra.yaml +1 -2
- package/dist/config/workspace.yaml +31 -0
- package/dist/contracts/types.d.ts +38 -1
- package/dist/index.d.ts +1 -1
- 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 -40
- package/dist/persistence/file-store.js +5 -2
- package/dist/persistence/sqlite-store.d.ts +68 -0
- package/dist/persistence/sqlite-store.js +569 -0
- package/dist/persistence/types.d.ts +83 -0
- package/dist/persistence/types.js +1 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +3 -0
- package/dist/runtime/agent-runtime-adapter.js +58 -2
- package/dist/runtime/checkpoint-maintenance.d.ts +11 -2
- package/dist/runtime/checkpoint-maintenance.js +41 -5
- package/dist/runtime/harness.d.ts +5 -1
- package/dist/runtime/harness.js +67 -4
- package/dist/runtime/health-monitor.d.ts +81 -0
- package/dist/runtime/health-monitor.js +448 -0
- package/dist/runtime/runtime-record-maintenance.d.ts +43 -0
- package/dist/runtime/runtime-record-maintenance.js +169 -0
- package/dist/runtime/store.d.ts +2 -0
- package/dist/runtime/store.js +38 -20
- package/dist/runtime/support/embedding-models.js +57 -1
- package/dist/runtime/thread-memory-sync.d.ts +3 -2
- package/dist/runtime/thread-memory-sync.js +7 -1
- package/dist/workspace/agent-binding-compiler.js +3 -1
- package/dist/workspace/support/workspace-ref-utils.d.ts +9 -0
- package/dist/workspace/support/workspace-ref-utils.js +38 -0
- package/package.json +2 -2
package/dist/runtime/store.js
CHANGED
|
@@ -54,9 +54,15 @@ export class FileBackedStore {
|
|
|
54
54
|
filePath;
|
|
55
55
|
delegate = new InMemoryStore();
|
|
56
56
|
loaded = false;
|
|
57
|
+
operationChain = Promise.resolve();
|
|
57
58
|
constructor(filePath) {
|
|
58
59
|
this.filePath = filePath;
|
|
59
60
|
}
|
|
61
|
+
async runSerialized(operation) {
|
|
62
|
+
const pending = this.operationChain.then(operation, operation);
|
|
63
|
+
this.operationChain = pending.then(() => undefined, () => undefined);
|
|
64
|
+
return pending;
|
|
65
|
+
}
|
|
60
66
|
async ensureLoaded() {
|
|
61
67
|
if (this.loaded) {
|
|
62
68
|
return;
|
|
@@ -81,36 +87,48 @@ export class FileBackedStore {
|
|
|
81
87
|
}, null, 2), "utf8");
|
|
82
88
|
}
|
|
83
89
|
async batch(operations) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
return this.runSerialized(async () => {
|
|
91
|
+
await this.ensureLoaded();
|
|
92
|
+
const result = await this.delegate.batch(operations);
|
|
93
|
+
if (operations.some((operation) => "value" in operation)) {
|
|
94
|
+
await this.persist();
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
});
|
|
90
98
|
}
|
|
91
99
|
async get(namespace, key) {
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
return this.runSerialized(async () => {
|
|
101
|
+
await this.ensureLoaded();
|
|
102
|
+
return this.delegate.get(namespace, key);
|
|
103
|
+
});
|
|
94
104
|
}
|
|
95
105
|
async search(namespacePrefix, options) {
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
return this.runSerialized(async () => {
|
|
107
|
+
await this.ensureLoaded();
|
|
108
|
+
return this.delegate.search(namespacePrefix, options);
|
|
109
|
+
});
|
|
98
110
|
}
|
|
99
111
|
async put(namespace, key, value, index) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
return this.runSerialized(async () => {
|
|
113
|
+
await this.ensureLoaded();
|
|
114
|
+
const result = await this.delegate.put(namespace, key, value, index);
|
|
115
|
+
await this.persist();
|
|
116
|
+
return result;
|
|
117
|
+
});
|
|
104
118
|
}
|
|
105
119
|
async delete(namespace, key) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
return this.runSerialized(async () => {
|
|
121
|
+
await this.ensureLoaded();
|
|
122
|
+
const result = await this.delegate.delete(namespace, key);
|
|
123
|
+
await this.persist();
|
|
124
|
+
return result;
|
|
125
|
+
});
|
|
110
126
|
}
|
|
111
127
|
async listNamespaces(options) {
|
|
112
|
-
|
|
113
|
-
|
|
128
|
+
return this.runSerialized(async () => {
|
|
129
|
+
await this.ensureLoaded();
|
|
130
|
+
return this.delegate.listNamespaces(options);
|
|
131
|
+
});
|
|
114
132
|
}
|
|
115
133
|
}
|
|
116
134
|
export function createInMemoryStore() {
|
|
@@ -1,7 +1,51 @@
|
|
|
1
1
|
import { OllamaEmbeddings } from "@langchain/ollama";
|
|
2
|
+
import { OpenAIEmbeddings } from "@langchain/openai";
|
|
2
3
|
import { compileEmbeddingModel } from "../../workspace/resource-compilers.js";
|
|
3
4
|
import { resolveRefId } from "../../workspace/support/workspace-ref-utils.js";
|
|
4
5
|
import { createLlamaIndexEmbeddingModel } from "./llamaindex.js";
|
|
6
|
+
function asObject(value) {
|
|
7
|
+
return typeof value === "object" && value ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
function isPlaceholderApiKey(value) {
|
|
10
|
+
return typeof value === "string" && value.trim().toLowerCase() === "dummy";
|
|
11
|
+
}
|
|
12
|
+
function buildAuthOmittingFetch(baseFetch = fetch) {
|
|
13
|
+
return async (input, init) => {
|
|
14
|
+
const sanitizedHeaders = new Headers(input instanceof Request ? input.headers : undefined);
|
|
15
|
+
const initHeaders = new Headers(init?.headers);
|
|
16
|
+
initHeaders.forEach((value, key) => {
|
|
17
|
+
sanitizedHeaders.set(key, value);
|
|
18
|
+
});
|
|
19
|
+
sanitizedHeaders.delete("authorization");
|
|
20
|
+
if (input instanceof Request) {
|
|
21
|
+
return baseFetch(new Request(input, {
|
|
22
|
+
...init,
|
|
23
|
+
headers: sanitizedHeaders,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
return baseFetch(input, {
|
|
27
|
+
...init,
|
|
28
|
+
headers: sanitizedHeaders,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function normalizeOpenAICompatibleInit(init) {
|
|
33
|
+
const normalized = { ...init };
|
|
34
|
+
const configuration = asObject(init.configuration) ?? {};
|
|
35
|
+
const baseUrl = typeof init.baseUrl === "string" && init.baseUrl.trim() ? init.baseUrl.trim() : undefined;
|
|
36
|
+
const omitAuthHeader = init.omitAuthHeader === true || isPlaceholderApiKey(init.apiKey);
|
|
37
|
+
const nextConfiguration = { ...configuration };
|
|
38
|
+
if (baseUrl && typeof nextConfiguration.baseURL !== "string") {
|
|
39
|
+
nextConfiguration.baseURL = baseUrl;
|
|
40
|
+
}
|
|
41
|
+
if (omitAuthHeader) {
|
|
42
|
+
nextConfiguration.fetch = buildAuthOmittingFetch(typeof configuration.fetch === "function" ? configuration.fetch : fetch);
|
|
43
|
+
}
|
|
44
|
+
normalized.configuration = nextConfiguration;
|
|
45
|
+
delete normalized.baseUrl;
|
|
46
|
+
delete normalized.omitAuthHeader;
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
5
49
|
export function resolveCompiledEmbeddingModelRef(workspace, embeddingModelRef) {
|
|
6
50
|
const resolvedId = embeddingModelRef ? resolveRefId(embeddingModelRef) : "default";
|
|
7
51
|
const embeddingModel = workspace.embeddings.get(resolvedId);
|
|
@@ -26,8 +70,20 @@ export async function resolveCompiledEmbeddingModel(embeddingModel, resolver) {
|
|
|
26
70
|
...(embeddingModel.init ?? {}),
|
|
27
71
|
});
|
|
28
72
|
}
|
|
73
|
+
if (embeddingModel.provider === "openai-compatible") {
|
|
74
|
+
return new OpenAIEmbeddings({
|
|
75
|
+
model: embeddingModel.model,
|
|
76
|
+
...normalizeOpenAICompatibleInit(embeddingModel.init ?? {}),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (embeddingModel.provider === "openai") {
|
|
80
|
+
return new OpenAIEmbeddings({
|
|
81
|
+
model: embeddingModel.model,
|
|
82
|
+
...(embeddingModel.init ?? {}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
29
85
|
if (embeddingModel.provider === "llamaindex-ollama") {
|
|
30
86
|
return createLlamaIndexEmbeddingModel(embeddingModel);
|
|
31
87
|
}
|
|
32
|
-
throw new Error(`Embedding model provider ${embeddingModel.provider} is not supported by the built-in runtime. Configure embeddingModelResolver or use ollama/llamaindex-ollama.`);
|
|
88
|
+
throw new Error(`Embedding model provider ${embeddingModel.provider} is not supported by the built-in runtime. Configure embeddingModelResolver or use openai-compatible/openai/ollama/llamaindex-ollama.`);
|
|
33
89
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { HarnessEvent, HarnessEventProjection } from "../contracts/types.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { RuntimePersistence } from "../persistence/types.js";
|
|
3
3
|
export declare class ThreadMemorySync implements HarnessEventProjection {
|
|
4
4
|
private readonly persistence;
|
|
5
5
|
private readonly store?;
|
|
6
6
|
private readonly pending;
|
|
7
|
+
private syncChain;
|
|
7
8
|
readonly name = "thread-memory-sync";
|
|
8
|
-
constructor(persistence:
|
|
9
|
+
constructor(persistence: RuntimePersistence, store?: {
|
|
9
10
|
put: (namespace: string[], key: string, value: Record<string, any>) => Promise<void>;
|
|
10
11
|
} | undefined);
|
|
11
12
|
shouldHandle(event: HarnessEvent): boolean;
|
|
@@ -53,6 +53,7 @@ export class ThreadMemorySync {
|
|
|
53
53
|
persistence;
|
|
54
54
|
store;
|
|
55
55
|
pending = new Set();
|
|
56
|
+
syncChain = Promise.resolve();
|
|
56
57
|
name = "thread-memory-sync";
|
|
57
58
|
constructor(persistence, store) {
|
|
58
59
|
this.persistence = persistence;
|
|
@@ -65,7 +66,12 @@ export class ThreadMemorySync {
|
|
|
65
66
|
if (!this.shouldHandle(event)) {
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
|
-
const task = this.
|
|
69
|
+
const task = this.syncChain
|
|
70
|
+
.then(() => this.syncThread(event.threadId))
|
|
71
|
+
.catch(() => {
|
|
72
|
+
// Fail open: background memory digestion must not break the hot path.
|
|
73
|
+
});
|
|
74
|
+
this.syncChain = task
|
|
69
75
|
.catch(() => {
|
|
70
76
|
// Fail open: background memory digestion must not break the hot path.
|
|
71
77
|
})
|
|
@@ -3,7 +3,7 @@ import { getSkillInheritancePolicy, resolveToolTargets } from "../extensions.js"
|
|
|
3
3
|
import { compileModel, compileTool } from "./resource-compilers.js";
|
|
4
4
|
import { inferAgentCapabilities } from "./support/agent-capabilities.js";
|
|
5
5
|
import { discoverSkillPaths } from "./support/discovery.js";
|
|
6
|
-
import { compileAgentMemories, getRuntimeDefaults, getRuntimeMemoryDefaults, getWorkspaceObject, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
|
|
6
|
+
import { compileAgentMemories, getResilienceConfig, getRuntimeDefaults, getRuntimeMemoryDefaults, getWorkspaceObject, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
|
|
7
7
|
const WORKSPACE_BOUNDARY_GUIDANCE = "Keep repository and file exploration bounded to the current workspace root unless the user explicitly asks for broader host or filesystem access. " +
|
|
8
8
|
"Do not inspect absolute paths outside the workspace, system directories, or unrelated repos by default. " +
|
|
9
9
|
"Prefer workspace-local tools, relative paths, and the current repository checkout when analyzing code.";
|
|
@@ -235,6 +235,7 @@ function resolveRuntimeMemoryConfig(agent, refs) {
|
|
|
235
235
|
export function compileBinding(workspaceRoot, agent, agents, referencedSubagentIds, refs, models, tools) {
|
|
236
236
|
const internalSubagent = referencedSubagentIds.has(agent.id);
|
|
237
237
|
const runtimeDefaults = getRuntimeDefaults(refs);
|
|
238
|
+
const resilience = getResilienceConfig(refs);
|
|
238
239
|
const compiledAgentSkills = compileAgentSkills(workspaceRoot, agent);
|
|
239
240
|
const compiledAgentMemory = compileAgentMemories(workspaceRoot, agent.memorySources);
|
|
240
241
|
const compiledAgentModel = requireModel(models, agent.modelRef || (internalSubagent ? "model/default" : ""), agent.id);
|
|
@@ -264,6 +265,7 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
|
|
|
264
265
|
workspaceRoot,
|
|
265
266
|
hostFacing: !internalSubagent,
|
|
266
267
|
capabilities: inferAgentCapabilities(agent),
|
|
268
|
+
resilience,
|
|
267
269
|
...(checkpointer ? { checkpointer: checkpointer.config } : {}),
|
|
268
270
|
...(store ? { store: store.config } : {}),
|
|
269
271
|
...(runtimeMemory ? { runtimeMemory: runtimeMemory.config } : {}),
|
|
@@ -20,11 +20,20 @@ export type RecoveryConfig = {
|
|
|
20
20
|
export type ConcurrencyConfig = {
|
|
21
21
|
maxConcurrentRuns: number;
|
|
22
22
|
};
|
|
23
|
+
export type ProviderRetryConfig = {
|
|
24
|
+
maxAttempts: number;
|
|
25
|
+
backoffMs: number;
|
|
26
|
+
retryableMessages: string[];
|
|
27
|
+
};
|
|
28
|
+
export type ResilienceConfig = {
|
|
29
|
+
providerRetries: ProviderRetryConfig;
|
|
30
|
+
};
|
|
23
31
|
export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
|
|
24
32
|
export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
|
|
25
33
|
export declare function getRuntimeMemoryDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
|
|
26
34
|
export declare function getRecoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): RecoveryConfig;
|
|
27
35
|
export declare function getConcurrencyConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ConcurrencyConfig;
|
|
36
|
+
export declare function getResilienceConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ResilienceConfig;
|
|
28
37
|
export declare function getRoutingSystemPrompt(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string | undefined;
|
|
29
38
|
export declare function getRoutingDefaultAgentId(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string | undefined;
|
|
30
39
|
export declare function isModelRoutingEnabled(refs: Map<string, WorkspaceObject | ParsedAgentObject>): boolean;
|
|
@@ -65,6 +65,44 @@ export function getConcurrencyConfig(refs) {
|
|
|
65
65
|
: 3;
|
|
66
66
|
return { maxConcurrentRuns };
|
|
67
67
|
}
|
|
68
|
+
export function getResilienceConfig(refs) {
|
|
69
|
+
const runtimeDefaults = getRuntimeDefaults(refs);
|
|
70
|
+
const resilience = typeof runtimeDefaults?.resilience === "object" && runtimeDefaults.resilience
|
|
71
|
+
? runtimeDefaults.resilience
|
|
72
|
+
: {};
|
|
73
|
+
const providerRetries = typeof resilience.providerRetries === "object" && resilience.providerRetries
|
|
74
|
+
? resilience.providerRetries
|
|
75
|
+
: {};
|
|
76
|
+
const maxAttempts = typeof providerRetries.maxAttempts === "number" &&
|
|
77
|
+
Number.isFinite(providerRetries.maxAttempts) &&
|
|
78
|
+
providerRetries.maxAttempts > 0
|
|
79
|
+
? Math.floor(providerRetries.maxAttempts)
|
|
80
|
+
: 2;
|
|
81
|
+
const backoffMs = typeof providerRetries.backoffMs === "number" &&
|
|
82
|
+
Number.isFinite(providerRetries.backoffMs) &&
|
|
83
|
+
providerRetries.backoffMs >= 0
|
|
84
|
+
? Math.floor(providerRetries.backoffMs)
|
|
85
|
+
: 1_000;
|
|
86
|
+
const retryableMessages = Array.isArray(providerRetries.retryableMessages)
|
|
87
|
+
? providerRetries.retryableMessages
|
|
88
|
+
.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
89
|
+
.map((value) => value.trim())
|
|
90
|
+
: [
|
|
91
|
+
"connection error",
|
|
92
|
+
"network error",
|
|
93
|
+
"fetch failed",
|
|
94
|
+
"socket hang up",
|
|
95
|
+
"econnreset",
|
|
96
|
+
"timed out",
|
|
97
|
+
];
|
|
98
|
+
return {
|
|
99
|
+
providerRetries: {
|
|
100
|
+
maxAttempts,
|
|
101
|
+
backoffMs,
|
|
102
|
+
retryableMessages,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
68
106
|
export function getRoutingSystemPrompt(refs) {
|
|
69
107
|
const routing = getRoutingObject(refs);
|
|
70
108
|
return typeof routing?.systemPrompt === "string" && routing.systemPrompt.trim() ? routing.systemPrompt : undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botbotgo/agent-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.77",
|
|
4
4
|
"description": "Workspace runtime for multi-agent applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@10.9.2",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
|
|
55
55
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
56
|
-
"test": "vitest run test/hello-file.test.ts test/public-api.test.ts test/memory-runtime.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/stock-research-app-config.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/product-boundary-docs.test.ts test/long-term-memory-docs.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts test/upstream-compat-regressions.test.ts test/yaml-format.test.ts test/config-secrets.test.ts test/init-command.test.ts test/coding-agent-guide.test.ts",
|
|
56
|
+
"test": "vitest run test/hello-file.test.ts test/public-api.test.ts test/runtime-health.test.ts test/memory-runtime.test.ts test/sqlite-persistence.test.ts test/runtime-record-maintenance.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/stock-research-app-config.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/product-boundary-docs.test.ts test/long-term-memory-docs.test.ts test/local-docs-persistence-inventory.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts test/upstream-compat-regressions.test.ts test/yaml-format.test.ts test/config-secrets.test.ts test/init-command.test.ts test/coding-agent-guide.test.ts",
|
|
57
57
|
"test:real-providers": "vitest run test/real-provider-harness.test.ts",
|
|
58
58
|
"release:prepare": "npm version patch --no-git-tag-version && node ./scripts/sync-example-version.mjs",
|
|
59
59
|
"release:pack": "npm pack --dry-run",
|