dsh-daoing-memory 0.1.0
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 +21 -0
- package/README.md +91 -0
- package/README.zh-CN.md +91 -0
- package/cordis.patch.yml +22 -0
- package/lib/client.js +10339 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +3138 -0
- package/lib/invariant.js +33 -0
- package/lib/tools.js +1472 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +4058 -0
- package/lib/typert.remote-client.d.ts +149 -0
- package/lib/typert.remote-client.d.ts.map +1 -0
- package/lib/typert.remote-client.js +3167 -0
- package/lib/types/client/ExperiencePage.d.ts +15 -0
- package/lib/types/client/ExperiencePage.d.ts.map +1 -0
- package/lib/types/client/ExperiencePage.js +117 -0
- package/lib/types/client/ExperiencePage.js.map +1 -0
- package/lib/types/client/FactDiaryPage.d.ts +17 -0
- package/lib/types/client/FactDiaryPage.d.ts.map +1 -0
- package/lib/types/client/FactDiaryPage.js +155 -0
- package/lib/types/client/FactDiaryPage.js.map +1 -0
- package/lib/types/client/HumanOpsPage.d.ts +16 -0
- package/lib/types/client/HumanOpsPage.d.ts.map +1 -0
- package/lib/types/client/HumanOpsPage.js +208 -0
- package/lib/types/client/HumanOpsPage.js.map +1 -0
- package/lib/types/client/LedgerPage.d.ts +13 -0
- package/lib/types/client/LedgerPage.d.ts.map +1 -0
- package/lib/types/client/LedgerPage.js +113 -0
- package/lib/types/client/LedgerPage.js.map +1 -0
- package/lib/types/client/MemoryNavSection.d.ts +20 -0
- package/lib/types/client/MemoryNavSection.d.ts.map +1 -0
- package/lib/types/client/MemoryNavSection.js +23 -0
- package/lib/types/client/MemoryNavSection.js.map +1 -0
- package/lib/types/client/Workbench.d.ts +23 -0
- package/lib/types/client/Workbench.d.ts.map +1 -0
- package/lib/types/client/Workbench.js +140 -0
- package/lib/types/client/Workbench.js.map +1 -0
- package/lib/types/client/actions.d.ts +165 -0
- package/lib/types/client/actions.d.ts.map +1 -0
- package/lib/types/client/actions.js +51 -0
- package/lib/types/client/actions.js.map +1 -0
- package/lib/types/client/index.d.ts +24 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +103 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/navStore.d.ts +45 -0
- package/lib/types/client/navStore.d.ts.map +1 -0
- package/lib/types/client/navStore.js +36 -0
- package/lib/types/client/navStore.js.map +1 -0
- package/lib/types/core.d.ts +195 -0
- package/lib/types/core.d.ts.map +1 -0
- package/lib/types/core.js +1304 -0
- package/lib/types/core.js.map +1 -0
- package/lib/types/index.d.ts +71 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +97 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/invariant.d.ts +21 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +33 -0
- package/lib/types/invariant.js.map +1 -0
- package/lib/types/service.d.ts +130 -0
- package/lib/types/service.d.ts.map +1 -0
- package/lib/types/service.js +417 -0
- package/lib/types/service.js.map +1 -0
- package/lib/types/store.d.ts +244 -0
- package/lib/types/store.d.ts.map +1 -0
- package/lib/types/store.js +864 -0
- package/lib/types/store.js.map +1 -0
- package/lib/types/tools.d.ts +25 -0
- package/lib/types/tools.d.ts.map +1 -0
- package/lib/types/tools.js +1144 -0
- package/lib/types/tools.js.map +1 -0
- package/lib/types/types.d.ts +836 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/types.js +8 -0
- package/lib/types/types.js.map +1 -0
- package/package.json +106 -0
- package/skill/memory-extraction.md +62 -0
package/lib/tools.js
ADDED
|
@@ -0,0 +1,1472 @@
|
|
|
1
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
2
|
+
//#region lib/types/tools.js
|
|
3
|
+
/**
|
|
4
|
+
* Model-facing tools over the memory library: the agent drives its own
|
|
5
|
+
* 生·用·修·记 loop through these tools.
|
|
6
|
+
*
|
|
7
|
+
* - memory_recall: 用 — before a task: recall + adjudication + budget.
|
|
8
|
+
* - memory_refine: 生 — after a complex task: refine into a candidate.
|
|
9
|
+
* - memory_report: 用·验 — report one outcome with attribution.
|
|
10
|
+
* - memory_revise: 修 — propose a revision for a challenged experience.
|
|
11
|
+
* - memory_verify: 修 — shadow-replay verification of a draft (V1).
|
|
12
|
+
* - memory_fact: 记 — append a diary entry (event layer).
|
|
13
|
+
* - memory_extract: 记 — propose extracted profile facts (upward channel).
|
|
14
|
+
* - memory_ledger: 账本 — query the audit ledger.
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-daoing-memory/tools
|
|
17
|
+
*/
|
|
18
|
+
const name = "memory-tools";
|
|
19
|
+
const inject = [
|
|
20
|
+
"memory",
|
|
21
|
+
"tools",
|
|
22
|
+
"systemPrompt"
|
|
23
|
+
];
|
|
24
|
+
/** Generic, args-only pending presentation shared by the memory tools. */
|
|
25
|
+
function present(title, kind, rawInput) {
|
|
26
|
+
return {
|
|
27
|
+
card: "generic",
|
|
28
|
+
title,
|
|
29
|
+
kind,
|
|
30
|
+
...rawInput === void 0 ? {} : { rawInput }
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Resolve the live calling agent; memory tools mutate shared state, so an owner is required. */
|
|
34
|
+
function callingAgent(exec) {
|
|
35
|
+
if (exec.agent === void 0) throw new Error("memory tools require a calling agent");
|
|
36
|
+
return exec.agent;
|
|
37
|
+
}
|
|
38
|
+
/** Format the Beta-posterior triple (trust, samples, last verified). */
|
|
39
|
+
function trustTriple(e) {
|
|
40
|
+
const last = e.lastVerifiedAt === void 0 ? "从未验证" : new Date(e.lastVerifiedAt).toISOString().slice(0, 10);
|
|
41
|
+
return `置信${e.trust.toFixed(2)}(样本${String(e.samples)}, 最近验证${last})`;
|
|
42
|
+
}
|
|
43
|
+
/** Compact text rendering of one recalled experience. */
|
|
44
|
+
function renderRecalled(result) {
|
|
45
|
+
if (result.none) return `【负向通道】无相关经验:${result.reason ?? "经验库中没有匹配项"}(这是一次全新探索)`;
|
|
46
|
+
const blocks = result.items.map((item) => {
|
|
47
|
+
const e = item.experience;
|
|
48
|
+
const steps = e.path.map((p) => `${String(p.order)}. ${p.action}`).join(" → ");
|
|
49
|
+
return [
|
|
50
|
+
`${e.kind === "negative" ? `⚠️ 负经验 ${e.id} [v${String(e.revision)} ${e.status}] ${trustTriple(e)}` : `经验 ${e.id} [v${String(e.revision)} ${e.status}] ${trustTriple(e)}`}(相关度 ${item.score.toFixed(2)},裁决:${item.verdict})`,
|
|
51
|
+
` 摘要: ${e.gist}`,
|
|
52
|
+
e.kind === "negative" && e.failureReason !== void 0 ? ` 失败原因: ${e.failureReason}` : "",
|
|
53
|
+
` 情境: ${e.situation.join(", ")}`,
|
|
54
|
+
` 路径: ${steps}`,
|
|
55
|
+
` 判断背景: ${e.reasoning}`,
|
|
56
|
+
e.limits.length > 0 ? ` 限制: ${e.limits.join("; ")}` : "",
|
|
57
|
+
item.conflicts.length > 0 ? ` ⚠️ 与当前情境可能冲突的边界: ${item.conflicts.join("; ")}` : ""
|
|
58
|
+
].filter(Boolean).join("\n");
|
|
59
|
+
});
|
|
60
|
+
const tail = result.omitted > 0 ? `\n(注入预算内省略 ${String(result.omitted)} 条)` : "";
|
|
61
|
+
return blocks.join("\n\n") + tail;
|
|
62
|
+
}
|
|
63
|
+
const RECALL_DESCRIPTION = "Recall earned experiences for a similar situation. Call this BEFORE starting a complex task. Returns verified paths with judgment context, an adjudication verdict (direct/reference/clue), limit-conflict warnings, and an explicit \"no relevant experience\" negative channel. Follow a direct verdict instead of re-exploring; treat reference verdicts as adaptation material.";
|
|
64
|
+
const RECALL_PARAMETERS = {
|
|
65
|
+
situation: {
|
|
66
|
+
type: "string",
|
|
67
|
+
required: true,
|
|
68
|
+
description: "The new task situation, described freely (task type, domain, symptoms, constraints)."
|
|
69
|
+
},
|
|
70
|
+
topK: {
|
|
71
|
+
type: "integer",
|
|
72
|
+
description: "Max candidates before adjudication (default 6)."
|
|
73
|
+
},
|
|
74
|
+
deep: {
|
|
75
|
+
type: "boolean",
|
|
76
|
+
description: "Also search archived experiences (deep lookup) when the normal set is thin."
|
|
77
|
+
},
|
|
78
|
+
context: {
|
|
79
|
+
type: "string",
|
|
80
|
+
description: "Active context/domain scope (e.g. \"coding-windows-build\"). Only same-context or globally-shared experiences are recalled."
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const RECALL_OUTPUT = {
|
|
84
|
+
schema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
additionalProperties: false,
|
|
87
|
+
properties: {
|
|
88
|
+
none: {
|
|
89
|
+
type: "boolean",
|
|
90
|
+
required: true
|
|
91
|
+
},
|
|
92
|
+
reason: { type: "string" },
|
|
93
|
+
omitted: {
|
|
94
|
+
type: "integer",
|
|
95
|
+
required: true
|
|
96
|
+
},
|
|
97
|
+
estimatedTokens: {
|
|
98
|
+
type: "integer",
|
|
99
|
+
required: true
|
|
100
|
+
},
|
|
101
|
+
items: {
|
|
102
|
+
type: "array",
|
|
103
|
+
required: true,
|
|
104
|
+
items: {
|
|
105
|
+
type: "object",
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
properties: {
|
|
108
|
+
id: {
|
|
109
|
+
type: "string",
|
|
110
|
+
required: true
|
|
111
|
+
},
|
|
112
|
+
revision: {
|
|
113
|
+
type: "integer",
|
|
114
|
+
required: true
|
|
115
|
+
},
|
|
116
|
+
kind: {
|
|
117
|
+
type: "string",
|
|
118
|
+
required: true
|
|
119
|
+
},
|
|
120
|
+
status: {
|
|
121
|
+
type: "string",
|
|
122
|
+
required: true
|
|
123
|
+
},
|
|
124
|
+
gist: {
|
|
125
|
+
type: "string",
|
|
126
|
+
required: true
|
|
127
|
+
},
|
|
128
|
+
failureReason: { type: "string" },
|
|
129
|
+
situation: {
|
|
130
|
+
type: "array",
|
|
131
|
+
required: true,
|
|
132
|
+
items: { type: "string" }
|
|
133
|
+
},
|
|
134
|
+
path: {
|
|
135
|
+
type: "array",
|
|
136
|
+
required: true,
|
|
137
|
+
items: {
|
|
138
|
+
type: "object",
|
|
139
|
+
additionalProperties: false,
|
|
140
|
+
properties: {
|
|
141
|
+
action: {
|
|
142
|
+
type: "string",
|
|
143
|
+
required: true
|
|
144
|
+
},
|
|
145
|
+
order: {
|
|
146
|
+
type: "integer",
|
|
147
|
+
required: true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
reasoning: {
|
|
153
|
+
type: "string",
|
|
154
|
+
required: true
|
|
155
|
+
},
|
|
156
|
+
limits: {
|
|
157
|
+
type: "array",
|
|
158
|
+
required: true,
|
|
159
|
+
items: { type: "string" }
|
|
160
|
+
},
|
|
161
|
+
trust: {
|
|
162
|
+
type: "number",
|
|
163
|
+
required: true
|
|
164
|
+
},
|
|
165
|
+
samples: {
|
|
166
|
+
type: "integer",
|
|
167
|
+
required: true
|
|
168
|
+
},
|
|
169
|
+
lastVerifiedAt: { type: "integer" },
|
|
170
|
+
score: {
|
|
171
|
+
type: "number",
|
|
172
|
+
required: true
|
|
173
|
+
},
|
|
174
|
+
verdict: {
|
|
175
|
+
type: "string",
|
|
176
|
+
required: true
|
|
177
|
+
},
|
|
178
|
+
conflicts: {
|
|
179
|
+
type: "array",
|
|
180
|
+
required: true,
|
|
181
|
+
items: { type: "string" }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
candidateTrials: {
|
|
187
|
+
type: "array",
|
|
188
|
+
required: true,
|
|
189
|
+
items: {
|
|
190
|
+
type: "object",
|
|
191
|
+
additionalProperties: false,
|
|
192
|
+
properties: {
|
|
193
|
+
id: {
|
|
194
|
+
type: "string",
|
|
195
|
+
required: true
|
|
196
|
+
},
|
|
197
|
+
revision: {
|
|
198
|
+
type: "integer",
|
|
199
|
+
required: true
|
|
200
|
+
},
|
|
201
|
+
gist: {
|
|
202
|
+
type: "string",
|
|
203
|
+
required: true
|
|
204
|
+
},
|
|
205
|
+
situation: {
|
|
206
|
+
type: "array",
|
|
207
|
+
required: true,
|
|
208
|
+
items: { type: "string" }
|
|
209
|
+
},
|
|
210
|
+
score: {
|
|
211
|
+
type: "number",
|
|
212
|
+
required: true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
consolidationDue: {
|
|
218
|
+
type: "boolean",
|
|
219
|
+
required: true
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
render: (_args, value) => [{
|
|
224
|
+
type: "text",
|
|
225
|
+
text: renderRecalled({
|
|
226
|
+
none: value.none,
|
|
227
|
+
omitted: value.omitted,
|
|
228
|
+
estimatedTokens: value.estimatedTokens,
|
|
229
|
+
candidateTrials: [],
|
|
230
|
+
consolidationDue: value.consolidationDue,
|
|
231
|
+
...value.reason === void 0 ? {} : { reason: value.reason },
|
|
232
|
+
items: value.items.map((item) => ({
|
|
233
|
+
score: item.score,
|
|
234
|
+
verdict: item.verdict,
|
|
235
|
+
conflicts: item.conflicts,
|
|
236
|
+
experience: {
|
|
237
|
+
id: item.id,
|
|
238
|
+
revision: item.revision,
|
|
239
|
+
kind: item.kind,
|
|
240
|
+
source: "agent",
|
|
241
|
+
family: "",
|
|
242
|
+
gist: item.gist,
|
|
243
|
+
situation: item.situation,
|
|
244
|
+
path: item.path,
|
|
245
|
+
reasoning: item.reasoning,
|
|
246
|
+
limits: item.limits,
|
|
247
|
+
status: item.status,
|
|
248
|
+
alpha: 0,
|
|
249
|
+
beta: 0,
|
|
250
|
+
samples: item.samples,
|
|
251
|
+
trust: item.trust,
|
|
252
|
+
weightedTrust: item.trust,
|
|
253
|
+
pinned: false,
|
|
254
|
+
tokensSaved: 0,
|
|
255
|
+
tokensSpent: 0,
|
|
256
|
+
context: item.context,
|
|
257
|
+
verifiedCount: item.verifiedCount,
|
|
258
|
+
rejectCount: item.rejectCount,
|
|
259
|
+
globalFlag: item.globalFlag,
|
|
260
|
+
createdAt: 0,
|
|
261
|
+
updatedAt: 0,
|
|
262
|
+
...item.failureReason === void 0 ? {} : { failureReason: item.failureReason },
|
|
263
|
+
...item.lastVerifiedAt === void 0 ? {} : { lastVerifiedAt: item.lastVerifiedAt }
|
|
264
|
+
}
|
|
265
|
+
}))
|
|
266
|
+
}) + (value.candidateTrials.length === 0 ? "" : "\n\n候选试探(未验证,可匹配则试一次,成功后 report 转正,失败会进冷宫):\n" + value.candidateTrials.map((t) => `- [${t.id}] ${t.gist}(相关度 ${t.score.toFixed(2)})`).join("\n")) + (value.consolidationDue ? "\n\n🧹 巩固周期已到:经验库积累了较多相关条目,建议在合适时机调用 memory_consolidate,把相关经验合并提炼为更精炼的一条。" : "")
|
|
267
|
+
}]
|
|
268
|
+
};
|
|
269
|
+
const REFINE_DESCRIPTION = "生: refine a completed complex task into a durable experience candidate (unverified; it must earn live status through verification). Call this AFTER a task with meaningful judgment (wrong paths tried, course corrected, limits hit, or a confirmed dead end). Do NOT refine trivial single-tool calls. ONE experience = ONE reusable lesson: if the task taught several distinct lessons, submit separate refine calls instead of one blob. Write every field GENERALIZED for future reuse — never a session chronicle (\"本会话/本次/用户当场…\"): the session is only the evidence pointer. Supply the episodic evidence pointer (trace/session/note) — assertions without evidence are rejected. kind=negative records a confirmed dead end with failureReason.";
|
|
270
|
+
const REFINE_PARAMETERS = {
|
|
271
|
+
kind: {
|
|
272
|
+
type: "string",
|
|
273
|
+
required: true,
|
|
274
|
+
enum: ["positive", "negative"],
|
|
275
|
+
description: "positive = a path that works; negative = a confirmed dead end (requires failureReason)."
|
|
276
|
+
},
|
|
277
|
+
family: {
|
|
278
|
+
type: "string",
|
|
279
|
+
required: true,
|
|
280
|
+
description: "Task-family tag for capacity budgeting and consolidation (e.g. \"windows-build\", \"pnpm-install\")."
|
|
281
|
+
},
|
|
282
|
+
gist: {
|
|
283
|
+
type: "string",
|
|
284
|
+
required: true,
|
|
285
|
+
description: "ONE crisp reusable lesson in one sentence (the path or the dead end). Not a multi-clause event report; if you need \";\" to cram several lessons, split them."
|
|
286
|
+
},
|
|
287
|
+
situation: {
|
|
288
|
+
type: "array",
|
|
289
|
+
required: true,
|
|
290
|
+
items: { type: "string" },
|
|
291
|
+
description: "The CLASS of situation that should trigger recall later (task type, domain, symptoms) — generalizable, not \"this specific task I just did\"."
|
|
292
|
+
},
|
|
293
|
+
path: {
|
|
294
|
+
type: "array",
|
|
295
|
+
required: true,
|
|
296
|
+
items: {
|
|
297
|
+
type: "object",
|
|
298
|
+
additionalProperties: false,
|
|
299
|
+
properties: {
|
|
300
|
+
action: {
|
|
301
|
+
type: "string",
|
|
302
|
+
required: true,
|
|
303
|
+
description: "What was done (tool call, action, decision)."
|
|
304
|
+
},
|
|
305
|
+
order: {
|
|
306
|
+
type: "integer",
|
|
307
|
+
required: true,
|
|
308
|
+
description: "Order matters."
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
description: "The actual steps in order (the dead-end steps for negative experiences)."
|
|
313
|
+
},
|
|
314
|
+
reasoning: {
|
|
315
|
+
type: "string",
|
|
316
|
+
required: true,
|
|
317
|
+
description: "TRANSFERABLE judgment background, de-contextualized: \"under <situation>, this path holds BECAUSE <cause>; the boundary is <limits>.\" Explain the why/what-was-corrected so a stranger could reuse it. NEVER narrate the session (\"本会话/本次/用户当场纠正…\") — cite the session only via the evidence pointer."
|
|
318
|
+
},
|
|
319
|
+
limits: {
|
|
320
|
+
type: "array",
|
|
321
|
+
items: { type: "string" },
|
|
322
|
+
description: "When this path does NOT apply (pitfalls, anti-patterns, boundary conditions)."
|
|
323
|
+
},
|
|
324
|
+
failureReason: {
|
|
325
|
+
type: "string",
|
|
326
|
+
description: "Required for kind=negative: the confirmed reason the path fails."
|
|
327
|
+
},
|
|
328
|
+
evidence: {
|
|
329
|
+
type: "object",
|
|
330
|
+
required: true,
|
|
331
|
+
additionalProperties: false,
|
|
332
|
+
properties: {
|
|
333
|
+
traceRef: {
|
|
334
|
+
type: "string",
|
|
335
|
+
description: "Pointer to the original execution trace."
|
|
336
|
+
},
|
|
337
|
+
sessionRef: {
|
|
338
|
+
type: "string",
|
|
339
|
+
description: "Pointer to the originating session."
|
|
340
|
+
},
|
|
341
|
+
note: {
|
|
342
|
+
type: "string",
|
|
343
|
+
description: "Free-text evidence note."
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
description: "Episodic evidence pointer — at least one field must be non-empty."
|
|
347
|
+
},
|
|
348
|
+
complexity: {
|
|
349
|
+
type: "object",
|
|
350
|
+
required: true,
|
|
351
|
+
additionalProperties: false,
|
|
352
|
+
properties: {
|
|
353
|
+
tokens: {
|
|
354
|
+
type: "integer",
|
|
355
|
+
description: "Approximate token cost of the trajectory."
|
|
356
|
+
},
|
|
357
|
+
steps: {
|
|
358
|
+
type: "integer",
|
|
359
|
+
description: "Step count of the trajectory."
|
|
360
|
+
},
|
|
361
|
+
hadFailure: {
|
|
362
|
+
type: "boolean",
|
|
363
|
+
description: "Whether the trajectory contained failures."
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
description: "Complexity gate input; below both gates (and no failure) the refine is rejected."
|
|
367
|
+
},
|
|
368
|
+
humanMarked: {
|
|
369
|
+
type: "boolean",
|
|
370
|
+
description: "True when a human asked to remember this — bypasses the complexity gate."
|
|
371
|
+
},
|
|
372
|
+
context: {
|
|
373
|
+
type: "string",
|
|
374
|
+
description: "Context/domain scope this experience belongs to (e.g. \"coding-windows-build\")."
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
const REFINE_OUTPUT = {
|
|
378
|
+
schema: {
|
|
379
|
+
type: "object",
|
|
380
|
+
additionalProperties: false,
|
|
381
|
+
properties: {
|
|
382
|
+
accepted: {
|
|
383
|
+
type: "boolean",
|
|
384
|
+
required: true
|
|
385
|
+
},
|
|
386
|
+
reason: { type: "string" },
|
|
387
|
+
id: { type: "string" },
|
|
388
|
+
revision: { type: "integer" },
|
|
389
|
+
status: { type: "string" },
|
|
390
|
+
corroboratedId: { type: "string" }
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
render: (_args, value) => [{
|
|
394
|
+
type: "text",
|
|
395
|
+
text: value.accepted ? `已沉淀候选经验 [${value.id ?? ""}] v${String(value.revision ?? 1)}(candidate,等待验证转正)` : `提炼被门控拒绝:${value.reason ?? "unknown"}${value.corroboratedId !== void 0 ? `(已佐证既有经验 ${value.corroboratedId})` : ""}`
|
|
396
|
+
}]
|
|
397
|
+
};
|
|
398
|
+
const REPORT_DESCRIPTION = "用·验: report one use outcome for an experience — using it IS verifying it (V0). Call this AFTER you followed a recalled experience (or after confirming a negative experience predicted a dead end: for negatives, outcome=success means the prediction held). Attribution for failures: experience (path defect), environment (infra/network/permission — never counts against the experience), unrelated, or unknown (insufficient signal — not counted). Objective evidence notes take priority over the claim. Repeated reports with the same dedupeKey count once. Reporting success on a candidate/draft revision promotes or adopts it.";
|
|
399
|
+
const REPORT_PARAMETERS = {
|
|
400
|
+
id: {
|
|
401
|
+
type: "string",
|
|
402
|
+
required: true,
|
|
403
|
+
description: "Experience family id (from memory_recall)."
|
|
404
|
+
},
|
|
405
|
+
revision: {
|
|
406
|
+
type: "integer",
|
|
407
|
+
description: "Target revision: omit for the active one; pass a draft revision to verify it."
|
|
408
|
+
},
|
|
409
|
+
outcome: {
|
|
410
|
+
type: "string",
|
|
411
|
+
required: true,
|
|
412
|
+
enum: ["success", "fail"],
|
|
413
|
+
description: "Did following the experience succeed (for negatives: did the prediction hold)?"
|
|
414
|
+
},
|
|
415
|
+
attribution: {
|
|
416
|
+
type: "string",
|
|
417
|
+
enum: [
|
|
418
|
+
"experience",
|
|
419
|
+
"environment",
|
|
420
|
+
"unrelated",
|
|
421
|
+
"unknown"
|
|
422
|
+
],
|
|
423
|
+
description: "On fail: why it failed. experience-attributed failures need an evidence note."
|
|
424
|
+
},
|
|
425
|
+
evidence: {
|
|
426
|
+
type: "object",
|
|
427
|
+
additionalProperties: false,
|
|
428
|
+
properties: {
|
|
429
|
+
traceRef: { type: "string" },
|
|
430
|
+
sessionRef: { type: "string" },
|
|
431
|
+
note: {
|
|
432
|
+
type: "string",
|
|
433
|
+
description: "Objective failure evidence (error text, symptom)."
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
description: "Objective evidence; its note can override the claimed attribution."
|
|
437
|
+
},
|
|
438
|
+
tokensUsed: {
|
|
439
|
+
type: "integer",
|
|
440
|
+
description: "Tokens this execution spent."
|
|
441
|
+
},
|
|
442
|
+
tokensSaved: {
|
|
443
|
+
type: "integer",
|
|
444
|
+
description: "Tokens saved versus re-exploring."
|
|
445
|
+
},
|
|
446
|
+
dedupeKey: {
|
|
447
|
+
type: "string",
|
|
448
|
+
description: "Idempotency key: one execution reports once."
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const REPORT_OUTPUT = {
|
|
452
|
+
schema: {
|
|
453
|
+
type: "object",
|
|
454
|
+
additionalProperties: false,
|
|
455
|
+
properties: {
|
|
456
|
+
id: {
|
|
457
|
+
type: "string",
|
|
458
|
+
required: true
|
|
459
|
+
},
|
|
460
|
+
status: {
|
|
461
|
+
type: "string",
|
|
462
|
+
required: true
|
|
463
|
+
},
|
|
464
|
+
trust: {
|
|
465
|
+
type: "number",
|
|
466
|
+
required: true
|
|
467
|
+
},
|
|
468
|
+
samples: {
|
|
469
|
+
type: "integer",
|
|
470
|
+
required: true
|
|
471
|
+
},
|
|
472
|
+
counted: {
|
|
473
|
+
type: "string",
|
|
474
|
+
required: true
|
|
475
|
+
},
|
|
476
|
+
attributionApplied: {
|
|
477
|
+
type: "string",
|
|
478
|
+
required: true
|
|
479
|
+
},
|
|
480
|
+
overrideNote: { type: "string" },
|
|
481
|
+
challenged: {
|
|
482
|
+
type: "boolean",
|
|
483
|
+
required: true
|
|
484
|
+
},
|
|
485
|
+
promoted: {
|
|
486
|
+
type: "boolean",
|
|
487
|
+
required: true
|
|
488
|
+
},
|
|
489
|
+
adopted: {
|
|
490
|
+
type: "boolean",
|
|
491
|
+
required: true
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
render: (_args, value) => {
|
|
496
|
+
const notes = [];
|
|
497
|
+
if (value.counted === "alpha") notes.push("计入成功样本 α+1");
|
|
498
|
+
else if (value.counted === "beta") notes.push("计入失败样本 β+1");
|
|
499
|
+
else notes.push("未计入(环境失败/信号不足/幂等去重)");
|
|
500
|
+
if (value.overrideNote !== void 0) notes.push(value.overrideNote);
|
|
501
|
+
if (value.challenged) notes.push("⚠️ 已隔离(challenged,移出召回集,需修订)");
|
|
502
|
+
if (value.adopted) notes.push("修订版通过验证转正(旧版转 superseded 可回滚)");
|
|
503
|
+
else if (value.promoted) notes.push("通过验证转正为 live");
|
|
504
|
+
return [{
|
|
505
|
+
type: "text",
|
|
506
|
+
text: `经验 [${value.id}] ${value.status} ${trustTripleShort(value)} — ${notes.join(";")}`
|
|
507
|
+
}];
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
/** Short trust triple for the report render. */
|
|
511
|
+
function trustTripleShort(value) {
|
|
512
|
+
return `置信${value.trust.toFixed(2)}(样本${String(value.samples)})`;
|
|
513
|
+
}
|
|
514
|
+
const REVISE_DESCRIPTION = "修: propose a revised draft (v+1) for a CHALLENGED experience. The challenged original is already quarantined (out of recall). The draft stays a candidate until verified: report one successful use against the draft revision (or use memory_verify shadow replay) to adopt it; adoption turns the old version into a superseded read-only index (rollback-able). Supply the diagnosis as reason.";
|
|
515
|
+
const REVISE_PARAMETERS = {
|
|
516
|
+
id: {
|
|
517
|
+
type: "string",
|
|
518
|
+
required: true,
|
|
519
|
+
description: "Experience family id (must be challenged)."
|
|
520
|
+
},
|
|
521
|
+
reason: {
|
|
522
|
+
type: "string",
|
|
523
|
+
required: true,
|
|
524
|
+
description: "Diagnosis: root cause of the failures and what this revision changes."
|
|
525
|
+
},
|
|
526
|
+
gist: {
|
|
527
|
+
type: "string",
|
|
528
|
+
description: "Replacement gist (kept when omitted)."
|
|
529
|
+
},
|
|
530
|
+
situation: {
|
|
531
|
+
type: "array",
|
|
532
|
+
items: { type: "string" },
|
|
533
|
+
description: "Replacement situations."
|
|
534
|
+
},
|
|
535
|
+
path: {
|
|
536
|
+
type: "array",
|
|
537
|
+
items: {
|
|
538
|
+
type: "object",
|
|
539
|
+
additionalProperties: false,
|
|
540
|
+
properties: {
|
|
541
|
+
action: {
|
|
542
|
+
type: "string",
|
|
543
|
+
required: true
|
|
544
|
+
},
|
|
545
|
+
order: {
|
|
546
|
+
type: "integer",
|
|
547
|
+
required: true
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
description: "Replacement path."
|
|
552
|
+
},
|
|
553
|
+
reasoning: {
|
|
554
|
+
type: "string",
|
|
555
|
+
description: "Replacement reasoning."
|
|
556
|
+
},
|
|
557
|
+
limits: {
|
|
558
|
+
type: "array",
|
|
559
|
+
items: { type: "string" },
|
|
560
|
+
description: "Replacement limits."
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
const REVISE_OUTPUT = {
|
|
564
|
+
schema: {
|
|
565
|
+
type: "object",
|
|
566
|
+
additionalProperties: false,
|
|
567
|
+
properties: {
|
|
568
|
+
id: {
|
|
569
|
+
type: "string",
|
|
570
|
+
required: true
|
|
571
|
+
},
|
|
572
|
+
revision: {
|
|
573
|
+
type: "integer",
|
|
574
|
+
required: true
|
|
575
|
+
},
|
|
576
|
+
status: {
|
|
577
|
+
type: "string",
|
|
578
|
+
required: true
|
|
579
|
+
},
|
|
580
|
+
parentRevision: { type: "integer" }
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
render: (_args, value) => [{
|
|
584
|
+
type: "text",
|
|
585
|
+
text: `已提出修订草案 [${value.id}] v${String(value.revision)}(candidate,验证通过才转正;父版 v${String(value.parentRevision ?? 0)})`
|
|
586
|
+
}]
|
|
587
|
+
};
|
|
588
|
+
const VERIFY_DESCRIPTION = "修·V1: controlled re-enactment (shadow replay) for a revised draft: replay historical situations with known outcomes against the draft. Passing adopts the draft without spending a real attempt. Use when a real retry is expensive or risky.";
|
|
589
|
+
const VERIFY_PARAMETERS = {
|
|
590
|
+
id: {
|
|
591
|
+
type: "string",
|
|
592
|
+
required: true,
|
|
593
|
+
description: "Experience family id."
|
|
594
|
+
},
|
|
595
|
+
revision: {
|
|
596
|
+
type: "integer",
|
|
597
|
+
required: true,
|
|
598
|
+
description: "The draft revision to verify."
|
|
599
|
+
},
|
|
600
|
+
samples: {
|
|
601
|
+
type: "array",
|
|
602
|
+
required: true,
|
|
603
|
+
items: {
|
|
604
|
+
type: "object",
|
|
605
|
+
additionalProperties: false,
|
|
606
|
+
properties: {
|
|
607
|
+
situation: {
|
|
608
|
+
type: "string",
|
|
609
|
+
required: true,
|
|
610
|
+
description: "A historical situation."
|
|
611
|
+
},
|
|
612
|
+
expected: {
|
|
613
|
+
type: "string",
|
|
614
|
+
required: true,
|
|
615
|
+
enum: ["success", "fail"],
|
|
616
|
+
description: "Its known outcome."
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
description: "Historical situations with known outcomes."
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
const VERIFY_OUTPUT = {
|
|
624
|
+
schema: {
|
|
625
|
+
type: "object",
|
|
626
|
+
additionalProperties: false,
|
|
627
|
+
properties: {
|
|
628
|
+
passed: {
|
|
629
|
+
type: "boolean",
|
|
630
|
+
required: true
|
|
631
|
+
},
|
|
632
|
+
agreement: {
|
|
633
|
+
type: "number",
|
|
634
|
+
required: true
|
|
635
|
+
},
|
|
636
|
+
reason: { type: "string" },
|
|
637
|
+
status: { type: "string" }
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
render: (_args, value) => [{
|
|
641
|
+
type: "text",
|
|
642
|
+
text: value.passed ? `影子重放通过(一致率 ${(value.agreement * 100).toFixed(0)}%),修订版已转正 live` : `影子重放未通过(一致率 ${(value.agreement * 100).toFixed(0)}%):${value.reason ?? ""}`
|
|
643
|
+
}]
|
|
644
|
+
};
|
|
645
|
+
const FACT_DESCRIPTION = "记: append one diary entry about an important interaction with the user (event layer — permanent, never auto-deleted). Record: what the user SAID, what they DELEGATED, what you PROMISED, what HAPPENED, PREFERENCE changes, and how you RESPONDED to the user (so the exchange is traceable both ways). Call this promptly after the interaction. When the response says extractionDue, call memory_extract to distill profile facts (the AI's perception of the user) AND open-loop concern memos from the pending diary window.";
|
|
646
|
+
const FACT_PARAMETERS = {
|
|
647
|
+
kind: {
|
|
648
|
+
type: "string",
|
|
649
|
+
required: true,
|
|
650
|
+
enum: [
|
|
651
|
+
"said",
|
|
652
|
+
"delegated",
|
|
653
|
+
"promised",
|
|
654
|
+
"happened",
|
|
655
|
+
"preference",
|
|
656
|
+
"other"
|
|
657
|
+
],
|
|
658
|
+
description: "What kind of interaction this records."
|
|
659
|
+
},
|
|
660
|
+
content: {
|
|
661
|
+
type: "string",
|
|
662
|
+
required: true,
|
|
663
|
+
description: "The diary content: who/what/when in one durable paragraph."
|
|
664
|
+
},
|
|
665
|
+
sessionRef: {
|
|
666
|
+
type: "string",
|
|
667
|
+
description: "Pointer to the session this happened in."
|
|
668
|
+
},
|
|
669
|
+
tags: {
|
|
670
|
+
type: "array",
|
|
671
|
+
items: { type: "string" },
|
|
672
|
+
description: "Free tags for retrieval."
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
const FACT_OUTPUT = {
|
|
676
|
+
schema: {
|
|
677
|
+
type: "object",
|
|
678
|
+
additionalProperties: false,
|
|
679
|
+
properties: {
|
|
680
|
+
id: {
|
|
681
|
+
type: "string",
|
|
682
|
+
required: true
|
|
683
|
+
},
|
|
684
|
+
extractionDue: {
|
|
685
|
+
type: "boolean",
|
|
686
|
+
required: true
|
|
687
|
+
},
|
|
688
|
+
pendingCount: {
|
|
689
|
+
type: "integer",
|
|
690
|
+
required: true
|
|
691
|
+
},
|
|
692
|
+
pending: {
|
|
693
|
+
type: "array",
|
|
694
|
+
required: true,
|
|
695
|
+
items: {
|
|
696
|
+
type: "object",
|
|
697
|
+
additionalProperties: false,
|
|
698
|
+
properties: {
|
|
699
|
+
id: {
|
|
700
|
+
type: "string",
|
|
701
|
+
required: true
|
|
702
|
+
},
|
|
703
|
+
kind: {
|
|
704
|
+
type: "string",
|
|
705
|
+
required: true
|
|
706
|
+
},
|
|
707
|
+
content: {
|
|
708
|
+
type: "string",
|
|
709
|
+
required: true
|
|
710
|
+
},
|
|
711
|
+
ts: {
|
|
712
|
+
type: "integer",
|
|
713
|
+
required: true
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
render: (_args, value) => [{
|
|
721
|
+
type: "text",
|
|
722
|
+
text: value.extractionDue ? `日记已记录 [${value.id}]。⏰ 提取周期已到:请阅读 ${String(value.pendingCount)} 条待提取日记,调用 memory_extract 提交稳定画像条目(带来源指针)。` : `日记已记录 [${value.id}](待提取 ${String(value.pendingCount)} 条,未到提取周期)`
|
|
723
|
+
}]
|
|
724
|
+
};
|
|
725
|
+
const EXTRACT_DESCRIPTION = "记·上升通道: distill the pending diary window into (a) profile facts and (b) concern memos. Facts = the AI's perception of the USER — durable traits/preferences/communication/values/thinking that help collaborate with THIS person (category per its enum definition). Project content, goals, decisions and task context are NOT profile: route open ones to concerns, drop the rest. ONLY the user's own statements may produce facts; when unsure use category=other with a note (parked for human ruling, never force-fit). A fact identical to an existing one (same category+factKey+value) stays ONE entry — re-propose it to add citations/corroboration, never a duplicate. Concerns = OPEN-LOOP MEMOS kept for the USER to be reminded of — something raised but NOT yet closed (todo / thinking / idea / open question / decision / one-off commitment). Every NEW memo MUST carry background (the scene: when/why it came up) so the user can recall it; if a question was already answered, record the answer and close it; use mention to summarize each later round of discussion. Recurring HABITS go to profile and reusable HOW-TOs go to experience — neither is a concern. NOT topic/project labels. action=new once per memo, then action=mention under the same concernId; action=status when it closes. Each proposal/mention needs source diary ids. Conflicting values supersede unlocked facts (old value kept as history); locked facts are parked for human ruling.";
|
|
726
|
+
const EXTRACT_PARAMETERS = {
|
|
727
|
+
proposals: {
|
|
728
|
+
type: "array",
|
|
729
|
+
required: true,
|
|
730
|
+
items: {
|
|
731
|
+
type: "object",
|
|
732
|
+
additionalProperties: false,
|
|
733
|
+
properties: {
|
|
734
|
+
category: {
|
|
735
|
+
type: "string",
|
|
736
|
+
required: true,
|
|
737
|
+
enum: [
|
|
738
|
+
"identity",
|
|
739
|
+
"preference",
|
|
740
|
+
"communication",
|
|
741
|
+
"habit",
|
|
742
|
+
"thinking",
|
|
743
|
+
"value",
|
|
744
|
+
"delegation",
|
|
745
|
+
"background",
|
|
746
|
+
"other"
|
|
747
|
+
],
|
|
748
|
+
description: "Profile facet = the AI's perception of the USER (NOT project content): identity=who/role; preference=non-business likes/style/taste; communication=how to talk and collaborate with them; habit=recurring behavior; thinking=reasoning/methodology style; value=values/beliefs/principles; delegation=how they delegate/authorize autonomy; background=durable context, environment, resources and accounts they own; other=uncertain (park for human ruling). Project goals/decisions/ideas are NOT profile — send open ones to concerns."
|
|
749
|
+
},
|
|
750
|
+
factKey: {
|
|
751
|
+
type: "string",
|
|
752
|
+
required: true,
|
|
753
|
+
description: "Stable slot key inside the category (e.g. \"timezone\")."
|
|
754
|
+
},
|
|
755
|
+
value: {
|
|
756
|
+
type: "string",
|
|
757
|
+
required: true,
|
|
758
|
+
description: "The fact value."
|
|
759
|
+
},
|
|
760
|
+
sourceDiaryIds: {
|
|
761
|
+
type: "array",
|
|
762
|
+
required: true,
|
|
763
|
+
items: { type: "string" },
|
|
764
|
+
description: "Diary entries this fact was extracted from (source pointers)."
|
|
765
|
+
},
|
|
766
|
+
note: {
|
|
767
|
+
type: "string",
|
|
768
|
+
description: "Optional extraction note; required-ish for other (why uncertain)."
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
description: "Proposed stable profile facts (only from the user's own statements)."
|
|
773
|
+
},
|
|
774
|
+
concerns: {
|
|
775
|
+
type: "array",
|
|
776
|
+
items: {
|
|
777
|
+
type: "object",
|
|
778
|
+
additionalProperties: false,
|
|
779
|
+
properties: {
|
|
780
|
+
action: {
|
|
781
|
+
type: "string",
|
|
782
|
+
required: true,
|
|
783
|
+
enum: [
|
|
784
|
+
"new",
|
|
785
|
+
"mention",
|
|
786
|
+
"status"
|
|
787
|
+
]
|
|
788
|
+
},
|
|
789
|
+
title: {
|
|
790
|
+
type: "string",
|
|
791
|
+
description: "new: the memo headline — what the user raised that is still open; mention: one-line SUMMARY of that round of discussion (this is how the sub-level is used)."
|
|
792
|
+
},
|
|
793
|
+
background: {
|
|
794
|
+
type: "string",
|
|
795
|
+
description: "new (REQUIRED for new): the background scene — when/why it came up and enough context that reading this memo alone lets the user recall it. Omit for mention/status."
|
|
796
|
+
},
|
|
797
|
+
kind: {
|
|
798
|
+
type: "string",
|
|
799
|
+
enum: [
|
|
800
|
+
"todo",
|
|
801
|
+
"thinking",
|
|
802
|
+
"idea",
|
|
803
|
+
"question",
|
|
804
|
+
"decision",
|
|
805
|
+
"commitment",
|
|
806
|
+
"other"
|
|
807
|
+
],
|
|
808
|
+
description: "new: the memo type — todo=raised but not done yet; thinking=something the user is mulling over/reflecting on; idea=a notion/aspiration; question=an open question (if it was already answered in the talk, record the answer and close it); decision=a pending or made decision (note the why); commitment=a ONE-OFF promise/appointment; other=uncertain. A RECURRING habit goes to profile, a REUSABLE how-to goes to experience — neither is a concern."
|
|
809
|
+
},
|
|
810
|
+
status: {
|
|
811
|
+
type: "string",
|
|
812
|
+
enum: [
|
|
813
|
+
"ongoing",
|
|
814
|
+
"concluded",
|
|
815
|
+
"recurring",
|
|
816
|
+
"paused"
|
|
817
|
+
],
|
|
818
|
+
description: "status: lifecycle for an existing memo — ongoing=still open; concluded=closed/done; recurring=keeps coming back; paused=parked."
|
|
819
|
+
},
|
|
820
|
+
concernId: {
|
|
821
|
+
type: "string",
|
|
822
|
+
description: "mention/status: the target top-level concern id."
|
|
823
|
+
},
|
|
824
|
+
sourceDiaryIds: {
|
|
825
|
+
type: "array",
|
|
826
|
+
required: true,
|
|
827
|
+
items: { type: "string" }
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
description: "Open-loop memos kept FOR THE USER to be reminded of: new = a raised-but-unclosed item (todo / thinking / idea / open question / decision / one-off commitment) WITH its background scene; mention = the same item touched again (summarize the discussion); status = it closed or changed. A question whose answer is already known must carry the answer. NOT topic/project labels; NOT recurring habits (those are profile); NOT reusable how-tos (those are experiences)."
|
|
832
|
+
},
|
|
833
|
+
summary: {
|
|
834
|
+
type: "string",
|
|
835
|
+
required: true,
|
|
836
|
+
description: "One-line summary of the extraction window."
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
const EXTRACT_OUTPUT = {
|
|
840
|
+
schema: {
|
|
841
|
+
type: "object",
|
|
842
|
+
additionalProperties: false,
|
|
843
|
+
properties: {
|
|
844
|
+
applied: {
|
|
845
|
+
type: "integer",
|
|
846
|
+
required: true
|
|
847
|
+
},
|
|
848
|
+
conflicts: {
|
|
849
|
+
type: "integer",
|
|
850
|
+
required: true
|
|
851
|
+
},
|
|
852
|
+
rejected: {
|
|
853
|
+
type: "array",
|
|
854
|
+
required: true,
|
|
855
|
+
items: {
|
|
856
|
+
type: "object",
|
|
857
|
+
additionalProperties: false,
|
|
858
|
+
properties: { reason: {
|
|
859
|
+
type: "string",
|
|
860
|
+
required: true
|
|
861
|
+
} }
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
appliedConcerns: {
|
|
865
|
+
type: "integer",
|
|
866
|
+
required: true
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
},
|
|
870
|
+
render: (_args, value) => [{
|
|
871
|
+
type: "text",
|
|
872
|
+
text: `画像提取完成:生效 ${String(value.applied)} 条,关心事项更新 ${String(value.appliedConcerns)} 项,待人工裁决冲突 ${String(value.conflicts)} 条,拒绝 ${String(value.rejected.length)} 条` + (value.rejected.length > 0 ? `(${value.rejected.map((r) => r.reason).join(";")})` : "")
|
|
873
|
+
}]
|
|
874
|
+
};
|
|
875
|
+
const LEDGER_DESCRIPTION = "Query the memory audit ledger (append-only event sourcing: every refine/use/challenge/revision/rollback/human operation). Filter by object or operation. Human operations are always audited.";
|
|
876
|
+
const LEDGER_PARAMETERS = {
|
|
877
|
+
objectType: {
|
|
878
|
+
type: "string",
|
|
879
|
+
enum: [
|
|
880
|
+
"experience",
|
|
881
|
+
"fact",
|
|
882
|
+
"diary",
|
|
883
|
+
"library"
|
|
884
|
+
],
|
|
885
|
+
description: "Filter by object type."
|
|
886
|
+
},
|
|
887
|
+
objectId: {
|
|
888
|
+
type: "string",
|
|
889
|
+
description: "Filter by object id (e.g. an experience id)."
|
|
890
|
+
},
|
|
891
|
+
op: {
|
|
892
|
+
type: "string",
|
|
893
|
+
description: "Filter by operation name (refine/use/challenge/propose/adopt/rollback/...)."
|
|
894
|
+
},
|
|
895
|
+
limit: {
|
|
896
|
+
type: "integer",
|
|
897
|
+
description: "Max blocks, newest first (default 20)."
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
const LEDGER_OUTPUT = {
|
|
901
|
+
schema: {
|
|
902
|
+
type: "object",
|
|
903
|
+
additionalProperties: false,
|
|
904
|
+
properties: { blocks: {
|
|
905
|
+
type: "array",
|
|
906
|
+
required: true,
|
|
907
|
+
items: {
|
|
908
|
+
type: "object",
|
|
909
|
+
additionalProperties: false,
|
|
910
|
+
properties: {
|
|
911
|
+
seq: {
|
|
912
|
+
type: "integer",
|
|
913
|
+
required: true
|
|
914
|
+
},
|
|
915
|
+
ts: {
|
|
916
|
+
type: "integer",
|
|
917
|
+
required: true
|
|
918
|
+
},
|
|
919
|
+
op: {
|
|
920
|
+
type: "string",
|
|
921
|
+
required: true
|
|
922
|
+
},
|
|
923
|
+
objectType: {
|
|
924
|
+
type: "string",
|
|
925
|
+
required: true
|
|
926
|
+
},
|
|
927
|
+
objectId: {
|
|
928
|
+
type: "string",
|
|
929
|
+
required: true
|
|
930
|
+
},
|
|
931
|
+
actor: {
|
|
932
|
+
type: "string",
|
|
933
|
+
required: true
|
|
934
|
+
},
|
|
935
|
+
reason: { type: "string" }
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
} }
|
|
939
|
+
},
|
|
940
|
+
render: (_args, value) => [{
|
|
941
|
+
type: "text",
|
|
942
|
+
text: value.blocks.length === 0 ? "账本中没有匹配的记录。" : value.blocks.map((b) => `#${String(b.seq)} ${new Date(b.ts).toISOString().slice(0, 16).replace("T", " ")} ${b.op} ${b.objectType}:${b.objectId} by ${b.actor}${b.reason !== void 0 ? `(${b.reason})` : ""}`).join("\n")
|
|
943
|
+
}]
|
|
944
|
+
};
|
|
945
|
+
const CONSOLIDATE_DESCRIPTION = "记·巩固: merge a set of closely related experiences into ONE more distilled experience. Use when the recall output flags consolidation due, or when you notice several experiences that restate the same underlying lesson from different sessions. For each merge: list the source experience ids (≥2, from memory_recall / the library) and write the consolidated gist/situation/path/reasoning/limits as a single transferable lesson — do not just concatenate; distill what is common and drop what is session-specific. The sources are archived (they leave recall but stay recoverable); the consolidated experience inherits their earned trust. Consolidation never hard-deletes anything.";
|
|
946
|
+
const CONSOLIDATE_PARAMETERS = {
|
|
947
|
+
merges: {
|
|
948
|
+
type: "array",
|
|
949
|
+
required: true,
|
|
950
|
+
description: "One entry per group of experiences to merge into one.",
|
|
951
|
+
items: {
|
|
952
|
+
type: "object",
|
|
953
|
+
additionalProperties: false,
|
|
954
|
+
properties: {
|
|
955
|
+
family: {
|
|
956
|
+
type: "string",
|
|
957
|
+
required: true,
|
|
958
|
+
description: "Family tag of the consolidated experience."
|
|
959
|
+
},
|
|
960
|
+
kind: {
|
|
961
|
+
type: "string",
|
|
962
|
+
required: true,
|
|
963
|
+
enum: ["positive", "negative"],
|
|
964
|
+
description: "Kind of the consolidated experience."
|
|
965
|
+
},
|
|
966
|
+
gist: {
|
|
967
|
+
type: "string",
|
|
968
|
+
required: true,
|
|
969
|
+
description: "The single distilled, transferable lesson (one sentence)."
|
|
970
|
+
},
|
|
971
|
+
situation: {
|
|
972
|
+
type: "array",
|
|
973
|
+
required: true,
|
|
974
|
+
items: { type: "string" },
|
|
975
|
+
description: "When it applies (generalized)."
|
|
976
|
+
},
|
|
977
|
+
path: {
|
|
978
|
+
type: "array",
|
|
979
|
+
required: true,
|
|
980
|
+
items: { type: "string" },
|
|
981
|
+
description: "The reusable success path as ordered actions."
|
|
982
|
+
},
|
|
983
|
+
reasoning: {
|
|
984
|
+
type: "string",
|
|
985
|
+
required: true,
|
|
986
|
+
description: "Why it works — transferable, never a session chronicle."
|
|
987
|
+
},
|
|
988
|
+
limits: {
|
|
989
|
+
type: "array",
|
|
990
|
+
required: true,
|
|
991
|
+
items: { type: "string" },
|
|
992
|
+
description: "Applicability limits/boundaries."
|
|
993
|
+
},
|
|
994
|
+
sourceIds: {
|
|
995
|
+
type: "array",
|
|
996
|
+
required: true,
|
|
997
|
+
items: { type: "string" },
|
|
998
|
+
description: "Family ids of the experiences being merged (≥2)."
|
|
999
|
+
},
|
|
1000
|
+
note: {
|
|
1001
|
+
type: "string",
|
|
1002
|
+
description: "Optional: why these belong together."
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
note: {
|
|
1008
|
+
type: "string",
|
|
1009
|
+
description: "Optional free-form summary of this consolidation run."
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
const CONSOLIDATE_OUTPUT = {
|
|
1013
|
+
schema: {
|
|
1014
|
+
type: "object",
|
|
1015
|
+
additionalProperties: false,
|
|
1016
|
+
properties: {
|
|
1017
|
+
consolidated: {
|
|
1018
|
+
type: "integer",
|
|
1019
|
+
required: true
|
|
1020
|
+
},
|
|
1021
|
+
archived: {
|
|
1022
|
+
type: "integer",
|
|
1023
|
+
required: true
|
|
1024
|
+
},
|
|
1025
|
+
skipped: {
|
|
1026
|
+
type: "array",
|
|
1027
|
+
required: true,
|
|
1028
|
+
items: {
|
|
1029
|
+
type: "object",
|
|
1030
|
+
additionalProperties: false,
|
|
1031
|
+
properties: {
|
|
1032
|
+
sourceIds: {
|
|
1033
|
+
type: "array",
|
|
1034
|
+
required: true,
|
|
1035
|
+
items: { type: "string" }
|
|
1036
|
+
},
|
|
1037
|
+
reason: {
|
|
1038
|
+
type: "string",
|
|
1039
|
+
required: true
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
render: (_args, value) => [{
|
|
1047
|
+
type: "text",
|
|
1048
|
+
text: `巩固完成:合并出 ${String(value.consolidated)} 条精炼经验,归档 ${String(value.archived)} 条来源。` + (value.skipped.length === 0 ? "" : `\n跳过 ${String(value.skipped.length)} 组:\n` + value.skipped.map((s) => `- ${s.sourceIds.join(",")}:${s.reason}`).join("\n"))
|
|
1049
|
+
}]
|
|
1050
|
+
};
|
|
1051
|
+
/**
|
|
1052
|
+
* Host plugin body: register the memory tools and the 生·用·修·记 protocol
|
|
1053
|
+
* section. Mounted by each agent preset that should see the tools.
|
|
1054
|
+
* @param ctx - host context inside the agent realm.
|
|
1055
|
+
*/
|
|
1056
|
+
function apply(ctx) {
|
|
1057
|
+
const memory = ctx.memory;
|
|
1058
|
+
ctx.systemPrompt.section({
|
|
1059
|
+
name: "tool:memory",
|
|
1060
|
+
order: 115,
|
|
1061
|
+
text: "Self-evolving memory (生·用·修·记): one shared library of VERIFIED procedural experiences (paths that worked, with judgment context and limits) plus permanent user facts. Unverified candidates never enter trusted recall — knowledge is earned. Experiences are scoped by a context/domain: pass context to recall so unrelated domains do not bleed in. 1) BEFORE a complex task, call memory_recall with the current context; follow direct verdicts, adapt reference verdicts, respect limit-conflict warnings; an explicit \"no relevant experience\" means explore fresh. If candidateTrials are returned, they are UNVERIFIED matches: you may trial one, then memory_report the outcome (success promotes it; failure sends it to the cold palace). 2) AFTER a complex task (wrong paths, corrections, limits hit, or a confirmed dead end), call memory_refine with the episodic evidence pointer and the context; never refine trivial calls. One lesson per call, written generalized: reasoning states the transferable why/boundary, NEVER a session chronicle (\"本会话/本次…\"); the session is only the evidence pointer. 3) AFTER acting on a recalled experience, call memory_report with the outcome and honest attribution (environment failures never count; insufficient signal is not counted); one execution reports once (dedupeKey). 4) A challenged experience is quarantined: call memory_revise with a diagnosis, then verify the draft (one successful use via memory_report with the draft revision) before it goes live. 5) Diary discipline: after important user interactions call memory_fact (what the user said/delegated, what you promised, what happened, preference changes, and how you RESPONDED); when the response says extractionDue, call memory_extract to distill BOTH stable profile facts (choose the facet by its definition; only the user's own statements qualify; unsure = category \"other\" with a note, never force-fit) AND evolving concerns (a one-off cared-about thing, or a big category discussed repeatedly: new once, then mention per round). 6) To learn from an external source (a book, a skill, a document, a transcript), extract its lessons and call memory_ingest with sourceType, sourceRef provenance and context; each draft becomes an earned candidate. External sources NEVER produce profile facts or concerns — only the user's own words may. 7) Consolidation: when memory_recall flags consolidationDue (or you notice several experiences restating the same lesson), call memory_consolidate to merge the related ones into a single distilled experience — list the source ids and write one transferable lesson, not a concatenation. This keeps the library lean; sources are archived, never hard-deleted."
|
|
1062
|
+
});
|
|
1063
|
+
ctx.systemPrompt.section({
|
|
1064
|
+
name: "tool:memory-profile",
|
|
1065
|
+
order: 116,
|
|
1066
|
+
text: () => {
|
|
1067
|
+
try {
|
|
1068
|
+
const snapshot = memory.profileSnapshot();
|
|
1069
|
+
return snapshot === "" ? "" : `Long-term memory of this user:\n${snapshot}`;
|
|
1070
|
+
} catch {
|
|
1071
|
+
return "";
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
ctx.tools.register(defineTool({
|
|
1076
|
+
name: "memory_recall",
|
|
1077
|
+
description: RECALL_DESCRIPTION,
|
|
1078
|
+
parameters: RECALL_PARAMETERS,
|
|
1079
|
+
output: RECALL_OUTPUT,
|
|
1080
|
+
execute: async (args, exec) => {
|
|
1081
|
+
const result = await memory.recall(callingAgent(exec), {
|
|
1082
|
+
situation: args.situation,
|
|
1083
|
+
topK: args.topK ?? 6,
|
|
1084
|
+
deep: args.deep === true,
|
|
1085
|
+
...args.context === void 0 ? {} : { context: args.context }
|
|
1086
|
+
});
|
|
1087
|
+
return {
|
|
1088
|
+
none: result.none,
|
|
1089
|
+
omitted: result.omitted,
|
|
1090
|
+
estimatedTokens: result.estimatedTokens,
|
|
1091
|
+
...result.reason === void 0 ? {} : { reason: result.reason },
|
|
1092
|
+
items: result.items.map((item) => ({
|
|
1093
|
+
id: item.experience.id,
|
|
1094
|
+
revision: item.experience.revision,
|
|
1095
|
+
kind: item.experience.kind,
|
|
1096
|
+
status: item.experience.status,
|
|
1097
|
+
gist: item.experience.gist,
|
|
1098
|
+
...item.experience.failureReason === void 0 ? {} : { failureReason: item.experience.failureReason },
|
|
1099
|
+
situation: [...item.experience.situation],
|
|
1100
|
+
path: item.experience.path.map((step) => ({
|
|
1101
|
+
action: step.action,
|
|
1102
|
+
order: step.order
|
|
1103
|
+
})),
|
|
1104
|
+
reasoning: item.experience.reasoning,
|
|
1105
|
+
limits: [...item.experience.limits],
|
|
1106
|
+
trust: item.experience.trust,
|
|
1107
|
+
samples: item.experience.samples,
|
|
1108
|
+
...item.experience.lastVerifiedAt === void 0 ? {} : { lastVerifiedAt: item.experience.lastVerifiedAt },
|
|
1109
|
+
score: item.score,
|
|
1110
|
+
verdict: item.verdict,
|
|
1111
|
+
conflicts: [...item.conflicts],
|
|
1112
|
+
context: item.experience.context,
|
|
1113
|
+
verifiedCount: item.experience.verifiedCount,
|
|
1114
|
+
rejectCount: item.experience.rejectCount,
|
|
1115
|
+
globalFlag: item.experience.globalFlag
|
|
1116
|
+
})),
|
|
1117
|
+
candidateTrials: result.candidateTrials.map((trial) => ({
|
|
1118
|
+
id: trial.experience.id,
|
|
1119
|
+
revision: trial.experience.revision,
|
|
1120
|
+
gist: trial.experience.gist,
|
|
1121
|
+
situation: [...trial.experience.situation],
|
|
1122
|
+
score: trial.score
|
|
1123
|
+
})),
|
|
1124
|
+
consolidationDue: result.consolidationDue
|
|
1125
|
+
};
|
|
1126
|
+
},
|
|
1127
|
+
presentCall: (args) => present("召回经验", "read", args.situation)
|
|
1128
|
+
}));
|
|
1129
|
+
ctx.tools.register(defineTool({
|
|
1130
|
+
name: "memory_refine",
|
|
1131
|
+
description: REFINE_DESCRIPTION,
|
|
1132
|
+
parameters: REFINE_PARAMETERS,
|
|
1133
|
+
output: REFINE_OUTPUT,
|
|
1134
|
+
execute: async (args, exec) => {
|
|
1135
|
+
const result = await memory.refine(callingAgent(exec), {
|
|
1136
|
+
kind: args.kind,
|
|
1137
|
+
family: args.family,
|
|
1138
|
+
gist: args.gist,
|
|
1139
|
+
situation: args.situation,
|
|
1140
|
+
path: args.path,
|
|
1141
|
+
reasoning: args.reasoning,
|
|
1142
|
+
limits: args.limits ?? [],
|
|
1143
|
+
...args.failureReason === void 0 ? {} : { failureReason: args.failureReason },
|
|
1144
|
+
evidence: args.evidence,
|
|
1145
|
+
complexity: args.complexity,
|
|
1146
|
+
humanMarked: args.humanMarked === true,
|
|
1147
|
+
...args.context === void 0 ? {} : { context: args.context }
|
|
1148
|
+
});
|
|
1149
|
+
return {
|
|
1150
|
+
accepted: result.accepted,
|
|
1151
|
+
...result.reason === void 0 ? {} : { reason: result.reason },
|
|
1152
|
+
...result.corroboratedId === void 0 ? {} : { corroboratedId: result.corroboratedId },
|
|
1153
|
+
...result.experience === void 0 ? {} : {
|
|
1154
|
+
id: result.experience.id,
|
|
1155
|
+
revision: result.experience.revision,
|
|
1156
|
+
status: result.experience.status
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
},
|
|
1160
|
+
presentCall: () => present("提炼经验", "other")
|
|
1161
|
+
}));
|
|
1162
|
+
ctx.tools.register(defineTool({
|
|
1163
|
+
name: "memory_report",
|
|
1164
|
+
description: REPORT_DESCRIPTION,
|
|
1165
|
+
parameters: REPORT_PARAMETERS,
|
|
1166
|
+
output: REPORT_OUTPUT,
|
|
1167
|
+
execute: async (args, exec) => {
|
|
1168
|
+
const result = await memory.report(callingAgent(exec), {
|
|
1169
|
+
id: args.id,
|
|
1170
|
+
...args.revision === void 0 ? {} : { revision: args.revision },
|
|
1171
|
+
outcome: args.outcome,
|
|
1172
|
+
...args.attribution === void 0 ? {} : { attribution: args.attribution },
|
|
1173
|
+
...args.evidence === void 0 ? {} : { evidence: args.evidence },
|
|
1174
|
+
...args.tokensUsed === void 0 ? {} : { tokensUsed: args.tokensUsed },
|
|
1175
|
+
...args.tokensSaved === void 0 ? {} : { tokensSaved: args.tokensSaved },
|
|
1176
|
+
...args.dedupeKey === void 0 ? {} : { dedupeKey: args.dedupeKey }
|
|
1177
|
+
});
|
|
1178
|
+
return {
|
|
1179
|
+
id: result.snapshot.id,
|
|
1180
|
+
status: result.snapshot.status,
|
|
1181
|
+
trust: result.snapshot.trust,
|
|
1182
|
+
samples: result.snapshot.samples,
|
|
1183
|
+
counted: result.counted,
|
|
1184
|
+
attributionApplied: result.attributionApplied,
|
|
1185
|
+
...result.overrideNote === void 0 ? {} : { overrideNote: result.overrideNote },
|
|
1186
|
+
challenged: result.challenged,
|
|
1187
|
+
promoted: result.promoted,
|
|
1188
|
+
adopted: result.adopted
|
|
1189
|
+
};
|
|
1190
|
+
},
|
|
1191
|
+
presentCall: (args) => present("汇报使用结果", "other", args.id)
|
|
1192
|
+
}));
|
|
1193
|
+
ctx.tools.register(defineTool({
|
|
1194
|
+
name: "memory_revise",
|
|
1195
|
+
description: REVISE_DESCRIPTION,
|
|
1196
|
+
parameters: REVISE_PARAMETERS,
|
|
1197
|
+
output: REVISE_OUTPUT,
|
|
1198
|
+
execute: async (args, exec) => {
|
|
1199
|
+
const snapshot = await memory.revise(callingAgent(exec), {
|
|
1200
|
+
id: args.id,
|
|
1201
|
+
reason: args.reason,
|
|
1202
|
+
...args.gist === void 0 ? {} : { gist: args.gist },
|
|
1203
|
+
...args.situation === void 0 ? {} : { situation: args.situation },
|
|
1204
|
+
...args.path === void 0 ? {} : { path: args.path },
|
|
1205
|
+
...args.reasoning === void 0 ? {} : { reasoning: args.reasoning },
|
|
1206
|
+
...args.limits === void 0 ? {} : { limits: args.limits }
|
|
1207
|
+
});
|
|
1208
|
+
return {
|
|
1209
|
+
id: snapshot.id,
|
|
1210
|
+
revision: snapshot.revision,
|
|
1211
|
+
status: snapshot.status,
|
|
1212
|
+
...snapshot.parentRevision === void 0 ? {} : { parentRevision: snapshot.parentRevision }
|
|
1213
|
+
};
|
|
1214
|
+
},
|
|
1215
|
+
presentCall: (args) => present("提出修订草案", "other", args.id)
|
|
1216
|
+
}));
|
|
1217
|
+
ctx.tools.register(defineTool({
|
|
1218
|
+
name: "memory_verify",
|
|
1219
|
+
description: VERIFY_DESCRIPTION,
|
|
1220
|
+
parameters: VERIFY_PARAMETERS,
|
|
1221
|
+
output: VERIFY_OUTPUT,
|
|
1222
|
+
execute: async (args, exec) => {
|
|
1223
|
+
const result = await memory.verifyShadow(callingAgent(exec), {
|
|
1224
|
+
id: args.id,
|
|
1225
|
+
revision: args.revision,
|
|
1226
|
+
samples: args.samples
|
|
1227
|
+
});
|
|
1228
|
+
return {
|
|
1229
|
+
passed: result.passed,
|
|
1230
|
+
agreement: result.agreement,
|
|
1231
|
+
...result.reason === void 0 ? {} : { reason: result.reason },
|
|
1232
|
+
...result.snapshot === void 0 ? {} : { status: result.snapshot.status }
|
|
1233
|
+
};
|
|
1234
|
+
},
|
|
1235
|
+
presentCall: (args) => present("影子重放验证", "other", args.id)
|
|
1236
|
+
}));
|
|
1237
|
+
ctx.tools.register(defineTool({
|
|
1238
|
+
name: "memory_fact",
|
|
1239
|
+
description: FACT_DESCRIPTION,
|
|
1240
|
+
parameters: FACT_PARAMETERS,
|
|
1241
|
+
output: FACT_OUTPUT,
|
|
1242
|
+
execute: async (args, exec) => {
|
|
1243
|
+
const result = await memory.appendDiary(callingAgent(exec), {
|
|
1244
|
+
kind: args.kind,
|
|
1245
|
+
content: args.content,
|
|
1246
|
+
...args.sessionRef === void 0 ? {} : { sessionRef: args.sessionRef },
|
|
1247
|
+
...args.tags === void 0 ? {} : { tags: args.tags }
|
|
1248
|
+
});
|
|
1249
|
+
return {
|
|
1250
|
+
id: result.entry.id,
|
|
1251
|
+
extractionDue: result.extractionDue,
|
|
1252
|
+
pendingCount: result.pendingDiary?.length ?? 0,
|
|
1253
|
+
pending: (result.pendingDiary ?? []).map((entry) => ({
|
|
1254
|
+
id: entry.id,
|
|
1255
|
+
kind: entry.kind,
|
|
1256
|
+
content: entry.content,
|
|
1257
|
+
ts: entry.ts
|
|
1258
|
+
}))
|
|
1259
|
+
};
|
|
1260
|
+
},
|
|
1261
|
+
presentCall: (args) => present("记日记", "other", args.kind)
|
|
1262
|
+
}));
|
|
1263
|
+
ctx.tools.register(defineTool({
|
|
1264
|
+
name: "memory_extract",
|
|
1265
|
+
description: EXTRACT_DESCRIPTION,
|
|
1266
|
+
parameters: EXTRACT_PARAMETERS,
|
|
1267
|
+
output: EXTRACT_OUTPUT,
|
|
1268
|
+
execute: async (args, exec) => {
|
|
1269
|
+
const result = await memory.extract(callingAgent(exec), {
|
|
1270
|
+
proposals: args.proposals,
|
|
1271
|
+
...args.concerns === void 0 ? {} : { concerns: args.concerns },
|
|
1272
|
+
summary: args.summary
|
|
1273
|
+
});
|
|
1274
|
+
return {
|
|
1275
|
+
applied: result.applied.length,
|
|
1276
|
+
conflicts: result.conflicts.length,
|
|
1277
|
+
rejected: result.rejected.map((entry) => ({ reason: entry.reason })),
|
|
1278
|
+
appliedConcerns: result.appliedConcerns
|
|
1279
|
+
};
|
|
1280
|
+
},
|
|
1281
|
+
presentCall: () => present("提取画像", "other")
|
|
1282
|
+
}));
|
|
1283
|
+
ctx.tools.register(defineTool({
|
|
1284
|
+
name: "memory_ingest",
|
|
1285
|
+
description: "摄取归一: batch-submit experiences extracted from ANY source (a book, a skill, a document, a transcript). Each item becomes an earned candidate carrying provenance (sourceType + sourceRef) and the declared context scope; candidates never recall until verified. ONE item = ONE reusable lesson, written GENERALIZED (transferable reasoning, never a session/source chronicle; the sourceRef is the evidence pointer). Near-duplicates of existing experiences are corroborated, not re-added. Call this after reading/extracting from an external source you want the library to learn from.",
|
|
1286
|
+
parameters: {
|
|
1287
|
+
sourceType: {
|
|
1288
|
+
type: "string",
|
|
1289
|
+
required: true,
|
|
1290
|
+
enum: [
|
|
1291
|
+
"conversation",
|
|
1292
|
+
"document",
|
|
1293
|
+
"skill",
|
|
1294
|
+
"book",
|
|
1295
|
+
"note",
|
|
1296
|
+
"other"
|
|
1297
|
+
],
|
|
1298
|
+
description: "What kind of source was read (sets the confidence prior)."
|
|
1299
|
+
},
|
|
1300
|
+
sourceRef: {
|
|
1301
|
+
type: "string",
|
|
1302
|
+
required: true,
|
|
1303
|
+
description: "Provenance pointer: which book / skill / session / document (audited)."
|
|
1304
|
+
},
|
|
1305
|
+
context: {
|
|
1306
|
+
type: "string",
|
|
1307
|
+
description: "Context/domain scope assigned to the produced candidates (e.g. \"coding-windows-build\")."
|
|
1308
|
+
},
|
|
1309
|
+
note: {
|
|
1310
|
+
type: "string",
|
|
1311
|
+
description: "Optional note about the ingest run (audited)."
|
|
1312
|
+
},
|
|
1313
|
+
experiences: {
|
|
1314
|
+
type: "array",
|
|
1315
|
+
required: true,
|
|
1316
|
+
description: "Extracted experience drafts; each becomes a candidate.",
|
|
1317
|
+
items: {
|
|
1318
|
+
type: "object",
|
|
1319
|
+
additionalProperties: false,
|
|
1320
|
+
properties: {
|
|
1321
|
+
kind: {
|
|
1322
|
+
type: "string",
|
|
1323
|
+
required: true,
|
|
1324
|
+
enum: ["positive", "negative"]
|
|
1325
|
+
},
|
|
1326
|
+
family: {
|
|
1327
|
+
type: "string",
|
|
1328
|
+
required: true
|
|
1329
|
+
},
|
|
1330
|
+
gist: {
|
|
1331
|
+
type: "string",
|
|
1332
|
+
required: true,
|
|
1333
|
+
description: "ONE crisp reusable lesson; split several lessons into several items."
|
|
1334
|
+
},
|
|
1335
|
+
situation: {
|
|
1336
|
+
type: "array",
|
|
1337
|
+
required: true,
|
|
1338
|
+
items: { type: "string" },
|
|
1339
|
+
description: "Generalizable situation class that triggers recall, not \"this task\"."
|
|
1340
|
+
},
|
|
1341
|
+
path: {
|
|
1342
|
+
type: "array",
|
|
1343
|
+
required: true,
|
|
1344
|
+
items: {
|
|
1345
|
+
type: "object",
|
|
1346
|
+
additionalProperties: false,
|
|
1347
|
+
properties: {
|
|
1348
|
+
order: {
|
|
1349
|
+
type: "integer",
|
|
1350
|
+
required: true
|
|
1351
|
+
},
|
|
1352
|
+
action: {
|
|
1353
|
+
type: "string",
|
|
1354
|
+
required: true
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1359
|
+
reasoning: {
|
|
1360
|
+
type: "string",
|
|
1361
|
+
required: true,
|
|
1362
|
+
description: "Transferable why (under <situation> this holds because <cause>); never session narration."
|
|
1363
|
+
},
|
|
1364
|
+
limits: {
|
|
1365
|
+
type: "array",
|
|
1366
|
+
required: true,
|
|
1367
|
+
items: { type: "string" }
|
|
1368
|
+
},
|
|
1369
|
+
failureReason: { type: "string" }
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
output: {
|
|
1375
|
+
schema: {
|
|
1376
|
+
type: "object",
|
|
1377
|
+
additionalProperties: false,
|
|
1378
|
+
properties: {
|
|
1379
|
+
accepted: {
|
|
1380
|
+
type: "integer",
|
|
1381
|
+
required: true
|
|
1382
|
+
},
|
|
1383
|
+
acceptedIds: {
|
|
1384
|
+
type: "array",
|
|
1385
|
+
required: true,
|
|
1386
|
+
items: { type: "string" }
|
|
1387
|
+
},
|
|
1388
|
+
rejected: {
|
|
1389
|
+
type: "array",
|
|
1390
|
+
required: true,
|
|
1391
|
+
items: {
|
|
1392
|
+
type: "object",
|
|
1393
|
+
additionalProperties: false,
|
|
1394
|
+
properties: {
|
|
1395
|
+
gist: {
|
|
1396
|
+
type: "string",
|
|
1397
|
+
required: true
|
|
1398
|
+
},
|
|
1399
|
+
reason: {
|
|
1400
|
+
type: "string",
|
|
1401
|
+
required: true
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
},
|
|
1408
|
+
render: (_args, value) => [{
|
|
1409
|
+
type: "text",
|
|
1410
|
+
text: `摄取完成:接受 ${String(value.accepted)} 条候选,拒收 ${String(value.rejected.length)} 条。` + (value.rejected.length === 0 ? "" : `\n拒收:\n${value.rejected.map((r) => `- ${r.gist}:${r.reason}`).join("\n")}`)
|
|
1411
|
+
}]
|
|
1412
|
+
},
|
|
1413
|
+
execute: async (args, exec) => {
|
|
1414
|
+
const result = await memory.ingest(callingAgent(exec), {
|
|
1415
|
+
sourceType: args.sourceType,
|
|
1416
|
+
sourceRef: args.sourceRef,
|
|
1417
|
+
...args.context === void 0 ? {} : { context: args.context },
|
|
1418
|
+
...args.note === void 0 ? {} : { note: args.note },
|
|
1419
|
+
experiences: args.experiences
|
|
1420
|
+
});
|
|
1421
|
+
return {
|
|
1422
|
+
accepted: result.accepted.length,
|
|
1423
|
+
acceptedIds: result.accepted.map((exp) => exp.id),
|
|
1424
|
+
rejected: result.rejected
|
|
1425
|
+
};
|
|
1426
|
+
},
|
|
1427
|
+
presentCall: (args) => present("摄取记忆", "other", args.sourceRef)
|
|
1428
|
+
}));
|
|
1429
|
+
ctx.tools.register(defineTool({
|
|
1430
|
+
name: "memory_ledger",
|
|
1431
|
+
description: LEDGER_DESCRIPTION,
|
|
1432
|
+
parameters: LEDGER_PARAMETERS,
|
|
1433
|
+
output: LEDGER_OUTPUT,
|
|
1434
|
+
execute: async (args, exec) => {
|
|
1435
|
+
return { blocks: (await memory.ledgerQuery(callingAgent(exec), {
|
|
1436
|
+
...args.objectType === void 0 ? {} : { objectType: args.objectType },
|
|
1437
|
+
...args.objectId === void 0 ? {} : { objectId: args.objectId },
|
|
1438
|
+
...args.op === void 0 ? {} : { op: args.op },
|
|
1439
|
+
limit: args.limit ?? 20
|
|
1440
|
+
})).map((block) => ({
|
|
1441
|
+
seq: block.seq,
|
|
1442
|
+
ts: block.ts,
|
|
1443
|
+
op: block.op,
|
|
1444
|
+
objectType: block.objectType,
|
|
1445
|
+
objectId: block.objectId,
|
|
1446
|
+
actor: block.actor,
|
|
1447
|
+
...block.reason === void 0 ? {} : { reason: block.reason }
|
|
1448
|
+
})) };
|
|
1449
|
+
},
|
|
1450
|
+
presentCall: () => present("查询账本", "read")
|
|
1451
|
+
}));
|
|
1452
|
+
ctx.tools.register(defineTool({
|
|
1453
|
+
name: "memory_consolidate",
|
|
1454
|
+
description: CONSOLIDATE_DESCRIPTION,
|
|
1455
|
+
parameters: CONSOLIDATE_PARAMETERS,
|
|
1456
|
+
output: CONSOLIDATE_OUTPUT,
|
|
1457
|
+
execute: async (args, exec) => {
|
|
1458
|
+
const result = await memory.consolidate(callingAgent(exec), {
|
|
1459
|
+
merges: args.merges,
|
|
1460
|
+
...args.note === void 0 ? {} : { note: args.note }
|
|
1461
|
+
});
|
|
1462
|
+
return {
|
|
1463
|
+
consolidated: result.consolidated,
|
|
1464
|
+
archived: result.archived,
|
|
1465
|
+
skipped: result.skipped
|
|
1466
|
+
};
|
|
1467
|
+
},
|
|
1468
|
+
presentCall: () => present("巩固记忆", "other")
|
|
1469
|
+
}));
|
|
1470
|
+
}
|
|
1471
|
+
//#endregion
|
|
1472
|
+
export { apply, inject, name };
|