@aigne/agent-runtime 1.12.0-beta.5
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/LICENSE.md +26 -0
- package/README.md +79 -0
- package/dist/agent-runners/ai-runner.cjs +133 -0
- package/dist/agent-runners/ai-runner.d.cts +33 -0
- package/dist/agent-runners/ai-runner.d.cts.map +1 -0
- package/dist/agent-runners/ai-runner.d.mts +33 -0
- package/dist/agent-runners/ai-runner.d.mts.map +1 -0
- package/dist/agent-runners/ai-runner.mjs +134 -0
- package/dist/agent-runners/ai-runner.mjs.map +1 -0
- package/dist/agent-runners/ash-runner.cjs +272 -0
- package/dist/agent-runners/ash-runner.d.cts +30 -0
- package/dist/agent-runners/ash-runner.d.cts.map +1 -0
- package/dist/agent-runners/ash-runner.d.mts +30 -0
- package/dist/agent-runners/ash-runner.d.mts.map +1 -0
- package/dist/agent-runners/ash-runner.mjs +273 -0
- package/dist/agent-runners/ash-runner.mjs.map +1 -0
- package/dist/agent-runners/budget.cjs +71 -0
- package/dist/agent-runners/budget.d.cts +40 -0
- package/dist/agent-runners/budget.d.cts.map +1 -0
- package/dist/agent-runners/budget.d.mts +40 -0
- package/dist/agent-runners/budget.d.mts.map +1 -0
- package/dist/agent-runners/budget.mjs +67 -0
- package/dist/agent-runners/budget.mjs.map +1 -0
- package/dist/agent-runners/code-runner.cjs +61 -0
- package/dist/agent-runners/code-runner.mjs +61 -0
- package/dist/agent-runners/code-runner.mjs.map +1 -0
- package/dist/agent-runners/env-whitelist.cjs +36 -0
- package/dist/agent-runners/env-whitelist.d.cts +5 -0
- package/dist/agent-runners/env-whitelist.d.cts.map +1 -0
- package/dist/agent-runners/env-whitelist.d.mts +5 -0
- package/dist/agent-runners/env-whitelist.d.mts.map +1 -0
- package/dist/agent-runners/env-whitelist.mjs +36 -0
- package/dist/agent-runners/env-whitelist.mjs.map +1 -0
- package/dist/agent-runners/guide-rails.cjs +47 -0
- package/dist/agent-runners/guide-rails.d.cts +16 -0
- package/dist/agent-runners/guide-rails.d.cts.map +1 -0
- package/dist/agent-runners/guide-rails.d.mts +16 -0
- package/dist/agent-runners/guide-rails.d.mts.map +1 -0
- package/dist/agent-runners/guide-rails.mjs +48 -0
- package/dist/agent-runners/guide-rails.mjs.map +1 -0
- package/dist/agent-runners/hook-channel.cjs +55 -0
- package/dist/agent-runners/hook-channel.d.cts +38 -0
- package/dist/agent-runners/hook-channel.d.cts.map +1 -0
- package/dist/agent-runners/hook-channel.d.mts +38 -0
- package/dist/agent-runners/hook-channel.d.mts.map +1 -0
- package/dist/agent-runners/hook-channel.mjs +55 -0
- package/dist/agent-runners/hook-channel.mjs.map +1 -0
- package/dist/agent-runners/hooks.cjs +107 -0
- package/dist/agent-runners/hooks.d.cts +41 -0
- package/dist/agent-runners/hooks.d.cts.map +1 -0
- package/dist/agent-runners/hooks.d.mts +41 -0
- package/dist/agent-runners/hooks.d.mts.map +1 -0
- package/dist/agent-runners/hooks.mjs +107 -0
- package/dist/agent-runners/hooks.mjs.map +1 -0
- package/dist/agent-runners/memory-afs.cjs +342 -0
- package/dist/agent-runners/memory-afs.d.cts +221 -0
- package/dist/agent-runners/memory-afs.d.cts.map +1 -0
- package/dist/agent-runners/memory-afs.d.mts +221 -0
- package/dist/agent-runners/memory-afs.d.mts.map +1 -0
- package/dist/agent-runners/memory-afs.mjs +326 -0
- package/dist/agent-runners/memory-afs.mjs.map +1 -0
- package/dist/agent-runners/memory-archive.cjs +263 -0
- package/dist/agent-runners/memory-archive.d.cts +47 -0
- package/dist/agent-runners/memory-archive.d.cts.map +1 -0
- package/dist/agent-runners/memory-archive.d.mts +47 -0
- package/dist/agent-runners/memory-archive.d.mts.map +1 -0
- package/dist/agent-runners/memory-archive.mjs +257 -0
- package/dist/agent-runners/memory-archive.mjs.map +1 -0
- package/dist/agent-runners/orchestrator-runner.cjs +302 -0
- package/dist/agent-runners/orchestrator-runner.d.cts +23 -0
- package/dist/agent-runners/orchestrator-runner.d.cts.map +1 -0
- package/dist/agent-runners/orchestrator-runner.d.mts +23 -0
- package/dist/agent-runners/orchestrator-runner.d.mts.map +1 -0
- package/dist/agent-runners/orchestrator-runner.mjs +303 -0
- package/dist/agent-runners/orchestrator-runner.mjs.map +1 -0
- package/dist/agent-runners/orchestrator-state.cjs +68 -0
- package/dist/agent-runners/orchestrator-state.d.cts +23 -0
- package/dist/agent-runners/orchestrator-state.d.cts.map +1 -0
- package/dist/agent-runners/orchestrator-state.d.mts +23 -0
- package/dist/agent-runners/orchestrator-state.d.mts.map +1 -0
- package/dist/agent-runners/orchestrator-state.mjs +65 -0
- package/dist/agent-runners/orchestrator-state.mjs.map +1 -0
- package/dist/agent-runners/retry.cjs +30 -0
- package/dist/agent-runners/retry.d.cts +13 -0
- package/dist/agent-runners/retry.d.cts.map +1 -0
- package/dist/agent-runners/retry.d.mts +13 -0
- package/dist/agent-runners/retry.d.mts.map +1 -0
- package/dist/agent-runners/retry.mjs +30 -0
- package/dist/agent-runners/retry.mjs.map +1 -0
- package/dist/agent-runners/scratchpad-afs.cjs +276 -0
- package/dist/agent-runners/scratchpad-afs.d.cts +103 -0
- package/dist/agent-runners/scratchpad-afs.d.cts.map +1 -0
- package/dist/agent-runners/scratchpad-afs.d.mts +103 -0
- package/dist/agent-runners/scratchpad-afs.d.mts.map +1 -0
- package/dist/agent-runners/scratchpad-afs.mjs +271 -0
- package/dist/agent-runners/scratchpad-afs.mjs.map +1 -0
- package/dist/agent-runners/stream-router.cjs +72 -0
- package/dist/agent-runners/stream-router.d.cts +22 -0
- package/dist/agent-runners/stream-router.d.cts.map +1 -0
- package/dist/agent-runners/stream-router.d.mts +22 -0
- package/dist/agent-runners/stream-router.d.mts.map +1 -0
- package/dist/agent-runners/stream-router.mjs +72 -0
- package/dist/agent-runners/stream-router.mjs.map +1 -0
- package/dist/agent-runners/system-prompt.cjs +35 -0
- package/dist/agent-runners/system-prompt.d.cts +17 -0
- package/dist/agent-runners/system-prompt.d.cts.map +1 -0
- package/dist/agent-runners/system-prompt.d.mts +17 -0
- package/dist/agent-runners/system-prompt.d.mts.map +1 -0
- package/dist/agent-runners/system-prompt.mjs +34 -0
- package/dist/agent-runners/system-prompt.mjs.map +1 -0
- package/dist/agent-runners/team-modes/aggregates.cjs +36 -0
- package/dist/agent-runners/team-modes/aggregates.mjs +35 -0
- package/dist/agent-runners/team-modes/aggregates.mjs.map +1 -0
- package/dist/agent-runners/team-modes/iterate.cjs +46 -0
- package/dist/agent-runners/team-modes/iterate.mjs +47 -0
- package/dist/agent-runners/team-modes/iterate.mjs.map +1 -0
- package/dist/agent-runners/team-modes/parallel.cjs +59 -0
- package/dist/agent-runners/team-modes/parallel.mjs +60 -0
- package/dist/agent-runners/team-modes/parallel.mjs.map +1 -0
- package/dist/agent-runners/team-modes/reflection.cjs +155 -0
- package/dist/agent-runners/team-modes/reflection.mjs +156 -0
- package/dist/agent-runners/team-modes/reflection.mjs.map +1 -0
- package/dist/agent-runners/team-modes/sequential.cjs +63 -0
- package/dist/agent-runners/team-modes/sequential.mjs +64 -0
- package/dist/agent-runners/team-modes/sequential.mjs.map +1 -0
- package/dist/agent-runners/team-modes/utils.cjs +39 -0
- package/dist/agent-runners/team-modes/utils.mjs +36 -0
- package/dist/agent-runners/team-modes/utils.mjs.map +1 -0
- package/dist/agent-runners/team-runner.cjs +277 -0
- package/dist/agent-runners/team-runner.d.cts +34 -0
- package/dist/agent-runners/team-runner.d.cts.map +1 -0
- package/dist/agent-runners/team-runner.d.mts +34 -0
- package/dist/agent-runners/team-runner.d.mts.map +1 -0
- package/dist/agent-runners/team-runner.mjs +278 -0
- package/dist/agent-runners/team-runner.mjs.map +1 -0
- package/dist/agent-runners/template-context.cjs +32 -0
- package/dist/agent-runners/template-context.d.cts +43 -0
- package/dist/agent-runners/template-context.d.cts.map +1 -0
- package/dist/agent-runners/template-context.d.mts +43 -0
- package/dist/agent-runners/template-context.d.mts.map +1 -0
- package/dist/agent-runners/template-context.mjs +33 -0
- package/dist/agent-runners/template-context.mjs.map +1 -0
- package/dist/index.cjs +123 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.mjs +29 -0
- package/dist/program/agent-action.cjs +496 -0
- package/dist/program/agent-action.d.cts +42 -0
- package/dist/program/agent-action.d.cts.map +1 -0
- package/dist/program/agent-action.d.mts +42 -0
- package/dist/program/agent-action.d.mts.map +1 -0
- package/dist/program/agent-action.mjs +497 -0
- package/dist/program/agent-action.mjs.map +1 -0
- package/dist/program/agent-messages-tools.cjs +137 -0
- package/dist/program/agent-messages-tools.d.cts +59 -0
- package/dist/program/agent-messages-tools.d.cts.map +1 -0
- package/dist/program/agent-messages-tools.d.mts +59 -0
- package/dist/program/agent-messages-tools.d.mts.map +1 -0
- package/dist/program/agent-messages-tools.mjs +130 -0
- package/dist/program/agent-messages-tools.mjs.map +1 -0
- package/dist/program/agent-runner-registry.cjs +314 -0
- package/dist/program/agent-runner-registry.d.cts +52 -0
- package/dist/program/agent-runner-registry.d.cts.map +1 -0
- package/dist/program/agent-runner-registry.d.mts +52 -0
- package/dist/program/agent-runner-registry.d.mts.map +1 -0
- package/dist/program/agent-runner-registry.mjs +313 -0
- package/dist/program/agent-runner-registry.mjs.map +1 -0
- package/dist/program/agent-subscribe.cjs +72 -0
- package/dist/program/agent-subscribe.d.cts +18 -0
- package/dist/program/agent-subscribe.d.cts.map +1 -0
- package/dist/program/agent-subscribe.d.mts +18 -0
- package/dist/program/agent-subscribe.d.mts.map +1 -0
- package/dist/program/agent-subscribe.mjs +73 -0
- package/dist/program/agent-subscribe.mjs.map +1 -0
- package/dist/program/agent-tools.cjs +135 -0
- package/dist/program/agent-tools.d.cts +35 -0
- package/dist/program/agent-tools.d.cts.map +1 -0
- package/dist/program/agent-tools.d.mts +35 -0
- package/dist/program/agent-tools.d.mts.map +1 -0
- package/dist/program/agent-tools.mjs +136 -0
- package/dist/program/agent-tools.mjs.map +1 -0
- package/dist/program/manifest-nesting.cjs +57 -0
- package/dist/program/manifest-nesting.d.cts +13 -0
- package/dist/program/manifest-nesting.d.cts.map +1 -0
- package/dist/program/manifest-nesting.d.mts +13 -0
- package/dist/program/manifest-nesting.d.mts.map +1 -0
- package/dist/program/manifest-nesting.mjs +57 -0
- package/dist/program/manifest-nesting.mjs.map +1 -0
- package/dist/program/result-contract.cjs +55 -0
- package/dist/program/result-contract.d.cts +23 -0
- package/dist/program/result-contract.d.cts.map +1 -0
- package/dist/program/result-contract.d.mts +23 -0
- package/dist/program/result-contract.d.mts.map +1 -0
- package/dist/program/result-contract.mjs +51 -0
- package/dist/program/result-contract.mjs.map +1 -0
- package/dist/program/runner-error.cjs +71 -0
- package/dist/program/runner-error.d.cts +25 -0
- package/dist/program/runner-error.d.cts.map +1 -0
- package/dist/program/runner-error.d.mts +25 -0
- package/dist/program/runner-error.d.mts.map +1 -0
- package/dist/program/runner-error.mjs +72 -0
- package/dist/program/runner-error.mjs.map +1 -0
- package/dist/program/scoped-afs.cjs +538 -0
- package/dist/program/scoped-afs.d.cts +245 -0
- package/dist/program/scoped-afs.d.cts.map +1 -0
- package/dist/program/scoped-afs.d.mts +245 -0
- package/dist/program/scoped-afs.d.mts.map +1 -0
- package/dist/program/scoped-afs.mjs +531 -0
- package/dist/program/scoped-afs.mjs.map +1 -0
- package/dist/program/session-hierarchy.cjs +1 -0
- package/dist/program/session-hierarchy.d.cts +2 -0
- package/dist/program/session-hierarchy.d.mts +2 -0
- package/dist/program/session-hierarchy.mjs +3 -0
- package/dist/program/tool-discovery/ash-frontmatter.cjs +83 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.cts +21 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.cts.map +1 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.mts +21 -0
- package/dist/program/tool-discovery/ash-frontmatter.d.mts.map +1 -0
- package/dist/program/tool-discovery/ash-frontmatter.mjs +84 -0
- package/dist/program/tool-discovery/ash-frontmatter.mjs.map +1 -0
- package/dist/program/tool-discovery/dot-path-resolver.cjs +60 -0
- package/dist/program/tool-discovery/dot-path-resolver.mjs +61 -0
- package/dist/program/tool-discovery/dot-path-resolver.mjs.map +1 -0
- package/dist/program/tool-discovery/skill-gate.cjs +36 -0
- package/dist/program/tool-discovery/skill-gate.d.cts +10 -0
- package/dist/program/tool-discovery/skill-gate.d.cts.map +1 -0
- package/dist/program/tool-discovery/skill-gate.d.mts +10 -0
- package/dist/program/tool-discovery/skill-gate.d.mts.map +1 -0
- package/dist/program/tool-discovery/skill-gate.mjs +35 -0
- package/dist/program/tool-discovery/skill-gate.mjs.map +1 -0
- package/dist/providers/basic/ash/dist/index.cjs +1809 -0
- package/dist/providers/basic/ash/dist/index.mjs +1807 -0
- package/dist/providers/basic/ash/dist/index.mjs.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
let ufo = require("ufo");
|
|
2
|
+
let _aigne_afs_index = require("@aigne/afs-index");
|
|
3
|
+
|
|
4
|
+
//#region src/agent-runners/memory-archive.ts
|
|
5
|
+
/** Upper bound on per-entry `content` payload (chars). Matches design §5. */
|
|
6
|
+
const ARCHIVE_CONTENT_LIMIT = 2048;
|
|
7
|
+
/**
|
|
8
|
+
* Stable 16-char hex hash (two interleaved FNV-1a 32-bit streams). Used by
|
|
9
|
+
* reflection archive to bind reviewer verdicts to the actor output they
|
|
10
|
+
* reviewed via `meta.target_hash`; same primitive is reused by forthcoming
|
|
11
|
+
* G1/G2 wirings for args-hashing.
|
|
12
|
+
*/
|
|
13
|
+
function contentHash(s) {
|
|
14
|
+
let a = -2128831035;
|
|
15
|
+
let b = 461845907;
|
|
16
|
+
for (let i = 0; i < s.length; i++) {
|
|
17
|
+
const c = s.charCodeAt(i);
|
|
18
|
+
a = Math.imul(a ^ c, 16777619);
|
|
19
|
+
b = Math.imul(b ^ c + i, 2246822507);
|
|
20
|
+
}
|
|
21
|
+
const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
22
|
+
return toHex(a) + toHex(b);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Stringify a value for archival `content`, bounding the output at
|
|
26
|
+
* `ARCHIVE_CONTENT_LIMIT` chars (truncated with `…` when over).
|
|
27
|
+
*/
|
|
28
|
+
function toContentString(v) {
|
|
29
|
+
const s = typeof v === "string" ? v : JSON.stringify(v ?? "");
|
|
30
|
+
return s.length > ARCHIVE_CONTENT_LIMIT ? `${s.slice(0, ARCHIVE_CONTENT_LIMIT - 1)}…` : s;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fire-and-forget call to `/memory/.actions/remember`. Silent when `afs`
|
|
34
|
+
* is absent or when the archive dispatch throws / returns non-success —
|
|
35
|
+
* by design, archive failures must never break the surrounding agent
|
|
36
|
+
* flow.
|
|
37
|
+
*/
|
|
38
|
+
async function tryRemember(afs, args) {
|
|
39
|
+
if (!afs?.exec) return;
|
|
40
|
+
try {
|
|
41
|
+
await afs.exec("/memory/.actions/remember", args, {});
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
/** Upper bound on per-tool-call archived `content` (chars). */
|
|
45
|
+
const TOOL_CALL_CONTENT_LIMIT = 300;
|
|
46
|
+
/** Paths whose invocations must NEVER be archived (recursion guard). */
|
|
47
|
+
const MEMORY_SELF_ACTION_PREFIX = "/memory/.actions/";
|
|
48
|
+
/**
|
|
49
|
+
* Decides whether `wrapToolProgress` should archive calls for this
|
|
50
|
+
* manifest's `memory` field. Default-on when memory is enabled; only
|
|
51
|
+
* `archiveTools: false` opts out.
|
|
52
|
+
*/
|
|
53
|
+
function isMemoryArchiveEnabled(memory) {
|
|
54
|
+
if (memory === true) return true;
|
|
55
|
+
if (memory && typeof memory === "object") return memory.archiveTools !== false;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Single-call archive: maps a `ToolCallRecord` to `/memory/.actions/remember`
|
|
60
|
+
* args per design §3.2. Double-guard against self-calls; fire-and-forget.
|
|
61
|
+
*
|
|
62
|
+
* The anchors emitted are a union of three sources:
|
|
63
|
+
*
|
|
64
|
+
* 1. The tool name (`afs_exec`, `custom_tool`, …) — generic but
|
|
65
|
+
* always present.
|
|
66
|
+
* 2. The literal `tool_call` topic — lets cross-session recall
|
|
67
|
+
* filter to "any past tool call" without naming a specific
|
|
68
|
+
* tool.
|
|
69
|
+
* 3. Path-derived topics from `extractPathTopicAnchors(call.path)`
|
|
70
|
+
* — pulls the same tier-1 retrieval signal that lifts R@1 from
|
|
71
|
+
* 13% → 100% on the docs/ corpus into tool-call observations.
|
|
72
|
+
* A run that called `/blocklets/runtime-demos/.actions/
|
|
73
|
+
* s12-memory-recall-run` becomes recallable by topic
|
|
74
|
+
* "s12-memory-recall-run", "runtime-demos", or "memory-recall-run"
|
|
75
|
+
* — not just by the generic "afs_exec".
|
|
76
|
+
*
|
|
77
|
+
* All three coalesce into a single `topic`-typed anchor with deduped
|
|
78
|
+
* `values` so the index keeps the lookup table compact.
|
|
79
|
+
*/
|
|
80
|
+
async function archiveToolCall(afs, call) {
|
|
81
|
+
if (!afs?.exec) return;
|
|
82
|
+
if (call.path.startsWith(MEMORY_SELF_ACTION_PREFIX)) return;
|
|
83
|
+
const argsStr = toContentString(call.args);
|
|
84
|
+
const content = argsStr.slice(0, TOOL_CALL_CONTENT_LIMIT);
|
|
85
|
+
const args_hash = contentHash(argsStr);
|
|
86
|
+
const duration_ms = Math.max(0, call.endTs - call.startTs);
|
|
87
|
+
const meta = {
|
|
88
|
+
tool: call.tool,
|
|
89
|
+
path: call.path,
|
|
90
|
+
status: call.status,
|
|
91
|
+
args_hash,
|
|
92
|
+
role: "tool",
|
|
93
|
+
round: call.round,
|
|
94
|
+
session: call.session,
|
|
95
|
+
duration_ms
|
|
96
|
+
};
|
|
97
|
+
if (call.error !== void 0) meta.error = call.error;
|
|
98
|
+
const pathValues = (0, _aigne_afs_index.extractPathTopicAnchors)(call.path).flatMap((a) => a.values);
|
|
99
|
+
await tryRemember(afs, {
|
|
100
|
+
content,
|
|
101
|
+
level: "observation",
|
|
102
|
+
layer: "execution",
|
|
103
|
+
anchors: [{
|
|
104
|
+
type: "topic",
|
|
105
|
+
values: [...new Set([
|
|
106
|
+
call.tool,
|
|
107
|
+
"tool_call",
|
|
108
|
+
...pathValues
|
|
109
|
+
])],
|
|
110
|
+
confidence: 1
|
|
111
|
+
}],
|
|
112
|
+
meta
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const ORPHAN_TTL_MS = 6e4;
|
|
116
|
+
function asString(v) {
|
|
117
|
+
return typeof v === "string" ? v : "";
|
|
118
|
+
}
|
|
119
|
+
function asRound(v) {
|
|
120
|
+
return typeof v === "number" ? v : 0;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Wrap a user-supplied `_on_tool_progress` callback so each completed
|
|
124
|
+
* (non-self, non-pending) tool call fires a fire-and-forget archive to
|
|
125
|
+
* the scope-local memory provider. See design §3.2.
|
|
126
|
+
*
|
|
127
|
+
* Always passes every event through to `userCallback` first — archive is
|
|
128
|
+
* a side channel and must be transparent to the caller.
|
|
129
|
+
*/
|
|
130
|
+
function wrapToolProgress(userCallback, ctx) {
|
|
131
|
+
const now = ctx.now ?? (() => Date.now());
|
|
132
|
+
const startMap = /* @__PURE__ */ new Map();
|
|
133
|
+
const wrapped = (event) => {
|
|
134
|
+
try {
|
|
135
|
+
userCallback?.(event);
|
|
136
|
+
} catch {}
|
|
137
|
+
const currentNow = now();
|
|
138
|
+
const cutoff = currentNow - ORPHAN_TTL_MS;
|
|
139
|
+
for (const [id, entry] of startMap) if (entry.startTs < cutoff) startMap.delete(id);
|
|
140
|
+
const calls = Array.isArray(event?.calls) ? event.calls : [];
|
|
141
|
+
const eventRound = asRound(event?.round);
|
|
142
|
+
if (event?.type === "tool_start") {
|
|
143
|
+
for (const call of calls) if (call?.id) startMap.set(call.id, {
|
|
144
|
+
startTs: currentNow,
|
|
145
|
+
round: eventRound,
|
|
146
|
+
args: call.args
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (event?.type !== "tool_result") return;
|
|
151
|
+
for (const call of calls) {
|
|
152
|
+
if (!call?.id) continue;
|
|
153
|
+
const path = asString(call.path);
|
|
154
|
+
if (path.startsWith(MEMORY_SELF_ACTION_PREFIX)) continue;
|
|
155
|
+
if (call.status === "pending") continue;
|
|
156
|
+
const start = startMap.get(call.id);
|
|
157
|
+
startMap.delete(call.id);
|
|
158
|
+
archiveToolCall(ctx.afs, {
|
|
159
|
+
id: call.id,
|
|
160
|
+
tool: asString(call.tool),
|
|
161
|
+
path,
|
|
162
|
+
status: call.status === "error" ? "error" : "ok",
|
|
163
|
+
args: call.args ?? start?.args,
|
|
164
|
+
error: typeof call.error === "string" ? call.error : void 0,
|
|
165
|
+
startTs: start?.startTs ?? currentNow,
|
|
166
|
+
endTs: currentNow,
|
|
167
|
+
round: start?.round ?? eventRound,
|
|
168
|
+
session: ctx.session
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
Object.defineProperty(wrapped, "__pendingSize", {
|
|
173
|
+
value: () => startMap.size,
|
|
174
|
+
enumerable: false,
|
|
175
|
+
configurable: true
|
|
176
|
+
});
|
|
177
|
+
return wrapped;
|
|
178
|
+
}
|
|
179
|
+
const HANDOFF_CONTENT_LIMIT = 200;
|
|
180
|
+
const HANDOFF_DEFAULT_LIMIT = 3;
|
|
181
|
+
/**
|
|
182
|
+
* Opt-in flag: only `{ handoffReadThrough: true }` turns on the appendix.
|
|
183
|
+
* Boolean-form `memory: true` does NOT enable handoff read-through — it
|
|
184
|
+
* stays archive-only by default.
|
|
185
|
+
*/
|
|
186
|
+
function isHandoffReadThroughEnabled(memory) {
|
|
187
|
+
if (!memory || typeof memory !== "object") return false;
|
|
188
|
+
return memory.handoffReadThrough === true;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Linear scan of `/memory/${domain}/observations` → top-N execution-layer
|
|
192
|
+
* entries sorted by createdAt DESC. Fire-and-forget on read: every failure
|
|
193
|
+
* mode (missing provider, list rejection, missing path) collapses to `[]`.
|
|
194
|
+
*
|
|
195
|
+
* Intentionally does not filter by anchors/memberName — memory provider
|
|
196
|
+
* stores anchors in the index, not in `meta`, so linear scan can't see
|
|
197
|
+
* them. See design §2.2.2.
|
|
198
|
+
*/
|
|
199
|
+
async function recallForHandoff(opts) {
|
|
200
|
+
if (!opts.afs?.list) return [];
|
|
201
|
+
try {
|
|
202
|
+
const path = (0, ufo.joinURL)("/memory", opts.domain, "observations");
|
|
203
|
+
return ((await opts.afs.list(path))?.data ?? []).filter((e) => e.meta?.layer === "execution").sort((a, b) => toEpoch(b.createdAt) - toEpoch(a.createdAt)).slice(0, opts.limit ?? HANDOFF_DEFAULT_LIMIT).map((e) => ({
|
|
204
|
+
content: typeof e.content === "string" ? e.content : JSON.stringify(e.content ?? ""),
|
|
205
|
+
meta: e.meta ?? {},
|
|
206
|
+
createdAt: toEpoch(e.createdAt)
|
|
207
|
+
}));
|
|
208
|
+
} catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function toEpoch(v) {
|
|
213
|
+
if (v instanceof Date) return v.getTime();
|
|
214
|
+
if (typeof v === "string" || typeof v === "number") return +new Date(v);
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Render recalled entries as a `## 相关历史经验` appendix suitable for
|
|
219
|
+
* appending to a child agent's `_system_append`. Empty input → `""`
|
|
220
|
+
* (no header-only dangle). Each content is defanged against six
|
|
221
|
+
* injection vectors (see design §5.2) and truncated to 200 chars.
|
|
222
|
+
*/
|
|
223
|
+
function formatRecallForPrompt(entries) {
|
|
224
|
+
if (entries.length === 0) return "";
|
|
225
|
+
return [
|
|
226
|
+
"## 相关历史经验",
|
|
227
|
+
"",
|
|
228
|
+
"以下是团队最近的 top-3 执行记录,仅供参考;如果与当前任务无关请忽略。",
|
|
229
|
+
"",
|
|
230
|
+
...entries.map((e, i) => {
|
|
231
|
+
const { round, role } = e.meta ?? {};
|
|
232
|
+
const safe = truncate(defangForPrompt(e.content ?? ""), HANDOFF_CONTENT_LIMIT);
|
|
233
|
+
return `${i + 1}. [${renderRoundLabel(round)}/${renderRoleLabel(role)}] ${safe}`;
|
|
234
|
+
})
|
|
235
|
+
].join("\n");
|
|
236
|
+
}
|
|
237
|
+
const SAFE_ROLE_LABEL = /^[a-zA-Z0-9_-]{1,32}$/;
|
|
238
|
+
function renderRoundLabel(round) {
|
|
239
|
+
return typeof round === "number" && Number.isFinite(round) ? String(round) : "?";
|
|
240
|
+
}
|
|
241
|
+
function renderRoleLabel(role) {
|
|
242
|
+
return typeof role === "string" && SAFE_ROLE_LABEL.test(role) ? role : "entry";
|
|
243
|
+
}
|
|
244
|
+
/** Unicode ranges that can reorder / hide visible text. */
|
|
245
|
+
const UNICODE_INVISIBLES = /[-]/g;
|
|
246
|
+
/** Neutralize the 6 injection vectors from design §5.2. Heading rule collapses
|
|
247
|
+
* any `#+` level to ` # ` so all depths produce the same neutralized form. */
|
|
248
|
+
function defangForPrompt(text) {
|
|
249
|
+
return text.replace(UNICODE_INVISIBLES, "").replace(/<\|/g, "‹|").replace(/\|>/g, "|›").replace(/^#+\s+/gm, " # ").replace(/^```+/gm, " ```").replace(/^---\s*$/gm, " ---");
|
|
250
|
+
}
|
|
251
|
+
function truncate(text, limit) {
|
|
252
|
+
return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//#endregion
|
|
256
|
+
exports.contentHash = contentHash;
|
|
257
|
+
exports.formatRecallForPrompt = formatRecallForPrompt;
|
|
258
|
+
exports.isHandoffReadThroughEnabled = isHandoffReadThroughEnabled;
|
|
259
|
+
exports.isMemoryArchiveEnabled = isMemoryArchiveEnabled;
|
|
260
|
+
exports.recallForHandoff = recallForHandoff;
|
|
261
|
+
exports.toContentString = toContentString;
|
|
262
|
+
exports.tryRemember = tryRemember;
|
|
263
|
+
exports.wrapToolProgress = wrapToolProgress;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AFSRoot } from "@aigne/afs";
|
|
2
|
+
|
|
3
|
+
//#region src/agent-runners/memory-archive.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decides whether `wrapToolProgress` should archive calls for this
|
|
6
|
+
* manifest's `memory` field. Default-on when memory is enabled; only
|
|
7
|
+
* `archiveTools: false` opts out.
|
|
8
|
+
*/
|
|
9
|
+
declare function isMemoryArchiveEnabled(memory: unknown): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Opt-in flag: only `{ handoffReadThrough: true }` turns on the appendix.
|
|
12
|
+
* Boolean-form `memory: true` does NOT enable handoff read-through — it
|
|
13
|
+
* stays archive-only by default.
|
|
14
|
+
*/
|
|
15
|
+
declare function isHandoffReadThroughEnabled(memory: unknown): boolean;
|
|
16
|
+
interface RecalledEntry {
|
|
17
|
+
content: string;
|
|
18
|
+
meta: Record<string, unknown>;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
}
|
|
21
|
+
interface HandoffRecallOptions {
|
|
22
|
+
afs: AFSRoot | undefined;
|
|
23
|
+
/** Resolved domain from `scope.memoryDomain` — NOT from the manifest. */
|
|
24
|
+
domain: string;
|
|
25
|
+
/** Default 3. */
|
|
26
|
+
limit?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Linear scan of `/memory/${domain}/observations` → top-N execution-layer
|
|
30
|
+
* entries sorted by createdAt DESC. Fire-and-forget on read: every failure
|
|
31
|
+
* mode (missing provider, list rejection, missing path) collapses to `[]`.
|
|
32
|
+
*
|
|
33
|
+
* Intentionally does not filter by anchors/memberName — memory provider
|
|
34
|
+
* stores anchors in the index, not in `meta`, so linear scan can't see
|
|
35
|
+
* them. See design §2.2.2.
|
|
36
|
+
*/
|
|
37
|
+
declare function recallForHandoff(opts: HandoffRecallOptions): Promise<ReadonlyArray<RecalledEntry>>;
|
|
38
|
+
/**
|
|
39
|
+
* Render recalled entries as a `## 相关历史经验` appendix suitable for
|
|
40
|
+
* appending to a child agent's `_system_append`. Empty input → `""`
|
|
41
|
+
* (no header-only dangle). Each content is defanged against six
|
|
42
|
+
* injection vectors (see design §5.2) and truncated to 200 chars.
|
|
43
|
+
*/
|
|
44
|
+
declare function formatRecallForPrompt(entries: ReadonlyArray<RecalledEntry>): string;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { RecalledEntry, formatRecallForPrompt, isHandoffReadThroughEnabled, isMemoryArchiveEnabled, recallForHandoff };
|
|
47
|
+
//# sourceMappingURL=memory-archive.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-archive.d.cts","names":[],"sources":["../../src/agent-runners/memory-archive.ts"],"mappings":";;;AAoTA;;;;;AAAA,iBAzOgB,sBAAA,CAAuB,MAAA;;;;;;iBA6MvB,2BAAA,CAA4B,MAAA;AAAA,UAK3B,aAAA;EACf,OAAA;EACA,IAAA,EAAM,MAAA;EACN,SAAA;AAAA;AAAA,UAGe,oBAAA;EACf,GAAA,EAAK,OAAA;;EAEL,MAAA;;EAEA,KAAA;AAAA;;;;;;;;;;iBAYoB,gBAAA,CACpB,IAAA,EAAM,oBAAA,GACL,OAAA,CAAQ,aAAA,CAAc,aAAA;;;;;;;iBAiCT,qBAAA,CAAsB,OAAA,EAAS,aAAA,CAAc,aAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AFSRoot } from "@aigne/afs";
|
|
2
|
+
|
|
3
|
+
//#region src/agent-runners/memory-archive.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decides whether `wrapToolProgress` should archive calls for this
|
|
6
|
+
* manifest's `memory` field. Default-on when memory is enabled; only
|
|
7
|
+
* `archiveTools: false` opts out.
|
|
8
|
+
*/
|
|
9
|
+
declare function isMemoryArchiveEnabled(memory: unknown): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Opt-in flag: only `{ handoffReadThrough: true }` turns on the appendix.
|
|
12
|
+
* Boolean-form `memory: true` does NOT enable handoff read-through — it
|
|
13
|
+
* stays archive-only by default.
|
|
14
|
+
*/
|
|
15
|
+
declare function isHandoffReadThroughEnabled(memory: unknown): boolean;
|
|
16
|
+
interface RecalledEntry {
|
|
17
|
+
content: string;
|
|
18
|
+
meta: Record<string, unknown>;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
}
|
|
21
|
+
interface HandoffRecallOptions {
|
|
22
|
+
afs: AFSRoot | undefined;
|
|
23
|
+
/** Resolved domain from `scope.memoryDomain` — NOT from the manifest. */
|
|
24
|
+
domain: string;
|
|
25
|
+
/** Default 3. */
|
|
26
|
+
limit?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Linear scan of `/memory/${domain}/observations` → top-N execution-layer
|
|
30
|
+
* entries sorted by createdAt DESC. Fire-and-forget on read: every failure
|
|
31
|
+
* mode (missing provider, list rejection, missing path) collapses to `[]`.
|
|
32
|
+
*
|
|
33
|
+
* Intentionally does not filter by anchors/memberName — memory provider
|
|
34
|
+
* stores anchors in the index, not in `meta`, so linear scan can't see
|
|
35
|
+
* them. See design §2.2.2.
|
|
36
|
+
*/
|
|
37
|
+
declare function recallForHandoff(opts: HandoffRecallOptions): Promise<ReadonlyArray<RecalledEntry>>;
|
|
38
|
+
/**
|
|
39
|
+
* Render recalled entries as a `## 相关历史经验` appendix suitable for
|
|
40
|
+
* appending to a child agent's `_system_append`. Empty input → `""`
|
|
41
|
+
* (no header-only dangle). Each content is defanged against six
|
|
42
|
+
* injection vectors (see design §5.2) and truncated to 200 chars.
|
|
43
|
+
*/
|
|
44
|
+
declare function formatRecallForPrompt(entries: ReadonlyArray<RecalledEntry>): string;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { RecalledEntry, formatRecallForPrompt, isHandoffReadThroughEnabled, isMemoryArchiveEnabled, recallForHandoff };
|
|
47
|
+
//# sourceMappingURL=memory-archive.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-archive.d.mts","names":[],"sources":["../../src/agent-runners/memory-archive.ts"],"mappings":";;;AAoTA;;;;;AAAA,iBAzOgB,sBAAA,CAAuB,MAAA;;;;;;iBA6MvB,2BAAA,CAA4B,MAAA;AAAA,UAK3B,aAAA;EACf,OAAA;EACA,IAAA,EAAM,MAAA;EACN,SAAA;AAAA;AAAA,UAGe,oBAAA;EACf,GAAA,EAAK,OAAA;;EAEL,MAAA;;EAEA,KAAA;AAAA;;;;;;;;;;iBAYoB,gBAAA,CACpB,IAAA,EAAM,oBAAA,GACL,OAAA,CAAQ,aAAA,CAAc,aAAA;;;;;;;iBAiCT,qBAAA,CAAsB,OAAA,EAAS,aAAA,CAAc,aAAA"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { joinURL } from "ufo";
|
|
2
|
+
import { extractPathTopicAnchors } from "@aigne/afs-index";
|
|
3
|
+
|
|
4
|
+
//#region src/agent-runners/memory-archive.ts
|
|
5
|
+
/** Upper bound on per-entry `content` payload (chars). Matches design §5. */
|
|
6
|
+
const ARCHIVE_CONTENT_LIMIT = 2048;
|
|
7
|
+
/**
|
|
8
|
+
* Stable 16-char hex hash (two interleaved FNV-1a 32-bit streams). Used by
|
|
9
|
+
* reflection archive to bind reviewer verdicts to the actor output they
|
|
10
|
+
* reviewed via `meta.target_hash`; same primitive is reused by forthcoming
|
|
11
|
+
* G1/G2 wirings for args-hashing.
|
|
12
|
+
*/
|
|
13
|
+
function contentHash(s) {
|
|
14
|
+
let a = -2128831035;
|
|
15
|
+
let b = 461845907;
|
|
16
|
+
for (let i = 0; i < s.length; i++) {
|
|
17
|
+
const c = s.charCodeAt(i);
|
|
18
|
+
a = Math.imul(a ^ c, 16777619);
|
|
19
|
+
b = Math.imul(b ^ c + i, 2246822507);
|
|
20
|
+
}
|
|
21
|
+
const toHex = (n) => (n >>> 0).toString(16).padStart(8, "0");
|
|
22
|
+
return toHex(a) + toHex(b);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Stringify a value for archival `content`, bounding the output at
|
|
26
|
+
* `ARCHIVE_CONTENT_LIMIT` chars (truncated with `…` when over).
|
|
27
|
+
*/
|
|
28
|
+
function toContentString(v) {
|
|
29
|
+
const s = typeof v === "string" ? v : JSON.stringify(v ?? "");
|
|
30
|
+
return s.length > ARCHIVE_CONTENT_LIMIT ? `${s.slice(0, ARCHIVE_CONTENT_LIMIT - 1)}…` : s;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fire-and-forget call to `/memory/.actions/remember`. Silent when `afs`
|
|
34
|
+
* is absent or when the archive dispatch throws / returns non-success —
|
|
35
|
+
* by design, archive failures must never break the surrounding agent
|
|
36
|
+
* flow.
|
|
37
|
+
*/
|
|
38
|
+
async function tryRemember(afs, args) {
|
|
39
|
+
if (!afs?.exec) return;
|
|
40
|
+
try {
|
|
41
|
+
await afs.exec("/memory/.actions/remember", args, {});
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
/** Upper bound on per-tool-call archived `content` (chars). */
|
|
45
|
+
const TOOL_CALL_CONTENT_LIMIT = 300;
|
|
46
|
+
/** Paths whose invocations must NEVER be archived (recursion guard). */
|
|
47
|
+
const MEMORY_SELF_ACTION_PREFIX = "/memory/.actions/";
|
|
48
|
+
/**
|
|
49
|
+
* Decides whether `wrapToolProgress` should archive calls for this
|
|
50
|
+
* manifest's `memory` field. Default-on when memory is enabled; only
|
|
51
|
+
* `archiveTools: false` opts out.
|
|
52
|
+
*/
|
|
53
|
+
function isMemoryArchiveEnabled(memory) {
|
|
54
|
+
if (memory === true) return true;
|
|
55
|
+
if (memory && typeof memory === "object") return memory.archiveTools !== false;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Single-call archive: maps a `ToolCallRecord` to `/memory/.actions/remember`
|
|
60
|
+
* args per design §3.2. Double-guard against self-calls; fire-and-forget.
|
|
61
|
+
*
|
|
62
|
+
* The anchors emitted are a union of three sources:
|
|
63
|
+
*
|
|
64
|
+
* 1. The tool name (`afs_exec`, `custom_tool`, …) — generic but
|
|
65
|
+
* always present.
|
|
66
|
+
* 2. The literal `tool_call` topic — lets cross-session recall
|
|
67
|
+
* filter to "any past tool call" without naming a specific
|
|
68
|
+
* tool.
|
|
69
|
+
* 3. Path-derived topics from `extractPathTopicAnchors(call.path)`
|
|
70
|
+
* — pulls the same tier-1 retrieval signal that lifts R@1 from
|
|
71
|
+
* 13% → 100% on the docs/ corpus into tool-call observations.
|
|
72
|
+
* A run that called `/blocklets/runtime-demos/.actions/
|
|
73
|
+
* s12-memory-recall-run` becomes recallable by topic
|
|
74
|
+
* "s12-memory-recall-run", "runtime-demos", or "memory-recall-run"
|
|
75
|
+
* — not just by the generic "afs_exec".
|
|
76
|
+
*
|
|
77
|
+
* All three coalesce into a single `topic`-typed anchor with deduped
|
|
78
|
+
* `values` so the index keeps the lookup table compact.
|
|
79
|
+
*/
|
|
80
|
+
async function archiveToolCall(afs, call) {
|
|
81
|
+
if (!afs?.exec) return;
|
|
82
|
+
if (call.path.startsWith(MEMORY_SELF_ACTION_PREFIX)) return;
|
|
83
|
+
const argsStr = toContentString(call.args);
|
|
84
|
+
const content = argsStr.slice(0, TOOL_CALL_CONTENT_LIMIT);
|
|
85
|
+
const args_hash = contentHash(argsStr);
|
|
86
|
+
const duration_ms = Math.max(0, call.endTs - call.startTs);
|
|
87
|
+
const meta = {
|
|
88
|
+
tool: call.tool,
|
|
89
|
+
path: call.path,
|
|
90
|
+
status: call.status,
|
|
91
|
+
args_hash,
|
|
92
|
+
role: "tool",
|
|
93
|
+
round: call.round,
|
|
94
|
+
session: call.session,
|
|
95
|
+
duration_ms
|
|
96
|
+
};
|
|
97
|
+
if (call.error !== void 0) meta.error = call.error;
|
|
98
|
+
const pathValues = extractPathTopicAnchors(call.path).flatMap((a) => a.values);
|
|
99
|
+
await tryRemember(afs, {
|
|
100
|
+
content,
|
|
101
|
+
level: "observation",
|
|
102
|
+
layer: "execution",
|
|
103
|
+
anchors: [{
|
|
104
|
+
type: "topic",
|
|
105
|
+
values: [...new Set([
|
|
106
|
+
call.tool,
|
|
107
|
+
"tool_call",
|
|
108
|
+
...pathValues
|
|
109
|
+
])],
|
|
110
|
+
confidence: 1
|
|
111
|
+
}],
|
|
112
|
+
meta
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const ORPHAN_TTL_MS = 6e4;
|
|
116
|
+
function asString(v) {
|
|
117
|
+
return typeof v === "string" ? v : "";
|
|
118
|
+
}
|
|
119
|
+
function asRound(v) {
|
|
120
|
+
return typeof v === "number" ? v : 0;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Wrap a user-supplied `_on_tool_progress` callback so each completed
|
|
124
|
+
* (non-self, non-pending) tool call fires a fire-and-forget archive to
|
|
125
|
+
* the scope-local memory provider. See design §3.2.
|
|
126
|
+
*
|
|
127
|
+
* Always passes every event through to `userCallback` first — archive is
|
|
128
|
+
* a side channel and must be transparent to the caller.
|
|
129
|
+
*/
|
|
130
|
+
function wrapToolProgress(userCallback, ctx) {
|
|
131
|
+
const now = ctx.now ?? (() => Date.now());
|
|
132
|
+
const startMap = /* @__PURE__ */ new Map();
|
|
133
|
+
const wrapped = (event) => {
|
|
134
|
+
try {
|
|
135
|
+
userCallback?.(event);
|
|
136
|
+
} catch {}
|
|
137
|
+
const currentNow = now();
|
|
138
|
+
const cutoff = currentNow - ORPHAN_TTL_MS;
|
|
139
|
+
for (const [id, entry] of startMap) if (entry.startTs < cutoff) startMap.delete(id);
|
|
140
|
+
const calls = Array.isArray(event?.calls) ? event.calls : [];
|
|
141
|
+
const eventRound = asRound(event?.round);
|
|
142
|
+
if (event?.type === "tool_start") {
|
|
143
|
+
for (const call of calls) if (call?.id) startMap.set(call.id, {
|
|
144
|
+
startTs: currentNow,
|
|
145
|
+
round: eventRound,
|
|
146
|
+
args: call.args
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (event?.type !== "tool_result") return;
|
|
151
|
+
for (const call of calls) {
|
|
152
|
+
if (!call?.id) continue;
|
|
153
|
+
const path = asString(call.path);
|
|
154
|
+
if (path.startsWith(MEMORY_SELF_ACTION_PREFIX)) continue;
|
|
155
|
+
if (call.status === "pending") continue;
|
|
156
|
+
const start = startMap.get(call.id);
|
|
157
|
+
startMap.delete(call.id);
|
|
158
|
+
archiveToolCall(ctx.afs, {
|
|
159
|
+
id: call.id,
|
|
160
|
+
tool: asString(call.tool),
|
|
161
|
+
path,
|
|
162
|
+
status: call.status === "error" ? "error" : "ok",
|
|
163
|
+
args: call.args ?? start?.args,
|
|
164
|
+
error: typeof call.error === "string" ? call.error : void 0,
|
|
165
|
+
startTs: start?.startTs ?? currentNow,
|
|
166
|
+
endTs: currentNow,
|
|
167
|
+
round: start?.round ?? eventRound,
|
|
168
|
+
session: ctx.session
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
Object.defineProperty(wrapped, "__pendingSize", {
|
|
173
|
+
value: () => startMap.size,
|
|
174
|
+
enumerable: false,
|
|
175
|
+
configurable: true
|
|
176
|
+
});
|
|
177
|
+
return wrapped;
|
|
178
|
+
}
|
|
179
|
+
const HANDOFF_CONTENT_LIMIT = 200;
|
|
180
|
+
const HANDOFF_DEFAULT_LIMIT = 3;
|
|
181
|
+
/**
|
|
182
|
+
* Opt-in flag: only `{ handoffReadThrough: true }` turns on the appendix.
|
|
183
|
+
* Boolean-form `memory: true` does NOT enable handoff read-through — it
|
|
184
|
+
* stays archive-only by default.
|
|
185
|
+
*/
|
|
186
|
+
function isHandoffReadThroughEnabled(memory) {
|
|
187
|
+
if (!memory || typeof memory !== "object") return false;
|
|
188
|
+
return memory.handoffReadThrough === true;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Linear scan of `/memory/${domain}/observations` → top-N execution-layer
|
|
192
|
+
* entries sorted by createdAt DESC. Fire-and-forget on read: every failure
|
|
193
|
+
* mode (missing provider, list rejection, missing path) collapses to `[]`.
|
|
194
|
+
*
|
|
195
|
+
* Intentionally does not filter by anchors/memberName — memory provider
|
|
196
|
+
* stores anchors in the index, not in `meta`, so linear scan can't see
|
|
197
|
+
* them. See design §2.2.2.
|
|
198
|
+
*/
|
|
199
|
+
async function recallForHandoff(opts) {
|
|
200
|
+
if (!opts.afs?.list) return [];
|
|
201
|
+
try {
|
|
202
|
+
const path = joinURL("/memory", opts.domain, "observations");
|
|
203
|
+
return ((await opts.afs.list(path))?.data ?? []).filter((e) => e.meta?.layer === "execution").sort((a, b) => toEpoch(b.createdAt) - toEpoch(a.createdAt)).slice(0, opts.limit ?? HANDOFF_DEFAULT_LIMIT).map((e) => ({
|
|
204
|
+
content: typeof e.content === "string" ? e.content : JSON.stringify(e.content ?? ""),
|
|
205
|
+
meta: e.meta ?? {},
|
|
206
|
+
createdAt: toEpoch(e.createdAt)
|
|
207
|
+
}));
|
|
208
|
+
} catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function toEpoch(v) {
|
|
213
|
+
if (v instanceof Date) return v.getTime();
|
|
214
|
+
if (typeof v === "string" || typeof v === "number") return +new Date(v);
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Render recalled entries as a `## 相关历史经验` appendix suitable for
|
|
219
|
+
* appending to a child agent's `_system_append`. Empty input → `""`
|
|
220
|
+
* (no header-only dangle). Each content is defanged against six
|
|
221
|
+
* injection vectors (see design §5.2) and truncated to 200 chars.
|
|
222
|
+
*/
|
|
223
|
+
function formatRecallForPrompt(entries) {
|
|
224
|
+
if (entries.length === 0) return "";
|
|
225
|
+
return [
|
|
226
|
+
"## 相关历史经验",
|
|
227
|
+
"",
|
|
228
|
+
"以下是团队最近的 top-3 执行记录,仅供参考;如果与当前任务无关请忽略。",
|
|
229
|
+
"",
|
|
230
|
+
...entries.map((e, i) => {
|
|
231
|
+
const { round, role } = e.meta ?? {};
|
|
232
|
+
const safe = truncate(defangForPrompt(e.content ?? ""), HANDOFF_CONTENT_LIMIT);
|
|
233
|
+
return `${i + 1}. [${renderRoundLabel(round)}/${renderRoleLabel(role)}] ${safe}`;
|
|
234
|
+
})
|
|
235
|
+
].join("\n");
|
|
236
|
+
}
|
|
237
|
+
const SAFE_ROLE_LABEL = /^[a-zA-Z0-9_-]{1,32}$/;
|
|
238
|
+
function renderRoundLabel(round) {
|
|
239
|
+
return typeof round === "number" && Number.isFinite(round) ? String(round) : "?";
|
|
240
|
+
}
|
|
241
|
+
function renderRoleLabel(role) {
|
|
242
|
+
return typeof role === "string" && SAFE_ROLE_LABEL.test(role) ? role : "entry";
|
|
243
|
+
}
|
|
244
|
+
/** Unicode ranges that can reorder / hide visible text. */
|
|
245
|
+
const UNICODE_INVISIBLES = /[-]/g;
|
|
246
|
+
/** Neutralize the 6 injection vectors from design §5.2. Heading rule collapses
|
|
247
|
+
* any `#+` level to ` # ` so all depths produce the same neutralized form. */
|
|
248
|
+
function defangForPrompt(text) {
|
|
249
|
+
return text.replace(UNICODE_INVISIBLES, "").replace(/<\|/g, "‹|").replace(/\|>/g, "|›").replace(/^#+\s+/gm, " # ").replace(/^```+/gm, " ```").replace(/^---\s*$/gm, " ---");
|
|
250
|
+
}
|
|
251
|
+
function truncate(text, limit) {
|
|
252
|
+
return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//#endregion
|
|
256
|
+
export { contentHash, formatRecallForPrompt, isHandoffReadThroughEnabled, isMemoryArchiveEnabled, recallForHandoff, toContentString, tryRemember, wrapToolProgress };
|
|
257
|
+
//# sourceMappingURL=memory-archive.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-archive.mjs","names":[],"sources":["../../src/agent-runners/memory-archive.ts"],"sourcesContent":["/**\n * Shared helpers for archiving agent observations to the scope-local\n * `/memory/.actions/remember` endpoint. Extracted from reflection.ts so\n * forthcoming wirings (G1 tool-call archive, G2 handoff read-through, …)\n * can share the same content-bounding + hashing + fire-and-forget policy.\n *\n * Phase 0 is a pure move — semantics must remain byte-identical to the\n * versions that previously lived alongside the reflection loop.\n */\n\nimport type { AFSEntry, AFSRoot } from \"@aigne/afs\";\nimport { extractPathTopicAnchors } from \"@aigne/afs-index\";\nimport { joinURL } from \"ufo\";\n\n/** Upper bound on per-entry `content` payload (chars). Matches design §5. */\nexport const ARCHIVE_CONTENT_LIMIT = 2048;\n\n/**\n * Stable 16-char hex hash (two interleaved FNV-1a 32-bit streams). Used by\n * reflection archive to bind reviewer verdicts to the actor output they\n * reviewed via `meta.target_hash`; same primitive is reused by forthcoming\n * G1/G2 wirings for args-hashing.\n */\nexport function contentHash(s: string): string {\n let a = 0x811c9dc5 | 0;\n let b = 0x1b873593 | 0;\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n a = Math.imul(a ^ c, 0x01000193);\n b = Math.imul(b ^ (c + i), 0x85ebca6b);\n }\n const toHex = (n: number) => (n >>> 0).toString(16).padStart(8, \"0\");\n return toHex(a) + toHex(b);\n}\n\n/**\n * Stringify a value for archival `content`, bounding the output at\n * `ARCHIVE_CONTENT_LIMIT` chars (truncated with `…` when over).\n */\nexport function toContentString(v: unknown): string {\n const s = typeof v === \"string\" ? v : JSON.stringify(v ?? \"\");\n return s.length > ARCHIVE_CONTENT_LIMIT ? `${s.slice(0, ARCHIVE_CONTENT_LIMIT - 1)}…` : s;\n}\n\n/**\n * Fire-and-forget call to `/memory/.actions/remember`. Silent when `afs`\n * is absent or when the archive dispatch throws / returns non-success —\n * by design, archive failures must never break the surrounding agent\n * flow.\n */\nexport async function tryRemember(\n afs: AFSRoot | undefined,\n args: Record<string, unknown>,\n): Promise<void> {\n if (!afs?.exec) return;\n try {\n await afs.exec(\"/memory/.actions/remember\", args, {});\n } catch {\n // Fire-and-forget: archive failures must never propagate.\n }\n}\n\n// ─── G1: Tool call archive ──────────────────────────────────────────────\n\n/** Upper bound on per-tool-call archived `content` (chars). */\nexport const TOOL_CALL_CONTENT_LIMIT = 300;\n\n/** Paths whose invocations must NEVER be archived (recursion guard). */\nconst MEMORY_SELF_ACTION_PREFIX = \"/memory/.actions/\";\n\n/**\n * Decides whether `wrapToolProgress` should archive calls for this\n * manifest's `memory` field. Default-on when memory is enabled; only\n * `archiveTools: false` opts out.\n */\nexport function isMemoryArchiveEnabled(memory: unknown): boolean {\n if (memory === true) return true;\n if (memory && typeof memory === \"object\") {\n return (memory as { archiveTools?: boolean }).archiveTools !== false;\n }\n return false;\n}\n\nexport interface ToolCallRecord {\n id: string;\n tool: string;\n path: string;\n status: \"ok\" | \"error\";\n args?: unknown;\n error?: string;\n startTs: number;\n endTs: number;\n round: number;\n session: string;\n}\n\n/**\n * Single-call archive: maps a `ToolCallRecord` to `/memory/.actions/remember`\n * args per design §3.2. Double-guard against self-calls; fire-and-forget.\n *\n * The anchors emitted are a union of three sources:\n *\n * 1. The tool name (`afs_exec`, `custom_tool`, …) — generic but\n * always present.\n * 2. The literal `tool_call` topic — lets cross-session recall\n * filter to \"any past tool call\" without naming a specific\n * tool.\n * 3. Path-derived topics from `extractPathTopicAnchors(call.path)`\n * — pulls the same tier-1 retrieval signal that lifts R@1 from\n * 13% → 100% on the docs/ corpus into tool-call observations.\n * A run that called `/blocklets/runtime-demos/.actions/\n * s12-memory-recall-run` becomes recallable by topic\n * \"s12-memory-recall-run\", \"runtime-demos\", or \"memory-recall-run\"\n * — not just by the generic \"afs_exec\".\n *\n * All three coalesce into a single `topic`-typed anchor with deduped\n * `values` so the index keeps the lookup table compact.\n */\nexport async function archiveToolCall(\n afs: AFSRoot | undefined,\n call: ToolCallRecord,\n): Promise<void> {\n if (!afs?.exec) return;\n if (call.path.startsWith(MEMORY_SELF_ACTION_PREFIX)) return;\n\n const argsStr = toContentString(call.args);\n const content = argsStr.slice(0, TOOL_CALL_CONTENT_LIMIT);\n const args_hash = contentHash(argsStr);\n const duration_ms = Math.max(0, call.endTs - call.startTs);\n\n const meta: Record<string, unknown> = {\n tool: call.tool,\n path: call.path,\n status: call.status,\n args_hash,\n role: \"tool\",\n round: call.round,\n session: call.session,\n duration_ms,\n };\n if (call.error !== undefined) meta.error = call.error;\n\n const pathAnchors = extractPathTopicAnchors(call.path);\n const pathValues = pathAnchors.flatMap((a) => a.values);\n const dedupedValues = [...new Set([call.tool, \"tool_call\", ...pathValues])];\n\n await tryRemember(afs, {\n content,\n level: \"observation\",\n layer: \"execution\",\n anchors: [{ type: \"topic\", values: dedupedValues, confidence: 1 }],\n meta,\n });\n}\n\n/** Shape of the events `agent-run` emits via `_on_tool_progress`. */\nexport interface ToolProgressEvent {\n type?: string;\n round?: number;\n calls?: Array<{\n id?: string;\n tool?: string;\n path?: string;\n status?: string;\n args?: unknown;\n result?: unknown;\n error?: string;\n }>;\n [key: string]: unknown;\n}\n\nexport interface WrapToolProgressContext {\n afs: AFSRoot | undefined;\n session: string;\n /** Injectable clock for deterministic orphan-cleanup tests. */\n now?: () => number;\n}\n\nconst ORPHAN_TTL_MS = 60_000;\n\nfunction asString(v: unknown): string {\n return typeof v === \"string\" ? v : \"\";\n}\n\nfunction asRound(v: unknown): number {\n return typeof v === \"number\" ? v : 0;\n}\n\n/**\n * Wrap a user-supplied `_on_tool_progress` callback so each completed\n * (non-self, non-pending) tool call fires a fire-and-forget archive to\n * the scope-local memory provider. See design §3.2.\n *\n * Always passes every event through to `userCallback` first — archive is\n * a side channel and must be transparent to the caller.\n */\nexport function wrapToolProgress(\n userCallback: ((event: ToolProgressEvent) => void) | undefined,\n ctx: WrapToolProgressContext,\n): (event: ToolProgressEvent) => void {\n const now = ctx.now ?? (() => Date.now());\n // ash's `tool_result` events do not carry the original `args` (only id /\n // tool / path / status / result|error). Pair them with `tool_start` here\n // by call.id so archived records preserve the input that was actually run.\n const startMap = new Map<string, { startTs: number; round: number; args: unknown }>();\n\n const wrapped = (event: ToolProgressEvent): void => {\n // Always passthrough first; user callback errors must not break archive.\n try {\n userCallback?.(event);\n } catch {\n // Swallow — archive side-channel must remain transparent.\n }\n\n const currentNow = now();\n const cutoff = currentNow - ORPHAN_TTL_MS;\n for (const [id, entry] of startMap) {\n if (entry.startTs < cutoff) startMap.delete(id);\n }\n\n const calls = Array.isArray(event?.calls) ? event.calls : [];\n const eventRound = asRound(event?.round);\n\n if (event?.type === \"tool_start\") {\n for (const call of calls) {\n if (call?.id) {\n startMap.set(call.id, {\n startTs: currentNow,\n round: eventRound,\n args: call.args,\n });\n }\n }\n return;\n }\n\n if (event?.type !== \"tool_result\") return;\n\n for (const call of calls) {\n if (!call?.id) continue;\n const path = asString(call.path);\n if (path.startsWith(MEMORY_SELF_ACTION_PREFIX)) continue;\n if (call.status === \"pending\") continue;\n\n const start = startMap.get(call.id);\n startMap.delete(call.id);\n void archiveToolCall(ctx.afs, {\n id: call.id,\n tool: asString(call.tool),\n path,\n status: call.status === \"error\" ? \"error\" : \"ok\",\n args: call.args ?? start?.args,\n error: typeof call.error === \"string\" ? call.error : undefined,\n startTs: start?.startTs ?? currentNow,\n endTs: currentNow,\n round: start?.round ?? eventRound,\n session: ctx.session,\n });\n }\n };\n\n Object.defineProperty(wrapped, \"__pendingSize\", {\n value: () => startMap.size,\n enumerable: false,\n configurable: true,\n });\n\n return wrapped;\n}\n\n// ─── G2: Handoff read-through ──────────────────────────────────────────\n\nconst HANDOFF_CONTENT_LIMIT = 200;\nconst HANDOFF_DEFAULT_LIMIT = 3;\n\n/**\n * Opt-in flag: only `{ handoffReadThrough: true }` turns on the appendix.\n * Boolean-form `memory: true` does NOT enable handoff read-through — it\n * stays archive-only by default.\n */\nexport function isHandoffReadThroughEnabled(memory: unknown): boolean {\n if (!memory || typeof memory !== \"object\") return false;\n return (memory as { handoffReadThrough?: boolean }).handoffReadThrough === true;\n}\n\nexport interface RecalledEntry {\n content: string;\n meta: Record<string, unknown>;\n createdAt: number;\n}\n\nexport interface HandoffRecallOptions {\n afs: AFSRoot | undefined;\n /** Resolved domain from `scope.memoryDomain` — NOT from the manifest. */\n domain: string;\n /** Default 3. */\n limit?: number;\n}\n\n/**\n * Linear scan of `/memory/${domain}/observations` → top-N execution-layer\n * entries sorted by createdAt DESC. Fire-and-forget on read: every failure\n * mode (missing provider, list rejection, missing path) collapses to `[]`.\n *\n * Intentionally does not filter by anchors/memberName — memory provider\n * stores anchors in the index, not in `meta`, so linear scan can't see\n * them. See design §2.2.2.\n */\nexport async function recallForHandoff(\n opts: HandoffRecallOptions,\n): Promise<ReadonlyArray<RecalledEntry>> {\n if (!opts.afs?.list) return [];\n try {\n const path = joinURL(\"/memory\", opts.domain, \"observations\");\n const result = await opts.afs.list(path);\n const entries = (result?.data ?? []) as AFSEntry[];\n\n return entries\n .filter((e) => (e.meta as { layer?: string } | undefined)?.layer === \"execution\")\n .sort((a, b) => toEpoch(b.createdAt) - toEpoch(a.createdAt))\n .slice(0, opts.limit ?? HANDOFF_DEFAULT_LIMIT)\n .map((e) => ({\n content: typeof e.content === \"string\" ? e.content : JSON.stringify(e.content ?? \"\"),\n meta: (e.meta ?? {}) as Record<string, unknown>,\n createdAt: toEpoch(e.createdAt),\n }));\n } catch {\n return [];\n }\n}\n\nfunction toEpoch(v: unknown): number {\n if (v instanceof Date) return v.getTime();\n if (typeof v === \"string\" || typeof v === \"number\") return +new Date(v);\n return 0;\n}\n\n/**\n * Render recalled entries as a `## 相关历史经验` appendix suitable for\n * appending to a child agent's `_system_append`. Empty input → `\"\"`\n * (no header-only dangle). Each content is defanged against six\n * injection vectors (see design §5.2) and truncated to 200 chars.\n */\nexport function formatRecallForPrompt(entries: ReadonlyArray<RecalledEntry>): string {\n if (entries.length === 0) return \"\";\n const lines = entries.map((e, i) => {\n const { round, role } = (e.meta ?? {}) as { round?: unknown; role?: unknown };\n const safe = truncate(defangForPrompt(e.content ?? \"\"), HANDOFF_CONTENT_LIMIT);\n return `${i + 1}. [${renderRoundLabel(round)}/${renderRoleLabel(role)}] ${safe}`;\n });\n return [\n \"## 相关历史经验\",\n \"\",\n \"以下是团队最近的 top-3 执行记录,仅供参考;如果与当前任务无关请忽略。\",\n \"\",\n ...lines,\n ].join(\"\\n\");\n}\n\nconst SAFE_ROLE_LABEL = /^[a-zA-Z0-9_-]{1,32}$/;\n\nfunction renderRoundLabel(round: unknown): string {\n return typeof round === \"number\" && Number.isFinite(round) ? String(round) : \"?\";\n}\n\nfunction renderRoleLabel(role: unknown): string {\n return typeof role === \"string\" && SAFE_ROLE_LABEL.test(role) ? role : \"entry\";\n}\n\n/** Unicode ranges that can reorder / hide visible text. */\nconst UNICODE_INVISIBLES = /[-]/g;\n\n/** Neutralize the 6 injection vectors from design §5.2. Heading rule collapses\n * any `#+` level to ` # ` so all depths produce the same neutralized form. */\nfunction defangForPrompt(text: string): string {\n return text\n .replace(UNICODE_INVISIBLES, \"\")\n .replace(/<\\|/g, \"‹|\")\n .replace(/\\|>/g, \"|›\")\n .replace(/^#+\\s+/gm, \" # \")\n .replace(/^```+/gm, \" ```\")\n .replace(/^---\\s*$/gm, \" ---\");\n}\n\nfunction truncate(text: string, limit: number): string {\n return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;\n}\n"],"mappings":"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"}
|