@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,159 @@
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 AgentHostAction =
7
+ | 'status'
8
+ | 'capabilities'
9
+ | 'capability'
10
+ | 'services'
11
+ | 'service'
12
+ | 'methods'
13
+ | 'method';
14
+
15
+ interface AgentHostToolArgs {
16
+ readonly action?: unknown;
17
+ readonly mode?: unknown;
18
+ readonly capabilityId?: unknown;
19
+ readonly endpointId?: unknown;
20
+ readonly methodId?: unknown;
21
+ readonly target?: unknown;
22
+ readonly query?: unknown;
23
+ readonly includeParameters?: unknown;
24
+ readonly limit?: unknown;
25
+ }
26
+
27
+ interface AgentHostToolDeps {
28
+ readonly commandRegistry: CommandRegistry;
29
+ readonly commandContext: CommandContext;
30
+ readonly toolRegistry: ToolRegistry;
31
+ readonly harnessTool?: Tool;
32
+ }
33
+
34
+ function readString(value: unknown): string {
35
+ return typeof value === 'string' ? value.trim() : '';
36
+ }
37
+
38
+ function normalizeHostAction(value: unknown): AgentHostAction | null {
39
+ const action = readString(value).toLowerCase().replace(/-/g, '_');
40
+ if (!action) return null;
41
+ if (action === 'status' || action === 'health' || action === 'readiness' || action === 'daemon_status' || action === 'connected_host_status') return 'status';
42
+ if (action === 'capabilities' || action === 'capability_map' || action === 'map' || action === 'inventory' || action === 'connected_host' || action === 'daemon' || action === 'host' || action === 'boundaries') return 'capabilities';
43
+ if (action === 'capability' || action === 'show_capability' || action === 'inspect_capability' || action === 'connected_host_capability') return 'capability';
44
+ if (action === 'services' || action === 'service_posture' || action === 'service_status' || action === 'endpoints' || action === 'endpoint_list') return 'services';
45
+ if (action === 'service' || action === 'endpoint' || action === 'service_endpoint') return 'service';
46
+ if (action === 'methods' || action === 'operator_methods' || action === 'operator_contract' || action === 'operations' || action === 'actions') return 'methods';
47
+ if (action === 'method' || action === 'operator_method' || action === 'operation') return 'method';
48
+ return null;
49
+ }
50
+
51
+ function readAction(args: AgentHostToolArgs): AgentHostAction {
52
+ const explicit = normalizeHostAction(args.action) ?? normalizeHostAction(args.mode);
53
+ if (explicit) return explicit;
54
+ if (readString(args.methodId)) return 'method';
55
+ if (readString(args.endpointId)) return 'service';
56
+ if (readString(args.capabilityId) || readString(args.target) || readString(args.query)) return 'capability';
57
+ return 'status';
58
+ }
59
+
60
+ function compactArgs(entries: Record<string, unknown>): Record<string, unknown> {
61
+ return Object.fromEntries(Object.entries(entries).filter(([, value]) => value !== undefined && value !== ''));
62
+ }
63
+
64
+ export function createAgentHostTool(deps: AgentHostToolDeps): Tool {
65
+ const harnessTool = deps.harnessTool ?? createAgentHarnessTool({
66
+ commandRegistry: deps.commandRegistry,
67
+ commandContext: deps.commandContext,
68
+ toolRegistry: deps.toolRegistry,
69
+ });
70
+
71
+ return {
72
+ definition: {
73
+ name: 'host',
74
+ description: 'Inspect GoodVibes host status, services, and methods.',
75
+ parameters: {
76
+ type: 'object',
77
+ properties: {
78
+ action: {
79
+ type: 'string',
80
+ enum: ['status', 'capabilities', 'capability', 'services', 'service', 'methods', 'method'],
81
+ description: 'Read host status, capabilities, services, and method posture.',
82
+ },
83
+ mode: { type: 'string', description: 'Alias for action.' },
84
+ capabilityId: { type: 'string', description: 'Connected-host capability id.' },
85
+ endpointId: { type: 'string', description: 'Service endpoint id.' },
86
+ methodId: { type: 'string', description: 'Daemon operator method id.' },
87
+ target: { type: 'string', description: 'Lookup target for a capability, endpoint, or method.' },
88
+ query: { type: 'string', description: 'Search text for capabilities or methods.' },
89
+ includeParameters: { type: 'boolean', description: 'Include detailed method schemas, route families, probes, or diagnostics.' },
90
+ limit: { type: 'number', description: 'Maximum methods to return for action:methods.' },
91
+ },
92
+ additionalProperties: false,
93
+ },
94
+ sideEffects: [],
95
+ concurrency: 'parallel',
96
+ },
97
+ execute: async (rawArgs: unknown) => {
98
+ const args = (rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs) ? rawArgs : {}) as AgentHostToolArgs;
99
+ const action = readAction(args);
100
+
101
+ if (action === 'status') {
102
+ return harnessTool.execute(compactArgs({
103
+ mode: 'connected_host_status',
104
+ includeParameters: args.includeParameters,
105
+ }));
106
+ }
107
+ if (action === 'capabilities') {
108
+ return harnessTool.execute(compactArgs({
109
+ mode: 'connected_host',
110
+ includeParameters: args.includeParameters,
111
+ }));
112
+ }
113
+ if (action === 'capability') {
114
+ return harnessTool.execute(compactArgs({
115
+ mode: 'connected_host_capability',
116
+ capabilityId: args.capabilityId,
117
+ target: args.target,
118
+ query: args.query,
119
+ }));
120
+ }
121
+ if (action === 'services') {
122
+ return harnessTool.execute(compactArgs({
123
+ mode: 'service_posture',
124
+ includeParameters: args.includeParameters,
125
+ }));
126
+ }
127
+ if (action === 'service') {
128
+ return harnessTool.execute(compactArgs({
129
+ mode: 'service_endpoint',
130
+ endpointId: args.endpointId,
131
+ target: args.target,
132
+ query: args.query,
133
+ }));
134
+ }
135
+ if (action === 'methods') {
136
+ return harnessTool.execute(compactArgs({
137
+ mode: 'operator_methods',
138
+ query: args.query ?? args.target,
139
+ includeParameters: args.includeParameters,
140
+ limit: args.limit,
141
+ }));
142
+ }
143
+ return harnessTool.execute(compactArgs({
144
+ mode: 'operator_method',
145
+ methodId: args.methodId,
146
+ target: args.target,
147
+ query: args.query,
148
+ }));
149
+ },
150
+ };
151
+ }
152
+
153
+ export function registerAgentHostTool(
154
+ registry: ToolRegistry,
155
+ commandRegistry: CommandRegistry,
156
+ commandContext: CommandContext,
157
+ ): void {
158
+ if (!registry.has('host')) registry.register(createAgentHostTool({ commandRegistry, commandContext, toolRegistry: registry }));
159
+ }
@@ -14,6 +14,7 @@ import { formatBatchIngest, formatFailure, formatIngest } from '../cli/agent-kno
14
14
  export type AgentKnowledgeIngestSourceKind =
15
15
  | 'url'
16
16
  | 'file'
17
+ | 'artifact'
17
18
  | 'urls_file'
18
19
  | 'bookmarks_file'
19
20
  | 'browser_history'
@@ -23,6 +24,7 @@ export interface AgentKnowledgeIngestToolArgs {
23
24
  readonly sourceKind?: unknown;
24
25
  readonly url?: unknown;
25
26
  readonly path?: unknown;
27
+ readonly artifactId?: unknown;
26
28
  readonly title?: unknown;
27
29
  readonly tags?: unknown;
28
30
  readonly folderPath?: unknown;
@@ -44,6 +46,7 @@ type JsonRecord = Record<string, unknown>;
44
46
  interface AgentKnowledgeIngestRequest {
45
47
  readonly method: typeof AGENT_KNOWLEDGE_METHODS[keyof typeof AGENT_KNOWLEDGE_METHODS];
46
48
  readonly target: string;
49
+ readonly targetLabel: string;
47
50
  readonly label: string;
48
51
  readonly payload: JsonRecord;
49
52
  readonly batch: boolean;
@@ -52,6 +55,7 @@ interface AgentKnowledgeIngestRequest {
52
55
  const SOURCE_KINDS: readonly AgentKnowledgeIngestSourceKind[] = [
53
56
  'url',
54
57
  'file',
58
+ 'artifact',
55
59
  'urls_file',
56
60
  'bookmarks_file',
57
61
  'browser_history',
@@ -161,6 +165,7 @@ function requestForArgs(args: AgentKnowledgeIngestToolArgs): AgentKnowledgeInges
161
165
  return {
162
166
  method: AGENT_KNOWLEDGE_METHODS.ingestUrl,
163
167
  target: url,
168
+ targetLabel: 'url',
164
169
  label: 'URL ingest',
165
170
  batch: false,
166
171
  payload: {
@@ -180,6 +185,7 @@ function requestForArgs(args: AgentKnowledgeIngestToolArgs): AgentKnowledgeInges
180
185
  return {
181
186
  method: AGENT_KNOWLEDGE_METHODS.ingestArtifact,
182
187
  target: path,
188
+ targetLabel: 'file',
183
189
  label: 'file ingest',
184
190
  batch: false,
185
191
  payload: {
@@ -194,6 +200,26 @@ function requestForArgs(args: AgentKnowledgeIngestToolArgs): AgentKnowledgeInges
194
200
  };
195
201
  }
196
202
 
203
+ if (sourceKind === 'artifact') {
204
+ const artifactId = readString(args.artifactId);
205
+ if (!artifactId) throw new Error('artifactId is required.');
206
+ return {
207
+ method: AGENT_KNOWLEDGE_METHODS.ingestArtifact,
208
+ target: artifactId,
209
+ targetLabel: 'artifact',
210
+ label: 'artifact ingest',
211
+ batch: false,
212
+ payload: {
213
+ artifactId,
214
+ title,
215
+ tags: [...tags],
216
+ folderPath: readOptionalString(args.folderPath),
217
+ connectorId: readOptionalString(args.connectorId) ?? 'goodvibes-agent-artifact-browser',
218
+ metadata: originMetadata,
219
+ },
220
+ };
221
+ }
222
+
197
223
  if (sourceKind === 'urls_file' || sourceKind === 'bookmarks_file') {
198
224
  const path = readString(args.path);
199
225
  if (!path) throw new Error('path is required.');
@@ -201,6 +227,7 @@ function requestForArgs(args: AgentKnowledgeIngestToolArgs): AgentKnowledgeInges
201
227
  return {
202
228
  method,
203
229
  target: path,
230
+ targetLabel: 'file',
204
231
  label: sourceKind === 'urls_file' ? 'URL-list import' : 'bookmarks import',
205
232
  batch: true,
206
233
  payload: {
@@ -215,6 +242,7 @@ function requestForArgs(args: AgentKnowledgeIngestToolArgs): AgentKnowledgeInges
215
242
  return {
216
243
  method: AGENT_KNOWLEDGE_METHODS.ingestBrowserHistory,
217
244
  target: 'local browser history',
245
+ targetLabel: 'source',
218
246
  label: 'browser-history import',
219
247
  batch: true,
220
248
  payload: {
@@ -240,6 +268,7 @@ function requestForArgs(args: AgentKnowledgeIngestToolArgs): AgentKnowledgeInges
240
268
  return {
241
269
  method: AGENT_KNOWLEDGE_METHODS.ingestConnector,
242
270
  target: connectorId,
271
+ targetLabel: 'connector',
243
272
  label: 'connector ingest',
244
273
  batch: true,
245
274
  payload: {
@@ -278,6 +307,10 @@ export function createAgentKnowledgeIngestTool(
278
307
  type: 'string',
279
308
  description: 'Local file or import path.',
280
309
  },
310
+ artifactId: {
311
+ type: 'string',
312
+ description: 'Saved artifact id when sourceKind is artifact.',
313
+ },
281
314
  title: {
282
315
  type: 'string',
283
316
  description: 'Optional source title for url or file ingest.',
@@ -381,7 +414,7 @@ export function createAgentKnowledgeIngestTool(
381
414
  request.target,
382
415
  request.label,
383
416
  request.method.route,
384
- request.label === 'file ingest' ? 'file' : 'url',
417
+ request.targetLabel,
385
418
  ));
386
419
  } catch (error) {
387
420
  return failure(formatKnowledgeFailure(await classifyKnowledgeError(error, connection, request.method.route)));
@@ -0,0 +1,327 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
4
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
5
+ import type { MemoryBundle, MemoryRecord, MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
6
+ import type { ShellPathService } from '@/runtime/index.ts';
7
+ import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
8
+ import type { CommandContext } from '../input/command-registry.ts';
9
+ import { AgentPersonaRegistry, type AgentPersonaRecord } from '../agent/persona-registry.ts';
10
+ import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
11
+ import { AgentSkillRegistry, type AgentSkillRecord } from '../agent/skill-registry.ts';
12
+ import {
13
+ resolveLearningConsolidationCandidate,
14
+ type LearningCandidate,
15
+ type LearningConsolidationFields,
16
+ } from './agent-harness-learning-curator.ts';
17
+
18
+ export type AgentLearningConsolidationMode = 'preview' | 'merge' | 'stale' | 'delete' | 'rollback' | 'recreate' | 'receipts';
19
+ export type AgentLearningConsolidationDomain = 'memory' | 'persona' | 'skill' | 'routine';
20
+ export type AgentLearningConsolidationWriteMode = 'merge' | 'stale' | 'delete' | 'rollback' | 'recreate';
21
+
22
+ export interface AgentLearningConsolidationToolArgs {
23
+ readonly mode?: unknown;
24
+ readonly candidateId?: unknown;
25
+ readonly query?: unknown;
26
+ readonly receiptId?: unknown;
27
+ readonly limit?: unknown;
28
+ readonly confirm?: unknown;
29
+ readonly explicitUserRequest?: unknown;
30
+ }
31
+
32
+ export interface LearningConsolidationReceipt {
33
+ readonly id: string;
34
+ readonly createdAt: string;
35
+ readonly domain: AgentLearningConsolidationDomain;
36
+ readonly candidateId: string;
37
+ readonly phase: AgentLearningConsolidationWriteMode;
38
+ readonly explicitUserRequest: string;
39
+ readonly survivorId: string;
40
+ readonly duplicateIds: readonly string[];
41
+ readonly beforeSurvivor?: Record<string, unknown>;
42
+ readonly beforeDuplicates: readonly Record<string, unknown>[];
43
+ }
44
+
45
+ export interface LearningConsolidationReceiptFile {
46
+ readonly version: 1;
47
+ readonly receipts: readonly LearningConsolidationReceipt[];
48
+ }
49
+
50
+ export const MODES: readonly AgentLearningConsolidationMode[] = ['preview', 'merge', 'stale', 'delete', 'rollback', 'recreate', 'receipts'];
51
+ export const WRITE_MODES = new Set<AgentLearningConsolidationMode>(['merge', 'stale', 'delete', 'rollback', 'recreate']);
52
+ export const RECEIPT_LIMIT = 100;
53
+ export const MEMORY_CLASSES = new Set(['decision', 'constraint', 'incident', 'pattern', 'fact', 'risk', 'runbook', 'architecture', 'ownership']);
54
+ export const MEMORY_SCOPES = new Set(['session', 'project', 'team']);
55
+ export const MEMORY_REVIEW_STATES = new Set(['fresh', 'reviewed', 'stale', 'contradicted']);
56
+
57
+ export function readString(value: unknown): string {
58
+ return typeof value === 'string' ? value.trim() : '';
59
+ }
60
+
61
+ export function readMode(value: unknown): AgentLearningConsolidationMode {
62
+ return typeof value === 'string' && MODES.includes(value as AgentLearningConsolidationMode)
63
+ ? value as AgentLearningConsolidationMode
64
+ : 'preview';
65
+ }
66
+
67
+ export function readLimit(value: unknown, fallback: number): number {
68
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
69
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
70
+ return Math.max(1, Math.min(RECEIPT_LIMIT, Math.trunc(parsed)));
71
+ }
72
+
73
+ function slugifyForDomain(value: string, fallback: AgentLearningConsolidationDomain): string {
74
+ const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
75
+ return slug || fallback;
76
+ }
77
+
78
+ export function nextGeneratedId(name: string, occupiedIds: Set<string>, fallback: AgentLearningConsolidationDomain): string {
79
+ const base = slugifyForDomain(name, fallback);
80
+ if (!occupiedIds.has(base)) return base;
81
+ for (let index = 2; index < 1000; index += 1) {
82
+ const candidate = `${base}-${index}`;
83
+ if (!occupiedIds.has(candidate)) return candidate;
84
+ }
85
+ throw new Error(`Could not allocate ${fallback} id for ${name}.`);
86
+ }
87
+
88
+ export function output(value: unknown): { readonly success: true; readonly output: string } {
89
+ return { success: true, output: JSON.stringify(value, null, 2) };
90
+ }
91
+
92
+ export function failure(error: string): { readonly success: false; readonly error: string } {
93
+ return { success: false, error };
94
+ }
95
+
96
+ export function requireConfirmedWrite(args: AgentLearningConsolidationToolArgs, mode: AgentLearningConsolidationWriteMode): string {
97
+ const explicitUserRequest = readString(args.explicitUserRequest);
98
+ if (!explicitUserRequest) throw new Error(`${mode} requires explicitUserRequest with the user's exact request or a short faithful summary.`);
99
+ if (args.confirm !== true) throw new Error(`${mode} requires confirm:true after an explicit user request.`);
100
+ return explicitUserRequest;
101
+ }
102
+
103
+ function isDomain(value: string): value is AgentLearningConsolidationDomain {
104
+ return value === 'memory' || value === 'persona' || value === 'skill' || value === 'routine';
105
+ }
106
+
107
+ export function receiptPath(shellPaths: ShellPathService): string {
108
+ return shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'learning', 'consolidation-receipts.json');
109
+ }
110
+
111
+ function parseReceipt(value: unknown): LearningConsolidationReceipt | null {
112
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
113
+ const record = value as Record<string, unknown>;
114
+ const id = readString(record.id);
115
+ const createdAt = readString(record.createdAt);
116
+ const domain = readString(record.domain);
117
+ const candidateId = readString(record.candidateId);
118
+ const phase = readString(record.phase);
119
+ const explicitUserRequest = readString(record.explicitUserRequest);
120
+ const survivorId = readString(record.survivorId);
121
+ const duplicateIds = Array.isArray(record.duplicateIds)
122
+ ? record.duplicateIds.filter((entry): entry is string => typeof entry === 'string')
123
+ : [];
124
+ const beforeDuplicates = Array.isArray(record.beforeDuplicates)
125
+ ? record.beforeDuplicates.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null && !Array.isArray(entry))
126
+ : [];
127
+ if (
128
+ !id
129
+ || !createdAt
130
+ || !isDomain(domain)
131
+ || !candidateId
132
+ || !['merge', 'stale', 'delete', 'rollback', 'recreate'].includes(phase)
133
+ || !explicitUserRequest
134
+ || !survivorId
135
+ ) return null;
136
+ const beforeSurvivor = typeof record.beforeSurvivor === 'object' && record.beforeSurvivor !== null && !Array.isArray(record.beforeSurvivor)
137
+ ? record.beforeSurvivor as Record<string, unknown>
138
+ : undefined;
139
+ return {
140
+ id,
141
+ createdAt,
142
+ domain,
143
+ candidateId,
144
+ phase: phase as AgentLearningConsolidationWriteMode,
145
+ explicitUserRequest,
146
+ survivorId,
147
+ duplicateIds,
148
+ ...(beforeSurvivor ? { beforeSurvivor } : {}),
149
+ beforeDuplicates,
150
+ };
151
+ }
152
+
153
+ export function readReceiptFile(shellPaths: ShellPathService): LearningConsolidationReceiptFile {
154
+ const path = receiptPath(shellPaths);
155
+ if (!existsSync(path)) return { version: 1, receipts: [] };
156
+ try {
157
+ const parsed: unknown = JSON.parse(readFileSync(path, 'utf-8'));
158
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return { version: 1, receipts: [] };
159
+ const receipts = Array.isArray((parsed as Record<string, unknown>).receipts)
160
+ ? ((parsed as Record<string, unknown>).receipts as unknown[]).map(parseReceipt).filter((entry): entry is LearningConsolidationReceipt => entry !== null)
161
+ : [];
162
+ return { version: 1, receipts };
163
+ } catch {
164
+ return { version: 1, receipts: [] };
165
+ }
166
+ }
167
+
168
+ export function writeReceipt(shellPaths: ShellPathService, receipt: LearningConsolidationReceipt): void {
169
+ const path = receiptPath(shellPaths);
170
+ const current = readReceiptFile(shellPaths).receipts;
171
+ const next: LearningConsolidationReceiptFile = {
172
+ version: 1,
173
+ receipts: [receipt, ...current.filter((entry) => entry.id !== receipt.id)].slice(0, RECEIPT_LIMIT),
174
+ };
175
+ mkdirSync(dirname(path), { recursive: true });
176
+ const tmpPath = `${path}.tmp`;
177
+ writeFileSync(tmpPath, `${JSON.stringify(next, null, 2)}\n`, 'utf-8');
178
+ renameSync(tmpPath, path);
179
+ }
180
+
181
+ export function receiptId(candidate: LearningCandidate, phase: AgentLearningConsolidationWriteMode): string {
182
+ const slug = candidate.id.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 48) || 'candidate';
183
+ return `lcon-${phase}-${Date.now().toString(36)}-${slug}`;
184
+ }
185
+
186
+ export function receiptIdFromReceipt(receipt: LearningConsolidationReceipt, phase: AgentLearningConsolidationWriteMode): string {
187
+ const slug = receipt.candidateId.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 48) || 'receipt';
188
+ return `lcon-${phase}-${Date.now().toString(36)}-${slug}`;
189
+ }
190
+
191
+ function minimalContext(shellPaths: ShellPathService, memoryRegistry: MemoryRegistry): CommandContext {
192
+ return {
193
+ workspace: { shellPaths },
194
+ clients: { agentKnowledgeApi: { memory: memoryRegistry } },
195
+ } as unknown as CommandContext;
196
+ }
197
+
198
+ export function resolveCandidate(
199
+ shellPaths: ShellPathService,
200
+ memoryRegistry: MemoryRegistry,
201
+ args: AgentLearningConsolidationToolArgs,
202
+ ): LearningCandidate {
203
+ const input = readString(args.candidateId) || readString(args.query);
204
+ const resolved = resolveLearningConsolidationCandidate(minimalContext(shellPaths, memoryRegistry), input);
205
+ if (resolved.status === 'found') return resolved.candidate;
206
+ if (resolved.status === 'ambiguous') {
207
+ throw new Error(`Ambiguous learning consolidation candidate ${resolved.input}. Candidates: ${JSON.stringify(resolved.candidates)}`);
208
+ }
209
+ throw new Error(resolved.usage);
210
+ }
211
+
212
+ export function domainForCandidate(candidate: LearningCandidate): AgentLearningConsolidationDomain {
213
+ const domain = String(candidate.domain);
214
+ if (!isDomain(domain)) throw new Error(`Candidate ${candidate.id} is not an applyable consolidation domain.`);
215
+ return domain;
216
+ }
217
+
218
+ export function cloneRecord(record: unknown): Record<string, unknown> {
219
+ return JSON.parse(JSON.stringify(record)) as Record<string, unknown>;
220
+ }
221
+
222
+ export function getRecord(
223
+ shellPaths: ShellPathService,
224
+ memoryRegistry: MemoryRegistry,
225
+ domain: AgentLearningConsolidationDomain,
226
+ id: string,
227
+ ): MemoryRecord | AgentPersonaRecord | AgentSkillRecord | AgentRoutineRecord {
228
+ if (domain === 'memory') {
229
+ const record = memoryRegistry.get(id);
230
+ if (!record) throw new Error(`Unknown Agent-local memory ${id}`);
231
+ return record;
232
+ }
233
+ if (domain === 'persona') {
234
+ const record = AgentPersonaRegistry.fromShellPaths(shellPaths).get(id);
235
+ if (!record) throw new Error(`Unknown Agent-local persona ${id}`);
236
+ return record;
237
+ }
238
+ if (domain === 'skill') {
239
+ const record = AgentSkillRegistry.fromShellPaths(shellPaths).get(id);
240
+ if (!record) throw new Error(`Unknown Agent-local skill ${id}`);
241
+ return record;
242
+ }
243
+ const record = AgentRoutineRegistry.fromShellPaths(shellPaths).get(id);
244
+ if (!record) throw new Error(`Unknown Agent-local routine ${id}`);
245
+ return record;
246
+ }
247
+
248
+ export function updateSurvivor(
249
+ shellPaths: ShellPathService,
250
+ memoryRegistry: MemoryRegistry,
251
+ domain: AgentLearningConsolidationDomain,
252
+ survivorId: string,
253
+ fields: LearningConsolidationFields,
254
+ ): Record<string, unknown> {
255
+ if (domain === 'memory') {
256
+ const record = memoryRegistry.update(survivorId, {
257
+ ...(fields.detail === undefined ? {} : { detail: fields.detail }),
258
+ ...(fields.tags === undefined ? {} : { tags: [...fields.tags] }),
259
+ });
260
+ if (!record) throw new Error(`Unknown Agent-local memory ${survivorId}`);
261
+ return cloneRecord(record);
262
+ }
263
+ if (domain === 'persona') {
264
+ const record = AgentPersonaRegistry.fromShellPaths(shellPaths).update(survivorId, {
265
+ ...(fields.description === undefined ? {} : { description: fields.description }),
266
+ ...(fields.tags === undefined ? {} : { tags: [...fields.tags] }),
267
+ ...(fields.triggers === undefined ? {} : { triggers: [...fields.triggers] }),
268
+ provenance: 'learning-curator-consolidation',
269
+ });
270
+ return cloneRecord(record);
271
+ }
272
+ if (domain === 'skill') {
273
+ const record = AgentSkillRegistry.fromShellPaths(shellPaths).update(survivorId, {
274
+ ...(fields.description === undefined ? {} : { description: fields.description }),
275
+ ...(fields.tags === undefined ? {} : { tags: [...fields.tags] }),
276
+ ...(fields.triggers === undefined ? {} : { triggers: [...fields.triggers] }),
277
+ provenance: 'learning-curator-consolidation',
278
+ });
279
+ return cloneRecord(record);
280
+ }
281
+ const record = AgentRoutineRegistry.fromShellPaths(shellPaths).update(survivorId, {
282
+ ...(fields.description === undefined ? {} : { description: fields.description }),
283
+ ...(fields.tags === undefined ? {} : { tags: [...fields.tags] }),
284
+ ...(fields.triggers === undefined ? {} : { triggers: [...fields.triggers] }),
285
+ provenance: 'learning-curator-consolidation',
286
+ });
287
+ return cloneRecord(record);
288
+ }
289
+
290
+ export function markDuplicateStale(
291
+ shellPaths: ShellPathService,
292
+ memoryRegistry: MemoryRegistry,
293
+ domain: AgentLearningConsolidationDomain,
294
+ duplicateId: string,
295
+ survivorId: string,
296
+ ): Record<string, unknown> {
297
+ const reason = `Duplicate of ${survivorId}; staged by learning curator consolidation.`;
298
+ if (domain === 'memory') {
299
+ const record = memoryRegistry.review(duplicateId, { state: 'stale', staleReason: reason, reviewedBy: 'agent' });
300
+ if (!record) throw new Error(`Unknown Agent-local memory ${duplicateId}`);
301
+ return cloneRecord(record);
302
+ }
303
+ if (domain === 'persona') return cloneRecord(AgentPersonaRegistry.fromShellPaths(shellPaths).markStale(duplicateId, reason));
304
+ if (domain === 'skill') return cloneRecord(AgentSkillRegistry.fromShellPaths(shellPaths).markStale(duplicateId, reason));
305
+ return cloneRecord(AgentRoutineRegistry.fromShellPaths(shellPaths).markStale(duplicateId, reason));
306
+ }
307
+
308
+ export function deleteDuplicate(
309
+ shellPaths: ShellPathService,
310
+ memoryRegistry: MemoryRegistry,
311
+ domain: AgentLearningConsolidationDomain,
312
+ duplicateId: string,
313
+ ): void {
314
+ if (domain === 'memory') {
315
+ if (!memoryRegistry.delete(duplicateId)) throw new Error(`Unknown Agent-local memory ${duplicateId}`);
316
+ return;
317
+ }
318
+ if (domain === 'persona') {
319
+ AgentPersonaRegistry.fromShellPaths(shellPaths).deletePersona(duplicateId);
320
+ return;
321
+ }
322
+ if (domain === 'skill') {
323
+ AgentSkillRegistry.fromShellPaths(shellPaths).deleteSkill(duplicateId);
324
+ return;
325
+ }
326
+ AgentRoutineRegistry.fromShellPaths(shellPaths).deleteRoutine(duplicateId);
327
+ }