@pellux/goodvibes-agent 1.1.6 → 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 (256) hide show
  1. package/CHANGELOG.md +176 -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 +132662 -91399
  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 +43 -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 +287 -0
  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 +267 -10
  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 +110 -89
  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/tools/tool-execution-safety.ts +41 -0
  256. package/src/version.ts +1 -1
@@ -0,0 +1,533 @@
1
+ import { getOperatorContract } from '@pellux/goodvibes-sdk/contracts';
2
+ import type { CommandContext } from '../input/command-registry.ts';
3
+ import type { McpSchemaSummary, McpToolRecord, McpToolSchema, OperatorContractMethod, PersonalOpsConnectorSignal, PersonalOpsConnectorTool, PersonalOpsWorkflow, PersonalOpsWorkflowStatus } from './agent-harness-personal-ops-types.ts';
4
+
5
+ export function readString(value: unknown): string {
6
+ return typeof value === 'string' ? value.trim() : '';
7
+ }
8
+
9
+ export function readLimit(value: unknown, fallback: number): number {
10
+ const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
11
+ if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
12
+ return Math.max(1, Math.min(12, Math.trunc(parsed)));
13
+ }
14
+
15
+ export function hasAny(text: string, tokens: readonly string[]): boolean {
16
+ return tokens.some((token) => text.includes(token));
17
+ }
18
+
19
+ export function operatorContractMethods(): readonly OperatorContractMethod[] {
20
+ const contract = getOperatorContract();
21
+ const methods = Array.isArray(contract.operator?.methods)
22
+ ? contract.operator.methods as OperatorContractMethod[]
23
+ : [];
24
+ return methods.filter((method) => method.id);
25
+ }
26
+
27
+ export function methodSearchText(method: OperatorContractMethod): string {
28
+ return [
29
+ method.id,
30
+ method.title,
31
+ method.description,
32
+ method.category,
33
+ method.http?.method,
34
+ method.http?.path,
35
+ ].filter(Boolean).join('\n').toLowerCase();
36
+ }
37
+
38
+ export function methodIdsMatching(tokens: readonly string[]): readonly string[] {
39
+ if (tokens.length === 0) return [];
40
+ return operatorContractMethods()
41
+ .filter((method) => {
42
+ const text = methodSearchText(method);
43
+ return tokens.some((token) => text.includes(token));
44
+ })
45
+ .map((method) => method.id)
46
+ .sort((left, right) => left.localeCompare(right));
47
+ }
48
+
49
+ export function mcpServerRecords(context: CommandContext): readonly {
50
+ readonly name: string;
51
+ readonly connected: boolean;
52
+ readonly trustMode: string;
53
+ readonly role: string;
54
+ readonly schemaFreshness: string;
55
+ readonly allowedHosts: readonly string[];
56
+ }[] {
57
+ const api = context.clients?.mcpApi ?? context.extensions?.mcpRegistry;
58
+ if (!api || typeof api.listServerSecurity !== 'function') return [];
59
+ try {
60
+ return api.listServerSecurity();
61
+ } catch {
62
+ return [];
63
+ }
64
+ }
65
+
66
+ export async function mcpToolRecords(context: CommandContext): Promise<readonly McpToolRecord[]> {
67
+ const api = context.clients?.mcpApi ?? context.extensions?.mcpRegistry;
68
+ if (!api || typeof (api as { readonly listAllTools?: unknown }).listAllTools !== 'function') return [];
69
+ try {
70
+ return await (api as { listAllTools: () => Promise<readonly McpToolRecord[]> }).listAllTools();
71
+ } catch {
72
+ return [];
73
+ }
74
+ }
75
+
76
+ export function mcpSchemaReader(context: CommandContext): ((qualifiedName: string) => Promise<McpToolSchema | null>) | null {
77
+ const api = context.clients?.mcpApi ?? context.extensions?.mcpRegistry;
78
+ if (!api || typeof (api as { readonly getToolSchema?: unknown }).getToolSchema !== 'function') return null;
79
+ return (qualifiedName) => (api as { getToolSchema: (qualifiedName: string) => Promise<McpToolSchema | null> }).getToolSchema(qualifiedName);
80
+ }
81
+
82
+ export function mcpToolCaller(context: CommandContext): ((qualifiedName: string, input: Readonly<Record<string, unknown>>) => Promise<unknown>) | null {
83
+ const api = context.clients?.mcpApi ?? context.extensions?.mcpRegistry;
84
+ if (!api || typeof (api as { readonly callTool?: unknown }).callTool !== 'function') return null;
85
+ return (qualifiedName, input) => (api as { callTool: (qualifiedName: string, input: Readonly<Record<string, unknown>>) => Promise<unknown> }).callTool(qualifiedName, input);
86
+ }
87
+
88
+ export function qualifiedToolName(tool: McpToolRecord): string {
89
+ return tool.qualifiedName ?? `mcp:${tool.serverName}:${tool.toolName}`;
90
+ }
91
+
92
+ export function isPersonalOpsTool(tool: McpToolRecord): boolean {
93
+ return /(mail|email|gmail|imap|smtp|inbox|message|thread|calendar|caldav|agenda|event|freebusy|availability|meeting)/i.test([
94
+ tool.serverName,
95
+ tool.toolName,
96
+ tool.description ?? '',
97
+ ].join(' '));
98
+ }
99
+
100
+ export function schemaObject(value: unknown): Record<string, unknown> | null {
101
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
102
+ return value as Record<string, unknown>;
103
+ }
104
+
105
+ export function schemaRequiredFields(schema: Record<string, unknown>): readonly string[] {
106
+ const required = schema.required;
107
+ if (!Array.isArray(required)) return [];
108
+ return required.filter((field): field is string => typeof field === 'string').sort((left, right) => left.localeCompare(right));
109
+ }
110
+
111
+ export function schemaOptionalFields(schema: Record<string, unknown>, requiredFields: readonly string[]): readonly string[] {
112
+ const properties = schemaObject(schema.properties);
113
+ if (!properties) return [];
114
+ const required = new Set(requiredFields);
115
+ return Object.keys(properties).filter((field) => !required.has(field)).sort((left, right) => left.localeCompare(right));
116
+ }
117
+
118
+ export function sampleFieldValue(field: string, capability: string, effect: PersonalOpsConnectorTool['effect']): unknown {
119
+ const normalized = field.toLowerCase();
120
+ if (/(limit|max|count|page|size)/.test(normalized)) return 10;
121
+ if (/(dry|preview)/.test(normalized)) return effect === 'confirmed-effect';
122
+ if (/(unread|important|include)/.test(normalized)) return true;
123
+ if (/(start|from|after|time_min|timemin|begin)/.test(normalized)) return capability.startsWith('calendar') ? '<start-iso>' : '<since>';
124
+ if (/(end|to|before|time_max|timemax|until)/.test(normalized)) return capability.startsWith('calendar') ? '<end-iso>' : '<until>';
125
+ if (/(thread|message|mail|email).*id|id$/.test(normalized)) return capability.startsWith('calendar') ? '<event-id>' : '<message-or-thread-id>';
126
+ if (/(event).*id|calendar.*id/.test(normalized)) return '<calendar-or-event-id>';
127
+ if (/(query|search|q|filter)/.test(normalized)) return capability.startsWith('calendar') ? 'today' : 'is:unread newer_than:7d';
128
+ if (/(mailbox|folder|label)/.test(normalized)) return capability.startsWith('calendar') ? '<calendar-name>' : '<mailbox-or-label>';
129
+ if (/(calendar|account|profile)/.test(normalized)) return '<account-or-calendar>';
130
+ if (/(subject|title|summary)/.test(normalized)) return '<reviewed subject>';
131
+ if (/(body|text|message|content|reply|draft|description|note)/.test(normalized)) return '<reviewed draft text>';
132
+ if (/(attendee|participant|invitee)/.test(normalized)) return ['<attendee@example.com>'];
133
+ return `<${field}>`;
134
+ }
135
+
136
+ export function sampleInputForFields(
137
+ requiredFields: readonly string[],
138
+ optionalFields: readonly string[],
139
+ capability: string,
140
+ effect: PersonalOpsConnectorTool['effect'],
141
+ ): Readonly<Record<string, unknown>> {
142
+ const fields = [...requiredFields, ...optionalFields.slice(0, Math.max(0, 4 - requiredFields.length))];
143
+ const sample: Record<string, unknown> = {};
144
+ for (const field of fields) sample[field] = sampleFieldValue(field, capability, effect);
145
+ return sample;
146
+ }
147
+
148
+ export function summarizeInputSchema(
149
+ qualifiedName: string,
150
+ inputSchema: unknown,
151
+ capability: string,
152
+ effect: PersonalOpsConnectorTool['effect'],
153
+ ): McpSchemaSummary | null {
154
+ const schema = schemaObject(inputSchema);
155
+ if (!schema) return null;
156
+ const requiredFields = schemaRequiredFields(schema);
157
+ const optionalFields = schemaOptionalFields(schema, requiredFields);
158
+ return {
159
+ schemaRoute: `mcp schema qualifiedName:"${qualifiedName}"`,
160
+ requiredFields,
161
+ optionalFields,
162
+ sampleInput: sampleInputForFields(requiredFields, optionalFields, capability, effect),
163
+ };
164
+ }
165
+
166
+ export async function mcpToolSchemas(
167
+ context: CommandContext,
168
+ tools: readonly McpToolRecord[],
169
+ ): Promise<ReadonlyMap<string, McpToolSchema>> {
170
+ const readSchema = mcpSchemaReader(context);
171
+ if (!readSchema) return new Map();
172
+ const schemas = new Map<string, McpToolSchema>();
173
+ const candidates = tools.filter(isPersonalOpsTool).slice(0, 50);
174
+ for (const tool of candidates) {
175
+ const qualifiedName = qualifiedToolName(tool);
176
+ try {
177
+ const schema = await readSchema(qualifiedName);
178
+ if (schema) schemas.set(qualifiedName, schema);
179
+ } catch {
180
+ // Schema lookup is best-effort. Personal Ops must keep connector readiness usable even when one MCP server fails schema expansion.
181
+ }
182
+ }
183
+ return schemas;
184
+ }
185
+
186
+ export function toolsByServer(tools: readonly McpToolRecord[]): ReadonlyMap<string, readonly McpToolRecord[]> {
187
+ const grouped = new Map<string, McpToolRecord[]>();
188
+ for (const tool of tools) {
189
+ const entries = grouped.get(tool.serverName) ?? [];
190
+ entries.push(tool);
191
+ grouped.set(tool.serverName, entries);
192
+ }
193
+ return grouped;
194
+ }
195
+
196
+ export function mcpServerSearchText(server: ReturnType<typeof mcpServerRecords>[number]): string {
197
+ return [
198
+ server.name,
199
+ server.connected ? 'connected' : 'disconnected',
200
+ server.trustMode,
201
+ server.role,
202
+ server.schemaFreshness,
203
+ ...server.allowedHosts,
204
+ ].join('\n').toLowerCase();
205
+ }
206
+
207
+ export function toolCapability(tool: McpToolRecord, lane: 'inbox' | 'calendar'): PersonalOpsConnectorTool | null {
208
+ const text = [tool.toolName, tool.description ?? ''].join(' ').toLowerCase();
209
+ const record = (effect: PersonalOpsConnectorTool['effect'], capability: string): PersonalOpsConnectorTool => ({
210
+ name: tool.toolName,
211
+ qualifiedName: qualifiedToolName(tool),
212
+ ...(tool.description ? { description: tool.description } : {}),
213
+ effect,
214
+ capability,
215
+ });
216
+ if (lane === 'inbox') {
217
+ if (/(send|reply|draft|compose|archive|label|delete|trash|mark|move)/.test(text)) {
218
+ return record('confirmed-effect', 'inbox-write');
219
+ }
220
+ if (/(mail|email|gmail|imap|smtp|inbox|message|thread)/.test(text) && /(list|search|get|read|fetch|find|unread|query)/.test(text)) {
221
+ return record('read-only', 'inbox-read');
222
+ }
223
+ } else {
224
+ if (/(create|update|edit|delete|cancel|invite|rsvp|move|reschedule)/.test(text)) {
225
+ return record('confirmed-effect', 'calendar-write');
226
+ }
227
+ if (/(calendar|caldav|agenda|event|freebusy|availability|meeting)/.test(text) && /(list|search|get|read|fetch|find|query|window|upcoming)/.test(text)) {
228
+ return record('read-only', 'calendar-read');
229
+ }
230
+ }
231
+ return null;
232
+ }
233
+
234
+ export function connectorToolSummary(tools: readonly McpToolRecord[], lane: 'inbox' | 'calendar'): {
235
+ readonly readTools: readonly PersonalOpsConnectorTool[];
236
+ readonly writeTools: readonly PersonalOpsConnectorTool[];
237
+ readonly capabilityTags: readonly string[];
238
+ } {
239
+ const classified = tools
240
+ .map((tool) => toolCapability(tool, lane))
241
+ .filter((tool): tool is PersonalOpsConnectorTool => tool !== null)
242
+ .sort((left, right) => left.name.localeCompare(right.name));
243
+ const readTools = classified.filter((tool) => tool.effect === 'read-only');
244
+ const writeTools = classified.filter((tool) => tool.effect === 'confirmed-effect');
245
+ const capabilityTags = [...new Set(classified.map((tool) => tool.capability))].sort((left, right) => left.localeCompare(right));
246
+ return { readTools, writeTools, capabilityTags };
247
+ }
248
+
249
+ export function enrichConnectorToolsWithSchemas(
250
+ tools: readonly PersonalOpsConnectorTool[],
251
+ schemasByQualifiedName: ReadonlyMap<string, McpToolSchema>,
252
+ ): readonly PersonalOpsConnectorTool[] {
253
+ return tools.map((tool) => {
254
+ if (!tool.qualifiedName) return tool;
255
+ const schema = schemasByQualifiedName.get(tool.qualifiedName);
256
+ const summary = summarizeInputSchema(tool.qualifiedName, schema?.inputSchema, tool.capability, tool.effect);
257
+ if (!summary) return tool;
258
+ return {
259
+ ...tool,
260
+ schemaRoute: summary.schemaRoute,
261
+ requiredFields: summary.requiredFields,
262
+ optionalFields: summary.optionalFields,
263
+ sampleInput: summary.sampleInput,
264
+ };
265
+ });
266
+ }
267
+
268
+ export function connectorSignalsMatching(
269
+ context: CommandContext,
270
+ tokens: readonly string[],
271
+ options: {
272
+ readonly lane: 'inbox' | 'calendar';
273
+ readonly toolsByServer: ReadonlyMap<string, readonly McpToolRecord[]>;
274
+ readonly schemasByQualifiedName: ReadonlyMap<string, McpToolSchema>;
275
+ },
276
+ ): readonly PersonalOpsConnectorSignal[] {
277
+ if (tokens.length === 0) return [];
278
+ return mcpServerRecords(context)
279
+ .filter((server) => {
280
+ const text = mcpServerSearchText(server);
281
+ return tokens.some((token) => text.includes(token));
282
+ })
283
+ .map((server) => {
284
+ const ready = server.connected && server.schemaFreshness === 'fresh' && server.trustMode !== 'blocked';
285
+ const serverTools = options.toolsByServer.get(server.name) ?? [];
286
+ const toolSummary = connectorToolSummary(serverTools, options.lane);
287
+ const readTools = enrichConnectorToolsWithSchemas(toolSummary.readTools, options.schemasByQualifiedName);
288
+ const writeTools = enrichConnectorToolsWithSchemas(toolSummary.writeTools, options.schemasByQualifiedName);
289
+ return {
290
+ id: `mcp:${server.name}`,
291
+ kind: 'mcp-server' as const,
292
+ label: server.name,
293
+ status: ready ? 'ready' as const : 'attention' as const,
294
+ summary: [
295
+ `${server.connected ? 'connected' : 'disconnected'} ${server.trustMode} ${server.schemaFreshness}`,
296
+ serverTools.length > 0 ? `${serverTools.length} tool(s)` : '',
297
+ toolSummary.capabilityTags.length > 0 ? toolSummary.capabilityTags.join(', ') : '',
298
+ ].filter(Boolean).join('; '),
299
+ modelRoute: `agent_harness mode:"mcp_server" mcpServerId:"${server.name}"`,
300
+ toolCount: serverTools.length,
301
+ capabilityTags: toolSummary.capabilityTags,
302
+ ...(readTools.length > 0 ? { readTools: readTools.slice(0, 8) } : {}),
303
+ ...(writeTools.length > 0 ? { writeTools: writeTools.slice(0, 8) } : {}),
304
+ };
305
+ })
306
+ .sort((left, right) => left.label.localeCompare(right.label));
307
+ }
308
+
309
+ export function connectorReady(signals: readonly PersonalOpsConnectorSignal[]): boolean {
310
+ return signals.some((signal) => signal.status === 'ready');
311
+ }
312
+
313
+ export function connectorToolCount(signals: readonly PersonalOpsConnectorSignal[], effect: PersonalOpsConnectorTool['effect']): number {
314
+ return signals.reduce((total, signal) => total + (effect === 'read-only' ? signal.readTools?.length ?? 0 : signal.writeTools?.length ?? 0), 0);
315
+ }
316
+
317
+ export function workflowStatus(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[]): PersonalOpsWorkflowStatus {
318
+ if (methodIds.length > 0 || connectorReady(connectors)) return 'ready';
319
+ if (connectors.length > 0) return 'attention';
320
+ return 'needs-setup';
321
+ }
322
+
323
+ export function workflowModelRoute(
324
+ methodQuery: string,
325
+ connectors: readonly PersonalOpsConnectorSignal[],
326
+ ): string {
327
+ const readyConnector = connectors.find((signal) => signal.status === 'ready') ?? connectors[0];
328
+ if (readyConnector) return readyConnector.modelRoute;
329
+ return `host action:"methods" query:"${methodQuery}"`;
330
+ }
331
+
332
+ export function workflowInspectRoutes(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[], fallbackQuery: string): readonly string[] {
333
+ const connectorRoutes = connectors.map((signal) => signal.modelRoute);
334
+ const methodRoutes = methodIds.slice(0, 6).map((methodId) => `host action:"method" methodId:"${methodId}"`);
335
+ const routes = [...connectorRoutes, ...methodRoutes];
336
+ return routes.length > 0 ? routes : [`personal_ops action:"lane" laneId:"${fallbackQuery}"`];
337
+ }
338
+
339
+ export function inboxWorkflows(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[]): readonly PersonalOpsWorkflow[] {
340
+ const status = workflowStatus(methodIds, connectors);
341
+ const inspectRoutes = workflowInspectRoutes(methodIds, connectors, 'inbox');
342
+ const modelRoute = workflowModelRoute('email', connectors);
343
+ const readToolCount = connectorToolCount(connectors, 'read-only');
344
+ const writeToolCount = connectorToolCount(connectors, 'confirmed-effect');
345
+ const setupPrerequisite = status === 'needs-setup'
346
+ ? ['Install or configure an email-capable daemon method, MCP server, or plugin first.']
347
+ : status === 'attention'
348
+ ? ['Review connector trust/schema freshness before reading inbox data.']
349
+ : [
350
+ 'Inspect the exact connector or daemon method schema before selecting inbox actions.',
351
+ readToolCount > 0
352
+ ? `${readToolCount} classified read-only inbox tool(s) are available.`
353
+ : 'No classified read-only inbox tools were returned; inspect the connector schema before claiming live inbox access.',
354
+ ];
355
+ return [
356
+ {
357
+ id: 'inbox-triage-briefing',
358
+ label: 'Inbox triage briefing',
359
+ status,
360
+ summary: 'Summarize unread or high-priority inbound messages without sending mail.',
361
+ next: status === 'ready'
362
+ ? 'Inspect the connector/method, list candidate messages, then summarize priorities and risks in the main conversation.'
363
+ : 'Finish email connector setup before attempting inbox triage.',
364
+ modelRoute,
365
+ inspectRoutes,
366
+ prerequisites: setupPrerequisite,
367
+ runBoundary: 'Read/list/search can run only through the reviewed connector or daemon method; sends and mutations require explicit confirmation.',
368
+ },
369
+ {
370
+ id: 'inbox-draft-reply',
371
+ label: 'Draft reply review',
372
+ status,
373
+ summary: 'Draft replies for selected messages while keeping send as a separate confirmed action.',
374
+ next: status === 'ready'
375
+ ? 'Read the selected thread through the connector, draft in chat, then ask for confirmation before any send route.'
376
+ : 'Finish email connector setup before drafting from live threads.',
377
+ modelRoute,
378
+ inspectRoutes,
379
+ prerequisites: [
380
+ ...setupPrerequisite,
381
+ 'The user must identify the account, mailbox, or message selection.',
382
+ writeToolCount > 0
383
+ ? `${writeToolCount} write-like inbox tool(s) require explicit confirmation before send, label, archive, delete, or move effects.`
384
+ : 'No write-like inbox tools were classified; keep sending and mailbox mutation out of scope until a connector route is inspected.',
385
+ ],
386
+ runBoundary: 'Drafting is conversation output; sending or labeling stays on the connector/tool route with confirmation.',
387
+ },
388
+ ];
389
+ }
390
+
391
+ export function calendarWorkflows(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[]): readonly PersonalOpsWorkflow[] {
392
+ const status = workflowStatus(methodIds, connectors);
393
+ const inspectRoutes = workflowInspectRoutes(methodIds, connectors, 'calendar');
394
+ const modelRoute = workflowModelRoute('calendar', connectors);
395
+ const readToolCount = connectorToolCount(connectors, 'read-only');
396
+ const writeToolCount = connectorToolCount(connectors, 'confirmed-effect');
397
+ const setupPrerequisite = status === 'needs-setup'
398
+ ? ['Install or configure a calendar-capable daemon method, CalDAV MCP server, or plugin first.']
399
+ : status === 'attention'
400
+ ? ['Review connector trust/schema freshness before reading calendar data.']
401
+ : [
402
+ 'Inspect the exact connector or daemon method schema before selecting agenda actions.',
403
+ readToolCount > 0
404
+ ? `${readToolCount} classified read-only calendar tool(s) are available.`
405
+ : 'No classified read-only calendar tools were returned; inspect the connector schema before claiming live agenda access.',
406
+ ];
407
+ return [
408
+ {
409
+ id: 'calendar-agenda-briefing',
410
+ label: 'Agenda briefing',
411
+ status,
412
+ summary: 'Read upcoming events and prepare a concise day or week briefing.',
413
+ next: status === 'ready'
414
+ ? 'Inspect the connector/method, fetch the requested window, then summarize agenda context and prep items.'
415
+ : 'Finish calendar connector setup before attempting agenda briefing.',
416
+ modelRoute,
417
+ inspectRoutes,
418
+ prerequisites: [...setupPrerequisite, 'The user must provide a date range or accept a bounded default window.'],
419
+ runBoundary: 'Calendar reads can run only through reviewed connector/method routes; event edits require explicit confirmation.',
420
+ },
421
+ {
422
+ id: 'calendar-conflict-scan',
423
+ label: 'Conflict scan',
424
+ status,
425
+ summary: 'Detect overlapping events or prep gaps and offer reminders instead of editing the calendar silently.',
426
+ next: status === 'ready'
427
+ ? 'Inspect the agenda source, scan the requested window, then propose reminders or follow-ups for confirmation.'
428
+ : 'Finish calendar connector setup before scanning conflicts.',
429
+ modelRoute,
430
+ inspectRoutes,
431
+ prerequisites: [
432
+ ...setupPrerequisite,
433
+ 'The user must provide a calendar/account scope if more than one exists.',
434
+ writeToolCount > 0
435
+ ? `${writeToolCount} write-like calendar tool(s) require explicit confirmation before event create, edit, delete, RSVP, or reschedule effects.`
436
+ : 'No write-like calendar tools were classified; keep event mutation out of scope until a connector route is inspected.',
437
+ ],
438
+ runBoundary: 'Conflict findings are advisory; reminders use schedule action:"remind" and calendar edits use confirmed connector actions.',
439
+ },
440
+ ];
441
+ }
442
+
443
+ export function hasMethod(methodIds: readonly string[], methodId: string): boolean {
444
+ return methodIds.includes(methodId);
445
+ }
446
+
447
+ export function taskWorkflows(methodIds: readonly string[]): readonly PersonalOpsWorkflow[] {
448
+ const hostTaskStatus: PersonalOpsWorkflowStatus = methodIds.length > 0 ? 'ready' : 'needs-setup';
449
+ return [
450
+ {
451
+ id: 'visible-work-plan-review',
452
+ label: 'Visible work-plan review',
453
+ status: 'ready',
454
+ summary: 'Track user-facing work items in the Agent-owned work plan before creating background or host work.',
455
+ next: 'Review the plan, add or update one item when useful, and keep status changes visible.',
456
+ modelRoute: 'agent_work_plan action:"list"',
457
+ inspectRoutes: [
458
+ 'workspace action:"action" actionId:"workplan"',
459
+ 'workspace action:"action" actionId:"workplan-add"',
460
+ 'workspace action:"action" actionId:"workplan-status"',
461
+ ],
462
+ prerequisites: ['Use the work plan when the user asks for task tracking, multi-step work, or a visible checkpoint.'],
463
+ runBoundary: 'Agent-owned work-plan edits stay local and visible; deletion/clear-completed routes require explicit confirmation.',
464
+ },
465
+ {
466
+ id: 'connected-host-task-review',
467
+ label: 'Connected-host task review',
468
+ status: hostTaskStatus,
469
+ summary: 'Inspect connected-host task state separately from Agent-owned work-plan state.',
470
+ next: hostTaskStatus === 'ready'
471
+ ? 'List host tasks, inspect one exact task id, then cancel or retry only through confirmed daemon controls when the user asks.'
472
+ : 'Update or connect the GoodVibes host until task inspection methods are present.',
473
+ modelRoute: 'workspace action:"action" actionId:"tasks-list"',
474
+ inspectRoutes: [
475
+ 'workspace action:"action" actionId:"tasks-list"',
476
+ 'workspace action:"action" actionId:"task-show"',
477
+ ...methodIds.slice(0, 6).map((methodId) => `host action:"method" methodId:"${methodId}"`),
478
+ ],
479
+ prerequisites: hostTaskStatus === 'ready'
480
+ ? [`${methodIds.length} task/work-plan daemon method(s) are discoverable.`]
481
+ : ['Connected-host task methods are not present in the current SDK operator contract.'],
482
+ runBoundary: 'Host task cancel/retry/create actions mutate connected-host state and require exact ids, confirmation, and explicit user request.',
483
+ },
484
+ ];
485
+ }
486
+
487
+ export function reminderWorkflows(methodIds: readonly string[], deliveryConfigured: boolean): readonly PersonalOpsWorkflow[] {
488
+ const scheduleStatus: PersonalOpsWorkflowStatus = methodIds.length > 0 ? 'ready' : 'needs-setup';
489
+ const reminderStatus: PersonalOpsWorkflowStatus = methodIds.length === 0 ? 'needs-setup' : deliveryConfigured ? 'ready' : 'attention';
490
+ return [
491
+ {
492
+ id: 'confirmed-reminder-request',
493
+ label: 'Confirmed reminder request',
494
+ status: reminderStatus,
495
+ summary: 'Create one visible reminder only from a direct user request with real timing and optional delivery target.',
496
+ next: reminderStatus === 'ready'
497
+ ? 'Collect title, timing, delivery scope, and explicit user request, then use the confirmed reminder route.'
498
+ : reminderStatus === 'attention'
499
+ ? 'Configure or confirm a delivery target before relying on reminder delivery, or create the reminder with explicit scope.'
500
+ : 'Update the connected GoodVibes host until schedule/reminder creation methods are available.',
501
+ modelRoute: 'schedule action:"remind"',
502
+ inspectRoutes: [
503
+ 'workspace action:"action" actionId:"schedule-reminder"',
504
+ 'channels action:"status"',
505
+ ...methodIds.filter((methodId) => methodId.startsWith('schedules.')).slice(0, 6).map((methodId) => `host action:"method" methodId:"${methodId}"`),
506
+ ],
507
+ prerequisites: [
508
+ 'The user must provide a concrete reminder title and time.',
509
+ deliveryConfigured ? 'At least one delivery target or channel is configured.' : 'No configured delivery target was detected; delivery setup may be needed.',
510
+ ],
511
+ runBoundary: 'Reminder creation requires confirm:true and explicitUserRequest; vague follow-up ideas stay as notes or work-plan items.',
512
+ },
513
+ {
514
+ id: 'connected-schedule-review',
515
+ label: 'Connected schedule review',
516
+ status: scheduleStatus,
517
+ summary: 'Review existing connected schedules before editing, running, pausing, resuming, or deleting one.',
518
+ next: scheduleStatus === 'ready'
519
+ ? 'List schedules or inspect the autonomy queue, then control only one exact schedule id through confirmed routes.'
520
+ : 'Update the connected GoodVibes host until schedule list/control methods are available.',
521
+ modelRoute: 'autonomy action:"item" queueItemId:"connected-schedules"',
522
+ inspectRoutes: [
523
+ 'workspace action:"action" actionId:"schedule-list"',
524
+ 'autonomy action:"item" queueItemId:"connected-schedules"',
525
+ 'workspace action:"action" actionId:"schedule-edit"',
526
+ ],
527
+ prerequisites: scheduleStatus === 'ready'
528
+ ? [`${methodIds.length} schedule/reminder daemon method(s) are discoverable.`]
529
+ : ['Connected-host schedule methods are not present in the current SDK operator contract.'],
530
+ runBoundary: 'Schedule edit/run/enable/disable/delete actions require exact ids, confirmation, and explicit user request.',
531
+ },
532
+ ];
533
+ }