@botbotgo/agent-harness 0.0.142 → 0.0.144
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/contracts/runtime.d.ts +14 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/adapter/flow/invocation-flow.d.ts +2 -0
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +2 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +1 -1
- package/dist/runtime/adapter/invocation-result.d.ts +2 -1
- package/dist/runtime/adapter/invocation-result.js +2 -0
- package/dist/runtime/adapter/local-tool-invocation.js +12 -0
- package/dist/runtime/adapter/model/invocation-request.d.ts +1 -0
- package/dist/runtime/adapter/model/invocation-request.js +13 -3
- package/dist/runtime/adapter/model/message-assembly.d.ts +1 -1
- package/dist/runtime/adapter/model/message-assembly.js +12 -1
- package/dist/runtime/agent-runtime-adapter.d.ts +2 -0
- package/dist/runtime/harness/run/stream-run.d.ts +2 -0
- package/dist/runtime/harness/run/stream-run.js +1 -0
- package/dist/runtime/harness/system/runtime-memory-candidates.d.ts +3 -0
- package/dist/runtime/harness/system/runtime-memory-candidates.js +110 -0
- package/dist/runtime/harness.d.ts +4 -0
- package/dist/runtime/harness.js +80 -2
- package/dist/tool-modules.d.ts +7 -0
- package/dist/tool-modules.js +1 -0
- package/dist/tools.d.ts +14 -0
- package/dist/workspace/object-loader.js +1 -0
- package/package.json +1 -1
|
@@ -97,6 +97,20 @@ export type RuntimeSnapshot = {
|
|
|
97
97
|
skills: RuntimeSnapshotSkill[];
|
|
98
98
|
memory: string[];
|
|
99
99
|
};
|
|
100
|
+
export type MemoryCandidate = {
|
|
101
|
+
content: string;
|
|
102
|
+
summary?: string;
|
|
103
|
+
kind?: string;
|
|
104
|
+
scope?: string;
|
|
105
|
+
confidence?: number;
|
|
106
|
+
tags?: string[];
|
|
107
|
+
sourceType?: string;
|
|
108
|
+
sourceRef?: string;
|
|
109
|
+
observedAt?: string;
|
|
110
|
+
sensitivity?: string;
|
|
111
|
+
noStore?: boolean;
|
|
112
|
+
provenance?: Record<string, unknown>;
|
|
113
|
+
};
|
|
100
114
|
/**
|
|
101
115
|
* Operator-facing projection of tool execution policy already compiled into a binding.
|
|
102
116
|
* This summarizes existing timeout, retry, validation, and retry-safety hints without
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.143";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.143";
|
|
@@ -12,6 +12,7 @@ export declare function executeRuntimeInvocation(options: {
|
|
|
12
12
|
context?: Record<string, unknown>;
|
|
13
13
|
state?: Record<string, unknown>;
|
|
14
14
|
files?: Record<string, unknown>;
|
|
15
|
+
memoryContext?: string;
|
|
15
16
|
};
|
|
16
17
|
resolveTools: (tools: CompiledTool[], binding?: CompiledAgentBinding) => unknown[];
|
|
17
18
|
getToolNameMapping: (binding: CompiledAgentBinding) => ToolNameMapping;
|
|
@@ -19,6 +20,7 @@ export declare function executeRuntimeInvocation(options: {
|
|
|
19
20
|
context?: Record<string, unknown>;
|
|
20
21
|
state?: Record<string, unknown>;
|
|
21
22
|
files?: Record<string, unknown>;
|
|
23
|
+
memoryContext?: string;
|
|
22
24
|
}) => Promise<Map<string, ExecutableTool>>;
|
|
23
25
|
callRuntimeWithToolParseRecovery: (request: unknown) => Promise<Record<string, unknown>>;
|
|
24
26
|
}): Promise<RunResult>;
|
|
@@ -15,6 +15,7 @@ export declare function streamRuntimeExecution(options: {
|
|
|
15
15
|
state?: Record<string, unknown>;
|
|
16
16
|
files?: Record<string, unknown>;
|
|
17
17
|
runId?: string;
|
|
18
|
+
memoryContext?: string;
|
|
18
19
|
};
|
|
19
20
|
primaryTools: CompiledTool[];
|
|
20
21
|
toolNameMapping: ToolNameMapping;
|
|
@@ -33,6 +34,7 @@ export declare function streamRuntimeExecution(options: {
|
|
|
33
34
|
context?: Record<string, unknown>;
|
|
34
35
|
state?: Record<string, unknown>;
|
|
35
36
|
files?: Record<string, unknown>;
|
|
37
|
+
memoryContext?: string;
|
|
36
38
|
}) => Promise<{
|
|
37
39
|
output: string;
|
|
38
40
|
metadata?: Record<string, unknown>;
|
|
@@ -8,7 +8,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
8
8
|
const request = buildInvocationRequest(options.binding, options.history, options.input, options.runtimeOptions);
|
|
9
9
|
try {
|
|
10
10
|
if (options.isLangChainBinding(options.binding) && options.canUseDirectModelStream && options.langChainStreamModel?.stream) {
|
|
11
|
-
const stream = await options.withTimeout(() => options.langChainStreamModel.stream(buildRawModelMessages(options.binding, options.getSystemPrompt(options.binding), options.history, options.input)), computeRemainingTimeoutMs(options.streamDeadlineAt, options.invokeTimeoutMs), "model stream start", "stream");
|
|
11
|
+
const stream = await options.withTimeout(() => options.langChainStreamModel.stream(buildRawModelMessages(options.binding, options.getSystemPrompt(options.binding), options.history, options.input, options.runtimeOptions.memoryContext)), computeRemainingTimeoutMs(options.streamDeadlineAt, options.invokeTimeoutMs), "model stream start", "stream");
|
|
12
12
|
let emitted = false;
|
|
13
13
|
const projected = projectTextStreamChunks(options.iterateWithTimeout(stream, options.streamIdleTimeoutMs, "model stream", options.streamDeadlineAt, options.invokeTimeoutMs));
|
|
14
14
|
let nextChunk = await projected.next();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { RunResult } from "../../contracts/types.js";
|
|
1
|
+
import type { MemoryCandidate, RunResult } from "../../contracts/types.js";
|
|
2
2
|
export type ExecutedToolResult = {
|
|
3
3
|
toolName: string;
|
|
4
4
|
output: unknown;
|
|
5
5
|
isError?: boolean;
|
|
6
|
+
memoryCandidates?: MemoryCandidate[];
|
|
6
7
|
};
|
|
7
8
|
export declare function finalizeInvocationResult(params: {
|
|
8
9
|
bindingAgentId: string;
|
|
@@ -18,6 +18,7 @@ export function finalizeInvocationResult(params) {
|
|
|
18
18
|
const structuredResponse = result.structuredResponse;
|
|
19
19
|
const files = asRecord(result.files);
|
|
20
20
|
const stateSnapshot = buildStateSnapshot(result);
|
|
21
|
+
const memoryCandidates = executedToolResults.flatMap((toolResult) => toolResult.memoryCandidates ?? []);
|
|
21
22
|
return {
|
|
22
23
|
threadId,
|
|
23
24
|
runId,
|
|
@@ -31,6 +32,7 @@ export function finalizeInvocationResult(params) {
|
|
|
31
32
|
...(structuredResponse !== undefined ? { structuredResponse } : {}),
|
|
32
33
|
metadata: {
|
|
33
34
|
...(executedToolResults.length > 0 ? { executedToolResults } : {}),
|
|
35
|
+
...(memoryCandidates.length > 0 ? { memoryCandidates } : {}),
|
|
34
36
|
...(structuredResponse !== undefined ? { structuredResponse } : {}),
|
|
35
37
|
...(outputContent !== undefined ? { outputContent } : {}),
|
|
36
38
|
...(contentBlocks.length > 0 ? { contentBlocks } : {}),
|
|
@@ -2,6 +2,7 @@ import { ToolMessage } from "@langchain/core/messages";
|
|
|
2
2
|
import { createModelFacingToolNameLookupCandidates, resolveModelFacingToolName } from "./tool/tool-name-mapping.js";
|
|
3
3
|
import { canReplayToolCallsLocally } from "./tool/tool-replay.js";
|
|
4
4
|
import { extractToolCallsFromResult, normalizeToolArgsForSchema, stringifyToolOutput } from "./tool/tool-arguments.js";
|
|
5
|
+
import { extractMemoryCandidatesFromToolOutput } from "../harness/system/runtime-memory-candidates.js";
|
|
5
6
|
export async function runLocalToolInvocationLoop({ binding, request, primaryTools, toolNameMapping, executableTools, builtinExecutableTools, callRuntimeWithToolParseRecovery, }) {
|
|
6
7
|
const executedToolResults = [];
|
|
7
8
|
let activeRequest = request;
|
|
@@ -9,6 +10,14 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
9
10
|
const maxToolIterations = 8;
|
|
10
11
|
let pendingResult;
|
|
11
12
|
let result;
|
|
13
|
+
const toolCatalog = new Map();
|
|
14
|
+
for (const tool of primaryTools) {
|
|
15
|
+
toolCatalog.set(tool.name, tool);
|
|
16
|
+
const modelFacingName = toolNameMapping.originalToModelFacing.get(tool.name);
|
|
17
|
+
if (modelFacingName) {
|
|
18
|
+
toolCatalog.set(modelFacingName, tool);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
12
21
|
for (let iteration = 0; iteration < maxToolIterations; iteration += 1) {
|
|
13
22
|
result = pendingResult ?? await callRuntimeWithToolParseRecovery(activeRequest);
|
|
14
23
|
pendingResult = undefined;
|
|
@@ -39,11 +48,14 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
39
48
|
if (!activeExecutable) {
|
|
40
49
|
throw new Error(`Tool ${toolCall.name} is not configured for this agent.`);
|
|
41
50
|
}
|
|
51
|
+
const compiledTool = toolCatalog.get(toolCall.name) ?? toolCatalog.get(resolvedToolName);
|
|
42
52
|
const normalizedArgs = normalizeToolArgsForSchema(toolCall.args, activeExecutable.schema);
|
|
43
53
|
const toolResult = await activeExecutable.invoke(normalizedArgs);
|
|
54
|
+
const memoryCandidates = compiledTool ? extractMemoryCandidatesFromToolOutput(compiledTool, toolResult) : [];
|
|
44
55
|
executedToolResults.push({
|
|
45
56
|
toolName: activeExecutable.name,
|
|
46
57
|
output: toolResult,
|
|
58
|
+
...(memoryCandidates.length > 0 ? { memoryCandidates } : {}),
|
|
47
59
|
});
|
|
48
60
|
nextMessages.push(new ToolMessage({
|
|
49
61
|
name: activeExecutable.name,
|
|
@@ -7,4 +7,5 @@ export declare function buildSlashCommandSkillInstruction(binding: CompiledAgent
|
|
|
7
7
|
export declare function buildInvocationRequest(binding: CompiledAgentBinding, history: TranscriptMessage[], input: MessageContent, options?: {
|
|
8
8
|
state?: Record<string, unknown>;
|
|
9
9
|
files?: Record<string, unknown>;
|
|
10
|
+
memoryContext?: string;
|
|
10
11
|
}): Record<string, unknown>;
|
|
@@ -37,11 +37,21 @@ export function buildSlashCommandSkillInstruction(binding, input) {
|
|
|
37
37
|
export function buildInvocationRequest(binding, history, input, options = {}) {
|
|
38
38
|
const userInvocableInstruction = buildSlashCommandSkillInstruction(binding, input);
|
|
39
39
|
const messages = buildAgentMessages(history, input);
|
|
40
|
+
const memoryInstruction = typeof options.memoryContext === "string" && options.memoryContext.trim().length > 0
|
|
41
|
+
? [
|
|
42
|
+
"Relevant durable memory was retrieved for this run.",
|
|
43
|
+
"Use it when it is still applicable, but prefer fresher direct evidence if there is any conflict.",
|
|
44
|
+
"",
|
|
45
|
+
options.memoryContext.trim(),
|
|
46
|
+
].join("\n")
|
|
47
|
+
: undefined;
|
|
40
48
|
return {
|
|
41
49
|
...(options.state ?? {}),
|
|
42
50
|
...(options.files ? { files: options.files } : {}),
|
|
43
|
-
messages:
|
|
44
|
-
? [{ role: "system", content:
|
|
45
|
-
:
|
|
51
|
+
messages: [
|
|
52
|
+
...(memoryInstruction ? [{ role: "system", content: memoryInstruction }] : []),
|
|
53
|
+
...(userInvocableInstruction ? [{ role: "system", content: userInvocableInstruction }] : []),
|
|
54
|
+
...messages,
|
|
55
|
+
],
|
|
46
56
|
};
|
|
47
57
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CompiledAgentBinding, MessageContent, TranscriptMessage } from "../../../contracts/types.js";
|
|
2
2
|
export declare function buildStateSnapshot(result: Record<string, unknown>): Record<string, unknown> | undefined;
|
|
3
|
-
export declare function buildRawModelMessages(binding: CompiledAgentBinding, systemPrompt: string | undefined, history: TranscriptMessage[], input: MessageContent): Array<{
|
|
3
|
+
export declare function buildRawModelMessages(binding: CompiledAgentBinding, systemPrompt: string | undefined, history: TranscriptMessage[], input: MessageContent, memoryContext?: string): Array<{
|
|
4
4
|
role: string;
|
|
5
5
|
content: MessageContent;
|
|
6
6
|
}>;
|
|
@@ -7,11 +7,22 @@ export function buildStateSnapshot(result) {
|
|
|
7
7
|
delete snapshot.files;
|
|
8
8
|
return Object.keys(snapshot).length > 0 ? snapshot : undefined;
|
|
9
9
|
}
|
|
10
|
-
export function buildRawModelMessages(binding, systemPrompt, history, input) {
|
|
10
|
+
export function buildRawModelMessages(binding, systemPrompt, history, input, memoryContext) {
|
|
11
11
|
const messages = [];
|
|
12
12
|
if (systemPrompt) {
|
|
13
13
|
messages.push({ role: "system", content: systemPrompt });
|
|
14
14
|
}
|
|
15
|
+
if (memoryContext?.trim()) {
|
|
16
|
+
messages.push({
|
|
17
|
+
role: "system",
|
|
18
|
+
content: [
|
|
19
|
+
"Relevant durable memory was retrieved for this run.",
|
|
20
|
+
"Use it when it still applies, but prefer fresher direct evidence if there is any conflict.",
|
|
21
|
+
"",
|
|
22
|
+
memoryContext.trim(),
|
|
23
|
+
].join("\n"),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
15
26
|
const userInvocableInstruction = buildSlashCommandSkillInstruction(binding, input);
|
|
16
27
|
if (userInvocableInstruction) {
|
|
17
28
|
messages.push({ role: "system", content: userInvocableInstruction });
|
|
@@ -80,12 +80,14 @@ export declare class AgentRuntimeAdapter {
|
|
|
80
80
|
context?: Record<string, unknown>;
|
|
81
81
|
state?: Record<string, unknown>;
|
|
82
82
|
files?: Record<string, unknown>;
|
|
83
|
+
memoryContext?: string;
|
|
83
84
|
}): Promise<RunResult>;
|
|
84
85
|
stream(binding: CompiledAgentBinding, input: MessageContent, threadId: string, history?: TranscriptMessage[], options?: {
|
|
85
86
|
context?: Record<string, unknown>;
|
|
86
87
|
state?: Record<string, unknown>;
|
|
87
88
|
files?: Record<string, unknown>;
|
|
88
89
|
runId?: string;
|
|
90
|
+
memoryContext?: string;
|
|
89
91
|
}): AsyncGenerator<RuntimeStreamChunk | string>;
|
|
90
92
|
}
|
|
91
93
|
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError, };
|
|
@@ -15,6 +15,7 @@ type StreamRunOptions = {
|
|
|
15
15
|
context?: Record<string, unknown>;
|
|
16
16
|
state?: Record<string, unknown>;
|
|
17
17
|
files?: Record<string, unknown>;
|
|
18
|
+
memoryContext?: string;
|
|
18
19
|
};
|
|
19
20
|
threadId: string;
|
|
20
21
|
runId: string;
|
|
@@ -28,6 +29,7 @@ type StreamRunOptions = {
|
|
|
28
29
|
state?: Record<string, unknown>;
|
|
29
30
|
files?: Record<string, unknown>;
|
|
30
31
|
runId: string;
|
|
32
|
+
memoryContext?: string;
|
|
31
33
|
}) => AsyncIterable<RuntimeStreamChunk>;
|
|
32
34
|
invokeWithHistory: (binding: CompiledAgentBinding, input: MessageContent, threadId: string, runId: string) => Promise<RunResult>;
|
|
33
35
|
emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<HarnessEvent>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CompiledTool, MemoryCandidate } from "../../../contracts/types.js";
|
|
2
|
+
export declare function extractMemoryCandidatesFromToolOutput(tool: CompiledTool, output: unknown): MemoryCandidate[];
|
|
3
|
+
export declare function renderMemoryCandidatesMarkdown(title: string, candidates: MemoryCandidate[]): string;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
function asRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
3
|
+
}
|
|
4
|
+
function asString(value) {
|
|
5
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
6
|
+
}
|
|
7
|
+
function asNumber(value) {
|
|
8
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
function asStringArray(value) {
|
|
11
|
+
if (!Array.isArray(value)) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const items = value
|
|
15
|
+
.filter((item) => typeof item === "string" && item.trim().length > 0)
|
|
16
|
+
.map((item) => item.trim());
|
|
17
|
+
return items.length > 0 ? items : undefined;
|
|
18
|
+
}
|
|
19
|
+
function stringifyCandidateContent(output) {
|
|
20
|
+
if (typeof output === "string") {
|
|
21
|
+
return output.trim();
|
|
22
|
+
}
|
|
23
|
+
if (typeof output === "number" || typeof output === "boolean") {
|
|
24
|
+
return String(output);
|
|
25
|
+
}
|
|
26
|
+
if (typeof output === "object" && output !== null) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.stringify(output, null, 2);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
function normalizeCandidate(value, fallback) {
|
|
37
|
+
const record = asRecord(value);
|
|
38
|
+
if (!record) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const content = asString(record.content);
|
|
42
|
+
if (!content) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
content,
|
|
47
|
+
...(asString(record.summary) ? { summary: asString(record.summary) } : {}),
|
|
48
|
+
...(asString(record.kind) ?? fallback.kind ? { kind: asString(record.kind) ?? fallback.kind } : {}),
|
|
49
|
+
...(asString(record.scope) ?? fallback.scope ? { scope: asString(record.scope) ?? fallback.scope } : {}),
|
|
50
|
+
...(asNumber(record.confidence) !== undefined ? { confidence: asNumber(record.confidence) } : {}),
|
|
51
|
+
...(asStringArray(record.tags) ?? fallback.tags ? { tags: asStringArray(record.tags) ?? fallback.tags } : {}),
|
|
52
|
+
...(asString(record.sourceType) ?? fallback.sourceType ? { sourceType: asString(record.sourceType) ?? fallback.sourceType } : {}),
|
|
53
|
+
...(asString(record.sourceRef) ? { sourceRef: asString(record.sourceRef) } : {}),
|
|
54
|
+
...(asString(record.observedAt) ? { observedAt: asString(record.observedAt) } : {}),
|
|
55
|
+
...(asString(record.sensitivity) ? { sensitivity: asString(record.sensitivity) } : {}),
|
|
56
|
+
...(typeof record.noStore === "boolean" ? { noStore: record.noStore } : {}),
|
|
57
|
+
...(asRecord(record.provenance) ? { provenance: asRecord(record.provenance) } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function extractMemoryCandidatesFromToolOutput(tool, output) {
|
|
61
|
+
const memoryConfig = asRecord(tool.config?.memory);
|
|
62
|
+
if (memoryConfig?.enabled !== true) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const fallback = {
|
|
66
|
+
sourceType: "tool-output",
|
|
67
|
+
kind: asString(memoryConfig.kind),
|
|
68
|
+
scope: asString(memoryConfig.scope) ?? "thread",
|
|
69
|
+
tags: asStringArray(memoryConfig.tags),
|
|
70
|
+
};
|
|
71
|
+
const explicitCandidates = asRecord(output)?.memoryCandidates;
|
|
72
|
+
if (Array.isArray(explicitCandidates)) {
|
|
73
|
+
const maxCandidates = asNumber(memoryConfig.maxCandidates) ?? explicitCandidates.length;
|
|
74
|
+
return explicitCandidates
|
|
75
|
+
.map((candidate) => normalizeCandidate(candidate, fallback))
|
|
76
|
+
.filter((candidate) => Boolean(candidate))
|
|
77
|
+
.slice(0, maxCandidates);
|
|
78
|
+
}
|
|
79
|
+
const content = stringifyCandidateContent(output);
|
|
80
|
+
if (!content) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
return [{
|
|
84
|
+
content,
|
|
85
|
+
...(fallback.kind ? { kind: fallback.kind } : {}),
|
|
86
|
+
...(fallback.scope ? { scope: fallback.scope } : {}),
|
|
87
|
+
...(fallback.tags ? { tags: fallback.tags } : {}),
|
|
88
|
+
sourceType: fallback.sourceType,
|
|
89
|
+
}];
|
|
90
|
+
}
|
|
91
|
+
export function renderMemoryCandidatesMarkdown(title, candidates) {
|
|
92
|
+
const lines = [`# ${title}`, ""];
|
|
93
|
+
if (candidates.length === 0) {
|
|
94
|
+
lines.push("(none)", "");
|
|
95
|
+
return lines.join("\n");
|
|
96
|
+
}
|
|
97
|
+
for (const candidate of candidates) {
|
|
98
|
+
lines.push(`## ${(candidate.summary ?? candidate.content).split("\n")[0].slice(0, 120)}`);
|
|
99
|
+
lines.push(`- kind: ${candidate.kind ?? "summary"}`);
|
|
100
|
+
lines.push(`- scope: ${candidate.scope ?? "thread"}`);
|
|
101
|
+
lines.push(`- source_type: ${candidate.sourceType ?? "tool-output"}`);
|
|
102
|
+
if (candidate.tags && candidate.tags.length > 0) {
|
|
103
|
+
lines.push(`- tags: ${candidate.tags.join(", ")}`);
|
|
104
|
+
}
|
|
105
|
+
lines.push("");
|
|
106
|
+
lines.push(candidate.summary ?? candidate.content);
|
|
107
|
+
lines.push("");
|
|
108
|
+
}
|
|
109
|
+
return lines.join("\n");
|
|
110
|
+
}
|
|
@@ -90,6 +90,10 @@ export declare class AgentHarnessRuntime {
|
|
|
90
90
|
private getRunCancellation;
|
|
91
91
|
private finalizeCancelledRun;
|
|
92
92
|
private invokeWithHistory;
|
|
93
|
+
private getWorkspaceMemoryNamespace;
|
|
94
|
+
private buildRuntimeMemoryContext;
|
|
95
|
+
private persistRuntimeMemoryCandidates;
|
|
96
|
+
private appendMemoryDigest;
|
|
93
97
|
private resolvePersistedRunPriority;
|
|
94
98
|
private enqueuePendingRunSlot;
|
|
95
99
|
private executeQueuedRun;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { SqlitePersistence } from "../persistence/sqlite-store.js";
|
|
2
3
|
import { createPersistentId } from "../utils/id.js";
|
|
3
4
|
import { AgentRuntimeAdapter } from "./agent-runtime-adapter.js";
|
|
@@ -28,6 +29,7 @@ import { bindingSupportsRunningReplay, getWorkspaceBinding, resolveWorkspaceAgen
|
|
|
28
29
|
import { describeWorkspaceInventory, getAgentInventoryRecord, listAgentSkills as listWorkspaceAgentSkills, } from "./harness/system/inventory.js";
|
|
29
30
|
import { createDefaultHealthSnapshot, isInventoryEnabled, isThreadMemorySyncEnabled, } from "./harness/runtime-defaults.js";
|
|
30
31
|
import { Mem0IngestionSync, readMem0RuntimeConfig } from "./harness/system/mem0-ingestion-sync.js";
|
|
32
|
+
import { renderMemoryCandidatesMarkdown } from "./harness/system/runtime-memory-candidates.js";
|
|
31
33
|
import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
|
|
32
34
|
import { initializeHarnessRuntime, reclaimExpiredClaimedRuns as reclaimHarnessExpiredClaimedRuns, recoverStartupRuns as recoverHarnessStartupRuns, isStaleRunningRun as isHarnessStaleRunningRun, } from "./harness/run/startup-runtime.js";
|
|
33
35
|
import { streamHarnessRun } from "./harness/run/stream-run.js";
|
|
@@ -349,9 +351,14 @@ export class AgentHarnessRuntime {
|
|
|
349
351
|
}
|
|
350
352
|
async invokeWithHistory(binding, input, threadId, runId, resumePayload, priorHistory, options = {}) {
|
|
351
353
|
const history = priorHistory ?? await this.loadPriorHistory(threadId, runId);
|
|
354
|
+
const memoryContext = options.memoryContext ?? await this.buildRuntimeMemoryContext(binding, threadId);
|
|
352
355
|
const startedAt = Date.now();
|
|
353
356
|
try {
|
|
354
|
-
const result = await this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, history,
|
|
357
|
+
const result = await this.runtimeAdapter.invoke(binding, input, threadId, runId, resumePayload, history, {
|
|
358
|
+
...options,
|
|
359
|
+
...(memoryContext ? { memoryContext } : {}),
|
|
360
|
+
});
|
|
361
|
+
await this.persistRuntimeMemoryCandidates(binding, threadId, runId, result.metadata?.memoryCandidates);
|
|
355
362
|
this.recordLlmSuccess(startedAt);
|
|
356
363
|
return result;
|
|
357
364
|
}
|
|
@@ -360,6 +367,74 @@ export class AgentHarnessRuntime {
|
|
|
360
367
|
throw error;
|
|
361
368
|
}
|
|
362
369
|
}
|
|
370
|
+
getWorkspaceMemoryNamespace(binding) {
|
|
371
|
+
const workspaceRoot = binding.harnessRuntime.workspaceRoot ?? this.workspace.workspaceRoot;
|
|
372
|
+
return ["memories", "workspaces", path.basename(workspaceRoot) || "default"];
|
|
373
|
+
}
|
|
374
|
+
async buildRuntimeMemoryContext(binding, threadId) {
|
|
375
|
+
const docs = await Promise.all([
|
|
376
|
+
this.runtimeMemoryStore.get(["memories", "threads", threadId], "durable-summary.md"),
|
|
377
|
+
this.runtimeMemoryStore.get(["memories", "threads", threadId], "tool-memory.md"),
|
|
378
|
+
this.runtimeMemoryStore.get(this.getWorkspaceMemoryNamespace(binding), "tool-memory.md"),
|
|
379
|
+
]);
|
|
380
|
+
const parts = docs
|
|
381
|
+
.map((doc) => doc?.value)
|
|
382
|
+
.map((value) => (typeof value?.content === "string" ? value.content.trim() : ""))
|
|
383
|
+
.filter((value) => value.length > 0);
|
|
384
|
+
if (parts.length === 0) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
return parts.join("\n\n");
|
|
388
|
+
}
|
|
389
|
+
async persistRuntimeMemoryCandidates(binding, threadId, runId, value) {
|
|
390
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const candidates = value
|
|
394
|
+
.filter((candidate) => typeof candidate === "object" && candidate !== null)
|
|
395
|
+
.filter((candidate) => candidate.noStore !== true && typeof candidate.content === "string" && candidate.content.trim().length > 0);
|
|
396
|
+
if (candidates.length === 0) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const threadCandidates = candidates.filter((candidate) => (candidate.scope ?? "thread") === "thread");
|
|
400
|
+
const workspaceCandidates = candidates.filter((candidate) => (candidate.scope ?? "thread") === "workspace");
|
|
401
|
+
const agentCandidates = candidates.filter((candidate) => (candidate.scope ?? "thread") === "agent");
|
|
402
|
+
await this.runtimeMemoryStore.put(["memories", "candidates", threadId], `${runId}.json`, {
|
|
403
|
+
runId,
|
|
404
|
+
threadId,
|
|
405
|
+
storedAt: new Date().toISOString(),
|
|
406
|
+
candidates,
|
|
407
|
+
});
|
|
408
|
+
const writes = [];
|
|
409
|
+
if (threadCandidates.length > 0) {
|
|
410
|
+
writes.push(this.appendMemoryDigest(["memories", "threads", threadId], "tool-memory.md", threadCandidates, 12, "Thread Tool Memory"));
|
|
411
|
+
}
|
|
412
|
+
if (workspaceCandidates.length > 0) {
|
|
413
|
+
writes.push(this.appendMemoryDigest(this.getWorkspaceMemoryNamespace(binding), "tool-memory.md", workspaceCandidates, 20, "Workspace Tool Memory"));
|
|
414
|
+
}
|
|
415
|
+
if (agentCandidates.length > 0) {
|
|
416
|
+
writes.push(this.appendMemoryDigest(["memories", "agents", binding.agent.id], "tool-memory.md", agentCandidates, 20, "Agent Tool Memory"));
|
|
417
|
+
}
|
|
418
|
+
await Promise.all(writes);
|
|
419
|
+
}
|
|
420
|
+
async appendMemoryDigest(namespace, key, candidates, maxEntries, title) {
|
|
421
|
+
const existing = await this.runtimeMemoryStore.get(namespace, key);
|
|
422
|
+
const existingItems = Array.isArray(existing?.value?.items)
|
|
423
|
+
? ((existing?.value).items ?? [])
|
|
424
|
+
: [];
|
|
425
|
+
const merged = [...existingItems];
|
|
426
|
+
for (const candidate of candidates) {
|
|
427
|
+
if (merged.some((entry) => entry.content === candidate.content && (entry.scope ?? "thread") === (candidate.scope ?? "thread"))) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
merged.push(candidate);
|
|
431
|
+
}
|
|
432
|
+
const recent = merged.slice(-maxEntries);
|
|
433
|
+
await this.runtimeMemoryStore.put(namespace, key, {
|
|
434
|
+
content: `${renderMemoryCandidatesMarkdown(title, recent)}\n`,
|
|
435
|
+
items: recent,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
363
438
|
async resolvePersistedRunPriority(threadId, runId) {
|
|
364
439
|
const persisted = await this.persistence.getRunRequest(threadId, runId);
|
|
365
440
|
return normalizeRunPriority(persisted?.priority);
|
|
@@ -514,7 +589,10 @@ export class AgentHarnessRuntime {
|
|
|
514
589
|
const stream = streamHarnessRun({
|
|
515
590
|
binding,
|
|
516
591
|
input: options.input,
|
|
517
|
-
invocation
|
|
592
|
+
invocation: {
|
|
593
|
+
...invocation,
|
|
594
|
+
memoryContext: await this.buildRuntimeMemoryContext(binding, threadId),
|
|
595
|
+
},
|
|
518
596
|
threadId,
|
|
519
597
|
runId,
|
|
520
598
|
selectedAgentId,
|
package/dist/tool-modules.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export type LoadedToolModule = {
|
|
|
6
6
|
schema: ReturnType<typeof normalizeToolSchema>;
|
|
7
7
|
description: string;
|
|
8
8
|
retryable?: boolean;
|
|
9
|
+
memory?: {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
kind?: string;
|
|
12
|
+
scope?: string;
|
|
13
|
+
maxCandidates?: number;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
};
|
|
9
16
|
};
|
|
10
17
|
export type LoadedSkillModule = {
|
|
11
18
|
name: string;
|
package/dist/tool-modules.js
CHANGED
|
@@ -22,6 +22,7 @@ function loadToolObjectDefinition(imported, exportName) {
|
|
|
22
22
|
schema: normalizeToolSchema(definition.schema),
|
|
23
23
|
description: definition.description.trim(),
|
|
24
24
|
retryable: definition.retryable === true,
|
|
25
|
+
...(definition.memory ? { memory: { ...definition.memory } } : {}),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
export function isSupportedToolModulePath(filePath) {
|
package/dist/tools.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export type ToolDefinitionObject = {
|
|
|
6
6
|
description: string;
|
|
7
7
|
schema: SchemaInput;
|
|
8
8
|
retryable?: boolean;
|
|
9
|
+
memory?: {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
kind?: string;
|
|
12
|
+
scope?: string;
|
|
13
|
+
maxCandidates?: number;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
};
|
|
9
16
|
invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
10
17
|
[TOOL_DEFINITION_MARKER]: true;
|
|
11
18
|
};
|
|
@@ -17,6 +24,13 @@ export declare function tool(definition: {
|
|
|
17
24
|
description: string;
|
|
18
25
|
schema: SchemaInput;
|
|
19
26
|
retryable?: boolean;
|
|
27
|
+
memory?: {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
kind?: string;
|
|
30
|
+
scope?: string;
|
|
31
|
+
maxCandidates?: number;
|
|
32
|
+
tags?: string[];
|
|
33
|
+
};
|
|
20
34
|
invoke: (input: unknown, context?: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
21
35
|
}): ToolDefinitionObject;
|
|
22
36
|
export {};
|
|
@@ -762,6 +762,7 @@ export async function readToolModuleItems(root) {
|
|
|
762
762
|
description: definition.description,
|
|
763
763
|
implementationName: definition.implementationName,
|
|
764
764
|
...(definition.retryable !== undefined ? { retryable: definition.retryable } : {}),
|
|
765
|
+
...(definition.memory ? { config: { memory: definition.memory } } : {}),
|
|
765
766
|
},
|
|
766
767
|
sourcePath: filePath,
|
|
767
768
|
});
|