@pellux/goodvibes-agent 1.1.7 → 1.3.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.
Files changed (274) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +46 -30
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
  5. package/dist/package/main.js +138903 -91477
  6. package/docs/README.md +14 -7
  7. package/docs/channels-remote-and-api.md +8 -5
  8. package/docs/connected-host.md +14 -12
  9. package/docs/getting-started.md +58 -30
  10. package/docs/knowledge-artifacts-and-multimodal.md +7 -5
  11. package/docs/project-planning.md +2 -2
  12. package/docs/providers-and-routing.md +11 -3
  13. package/docs/tools-and-commands.md +116 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +142 -5
  17. package/release/release-readiness.json +124 -94
  18. package/src/agent/assistant-cockpit.ts +247 -0
  19. package/src/agent/autonomy-schedule-format.ts +96 -0
  20. package/src/agent/autonomy-schedule.ts +514 -0
  21. package/src/agent/channel-delivery-receipts.ts +292 -0
  22. package/src/agent/competitive-feature-inventory.ts +294 -0
  23. package/src/agent/document-registry-types.ts +106 -0
  24. package/src/agent/document-registry.ts +734 -0
  25. package/src/agent/harness-control.ts +2 -2
  26. package/src/agent/memory-prompt.ts +2 -2
  27. package/src/agent/operator-actions.ts +50 -6
  28. package/src/agent/persona-registry.ts +8 -0
  29. package/src/agent/project-context-files.ts +273 -0
  30. package/src/agent/prompt-context-receipts.ts +502 -0
  31. package/src/agent/reminder-schedule-format.ts +16 -2
  32. package/src/agent/research-run-registry.ts +582 -0
  33. package/src/agent/research-source-registry.ts +441 -0
  34. package/src/agent/routine-registry.ts +19 -3
  35. package/src/agent/routine-schedule-format.ts +27 -2
  36. package/src/agent/runtime-profile-starters.ts +7 -0
  37. package/src/agent/runtime-profile.ts +106 -4
  38. package/src/agent/schedule-edit-format.ts +129 -0
  39. package/src/agent/schedule-edit.ts +496 -0
  40. package/src/agent/schedule-next-routes.ts +42 -0
  41. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  42. package/src/agent/setup-wizard-checkpoint.ts +140 -0
  43. package/src/agent/setup-wizard.ts +683 -0
  44. package/src/agent/skill-registry-types.ts +102 -0
  45. package/src/agent/skill-registry.ts +81 -107
  46. package/src/agent/vibe-confirmation-routes.ts +62 -0
  47. package/src/agent/vibe-file.ts +285 -0
  48. package/src/cli/agent-knowledge-command.ts +45 -2
  49. package/src/cli/help.ts +3 -0
  50. package/src/cli/profiles-command.ts +25 -11
  51. package/src/config/agent-settings-policy.ts +1 -18
  52. package/src/input/agent-workspace-activation.ts +53 -0
  53. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  54. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  55. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  56. package/src/input/agent-workspace-categories.ts +153 -155
  57. package/src/input/agent-workspace-channel-triage.ts +481 -0
  58. package/src/input/agent-workspace-channels.ts +263 -0
  59. package/src/input/agent-workspace-command-editor.ts +152 -0
  60. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  61. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  62. package/src/input/agent-workspace-document-editor.ts +502 -0
  63. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  64. package/src/input/agent-workspace-host-category.ts +1 -1
  65. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  66. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  67. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  68. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  69. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  70. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  71. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  72. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  73. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  74. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  75. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  76. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  77. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  78. package/src/input/agent-workspace-settings.ts +313 -30
  79. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  80. package/src/input/agent-workspace-setup-snapshot.ts +217 -0
  81. package/src/input/agent-workspace-setup.ts +61 -1
  82. package/src/input/agent-workspace-snapshot.ts +184 -149
  83. package/src/input/agent-workspace-types.ts +287 -2
  84. package/src/input/agent-workspace.ts +23 -1
  85. package/src/input/command-registry.ts +9 -2
  86. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  87. package/src/input/commands/channels-runtime.ts +102 -114
  88. package/src/input/commands/delegation-runtime.ts +87 -24
  89. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  90. package/src/input/commands/knowledge.ts +28 -14
  91. package/src/input/commands/operator-actions-runtime.ts +6 -3
  92. package/src/input/commands/schedule-runtime.ts +71 -14
  93. package/src/input/commands/session-content.ts +2 -2
  94. package/src/input/commands/shell-core.ts +2 -0
  95. package/src/input/commands/vibe-runtime.ts +140 -0
  96. package/src/input/commands.ts +2 -0
  97. package/src/input/connected-host-routes.ts +123 -0
  98. package/src/input/settings-modal-types.ts +14 -0
  99. package/src/input/setup-wizard-live-receipts.ts +76 -0
  100. package/src/main.ts +15 -15
  101. package/src/panels/qr-panel.ts +2 -2
  102. package/src/renderer/agent-workspace-context-lines.ts +701 -0
  103. package/src/renderer/agent-workspace.ts +68 -421
  104. package/src/renderer/settings-modal-helpers.ts +12 -0
  105. package/src/renderer/settings-modal.ts +12 -0
  106. package/src/runtime/bootstrap-command-context.ts +11 -1
  107. package/src/runtime/bootstrap-command-parts.ts +14 -3
  108. package/src/runtime/bootstrap-core.ts +39 -1
  109. package/src/runtime/bootstrap-shell.ts +6 -0
  110. package/src/runtime/bootstrap.ts +113 -30
  111. package/src/runtime/connected-host-auth.ts +3 -2
  112. package/src/runtime/execution-ledger.ts +231 -0
  113. package/src/runtime/services.ts +88 -10
  114. package/src/runtime/tool-permission-safety.ts +199 -5
  115. package/src/shell/session-continuity-hints.ts +39 -0
  116. package/src/tools/agent-artifacts-tool.ts +606 -0
  117. package/src/tools/agent-audit-tool.ts +155 -0
  118. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  119. package/src/tools/agent-autonomy-tool.ts +140 -0
  120. package/src/tools/agent-channel-send-tool.ts +24 -2
  121. package/src/tools/agent-channels-tool.ts +140 -0
  122. package/src/tools/agent-computer-tool.ts +168 -0
  123. package/src/tools/agent-context-policy.ts +137 -7
  124. package/src/tools/agent-context-tool.ts +143 -0
  125. package/src/tools/agent-delegation-tool.ts +128 -0
  126. package/src/tools/agent-device-tool.ts +240 -0
  127. package/src/tools/agent-documents-tool.ts +684 -0
  128. package/src/tools/agent-execution-tool.ts +230 -0
  129. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  130. package/src/tools/agent-harness-agent-orchestration.ts +791 -0
  131. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  132. package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
  133. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  134. package/src/tools/agent-harness-autonomy-queue.ts +571 -0
  135. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  136. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  137. package/src/tools/agent-harness-background-processes.ts +798 -0
  138. package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
  139. package/src/tools/agent-harness-browser-control.ts +346 -0
  140. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  141. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  142. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  143. package/src/tools/agent-harness-command-catalog.ts +4 -2
  144. package/src/tools/agent-harness-command-runner.ts +42 -0
  145. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  146. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  147. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  148. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  149. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  150. package/src/tools/agent-harness-document-ops.ts +671 -0
  151. package/src/tools/agent-harness-execution-history.ts +489 -0
  152. package/src/tools/agent-harness-execution-posture.ts +385 -0
  153. package/src/tools/agent-harness-file-recovery.ts +135 -0
  154. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  155. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  156. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  157. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  158. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  159. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  160. package/src/tools/agent-harness-learning-curator.ts +417 -0
  161. package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
  162. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  163. package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
  164. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  165. package/src/tools/agent-harness-local-model-url.ts +78 -0
  166. package/src/tools/agent-harness-media-posture.ts +223 -2
  167. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  168. package/src/tools/agent-harness-memory-posture.ts +672 -0
  169. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  170. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  171. package/src/tools/agent-harness-metadata.ts +100 -168
  172. package/src/tools/agent-harness-mode-catalog.ts +86 -33
  173. package/src/tools/agent-harness-model-catalog.ts +162 -0
  174. package/src/tools/agent-harness-model-provider-health.ts +283 -0
  175. package/src/tools/agent-harness-model-readiness.ts +432 -0
  176. package/src/tools/agent-harness-model-routing-types.ts +327 -0
  177. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  178. package/src/tools/agent-harness-model-routing.ts +165 -193
  179. package/src/tools/agent-harness-operator-methods.ts +115 -133
  180. package/src/tools/agent-harness-pairing-posture.ts +452 -18
  181. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  182. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  183. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  184. package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
  185. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  186. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  187. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  188. package/src/tools/agent-harness-personal-ops-records.ts +684 -0
  189. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  190. package/src/tools/agent-harness-personal-ops-types.ts +240 -0
  191. package/src/tools/agent-harness-personal-ops.ts +613 -0
  192. package/src/tools/agent-harness-project-context.ts +144 -0
  193. package/src/tools/agent-harness-prompt-context.ts +589 -0
  194. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  195. package/src/tools/agent-harness-release-evidence.ts +9 -7
  196. package/src/tools/agent-harness-release-readiness.ts +9 -7
  197. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  198. package/src/tools/agent-harness-research-briefing.ts +446 -0
  199. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  200. package/src/tools/agent-harness-research-queue.ts +250 -0
  201. package/src/tools/agent-harness-research-runs.ts +307 -0
  202. package/src/tools/agent-harness-research-workflow.ts +395 -0
  203. package/src/tools/agent-harness-security-posture.ts +2 -2
  204. package/src/tools/agent-harness-service-posture.ts +9 -9
  205. package/src/tools/agent-harness-session-metadata.ts +3 -3
  206. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  207. package/src/tools/agent-harness-setup-describe.ts +557 -0
  208. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  209. package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
  210. package/src/tools/agent-harness-setup-plan.ts +253 -0
  211. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  212. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  213. package/src/tools/agent-harness-setup-posture.ts +449 -221
  214. package/src/tools/agent-harness-setup-smoke.ts +533 -0
  215. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  216. package/src/tools/agent-harness-tool-schema.ts +142 -7
  217. package/src/tools/agent-harness-tool-types.ts +83 -0
  218. package/src/tools/agent-harness-tool-utils.ts +43 -0
  219. package/src/tools/agent-harness-tool.ts +319 -370
  220. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  221. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  222. package/src/tools/agent-harness-vibe-health.ts +105 -0
  223. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  224. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  225. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  226. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  227. package/src/tools/agent-host-tool.ts +159 -0
  228. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  229. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  230. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  231. package/src/tools/agent-memory-tool.ts +253 -0
  232. package/src/tools/agent-model-compare-export.ts +315 -0
  233. package/src/tools/agent-model-compare-handoff.ts +592 -0
  234. package/src/tools/agent-model-compare-judgment.ts +633 -0
  235. package/src/tools/agent-model-compare-run.ts +735 -0
  236. package/src/tools/agent-model-compare-tool.ts +698 -0
  237. package/src/tools/agent-model-compare-types.ts +191 -0
  238. package/src/tools/agent-model-compare-utils.ts +93 -0
  239. package/src/tools/agent-models-tool.ts +208 -0
  240. package/src/tools/agent-operator-action-tool.ts +1 -1
  241. package/src/tools/agent-operator-method-tool.ts +643 -0
  242. package/src/tools/agent-personal-ops-tool.ts +197 -0
  243. package/src/tools/agent-policy-explanation.ts +415 -0
  244. package/src/tools/agent-research-report-tool.ts +608 -0
  245. package/src/tools/agent-research-runner.ts +367 -0
  246. package/src/tools/agent-research-runs-tool.ts +434 -0
  247. package/src/tools/agent-research-sources-tool.ts +443 -0
  248. package/src/tools/agent-research-tool.ts +515 -0
  249. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  250. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  251. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  252. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  253. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  254. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  255. package/src/tools/agent-route-planner-helpers.ts +667 -0
  256. package/src/tools/agent-route-planner.ts +185 -0
  257. package/src/tools/agent-route-tool.ts +105 -0
  258. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  259. package/src/tools/agent-schedule-tool.ts +282 -0
  260. package/src/tools/agent-security-tool.ts +145 -0
  261. package/src/tools/agent-sessions-tool.ts +122 -0
  262. package/src/tools/agent-settings-import-tool.ts +104 -0
  263. package/src/tools/agent-settings-tool.ts +176 -0
  264. package/src/tools/agent-setup-tool.ts +226 -0
  265. package/src/tools/agent-support-tool.ts +122 -0
  266. package/src/tools/agent-terminal-process-tools.ts +167 -0
  267. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  268. package/src/tools/agent-tool-policy-guard.ts +106 -85
  269. package/src/tools/agent-vibe-tool.ts +297 -0
  270. package/src/tools/agent-work-plan-tool.ts +265 -6
  271. package/src/tools/agent-workspace-tool.ts +305 -0
  272. package/src/tools/artifact-archive.ts +169 -0
  273. package/src/tools/tool-definition-compaction.ts +36 -0
  274. package/src/version.ts +1 -1
@@ -0,0 +1,295 @@
1
+ import type { AgentWorkspaceLocalLibraryItem } from '../input/agent-workspace-types.ts';
2
+ import { previewHarnessText } from './agent-harness-text.ts';
3
+ import type { LearningCandidate, LearningConsolidationDiff, LearningConsolidationFields, LearningConsolidationPlan, LocalLearningCandidateDomain } from './agent-harness-learning-curator-types.ts';
4
+ import { clampScore, isReviewed, itemFreshness, itemSourceQuality, localRegistryModelRoute, localRegistryRoute, routeList, routeValue, scoresForItem } from './agent-harness-learning-curator-common.ts';
5
+ const CONSOLIDATION_DOMAINS: readonly LocalLearningCandidateDomain[] = ['memory', 'persona', 'skill', 'routine'];
6
+
7
+ function normalizeDuplicateKey(value: string): string {
8
+ return value
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]+/g, ' ')
11
+ .trim()
12
+ .replace(/\s+/g, ' ');
13
+ }
14
+
15
+ function duplicateKeySlug(value: string): string {
16
+ return normalizeDuplicateKey(value).replace(/\s+/g, '-').slice(0, 52) || 'duplicate';
17
+ }
18
+
19
+ function uniqueText(values: readonly string[]): readonly string[] {
20
+ const seen = new Set<string>();
21
+ const result: string[] = [];
22
+ for (const value of values) {
23
+ const trimmed = value.trim();
24
+ if (!trimmed) continue;
25
+ const key = trimmed.toLowerCase();
26
+ if (seen.has(key)) continue;
27
+ seen.add(key);
28
+ result.push(trimmed);
29
+ }
30
+ return result;
31
+ }
32
+
33
+ function uniqueSorted(values: readonly string[]): readonly string[] {
34
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))]
35
+ .sort((left, right) => left.localeCompare(right));
36
+ }
37
+
38
+ function itemConsolidationScore(item: AgentWorkspaceLocalLibraryItem): number {
39
+ const scores = scoresForItem(item);
40
+ const reviewSignal = isReviewed(item) ? 26 : item.reviewState === 'fresh' ? 10 : 0;
41
+ const usageSignal = (item.enabled ? 12 : 0) + (item.active ? 16 : 0) + Math.min(12, (item.startCount ?? 0) * 3);
42
+ const confidenceSignal = item.confidence === undefined ? 0 : item.confidence / 4;
43
+ return scores.usefulness + scores.freshness + scores.sourceQuality - scores.risk + reviewSignal + usageSignal + confidenceSignal;
44
+ }
45
+
46
+ function chooseConsolidationSurvivor(items: readonly AgentWorkspaceLocalLibraryItem[]): AgentWorkspaceLocalLibraryItem {
47
+ return [...items].sort((left, right) => {
48
+ const byScore = itemConsolidationScore(right) - itemConsolidationScore(left);
49
+ if (byScore !== 0) return byScore;
50
+ const byReview = (isReviewed(right) ? 1 : 0) - (isReviewed(left) ? 1 : 0);
51
+ if (byReview !== 0) return byReview;
52
+ return left.id.localeCompare(right.id);
53
+ })[0]!;
54
+ }
55
+
56
+ function mergeableDescription(domain: LocalLearningCandidateDomain, item: AgentWorkspaceLocalLibraryItem): string {
57
+ if (domain === 'memory') {
58
+ const fallback = item.scope && item.cls ? `${item.scope}/${item.cls}` : '';
59
+ return item.description === fallback ? '' : item.description;
60
+ }
61
+ return item.description;
62
+ }
63
+
64
+ function mergedDescription(
65
+ domain: LocalLearningCandidateDomain,
66
+ survivor: AgentWorkspaceLocalLibraryItem,
67
+ duplicates: readonly AgentWorkspaceLocalLibraryItem[],
68
+ ): string {
69
+ const descriptions = uniqueText([survivor, ...duplicates].map((item) => mergeableDescription(domain, item)));
70
+ if (descriptions.length === 0) return '';
71
+ return previewHarnessText(descriptions.join('\n\n'), 900);
72
+ }
73
+
74
+ function mergedTags(items: readonly AgentWorkspaceLocalLibraryItem[]): readonly string[] {
75
+ return uniqueSorted(items.flatMap((item) => item.tags));
76
+ }
77
+
78
+ function mergedTriggers(items: readonly AgentWorkspaceLocalLibraryItem[]): readonly string[] {
79
+ return uniqueSorted(items.flatMap((item) => item.triggers));
80
+ }
81
+
82
+ function updateFieldsForConsolidation(
83
+ domain: LocalLearningCandidateDomain,
84
+ survivor: AgentWorkspaceLocalLibraryItem,
85
+ duplicates: readonly AgentWorkspaceLocalLibraryItem[],
86
+ ): LearningConsolidationFields | undefined {
87
+ const all = [survivor, ...duplicates];
88
+ const tags = mergedTags(all);
89
+ const triggers = mergedTriggers(all);
90
+ const description = mergedDescription(domain, survivor, duplicates);
91
+ if (domain === 'memory') {
92
+ const fields: LearningConsolidationFields = {
93
+ ...(description ? { detail: description } : {}),
94
+ ...(tags.length > 0 ? { tags } : {}),
95
+ };
96
+ return Object.keys(fields).length > 0 ? fields : undefined;
97
+ }
98
+ const fields: LearningConsolidationFields = {
99
+ ...(description ? { description } : {}),
100
+ ...(tags.length > 0 ? { tags } : {}),
101
+ ...(triggers.length > 0 ? { triggers } : {}),
102
+ };
103
+ return Object.keys(fields).length > 0 ? fields : undefined;
104
+ }
105
+
106
+ function rollbackFieldsForConsolidation(
107
+ domain: LocalLearningCandidateDomain,
108
+ survivor: AgentWorkspaceLocalLibraryItem,
109
+ ): LearningConsolidationFields {
110
+ const description = mergeableDescription(domain, survivor);
111
+ if (domain === 'memory') {
112
+ return {
113
+ ...(description ? { detail: description } : {}),
114
+ tags: survivor.tags,
115
+ };
116
+ }
117
+ return {
118
+ ...(description ? { description } : {}),
119
+ tags: survivor.tags,
120
+ triggers: survivor.triggers,
121
+ };
122
+ }
123
+
124
+ function consolidationDiff(
125
+ field: string,
126
+ survivor: string,
127
+ duplicateValues: readonly string[],
128
+ merged: string,
129
+ ): LearningConsolidationDiff | null {
130
+ const duplicates = uniqueText(duplicateValues);
131
+ if (duplicates.length === 0 && !merged) return null;
132
+ const normalizedSurvivor = survivor.trim().toLowerCase();
133
+ const differs = duplicates.some((value) => value.trim().toLowerCase() !== normalizedSurvivor)
134
+ || merged.trim().toLowerCase() !== normalizedSurvivor;
135
+ if (!differs) return null;
136
+ return {
137
+ field,
138
+ survivor: survivor || '(empty)',
139
+ duplicates,
140
+ merged: merged || '(empty)',
141
+ };
142
+ }
143
+
144
+ function consolidationDiffs(
145
+ domain: LocalLearningCandidateDomain,
146
+ survivor: AgentWorkspaceLocalLibraryItem,
147
+ duplicates: readonly AgentWorkspaceLocalLibraryItem[],
148
+ ): readonly LearningConsolidationDiff[] {
149
+ const all = [survivor, ...duplicates];
150
+ const tags = mergedTags(all).join(', ');
151
+ const triggers = mergedTriggers(all).join(', ');
152
+ return [
153
+ consolidationDiff('name', survivor.name, duplicates.map((item) => item.name), survivor.name),
154
+ consolidationDiff('description', mergeableDescription(domain, survivor), duplicates.map((item) => mergeableDescription(domain, item)), mergedDescription(domain, survivor, duplicates)),
155
+ consolidationDiff('tags', survivor.tags.join(', '), duplicates.map((item) => item.tags.join(', ')), tags),
156
+ domain === 'memory' ? null : consolidationDiff('triggers', survivor.triggers.join(', '), duplicates.map((item) => item.triggers.join(', ')), triggers),
157
+ ].filter((diff): diff is LearningConsolidationDiff => diff !== null);
158
+ }
159
+
160
+ function updateRouteForConsolidation(
161
+ domain: LocalLearningCandidateDomain,
162
+ survivor: AgentWorkspaceLocalLibraryItem,
163
+ duplicates: readonly AgentWorkspaceLocalLibraryItem[],
164
+ ): string | undefined {
165
+ const updateFields = updateFieldsForConsolidation(domain, survivor, duplicates);
166
+ if (!updateFields) return undefined;
167
+ const fields: string[] = [];
168
+ if (domain === 'memory') {
169
+ if (updateFields.detail) fields.push(`detail:"${routeValue(updateFields.detail)}"`);
170
+ if (updateFields.tags && updateFields.tags.length > 0) fields.push(`tags:${routeList(updateFields.tags)}`);
171
+ } else {
172
+ if (updateFields.description) fields.push(`description:"${routeValue(updateFields.description)}"`);
173
+ if (updateFields.tags && updateFields.tags.length > 0) fields.push(`tags:${routeList(updateFields.tags)}`);
174
+ if (updateFields.triggers && updateFields.triggers.length > 0) fields.push(`triggers:${routeList(updateFields.triggers)}`);
175
+ }
176
+ if (fields.length === 0) return undefined;
177
+ return [
178
+ localRegistryRoute(domain, 'update', survivor.id),
179
+ ...fields,
180
+ 'provenance:"learning-curator-consolidation"',
181
+ ].join(' ');
182
+ }
183
+
184
+ function rollbackUpdateRouteForConsolidation(domain: LocalLearningCandidateDomain, survivor: AgentWorkspaceLocalLibraryItem): string {
185
+ const rollbackFields = rollbackFieldsForConsolidation(domain, survivor);
186
+ const fields: string[] = [];
187
+ if (domain === 'memory') {
188
+ if (rollbackFields.detail) fields.push(`detail:"${routeValue(rollbackFields.detail)}"`);
189
+ fields.push(`tags:${routeList(rollbackFields.tags ?? [])}`);
190
+ } else {
191
+ if (rollbackFields.description) fields.push(`description:"${routeValue(rollbackFields.description)}"`);
192
+ fields.push(`tags:${routeList(rollbackFields.tags ?? [])}`);
193
+ fields.push(`triggers:${routeList(rollbackFields.triggers ?? [])}`);
194
+ }
195
+ return [
196
+ localRegistryRoute(domain, 'update', survivor.id),
197
+ ...fields,
198
+ 'provenance:"rollback-learning-curator-consolidation"',
199
+ ].join(' ');
200
+ }
201
+
202
+ function consolidationProposalFields(plan: LearningConsolidationPlan): Readonly<Record<string, string>> {
203
+ return {
204
+ survivorId: plan.survivorId,
205
+ duplicateIds: plan.duplicateIds.join(','),
206
+ visibleDiff: plan.diffs.length > 0
207
+ ? plan.diffs.map((diff) => `${diff.field}: keep [${diff.survivor}], merge [${diff.merged}]`).join('\n')
208
+ : 'Visible metadata matches; inspect record bodies before marking duplicates stale.',
209
+ staleFirst: plan.staleRoutes.join('\n'),
210
+ rollback: plan.rollbackRoutes.join('\n'),
211
+ };
212
+ }
213
+
214
+ function consolidationCandidate(
215
+ domain: LocalLearningCandidateDomain,
216
+ key: string,
217
+ items: readonly AgentWorkspaceLocalLibraryItem[],
218
+ ): LearningCandidate | null {
219
+ if (items.length < 2) return null;
220
+ const survivor = chooseConsolidationSurvivor(items);
221
+ const duplicates = items.filter((item) => item.id !== survivor.id);
222
+ if (duplicates.length === 0) return null;
223
+ const updateRoute = updateRouteForConsolidation(domain, survivor, duplicates);
224
+ const updateFields = updateFieldsForConsolidation(domain, survivor, duplicates);
225
+ const rollbackFields = rollbackFieldsForConsolidation(domain, survivor);
226
+ const staleRoutes = duplicates.map((item) => `${localRegistryRoute(domain, 'stale', item.id)} reason:"Duplicate of ${routeValue(survivor.id)}; staged by learning curator consolidation."`);
227
+ const deleteRoutes = duplicates.map((item) => `${localRegistryRoute(domain, 'delete', item.id)} confirm:true explicitUserRequest:"Delete duplicate ${routeValue(domain)} ${routeValue(item.id)} after reviewed consolidation."`);
228
+ const rollbackRoutes = [
229
+ ...(updateRoute ? [rollbackUpdateRouteForConsolidation(domain, survivor)] : []),
230
+ ...duplicates.map((item) => localRegistryRoute(domain, 'review', item.id)),
231
+ ];
232
+ const diffs = consolidationDiffs(domain, survivor, duplicates);
233
+ const plan: LearningConsolidationPlan = {
234
+ survivorId: survivor.id,
235
+ duplicateIds: duplicates.map((item) => item.id),
236
+ sharedKey: key,
237
+ diffs,
238
+ ...(updateFields ? { updateFields } : {}),
239
+ rollbackFields,
240
+ ...(updateRoute ? { updateRoute } : {}),
241
+ staleRoutes,
242
+ deleteRoutes,
243
+ rollbackRoutes,
244
+ };
245
+ const enabledDuplicateCount = duplicates.filter((item) => item.enabled || item.active).length;
246
+ return {
247
+ id: `consolidation:${domain}:${duplicateKeySlug(key)}`,
248
+ label: `Consolidate duplicate ${domain}: ${survivor.name}`,
249
+ domain,
250
+ recordId: survivor.id,
251
+ status: 'needs-consolidation',
252
+ priority: clampScore(72 + Math.min(16, duplicates.length * 4) + enabledDuplicateCount * 5),
253
+ reason: `${items.length} Agent-local ${domain} records share the same normalized name.`,
254
+ next: 'Inspect every record, update the survivor with merged visible fields, mark duplicates stale first, then delete only after explicit review.',
255
+ scores: {
256
+ usefulness: clampScore(64 + Math.min(18, items.length * 4)),
257
+ freshness: Math.max(...items.map(itemFreshness)),
258
+ sourceQuality: Math.max(...items.map(itemSourceQuality)),
259
+ risk: clampScore(34 + enabledDuplicateCount * 12 + Math.min(18, duplicates.length * 4)),
260
+ },
261
+ reviewState: survivor.reviewState,
262
+ ...(survivor.enabled === undefined ? {} : { enabled: survivor.enabled }),
263
+ ...(survivor.active === undefined ? {} : { active: survivor.active }),
264
+ ...(survivor.confidence === undefined ? {} : { confidence: survivor.confidence }),
265
+ proposalFields: consolidationProposalFields(plan),
266
+ consolidation: plan,
267
+ inspectRoute: localRegistryRoute(domain, 'get', survivor.id),
268
+ modelRoute: 'memory action:"curator" query:"consolidation"',
269
+ reviewRoute: localRegistryRoute(domain, 'review', survivor.id),
270
+ ...(staleRoutes[0] ? { staleRoute: staleRoutes[0] } : {}),
271
+ ...(updateRoute ? { updateRoute } : {}),
272
+ ...(deleteRoutes[0] ? { deleteRoute: deleteRoutes[0] } : {}),
273
+ cleanupRoutes: staleRoutes,
274
+ rollbackRoutes,
275
+ };
276
+ }
277
+
278
+ export function consolidationCandidatesForDomain(
279
+ domain: LocalLearningCandidateDomain,
280
+ items: readonly AgentWorkspaceLocalLibraryItem[],
281
+ ): readonly LearningCandidate[] {
282
+ if (!CONSOLIDATION_DOMAINS.includes(domain)) return [];
283
+ const groups = new Map<string, AgentWorkspaceLocalLibraryItem[]>();
284
+ for (const item of items) {
285
+ const key = normalizeDuplicateKey(item.name);
286
+ if (key.length < 4) continue;
287
+ const group = groups.get(key) ?? [];
288
+ group.push(item);
289
+ groups.set(key, group);
290
+ }
291
+ return [...groups.entries()].flatMap(([key, group]) => {
292
+ const candidate = consolidationCandidate(domain, key, group);
293
+ return candidate ? [candidate] : [];
294
+ });
295
+ }