@botbotgo/agent-harness 0.0.5 → 0.0.7
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 +13 -21
- package/dist/api.js +44 -45
- package/dist/contracts/types.d.ts +53 -2
- package/dist/index.d.ts +1 -7
- package/dist/index.js +1 -7
- package/dist/persistence/file-store.d.ts +15 -3
- package/dist/persistence/file-store.js +44 -2
- package/dist/runtime/agent-runtime-adapter.js +7 -2
- package/dist/runtime/harness.d.ts +8 -15
- package/dist/runtime/harness.js +118 -58
- package/dist/runtime/parsing/output-parsing.d.ts +1 -0
- package/dist/runtime/parsing/output-parsing.js +67 -9
- package/dist/runtime/thread-memory-sync.js +9 -9
- package/dist/workspace/support/discovery.js +30 -2
- package/package.json +2 -2
package/dist/api.d.ts
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AgentHarnessHandle, RunOptions, RuntimeAdapterOptions, ThreadRecord, WorkspaceLoadOptions, WorkspaceBundle } from "./contracts/types.js";
|
|
2
2
|
import { AgentHarness } from "./runtime/harness.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function
|
|
14
|
-
export
|
|
15
|
-
export declare function rejectHarnessRun(harness: AgentHarness, threadId: string, runId?: string): Promise<import("./contracts/types.js").RunResult>;
|
|
16
|
-
export declare function restartHarnessConversation(harness: AgentHarness, options: RestartConversationOptions): Promise<import("./contracts/types.js").RunResult & {
|
|
17
|
-
restart: Record<string, string>;
|
|
18
|
-
}>;
|
|
19
|
-
export declare function listHarnessArtifacts(harness: AgentHarness, threadId: string, runId?: string): Promise<ArtifactListing>;
|
|
20
|
-
export declare function subscribeHarness(harness: AgentHarness, listener: Parameters<AgentHarness["subscribe"]>[0]): () => void;
|
|
21
|
-
export declare function closeHarness(harness: AgentHarness): Promise<void>;
|
|
22
|
-
export { loadWorkspaceBundle as loadWorkspaceFacade, listHarnessSessions as listSessionsFacade, getHarnessApproval as getApprovalFacade, runHarness as runFacade, streamHarness as streamFacade, streamHarnessEvents as streamEventsFacade, resumeHarness as resumeFacade, submitHarnessDecision as submitDecisionFacade, approveHarnessRun as approveFacade, rejectHarnessRun as rejectFacade, restartHarnessConversation as restartConversationFacade, listHarnessArtifacts as artifactsFacade, subscribeHarness as subscribeFacade, closeHarness as closeFacade, };
|
|
3
|
+
type CreateAgentHarnessOptions = {
|
|
4
|
+
load?: WorkspaceLoadOptions;
|
|
5
|
+
adapter?: RuntimeAdapterOptions;
|
|
6
|
+
};
|
|
7
|
+
export declare function createAgentHarness(): Promise<AgentHarnessHandle>;
|
|
8
|
+
export declare function createAgentHarness(workspaceRoot: string, options?: CreateAgentHarnessOptions): Promise<AgentHarnessHandle>;
|
|
9
|
+
export declare function createAgentHarness(workspace: WorkspaceBundle, options?: CreateAgentHarnessOptions): Promise<AgentHarnessHandle>;
|
|
10
|
+
export declare function run(harness: AgentHarnessHandle, options: RunOptions): Promise<import("./contracts/types.js").RunResult>;
|
|
11
|
+
export declare function subscribe(harness: AgentHarnessHandle, listener: Parameters<AgentHarness["subscribe"]>[0]): () => void;
|
|
12
|
+
export declare function getThread(harness: AgentHarnessHandle, threadId: string): Promise<ThreadRecord | null>;
|
|
13
|
+
export declare function stop(harness: AgentHarnessHandle): Promise<void>;
|
|
14
|
+
export {};
|
package/dist/api.js
CHANGED
|
@@ -1,48 +1,47 @@
|
|
|
1
1
|
import { AgentHarness } from "./runtime/harness.js";
|
|
2
2
|
import { loadWorkspace } from "./workspace/compile.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const HARNESS_INSTANCE = Symbol.for("@botbotgo/agent-harness/instance");
|
|
4
|
+
function registerHarness(harness) {
|
|
5
|
+
const handle = { kind: "AgentHarnessHandle" };
|
|
6
|
+
Object.defineProperty(handle, HARNESS_INSTANCE, {
|
|
7
|
+
value: harness,
|
|
8
|
+
enumerable: false,
|
|
9
|
+
configurable: false,
|
|
10
|
+
writable: false,
|
|
11
|
+
});
|
|
12
|
+
return Object.freeze(handle);
|
|
13
|
+
}
|
|
14
|
+
function requireHarness(handle) {
|
|
15
|
+
if (handle instanceof AgentHarness) {
|
|
16
|
+
return handle;
|
|
17
|
+
}
|
|
18
|
+
if (typeof handle === "object" && handle !== null && "run" in handle && "subscribe" in handle && "close" in handle) {
|
|
19
|
+
return handle;
|
|
20
|
+
}
|
|
21
|
+
const harness = handle[HARNESS_INSTANCE];
|
|
22
|
+
if (!harness) {
|
|
23
|
+
throw new Error("Unknown or stopped AgentHarnessHandle");
|
|
24
|
+
}
|
|
25
|
+
return harness;
|
|
26
|
+
}
|
|
27
|
+
export async function createAgentHarness(input = process.cwd(), options = {}) {
|
|
28
|
+
const workspace = typeof input === "string"
|
|
29
|
+
? await loadWorkspace(input, options.load ?? {})
|
|
30
|
+
: input;
|
|
31
|
+
const harness = new AgentHarness(workspace, options.adapter ?? {});
|
|
32
|
+
await harness.initialize();
|
|
33
|
+
return registerHarness(harness);
|
|
34
|
+
}
|
|
35
|
+
export async function run(harness, options) {
|
|
36
|
+
return requireHarness(harness).run(options);
|
|
37
|
+
}
|
|
38
|
+
export function subscribe(harness, listener) {
|
|
39
|
+
return requireHarness(harness).subscribe(listener);
|
|
40
|
+
}
|
|
41
|
+
export async function getThread(harness, threadId) {
|
|
42
|
+
return requireHarness(harness).getThread(threadId);
|
|
43
|
+
}
|
|
44
|
+
export async function stop(harness) {
|
|
45
|
+
const instance = requireHarness(harness);
|
|
46
|
+
return instance.close();
|
|
5
47
|
}
|
|
6
|
-
export function createHarness(workspace, adapterOptions = {}) {
|
|
7
|
-
return new AgentHarness(workspace, adapterOptions);
|
|
8
|
-
}
|
|
9
|
-
export async function listHarnessSessions(harness, filter) {
|
|
10
|
-
return harness.listSessions(filter);
|
|
11
|
-
}
|
|
12
|
-
export async function getHarnessApproval(harness, approvalId) {
|
|
13
|
-
return harness.getApproval(approvalId);
|
|
14
|
-
}
|
|
15
|
-
export async function runHarness(harness, options) {
|
|
16
|
-
return harness.run(options);
|
|
17
|
-
}
|
|
18
|
-
export async function streamHarness(harness, options) {
|
|
19
|
-
return harness.stream(options);
|
|
20
|
-
}
|
|
21
|
-
export async function streamHarnessEvents(harness, options) {
|
|
22
|
-
return harness.streamEvents(options);
|
|
23
|
-
}
|
|
24
|
-
export async function resumeHarness(harness, options) {
|
|
25
|
-
return harness.resume(options);
|
|
26
|
-
}
|
|
27
|
-
export async function submitHarnessDecision(harness, options) {
|
|
28
|
-
return harness.submitDecision(options);
|
|
29
|
-
}
|
|
30
|
-
export async function approveHarnessRun(harness, threadId, runId) {
|
|
31
|
-
return harness.approve(threadId, runId);
|
|
32
|
-
}
|
|
33
|
-
export async function rejectHarnessRun(harness, threadId, runId) {
|
|
34
|
-
return harness.reject(threadId, runId);
|
|
35
|
-
}
|
|
36
|
-
export async function restartHarnessConversation(harness, options) {
|
|
37
|
-
return harness.restartConversation(options);
|
|
38
|
-
}
|
|
39
|
-
export async function listHarnessArtifacts(harness, threadId, runId) {
|
|
40
|
-
return harness.artifacts(threadId, runId);
|
|
41
|
-
}
|
|
42
|
-
export function subscribeHarness(harness, listener) {
|
|
43
|
-
return harness.subscribe(listener);
|
|
44
|
-
}
|
|
45
|
-
export async function closeHarness(harness) {
|
|
46
|
-
return harness.close();
|
|
47
|
-
}
|
|
48
|
-
export { loadWorkspaceBundle as loadWorkspaceFacade, listHarnessSessions as listSessionsFacade, getHarnessApproval as getApprovalFacade, runHarness as runFacade, streamHarness as streamFacade, streamHarnessEvents as streamEventsFacade, resumeHarness as resumeFacade, submitHarnessDecision as submitDecisionFacade, approveHarnessRun as approveFacade, rejectHarnessRun as rejectFacade, restartHarnessConversation as restartConversationFacade, listHarnessArtifacts as artifactsFacade, subscribeHarness as subscribeFacade, closeHarness as closeFacade, };
|
|
@@ -171,7 +171,7 @@ export type WorkspaceLoadOptions = {
|
|
|
171
171
|
overlayRoots?: string[];
|
|
172
172
|
builtinSources?: string[];
|
|
173
173
|
};
|
|
174
|
-
export type
|
|
174
|
+
export type ThreadSummary = {
|
|
175
175
|
agentId: string;
|
|
176
176
|
threadId: string;
|
|
177
177
|
latestRunId: string;
|
|
@@ -179,6 +179,7 @@ export type SessionRecord = {
|
|
|
179
179
|
updatedAt: string;
|
|
180
180
|
status: RunState;
|
|
181
181
|
};
|
|
182
|
+
export type SessionRecord = ThreadSummary;
|
|
182
183
|
export type HarnessEvent = {
|
|
183
184
|
eventId: string;
|
|
184
185
|
eventType: string;
|
|
@@ -200,11 +201,31 @@ export type RunResult = {
|
|
|
200
201
|
pendingActionId?: string;
|
|
201
202
|
delegationId?: string;
|
|
202
203
|
};
|
|
203
|
-
export type
|
|
204
|
+
export type RunListeners = {
|
|
205
|
+
onChunk?: (chunk: string) => void | Promise<void>;
|
|
206
|
+
onEvent?: (event: HarnessEvent) => void | Promise<void>;
|
|
207
|
+
onReasoning?: (chunk: string) => void | Promise<void>;
|
|
208
|
+
onStep?: (step: string) => void | Promise<void>;
|
|
209
|
+
onToolResult?: (item: {
|
|
210
|
+
toolName: string;
|
|
211
|
+
output: unknown;
|
|
212
|
+
}) => void | Promise<void>;
|
|
213
|
+
};
|
|
214
|
+
export type RunStartOptions = {
|
|
204
215
|
agentId?: string;
|
|
205
216
|
input: string;
|
|
206
217
|
threadId?: string;
|
|
218
|
+
listeners?: RunListeners;
|
|
207
219
|
};
|
|
220
|
+
export type RunDecisionOptions = {
|
|
221
|
+
threadId: string;
|
|
222
|
+
runId?: string;
|
|
223
|
+
approvalId?: string;
|
|
224
|
+
decision: "approve" | "edit" | "reject";
|
|
225
|
+
editedInput?: Record<string, unknown>;
|
|
226
|
+
listeners?: RunListeners;
|
|
227
|
+
};
|
|
228
|
+
export type RunOptions = RunStartOptions | RunDecisionOptions;
|
|
208
229
|
export type HarnessStreamItem = {
|
|
209
230
|
type: "event";
|
|
210
231
|
event: HarnessEvent;
|
|
@@ -240,6 +261,33 @@ export type TranscriptMessage = {
|
|
|
240
261
|
runId: string;
|
|
241
262
|
createdAt: string;
|
|
242
263
|
};
|
|
264
|
+
export type ThreadRunRecord = {
|
|
265
|
+
runId: string;
|
|
266
|
+
agentId: string;
|
|
267
|
+
executionMode: string;
|
|
268
|
+
createdAt: string;
|
|
269
|
+
updatedAt: string;
|
|
270
|
+
state: RunState;
|
|
271
|
+
checkpointRef: string | null;
|
|
272
|
+
resumable: boolean;
|
|
273
|
+
};
|
|
274
|
+
export type ThreadRecord = {
|
|
275
|
+
threadId: string;
|
|
276
|
+
entryAgentId: string;
|
|
277
|
+
currentState: RunState;
|
|
278
|
+
latestRunId: string;
|
|
279
|
+
createdAt: string;
|
|
280
|
+
updatedAt: string;
|
|
281
|
+
messages: TranscriptMessage[];
|
|
282
|
+
runs: ThreadRunRecord[];
|
|
283
|
+
pendingDecision?: {
|
|
284
|
+
approvalId: string;
|
|
285
|
+
pendingActionId: string;
|
|
286
|
+
toolName: string;
|
|
287
|
+
allowedDecisions: Array<"approve" | "edit" | "reject">;
|
|
288
|
+
requestedAt: string;
|
|
289
|
+
};
|
|
290
|
+
};
|
|
243
291
|
export type ResumeOptions = {
|
|
244
292
|
threadId?: string;
|
|
245
293
|
runId?: string;
|
|
@@ -357,3 +405,6 @@ export type EventSubscriber = {
|
|
|
357
405
|
kind: string;
|
|
358
406
|
onEvent: (event: HarnessEvent) => void | Promise<void>;
|
|
359
407
|
};
|
|
408
|
+
export type AgentHarnessHandle = {
|
|
409
|
+
readonly kind: "AgentHarnessHandle";
|
|
410
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from "./extensions.js";
|
|
3
|
-
export * from "./api.js";
|
|
4
|
-
export * from "./presentation.js";
|
|
5
|
-
export * from "./runtime/index.js";
|
|
6
|
-
export * from "./workspace/index.js";
|
|
7
|
-
export { createBuiltinBackendResolver, createBuiltinToolResolver } from "./vendor/builtins.js";
|
|
1
|
+
export { createAgentHarness, getThread, run, subscribe, stop } from "./api.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from "./extensions.js";
|
|
3
|
-
export * from "./api.js";
|
|
4
|
-
export * from "./presentation.js";
|
|
5
|
-
export * from "./runtime/index.js";
|
|
6
|
-
export * from "./workspace/index.js";
|
|
7
|
-
export { createBuiltinBackendResolver, createBuiltinToolResolver } from "./vendor/builtins.js";
|
|
1
|
+
export { createAgentHarness, getThread, run, subscribe, stop } from "./api.js";
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type { ApprovalRecord, ArtifactListing, ArtifactRecord, DelegationRecord, HarnessEvent, RunState,
|
|
1
|
+
import type { ApprovalRecord, ArtifactListing, ArtifactRecord, DelegationRecord, HarnessEvent, RunState, ThreadSummary, ThreadRunRecord, TranscriptMessage } from "../contracts/types.js";
|
|
2
|
+
type ThreadMeta = {
|
|
3
|
+
threadId: string;
|
|
4
|
+
workspaceId: string;
|
|
5
|
+
entryAgentId: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
status: RunState;
|
|
9
|
+
latestRunId: string;
|
|
10
|
+
};
|
|
2
11
|
export declare class FilePersistence {
|
|
3
12
|
private readonly runRoot;
|
|
4
13
|
constructor(runRoot: string);
|
|
@@ -21,8 +30,10 @@ export declare class FilePersistence {
|
|
|
21
30
|
}): Promise<void>;
|
|
22
31
|
setRunState(threadId: string, runId: string, state: RunState, checkpointRef?: string | null): Promise<void>;
|
|
23
32
|
appendEvent(event: HarnessEvent): Promise<void>;
|
|
24
|
-
listSessions(): Promise<
|
|
25
|
-
getSession(threadId: string): Promise<
|
|
33
|
+
listSessions(): Promise<ThreadSummary[]>;
|
|
34
|
+
getSession(threadId: string): Promise<ThreadSummary | null>;
|
|
35
|
+
getThreadMeta(threadId: string): Promise<ThreadMeta | null>;
|
|
36
|
+
listThreadRuns(threadId: string): Promise<ThreadRunRecord[]>;
|
|
26
37
|
listRunEvents(threadId: string, runId: string): Promise<HarnessEvent[]>;
|
|
27
38
|
listApprovals(): Promise<ApprovalRecord[]>;
|
|
28
39
|
getApproval(approvalId: string): Promise<ApprovalRecord | null>;
|
|
@@ -37,3 +48,4 @@ export declare class FilePersistence {
|
|
|
37
48
|
appendThreadMessage(threadId: string, message: TranscriptMessage): Promise<void>;
|
|
38
49
|
listThreadMessages(threadId: string, limit?: number): Promise<TranscriptMessage[]>;
|
|
39
50
|
}
|
|
51
|
+
export {};
|
|
@@ -94,7 +94,11 @@ export class FilePersistence {
|
|
|
94
94
|
}
|
|
95
95
|
async setRunState(threadId, runId, state, checkpointRef) {
|
|
96
96
|
const lifecyclePath = path.join(this.runDir(threadId, runId), "lifecycle.json");
|
|
97
|
-
const
|
|
97
|
+
const runMetaPath = path.join(this.runDir(threadId, runId), "meta.json");
|
|
98
|
+
const [lifecycle, runMeta] = await Promise.all([
|
|
99
|
+
readJson(lifecyclePath),
|
|
100
|
+
readJson(runMetaPath),
|
|
101
|
+
]);
|
|
98
102
|
const now = new Date().toISOString();
|
|
99
103
|
const next = {
|
|
100
104
|
state,
|
|
@@ -104,7 +108,13 @@ export class FilePersistence {
|
|
|
104
108
|
resumable: state === "waiting_for_approval",
|
|
105
109
|
checkpointRef: checkpointRef ?? lifecycle.checkpointRef,
|
|
106
110
|
};
|
|
107
|
-
await
|
|
111
|
+
await Promise.all([
|
|
112
|
+
writeJson(lifecyclePath, next),
|
|
113
|
+
writeJson(runMetaPath, {
|
|
114
|
+
...runMeta,
|
|
115
|
+
updatedAt: now,
|
|
116
|
+
}),
|
|
117
|
+
]);
|
|
108
118
|
if (checkpointRef !== undefined) {
|
|
109
119
|
await writeJson(path.join(this.runDir(threadId, runId), "checkpoint-ref.json"), {
|
|
110
120
|
threadId,
|
|
@@ -176,6 +186,38 @@ export class FilePersistence {
|
|
|
176
186
|
status: index.status,
|
|
177
187
|
};
|
|
178
188
|
}
|
|
189
|
+
async getThreadMeta(threadId) {
|
|
190
|
+
const filePath = path.join(this.threadDir(threadId), "meta.json");
|
|
191
|
+
if (!(await fileExists(filePath))) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return readJson(filePath);
|
|
195
|
+
}
|
|
196
|
+
async listThreadRuns(threadId) {
|
|
197
|
+
const runsDir = path.join(this.threadDir(threadId), "runs");
|
|
198
|
+
if (!(await fileExists(runsDir))) {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
const runIds = (await readdir(runsDir)).sort();
|
|
202
|
+
const runs = await Promise.all(runIds.map(async (runId) => {
|
|
203
|
+
const runDir = this.runDir(threadId, runId);
|
|
204
|
+
const [meta, lifecycle] = await Promise.all([
|
|
205
|
+
readJson(path.join(runDir, "meta.json")),
|
|
206
|
+
readJson(path.join(runDir, "lifecycle.json")),
|
|
207
|
+
]);
|
|
208
|
+
return {
|
|
209
|
+
runId: meta.runId,
|
|
210
|
+
agentId: meta.agentId,
|
|
211
|
+
executionMode: meta.executionMode,
|
|
212
|
+
createdAt: meta.createdAt,
|
|
213
|
+
updatedAt: meta.updatedAt,
|
|
214
|
+
state: lifecycle.state,
|
|
215
|
+
checkpointRef: lifecycle.checkpointRef,
|
|
216
|
+
resumable: lifecycle.resumable,
|
|
217
|
+
};
|
|
218
|
+
}));
|
|
219
|
+
return runs.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
220
|
+
}
|
|
179
221
|
async listRunEvents(threadId, runId) {
|
|
180
222
|
const eventsDir = path.join(this.runDir(threadId, runId), "events");
|
|
181
223
|
if (!(await fileExists(eventsDir))) {
|
|
@@ -5,7 +5,7 @@ import { ChatGoogle } from "@langchain/google";
|
|
|
5
5
|
import { ChatOllama } from "@langchain/ollama";
|
|
6
6
|
import { ChatOpenAI } from "@langchain/openai";
|
|
7
7
|
import { createAgent, humanInTheLoopMiddleware, initChatModel } from "langchain";
|
|
8
|
-
import { extractReasoningText, extractToolFallbackContext, extractVisibleOutput, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
8
|
+
import { extractEmptyAssistantMessageFailure, extractReasoningText, extractToolFallbackContext, extractVisibleOutput, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
9
9
|
import { extractAgentStep, extractInterruptPayload, extractReasoningStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
|
|
10
10
|
import { wrapToolForExecution } from "./tool-hitl.js";
|
|
11
11
|
const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
@@ -382,8 +382,13 @@ export class AgentRuntimeAdapter {
|
|
|
382
382
|
const interruptContent = Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? JSON.stringify(result.__interrupt__) : undefined;
|
|
383
383
|
const extractedOutput = extractVisibleOutput(result);
|
|
384
384
|
const visibleOutput = extractedOutput && !isLikelyToolArgsObject(tryParseJson(extractedOutput)) ? extractedOutput : "";
|
|
385
|
+
const emptyAssistantMessageFailure = extractEmptyAssistantMessageFailure(result);
|
|
385
386
|
const toolFallback = extractToolFallbackContext(result);
|
|
386
|
-
const
|
|
387
|
+
const synthesizedOutput = await this.synthesizeDeepAgentAnswer(binding, input, result);
|
|
388
|
+
if (!visibleOutput && !synthesizedOutput && !toolFallback && emptyAssistantMessageFailure) {
|
|
389
|
+
throw new Error(emptyAssistantMessageFailure);
|
|
390
|
+
}
|
|
391
|
+
const output = visibleOutput || synthesizedOutput || toolFallback || JSON.stringify(result, null, 2);
|
|
387
392
|
return {
|
|
388
393
|
threadId,
|
|
389
394
|
runId,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ApprovalRecord, ArtifactListing, DelegationRecord, HarnessEvent, HarnessStreamItem, RestartConversationOptions, RuntimeAdapterOptions, ResumeOptions, RunOptions, RunResult, SessionRecord, WorkspaceBundle } from "../contracts/types.js";
|
|
1
|
+
import type { ApprovalRecord, HarnessEvent, HarnessStreamItem, RunStartOptions, RestartConversationOptions, RuntimeAdapterOptions, ResumeOptions, RunOptions, RunResult, ThreadSummary, ThreadRecord, WorkspaceBundle } from "../contracts/types.js";
|
|
3
2
|
export declare class AgentHarness {
|
|
4
3
|
private readonly workspace;
|
|
5
4
|
private readonly runtimeAdapterOptions;
|
|
@@ -25,31 +24,25 @@ export declare class AgentHarness {
|
|
|
25
24
|
private resolveVectorStore;
|
|
26
25
|
constructor(workspace: WorkspaceBundle, runtimeAdapterOptions?: RuntimeAdapterOptions);
|
|
27
26
|
initialize(): Promise<void>;
|
|
28
|
-
subscribeEvents(listener: (event: HarnessEvent) => void): () => void;
|
|
29
27
|
subscribe(listener: (event: HarnessEvent) => void): () => void;
|
|
30
28
|
listSessions(filter?: {
|
|
31
29
|
agentId?: string;
|
|
32
|
-
}): Promise<
|
|
33
|
-
getSession
|
|
34
|
-
|
|
30
|
+
}): Promise<ThreadSummary[]>;
|
|
31
|
+
private getSession;
|
|
32
|
+
getThread(threadId: string): Promise<ThreadRecord | null>;
|
|
35
33
|
listPendingApprovals(): Promise<ApprovalRecord[]>;
|
|
36
|
-
getApproval(approvalId: string): Promise<ApprovalRecord | null>;
|
|
37
|
-
listDelegations(): Promise<DelegationRecord[]>;
|
|
38
34
|
routeAgent(input: string, options?: {
|
|
39
35
|
threadId?: string;
|
|
40
36
|
}): Promise<string>;
|
|
41
|
-
artifacts(threadId: string, runId?: string): Promise<ArtifactListing>;
|
|
42
37
|
private emit;
|
|
43
38
|
private persistApproval;
|
|
44
39
|
private resolveApprovalRecord;
|
|
40
|
+
private isDecisionRun;
|
|
41
|
+
private notifyListener;
|
|
42
|
+
private dispatchRunListeners;
|
|
45
43
|
run(options: RunOptions): Promise<RunResult>;
|
|
46
|
-
|
|
47
|
-
streamEvents(options: RunOptions): AsyncGenerator<HarnessStreamItem>;
|
|
48
|
-
streamReadable(options: RunOptions): Promise<Readable>;
|
|
44
|
+
streamEvents(options: RunStartOptions): AsyncGenerator<HarnessStreamItem>;
|
|
49
45
|
resume(options: ResumeOptions): Promise<RunResult>;
|
|
50
|
-
submitDecision(options: ResumeOptions): Promise<RunResult>;
|
|
51
|
-
approve(threadId: string, runId?: string): Promise<RunResult>;
|
|
52
|
-
reject(threadId: string, runId?: string): Promise<RunResult>;
|
|
53
46
|
restartConversation(options: RestartConversationOptions): Promise<RunResult & {
|
|
54
47
|
restart: Record<string, string>;
|
|
55
48
|
}>;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Readable } from "node:stream";
|
|
2
1
|
import { AUTO_AGENT_ID } from "../contracts/types.js";
|
|
3
2
|
import { FilePersistence } from "../persistence/file-store.js";
|
|
4
3
|
import { createPersistentId } from "../utils/id.js";
|
|
@@ -147,38 +146,57 @@ export class AgentHarness {
|
|
|
147
146
|
async initialize() {
|
|
148
147
|
await this.persistence.initialize();
|
|
149
148
|
}
|
|
150
|
-
subscribeEvents(listener) {
|
|
151
|
-
return this.eventBus.subscribe(listener);
|
|
152
|
-
}
|
|
153
149
|
subscribe(listener) {
|
|
154
|
-
return this.
|
|
150
|
+
return this.eventBus.subscribe(listener);
|
|
155
151
|
}
|
|
156
152
|
async listSessions(filter) {
|
|
157
|
-
const
|
|
153
|
+
const threadSummaries = await this.persistence.listSessions();
|
|
158
154
|
if (!filter?.agentId) {
|
|
159
|
-
return
|
|
155
|
+
return threadSummaries;
|
|
160
156
|
}
|
|
161
|
-
return
|
|
157
|
+
return threadSummaries.filter((thread) => thread.agentId === filter.agentId);
|
|
162
158
|
}
|
|
163
159
|
async getSession(threadId) {
|
|
164
160
|
return this.persistence.getSession(threadId);
|
|
165
161
|
}
|
|
166
|
-
async
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
162
|
+
async getThread(threadId) {
|
|
163
|
+
const [threadSummary, meta, messages, runs] = await Promise.all([
|
|
164
|
+
this.getSession(threadId),
|
|
165
|
+
this.persistence.getThreadMeta(threadId),
|
|
166
|
+
this.persistence.listThreadMessages(threadId, 200),
|
|
167
|
+
this.persistence.listThreadRuns(threadId),
|
|
168
|
+
]);
|
|
169
|
+
if (!threadSummary || !meta) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const latestRunId = threadSummary.latestRunId;
|
|
173
|
+
const latestApprovals = await this.persistence.getRunApprovals(threadId, latestRunId);
|
|
174
|
+
const pendingApproval = latestApprovals
|
|
175
|
+
.filter((approval) => approval.status === "pending")
|
|
176
|
+
.sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
|
|
177
|
+
return {
|
|
178
|
+
threadId,
|
|
179
|
+
entryAgentId: meta.entryAgentId,
|
|
180
|
+
currentState: threadSummary.status,
|
|
181
|
+
latestRunId,
|
|
182
|
+
createdAt: meta.createdAt,
|
|
183
|
+
updatedAt: threadSummary.updatedAt,
|
|
184
|
+
messages,
|
|
185
|
+
runs,
|
|
186
|
+
pendingDecision: pendingApproval
|
|
187
|
+
? {
|
|
188
|
+
approvalId: pendingApproval.approvalId,
|
|
189
|
+
pendingActionId: pendingApproval.pendingActionId,
|
|
190
|
+
toolName: pendingApproval.toolName,
|
|
191
|
+
allowedDecisions: pendingApproval.allowedDecisions,
|
|
192
|
+
requestedAt: pendingApproval.requestedAt,
|
|
193
|
+
}
|
|
194
|
+
: undefined,
|
|
195
|
+
};
|
|
172
196
|
}
|
|
173
197
|
async listPendingApprovals() {
|
|
174
198
|
return (await this.persistence.listApprovals()).filter((approval) => approval.status === "pending");
|
|
175
199
|
}
|
|
176
|
-
async getApproval(approvalId) {
|
|
177
|
-
return this.persistence.getApproval(approvalId);
|
|
178
|
-
}
|
|
179
|
-
async listDelegations() {
|
|
180
|
-
return this.persistence.listDelegations();
|
|
181
|
-
}
|
|
182
200
|
async routeAgent(input, options = {}) {
|
|
183
201
|
const routingInput = await this.buildRoutingInput(input, options.threadId);
|
|
184
202
|
const normalized = input.trim().toLowerCase();
|
|
@@ -210,13 +228,6 @@ export class AgentHarness {
|
|
|
210
228
|
return heuristicRoute(input, primaryBinding, secondaryBinding);
|
|
211
229
|
}
|
|
212
230
|
}
|
|
213
|
-
async artifacts(threadId, runId) {
|
|
214
|
-
const session = await this.getSession(threadId);
|
|
215
|
-
if (!session) {
|
|
216
|
-
throw new Error(`Unknown thread ${threadId}`);
|
|
217
|
-
}
|
|
218
|
-
return this.persistence.listArtifacts(threadId, runId ?? session.latestRunId);
|
|
219
|
-
}
|
|
220
231
|
async emit(threadId, runId, sequence, eventType, payload, source = "runtime") {
|
|
221
232
|
const event = createHarnessEvent(threadId, runId, sequence, eventType, payload, source);
|
|
222
233
|
await this.persistence.appendEvent(event);
|
|
@@ -239,7 +250,7 @@ export class AgentHarness {
|
|
|
239
250
|
});
|
|
240
251
|
return approval;
|
|
241
252
|
}
|
|
242
|
-
async resolveApprovalRecord(options,
|
|
253
|
+
async resolveApprovalRecord(options, thread) {
|
|
243
254
|
if (options.approvalId) {
|
|
244
255
|
const approval = await this.persistence.getApproval(options.approvalId);
|
|
245
256
|
if (!approval) {
|
|
@@ -247,8 +258,8 @@ export class AgentHarness {
|
|
|
247
258
|
}
|
|
248
259
|
return approval;
|
|
249
260
|
}
|
|
250
|
-
const runId = options.runId ??
|
|
251
|
-
const approvals = await this.persistence.getRunApprovals(options.threadId ??
|
|
261
|
+
const runId = options.runId ?? thread.latestRunId;
|
|
262
|
+
const approvals = await this.persistence.getRunApprovals(options.threadId ?? thread.threadId, runId);
|
|
252
263
|
const approval = approvals
|
|
253
264
|
.filter((candidate) => candidate.status === "pending")
|
|
254
265
|
.sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
|
|
@@ -257,7 +268,75 @@ export class AgentHarness {
|
|
|
257
268
|
}
|
|
258
269
|
return approval;
|
|
259
270
|
}
|
|
271
|
+
isDecisionRun(options) {
|
|
272
|
+
return "decision" in options;
|
|
273
|
+
}
|
|
274
|
+
async notifyListener(listener, value) {
|
|
275
|
+
if (!listener) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
await listener(value);
|
|
279
|
+
}
|
|
280
|
+
async dispatchRunListeners(stream, listeners) {
|
|
281
|
+
let latestEvent;
|
|
282
|
+
let output = "";
|
|
283
|
+
for await (const item of stream) {
|
|
284
|
+
if (item.type === "event") {
|
|
285
|
+
latestEvent = item.event;
|
|
286
|
+
await this.notifyListener(listeners.onEvent, item.event);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (item.type === "content") {
|
|
290
|
+
output += item.content;
|
|
291
|
+
await this.notifyListener(listeners.onChunk, item.content);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (item.type === "reasoning") {
|
|
295
|
+
await this.notifyListener(listeners.onReasoning, item.content);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (item.type === "step") {
|
|
299
|
+
await this.notifyListener(listeners.onStep, item.content);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (item.type === "tool-result") {
|
|
303
|
+
await this.notifyListener(listeners.onToolResult, {
|
|
304
|
+
toolName: item.toolName,
|
|
305
|
+
output: item.output,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (!latestEvent) {
|
|
310
|
+
throw new Error("run did not emit any events");
|
|
311
|
+
}
|
|
312
|
+
const thread = await this.getThread(latestEvent.threadId);
|
|
313
|
+
if (!thread) {
|
|
314
|
+
throw new Error(`Unknown thread ${latestEvent.threadId}`);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
threadId: thread.threadId,
|
|
318
|
+
runId: thread.latestRunId,
|
|
319
|
+
agentId: thread.runs[0]?.agentId ?? thread.entryAgentId,
|
|
320
|
+
state: thread.currentState,
|
|
321
|
+
output,
|
|
322
|
+
approvalId: thread.pendingDecision?.approvalId,
|
|
323
|
+
pendingActionId: thread.pendingDecision?.pendingActionId,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
260
326
|
async run(options) {
|
|
327
|
+
if (this.isDecisionRun(options)) {
|
|
328
|
+
const resumeOptions = {
|
|
329
|
+
threadId: options.threadId,
|
|
330
|
+
runId: options.runId,
|
|
331
|
+
approvalId: options.approvalId,
|
|
332
|
+
decision: options.decision,
|
|
333
|
+
editedInput: options.editedInput,
|
|
334
|
+
};
|
|
335
|
+
return this.resume(resumeOptions);
|
|
336
|
+
}
|
|
337
|
+
if (options.listeners) {
|
|
338
|
+
return this.dispatchRunListeners(this.streamEvents(options), options.listeners);
|
|
339
|
+
}
|
|
261
340
|
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
262
341
|
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
263
342
|
if (!binding) {
|
|
@@ -359,13 +438,6 @@ export class AgentHarness {
|
|
|
359
438
|
};
|
|
360
439
|
}
|
|
361
440
|
}
|
|
362
|
-
async *stream(options) {
|
|
363
|
-
for await (const item of this.streamEvents(options)) {
|
|
364
|
-
if (item.type === "content") {
|
|
365
|
-
yield item.content;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
441
|
async *streamEvents(options) {
|
|
370
442
|
const selectedAgentId = await this.resolveSelectedAgentId(options.input, options.agentId, options.threadId);
|
|
371
443
|
const binding = this.workspace.bindings.get(selectedAgentId);
|
|
@@ -592,25 +664,22 @@ export class AgentHarness {
|
|
|
592
664
|
}
|
|
593
665
|
}
|
|
594
666
|
}
|
|
595
|
-
async streamReadable(options) {
|
|
596
|
-
return Readable.from(this.stream(options));
|
|
597
|
-
}
|
|
598
667
|
async resume(options) {
|
|
599
668
|
const approvalById = options.approvalId ? await this.persistence.getApproval(options.approvalId) : null;
|
|
600
|
-
const
|
|
669
|
+
const thread = options.threadId
|
|
601
670
|
? await this.getSession(options.threadId)
|
|
602
671
|
: approvalById
|
|
603
672
|
? await this.getSession(approvalById.threadId)
|
|
604
673
|
: null;
|
|
605
|
-
if (!
|
|
674
|
+
if (!thread) {
|
|
606
675
|
throw new Error("resume requires either threadId or approvalId");
|
|
607
676
|
}
|
|
608
|
-
const approval = approvalById ?? await this.resolveApprovalRecord(options,
|
|
677
|
+
const approval = approvalById ?? await this.resolveApprovalRecord(options, thread);
|
|
609
678
|
const threadId = approval.threadId;
|
|
610
679
|
const runId = approval.runId;
|
|
611
|
-
const binding = this.workspace.bindings.get(
|
|
680
|
+
const binding = this.workspace.bindings.get(thread.agentId);
|
|
612
681
|
if (!binding) {
|
|
613
|
-
throw new Error(`Unknown agent ${
|
|
682
|
+
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
614
683
|
}
|
|
615
684
|
await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
|
|
616
685
|
await this.emit(threadId, runId, 5, "run.resumed", {
|
|
@@ -655,24 +724,15 @@ export class AgentHarness {
|
|
|
655
724
|
pendingActionId: approval.pendingActionId,
|
|
656
725
|
};
|
|
657
726
|
}
|
|
658
|
-
async submitDecision(options) {
|
|
659
|
-
return this.resume(options);
|
|
660
|
-
}
|
|
661
|
-
async approve(threadId, runId) {
|
|
662
|
-
return this.submitDecision({ threadId, runId, decision: "approve" });
|
|
663
|
-
}
|
|
664
|
-
async reject(threadId, runId) {
|
|
665
|
-
return this.submitDecision({ threadId, runId, decision: "reject" });
|
|
666
|
-
}
|
|
667
727
|
async restartConversation(options) {
|
|
668
|
-
const
|
|
669
|
-
if (!
|
|
728
|
+
const thread = await this.getSession(options.threadId);
|
|
729
|
+
if (!thread) {
|
|
670
730
|
throw new Error(`Unknown thread ${options.threadId}`);
|
|
671
731
|
}
|
|
672
|
-
const sourceRunId =
|
|
732
|
+
const sourceRunId = thread.latestRunId;
|
|
673
733
|
const targetThreadId = options.mode === "restart-new-thread" ? createPersistentId() : options.threadId;
|
|
674
734
|
const result = await this.run({
|
|
675
|
-
agentId:
|
|
735
|
+
agentId: thread.agentId,
|
|
676
736
|
input: options.input,
|
|
677
737
|
threadId: options.mode === "restart-new-thread" ? undefined : targetThreadId,
|
|
678
738
|
});
|
|
@@ -6,6 +6,7 @@ export declare function readTextContent(value: unknown): string;
|
|
|
6
6
|
export declare function hasToolCalls(value: unknown): boolean;
|
|
7
7
|
export declare function extractToolFallbackContext(value: unknown): string;
|
|
8
8
|
export declare function extractVisibleOutput(value: unknown): string;
|
|
9
|
+
export declare function extractEmptyAssistantMessageFailure(value: unknown): string;
|
|
9
10
|
export declare function isToolCallParseFailure(error: unknown): boolean;
|
|
10
11
|
export declare const STRICT_TOOL_JSON_INSTRUCTION = "When calling tools, return only the tool call itself. The arguments must be a pure JSON object with no explanatory text before or after it.";
|
|
11
12
|
export declare function wrapResolvedModel<T>(value: T): T;
|
|
@@ -225,19 +225,56 @@ function extractAssistantTextFromMessages(messages) {
|
|
|
225
225
|
: typeof message.getType === "function"
|
|
226
226
|
? message.getType()
|
|
227
227
|
: undefined;
|
|
228
|
+
if (!(typeName === "AIMessage" || runtimeType === "ai"))
|
|
229
|
+
continue;
|
|
228
230
|
const kwargs = typeof typed.kwargs === "object" && typed.kwargs ? typed.kwargs : undefined;
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return content;
|
|
237
|
-
}
|
|
231
|
+
if (hasToolCalls(message))
|
|
232
|
+
continue;
|
|
233
|
+
if (Array.isArray(kwargs?.tool_calls) && kwargs.tool_calls.length > 0)
|
|
234
|
+
continue;
|
|
235
|
+
const content = extractMessageContent(message);
|
|
236
|
+
if (content)
|
|
237
|
+
return content;
|
|
238
238
|
}
|
|
239
239
|
return "";
|
|
240
240
|
}
|
|
241
|
+
function extractEmptyAssistantMessageReason(messages) {
|
|
242
|
+
if (!Array.isArray(messages))
|
|
243
|
+
return null;
|
|
244
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
245
|
+
const message = messages[index];
|
|
246
|
+
if (typeof message !== "object" || !message)
|
|
247
|
+
continue;
|
|
248
|
+
const typed = message;
|
|
249
|
+
const ids = Array.isArray(typed.id) ? typed.id.filter((item) => typeof item === "string") : [];
|
|
250
|
+
const typeName = ids.at(-1);
|
|
251
|
+
const runtimeType = typeof message._getType === "function"
|
|
252
|
+
? message._getType()
|
|
253
|
+
: typeof message.getType === "function"
|
|
254
|
+
? message.getType()
|
|
255
|
+
: undefined;
|
|
256
|
+
const kwargs = typeof typed.kwargs === "object" && typed.kwargs ? typed.kwargs : undefined;
|
|
257
|
+
if (!(typeName === "AIMessage" || runtimeType === "ai")) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (hasToolCalls(message)) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const content = extractMessageContent(message).trim();
|
|
264
|
+
if (content) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const responseMetadata = typeof kwargs?.response_metadata === "object" && kwargs.response_metadata
|
|
268
|
+
? kwargs.response_metadata
|
|
269
|
+
: {};
|
|
270
|
+
return {
|
|
271
|
+
finishReason: typeof responseMetadata.finish_reason === "string" ? responseMetadata.finish_reason : undefined,
|
|
272
|
+
modelProvider: typeof responseMetadata.model_provider === "string" ? responseMetadata.model_provider : undefined,
|
|
273
|
+
modelName: typeof responseMetadata.model_name === "string" ? responseMetadata.model_name : undefined,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
241
278
|
function extractStructuredOutputText(value) {
|
|
242
279
|
if (typeof value !== "object" || !value)
|
|
243
280
|
return "";
|
|
@@ -304,6 +341,27 @@ export function extractVisibleOutput(value) {
|
|
|
304
341
|
}
|
|
305
342
|
return "";
|
|
306
343
|
}
|
|
344
|
+
export function extractEmptyAssistantMessageFailure(value) {
|
|
345
|
+
if (typeof value !== "object" || !value)
|
|
346
|
+
return "";
|
|
347
|
+
const typed = value;
|
|
348
|
+
const reason = extractEmptyAssistantMessageReason(typed.messages)
|
|
349
|
+
?? (typeof typed.output === "object" && typed.output
|
|
350
|
+
? extractEmptyAssistantMessageReason(typed.output.messages)
|
|
351
|
+
: null);
|
|
352
|
+
if (!reason) {
|
|
353
|
+
return "";
|
|
354
|
+
}
|
|
355
|
+
const detailParts = [
|
|
356
|
+
reason.finishReason ? `finish_reason=${reason.finishReason}` : "",
|
|
357
|
+
reason.modelProvider ? `model_provider=${reason.modelProvider}` : "",
|
|
358
|
+
reason.modelName ? `model_name=${reason.modelName}` : "",
|
|
359
|
+
].filter(Boolean);
|
|
360
|
+
return [
|
|
361
|
+
"empty_final_ai_message: model returned no visible assistant content and no tool calls",
|
|
362
|
+
detailParts.length > 0 ? `(${detailParts.join(", ")})` : "",
|
|
363
|
+
].filter(Boolean).join(" ");
|
|
364
|
+
}
|
|
307
365
|
function isAiMessageLike(value) {
|
|
308
366
|
if (typeof value !== "object" || !value)
|
|
309
367
|
return false;
|
|
@@ -5,17 +5,17 @@ function excerpt(message) {
|
|
|
5
5
|
const normalized = message.content.replace(/\s+/g, " ").trim();
|
|
6
6
|
return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
|
|
7
7
|
}
|
|
8
|
-
function renderStatusMarkdown(
|
|
8
|
+
function renderStatusMarkdown(thread, messages) {
|
|
9
9
|
const userMessages = messages.filter((message) => message.role === "user");
|
|
10
10
|
const assistantMessages = messages.filter((message) => message.role === "assistant");
|
|
11
11
|
return [
|
|
12
12
|
"# Thread Status",
|
|
13
13
|
"",
|
|
14
|
-
`- thread_id: ${
|
|
15
|
-
`- latest_run_id: ${
|
|
16
|
-
`- agent_id: ${
|
|
17
|
-
`- status: ${
|
|
18
|
-
`- updated_at: ${
|
|
14
|
+
`- thread_id: ${thread.threadId}`,
|
|
15
|
+
`- latest_run_id: ${thread.latestRunId}`,
|
|
16
|
+
`- agent_id: ${thread.agentId}`,
|
|
17
|
+
`- status: ${thread.status}`,
|
|
18
|
+
`- updated_at: ${thread.updatedAt}`,
|
|
19
19
|
"",
|
|
20
20
|
"## Recent User Message",
|
|
21
21
|
excerpt(userMessages.at(-1)),
|
|
@@ -64,8 +64,8 @@ export class ThreadMemorySync {
|
|
|
64
64
|
await task;
|
|
65
65
|
}
|
|
66
66
|
async syncThread(threadId) {
|
|
67
|
-
const
|
|
68
|
-
if (!
|
|
67
|
+
const thread = await this.persistence.getSession(threadId);
|
|
68
|
+
if (!thread) {
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
const [messages, approvals] = await Promise.all([
|
|
@@ -77,7 +77,7 @@ export class ThreadMemorySync {
|
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
await Promise.all([
|
|
80
|
-
this.store.put(["memories", "threads", threadId], "status.md", { content: `${renderStatusMarkdown(
|
|
80
|
+
this.store.put(["memories", "threads", threadId], "status.md", { content: `${renderStatusMarkdown(thread, messages)}\n` }),
|
|
81
81
|
this.store.put(["memories", "threads", threadId], "open-approvals.md", { content: `${renderOpenApprovalsMarkdown(pendingApprovals)}\n` }),
|
|
82
82
|
]);
|
|
83
83
|
}
|
|
@@ -10,13 +10,41 @@ function resolveBuiltinPath(kind, ref) {
|
|
|
10
10
|
throw new Error(`Unsupported builtin skill discovery path ${ref}`);
|
|
11
11
|
}
|
|
12
12
|
const suffix = normalized.replace(/^skills\/?/, "");
|
|
13
|
-
|
|
13
|
+
const root = builtinSkillsRoot();
|
|
14
|
+
if (!suffix) {
|
|
15
|
+
return root;
|
|
16
|
+
}
|
|
17
|
+
const candidate = path.join(root, suffix);
|
|
18
|
+
if (existsSync(candidate)) {
|
|
19
|
+
return candidate;
|
|
20
|
+
}
|
|
21
|
+
const sourceFallback = root.includes(`${path.sep}dist${path.sep}skills`)
|
|
22
|
+
? path.join(path.resolve(root, "..", ".."), "skills", suffix)
|
|
23
|
+
: null;
|
|
24
|
+
if (sourceFallback && existsSync(sourceFallback)) {
|
|
25
|
+
return sourceFallback;
|
|
26
|
+
}
|
|
27
|
+
return candidate;
|
|
14
28
|
}
|
|
15
29
|
if (!normalized.startsWith("agents")) {
|
|
16
30
|
throw new Error(`Unsupported builtin subagent discovery path ${ref}`);
|
|
17
31
|
}
|
|
18
32
|
const suffix = normalized.replace(/^agents\/?/, "");
|
|
19
|
-
|
|
33
|
+
const root = path.join(builtinConfigRoot(), "agents");
|
|
34
|
+
if (!suffix) {
|
|
35
|
+
return root;
|
|
36
|
+
}
|
|
37
|
+
const candidate = path.join(root, suffix);
|
|
38
|
+
if (existsSync(candidate)) {
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
const sourceFallback = root.includes(`${path.sep}dist${path.sep}config${path.sep}agents`)
|
|
42
|
+
? path.join(path.resolve(root, "..", "..", ".."), "config", "agents", suffix)
|
|
43
|
+
: null;
|
|
44
|
+
if (sourceFallback && existsSync(sourceFallback)) {
|
|
45
|
+
return sourceFallback;
|
|
46
|
+
}
|
|
47
|
+
return candidate;
|
|
20
48
|
}
|
|
21
49
|
function preferPackageConvention(root, kind) {
|
|
22
50
|
const candidates = kind === "skills"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botbotgo/agent-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Agent Harness framework package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@botbotgo/agent-harness-builtin": "0.0.
|
|
28
|
+
"@botbotgo/agent-harness-builtin": "0.0.7"
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
|