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/inject.js DELETED
@@ -1,164 +0,0 @@
1
- import { isArchived } from "./types.js";
2
- import { mergeHarnessStates } from "./state.js";
3
- import { projectKeyOf } from "./project.js";
4
- import { entryLine } from "./render.js";
5
- import { recordInjection } from "./usage.js";
6
- import { recentUserText, rankEntries, } from "./injection-rank.js";
7
- import { formatEntriesDirectoryRanked, rankedDirectoryEntries } from "./injection-directory.js";
8
- import { MAX_INJECTED_ENTRIES_PER_KIND, MAX_INJECTED_CONTENT_LENGTH } from "./injection-caps.js";
9
- import { DEFAULT_DIRECTORY_LINES } from "./injection-directory.js";
10
- /** Compat surface: ranking + directory + caps moved out 2026-10-03. */
11
- export { rankEntries, recencyScore, recentUserText, sessionEventsOf, tokenize } from "./injection-rank.js";
12
- export { RECENCY_HALF_LIFE_MS, MAX_QUERY_MESSAGES, MAX_QUERY_CHARS } from "./injection-rank.js";
13
- export { directoryLine, formatEntriesDirectory, formatEntriesDirectoryCapped, formatEntriesDirectoryRanked, rankedDirectoryEntries, DEFAULT_DIRECTORY_LINES, } from "./injection-directory.js";
14
- export { MAX_INJECTED_ENTRIES_PER_KIND, MAX_INJECTED_CONTENT_LENGTH } from "./injection-caps.js";
15
- /** How many `parentSession` hops a child walks to inherit entries. */
16
- export const MAX_PARENT_CHAIN_DEPTH = 8;
17
- /** True when the state carries at least one entry of any kind. */
18
- export function hasAnyEntries(state) {
19
- return Object.values(state.entries).some((byKind) => Object.keys(byKind).length > 0);
20
- }
21
- /** The additive prompt-notes block (empty when there are no visible prompt entries). */
22
- export function formatPromptEntriesSection(entries, query) {
23
- const visible = entries.filter((entry) => !isArchived(entry));
24
- if (visible.length === 0) {
25
- return "";
26
- }
27
- const lines = [
28
- "# Continual Harness — Prompt Notes",
29
- "Supplemental prompt notes (the base system prompt is immutable). Use evolve_list for the full text of any note.",
30
- ];
31
- for (const entry of rankEntries(visible, query).slice(0, MAX_INJECTED_ENTRIES_PER_KIND)) {
32
- lines.push(entryLine(entry, MAX_INJECTED_CONTENT_LENGTH));
33
- }
34
- const overflow = visible.length - Math.min(visible.length, MAX_INJECTED_ENTRIES_PER_KIND);
35
- if (overflow > 0) {
36
- lines.push(`- +${overflow} more prompt notes (evolve_list)`);
37
- }
38
- return lines.join("\n");
39
- }
40
- /** The reusable delegation-specs block (empty when there are no visible subagent entries). */
41
- export function formatSubagentSpecsSection(entries, query) {
42
- const visible = entries.filter((entry) => !isArchived(entry));
43
- if (visible.length === 0) {
44
- return "";
45
- }
46
- const lines = [
47
- "# Continual Harness — Delegation Specs",
48
- "Reusable subagent specs: when you delegate work that matches a spec, assemble the child prompt from its content. Children inherit these specs through their parent chain.",
49
- ];
50
- for (const entry of rankEntries(visible, query).slice(0, MAX_INJECTED_ENTRIES_PER_KIND)) {
51
- lines.push(entryLine(entry, MAX_INJECTED_CONTENT_LENGTH));
52
- }
53
- const overflow = visible.length - Math.min(visible.length, MAX_INJECTED_ENTRIES_PER_KIND);
54
- if (overflow > 0) {
55
- lines.push(`- +${overflow} more delegation specs (evolve_list)`);
56
- }
57
- return lines.join("\n");
58
- }
59
- /**
60
- * Walk the parent-session chain from `agent` upward and return the nearest
61
- * session whose local store is non-empty, if any. Children inherit their
62
- * ancestor's prompt notes and delegation specs; the chain walk stops at the
63
- * first store that has entries (deep descendants do not re-inject ancestors
64
- * beyond the nearest carrying store).
65
- */
66
- export function nearestLocalStateWithEntries(engine, agent) {
67
- let cursor = agent;
68
- for (let depth = 0; cursor !== undefined && depth < MAX_PARENT_CHAIN_DEPTH; depth += 1) {
69
- const state = engine.load("local", cursor.id);
70
- if (hasAnyEntries(state)) {
71
- return state;
72
- }
73
- cursor = cursor.session?.header?.parentSession
74
- ? { id: cursor.session.header.parentSession }
75
- : undefined;
76
- }
77
- return undefined;
78
- }
79
- /**
80
- * The single three-store merge every injection path reads from: the global
81
- * store merged with this project's store (when the session cwd resolves one)
82
- * and the nearest carrying local store (precedence global < project < local —
83
- * see {@link mergeHarnessStates}). One home, so the memory section and the
84
- * entry directory can never disagree about what the model can see.
85
- */
86
- export function mergedInjectionState(engine, agent, opts) {
87
- const globalState = engine.load("global", undefined);
88
- const projectKey = opts?.projectKey ?? projectKeyOf(agent);
89
- const projectState = projectKey ? engine.load("project", projectKey) : undefined;
90
- const localState = nearestLocalStateWithEntries(engine, agent);
91
- return mergeHarnessStates(globalState, localState, projectState ? { projectState } : undefined);
92
- }
93
- /**
94
- * Compose the full injected block for one assembling agent: global entries
95
- * merged with this project's store (when the session cwd resolves one) and
96
- * the nearest carrying local store (precedence global < project < local).
97
- * The optional `query` — when absent, derived from the agent's most recent
98
- * direct user messages — ranks which entries fill the per-kind cap
99
- * (relevance first, then recency; see {@link rankEntries}). Returns "" when
100
- * nothing is injectable — the prompt renderer then drops the section, so an
101
- * empty store adds zero tokens to every assembly.
102
- *
103
- * `opts.directoryLines` caps the entry-directory index (2026-08-22 throttle);
104
- * `opts.projectKey` pins the project layer explicitly (tests, tools) —
105
- * otherwise it is derived from the agent's session cwd, best-effort.
106
- * `opts.includeMemoryDirectory` (default true) drops memory lines from the
107
- * directory when the memory section already carries them (evolve v2) — one
108
- * fact, one home, so the same memories are never paid for twice.
109
- * Usage recording covers ALL kinds — memories and skills appear as directory
110
- * lines, prompts/subagents as content — and is deduped per session so the
111
- * counts read "how many sessions saw this", not "how many prompt builds".
112
- */
113
- export function entriesSectionText(engine, agent, query, opts) {
114
- if (!agent) {
115
- return "";
116
- }
117
- const merged = mergedInjectionState(engine, agent, opts);
118
- const promptEntries = Object.values(merged.entries.prompt);
119
- const subagentEntries = Object.values(merged.entries.subagent);
120
- const relevanceQuery = (query ?? recentUserText(agent)).trim();
121
- // Build injected text and collect which entries were included (gap B1).
122
- const promptText = formatPromptEntriesSection(promptEntries, relevanceQuery);
123
- const subagentText = formatSubagentSpecsSection(subagentEntries, relevanceQuery);
124
- const injectedKeys = new Set();
125
- // Collect keys from the visible (ranked, capped) entries that actually appear.
126
- const visiblePrompt = promptEntries.filter((e) => !isArchived(e));
127
- const visibleSubagent = subagentEntries.filter((e) => !isArchived(e));
128
- for (const entry of rankEntries(visiblePrompt, relevanceQuery).slice(0, MAX_INJECTED_ENTRIES_PER_KIND)) {
129
- injectedKeys.add(`prompt:${entry.id}`);
130
- }
131
- for (const entry of rankEntries(visibleSubagent, relevanceQuery).slice(0, MAX_INJECTED_ENTRIES_PER_KIND)) {
132
- injectedKeys.add(`subagent:${entry.id}`);
133
- }
134
- // Gap B3: lightweight directory of ALL entries (id+title, one line each).
135
- // Zero-cost index so the model knows what exists and can ask for full text.
136
- const directoryLines = opts?.directoryLines ?? DEFAULT_DIRECTORY_LINES;
137
- const promptKind = Object.values(merged.entries.prompt);
138
- const subagentKind = Object.values(merged.entries.subagent);
139
- // v2: memories live in their own content-carrying section, so the directory
140
- // stops listing them (passing [] keeps the positional contract intact).
141
- const memoryKind = (opts?.includeMemoryDirectory ?? true) ? Object.values(merged.entries.memory) : [];
142
- const skillKind = Object.values(merged.entries.skill);
143
- const directoryText = formatEntriesDirectoryRanked(directoryLines, relevanceQuery, promptKind, subagentKind, memoryKind, skillKind);
144
- // Directory-visible keys count too: a memory's injection IS its directory
145
- // line. Set semantics keep content-injected entries single-counted. Keys
146
- // come from the single display-ordered source (type hooks included), not
147
- // substring matching on the rendered text.
148
- for (const entry of rankedDirectoryEntries(directoryLines, relevanceQuery, promptKind, subagentKind, memoryKind, skillKind)) {
149
- injectedKeys.add(`${entry.kind}:${entry.id}`);
150
- }
151
- // Record usage durably (best-effort: failure never blocks injection).
152
- // Deduped per session — see recordInjection.
153
- if (injectedKeys.size > 0) {
154
- try {
155
- recordInjection(engine.baseDir, [...injectedKeys], agent.id);
156
- }
157
- catch {
158
- // Usage recording is diagnostic; never interrupt the injection path.
159
- }
160
- }
161
- const parts = [promptText, subagentText, directoryText].filter((part) => part.length > 0);
162
- return parts.join("\n\n");
163
- }
164
- //# sourceMappingURL=inject.js.map
@@ -1,4 +0,0 @@
1
- /** Shared injection budgets (mirrors render.ts caps). */
2
- export declare const MAX_INJECTED_ENTRIES_PER_KIND = 6;
3
- export declare const MAX_INJECTED_CONTENT_LENGTH = 180;
4
- //# sourceMappingURL=injection-caps.d.ts.map
@@ -1,4 +0,0 @@
1
- /** Shared injection budgets (mirrors render.ts caps). */
2
- export const MAX_INJECTED_ENTRIES_PER_KIND = 6;
3
- export const MAX_INJECTED_CONTENT_LENGTH = 180;
4
- //# sourceMappingURL=injection-caps.js.map
@@ -1,29 +0,0 @@
1
- /**
2
- * The entry directory: a lightweight one-line-per-entry index of ALL
3
- * non-archived entries across kinds, appended after the curated top-N
4
- * injection sections so the model has a zero-cost overview of what exists
5
- * and can ask for full text via `evolve_list`.
6
- */
7
- import type { HarnessEntry } from "./types.js";
8
- /** Directory lines injected per build before folding into a counter. */
9
- export declare const DEFAULT_DIRECTORY_LINES = 15;
10
- /** One directory index line: memory lines carry their recall-type hook. */
11
- export declare function directoryLine(entry: HarnessEntry): string;
12
- export declare function formatEntriesDirectory(...kindEntries: readonly HarnessEntry[][]): string;
13
- /** {@link formatEntriesDirectory} with an explicit cap (configurable). */
14
- export declare function formatEntriesDirectoryCapped(maxLines: number, ...kindEntries: readonly HarnessEntry[][]): string;
15
- /**
16
- * {@link formatEntriesDirectoryCapped} with a relevance query: entries are
17
- * ordered by {@link rankEntries} (relevance first, then recency) so the cap
18
- * folds the least relevant entries — never the dictionary tail. An empty
19
- * query degrades to the valence/recency/stable order, still deterministic.
20
- */
21
- export declare function formatEntriesDirectoryRanked(maxLines: number, query: string | undefined, ...kindEntries: readonly HarnessEntry[][]): string;
22
- /**
23
- * The directory entries actually shown under the cap, in display order —
24
- * the single source for both the rendered text and usage accounting (so a
25
- * memory's injection IS its directory line even with the `:type` hook).
26
- * Returns [] when the directory is suppressed or empty.
27
- */
28
- export declare function rankedDirectoryEntries(maxLines: number, query: string | undefined, ...kindEntries: readonly HarnessEntry[][]): HarnessEntry[];
29
- //# sourceMappingURL=injection-directory.d.ts.map
@@ -1,78 +0,0 @@
1
- import { MEMORY_TYPE_KEY, isArchived, isMemoryType } from "./types.js";
2
- import { rankEntries } from "./injection-rank.js";
3
- import { MAX_INJECTED_ENTRIES_PER_KIND } from "./injection-caps.js";
4
- /** Directory lines injected per build before folding into a counter. */
5
- export const DEFAULT_DIRECTORY_LINES = 15;
6
- /** One directory index line: memory lines carry their recall-type hook. */
7
- export function directoryLine(entry) {
8
- if (entry.kind === "memory") {
9
- const type = entry.metadata[MEMORY_TYPE_KEY];
10
- const hook = isMemoryType(type) ? `:${type}` : "";
11
- return `- [memory${hook}:${entry.id}] ${entry.title}`;
12
- }
13
- return `- [${entry.kind}:${entry.id}] ${entry.title}`;
14
- }
15
- /** Positional contract for the directory variadics: the first two arrays
16
- * must be the content-section kinds (prompt, subagent) — the redundancy
17
- * check counts exactly those as already visible. Callers pass
18
- * (prompt, subagent, memory, skill); any other order miscounts. */
19
- const CONTENT_SECTION_KINDS = 2;
20
- export function formatEntriesDirectory(...kindEntries) {
21
- return formatEntriesDirectoryCapped(DEFAULT_DIRECTORY_LINES, ...kindEntries);
22
- }
23
- /** {@link formatEntriesDirectory} with an explicit cap (configurable). */
24
- export function formatEntriesDirectoryCapped(maxLines, ...kindEntries) {
25
- return formatEntriesDirectoryRanked(maxLines, undefined, ...kindEntries);
26
- }
27
- /**
28
- * {@link formatEntriesDirectoryCapped} with a relevance query: entries are
29
- * ordered by {@link rankEntries} (relevance first, then recency) so the cap
30
- * folds the least relevant entries — never the dictionary tail. An empty
31
- * query degrades to the valence/recency/stable order, still deterministic.
32
- */
33
- export function formatEntriesDirectoryRanked(maxLines, query, ...kindEntries) {
34
- const total = kindEntries.flat().filter((e) => !isArchived(e)).length;
35
- if (total === 0) {
36
- return "";
37
- }
38
- // Suppressed (every entry already content-visible) renders as "" — the
39
- // prompt renderer then drops the section entirely.
40
- const shown = rankedDirectoryEntries(maxLines, query, ...kindEntries);
41
- if (shown.length === 0) {
42
- return "";
43
- }
44
- const lines = ["# Continual Harness — Entry Directory", "All entries (use evolve_list for full text of any entry):"];
45
- for (const entry of shown) {
46
- lines.push(directoryLine(entry));
47
- }
48
- const hidden = total - shown.length;
49
- if (hidden > 0) {
50
- lines.push(`- …and ${hidden} more entries (evolve_list for the full index)`);
51
- }
52
- return lines.join("\n");
53
- }
54
- /**
55
- * The directory entries actually shown under the cap, in display order —
56
- * the single source for both the rendered text and usage accounting (so a
57
- * memory's injection IS its directory line even with the `:type` hook).
58
- * Returns [] when the directory is suppressed or empty.
59
- */
60
- export function rankedDirectoryEntries(maxLines, query, ...kindEntries) {
61
- const allEntries = kindEntries.flat().filter((e) => !isArchived(e));
62
- if (allEntries.length === 0) {
63
- return [];
64
- }
65
- // Skip the directory only when EVERY entry is already content-visible.
66
- // Only the first two arrays (prompt, subagent) have curated sections —
67
- // memories and skills have NO content injection, so they are invisible
68
- // unless the directory lists them (pre-2026-08-22 the redundancy check
69
- // wrongly counted them as "already shown", hiding small stores entirely).
70
- const contentVisible = kindEntries
71
- .slice(0, CONTENT_SECTION_KINDS)
72
- .reduce((sum, entries) => sum + Math.min(entries.filter((e) => !isArchived(e)).length, MAX_INJECTED_ENTRIES_PER_KIND), 0);
73
- if (allEntries.length <= contentVisible) {
74
- return [];
75
- }
76
- return rankEntries(allEntries, query).slice(0, Math.max(maxLines, 1));
77
- }
78
- //# sourceMappingURL=injection-directory.js.map
@@ -1,85 +0,0 @@
1
- /**
2
- * Injection ranking core: deterministic entry ordering (relevance →
3
- * negative valence → recency → stable order) and the relevance query
4
- * composed from the assembling agent's most recent direct user messages.
5
- * Pure logic — no store access, no host API.
6
- */
7
- import type { HarnessEntry } from "./types.js";
8
- import { tokenize } from "./search.js";
9
- /** Recency half-life for the injection ranking: an entry this old scores 0. */
10
- export declare const RECENCY_HALF_LIFE_MS: number;
11
- /** At most this many recent user messages feed the relevance query. */
12
- export declare const MAX_QUERY_MESSAGES = 3;
13
- /** Query text handed to the relevance scorer is capped at this many chars. */
14
- export declare const MAX_QUERY_CHARS = 400;
15
- /** A user-message event's durable shape, loosened for duck typing. */
16
- export interface UserMessageEventLike {
17
- type?: string;
18
- data?: {
19
- content?: unknown;
20
- source?: {
21
- kind?: string;
22
- };
23
- };
24
- }
25
- /** The minimal agent shape the section provider needs (duck-typed). */
26
- export interface AgentLike {
27
- id: string;
28
- session?: {
29
- header?: {
30
- parentSession?: string;
31
- };
32
- /**
33
- * Live append-only session log as the public `session.events` getter.
34
- * Typed loosely (`unknown[]`) so the real `SessionEvent[]` union from
35
- * dsh-session is assignable; rows are narrowed to
36
- * {@link UserMessageEventLike} at read time.
37
- */
38
- events?: readonly unknown[];
39
- /**
40
- * Full-log snapshot reader that answers the same rows in the same order
41
- * where the `events` getter is absent. Called with no arguments for the
42
- * whole log, which is the read {@link sessionEventsOf} needs.
43
- */
44
- snapshotEvents?: () => readonly unknown[];
45
- };
46
- }
47
- /**
48
- * The session's event log through whichever reader the running harness
49
- * generation exposes. Returns [] when neither is present, so every caller
50
- * degrades to its empty-log path rather than throwing.
51
- */
52
- export declare function sessionEventsOf(agent: AgentLike | undefined): readonly unknown[];
53
- /**
54
- * Normalized recency in [0, 1]: 1 when the entry was just updated, decaying
55
- * linearly to 0 after {@link RECENCY_HALF_LIFE_MS}. Unparseable timestamps
56
- * score 0 (never preferred over a timestamped entry).
57
- */
58
- export declare function recencyScore(entry: HarnessEntry, now: number): number;
59
- /**
60
- * Rank entries for injection, best first. With no query the ranking is
61
- * negative-valence first (contradicted entries last), then pure recency
62
- * (newest first). With a query, entries are scored once against a per-call
63
- * BM25 index (CJK bigrams; field-weighted title ×2 — see search.ts): any
64
- * entry with a positive score (≥1 matched token) outranks every hit-less
65
- * entry (score exactly 0), scores decide the order among relevant entries,
66
- * the negative-valence counter breaks remaining ties (contradicted entries
67
- * sink), recency breaks remaining ties, and the stable dictionary order is
68
- * the final tiebreak, so the result is deterministic. The input is never
69
- * mutated.
70
- */
71
- export declare function rankEntries(entries: readonly HarnessEntry[], query?: string, now?: number): HarnessEntry[];
72
- /**
73
- * Compose the relevance query from the assembling agent's most recent direct
74
- * user messages (event rows whose `type` is `user/message` and whose source
75
- * is a human `user`, so injected plugin context and tool results never leak
76
- * into the query). Returns "" when nothing qualifies — the ranking then
77
- * falls back to pure recency.
78
- */
79
- export declare function recentUserText(agent: AgentLike | undefined, opts?: {
80
- maxMessages?: number;
81
- maxChars?: number;
82
- }): string;
83
- /** CJK-bigram tokenizer re-exported for ranking consumers (see search.ts). */
84
- export { tokenize };
85
- //# sourceMappingURL=injection-rank.d.ts.map
@@ -1,161 +0,0 @@
1
- import { VALENCE_NEGATIVE_KEY } from "./types.js";
2
- import { buildRelevanceIndex, relevanceScore, tokenize } from "./search.js";
3
- /** Recency half-life for the injection ranking: an entry this old scores 0. */
4
- export const RECENCY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1000;
5
- /** At most this many recent user messages feed the relevance query. */
6
- export const MAX_QUERY_MESSAGES = 3;
7
- /** Query text handed to the relevance scorer is capped at this many chars. */
8
- export const MAX_QUERY_CHARS = 400;
9
- /**
10
- * The session's event log through whichever reader the running harness
11
- * generation exposes. Returns [] when neither is present, so every caller
12
- * degrades to its empty-log path rather than throwing.
13
- */
14
- export function sessionEventsOf(agent) {
15
- const session = agent?.session;
16
- if (session === undefined) {
17
- return [];
18
- }
19
- if (Array.isArray(session.events)) {
20
- return session.events;
21
- }
22
- const snapshot = session.snapshotEvents?.();
23
- return Array.isArray(snapshot) ? snapshot : [];
24
- }
25
- /** Stable dictionary-order tiebreak used when two entries score equally. */
26
- function stableCompare(a, b) {
27
- return [a.path, a.title, a.id].join("\0").localeCompare([b.path, b.title, b.id].join("\0"));
28
- }
29
- /**
30
- * Normalized recency in [0, 1]: 1 when the entry was just updated, decaying
31
- * linearly to 0 after {@link RECENCY_HALF_LIFE_MS}. Unparseable timestamps
32
- * score 0 (never preferred over a timestamped entry).
33
- */
34
- export function recencyScore(entry, now) {
35
- const updatedAt = Date.parse(entry.updated_at);
36
- if (Number.isNaN(updatedAt)) {
37
- return 0;
38
- }
39
- const age = now - updatedAt;
40
- if (age <= 0) {
41
- return 1;
42
- }
43
- return Math.max(0, 1 - age / RECENCY_HALF_LIFE_MS);
44
- }
45
- /**
46
- * Negative-valence counter (P1 效价反馈): entries contradicted by later
47
- * assessments sink below clean ones at equal relevance/recency — the
48
- * behavioral analog of a confidence penalty (pi-continuous-learning's
49
- * contradicted −0.15), without inventing a new score axis.
50
- */
51
- function negativeValence(entry) {
52
- const value = entry.metadata[VALENCE_NEGATIVE_KEY];
53
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
54
- }
55
- /**
56
- * Rank entries for injection, best first. With no query the ranking is
57
- * negative-valence first (contradicted entries last), then pure recency
58
- * (newest first). With a query, entries are scored once against a per-call
59
- * BM25 index (CJK bigrams; field-weighted title ×2 — see search.ts): any
60
- * entry with a positive score (≥1 matched token) outranks every hit-less
61
- * entry (score exactly 0), scores decide the order among relevant entries,
62
- * the negative-valence counter breaks remaining ties (contradicted entries
63
- * sink), recency breaks remaining ties, and the stable dictionary order is
64
- * the final tiebreak, so the result is deterministic. The input is never
65
- * mutated.
66
- */
67
- export function rankEntries(entries, query, now = Date.now()) {
68
- const q = (query ?? "").trim();
69
- if (q.length === 0) {
70
- return [...entries].sort((a, b) => {
71
- const valenceDelta = negativeValence(a) - negativeValence(b);
72
- if (valenceDelta !== 0) {
73
- return valenceDelta;
74
- }
75
- const recencyDelta = recencyScore(b, now) - recencyScore(a, now);
76
- if (recencyDelta !== 0) {
77
- return recencyDelta;
78
- }
79
- return stableCompare(a, b);
80
- });
81
- }
82
- // Precompute scores once: the old comparator re-tokenized both sides on
83
- // every comparison (O(n log n) tokenizations); one index + one score per
84
- // entry turns the pass into table lookups.
85
- const index = buildRelevanceIndex(entries);
86
- const scores = new Map(entries.map((entry) => [entry, relevanceScore(index, entry, q)]));
87
- return [...entries].sort((a, b) => {
88
- const relevanceDelta = (scores.get(b) ?? 0) - (scores.get(a) ?? 0);
89
- if (relevanceDelta !== 0) {
90
- return relevanceDelta;
91
- }
92
- const valenceDelta = negativeValence(a) - negativeValence(b);
93
- if (valenceDelta !== 0) {
94
- return valenceDelta;
95
- }
96
- const recencyDelta = recencyScore(b, now) - recencyScore(a, now);
97
- if (recencyDelta !== 0) {
98
- return recencyDelta;
99
- }
100
- return stableCompare(a, b);
101
- });
102
- }
103
- /**
104
- * Extract the text of a message's content blocks without depending on the
105
- * dsh-llm ContentBlock type: string blocks pass through, object blocks
106
- * contribute their `text` field when present.
107
- */
108
- function extractBlockText(content) {
109
- if (typeof content === "string") {
110
- return content;
111
- }
112
- if (!Array.isArray(content)) {
113
- return "";
114
- }
115
- return content
116
- .map((block) => {
117
- if (typeof block === "string") {
118
- return block;
119
- }
120
- if (block !== null && typeof block === "object" && "text" in block && typeof block.text === "string") {
121
- return block.text;
122
- }
123
- return "";
124
- })
125
- .filter((text) => text.length > 0)
126
- .join(" ");
127
- }
128
- /**
129
- * Compose the relevance query from the assembling agent's most recent direct
130
- * user messages (event rows whose `type` is `user/message` and whose source
131
- * is a human `user`, so injected plugin context and tool results never leak
132
- * into the query). Returns "" when nothing qualifies — the ranking then
133
- * falls back to pure recency.
134
- */
135
- export function recentUserText(agent, opts) {
136
- const events = sessionEventsOf(agent);
137
- if (events.length === 0) {
138
- return "";
139
- }
140
- const maxMessages = opts?.maxMessages ?? MAX_QUERY_MESSAGES;
141
- const maxChars = opts?.maxChars ?? MAX_QUERY_CHARS;
142
- const parts = [];
143
- for (let i = events.length - 1; i >= 0 && parts.length < maxMessages; i -= 1) {
144
- const event = events[i];
145
- if (event?.type !== "user/message") {
146
- continue;
147
- }
148
- const source = event.data?.source;
149
- if (source && source.kind !== "user") {
150
- continue;
151
- }
152
- const text = extractBlockText(event.data?.content).trim();
153
- if (text.length > 0) {
154
- parts.unshift(text);
155
- }
156
- }
157
- return parts.join(" ").slice(0, maxChars);
158
- }
159
- /** CJK-bigram tokenizer re-exported for ranking consumers (see search.ts). */
160
- export { tokenize };
161
- //# sourceMappingURL=injection-rank.js.map
package/lib/listener.d.ts DELETED
@@ -1,45 +0,0 @@
1
- /**
2
- * The moment-driven extraction listener: registers the host event wiring and
3
- * the per-session serial scheduler. The extraction logic itself lives in
4
- * `extraction-phase.ts`; this module owns only WHEN it runs.
5
- *
6
- * Moments (refactor 2026-10-03, B verdict):
7
- * - `session/event` (compaction/start) — before trajectory data is summarized away.
8
- * - goal blocked streak — `goalBlockedWrapupTurns` consecutive idle probes
9
- * observing the session goal in phase "blocked" trigger one extraction so
10
- * the stuck encounter is distilled before the session moves on. The probe
11
- * is a mechanical in-memory check; it costs no LLM call until the moment fires.
12
- * - session close — the bounded drain (`sessionCloseDrainMs`) lets an
13
- * in-flight extraction settle before the owner signal aborts.
14
- * - manual `/evolve wrapup` — its own consultation flow (`wrapup.ts`).
15
- *
16
- * Per-turn snapshot extraction was removed: it burned 90% of the plugin's
17
- * direct LLM calls for a fifth of the sediment (audit 2026-10-03). A
18
- * successful turn only advances mechanical counters.
19
- *
20
- * All auxiliary work is fire-and-forget with error containment: an automatic
21
- * failure never disturbs the agent loop, and every decision lands in the
22
- * shared `reviews.jsonl` audit trail (see `audit-log.ts`).
23
- */
24
- import type { Context } from "@deepseek-ai/cordis";
25
- import type { EvolutionEngine } from "./service.js";
26
- import { type ReviewScheduler } from "./review-scheduler.js";
27
- import { type TurnSnapshot } from "./turn-snapshot.js";
28
- import { type AutoReviewConfig } from "./extraction-phase.js";
29
- /** Default session-close drain budget: an in-flight extraction gets 15s to settle. */
30
- export declare const SESSION_CLOSE_DRAIN_MS_DEFAULT = 15000;
31
- /**
32
- * Resolve the session-close drain budget: absent/invalid → default, 0 →
33
- * immediate abort (legacy). Negative or non-finite values fail back to the
34
- * default rather than silently disabling the drain.
35
- */
36
- export declare function resolveSessionCloseDrainMs(raw?: number): number;
37
- /**
38
- * Bounded session-close drain for one scheduler: wait for in-flight work up
39
- * to drainMs, then abort the owner signal and drop pending captures. Never
40
- * rejects — disposal must not hang the host on a stuck model call, and the
41
- * abort below is idempotent when the drain already settled.
42
- */
43
- export declare function drainSchedulerOnDispose(scheduler: Pick<ReviewScheduler<TurnSnapshot>, "drain" | "shutdown">, drainMs: number): Promise<void>;
44
- export declare function registerAutoReview(ctx: Context, engine: EvolutionEngine, config: AutoReviewConfig): void;
45
- //# sourceMappingURL=listener.d.ts.map