@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,606 @@
1
+ import type { CommandContext } from '../input/command-registry.ts';
2
+ import { AgentResearchRunRegistry, type AgentResearchRunRecord } from '../agent/research-run-registry.ts';
3
+ import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
4
+ import type { AgentWorkspaceLocalLibraryItem } from '../input/agent-workspace-types.ts';
5
+ import type { WorkPlanItem } from '../work-plans/work-plan-store.ts';
6
+ import { previewHarnessText } from './agent-harness-text.ts';
7
+ import { agentHarnessVibeHealth } from './agent-harness-vibe-health.ts';
8
+ import type { LearningCandidate, LearningProposalTarget, SessionInfoLike, SessionManagerLike, VibeCandidateKind, LocalLearningCandidateDomain } from './agent-harness-learning-curator-types.ts';
9
+ import { candidateBase, clampScore, isReviewed, itemFreshness, itemSourceQuality, itemUsefulness, localRegistryModelRoute, localRegistryRoute, missingRequirementCount, routeValue } from './agent-harness-learning-curator-common.ts';
10
+ import { consolidationCandidatesForDomain } from './agent-harness-learning-curator-consolidation.ts';
11
+ function captureCandidate(): LearningCandidate {
12
+ return {
13
+ id: 'capture:reviewed-lesson',
14
+ label: 'Capture reviewed lesson',
15
+ domain: 'capture',
16
+ recordId: null,
17
+ status: 'ready',
18
+ priority: 30,
19
+ reason: 'No urgent local-learning review candidates are present.',
20
+ next: 'After a repeated workflow, useful preference, or durable lesson appears, capture it as a local memory, skill, routine, or persona for review.',
21
+ scores: { usefulness: 55, freshness: 85, sourceQuality: 60, risk: 10 },
22
+ inspectRoute: 'agent_harness mode:"workspace_action" actionId:"learned-behavior"',
23
+ modelRoute: 'agent_harness mode:"workspace_actions" query:"learned behavior"',
24
+ createRoute: 'agent_harness mode:"run_workspace_action" actionId:"learned-behavior" confirm:true explicitUserRequest:"..."',
25
+ };
26
+ }
27
+
28
+ function stableVibeIdSuffix(value: string): string {
29
+ const slug = value
30
+ .toLowerCase()
31
+ .replace(/[^a-z0-9]+/g, '-')
32
+ .replace(/^-+|-+$/g, '')
33
+ .slice(0, 52) || 'vibe';
34
+ let hash = 0;
35
+ for (let index = 0; index < value.length; index += 1) {
36
+ hash = ((hash * 31) + value.charCodeAt(index)) >>> 0;
37
+ }
38
+ return `${slug}-${hash.toString(36)}`;
39
+ }
40
+
41
+ function vibeStatusRoute(): string {
42
+ return 'vibe action:"status"';
43
+ }
44
+
45
+ function vibeCommandInspectRoute(): string {
46
+ return 'vibe action:"status"';
47
+ }
48
+
49
+ function vibeCandidate(
50
+ kind: VibeCandidateKind,
51
+ path: string,
52
+ scope: string,
53
+ reason: string,
54
+ ): LearningCandidate {
55
+ const isBlocked = kind === 'blocked';
56
+ return {
57
+ id: `vibe:${kind}:${stableVibeIdSuffix(path)}`,
58
+ label: `${isBlocked ? 'Blocked' : 'Truncated'} ${scope} VIBE.md`,
59
+ domain: 'vibe',
60
+ recordId: path,
61
+ status: isBlocked ? 'needs-setup' : 'needs-review',
62
+ priority: isBlocked ? 88 : 72,
63
+ reason,
64
+ next: isBlocked
65
+ ? 'Inspect /vibe status, edit or remove blocked content, then rerun status before relying on personality instructions.'
66
+ : 'Inspect /vibe status and shorten the file if omitted personality instructions matter for this user.',
67
+ scores: {
68
+ usefulness: 75,
69
+ freshness: isBlocked ? 45 : 58,
70
+ sourceQuality: scope === 'project' ? 76 : 70,
71
+ risk: isBlocked ? 86 : 58,
72
+ },
73
+ ...(isBlocked ? { missingRequirements: ['VIBE.md file is not loaded into the prompt until the blocked content is repaired.'] } : {}),
74
+ proposalFields: {
75
+ path,
76
+ scope,
77
+ kind,
78
+ reason: previewHarnessText(reason, 220),
79
+ statusRoute: vibeStatusRoute(),
80
+ },
81
+ inspectRoute: vibeStatusRoute(),
82
+ modelRoute: 'memory action:"curator" query:"vibe"',
83
+ reviewRoute: vibeCommandInspectRoute(),
84
+ createRoute: 'vibe action:"init" scope:"project" confirm:true explicitUserRequest:"Create or refresh the project VIBE.md starter."',
85
+ };
86
+ }
87
+
88
+ function vibeHealthCandidates(context: CommandContext): readonly LearningCandidate[] {
89
+ const health = agentHarnessVibeHealth(context);
90
+ return [
91
+ ...health.blockedFiles.map((file) => vibeCandidate('blocked', file.path, file.scope, file.reason)),
92
+ ...health.files
93
+ .filter((file) => file.truncated)
94
+ .map((file) => vibeCandidate(
95
+ 'truncated',
96
+ file.path,
97
+ file.scope,
98
+ 'VIBE.md was loaded, but only the first safe prompt budget was applied.',
99
+ )),
100
+ ];
101
+ }
102
+
103
+ function noteProposalTarget(item: AgentWorkspaceLocalLibraryItem): LearningProposalTarget | null {
104
+ const tags = item.tags.map((tag) => tag.toLowerCase());
105
+ const text = [item.name, item.description, ...tags].join('\n').toLowerCase();
106
+ if (tags.some((tag) => ['memory', 'fact', 'decision', 'constraint', 'risk', 'pattern', 'incident', 'architecture', 'ownership'].includes(tag))) return 'memory';
107
+ if (tags.some((tag) => ['routine', 'workflow', 'runbook', 'process'].includes(tag))) return 'routine';
108
+ if (tags.some((tag) => ['persona', 'style', 'preference', 'tone'].includes(tag))) return 'persona';
109
+ if (tags.some((tag) => ['skill', 'procedure', 'lesson', 'learned', 'howto'].includes(tag))) return 'skill';
110
+ if (/\b(memory|remember|fact|decision|constraint|risk|incident|pattern|architecture|ownership)\b/.test(text)) return 'memory';
111
+ if (/\b(repeat|routine|workflow|every time|checklist|runbook)\b/.test(text)) return 'routine';
112
+ if (/\b(style|tone|preference|respond|answer)\b/.test(text)) return 'persona';
113
+ if (/\b(lesson|procedure|steps|how to|when asked)\b/.test(text)) return 'skill';
114
+ return null;
115
+ }
116
+
117
+ function proposalSubject(target: LearningProposalTarget): string {
118
+ return target === 'memory' ? 'durable memory' : `reusable ${target} behavior`;
119
+ }
120
+
121
+ function noteBehaviorProposalCandidate(item: AgentWorkspaceLocalLibraryItem): LearningCandidate | null {
122
+ if (!isReviewed(item)) return null;
123
+ const target = noteProposalTarget(item);
124
+ if (!target) return null;
125
+ const actionId = target === 'memory'
126
+ ? 'notes-to-memory'
127
+ : target === 'skill'
128
+ ? 'notes-to-skill'
129
+ : target === 'routine'
130
+ ? 'notes-to-routine'
131
+ : 'notes-to-persona';
132
+ const label = `${item.name} -> ${target}`;
133
+ return {
134
+ id: `note-proposal:${target}:${item.id}`,
135
+ label,
136
+ domain: 'note',
137
+ recordId: item.id,
138
+ status: 'proposal-ready',
139
+ priority: target === 'routine' ? 64 : target === 'memory' ? 62 : 60,
140
+ reason: `Reviewed note looks like ${proposalSubject(target)}.`,
141
+ next: `Preview the selected-note ${target} promotion, then save it only if the user wants this durable context.`,
142
+ scores: {
143
+ usefulness: clampScore(itemUsefulness(item) + 8),
144
+ freshness: itemFreshness(item),
145
+ sourceQuality: itemSourceQuality(item),
146
+ risk: 24,
147
+ },
148
+ reviewState: item.reviewState,
149
+ proposalTarget: target,
150
+ inspectRoute: localRegistryRoute('note', 'get', item.id),
151
+ modelRoute: `agent_harness mode:"workspace_action" actionId:"${actionId}"`,
152
+ createRoute: `agent_harness mode:"run_workspace_action" actionId:"${actionId}" recordId:"${item.id}" confirm:true explicitUserRequest:"..."`,
153
+ };
154
+ }
155
+
156
+ function workPlanProposalTarget(item: WorkPlanItem): LearningProposalTarget | null {
157
+ const text = [item.title, item.notes ?? '', item.source ?? '', item.owner ?? ''].join('\n').toLowerCase();
158
+ if (/\b(memory|remember|fact|decision|constraint|risk|incident|pattern|architecture|ownership)\b/.test(text)) return 'memory';
159
+ if (/\b(style|tone|preference|respond|answer|voice|persona)\b/.test(text)) return 'persona';
160
+ if (/\b(repeat|routine|workflow|every time|checklist|runbook|process|before release|after release)\b/.test(text)) return 'routine';
161
+ if (/\b(lesson|procedure|steps|how to|when asked|debug|fix|review|test|release|deploy|triage)\b/.test(text)) return 'skill';
162
+ return null;
163
+ }
164
+
165
+ function inferMemoryClass(text: string): string {
166
+ const normalized = text.toLowerCase();
167
+ if (/\bdecision|decided|choose|selected\b/.test(normalized)) return 'decision';
168
+ if (/\bconstraint|must|never|always|required\b/.test(normalized)) return 'constraint';
169
+ if (/\brisk|hazard|regression\b/.test(normalized)) return 'risk';
170
+ if (/\bincident|outage|failure\b/.test(normalized)) return 'incident';
171
+ if (/\bpattern|repeat|recurring\b/.test(normalized)) return 'pattern';
172
+ if (/\barchitecture|design|system\b/.test(normalized)) return 'architecture';
173
+ if (/\bowner|ownership|responsible\b/.test(normalized)) return 'ownership';
174
+ if (/\brunbook|checklist\b/.test(normalized)) return 'runbook';
175
+ return 'fact';
176
+ }
177
+
178
+ function completedWorkFreshness(item: WorkPlanItem): number {
179
+ if (!item.completedAt) return 70;
180
+ const ageDays = Math.max(0, (Date.now() - item.completedAt) / (24 * 60 * 60 * 1000));
181
+ return clampScore(96 - Math.min(45, ageDays * 3));
182
+ }
183
+
184
+ function completedIsoFreshness(iso: string | undefined): number {
185
+ if (!iso) return 70;
186
+ const parsed = Date.parse(iso);
187
+ if (!Number.isFinite(parsed)) return 70;
188
+ const ageDays = Math.max(0, (Date.now() - parsed) / (24 * 60 * 60 * 1000));
189
+ return clampScore(96 - Math.min(45, ageDays * 3));
190
+ }
191
+
192
+ function completedTimestampFreshness(timestamp: number | undefined): number {
193
+ if (timestamp === undefined || !Number.isFinite(timestamp) || timestamp <= 0) return 70;
194
+ const ageDays = Math.max(0, (Date.now() - timestamp) / (24 * 60 * 60 * 1000));
195
+ return clampScore(96 - Math.min(45, ageDays * 3));
196
+ }
197
+
198
+ function completedWorkDetail(item: WorkPlanItem, notes: string): string {
199
+ return [
200
+ `Completed work: ${item.title}`,
201
+ item.owner ? `Owner: ${item.owner}` : '',
202
+ item.source ? `Source: ${item.source}` : '',
203
+ '',
204
+ notes,
205
+ ].filter(Boolean).join('\n');
206
+ }
207
+
208
+ function workPlanCompletionCandidate(item: WorkPlanItem): LearningCandidate | null {
209
+ if (item.status !== 'done') return null;
210
+ const target = workPlanProposalTarget(item);
211
+ if (!target) return null;
212
+ const notes = item.notes?.trim() || `Completed work item: ${item.title}`;
213
+ const name = previewHarnessText(item.title, 80);
214
+ const description = target === 'memory'
215
+ ? `Durable memory learned from completed work: ${name}`
216
+ : target === 'routine'
217
+ ? `Repeatable workflow learned from completed work: ${name}`
218
+ : target === 'persona'
219
+ ? `Operating preference learned from completed work: ${name}`
220
+ : `Reusable skill learned from completed work: ${name}`;
221
+ const detail = completedWorkDetail(item, notes);
222
+ return {
223
+ id: `work-plan-proposal:${target}:${item.id}`,
224
+ label: `${item.title} -> ${target}`,
225
+ domain: 'work_plan',
226
+ recordId: item.id,
227
+ status: 'proposal-ready',
228
+ priority: target === 'routine' ? 62 : target === 'memory' ? 60 : 58,
229
+ reason: `Completed work item looks like ${proposalSubject(target)}.`,
230
+ next: `Review the completed work notes, then capture this as Agent-local ${target} only if the user wants it reused.`,
231
+ scores: {
232
+ usefulness: clampScore(62 + Math.min(18, notes.length / 12)),
233
+ freshness: completedWorkFreshness(item),
234
+ sourceQuality: item.source ? 72 : 64,
235
+ risk: 28,
236
+ },
237
+ proposalTarget: target,
238
+ proposalFields: target === 'memory' ? {
239
+ cls: inferMemoryClass(`${item.title}\n${notes}`),
240
+ scope: 'project',
241
+ summary: previewHarnessText(item.title, 140),
242
+ detail,
243
+ tags: 'learned,completed-work,memory',
244
+ confidence: '80',
245
+ } : {
246
+ target,
247
+ name,
248
+ description: previewHarnessText(description, 140),
249
+ notes: detail,
250
+ triggers: target === 'routine' ? 'workflow, checklist' : target === 'persona' ? 'preference' : 'lesson, procedure',
251
+ tags: `learned,completed-work,${target}`,
252
+ enable: 'yes',
253
+ },
254
+ inspectRoute: `agent_work_plan action:"get" id:"${item.id}"`,
255
+ modelRoute: 'agent_work_plan action:"get"',
256
+ createRoute: target === 'memory'
257
+ ? 'agent_harness mode:"run_workspace_action" actionId:"memory-create" confirm:true explicitUserRequest:"..."'
258
+ : 'agent_harness mode:"run_workspace_action" actionId:"learned-behavior" confirm:true explicitUserRequest:"..."',
259
+ };
260
+ }
261
+
262
+ function researchRunProposalTarget(run: AgentResearchRunRecord): LearningProposalTarget | null {
263
+ const text = [
264
+ run.title,
265
+ run.question,
266
+ run.goal,
267
+ run.note ?? '',
268
+ run.reportArtifactId ?? '',
269
+ ...run.plan,
270
+ ...run.nextSteps,
271
+ ...run.sourceIds,
272
+ ...run.checkpoints.map((checkpoint) => checkpoint.note),
273
+ ].join('\n').toLowerCase();
274
+ if (/\b(memory|remember|fact|decision|constraint|risk|incident|pattern|architecture|ownership)\b/.test(text)) return 'memory';
275
+ if (/\b(style|tone|preference|respond|answer|voice|persona)\b/.test(text)) return 'persona';
276
+ if (/\b(repeat|routine|workflow|every time|checklist|runbook|process|before report|after report)\b/.test(text)) return 'routine';
277
+ if (/\b(lesson|procedure|steps|how to|when asked|research|source|citation|report|credibility|synthesize)\b/.test(text)) return 'skill';
278
+ return null;
279
+ }
280
+
281
+ function researchRunDetail(run: AgentResearchRunRecord): string {
282
+ return [
283
+ `Completed research run: ${run.title}`,
284
+ `Question: ${run.question}`,
285
+ `Goal: ${run.goal}`,
286
+ run.reportArtifactId ? `Report artifact: ${run.reportArtifactId}` : '',
287
+ run.sourceIds.length > 0 ? `Sources: ${run.sourceIds.join(', ')}` : '',
288
+ '',
289
+ run.note ? `Completion note: ${run.note}` : '',
290
+ run.plan.length > 0 ? `Plan:\n${run.plan.map((step) => `- ${step}`).join('\n')}` : '',
291
+ run.checkpoints.length > 0 ? `Recent checkpoints:\n${run.checkpoints.slice(-3).map((checkpoint) => `- ${checkpoint.note}`).join('\n')}` : '',
292
+ ].filter(Boolean).join('\n');
293
+ }
294
+
295
+ function researchRunCompletionCandidate(run: AgentResearchRunRecord): LearningCandidate | null {
296
+ if (run.status !== 'completed') return null;
297
+ const target = researchRunProposalTarget(run);
298
+ if (!target) return null;
299
+ const name = previewHarnessText(run.title, 80);
300
+ const detail = researchRunDetail(run);
301
+ const description = target === 'memory'
302
+ ? `Durable memory learned from completed research: ${name}`
303
+ : target === 'routine'
304
+ ? `Repeatable research workflow learned from completed run: ${name}`
305
+ : target === 'persona'
306
+ ? `Operating preference learned from completed research: ${name}`
307
+ : `Reusable research skill learned from completed run: ${name}`;
308
+ return {
309
+ id: `research-run-proposal:${target}:${run.id}`,
310
+ label: `${run.title} -> ${target}`,
311
+ domain: 'research_run',
312
+ recordId: run.id,
313
+ status: 'proposal-ready',
314
+ priority: target === 'memory' ? 57 : 55,
315
+ reason: `Completed research run looks like ${proposalSubject(target)}.`,
316
+ next: `Review the run ledger and report artifact, then capture this as Agent-local ${target} only if it should guide future work.`,
317
+ scores: {
318
+ usefulness: clampScore(60 + Math.min(16, run.sourceIds.length * 3) + Math.min(10, run.checkpoints.length * 2)),
319
+ freshness: completedIsoFreshness(run.completedAt),
320
+ sourceQuality: run.reportArtifactId ? 78 : run.sourceIds.length > 0 ? 70 : 62,
321
+ risk: target === 'memory' ? 32 : 30,
322
+ },
323
+ proposalTarget: target,
324
+ proposalFields: target === 'memory' ? {
325
+ cls: inferMemoryClass(`${run.title}\n${run.note ?? ''}\n${run.goal}`),
326
+ scope: 'project',
327
+ summary: previewHarnessText(run.note || run.title, 140),
328
+ detail,
329
+ tags: 'learned,research-run,memory',
330
+ confidence: run.reportArtifactId ? '82' : '76',
331
+ } : {
332
+ target,
333
+ name,
334
+ description: previewHarnessText(description, 140),
335
+ notes: detail,
336
+ triggers: target === 'routine' ? 'research, report, workflow' : target === 'persona' ? 'research preference' : 'research, sources, report',
337
+ tags: `learned,research-run,${target}`,
338
+ enable: 'yes',
339
+ },
340
+ inspectRoute: `agent_harness mode:"research_run" runId:"${run.id}"`,
341
+ modelRoute: 'agent_harness mode:"research_run"',
342
+ createRoute: target === 'memory'
343
+ ? 'agent_harness mode:"run_workspace_action" actionId:"memory-create" confirm:true explicitUserRequest:"..."'
344
+ : 'agent_harness mode:"run_workspace_action" actionId:"learned-behavior" confirm:true explicitUserRequest:"..."',
345
+ };
346
+ }
347
+
348
+ function extractMessageText(message: unknown): string {
349
+ if (typeof message === 'string') return message;
350
+ if (typeof message !== 'object' || message === null) return '';
351
+ const record = message as Record<string, unknown>;
352
+ const content = record.content;
353
+ if (typeof content === 'string') return content;
354
+ if (Array.isArray(content)) {
355
+ return content.map((part) => {
356
+ if (typeof part === 'string') return part;
357
+ if (typeof part !== 'object' || part === null) return '';
358
+ const partRecord = part as Record<string, unknown>;
359
+ return typeof partRecord.text === 'string' ? partRecord.text : '';
360
+ }).filter(Boolean).join('\n');
361
+ }
362
+ return '';
363
+ }
364
+
365
+ function sessionLoadedText(manager: SessionManagerLike, session: SessionInfoLike): string {
366
+ if (typeof manager.load !== 'function') return '';
367
+ try {
368
+ const loaded = manager.load(session.name);
369
+ const metaTitle = loaded.meta?.title ?? '';
370
+ const messageText = (loaded.messages ?? []).map(extractMessageText).filter(Boolean).slice(-8).join('\n');
371
+ return [metaTitle, messageText].filter(Boolean).join('\n');
372
+ } catch {
373
+ return '';
374
+ }
375
+ }
376
+
377
+ function sessionProposalTarget(text: string): LearningProposalTarget | null {
378
+ const normalized = text.toLowerCase();
379
+ if (/\b(memory|remember|fact|decision|constraint|risk|incident|pattern|architecture|ownership)\b/.test(normalized)) return 'memory';
380
+ if (/\b(style|tone|preference|respond|answer|voice|persona)\b/.test(normalized)) return 'persona';
381
+ if (/\b(repeat|routine|workflow|every time|checklist|runbook|process)\b/.test(normalized)) return 'routine';
382
+ if (/\b(lesson|procedure|steps|how to|when asked|debug|fix|review|test|release|deploy|triage|research|source|citation|report)\b/.test(normalized)) return 'skill';
383
+ return null;
384
+ }
385
+
386
+ function sessionDetail(session: SessionInfoLike, loadedText: string): string {
387
+ return [
388
+ `Saved session: ${session.title || session.name}`,
389
+ `Session id: ${session.name}`,
390
+ session.model ? `Model: ${session.model}` : '',
391
+ session.provider ? `Provider: ${session.provider}` : '',
392
+ session.messageCount ? `Messages: ${session.messageCount}` : '',
393
+ '',
394
+ loadedText ? `Relevant transcript:\n${previewHarnessText(loadedText, 1200)}` : 'Transcript unavailable from the session manager; inspect the session before capturing durable context.',
395
+ ].filter(Boolean).join('\n');
396
+ }
397
+
398
+ function sessionCompletionCandidate(manager: SessionManagerLike, session: SessionInfoLike): LearningCandidate | null {
399
+ if (!session.name) return null;
400
+ const loadedText = sessionLoadedText(manager, session);
401
+ const text = [session.title ?? '', session.name, loadedText].join('\n');
402
+ const target = sessionProposalTarget(text);
403
+ if (!target) return null;
404
+ const labelBase = session.title || session.name;
405
+ const detail = sessionDetail(session, loadedText);
406
+ const description = target === 'memory'
407
+ ? `Durable memory learned from saved session: ${previewHarnessText(labelBase, 80)}`
408
+ : target === 'routine'
409
+ ? `Repeatable workflow learned from saved session: ${previewHarnessText(labelBase, 80)}`
410
+ : target === 'persona'
411
+ ? `Operating preference learned from saved session: ${previewHarnessText(labelBase, 80)}`
412
+ : `Reusable skill learned from saved session: ${previewHarnessText(labelBase, 80)}`;
413
+ return {
414
+ id: `session-proposal:${target}:${session.name}`,
415
+ label: `${labelBase} -> ${target}`,
416
+ domain: 'session',
417
+ recordId: session.name,
418
+ status: 'proposal-ready',
419
+ priority: target === 'memory' ? 54 : 52,
420
+ reason: `Saved session looks like ${proposalSubject(target)}.`,
421
+ next: `Inspect the saved session transcript, then capture this as Agent-local ${target} only if it should guide future work.`,
422
+ scores: {
423
+ usefulness: clampScore(52 + Math.min(18, (session.messageCount ?? 0) / 2) + (loadedText ? 8 : 0)),
424
+ freshness: completedTimestampFreshness(session.timestamp),
425
+ sourceQuality: loadedText ? 68 : 58,
426
+ risk: loadedText ? 34 : 42,
427
+ },
428
+ proposalTarget: target,
429
+ proposalFields: target === 'memory' ? {
430
+ cls: inferMemoryClass(text),
431
+ scope: 'project',
432
+ summary: previewHarnessText(loadedText || labelBase, 140),
433
+ detail,
434
+ tags: 'learned,saved-session,memory',
435
+ confidence: loadedText ? '76' : '68',
436
+ } : {
437
+ target,
438
+ name: previewHarnessText(labelBase, 80),
439
+ description: previewHarnessText(description, 140),
440
+ notes: detail,
441
+ triggers: target === 'routine' ? 'session, workflow' : target === 'persona' ? 'session preference' : 'session, lesson',
442
+ tags: `learned,saved-session,${target}`,
443
+ enable: 'yes',
444
+ },
445
+ inspectRoute: `sessions action:"get" sessionId:"${session.name}"`,
446
+ modelRoute: 'sessions action:"get"',
447
+ createRoute: target === 'memory'
448
+ ? 'agent_harness mode:"run_workspace_action" actionId:"memory-create" confirm:true explicitUserRequest:"..."'
449
+ : 'agent_harness mode:"run_workspace_action" actionId:"learned-behavior" confirm:true explicitUserRequest:"..."',
450
+ };
451
+ }
452
+
453
+ function notePromotionCandidate(item: AgentWorkspaceLocalLibraryItem): LearningCandidate | null {
454
+ if (!isReviewed(item) || !item.description.includes('Origin URL')) return null;
455
+ return {
456
+ id: `note-promote:${item.id}`,
457
+ label: item.name,
458
+ domain: 'note',
459
+ recordId: item.id,
460
+ status: 'ready-to-promote',
461
+ priority: 52,
462
+ reason: 'Reviewed note appears to have a source URL that may belong in isolated Agent Knowledge.',
463
+ next: 'Promote only if the source is durable, useful later, and the user wants it in Agent Knowledge.',
464
+ scores: {
465
+ usefulness: itemUsefulness(item),
466
+ freshness: itemFreshness(item),
467
+ sourceQuality: clampScore(itemSourceQuality(item) + 8),
468
+ risk: 18,
469
+ },
470
+ reviewState: item.reviewState,
471
+ inspectRoute: localRegistryRoute('note', 'get', item.id),
472
+ modelRoute: localRegistryModelRoute('note'),
473
+ createRoute: 'agent_harness mode:"workspace_action" actionId:"notes-to-knowledge"',
474
+ };
475
+ }
476
+
477
+ function candidatesForItem(domain: LocalLearningCandidateDomain, item: AgentWorkspaceLocalLibraryItem): LearningCandidate[] {
478
+ const candidates: LearningCandidate[] = [];
479
+ const missing = missingRequirementCount(item);
480
+ if (missing > 0 && (domain === 'skill' || domain === 'skill_bundle' || domain === 'routine')) {
481
+ candidates.push(candidateBase(
482
+ domain,
483
+ item,
484
+ 'needs-setup',
485
+ item.enabled ? 88 : 68,
486
+ `${missing} setup requirement(s) are missing.`,
487
+ 'Resolve setup requirements before enabling, scheduling, or relying on this behavior.',
488
+ ));
489
+ }
490
+ if (item.confidence !== undefined && item.confidence < 70) {
491
+ candidates.push(candidateBase(
492
+ domain,
493
+ item,
494
+ 'low-confidence',
495
+ 78 - Math.floor(item.confidence / 5),
496
+ `Confidence is ${item.confidence}%, below the durable-memory threshold.`,
497
+ 'Review, update confidence, or mark stale before using this memory as prompt context.',
498
+ ));
499
+ }
500
+ if (!isReviewed(item)) {
501
+ const enabledOrActive = item.enabled === true || item.active === true;
502
+ candidates.push(candidateBase(
503
+ domain,
504
+ item,
505
+ 'needs-review',
506
+ item.reviewState === 'stale' ? 92 : enabledOrActive ? 88 : 74,
507
+ item.reviewState === 'stale'
508
+ ? 'Record is stale and should not silently guide the assistant.'
509
+ : enabledOrActive
510
+ ? 'Record can influence the assistant but is not reviewed.'
511
+ : 'Record is fresh and waiting for review.',
512
+ 'Inspect provenance and content, then review it, revise it, or mark it stale.',
513
+ ));
514
+ }
515
+ if (domain === 'note') {
516
+ const behaviorProposal = noteBehaviorProposalCandidate(item);
517
+ if (behaviorProposal) candidates.push(behaviorProposal);
518
+ const promotion = notePromotionCandidate(item);
519
+ if (promotion) candidates.push(promotion);
520
+ }
521
+ return candidates;
522
+ }
523
+
524
+ function workPlanCompletionCandidates(context: CommandContext): readonly LearningCandidate[] {
525
+ try {
526
+ return (context.workspace?.workPlanStore?.listItems?.() ?? [])
527
+ .flatMap((item) => {
528
+ const candidate = workPlanCompletionCandidate(item);
529
+ return candidate ? [candidate] : [];
530
+ });
531
+ } catch {
532
+ return [];
533
+ }
534
+ }
535
+
536
+ function researchRunCompletionCandidates(context: CommandContext): readonly LearningCandidate[] {
537
+ const shellPaths = context.workspace?.shellPaths;
538
+ if (!shellPaths) return [];
539
+ try {
540
+ return AgentResearchRunRegistry.fromShellPaths(shellPaths)
541
+ .snapshot()
542
+ .completed
543
+ .flatMap((run) => {
544
+ const candidate = researchRunCompletionCandidate(run);
545
+ return candidate ? [candidate] : [];
546
+ });
547
+ } catch {
548
+ return [];
549
+ }
550
+ }
551
+
552
+ function sessionCompletionCandidates(context: CommandContext): readonly LearningCandidate[] {
553
+ const manager = context.session?.sessionManager as SessionManagerLike | undefined;
554
+ if (typeof manager?.list !== 'function') return [];
555
+ try {
556
+ return manager.list()
557
+ .filter((session) => session.name !== context.session?.runtime?.sessionId)
558
+ .flatMap((session) => {
559
+ const candidate = sessionCompletionCandidate(manager, session);
560
+ return candidate ? [candidate] : [];
561
+ });
562
+ } catch {
563
+ return [];
564
+ }
565
+ }
566
+
567
+ export function candidateSearchText(candidate: LearningCandidate): string {
568
+ return [
569
+ candidate.id,
570
+ candidate.label,
571
+ candidate.domain,
572
+ candidate.recordId ?? '',
573
+ candidate.status,
574
+ candidate.reason,
575
+ candidate.next,
576
+ candidate.reviewState ?? '',
577
+ candidate.missingRequirements?.join('\n') ?? '',
578
+ candidate.proposalTarget ?? '',
579
+ Object.values(candidate.proposalFields ?? {}).join('\n'),
580
+ candidate.consolidation ? JSON.stringify(candidate.consolidation) : '',
581
+ candidate.inspectRoute,
582
+ candidate.modelRoute,
583
+ ].join('\n').toLowerCase();
584
+ }
585
+
586
+ export function buildLearningCandidates(context: CommandContext): readonly LearningCandidate[] {
587
+ const snapshot = buildAgentWorkspaceRuntimeSnapshot(context);
588
+ const candidates = [
589
+ ...snapshot.localMemories.flatMap((item) => candidatesForItem('memory', item)),
590
+ ...snapshot.localNotes.flatMap((item) => candidatesForItem('note', item)),
591
+ ...snapshot.localPersonas.flatMap((item) => candidatesForItem('persona', item)),
592
+ ...snapshot.localSkills.flatMap((item) => candidatesForItem('skill', item)),
593
+ ...snapshot.localSkillBundles.flatMap((item) => candidatesForItem('skill_bundle', item)),
594
+ ...snapshot.localRoutines.flatMap((item) => candidatesForItem('routine', item)),
595
+ ...consolidationCandidatesForDomain('memory', snapshot.localMemories),
596
+ ...consolidationCandidatesForDomain('persona', snapshot.localPersonas),
597
+ ...consolidationCandidatesForDomain('skill', snapshot.localSkills),
598
+ ...consolidationCandidatesForDomain('routine', snapshot.localRoutines),
599
+ ...workPlanCompletionCandidates(context),
600
+ ...researchRunCompletionCandidates(context),
601
+ ...sessionCompletionCandidates(context),
602
+ ...vibeHealthCandidates(context),
603
+ ];
604
+ if (candidates.length === 0) candidates.push(captureCandidate());
605
+ return candidates.sort((left, right) => right.priority - left.priority || left.label.localeCompare(right.label));
606
+ }