@pellux/goodvibes-agent 1.1.7 → 1.2.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 (255) hide show
  1. package/CHANGELOG.md +170 -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 +132577 -91483
  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 +56 -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 +114 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +130 -5
  17. package/release/release-readiness.json +120 -90
  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 +296 -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-checkpoint.ts +140 -0
  42. package/src/agent/setup-wizard.ts +447 -0
  43. package/src/agent/skill-registry-types.ts +102 -0
  44. package/src/agent/skill-registry.ts +81 -107
  45. package/src/agent/vibe-confirmation-routes.ts +62 -0
  46. package/src/agent/vibe-file.ts +285 -0
  47. package/src/cli/agent-knowledge-command.ts +45 -2
  48. package/src/cli/help.ts +3 -0
  49. package/src/cli/profiles-command.ts +25 -11
  50. package/src/config/agent-settings-policy.ts +1 -18
  51. package/src/input/agent-workspace-activation.ts +53 -0
  52. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  53. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  54. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  55. package/src/input/agent-workspace-categories.ts +153 -155
  56. package/src/input/agent-workspace-channel-triage.ts +481 -0
  57. package/src/input/agent-workspace-channels.ts +263 -0
  58. package/src/input/agent-workspace-command-editor.ts +152 -0
  59. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  60. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  61. package/src/input/agent-workspace-document-editor.ts +502 -0
  62. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  63. package/src/input/agent-workspace-host-category.ts +1 -1
  64. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  65. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  66. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  67. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  68. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  69. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  70. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  71. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  72. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  73. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  74. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  75. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  76. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  77. package/src/input/agent-workspace-settings.ts +262 -30
  78. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  79. package/src/input/agent-workspace-setup-snapshot.ts +198 -0
  80. package/src/input/agent-workspace-setup.ts +34 -1
  81. package/src/input/agent-workspace-snapshot.ts +162 -147
  82. package/src/input/agent-workspace-types.ts +287 -2
  83. package/src/input/agent-workspace.ts +18 -0
  84. package/src/input/command-registry.ts +9 -2
  85. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  86. package/src/input/commands/channels-runtime.ts +102 -114
  87. package/src/input/commands/delegation-runtime.ts +87 -24
  88. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  89. package/src/input/commands/knowledge.ts +28 -14
  90. package/src/input/commands/operator-actions-runtime.ts +6 -3
  91. package/src/input/commands/schedule-runtime.ts +71 -14
  92. package/src/input/commands/session-content.ts +2 -2
  93. package/src/input/commands/shell-core.ts +2 -0
  94. package/src/input/commands/vibe-runtime.ts +140 -0
  95. package/src/input/commands.ts +2 -0
  96. package/src/input/connected-host-routes.ts +123 -0
  97. package/src/input/settings-modal-types.ts +14 -0
  98. package/src/main.ts +15 -15
  99. package/src/panels/qr-panel.ts +2 -2
  100. package/src/renderer/agent-workspace-context-lines.ts +686 -0
  101. package/src/renderer/agent-workspace.ts +30 -419
  102. package/src/renderer/settings-modal-helpers.ts +12 -0
  103. package/src/renderer/settings-modal.ts +12 -0
  104. package/src/runtime/bootstrap-command-context.ts +11 -1
  105. package/src/runtime/bootstrap-command-parts.ts +14 -3
  106. package/src/runtime/bootstrap-core.ts +39 -1
  107. package/src/runtime/bootstrap-shell.ts +6 -0
  108. package/src/runtime/bootstrap.ts +113 -30
  109. package/src/runtime/connected-host-auth.ts +3 -2
  110. package/src/runtime/execution-ledger.ts +231 -0
  111. package/src/runtime/services.ts +88 -10
  112. package/src/runtime/tool-permission-safety.ts +199 -5
  113. package/src/shell/session-continuity-hints.ts +39 -0
  114. package/src/tools/agent-artifacts-tool.ts +606 -0
  115. package/src/tools/agent-audit-tool.ts +155 -0
  116. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  117. package/src/tools/agent-autonomy-tool.ts +140 -0
  118. package/src/tools/agent-channel-send-tool.ts +24 -2
  119. package/src/tools/agent-channels-tool.ts +140 -0
  120. package/src/tools/agent-computer-tool.ts +168 -0
  121. package/src/tools/agent-context-policy.ts +137 -7
  122. package/src/tools/agent-context-tool.ts +143 -0
  123. package/src/tools/agent-delegation-tool.ts +128 -0
  124. package/src/tools/agent-device-tool.ts +240 -0
  125. package/src/tools/agent-documents-tool.ts +684 -0
  126. package/src/tools/agent-execution-tool.ts +230 -0
  127. package/src/tools/agent-harness-agent-orchestration.ts +703 -0
  128. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  129. package/src/tools/agent-harness-autonomy-live-records.ts +588 -0
  130. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  131. package/src/tools/agent-harness-autonomy-queue.ts +560 -0
  132. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  133. package/src/tools/agent-harness-background-processes.ts +794 -0
  134. package/src/tools/agent-harness-browser-cockpit-route.ts +219 -0
  135. package/src/tools/agent-harness-browser-control.ts +338 -0
  136. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  137. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  138. package/src/tools/agent-harness-command-catalog.ts +4 -2
  139. package/src/tools/agent-harness-command-runner.ts +42 -0
  140. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  141. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  142. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  143. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  144. package/src/tools/agent-harness-document-ops.ts +671 -0
  145. package/src/tools/agent-harness-execution-history.ts +489 -0
  146. package/src/tools/agent-harness-execution-posture.ts +382 -0
  147. package/src/tools/agent-harness-file-recovery.ts +135 -0
  148. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  149. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  150. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  151. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  152. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  153. package/src/tools/agent-harness-learning-curator.ts +417 -0
  154. package/src/tools/agent-harness-local-model-benchmarks.ts +199 -0
  155. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  156. package/src/tools/agent-harness-local-model-endpoints.ts +673 -0
  157. package/src/tools/agent-harness-media-posture.ts +208 -2
  158. package/src/tools/agent-harness-memory-posture.ts +556 -0
  159. package/src/tools/agent-harness-metadata.ts +100 -168
  160. package/src/tools/agent-harness-mode-catalog.ts +84 -33
  161. package/src/tools/agent-harness-model-catalog.ts +162 -0
  162. package/src/tools/agent-harness-model-provider-health.ts +186 -0
  163. package/src/tools/agent-harness-model-readiness.ts +406 -0
  164. package/src/tools/agent-harness-model-routing-types.ts +266 -0
  165. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  166. package/src/tools/agent-harness-model-routing.ts +137 -190
  167. package/src/tools/agent-harness-operator-methods.ts +115 -133
  168. package/src/tools/agent-harness-pairing-posture.ts +431 -18
  169. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  170. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  171. package/src/tools/agent-harness-personal-ops-lanes.ts +204 -0
  172. package/src/tools/agent-harness-personal-ops-records.ts +732 -0
  173. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  174. package/src/tools/agent-harness-personal-ops-types.ts +222 -0
  175. package/src/tools/agent-harness-personal-ops.ts +606 -0
  176. package/src/tools/agent-harness-project-context.ts +144 -0
  177. package/src/tools/agent-harness-prompt-context.ts +589 -0
  178. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  179. package/src/tools/agent-harness-release-evidence.ts +9 -7
  180. package/src/tools/agent-harness-release-readiness.ts +9 -7
  181. package/src/tools/agent-harness-research-briefing.ts +427 -0
  182. package/src/tools/agent-harness-research-queue.ts +250 -0
  183. package/src/tools/agent-harness-research-runs.ts +295 -0
  184. package/src/tools/agent-harness-research-workflow.ts +322 -0
  185. package/src/tools/agent-harness-security-posture.ts +2 -2
  186. package/src/tools/agent-harness-service-posture.ts +9 -9
  187. package/src/tools/agent-harness-session-metadata.ts +3 -3
  188. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  189. package/src/tools/agent-harness-setup-describe.ts +557 -0
  190. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  191. package/src/tools/agent-harness-setup-model-helpers.ts +101 -0
  192. package/src/tools/agent-harness-setup-plan.ts +253 -0
  193. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  194. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  195. package/src/tools/agent-harness-setup-posture.ts +449 -221
  196. package/src/tools/agent-harness-setup-smoke.ts +508 -0
  197. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  198. package/src/tools/agent-harness-tool-schema.ts +120 -7
  199. package/src/tools/agent-harness-tool-types.ts +78 -0
  200. package/src/tools/agent-harness-tool-utils.ts +43 -0
  201. package/src/tools/agent-harness-tool.ts +311 -370
  202. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  203. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  204. package/src/tools/agent-harness-vibe-health.ts +105 -0
  205. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  206. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  207. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  208. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  209. package/src/tools/agent-host-tool.ts +159 -0
  210. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  211. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  212. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  213. package/src/tools/agent-memory-tool.ts +214 -0
  214. package/src/tools/agent-model-compare-export.ts +315 -0
  215. package/src/tools/agent-model-compare-handoff.ts +592 -0
  216. package/src/tools/agent-model-compare-judgment.ts +633 -0
  217. package/src/tools/agent-model-compare-run.ts +722 -0
  218. package/src/tools/agent-model-compare-tool.ts +698 -0
  219. package/src/tools/agent-model-compare-types.ts +191 -0
  220. package/src/tools/agent-model-compare-utils.ts +93 -0
  221. package/src/tools/agent-models-tool.ts +208 -0
  222. package/src/tools/agent-operator-action-tool.ts +1 -1
  223. package/src/tools/agent-operator-method-tool.ts +643 -0
  224. package/src/tools/agent-personal-ops-tool.ts +197 -0
  225. package/src/tools/agent-policy-explanation.ts +415 -0
  226. package/src/tools/agent-research-report-tool.ts +608 -0
  227. package/src/tools/agent-research-runs-tool.ts +434 -0
  228. package/src/tools/agent-research-sources-tool.ts +443 -0
  229. package/src/tools/agent-research-tool.ts +687 -0
  230. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  231. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  232. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  233. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  234. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  235. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  236. package/src/tools/agent-route-planner-helpers.ts +667 -0
  237. package/src/tools/agent-route-planner.ts +185 -0
  238. package/src/tools/agent-route-tool.ts +105 -0
  239. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  240. package/src/tools/agent-schedule-tool.ts +282 -0
  241. package/src/tools/agent-security-tool.ts +145 -0
  242. package/src/tools/agent-sessions-tool.ts +122 -0
  243. package/src/tools/agent-settings-import-tool.ts +104 -0
  244. package/src/tools/agent-settings-tool.ts +176 -0
  245. package/src/tools/agent-setup-tool.ts +226 -0
  246. package/src/tools/agent-support-tool.ts +122 -0
  247. package/src/tools/agent-terminal-process-tools.ts +167 -0
  248. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  249. package/src/tools/agent-tool-policy-guard.ts +106 -85
  250. package/src/tools/agent-vibe-tool.ts +297 -0
  251. package/src/tools/agent-work-plan-tool.ts +265 -6
  252. package/src/tools/agent-workspace-tool.ts +305 -0
  253. package/src/tools/artifact-archive.ts +169 -0
  254. package/src/tools/tool-definition-compaction.ts +36 -0
  255. package/src/version.ts +1 -1
@@ -0,0 +1,214 @@
1
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
2
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
3
+ import type { CommandContext, CommandRegistry } from '../input/command-registry.ts';
4
+ import { createAgentHarnessTool } from './agent-harness-tool.ts';
5
+
6
+ type AgentMemoryAction =
7
+ | 'status'
8
+ | 'provider'
9
+ | 'curator'
10
+ | 'candidate'
11
+ | 'list'
12
+ | 'search'
13
+ | 'get'
14
+ | 'create'
15
+ | 'update'
16
+ | 'review'
17
+ | 'stale'
18
+ | 'delete';
19
+
20
+ interface AgentMemoryToolArgs {
21
+ readonly action?: unknown;
22
+ readonly mode?: unknown;
23
+ readonly id?: unknown;
24
+ readonly recordId?: unknown;
25
+ readonly providerId?: unknown;
26
+ readonly candidateId?: unknown;
27
+ readonly target?: unknown;
28
+ readonly query?: unknown;
29
+ readonly includeParameters?: unknown;
30
+ readonly limit?: unknown;
31
+ readonly cls?: unknown;
32
+ readonly scope?: unknown;
33
+ readonly summary?: unknown;
34
+ readonly detail?: unknown;
35
+ readonly body?: unknown;
36
+ readonly confidence?: unknown;
37
+ readonly tags?: unknown;
38
+ readonly reason?: unknown;
39
+ readonly confirm?: unknown;
40
+ readonly explicitUserRequest?: unknown;
41
+ }
42
+
43
+ interface AgentMemoryToolDeps {
44
+ readonly commandRegistry: CommandRegistry;
45
+ readonly commandContext: CommandContext;
46
+ readonly toolRegistry: ToolRegistry;
47
+ readonly harnessTool?: Tool;
48
+ readonly localRegistryTool?: Tool;
49
+ }
50
+
51
+ function error(message: string): { readonly success: false; readonly error: string } {
52
+ return { success: false, error: message };
53
+ }
54
+
55
+ function readString(value: unknown): string {
56
+ return typeof value === 'string' ? value.trim() : '';
57
+ }
58
+
59
+ function normalizeMemoryAction(value: unknown): AgentMemoryAction | null {
60
+ const action = readString(value).toLowerCase().replace(/-/g, '_');
61
+ if (!action) return null;
62
+ if (action === 'status' || action === 'summary' || action === 'posture' || action === 'memory_posture' || action === 'recall' || action === 'providers') return 'status';
63
+ if (action === 'provider' || action === 'memory_provider' || action === 'embedding' || action === 'external' || action === 'external_provider') return 'provider';
64
+ if (action === 'curator' || action === 'learning' || action === 'learning_curator' || action === 'queue' || action === 'review_queue' || action === 'plan') return 'curator';
65
+ if (action === 'candidate' || action === 'learning_candidate' || action === 'card' || action === 'inspect_candidate') return 'candidate';
66
+ if (action === 'list' || action === 'records' || action === 'memories') return 'list';
67
+ if (action === 'search' || action === 'find' || action === 'lookup') return 'search';
68
+ if (action === 'get' || action === 'show' || action === 'inspect' || action === 'read') return 'get';
69
+ if (action === 'create' || action === 'add' || action === 'remember') return 'create';
70
+ if (action === 'update' || action === 'edit') return 'update';
71
+ if (action === 'review' || action === 'approve') return 'review';
72
+ if (action === 'stale' || action === 'mark_stale') return 'stale';
73
+ if (action === 'delete' || action === 'remove' || action === 'forget') return 'delete';
74
+ return null;
75
+ }
76
+
77
+ function readAction(args: AgentMemoryToolArgs): AgentMemoryAction {
78
+ const explicit = normalizeMemoryAction(args.action) ?? normalizeMemoryAction(args.mode);
79
+ if (explicit) return explicit;
80
+ if (readString(args.providerId)) return 'provider';
81
+ if (readString(args.candidateId)) return 'candidate';
82
+ if (readString(args.id) || readString(args.recordId)) return 'get';
83
+ if (readString(args.query) || readString(args.target)) return 'search';
84
+ return 'status';
85
+ }
86
+
87
+ function compactArgs(entries: Record<string, unknown>): Record<string, unknown> {
88
+ return Object.fromEntries(Object.entries(entries).filter(([, value]) => value !== undefined && value !== ''));
89
+ }
90
+
91
+ function lookupId(args: AgentMemoryToolArgs): string {
92
+ return readString(args.id) || readString(args.recordId);
93
+ }
94
+
95
+ function findRegisteredTool(registry: ToolRegistry, name: string): Tool | null {
96
+ return registry.list().find((tool) => tool.definition.name === name) ?? null;
97
+ }
98
+
99
+ function localMemoryArgs(action: AgentMemoryAction, args: AgentMemoryToolArgs): Record<string, unknown> {
100
+ return compactArgs({
101
+ domain: 'memory',
102
+ action,
103
+ id: lookupId(args),
104
+ query: args.query ?? args.target,
105
+ cls: args.cls,
106
+ scope: args.scope,
107
+ summary: args.summary,
108
+ detail: args.detail,
109
+ body: args.body,
110
+ confidence: args.confidence,
111
+ tags: args.tags,
112
+ reason: args.reason,
113
+ confirm: args.confirm,
114
+ explicitUserRequest: args.explicitUserRequest,
115
+ });
116
+ }
117
+
118
+ export function createAgentMemoryTool(deps: AgentMemoryToolDeps): Tool {
119
+ const harnessTool = deps.harnessTool ?? createAgentHarnessTool({
120
+ commandRegistry: deps.commandRegistry,
121
+ commandContext: deps.commandContext,
122
+ toolRegistry: deps.toolRegistry,
123
+ });
124
+
125
+ return {
126
+ definition: {
127
+ name: 'memory',
128
+ description: 'Inspect memory posture, providers, curator, and records.',
129
+ parameters: {
130
+ type: 'object',
131
+ properties: {
132
+ action: {
133
+ type: 'string',
134
+ enum: ['status', 'provider', 'curator', 'candidate', 'list', 'search', 'get', 'create', 'update', 'review', 'stale', 'delete'],
135
+ description: 'Read memory posture/providers/curator or operate records.',
136
+ },
137
+ mode: { type: 'string', description: 'Alias for action.' },
138
+ id: { type: 'string', description: 'Agent-local memory record id.' },
139
+ recordId: { type: 'string', description: 'Alias for id.' },
140
+ providerId: { type: 'string', description: 'Embedding or external-memory provider id.' },
141
+ candidateId: { type: 'string', description: 'Learning curator candidate id.' },
142
+ target: { type: 'string', description: 'Lookup target or search text.' },
143
+ query: { type: 'string', description: 'Search text.' },
144
+ includeParameters: { type: 'boolean', description: 'Include provider setup contracts or candidate details.' },
145
+ limit: { type: 'number', description: 'Maximum posture, curator, or search rows.' },
146
+ cls: { type: 'string', description: 'Memory class for create/update.' },
147
+ scope: { type: 'string', description: 'Memory scope for create/update.' },
148
+ summary: { type: 'string', description: 'Memory summary for create/update.' },
149
+ detail: { type: 'string', description: 'Memory detail for create/update.' },
150
+ body: { type: 'string', description: 'Alias for detail.' },
151
+ confidence: { type: 'number', description: 'Review confidence, 0-100.' },
152
+ tags: { type: 'array', items: { type: 'string' }, description: 'Memory tags.' },
153
+ reason: { type: 'string', description: 'Reason for stale/delete decisions.' },
154
+ confirm: { type: 'boolean', description: 'Required for deletion and other confirmed memory effects.' },
155
+ explicitUserRequest: { type: 'string', description: 'User request authorizing confirmed memory effects.' },
156
+ },
157
+ additionalProperties: false,
158
+ },
159
+ sideEffects: ['state'],
160
+ concurrency: 'serial',
161
+ },
162
+ execute: async (rawArgs: unknown) => {
163
+ const args = (rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs) ? rawArgs : {}) as AgentMemoryToolArgs;
164
+ const action = readAction(args);
165
+
166
+ if (action === 'status') {
167
+ return harnessTool.execute(compactArgs({
168
+ mode: 'memory_posture',
169
+ query: args.query ?? args.target,
170
+ limit: args.limit,
171
+ includeParameters: args.includeParameters,
172
+ }));
173
+ }
174
+ if (action === 'provider') {
175
+ return harnessTool.execute(compactArgs({
176
+ mode: 'memory_provider',
177
+ providerId: args.providerId ?? args.id,
178
+ target: args.providerId || args.id ? undefined : args.target,
179
+ query: args.providerId || args.id ? undefined : args.query,
180
+ includeParameters: args.includeParameters,
181
+ }));
182
+ }
183
+ if (action === 'curator') {
184
+ return harnessTool.execute(compactArgs({
185
+ mode: 'learning_curator',
186
+ query: args.query ?? args.target,
187
+ limit: args.limit,
188
+ includeParameters: args.includeParameters,
189
+ }));
190
+ }
191
+ if (action === 'candidate') {
192
+ return harnessTool.execute(compactArgs({
193
+ mode: 'learning_candidate',
194
+ candidateId: args.candidateId ?? args.id,
195
+ target: args.candidateId || args.id ? undefined : args.target,
196
+ query: args.candidateId || args.id ? undefined : args.query,
197
+ includeParameters: args.includeParameters,
198
+ }));
199
+ }
200
+
201
+ const localRegistryTool = deps.localRegistryTool ?? findRegisteredTool(deps.toolRegistry, 'agent_local_registry');
202
+ if (!localRegistryTool) return error('Agent-local memory registry is unavailable in this runtime.');
203
+ return localRegistryTool.execute(localMemoryArgs(action, args));
204
+ },
205
+ };
206
+ }
207
+
208
+ export function registerAgentMemoryTool(
209
+ registry: ToolRegistry,
210
+ commandRegistry: CommandRegistry,
211
+ commandContext: CommandContext,
212
+ ): void {
213
+ if (!registry.has('memory')) registry.register(createAgentMemoryTool({ commandRegistry, commandContext, toolRegistry: registry }));
214
+ }
@@ -0,0 +1,315 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import type { AgentModelCompareArtifactStore, LoadedComparisonJudgment, SavedComparisonArtifact, StoredComparison } from './agent-model-compare-types.ts';
3
+ import { DEFAULT_SIDE_BY_SIDE_PREVIEW_BYTES, MAX_HANDOFF_ARTIFACT_BYTES } from './agent-model-compare-types.ts';
4
+ import { formatComparisonDimensionLines, formatUsage, toSavedComparisonArtifact } from './agent-model-compare-run.ts';
5
+ import { isTextLike, readString, previewText } from './agent-model-compare-utils.ts';
6
+
7
+ export function markdownBlock(value: string): string {
8
+ const fence = value.includes('```') ? '~~~~' : '```';
9
+ return `${fence}\n${value || '(empty)'}\n${fence}`;
10
+ }
11
+
12
+ export function comparisonExportMarkdown(comparison: StoredComparison, reveal: boolean): string {
13
+ const lines = [
14
+ '# Blind Model Comparison',
15
+ '',
16
+ `Comparison: ${comparison.comparisonId}`,
17
+ `Created: ${comparison.createdAt}`,
18
+ `Prompt: ${comparison.promptPreview}`,
19
+ `Rubric: ${comparison.rubric || '(none)'}`,
20
+ ...(comparison.sourceArtifact ? [`Source artifact: ${comparison.sourceArtifact.artifactId} (${comparison.sourceArtifact.mimeType}, ${comparison.sourceArtifact.sizeBytes} bytes)`] : []),
21
+ ...(comparison.benchmarkKind ? [`Benchmark: ${comparison.benchmarkKind}`] : []),
22
+ ...(comparison.taskType ? [`Task type: ${comparison.taskType}`] : []),
23
+ ...(comparison.documentId ? [`Document: ${comparison.documentId}`] : []),
24
+ `Reveal included: ${reveal ? 'yes' : 'no'}`,
25
+ '',
26
+ '## Candidates',
27
+ '',
28
+ ];
29
+ for (const candidate of comparison.candidates) {
30
+ lines.push(`### Candidate ${candidate.blindId}`);
31
+ lines.push('');
32
+ lines.push(`Status: ${candidate.status}`);
33
+ lines.push(`Latency: ${candidate.latencyMs}ms`);
34
+ if (reveal) lines.push(`Model: ${candidate.model.registryKey} (${candidate.model.displayName})`);
35
+ if (candidate.status === 'failed') {
36
+ lines.push(`Error: ${reveal ? candidate.error ?? 'unknown' : 'Provider-specific error hidden until reveal.'}`);
37
+ lines.push('');
38
+ continue;
39
+ }
40
+ lines.push(`Stop: ${candidate.stopReason ?? 'unknown'}`);
41
+ lines.push(`Usage: ${formatUsage(candidate.usage)}`);
42
+ lines.push('');
43
+ lines.push(markdownBlock(candidate.content));
44
+ lines.push('');
45
+ }
46
+ lines.push('## Decision Worksheet');
47
+ lines.push('');
48
+ lines.push('- Winner:');
49
+ lines.push('- Reasons:');
50
+ lines.push('- Risks:');
51
+ lines.push('');
52
+ return lines.join('\n');
53
+ }
54
+
55
+ export function judgmentExportMarkdown(judgment: LoadedComparisonJudgment): string {
56
+ const lines = [
57
+ '# Blind Model Comparison Judgment',
58
+ '',
59
+ `Judgment: ${judgment.judgmentId}`,
60
+ `Comparison: ${judgment.comparisonId}`,
61
+ `Winner: Candidate ${judgment.winnerBlindId}`,
62
+ ...(judgment.winnerModel ? [`Winner model: ${judgment.winnerModel.registryKey} (${judgment.winnerModel.displayName})`] : ['Winner model: (not revealed in judgment)']),
63
+ '',
64
+ '## Reasons',
65
+ '',
66
+ judgment.reasons || '(none)',
67
+ '',
68
+ ];
69
+ if (judgment.notes) {
70
+ lines.push('## Notes');
71
+ lines.push('');
72
+ lines.push(judgment.notes);
73
+ lines.push('');
74
+ }
75
+ lines.push('## Route Update');
76
+ lines.push('');
77
+ if (judgment.winnerModel) {
78
+ lines.push(`Confirmed apply route: agent_model_compare mode:"apply" artifactId:"${judgment.artifact.artifactId}" confirm:true explicitUserRequest:"..."`);
79
+ } else {
80
+ lines.push('Save or reveal the judgment before applying a model route update.');
81
+ }
82
+ lines.push('');
83
+ return lines.join('\n');
84
+ }
85
+
86
+ export async function saveComparisonExportArtifact(input: {
87
+ readonly artifactStore?: AgentModelCompareArtifactStore;
88
+ readonly sourceArtifactId: string;
89
+ readonly sourceKind: 'comparison' | 'judgment';
90
+ readonly comparisonId: string;
91
+ readonly markdown: string;
92
+ readonly reveal: boolean;
93
+ }): Promise<SavedComparisonArtifact> {
94
+ if (!input.artifactStore) throw new Error('Cannot export comparison because the artifact store is unavailable.');
95
+ const exportId = `exp_${randomUUID()}`;
96
+ const descriptor = await input.artifactStore.create({
97
+ kind: 'data',
98
+ mimeType: 'text/markdown',
99
+ filename: `blind-model-comparison-export-${exportId}.md`,
100
+ text: input.markdown,
101
+ metadata: {
102
+ purpose: 'agent-model-compare-export',
103
+ exportId,
104
+ sourceArtifactId: input.sourceArtifactId,
105
+ sourceKind: input.sourceKind,
106
+ comparisonId: input.comparisonId,
107
+ revealIncludedInExport: input.reveal,
108
+ },
109
+ });
110
+ return toSavedComparisonArtifact(descriptor);
111
+ }
112
+
113
+ export async function loadHandoffRelatedArtifacts(
114
+ artifactStore: AgentModelCompareArtifactStore | undefined,
115
+ artifactIds: readonly string[],
116
+ maxBytes = MAX_HANDOFF_ARTIFACT_BYTES,
117
+ ): Promise<readonly { readonly artifact: SavedComparisonArtifact; readonly text?: string; readonly truncatedBytes: number }[]> {
118
+ if (!artifactStore?.readContent) {
119
+ throw new Error('Reviewer handoff requires an artifact store with readContent support.');
120
+ }
121
+ const related: { readonly artifact: SavedComparisonArtifact; readonly text?: string; readonly truncatedBytes: number }[] = [];
122
+ for (const artifactId of artifactIds) {
123
+ const { record, buffer } = await artifactStore.readContent(artifactId);
124
+ const artifact = toSavedComparisonArtifact(record);
125
+ if (!isTextLike(record.mimeType)) {
126
+ related.push({ artifact, truncatedBytes: 0 });
127
+ continue;
128
+ }
129
+ const sliced = buffer.subarray(0, Math.min(buffer.byteLength, maxBytes));
130
+ related.push({
131
+ artifact,
132
+ text: sliced.toString('utf-8').replace(/\0/g, '').trimEnd(),
133
+ truncatedBytes: Math.max(0, buffer.byteLength - sliced.byteLength),
134
+ });
135
+ }
136
+ return related;
137
+ }
138
+
139
+ export function comparisonHandoffMarkdown(input: {
140
+ readonly sourceKind: 'comparison' | 'judgment';
141
+ readonly sourceArtifactId: string;
142
+ readonly comparisonId: string;
143
+ readonly comparisonMarkdown: string;
144
+ readonly reveal: boolean;
145
+ readonly relatedArtifacts: readonly { readonly artifact: SavedComparisonArtifact; readonly text?: string; readonly truncatedBytes: number }[];
146
+ }): string {
147
+ const lines = [
148
+ '# Blind Model Comparison Reviewer Handoff',
149
+ '',
150
+ `Comparison: ${input.comparisonId}`,
151
+ `Comparison artifact: ${input.sourceArtifactId}`,
152
+ `Comparison artifact kind: ${input.sourceKind}`,
153
+ `Reveal included: ${input.reveal ? 'yes' : 'no'}`,
154
+ `Related artifacts: ${input.relatedArtifacts.length}`,
155
+ '',
156
+ '## Handoff Policy',
157
+ '',
158
+ '- This packet is local reviewer evidence.',
159
+ '- It does not change model routing.',
160
+ '- Route changes require a separate confirmed `agent_model_compare mode:"apply"` call.',
161
+ '',
162
+ '## Related Artifacts',
163
+ '',
164
+ ];
165
+ for (const related of input.relatedArtifacts) {
166
+ lines.push(`### ${related.artifact.artifactId}`);
167
+ lines.push('');
168
+ lines.push(`Filename: ${related.artifact.filename ?? '(none)'}`);
169
+ lines.push(`MIME: ${related.artifact.mimeType}`);
170
+ lines.push(`Size: ${related.artifact.sizeBytes} bytes`);
171
+ lines.push('');
172
+ if (related.text === undefined) {
173
+ lines.push('_Content omitted for non-text artifact; binary/base64 bytes were not included._');
174
+ } else {
175
+ lines.push(markdownBlock(related.text || '(empty text artifact)'));
176
+ if (related.truncatedBytes > 0) {
177
+ lines.push('');
178
+ lines.push(`_Truncated ${related.truncatedBytes} byte(s) from this artifact preview._`);
179
+ }
180
+ }
181
+ lines.push('');
182
+ }
183
+ lines.push('## Comparison Evidence');
184
+ lines.push('');
185
+ lines.push(input.comparisonMarkdown);
186
+ lines.push('');
187
+ return lines.join('\n');
188
+ }
189
+
190
+ export function formatRelatedArtifactEvidence(input: {
191
+ readonly artifacts: readonly { readonly artifact: SavedComparisonArtifact; readonly text?: string; readonly truncatedBytes: number }[];
192
+ readonly previewBytes: number;
193
+ }): readonly string[] {
194
+ if (input.artifacts.length === 0) return [' No related artifacts were provided.'];
195
+ const lines: string[] = [];
196
+ for (const related of input.artifacts) {
197
+ lines.push(` ${related.artifact.artifactId}${related.artifact.filename ? ` ${related.artifact.filename}` : ''} (${related.artifact.mimeType}, ${related.artifact.sizeBytes} bytes)`);
198
+ if (related.text === undefined) {
199
+ lines.push(' non-text artifact; content omitted');
200
+ continue;
201
+ }
202
+ const excerpt = previewText(related.text || '(empty text artifact)', input.previewBytes)
203
+ .split('\n')
204
+ .map((line) => ` ${line}`)
205
+ .join('\n');
206
+ lines.push(excerpt);
207
+ if (related.truncatedBytes > 0) lines.push(` truncated ${related.truncatedBytes} byte(s)`);
208
+ }
209
+ return lines;
210
+ }
211
+
212
+ export function formatComparisonEvidencePane(input: {
213
+ readonly comparison?: StoredComparison;
214
+ readonly judgment?: LoadedComparisonJudgment;
215
+ readonly reveal: boolean;
216
+ readonly previewBytes: number;
217
+ }): readonly string[] {
218
+ if (input.judgment) {
219
+ const judgment = input.judgment;
220
+ return [
221
+ ` judgment ${judgment.judgmentId}`,
222
+ ` comparison ${judgment.comparisonId}`,
223
+ ` winner Candidate ${judgment.winnerBlindId}`,
224
+ ` winner model ${judgment.winnerModel ? `${judgment.winnerModel.registryKey} (${judgment.winnerModel.displayName})` : '(not revealed)'}`,
225
+ ...(judgment.benchmarkKind ? [` benchmark ${judgment.benchmarkKind}`] : []),
226
+ ...(judgment.taskType ? [` task type ${judgment.taskType}`] : []),
227
+ ...(judgment.documentId ? [` document ${judgment.documentId}`] : []),
228
+ ` reasons ${previewText(judgment.reasons || '(none)', input.previewBytes)}`,
229
+ ...(judgment.notes ? [` notes ${previewText(judgment.notes, input.previewBytes)}`] : []),
230
+ ];
231
+ }
232
+ const comparison = input.comparison;
233
+ if (!comparison) return [' No comparison evidence loaded.'];
234
+ const lines = [
235
+ ` comparison ${comparison.comparisonId}`,
236
+ ` prompt ${comparison.promptPreview}`,
237
+ ` rubric ${comparison.rubric || '(none)'}`,
238
+ ...formatComparisonDimensionLines(comparison).map((line) => ` ${line}`),
239
+ ` candidates ${comparison.candidates.length}`,
240
+ ];
241
+ for (const candidate of comparison.candidates) {
242
+ lines.push(` Candidate ${candidate.blindId}: ${candidate.status}; latency ${candidate.latencyMs}ms`);
243
+ if (input.reveal) lines.push(` model ${candidate.model.registryKey} (${candidate.model.displayName})`);
244
+ if (candidate.status === 'failed') {
245
+ lines.push(` error ${input.reveal ? candidate.error ?? 'unknown' : 'Provider-specific error hidden until reveal.'}`);
246
+ continue;
247
+ }
248
+ lines.push(` output ${previewText(candidate.content || '(empty)', input.previewBytes)}`);
249
+ }
250
+ return lines;
251
+ }
252
+
253
+ export function formatSideBySideReview(input: {
254
+ readonly sourceKind: 'comparison' | 'judgment';
255
+ readonly sourceArtifactId: string;
256
+ readonly comparisonId: string;
257
+ readonly comparison?: StoredComparison;
258
+ readonly judgment?: LoadedComparisonJudgment;
259
+ readonly reveal: boolean;
260
+ readonly relatedArtifacts: readonly { readonly artifact: SavedComparisonArtifact; readonly text?: string; readonly truncatedBytes: number }[];
261
+ readonly previewBytes: number;
262
+ }): string {
263
+ return [
264
+ 'Blind model comparison side-by-side reviewer view',
265
+ `comparison ${input.comparisonId}`,
266
+ `source ${input.sourceArtifactId} (${input.sourceKind})`,
267
+ `related artifacts ${input.relatedArtifacts.length}`,
268
+ `preview bytes ${input.previewBytes}`,
269
+ '',
270
+ 'Left pane: related document/artifact evidence',
271
+ ...formatRelatedArtifactEvidence({ artifacts: input.relatedArtifacts, previewBytes: input.previewBytes }),
272
+ '',
273
+ 'Right pane: comparison evidence',
274
+ ...formatComparisonEvidencePane({
275
+ comparison: input.comparison,
276
+ judgment: input.judgment,
277
+ reveal: input.reveal,
278
+ previewBytes: input.previewBytes,
279
+ }),
280
+ '',
281
+ 'Reviewer next actions',
282
+ ` create handoff agent_model_compare mode:"handoff" artifactId:"${input.sourceArtifactId}" relatedArtifactIds:${JSON.stringify(input.relatedArtifacts.map((entry) => entry.artifact.artifactId))} confirm:true explicitUserRequest:"..."`,
283
+ ` export report agent_model_compare mode:"export" artifactId:"${input.sourceArtifactId}" confirm:true explicitUserRequest:"..."`,
284
+ 'No selected model was changed.',
285
+ ].join('\n');
286
+ }
287
+
288
+ export async function saveComparisonHandoffArtifact(input: {
289
+ readonly artifactStore?: AgentModelCompareArtifactStore;
290
+ readonly sourceArtifactId: string;
291
+ readonly sourceKind: 'comparison' | 'judgment';
292
+ readonly comparisonId: string;
293
+ readonly relatedArtifactIds: readonly string[];
294
+ readonly markdown: string;
295
+ readonly reveal: boolean;
296
+ }): Promise<SavedComparisonArtifact> {
297
+ if (!input.artifactStore) throw new Error('Cannot create reviewer handoff because the artifact store is unavailable.');
298
+ const handoffId = `hnd_${randomUUID()}`;
299
+ const descriptor = await input.artifactStore.create({
300
+ kind: 'data',
301
+ mimeType: 'text/markdown',
302
+ filename: `blind-model-comparison-handoff-${handoffId}.md`,
303
+ text: input.markdown,
304
+ metadata: {
305
+ purpose: 'agent-model-compare-handoff',
306
+ handoffId,
307
+ sourceArtifactId: input.sourceArtifactId,
308
+ sourceKind: input.sourceKind,
309
+ relatedArtifactIds: input.relatedArtifactIds,
310
+ comparisonId: input.comparisonId,
311
+ revealIncludedInHandoff: input.reveal,
312
+ },
313
+ });
314
+ return toSavedComparisonArtifact(descriptor);
315
+ }