dsh-continual-evolve 0.11.0 → 0.12.1

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.
Files changed (161) hide show
  1. package/README.md +42 -115
  2. package/README.zh.md +33 -93
  3. package/client/client.js +253 -0
  4. package/cordis.patch.yml +2 -2
  5. package/lib/card-routes.d.ts +32 -0
  6. package/lib/card-routes.js +92 -0
  7. package/lib/index.d.ts +65 -217
  8. package/lib/index.js +74 -234
  9. package/lib/known-workspaces.d.ts +22 -0
  10. package/lib/known-workspaces.js +42 -0
  11. package/lib/memory-guide.d.ts +13 -14
  12. package/lib/memory-guide.js +34 -21
  13. package/lib/memory-section.d.ts +77 -0
  14. package/lib/memory-section.js +189 -0
  15. package/lib/memory-snapshot.d.ts +54 -0
  16. package/lib/memory-snapshot.js +125 -0
  17. package/package.json +8 -17
  18. package/lib/apply.d.ts +0 -22
  19. package/lib/apply.js +0 -157
  20. package/lib/approval.d.ts +0 -73
  21. package/lib/approval.js +0 -127
  22. package/lib/audit-log.d.ts +0 -19
  23. package/lib/audit-log.js +0 -52
  24. package/lib/auto.d.ts +0 -236
  25. package/lib/auto.js +0 -773
  26. package/lib/autocase.d.ts +0 -38
  27. package/lib/autocase.js +0 -76
  28. package/lib/benchmark-command.d.ts +0 -9
  29. package/lib/benchmark-command.js +0 -357
  30. package/lib/benchmark.d.ts +0 -151
  31. package/lib/benchmark.js +0 -250
  32. package/lib/command-util.d.ts +0 -70
  33. package/lib/command-util.js +0 -123
  34. package/lib/command.d.ts +0 -13
  35. package/lib/command.js +0 -134
  36. package/lib/consolidate.d.ts +0 -90
  37. package/lib/consolidate.js +0 -180
  38. package/lib/copy.d.ts +0 -37
  39. package/lib/copy.js +0 -58
  40. package/lib/declines.d.ts +0 -103
  41. package/lib/declines.js +0 -179
  42. package/lib/evaluate.d.ts +0 -74
  43. package/lib/evaluate.js +0 -271
  44. package/lib/evolve-event.d.ts +0 -40
  45. package/lib/evolve-event.js +0 -60
  46. package/lib/extraction-phase.d.ts +0 -132
  47. package/lib/extraction-phase.js +0 -236
  48. package/lib/failures.d.ts +0 -42
  49. package/lib/failures.js +0 -171
  50. package/lib/fate.d.ts +0 -130
  51. package/lib/fate.js +0 -350
  52. package/lib/goal-command.d.ts +0 -7
  53. package/lib/goal-command.js +0 -37
  54. package/lib/goal.d.ts +0 -72
  55. package/lib/goal.js +0 -72
  56. package/lib/inject.d.ts +0 -90
  57. package/lib/inject.js +0 -164
  58. package/lib/injection-caps.d.ts +0 -4
  59. package/lib/injection-caps.js +0 -4
  60. package/lib/injection-directory.d.ts +0 -29
  61. package/lib/injection-directory.js +0 -78
  62. package/lib/injection-rank.d.ts +0 -85
  63. package/lib/injection-rank.js +0 -161
  64. package/lib/listener.d.ts +0 -45
  65. package/lib/listener.js +0 -199
  66. package/lib/llm-text.d.ts +0 -104
  67. package/lib/llm-text.js +0 -140
  68. package/lib/logfile.d.ts +0 -71
  69. package/lib/logfile.js +0 -159
  70. package/lib/maintenance-commands.d.ts +0 -23
  71. package/lib/maintenance-commands.js +0 -136
  72. package/lib/memory-agent.d.ts +0 -93
  73. package/lib/memory-agent.js +0 -300
  74. package/lib/memory-apply.d.ts +0 -44
  75. package/lib/memory-apply.js +0 -202
  76. package/lib/memory-benchmark.d.ts +0 -76
  77. package/lib/memory-benchmark.js +0 -170
  78. package/lib/memory-index.d.ts +0 -114
  79. package/lib/memory-index.js +0 -197
  80. package/lib/memory-manifest.d.ts +0 -16
  81. package/lib/memory-manifest.js +0 -70
  82. package/lib/message-source.d.ts +0 -23
  83. package/lib/message-source.js +0 -12
  84. package/lib/mount-command.d.ts +0 -10
  85. package/lib/mount-command.js +0 -48
  86. package/lib/mount.d.ts +0 -42
  87. package/lib/mount.js +0 -212
  88. package/lib/notify.d.ts +0 -48
  89. package/lib/notify.js +0 -57
  90. package/lib/plan-command.d.ts +0 -12
  91. package/lib/plan-command.js +0 -38
  92. package/lib/plan.d.ts +0 -16
  93. package/lib/plan.js +0 -126
  94. package/lib/planner.d.ts +0 -61
  95. package/lib/planner.js +0 -144
  96. package/lib/pool.d.ts +0 -7
  97. package/lib/pool.js +0 -25
  98. package/lib/prefix-cache.d.ts +0 -79
  99. package/lib/prefix-cache.js +0 -168
  100. package/lib/project.d.ts +0 -28
  101. package/lib/project.js +0 -66
  102. package/lib/projection.d.ts +0 -22
  103. package/lib/projection.js +0 -112
  104. package/lib/promotion.d.ts +0 -94
  105. package/lib/promotion.js +0 -171
  106. package/lib/recall.d.ts +0 -85
  107. package/lib/recall.js +0 -142
  108. package/lib/record-language.d.ts +0 -85
  109. package/lib/record-language.js +0 -152
  110. package/lib/render.d.ts +0 -13
  111. package/lib/render.js +0 -80
  112. package/lib/review-scheduler.d.ts +0 -38
  113. package/lib/review-scheduler.js +0 -183
  114. package/lib/review.d.ts +0 -60
  115. package/lib/review.js +0 -145
  116. package/lib/rollback.d.ts +0 -9
  117. package/lib/rollback.js +0 -68
  118. package/lib/rubric.d.ts +0 -29
  119. package/lib/rubric.js +0 -119
  120. package/lib/runtime-commands.d.ts +0 -14
  121. package/lib/runtime-commands.js +0 -115
  122. package/lib/runtime.d.ts +0 -24
  123. package/lib/runtime.js +0 -87
  124. package/lib/score.d.ts +0 -64
  125. package/lib/score.js +0 -191
  126. package/lib/search.d.ts +0 -83
  127. package/lib/search.js +0 -136
  128. package/lib/service.d.ts +0 -63
  129. package/lib/service.js +0 -253
  130. package/lib/skill-render.d.ts +0 -23
  131. package/lib/skill-render.js +0 -69
  132. package/lib/skill.d.ts +0 -15
  133. package/lib/skill.js +0 -80
  134. package/lib/skillquality.d.ts +0 -85
  135. package/lib/skillquality.js +0 -320
  136. package/lib/source.d.ts +0 -30
  137. package/lib/source.js +0 -43
  138. package/lib/state.d.ts +0 -26
  139. package/lib/state.js +0 -151
  140. package/lib/store.d.ts +0 -61
  141. package/lib/store.js +0 -164
  142. package/lib/token-usage.d.ts +0 -68
  143. package/lib/token-usage.js +0 -211
  144. package/lib/tool.d.ts +0 -34
  145. package/lib/tool.js +0 -320
  146. package/lib/turn-snapshot.d.ts +0 -78
  147. package/lib/turn-snapshot.js +0 -252
  148. package/lib/types.d.ts +0 -218
  149. package/lib/types.js +0 -133
  150. package/lib/usage.d.ts +0 -36
  151. package/lib/usage.js +0 -98
  152. package/lib/validate.d.ts +0 -28
  153. package/lib/validate.js +0 -181
  154. package/lib/wrapup-candidates.d.ts +0 -127
  155. package/lib/wrapup-candidates.js +0 -161
  156. package/lib/wrapup-command.d.ts +0 -10
  157. package/lib/wrapup-command.js +0 -243
  158. package/lib/wrapup-proposals.d.ts +0 -101
  159. package/lib/wrapup-proposals.js +0 -281
  160. package/lib/wrapup.d.ts +0 -56
  161. package/lib/wrapup.js +0 -105
package/lib/tool.js DELETED
@@ -1,320 +0,0 @@
1
- import { defineTool } from "@deepseek-ai/dsh-tools";
2
- import { MEMORY_TYPE_KEY } from "./types.js";
3
- import { projectKeyOf } from "./project.js";
4
- import { formatHarnessStateForPrompt } from "./render.js";
5
- import { requireGlobalApproval } from "./approval.js";
6
- import { CONFLICT_WARN_SCORE, buildConflictNotice, mostSimilarEntry } from "./promotion.js";
7
- import { entrySourceOf } from "./source.js";
8
- import { formatRecallResult, recallMemories } from "./recall.js";
9
- import { getUsageCount, loadUsage } from "./usage.js";
10
- import { buildEvolveCompleteEvent, emitEvolveComplete } from "./evolve-event.js";
11
- import { DEFAULT_REVIEWS_RETAIN } from "./types.js";
12
- const SCOPES = ["local", "project", "global"];
13
- /**
14
- * Accept the string form (`scope: "project"`) with the legacy boolean tool
15
- * parameter (`global: true`) as fallback. `scope` wins when both are given.
16
- */
17
- export function scopeOf(value, fallback) {
18
- if (value === "global" || value === true) {
19
- return "global";
20
- }
21
- if (value === "project") {
22
- return "project";
23
- }
24
- return fallback;
25
- }
26
- /** The calling agent's session id; tools always run inside an agent scope. */
27
- function sessionIdOf(exec) {
28
- return exec.agent?.id;
29
- }
30
- /**
31
- * Collect the target ids for an evolve_delete call: legacy single `id`
32
- * plus the batch `ids` array, de-duplicated in first-seen order. Throws
33
- * loudly when neither carries an id — a silent no-op delete would look
34
- * like success while deleting nothing.
35
- *
36
- * @throws when both `id` and `ids` are absent or empty.
37
- */
38
- export function collectDeleteIds(id, ids) {
39
- const collected = [];
40
- if (typeof id === "string" && id.length > 0)
41
- collected.push(id);
42
- if (Array.isArray(ids)) {
43
- for (const item of ids) {
44
- if (typeof item === "string" && item.length > 0 && !collected.includes(item)) {
45
- collected.push(item);
46
- }
47
- }
48
- }
49
- if (collected.length === 0) {
50
- throw new Error("evolve_delete requires id or a non-empty ids array");
51
- }
52
- return collected;
53
- }
54
- /**
55
- * The store id a scope reads/writes: session id for local, derived project
56
- * key for project, undefined for global. Throws for project when the
57
- * session cwd is unavailable — fail loud instead of writing local-by-mistake.
58
- */
59
- function storeIdFor(scope, exec) {
60
- if (scope === "local") {
61
- return sessionIdOf(exec);
62
- }
63
- if (scope === "project") {
64
- const key = projectKeyOf(exec.agent);
65
- if (!key) {
66
- throw new Error("project scope needs the session cwd (unavailable for this agent) — use local or global instead");
67
- }
68
- return key;
69
- }
70
- return undefined;
71
- }
72
- /** Human-approval gate shared by global and project writes (both cross-session). */
73
- function needsApproval(scope) {
74
- return scope === "global" || scope === "project";
75
- }
76
- /** Store label used in approval prompts. */
77
- function storeLabel(scope) {
78
- return scope === "project" ? "本项目跨会话 store" : "跨会话全局 store";
79
- }
80
- function textResult(text) {
81
- return { text };
82
- }
83
- export function registerEvolveTools(ctx, engine, opts) {
84
- ctx.tools.register(defineTool({
85
- name: "evolve_list",
86
- description: "List the harness state (prompt notes, memories, skills, subagent specs) for one store.",
87
- parameters: {
88
- scope: {
89
- type: "string",
90
- enum: SCOPES,
91
- description: "'local' (default), 'project', or 'global'.",
92
- },
93
- },
94
- output: {
95
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string", required: true } } },
96
- render: (_args, value) => [{ type: "text", text: value.text ?? "" }],
97
- },
98
- execute: async (args, exec) => {
99
- const scope = scopeOf(args.scope, "local");
100
- const state = engine.load(scope, storeIdFor(scope, exec));
101
- const text = formatHarnessStateForPrompt(state);
102
- // Append injection usage counts (gap B1).
103
- const usage = loadUsage(engine.baseDir);
104
- const usageLines = [];
105
- for (const kind of Object.keys(state.entries)) {
106
- for (const entry of Object.values(state.entries[kind])) {
107
- const count = getUsageCount(usage, kind, entry.id);
108
- if (count > 0) {
109
- usageLines.push(`${kind}:${entry.id} — injected ${count}×`);
110
- }
111
- }
112
- }
113
- if (usageLines.length > 0) {
114
- return textResult(`${text}\n\n# Injection Usage\n${usageLines.join("\n")}`);
115
- }
116
- return textResult(text);
117
- },
118
- }));
119
- ctx.tools.register(defineTool({
120
- name: "evolve_recall",
121
- description: "Read-only targeted recall across the harness stores: filter by query, kind, scope, and memory type; returns full content with version, source, and staleness signals. Prefer this over evolve_list when looking for something specific.",
122
- parameters: {
123
- query: { type: "string", description: "Free-text relevance query. Empty → most-recently-updated first." },
124
- kinds: {
125
- type: "array",
126
- items: { type: "string" },
127
- description: "Entry kinds (default memory only).",
128
- },
129
- scopes: {
130
- type: "array",
131
- items: { type: "string" },
132
- description: "Stores to search (default all three).",
133
- },
134
- memoryTypes: {
135
- type: "array",
136
- items: { type: "string" },
137
- description: "Memory types: user, feedback, project, reference.",
138
- },
139
- limit: { type: "number", description: "Max hits (default 10, max 50)." },
140
- includeArchived: { type: "boolean", description: "Include archived entries (default false)." },
141
- },
142
- output: {
143
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string", required: true } } },
144
- render: (_args, value) => [{ type: "text", text: value.text ?? "" }],
145
- },
146
- execute: async (args, exec) => {
147
- const result = recallMemories(engine, { sessionId: sessionIdOf(exec), projectKey: projectKeyOf(exec.agent) ?? undefined }, {
148
- ...(typeof args.query === "string" ? { query: args.query } : {}),
149
- ...(Array.isArray(args.kinds) ? { kinds: args.kinds } : {}),
150
- ...(Array.isArray(args.scopes) ? { scopes: args.scopes } : {}),
151
- ...(Array.isArray(args.memoryTypes) ? { memoryTypes: args.memoryTypes } : {}),
152
- ...(typeof args.limit === "number" ? { limit: args.limit } : {}),
153
- ...(typeof args.includeArchived === "boolean" ? { includeArchived: args.includeArchived } : {}),
154
- });
155
- return textResult(formatRecallResult(result, typeof args.query === "string" ? args.query : undefined));
156
- },
157
- }));
158
- ctx.tools.register(defineTool({
159
- name: "evolve_add",
160
- description: "Create one harness entry (prompt/memory/skill/subagent). Snapshots, versioning, and audit are automatic.",
161
- parameters: {
162
- kind: { type: "string", enum: ["prompt", "memory", "skill", "subagent"], required: true, description: "Entry kind." },
163
- title: { type: "string", required: true, description: "Stable title." },
164
- content: { type: "string", required: true, description: "Entry body." },
165
- path: { type: "string", description: "Optional grouping path." },
166
- skill_kind: { type: "string", enum: ["executable", "guidance"], description: "Skills: executable (default, needs reference) or guidance (SKILL.md, no reference)." },
167
- reference: { type: "object", additionalProperties: true, description: "Executable skills: {type:'python', import, callable}." },
168
- arguments: { type: "object", additionalProperties: true, description: "Executable skills: accepted input contract." },
169
- memoryType: { type: "string", enum: ["user", "feedback", "project", "reference"], description: "Required for memory (feedback/project need Why + How to apply)." },
170
- scope: { type: "string", enum: SCOPES, description: "'local' (default), 'project', or 'global'." },
171
- global: { type: "boolean", description: "Shorthand for scope='global' (requires human approval)." },
172
- },
173
- output: {
174
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string", required: true } } },
175
- render: (_args, value) => [{ type: "text", text: value.text ?? "" }],
176
- },
177
- execute: async (args, exec) => {
178
- const scope = scopeOf(args.scope ?? args.global, "local");
179
- // Resolve first: a project scope without a session cwd fails
180
- // here, before any human approval question is asked.
181
- const storeId = storeIdFor(scope, exec);
182
- if (needsApproval(scope) && opts.requireGlobalApproval) {
183
- // Informed approval: surface a similarity hit against the
184
- // target store BEFORE the human decides — the engine's
185
- // write-time guard still has the final say.
186
- const targetState = engine.load(scope, storeId);
187
- const hit = mostSimilarEntry(Object.values(targetState.entries[args.kind]), args.title ?? "", args.content ?? "", CONFLICT_WARN_SCORE);
188
- const conflictNote = hit ? ` ⚠️ ${buildConflictNotice(hit)}——建议改用 evolve_update` : "";
189
- await requireGlobalApproval(ctx, exec.agent, exec.signal, `evolve_add ${args.kind} "${args.title}" → ${storeLabel(scope)}${conflictNote}`, opts.recordLanguage);
190
- }
191
- const edit = {
192
- action: "create",
193
- kind: args.kind,
194
- title: args.title,
195
- content: args.content,
196
- };
197
- if (args.path !== undefined)
198
- edit.path = args.path;
199
- if (args.skill_kind !== undefined)
200
- edit.skill_kind = args.skill_kind;
201
- if (args.reference !== undefined)
202
- edit.reference = args.reference;
203
- if (args.arguments !== undefined)
204
- edit.arguments = args.arguments;
205
- if (args.kind === "memory" && args.memoryType !== undefined) {
206
- edit.metadata = { [MEMORY_TYPE_KEY]: args.memoryType };
207
- }
208
- return textResult(applyEditsText(engine, scope, storeId, [edit], exec.agent, sessionIdOf(exec)));
209
- },
210
- }));
211
- ctx.tools.register(defineTool({
212
- name: "evolve_update",
213
- description: "Update one harness entry by id. Pass only the fields that change.",
214
- parameters: {
215
- kind: { type: "string", enum: ["prompt", "memory", "skill", "subagent"], required: true },
216
- id: { type: "string", required: true, description: "Existing entry id." },
217
- title: { type: "string" },
218
- content: { type: "string" },
219
- memoryType: { type: "string", enum: ["user", "feedback", "project", "reference"], description: "Memory: (re)classify the recall type." },
220
- scope: { type: "string", enum: SCOPES, description: "'local' (default), 'project', or 'global'." },
221
- global: { type: "boolean", description: "Shorthand for scope='global' (requires human approval)." },
222
- },
223
- output: {
224
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string", required: true } } },
225
- render: (_args, value) => [{ type: "text", text: value.text ?? "" }],
226
- },
227
- execute: async (args, exec) => {
228
- const scope = scopeOf(args.scope ?? args.global, "local");
229
- const storeId = storeIdFor(scope, exec);
230
- if (needsApproval(scope) && opts.requireGlobalApproval) {
231
- await requireGlobalApproval(ctx, exec.agent, exec.signal, `evolve_update ${args.kind}:${args.id} → ${storeLabel(scope)}`, opts.recordLanguage);
232
- }
233
- const edit = { action: "update", kind: args.kind, id: args.id };
234
- if (args.title !== undefined)
235
- edit.title = args.title;
236
- if (args.content !== undefined)
237
- edit.content = args.content;
238
- if (args.memoryType !== undefined)
239
- edit.metadata = { [MEMORY_TYPE_KEY]: args.memoryType };
240
- return textResult(applyEditsText(engine, scope, storeId, [edit], exec.agent, sessionIdOf(exec)));
241
- },
242
- }));
243
- ctx.tools.register(defineTool({
244
- name: "evolve_delete",
245
- description: "Delete harness entries: `id` for one, `ids` for a batch (one refinement, one approval). Ids are addressed verbatim, scope-prefixed or not.",
246
- parameters: {
247
- kind: { type: "string", enum: ["prompt", "memory", "skill", "subagent"], required: true },
248
- id: { type: "string", description: "One entry id (or use ids for a batch)." },
249
- ids: {
250
- type: "array",
251
- items: { type: "string" },
252
- description: "Entry ids for one batch delete.",
253
- },
254
- scope: { type: "string", enum: SCOPES, description: "'local' (default), 'project', or 'global'." },
255
- global: { type: "boolean", description: "Shorthand for scope='global' (requires human approval)." },
256
- },
257
- output: {
258
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string", required: true } } },
259
- render: (_args, value) => [{ type: "text", text: value.text ?? "" }],
260
- },
261
- execute: async (args, exec) => {
262
- const scope = scopeOf(args.scope ?? args.global, "local");
263
- const storeId = storeIdFor(scope, exec);
264
- const ids = collectDeleteIds(args.id, args.ids);
265
- if (needsApproval(scope) && opts.requireGlobalApproval) {
266
- await requireGlobalApproval(ctx, exec.agent, exec.signal, `evolve_delete ${args.kind}:${ids.length > 1 ? `${ids.length} entries (${ids.join(", ")})` : ids[0]} → ${storeLabel(scope)}`, opts.recordLanguage);
267
- }
268
- const edits = ids.map((id) => ({ action: "delete", kind: args.kind, id }));
269
- return textResult(applyEditsText(engine, scope, storeId, edits, exec.agent, sessionIdOf(exec)));
270
- },
271
- }));
272
- ctx.tools.register(defineTool({
273
- name: "evolve_rollback",
274
- description: "Deterministically revert a previous refinement by its id (from evolve_list history or /evolve).",
275
- parameters: {
276
- refinementId: { type: "string", required: true, description: "Refinement id to roll back." },
277
- scope: { type: "string", enum: SCOPES, description: "'local' (default), 'project', or 'global'." },
278
- global: { type: "boolean", description: "Shorthand for scope='global'." },
279
- },
280
- output: {
281
- schema: { type: "object", additionalProperties: false, properties: { text: { type: "string", required: true } } },
282
- render: (_args, value) => [{ type: "text", text: value.text ?? "" }],
283
- },
284
- execute: async (args, exec) => {
285
- const scope = scopeOf(args.scope ?? args.global, "local");
286
- const result = engine.rollback(scope, storeIdFor(scope, exec), args.refinementId);
287
- return textResult(`Rolled back ${result.rollbackOf ?? result.id}: ${result.appliedEdits.filter((e) => e.applied).length} edit(s) reverted.`);
288
- },
289
- }));
290
- }
291
- function applyEditsText(engine, scope, storeId, edits, agent, eventSessionId) {
292
- const result = engine.apply(scope, storeId, {
293
- summary: "Direct tool edit",
294
- rationale: "Model-invoked single edit via evolve_* tool.",
295
- expectedOutcome: "Entry is created, updated, or deleted as requested.",
296
- edits,
297
- }, agent
298
- ? {
299
- scope,
300
- ...(entrySourceOf(agent, eventSessionId ?? storeId) ? { source: entrySourceOf(agent, eventSessionId ?? storeId) } : {}),
301
- }
302
- : { scope });
303
- const applied = result.appliedEdits.filter((e) => e.applied);
304
- const failed = result.appliedEdits.filter((e) => !e.applied);
305
- // Gap C4: emit structured evolve_complete event for third-party consumers.
306
- // The event keys on the live session (not the project store key).
307
- const eventSession = eventSessionId ?? storeId;
308
- if (applied.length > 0 && eventSession) {
309
- emitEvolveComplete(engine.baseDir, buildEvolveCompleteEvent(result, "manual_tool", eventSession), engine.retention?.reviews ?? DEFAULT_REVIEWS_RETAIN);
310
- }
311
- const lines = [`refinement ${result.id}: ${applied.length} applied, ${failed.length} failed`];
312
- for (const e of applied) {
313
- lines.push(`- ${e.action} ${e.kind}:${e.id} (v${(e.after?.version ?? e.before?.version) ?? "?"})`);
314
- }
315
- for (const e of failed) {
316
- lines.push(`- failed ${e.action} ${e.kind}:${e.id ?? "(computed)"} — ${e.error ?? "unknown error"}`);
317
- }
318
- return lines.join("\n");
319
- }
320
- //# sourceMappingURL=tool.js.map
@@ -1,78 +0,0 @@
1
- /**
2
- * Turn-boundary snapshots for the automatic review gate.
3
- *
4
- * The scheduler must see a bounded, mechanically filtered slice of the
5
- * current session, not a fresh full-history LLM request on every turn. This
6
- * module owns the snapshot boundary, cursor-compatible incremental rows, and
7
- * the cheap eligibility checks that run before any model call.
8
- */
9
- import type { Context } from "@deepseek-ai/cordis";
10
- import type { Agent } from "@deepseek-ai/dsh-agent";
11
- /** Why a scheduler snapshot was captured. */
12
- export type TurnSnapshotReason = "compact" | "session_close" | "goal_blocked";
13
- /** Mechanical reasons a snapshot can be skipped without an LLM call. */
14
- export type SnapshotSkipReason = "no-new-events" | "internal-agent" | "direct-memory-write" | "no-user-prose";
15
- /** One immutable scheduler input. The cursor is a durable event boundary. */
16
- export interface TurnSnapshot {
17
- readonly sessionId: string;
18
- readonly projectKey?: string;
19
- readonly turn: number;
20
- readonly reason: TurnSnapshotReason;
21
- readonly cursor: string;
22
- readonly events: readonly unknown[];
23
- readonly trajectory: string;
24
- readonly userText: string;
25
- readonly sourceSeqs: readonly number[];
26
- readonly maxChars: number;
27
- readonly minUserWords: number;
28
- /** Cursor supplied by the shared scheduler before this snapshot selected events. */
29
- readonly capturedAfterCursor?: string;
30
- readonly eligible: boolean;
31
- readonly skipReason?: SnapshotSkipReason;
32
- }
33
- /** Eligibility result kept separate so tests and future extractors share it. */
34
- export interface SnapshotEligibility {
35
- readonly eligible: boolean;
36
- readonly reason?: SnapshotSkipReason;
37
- readonly userText: string;
38
- }
39
- /** ZCode's minimum user-prose threshold, counted as lexical words. */
40
- export declare const MEMORY_MIN_USER_WORDS = 3;
41
- /**
42
- * Capture the current durable surface and select only rows after `cursor`.
43
- * A seq-bearing session uses `seq:<n>`; older/test surfaces fall back to an
44
- * event-count cursor so the scheduler still has a deterministic boundary.
45
- */
46
- export declare function captureTurnSnapshot(ctx: Context, agent: Agent, opts: {
47
- turn: number;
48
- reason: TurnSnapshotReason;
49
- cursor?: string;
50
- maxChars: number;
51
- minUserWords?: number;
52
- }): Promise<TurnSnapshot>;
53
- /**
54
- * Rebase one captured snapshot onto a phase-specific cursor while preserving
55
- * its outer boundary. The memory phase uses this after a downstream review or
56
- * fate failure: already-processed evidence is removed, but newly captured rows
57
- * remain available without moving the shared scheduler cursor backwards.
58
- */
59
- export declare function sliceTurnSnapshot(snapshot: TurnSnapshot, cursor: string): TurnSnapshot;
60
- /**
61
- * Decide whether the incremental rows deserve a model call. This intentionally
62
- * does not decide whether a fact is valuable; that remains the review model's
63
- * job. It only removes mechanical noise before the scheduler spends tokens.
64
- */
65
- export declare function evaluateSnapshotEligibility(events: readonly unknown[], internalAgent?: boolean, minUserWords?: number): SnapshotEligibility;
66
- /** True when an assistant row records an explicit evolve memory mutation. */
67
- export declare function containsDirectMemoryWrite(events: readonly unknown[]): boolean;
68
- /** Serialize only user/assistant text, retaining the bounded tail. */
69
- export declare function serializeSnapshotEvents(events: readonly unknown[], maxChars: number): string;
70
- /** A DSH subagent header is an internal source when its origin says so. */
71
- export declare function isInternalAgent(agent: Agent | undefined): boolean;
72
- /**
73
- * Durable-boundary ordering shared by the scheduler and the extraction
74
- * phase: returns a positive number when `left` is newer than `right`,
75
- * negative when older, and undefined when the two shapes are incomparable.
76
- */
77
- export declare function compareReviewCursors(left: string, right: string): number | undefined;
78
- //# sourceMappingURL=turn-snapshot.d.ts.map
@@ -1,252 +0,0 @@
1
- import { sessionEventsOf } from "./inject.js";
2
- import { projectKeyOf } from "./project.js";
3
- /** ZCode's minimum user-prose threshold, counted as lexical words. */
4
- export const MEMORY_MIN_USER_WORDS = 3;
5
- const WORD_SEGMENTER = new Intl.Segmenter("und", { granularity: "word" });
6
- function countUserWords(text) {
7
- return [...WORD_SEGMENTER.segment(text)].filter((segment) => segment.isWordLike).length;
8
- }
9
- function containsEligibleUserProse(events, minUserWords) {
10
- return events.some((event) => {
11
- if (!isDirectUserEvent(event))
12
- return false;
13
- const row = event;
14
- return countUserWords(contentText(row.data?.content)) >= minUserWords;
15
- });
16
- }
17
- /**
18
- * Capture the current durable surface and select only rows after `cursor`.
19
- * A seq-bearing session uses `seq:<n>`; older/test surfaces fall back to an
20
- * event-count cursor so the scheduler still has a deterministic boundary.
21
- */
22
- export async function captureTurnSnapshot(ctx, agent, opts) {
23
- const allEvents = await readSurface(ctx, agent);
24
- const cursor = boundaryCursor(allEvents);
25
- const events = eventsAfterCursor(allEvents, opts.cursor);
26
- const minUserWords = opts.minUserWords ?? MEMORY_MIN_USER_WORDS;
27
- const eligibility = evaluateSnapshotEligibility(events, isInternalAgent(agent), minUserWords);
28
- const projectKey = projectKeyOf(agent);
29
- return {
30
- sessionId: agent.id,
31
- ...(projectKey ? { projectKey } : {}),
32
- turn: opts.turn,
33
- reason: opts.reason,
34
- cursor,
35
- events,
36
- trajectory: eligibility.eligible ? serializeSnapshotEvents(events, opts.maxChars) : "",
37
- userText: eligibility.userText,
38
- sourceSeqs: directUserSeqs(events),
39
- maxChars: opts.maxChars,
40
- minUserWords,
41
- ...(opts.cursor === undefined ? {} : { capturedAfterCursor: opts.cursor }),
42
- eligible: eligibility.eligible,
43
- ...(eligibility.reason ? { skipReason: eligibility.reason } : {}),
44
- };
45
- }
46
- /**
47
- * Rebase one captured snapshot onto a phase-specific cursor while preserving
48
- * its outer boundary. The memory phase uses this after a downstream review or
49
- * fate failure: already-processed evidence is removed, but newly captured rows
50
- * remain available without moving the shared scheduler cursor backwards.
51
- */
52
- export function sliceTurnSnapshot(snapshot, cursor) {
53
- const events = sliceSnapshotEvents(snapshot, cursor);
54
- const eligibility = evaluateSnapshotEligibility(events, false, snapshot.minUserWords);
55
- return {
56
- ...snapshot,
57
- events,
58
- trajectory: eligibility.eligible ? serializeSnapshotEvents(events, snapshot.maxChars) : "",
59
- userText: eligibility.userText,
60
- sourceSeqs: directUserSeqs(events),
61
- eligible: eligibility.eligible,
62
- ...(eligibility.reason ? { skipReason: eligibility.reason } : {}),
63
- };
64
- }
65
- /**
66
- * Decide whether the incremental rows deserve a model call. This intentionally
67
- * does not decide whether a fact is valuable; that remains the review model's
68
- * job. It only removes mechanical noise before the scheduler spends tokens.
69
- */
70
- export function evaluateSnapshotEligibility(events, internalAgent = false, minUserWords = MEMORY_MIN_USER_WORDS) {
71
- if (internalAgent)
72
- return { eligible: false, reason: "internal-agent", userText: "" };
73
- if (events.length === 0)
74
- return { eligible: false, reason: "no-new-events", userText: "" };
75
- if (containsDirectMemoryWrite(events))
76
- return { eligible: false, reason: "direct-memory-write", userText: "" };
77
- const userText = directUserText(events);
78
- if (!containsEligibleUserProse(events, minUserWords))
79
- return { eligible: false, reason: "no-user-prose", userText };
80
- return { eligible: true, userText };
81
- }
82
- /** True when an assistant row records an explicit evolve memory mutation. */
83
- export function containsDirectMemoryWrite(events) {
84
- for (const event of events) {
85
- if (!event || typeof event !== "object")
86
- continue;
87
- const row = event;
88
- if (row.type === "tool/call" || row.type === "tool-call") {
89
- if (isMemoryMutationName(readNestedString(row.data, ["name", "tool", "toolName"])))
90
- return true;
91
- }
92
- if (row.type === "assistant/message" && containsMemoryMutationName(row.data))
93
- return true;
94
- }
95
- return false;
96
- }
97
- /** Serialize only user/assistant text, retaining the bounded tail. */
98
- export function serializeSnapshotEvents(events, maxChars) {
99
- const lines = [];
100
- for (const raw of events) {
101
- if (!raw || typeof raw !== "object")
102
- continue;
103
- const event = raw;
104
- if (event.type === "user/message" && !isDirectUserEvent(raw))
105
- continue;
106
- const role = event.type === "user/message" ? "user" : event.type === "assistant/message" ? "assistant" : null;
107
- if (role === null)
108
- continue;
109
- const text = contentText(event.data?.content).trim();
110
- if (text.length > 0)
111
- lines.push(`${role}: ${text}`);
112
- }
113
- const joined = lines.join("\n");
114
- return joined.length <= maxChars ? joined : joined.slice(-maxChars);
115
- }
116
- /** A DSH subagent header is an internal source when its origin says so. */
117
- export function isInternalAgent(agent) {
118
- const header = agent?.session?.header;
119
- return header?.origin === "subagent" || header?.origin === "internal" || header?.isSubagent === true;
120
- }
121
- async function readSurface(ctx, agent) {
122
- const reader = ctx.sessionQuery;
123
- if (reader) {
124
- const snapshot = await reader.readSurface(agent.id);
125
- return Array.isArray(snapshot.events) ? snapshot.events : [];
126
- }
127
- return sessionEventsOf(agent);
128
- }
129
- function boundaryCursor(events) {
130
- const seqs = events.map(eventSeq).filter((seq) => seq !== undefined);
131
- return seqs.length > 0 ? `seq:${Math.max(...seqs)}` : `index:${events.length}`;
132
- }
133
- function sliceSnapshotEvents(snapshot, cursor) {
134
- if (snapshot.cursor.startsWith("index:") && cursor.startsWith("index:") && snapshot.capturedAfterCursor?.startsWith("index:")) {
135
- const start = Number(snapshot.capturedAfterCursor.slice("index:".length));
136
- const phaseBoundary = Number(cursor.slice("index:".length));
137
- if (!Number.isInteger(start) || !Number.isInteger(phaseBoundary))
138
- return snapshot.events;
139
- const relative = phaseBoundary - start;
140
- if (relative <= 0)
141
- return snapshot.events;
142
- return relative >= snapshot.events.length ? [] : snapshot.events.slice(relative);
143
- }
144
- return eventsAfterCursor(snapshot.events, cursor);
145
- }
146
- function eventsAfterCursor(events, cursor) {
147
- if (cursor === undefined)
148
- return events;
149
- if (cursor.startsWith("seq:")) {
150
- const boundary = Number(cursor.slice(4));
151
- if (!Number.isFinite(boundary))
152
- return events;
153
- return events.filter((event) => {
154
- const seq = eventSeq(event);
155
- return seq === undefined || seq > boundary;
156
- });
157
- }
158
- if (cursor.startsWith("index:")) {
159
- const boundary = Number(cursor.slice(6));
160
- return Number.isInteger(boundary) && boundary >= 0 ? events.slice(boundary) : events;
161
- }
162
- return events;
163
- }
164
- function directUserText(events) {
165
- return events
166
- .filter(isDirectUserEvent)
167
- .map((event) => contentText(event.data?.content).trim())
168
- .filter(Boolean)
169
- .join("\n");
170
- }
171
- function directUserSeqs(events) {
172
- return events
173
- .filter(isDirectUserEvent)
174
- .map(eventSeq)
175
- .filter((seq) => seq !== undefined);
176
- }
177
- function isDirectUserEvent(event) {
178
- if (!event || typeof event !== "object")
179
- return false;
180
- const row = event;
181
- if (row.type !== "user/message")
182
- return false;
183
- if (row.data?.visibility === "model-only")
184
- return false;
185
- if (row.data?.source?.synthetic === true)
186
- return false;
187
- if (row.data?.source?.kind !== undefined && row.data.source.kind !== "user")
188
- return false;
189
- return contentText(row.data?.content).trim().length > 0;
190
- }
191
- function contentText(content) {
192
- if (typeof content === "string")
193
- return content;
194
- if (!Array.isArray(content))
195
- return "";
196
- return content
197
- .map((block) => {
198
- if (typeof block === "string")
199
- return block;
200
- if (!block || typeof block !== "object")
201
- return "";
202
- const value = block;
203
- if (value.synthetic === true || value.ignored === true || (value.type !== undefined && value.type !== "text"))
204
- return "";
205
- return typeof value.text === "string" ? value.text : "";
206
- })
207
- .filter(Boolean)
208
- .join(" ");
209
- }
210
- function eventSeq(event) {
211
- if (!event || typeof event !== "object")
212
- return undefined;
213
- const seq = event.seq;
214
- return typeof seq === "number" && Number.isInteger(seq) ? seq : undefined;
215
- }
216
- function containsMemoryMutationName(value) {
217
- if (typeof value === "string")
218
- return isMemoryMutationName(value);
219
- if (Array.isArray(value))
220
- return value.some(containsMemoryMutationName);
221
- if (!value || typeof value !== "object")
222
- return false;
223
- return Object.values(value).some(containsMemoryMutationName);
224
- }
225
- function isMemoryMutationName(value) {
226
- return value === "evolve_add" || value === "evolve_update" || value === "evolve_delete";
227
- }
228
- function readNestedString(value, keys) {
229
- if (!value || typeof value !== "object")
230
- return undefined;
231
- const row = value;
232
- for (const key of keys) {
233
- if (typeof row[key] === "string")
234
- return row[key];
235
- }
236
- return undefined;
237
- }
238
- /**
239
- * Durable-boundary ordering shared by the scheduler and the extraction
240
- * phase: returns a positive number when `left` is newer than `right`,
241
- * negative when older, and undefined when the two shapes are incomparable.
242
- */
243
- export function compareReviewCursors(left, right) {
244
- if (left === right)
245
- return 0;
246
- const leftMatch = /^(seq|index):(\d+)$/.exec(left);
247
- const rightMatch = /^(seq|index):(\d+)$/.exec(right);
248
- if (!leftMatch || !rightMatch || leftMatch[1] !== rightMatch[1])
249
- return undefined;
250
- return Number(leftMatch[2]) - Number(rightMatch[2]);
251
- }
252
- //# sourceMappingURL=turn-snapshot.js.map