@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,197 @@
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 AgentPersonalOpsAction = 'briefing' | 'status' | 'queue' | 'intake' | 'lane' | 'read';
7
+
8
+ interface AgentPersonalOpsToolArgs {
9
+ readonly action?: unknown;
10
+ readonly mode?: unknown;
11
+ readonly laneId?: unknown;
12
+ readonly recordId?: unknown;
13
+ readonly query?: unknown;
14
+ readonly target?: unknown;
15
+ readonly fields?: unknown;
16
+ readonly saveReviewCards?: unknown;
17
+ readonly saveReview?: unknown;
18
+ readonly artifactTitle?: unknown;
19
+ readonly includeParameters?: unknown;
20
+ readonly limit?: unknown;
21
+ readonly confirm?: unknown;
22
+ readonly explicitUserRequest?: unknown;
23
+ }
24
+
25
+ interface AgentPersonalOpsToolDeps {
26
+ readonly commandRegistry: CommandRegistry;
27
+ readonly commandContext: CommandContext;
28
+ readonly toolRegistry: ToolRegistry;
29
+ readonly harnessTool?: Tool;
30
+ }
31
+
32
+ function error(message: string): { readonly success: false; readonly error: string } {
33
+ return { success: false, error: message };
34
+ }
35
+
36
+ function readString(value: unknown): string {
37
+ return typeof value === 'string' ? value.trim() : '';
38
+ }
39
+
40
+ function normalizePersonalOpsAction(value: unknown): AgentPersonalOpsAction | null {
41
+ const action = readString(value).toLowerCase().replace(/-/g, '_');
42
+ if (!action) return null;
43
+ if (action === 'briefing' || action === 'brief' || action === 'daily' || action === 'daily_brief' || action === 'morning') return 'briefing';
44
+ if (action === 'status' || action === 'summary' || action === 'overview' || action === 'map' || action === 'list') return 'status';
45
+ if (action === 'queue' || action === 'queues' || action === 'review_queue' || action === 'review_queues' || action === 'personal_queue' || action === 'ops_queue') return 'queue';
46
+ if (action === 'intake' || action === 'request' || action === 'route' || action === 'plan' || action === 'triage' || action === 'draft') return 'intake';
47
+ if (action === 'lane' || action === 'inspect' || action === 'show') return 'lane';
48
+ if (action === 'read' || action === 'run' || action === 'execute' || action === 'fresh_read' || action === 'refresh') return 'read';
49
+ return null;
50
+ }
51
+
52
+ function readAction(args: AgentPersonalOpsToolArgs): AgentPersonalOpsAction {
53
+ const explicit = normalizePersonalOpsAction(args.action) ?? normalizePersonalOpsAction(args.mode);
54
+ if (explicit) return explicit;
55
+ if (readString(args.recordId)) return 'read';
56
+ if (readString(args.laneId)) return 'lane';
57
+ if (readString(args.query) || readString(args.target)) return 'intake';
58
+ return 'briefing';
59
+ }
60
+
61
+ function compactArgs(entries: Record<string, unknown>): Record<string, unknown> {
62
+ return Object.fromEntries(Object.entries(entries).filter(([, value]) => value !== undefined && value !== ''));
63
+ }
64
+
65
+ function readFields(args: AgentPersonalOpsToolArgs): Record<string, unknown> | undefined {
66
+ const fields = args.fields && typeof args.fields === 'object' && !Array.isArray(args.fields)
67
+ ? { ...(args.fields as Record<string, unknown>) }
68
+ : {};
69
+ if (args.saveReviewCards !== undefined) fields.saveReviewCards = args.saveReviewCards;
70
+ if (args.saveReview !== undefined) fields.saveReview = args.saveReview;
71
+ const artifactTitle = readString(args.artifactTitle);
72
+ if (artifactTitle) fields.artifactTitle = artifactTitle;
73
+ return Object.keys(fields).length > 0 ? fields : undefined;
74
+ }
75
+
76
+ function briefingArgs(args: AgentPersonalOpsToolArgs): Record<string, unknown> {
77
+ return compactArgs({
78
+ mode: 'personal_ops_briefing',
79
+ query: args.query,
80
+ target: args.target,
81
+ limit: args.limit,
82
+ includeParameters: args.includeParameters,
83
+ });
84
+ }
85
+
86
+ function statusArgs(args: AgentPersonalOpsToolArgs): Record<string, unknown> {
87
+ return compactArgs({
88
+ mode: 'personal_ops',
89
+ includeParameters: args.includeParameters,
90
+ });
91
+ }
92
+
93
+ function intakeArgs(args: AgentPersonalOpsToolArgs): Record<string, unknown> {
94
+ return compactArgs({
95
+ mode: 'personal_ops_intake',
96
+ query: args.query,
97
+ target: args.target,
98
+ limit: args.limit,
99
+ includeParameters: args.includeParameters,
100
+ });
101
+ }
102
+
103
+ function queueArgs(args: AgentPersonalOpsToolArgs): Record<string, unknown> {
104
+ return compactArgs({
105
+ mode: 'personal_ops_queue',
106
+ query: args.query,
107
+ target: args.target,
108
+ limit: args.limit,
109
+ includeParameters: args.includeParameters,
110
+ });
111
+ }
112
+
113
+ function laneArgs(args: AgentPersonalOpsToolArgs): Record<string, unknown> {
114
+ return compactArgs({
115
+ mode: 'personal_ops_lane',
116
+ laneId: args.laneId,
117
+ target: args.target,
118
+ query: args.query,
119
+ includeParameters: args.includeParameters,
120
+ });
121
+ }
122
+
123
+ function readArgs(args: AgentPersonalOpsToolArgs): Record<string, unknown> {
124
+ return compactArgs({
125
+ mode: 'run_personal_ops_read',
126
+ laneId: args.laneId,
127
+ recordId: args.recordId,
128
+ target: args.target,
129
+ query: args.query,
130
+ fields: readFields(args),
131
+ includeParameters: args.includeParameters,
132
+ confirm: args.confirm,
133
+ explicitUserRequest: args.explicitUserRequest,
134
+ });
135
+ }
136
+
137
+ export function createAgentPersonalOpsTool(deps: AgentPersonalOpsToolDeps): Tool {
138
+ const harnessTool = deps.harnessTool ?? createAgentHarnessTool({
139
+ commandRegistry: deps.commandRegistry,
140
+ commandContext: deps.commandContext,
141
+ toolRegistry: deps.toolRegistry,
142
+ });
143
+
144
+ return {
145
+ definition: {
146
+ name: 'personal_ops',
147
+ description: 'Plan and run safe Personal Ops workflows.',
148
+ parameters: {
149
+ type: 'object',
150
+ properties: {
151
+ action: {
152
+ type: 'string',
153
+ enum: ['briefing', 'status', 'queue', 'intake', 'lane', 'read'],
154
+ description: 'Discovery actions are read-only; read needs confirmation.',
155
+ },
156
+ mode: { type: 'string', description: 'Alias for action.' },
157
+ laneId: { type: 'string', enum: ['inbox', 'calendar', 'notes', 'tasks', 'reminders', 'routines', 'delivery'], description: 'Personal Ops lane id.' },
158
+ recordId: { type: 'string', description: 'Selected inbox/calendar read operation record id.' },
159
+ query: { type: 'string', description: 'Personal operations request or lane lookup text.' },
160
+ target: { type: 'string', description: 'Alias for query or lookup target.' },
161
+ fields: { type: 'object', additionalProperties: true, description: 'Connector input fields for action:read.' },
162
+ saveReviewCards: { type: 'boolean', description: 'For action:read, save redacted review cards as a local artifact.' },
163
+ saveReview: { type: 'boolean', description: 'Alias for saveReviewCards.' },
164
+ artifactTitle: { type: 'string', description: 'Optional title for a saved read-review artifact.' },
165
+ includeParameters: { type: 'boolean', description: 'Include bounded operation schema and route detail where supported.' },
166
+ limit: { type: 'number', description: 'Maximum briefing or intake rows.' },
167
+ confirm: { type: 'boolean', description: 'Required true for action:read.' },
168
+ explicitUserRequest: { type: 'string', description: 'User request authorizing action:read.' },
169
+ },
170
+ additionalProperties: false,
171
+ },
172
+ sideEffects: ['state'],
173
+ concurrency: 'serial',
174
+ },
175
+ execute: async (rawArgs: unknown) => {
176
+ const args = (rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs) ? rawArgs : {}) as AgentPersonalOpsToolArgs;
177
+ const action = readAction(args);
178
+
179
+ if (action === 'briefing') return harnessTool.execute(briefingArgs(args));
180
+ if (action === 'status') return harnessTool.execute(statusArgs(args));
181
+ if (action === 'queue') return harnessTool.execute(queueArgs(args));
182
+ if (action === 'intake') return harnessTool.execute(intakeArgs(args));
183
+ if (action === 'lane') return harnessTool.execute(laneArgs(args));
184
+ if (action === 'read') return harnessTool.execute(readArgs(args));
185
+
186
+ return error('Unknown Personal Ops action. Use action:"briefing" for the user-first daily plan.');
187
+ },
188
+ };
189
+ }
190
+
191
+ export function registerAgentPersonalOpsTool(
192
+ registry: ToolRegistry,
193
+ commandRegistry: CommandRegistry,
194
+ commandContext: CommandContext,
195
+ ): void {
196
+ if (!registry.has('personal_ops')) registry.register(createAgentPersonalOpsTool({ commandRegistry, commandContext, toolRegistry: registry }));
197
+ }
@@ -0,0 +1,415 @@
1
+ import type { PermissionCategory, PermissionDecisionReasonCode, PermissionDecisionSource } from '@pellux/goodvibes-sdk/platform/permissions';
2
+ import { analyzePermissionRequest } from '@pellux/goodvibes-sdk/platform/permissions';
3
+ import type { ToolDefinition } from '@pellux/goodvibes-sdk/platform/types';
4
+ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
5
+ import type { CommandContext } from '../input/command-registry.ts';
6
+ import { fallbackPermissionCategoryForArgs } from '../runtime/tool-permission-safety.ts';
7
+ import { HARNESS_MODE_DESCRIPTORS } from './agent-harness-mode-catalog.ts';
8
+ import { explainAgentToolPolicyInvocation } from './agent-tool-policy-guard.ts';
9
+
10
+ type PolicyExplanationStatus = 'allowed' | 'confirmation_required' | 'denied';
11
+ type PermissionPredictionOutcome = 'allowed' | 'prompt' | 'denied';
12
+ type PermissionToolKey =
13
+ | 'read'
14
+ | 'write'
15
+ | 'edit'
16
+ | 'exec'
17
+ | 'find'
18
+ | 'fetch'
19
+ | 'analyze'
20
+ | 'inspect'
21
+ | 'agent'
22
+ | 'state'
23
+ | 'workflow'
24
+ | 'registry'
25
+ | 'delegate'
26
+ | 'mcp';
27
+
28
+ export interface AgentPolicyExplainArgs {
29
+ readonly toolName?: unknown;
30
+ readonly tool?: unknown;
31
+ readonly toolArgs?: unknown;
32
+ readonly args?: unknown;
33
+ readonly target?: unknown;
34
+ readonly query?: unknown;
35
+ readonly includeParameters?: unknown;
36
+ }
37
+
38
+ export type AgentPolicyExplanationResolution =
39
+ | { readonly status: 'found'; readonly explanation: Record<string, unknown> }
40
+ | { readonly status: 'ambiguous'; readonly input: string; readonly candidates: readonly Record<string, unknown>[] }
41
+ | { readonly status: 'missing_lookup'; readonly usage: string };
42
+
43
+ interface PermissionPrediction {
44
+ readonly outcome: PermissionPredictionOutcome;
45
+ readonly sourceLayer: PermissionDecisionSource | 'user_prompt_pending';
46
+ readonly reasonCode: PermissionDecisionReasonCode | 'prompt_required';
47
+ readonly mode: string;
48
+ readonly reason: string;
49
+ readonly configuredAction?: string;
50
+ }
51
+
52
+ const TOOL_CONFIG_KEYS: Readonly<Record<string, PermissionToolKey>> = {
53
+ read: 'read',
54
+ write: 'write',
55
+ edit: 'edit',
56
+ exec: 'exec',
57
+ find: 'find',
58
+ fetch: 'fetch',
59
+ analyze: 'analyze',
60
+ inspect: 'inspect',
61
+ agent: 'agent',
62
+ state: 'state',
63
+ workflow: 'workflow',
64
+ registry: 'registry',
65
+ goodvibes_context: 'state',
66
+ goodvibes_settings: 'write',
67
+ delegate: 'delegate',
68
+ mcp: 'mcp',
69
+ };
70
+
71
+ const SECRET_KEY_RE = /(?:api[_-]?key|token|secret|password|authorization|auth|bearer|webhook)/i;
72
+
73
+ function isRecord(value: unknown): value is Record<string, unknown> {
74
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
75
+ }
76
+
77
+ function readString(value: unknown): string {
78
+ return typeof value === 'string' ? value.trim() : '';
79
+ }
80
+
81
+ function readBoolean(value: unknown): boolean {
82
+ return value === true || (typeof value === 'string' && ['true', 'yes', 'apply', 'run'].includes(value.trim().toLowerCase()));
83
+ }
84
+
85
+ function readToolArgs(args: AgentPolicyExplainArgs): Record<string, unknown> {
86
+ if (isRecord(args.toolArgs)) return args.toolArgs;
87
+ if (isRecord(args.args)) return args.args;
88
+ return {};
89
+ }
90
+
91
+ function redactValue(key: string, value: unknown, depth = 0): unknown {
92
+ if (SECRET_KEY_RE.test(key)) return '<redacted>';
93
+ if (depth > 2) return '<nested>';
94
+ if (Array.isArray(value)) return value.slice(0, 8).map((entry) => redactValue(key, entry, depth + 1));
95
+ if (isRecord(value)) {
96
+ return Object.fromEntries(Object.entries(value).slice(0, 20).map(([entryKey, entryValue]) => [entryKey, redactValue(entryKey, entryValue, depth + 1)]));
97
+ }
98
+ if (typeof value === 'string' && /(?:[?&](?:token|secret|password|api[_-]?key)=|bearer\s+)/i.test(value)) return '<redacted>';
99
+ if (typeof value === 'string' && value.length > 240) return `${value.slice(0, 237)}...`;
100
+ return value;
101
+ }
102
+
103
+ function redactArgs(args: Record<string, unknown>): Record<string, unknown> {
104
+ return Object.fromEntries(Object.entries(args).slice(0, 30).map(([key, value]) => [key, redactValue(key, value)]));
105
+ }
106
+
107
+ function hasSchemaProperty(definition: ToolDefinition | undefined, property: string): boolean {
108
+ const parameters = definition?.parameters;
109
+ if (!isRecord(parameters)) return false;
110
+ const properties = parameters.properties;
111
+ return isRecord(properties) && property in properties;
112
+ }
113
+
114
+ function sideEffects(definition: ToolDefinition | undefined): readonly string[] {
115
+ return Array.isArray(definition?.sideEffects) ? definition.sideEffects.filter((entry): entry is string => typeof entry === 'string') : [];
116
+ }
117
+
118
+ function findToolDefinition(toolRegistry: ToolRegistry, name: string): ToolDefinition | undefined {
119
+ return toolRegistry.getToolDefinitions().find((definition) => definition.name === name);
120
+ }
121
+
122
+ function toolSearchText(definition: ToolDefinition): string {
123
+ return [definition.name, definition.description, ...(definition.sideEffects ?? [])].filter(Boolean).join('\n').toLowerCase();
124
+ }
125
+
126
+ function resolveToolName(toolRegistry: ToolRegistry, args: AgentPolicyExplainArgs): {
127
+ readonly status: 'found' | 'ambiguous' | 'missing_lookup';
128
+ readonly toolName?: string;
129
+ readonly input?: string;
130
+ readonly candidates?: readonly Record<string, unknown>[];
131
+ readonly explicit?: boolean;
132
+ } {
133
+ const explicit = readString(args.toolName) || readString(args.tool);
134
+ if (explicit) return { status: 'found', toolName: explicit, input: explicit, explicit: true };
135
+ const input = readString(args.target) || readString(args.query);
136
+ if (!input) return { status: 'missing_lookup' };
137
+
138
+ const definitions = toolRegistry.getToolDefinitions();
139
+ const exact = definitions.find((definition) => definition.name === input);
140
+ if (exact) return { status: 'found', toolName: exact.name, input, explicit: false };
141
+ const lowered = input.toLowerCase();
142
+ const insensitive = definitions.find((definition) => definition.name.toLowerCase() === lowered);
143
+ if (insensitive) return { status: 'found', toolName: insensitive.name, input, explicit: false };
144
+ const matches = definitions.filter((definition) => toolSearchText(definition).includes(lowered)).slice(0, 8);
145
+ if (matches.length === 1) return { status: 'found', toolName: matches[0]!.name, input, explicit: false };
146
+ if (matches.length > 1) {
147
+ return {
148
+ status: 'ambiguous',
149
+ input,
150
+ candidates: matches.map((definition) => ({
151
+ toolName: definition.name,
152
+ description: definition.description,
153
+ modelRoute: `security action:"explain" toolName:"${definition.name}" toolArgs:{...}`,
154
+ })),
155
+ };
156
+ }
157
+ return { status: 'found', toolName: input, input, explicit: false };
158
+ }
159
+
160
+ function readConfigValue(context: CommandContext, key: string): unknown {
161
+ try {
162
+ const manager = context.platform.configManager as unknown as { get?: (key: string) => unknown };
163
+ return manager.get?.(key);
164
+ } catch {
165
+ return undefined;
166
+ }
167
+ }
168
+
169
+ function readPermissionMode(context: CommandContext): string {
170
+ const configured = readString(readConfigValue(context, 'permissions.mode'));
171
+ if (configured) return configured;
172
+ const mode = context.platform.config?.permissions?.mode;
173
+ return typeof mode === 'string' && mode ? mode : 'prompt';
174
+ }
175
+
176
+ function readPermissionToolAction(context: CommandContext, key: PermissionToolKey): string | null {
177
+ const configured = readString(readConfigValue(context, `permissions.tools.${key}`));
178
+ if (configured) return configured;
179
+ const action = context.platform.config?.permissions?.tools?.[key];
180
+ return typeof action === 'string' && action ? action : null;
181
+ }
182
+
183
+ function readAutoApprove(context: CommandContext): boolean {
184
+ const configured = readConfigValue(context, 'behavior.autoApprove');
185
+ if (typeof configured === 'boolean') return configured;
186
+ return context.platform.config?.behavior?.autoApprove === true;
187
+ }
188
+
189
+ function predictPermission(context: CommandContext, toolName: string, category: PermissionCategory): PermissionPrediction {
190
+ if (readAutoApprove(context)) {
191
+ return {
192
+ outcome: 'allowed',
193
+ sourceLayer: 'config_policy',
194
+ reasonCode: 'config_allow',
195
+ mode: readPermissionMode(context),
196
+ reason: 'Automatic tool approval is enabled for this session.',
197
+ };
198
+ }
199
+
200
+ const mode = readPermissionMode(context);
201
+ if (mode === 'allow-all') {
202
+ return {
203
+ outcome: 'allowed',
204
+ sourceLayer: 'runtime_mode',
205
+ reasonCode: 'mode_allow_all',
206
+ mode,
207
+ reason: 'Permission mode is allow-all, so the permission layer will not prompt.',
208
+ };
209
+ }
210
+
211
+ if (mode === 'custom') {
212
+ const toolConfigKey = TOOL_CONFIG_KEYS[toolName];
213
+ if (!toolConfigKey) {
214
+ return {
215
+ outcome: 'prompt',
216
+ sourceLayer: 'user_prompt_pending',
217
+ reasonCode: 'prompt_required',
218
+ mode,
219
+ reason: 'Custom permission mode has no per-tool rule for this tool.',
220
+ };
221
+ }
222
+ const configuredAction = readPermissionToolAction(context, toolConfigKey) ?? 'prompt';
223
+ if (configuredAction === 'allow') {
224
+ return {
225
+ outcome: 'allowed',
226
+ sourceLayer: 'config_policy',
227
+ reasonCode: 'config_allow',
228
+ mode,
229
+ configuredAction,
230
+ reason: `Custom permission rule permissions.tools.${toolConfigKey} is allow.`,
231
+ };
232
+ }
233
+ if (configuredAction === 'deny') {
234
+ return {
235
+ outcome: 'denied',
236
+ sourceLayer: 'config_policy',
237
+ reasonCode: 'config_deny',
238
+ mode,
239
+ configuredAction,
240
+ reason: `Custom permission rule permissions.tools.${toolConfigKey} is deny.`,
241
+ };
242
+ }
243
+ return {
244
+ outcome: 'prompt',
245
+ sourceLayer: 'user_prompt_pending',
246
+ reasonCode: 'prompt_required',
247
+ mode,
248
+ configuredAction,
249
+ reason: `Custom permission rule permissions.tools.${toolConfigKey} is prompt.`,
250
+ };
251
+ }
252
+
253
+ if (category === 'read') {
254
+ return {
255
+ outcome: 'allowed',
256
+ sourceLayer: 'config_policy',
257
+ reasonCode: 'config_allow',
258
+ mode,
259
+ reason: 'Prompt mode auto-allows read-only actions.',
260
+ };
261
+ }
262
+
263
+ return {
264
+ outcome: 'prompt',
265
+ sourceLayer: 'user_prompt_pending',
266
+ reasonCode: 'prompt_required',
267
+ mode,
268
+ reason: `Prompt mode asks before ${category} actions.`,
269
+ };
270
+ }
271
+
272
+ function harnessModeRequiresConfirmation(toolName: string, toolArgs: Record<string, unknown>): boolean {
273
+ if (toolName !== 'agent_harness') return false;
274
+ const mode = readString(toolArgs.mode);
275
+ if (!mode) return false;
276
+ return HARNESS_MODE_DESCRIPTORS.some((descriptor) => descriptor.id === mode && descriptor.requiresConfirmation === true);
277
+ }
278
+
279
+ function confirmationState(toolName: string, toolArgs: Record<string, unknown>, definition: ToolDefinition | undefined, category: PermissionCategory): {
280
+ readonly required: boolean;
281
+ readonly confirmed: boolean;
282
+ readonly reason: string;
283
+ } {
284
+ const schemaRequestsConfirmation = hasSchemaProperty(definition, 'confirm') && hasSchemaProperty(definition, 'explicitUserRequest');
285
+ const required = harnessModeRequiresConfirmation(toolName, toolArgs)
286
+ || (schemaRequestsConfirmation && category !== 'read');
287
+ if (!required) {
288
+ return {
289
+ required: false,
290
+ confirmed: true,
291
+ reason: 'The tool contract does not require typed confirmation for this read-only route.',
292
+ };
293
+ }
294
+ const confirmed = readBoolean(toolArgs.confirm) && readString(toolArgs.explicitUserRequest).length > 0;
295
+ return {
296
+ required,
297
+ confirmed,
298
+ reason: confirmed
299
+ ? 'Typed confirmation is present: confirm:true and explicitUserRequest are set.'
300
+ : 'Typed confirmation is required: pass confirm:true and explicitUserRequest.',
301
+ };
302
+ }
303
+
304
+ function statusFor(
305
+ guard: ReturnType<typeof explainAgentToolPolicyInvocation>,
306
+ permission: PermissionPrediction,
307
+ confirmation: ReturnType<typeof confirmationState>,
308
+ ): PolicyExplanationStatus {
309
+ if (guard.status === 'denied' || permission.outcome === 'denied') return 'denied';
310
+ if (permission.outcome === 'prompt' || (confirmation.required && !confirmation.confirmed)) return 'confirmation_required';
311
+ return 'allowed';
312
+ }
313
+
314
+ function userExplanation(status: PolicyExplanationStatus, category: PermissionCategory): string {
315
+ if (status === 'denied') return 'This action is denied before execution. Use the recommended safer route or change policy explicitly.';
316
+ if (status === 'confirmation_required') return `This ${category} action is available only after the user-visible confirmation or permission prompt is satisfied.`;
317
+ return `This ${category} action is allowed by the current Agent and permission policy.`;
318
+ }
319
+
320
+ export function explainAgentPolicyDecision(
321
+ context: CommandContext,
322
+ toolRegistry: ToolRegistry,
323
+ args: AgentPolicyExplainArgs,
324
+ ): AgentPolicyExplanationResolution {
325
+ const resolved = resolveToolName(toolRegistry, args);
326
+ if (resolved.status === 'missing_lookup') {
327
+ return {
328
+ status: 'missing_lookup',
329
+ usage: 'policy_explain requires toolName or a target/query matching one model tool.',
330
+ };
331
+ }
332
+ if (resolved.status === 'ambiguous') {
333
+ return {
334
+ status: 'ambiguous',
335
+ input: resolved.input ?? '',
336
+ candidates: resolved.candidates ?? [],
337
+ };
338
+ }
339
+
340
+ const toolName = resolved.toolName!;
341
+ const toolArgs = readToolArgs(args);
342
+ const definition = findToolDefinition(toolRegistry, toolName);
343
+ const category = fallbackPermissionCategoryForArgs(toolName, toolArgs);
344
+ const analysis = analyzePermissionRequest(toolName, toolArgs, category);
345
+ const guard = explainAgentToolPolicyInvocation(toolName, toolArgs);
346
+ const permission = predictPermission(context, toolName, category);
347
+ const confirmation = confirmationState(toolName, toolArgs, definition, category);
348
+ const status = statusFor(guard, permission, confirmation);
349
+ const requiredActions = [
350
+ ...(permission.outcome === 'prompt' ? [`Answer the ${category} permission prompt for ${toolName}.`] : []),
351
+ ...(confirmation.required && !confirmation.confirmed ? ['Call the route with confirm:true and explicitUserRequest.'] : []),
352
+ ];
353
+
354
+ return {
355
+ status: 'found',
356
+ explanation: {
357
+ status,
358
+ toolName,
359
+ registered: definition !== undefined,
360
+ category,
361
+ userExplanation: userExplanation(status, category),
362
+ policyLayers: [
363
+ {
364
+ layer: 'Agent route guard',
365
+ outcome: guard.status,
366
+ reason: guard.reason,
367
+ ...(guard.allowedModes ? { allowedModes: guard.allowedModes } : {}),
368
+ },
369
+ {
370
+ layer: 'Permission mode',
371
+ outcome: permission.outcome,
372
+ mode: permission.mode,
373
+ sourceLayer: permission.sourceLayer,
374
+ reasonCode: permission.reasonCode,
375
+ reason: permission.reason,
376
+ ...(permission.configuredAction ? { configuredAction: permission.configuredAction } : {}),
377
+ },
378
+ {
379
+ layer: 'Tool confirmation',
380
+ outcome: confirmation.required ? (confirmation.confirmed ? 'confirmed' : 'required') : 'not_required',
381
+ reason: confirmation.reason,
382
+ },
383
+ ],
384
+ requiredActions,
385
+ analysis,
386
+ preflight: {
387
+ approvedWithoutMoreInput: status === 'allowed',
388
+ permissionOutcome: permission.outcome,
389
+ toolConfirmationRequired: confirmation.required,
390
+ toolConfirmationSatisfied: confirmation.confirmed,
391
+ },
392
+ toolArgs: redactArgs(toolArgs),
393
+ routes: {
394
+ explain: `security action:"explain" toolName:"${toolName}" toolArgs:{...}`,
395
+ inspectTool: `agent_harness mode:"tool" toolName:"${toolName}" includeParameters:true`,
396
+ permissions: 'workspace action:"actions" categoryId:"tools-permissions"',
397
+ securityPosture: 'security action:"status" includeParameters:true',
398
+ },
399
+ notes: [
400
+ 'This is a read-only preflight; final execution still uses the live safety guard.',
401
+ 'Secret-looking argument values are redacted in this explanation.',
402
+ ],
403
+ ...(args.includeParameters === true && definition ? {
404
+ toolDefinition: {
405
+ name: definition.name,
406
+ description: definition.description,
407
+ sideEffects: sideEffects(definition),
408
+ hasConfirmParameter: hasSchemaProperty(definition, 'confirm'),
409
+ hasExplicitUserRequestParameter: hasSchemaProperty(definition, 'explicitUserRequest'),
410
+ },
411
+ } : {}),
412
+ ...(resolved.input ? { lookup: { input: resolved.input, explicit: resolved.explicit === true } } : {}),
413
+ },
414
+ };
415
+ }