@pellux/goodvibes-agent 1.4.4 → 1.5.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 (227) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -7
  3. package/dist/package/main.js +7424 -12241
  4. package/docs/README.md +2 -2
  5. package/docs/channels-remote-and-api.md +1 -1
  6. package/docs/getting-started.md +2 -2
  7. package/docs/release-and-publishing.md +1 -1
  8. package/docs/tools-and-commands.md +5 -5
  9. package/package.json +4 -2
  10. package/release/performance-snapshot.json +2 -2
  11. package/release/release-notes.md +11 -7
  12. package/release/release-readiness.json +6 -6
  13. package/src/agent/behavior-discovery-summary.ts +4 -18
  14. package/src/agent/calendar-registry.ts +322 -0
  15. package/src/agent/competitive-feature-inventory.ts +268 -130
  16. package/src/agent/document-registry.ts +5 -1
  17. package/src/agent/email/email-service.ts +350 -0
  18. package/src/agent/email/imap-client.ts +596 -0
  19. package/src/agent/email/smtp-client.ts +453 -0
  20. package/src/agent/ics-calendar.ts +662 -0
  21. package/src/agent/ics-timezone.ts +172 -0
  22. package/src/agent/markdown-frontmatter.ts +31 -0
  23. package/src/agent/memory-safety.ts +1 -1
  24. package/src/agent/note-registry.ts +3 -9
  25. package/src/agent/persona-discovery.ts +3 -15
  26. package/src/agent/persona-registry.ts +1 -10
  27. package/src/agent/research-source-registry.ts +5 -1
  28. package/src/agent/routine-discovery.ts +3 -15
  29. package/src/agent/runtime-profile.ts +3 -0
  30. package/src/agent/skill-discovery.ts +3 -15
  31. package/src/agent/skill-draft-proposer.ts +203 -0
  32. package/src/agent/skill-draft-runner.ts +201 -0
  33. package/src/agent/skill-registry.ts +36 -1
  34. package/src/agent/skill-standard.ts +99 -0
  35. package/src/agent/vibe-file.ts +7 -22
  36. package/src/cli/completion.ts +1 -1
  37. package/src/cli/config-overrides.ts +10 -1
  38. package/src/cli/redaction.ts +17 -5
  39. package/src/config/provider-model.ts +2 -1
  40. package/src/config/secret-config.ts +13 -6
  41. package/src/core/activity-feed.ts +97 -0
  42. package/src/core/away-digest.ts +161 -0
  43. package/src/core/conversation-rendering.ts +7 -3
  44. package/src/core/conversation.ts +22 -15
  45. package/src/core/hardware-profile.ts +362 -0
  46. package/src/core/last-seen-store.ts +78 -0
  47. package/src/core/plain-language.ts +52 -0
  48. package/src/core/setup-incomplete-hint.ts +90 -0
  49. package/src/core/system-message-router.ts +38 -87
  50. package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
  51. package/src/input/agent-workspace-basic-command-editors.ts +39 -141
  52. package/src/input/agent-workspace-categories.ts +40 -125
  53. package/src/input/agent-workspace-command-editor.ts +4 -0
  54. package/src/input/agent-workspace-host-category.ts +0 -5
  55. package/src/input/agent-workspace-local-editor-submission.ts +3 -1
  56. package/src/input/agent-workspace-navigation.ts +10 -3
  57. package/src/input/agent-workspace-onboarding-actions.ts +132 -0
  58. package/src/input/agent-workspace-onboarding-categories.ts +29 -26
  59. package/src/input/agent-workspace-onboarding-finish.ts +11 -4
  60. package/src/input/agent-workspace-onboarding-state.ts +111 -0
  61. package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
  62. package/src/input/agent-workspace-profile-editors.ts +155 -0
  63. package/src/input/agent-workspace-subscription-editor.ts +7 -0
  64. package/src/input/agent-workspace-types.ts +5 -1
  65. package/src/input/agent-workspace.ts +65 -39
  66. package/src/input/command-registry.ts +18 -5
  67. package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
  68. package/src/input/commands/agent-skills-runtime.ts +60 -3
  69. package/src/input/commands/calendar-runtime.ts +209 -0
  70. package/src/input/commands/channels-runtime.ts +1 -0
  71. package/src/input/commands/command-error.ts +9 -0
  72. package/src/input/commands/compat-runtime.ts +1 -0
  73. package/src/input/commands/config.ts +1 -0
  74. package/src/input/commands/conversation-runtime.ts +1 -1
  75. package/src/input/commands/delegation-runtime.ts +5 -6
  76. package/src/input/commands/email-runtime.ts +387 -0
  77. package/src/input/commands/experience-runtime.ts +17 -4
  78. package/src/input/commands/guidance-runtime.ts +1 -1
  79. package/src/input/commands/health-runtime.ts +6 -1
  80. package/src/input/commands/knowledge-format.ts +73 -0
  81. package/src/input/commands/knowledge.ts +9 -74
  82. package/src/input/commands/local-provider-runtime.ts +2 -1
  83. package/src/input/commands/local-runtime.ts +10 -4
  84. package/src/input/commands/mcp-runtime.ts +7 -0
  85. package/src/input/commands/notify-runtime.ts +17 -1
  86. package/src/input/commands/onboarding-runtime.ts +1 -0
  87. package/src/input/commands/operator-actions-runtime.ts +1 -0
  88. package/src/input/commands/operator-runtime.ts +3 -0
  89. package/src/input/commands/personas-runtime.ts +1 -0
  90. package/src/input/commands/platform-access-runtime.ts +40 -17
  91. package/src/input/commands/product-runtime.ts +6 -1
  92. package/src/input/commands/provider-accounts-runtime.ts +3 -2
  93. package/src/input/commands/qrcode-runtime.ts +1 -0
  94. package/src/input/commands/routines-runtime.ts +1 -0
  95. package/src/input/commands/runtime-services.ts +0 -13
  96. package/src/input/commands/security-runtime.ts +1 -0
  97. package/src/input/commands/session-content.ts +1 -0
  98. package/src/input/commands/shell-core.ts +5 -2
  99. package/src/input/commands/subscription-runtime.ts +33 -9
  100. package/src/input/commands/support-bundle-runtime.ts +8 -1
  101. package/src/input/commands/tasks-runtime.ts +1 -0
  102. package/src/input/commands/tts-runtime.ts +1 -0
  103. package/src/input/commands/vibe-runtime.ts +1 -0
  104. package/src/input/commands.ts +4 -0
  105. package/src/input/feed-context-factory.ts +3 -12
  106. package/src/input/handler-command-route.ts +7 -60
  107. package/src/input/handler-feed-routes.ts +0 -194
  108. package/src/input/handler-feed.ts +2 -54
  109. package/src/input/handler-interactions.ts +0 -2
  110. package/src/input/handler-modal-stack.ts +0 -9
  111. package/src/input/handler-picker-routes.ts +24 -1
  112. package/src/input/handler-shortcuts.ts +11 -40
  113. package/src/input/handler-ui-state.ts +13 -0
  114. package/src/input/handler.ts +8 -35
  115. package/src/input/keybindings.ts +40 -17
  116. package/src/input/model-picker-local-fit.ts +130 -0
  117. package/src/input/submission-router.ts +0 -5
  118. package/src/main.ts +111 -89
  119. package/src/renderer/activity-sidebar.ts +186 -0
  120. package/src/renderer/agent-workspace-context-lines.ts +5 -48
  121. package/src/renderer/agent-workspace-style.ts +1 -1
  122. package/src/renderer/agent-workspace.ts +14 -2
  123. package/src/renderer/compositor.ts +37 -125
  124. package/src/renderer/conversation-overlays.ts +3 -3
  125. package/src/renderer/help-overlay.ts +7 -5
  126. package/src/renderer/model-workspace.ts +101 -86
  127. package/src/{panels → renderer}/polish.ts +3 -3
  128. package/src/renderer/shell-surface.ts +4 -5
  129. package/src/renderer/status-token.ts +31 -11
  130. package/src/renderer/tab-strip.ts +1 -1
  131. package/src/renderer/tool-call.ts +7 -8
  132. package/src/renderer/tool-labels.ts +92 -0
  133. package/src/renderer/ui-factory.ts +48 -96
  134. package/src/runtime/bootstrap-command-context.ts +0 -5
  135. package/src/runtime/bootstrap-command-parts.ts +6 -15
  136. package/src/runtime/bootstrap-core.ts +34 -13
  137. package/src/runtime/bootstrap-hook-bridge.ts +3 -1
  138. package/src/runtime/bootstrap-shell.ts +3 -50
  139. package/src/runtime/bootstrap.ts +3 -4
  140. package/src/runtime/context.ts +1 -1
  141. package/src/runtime/diagnostics/panels/index.ts +0 -1
  142. package/src/runtime/diagnostics/panels/policy.ts +1 -1
  143. package/src/runtime/execution-ledger.ts +16 -1
  144. package/src/runtime/index.ts +6 -1
  145. package/src/runtime/onboarding/index.ts +1 -0
  146. package/src/runtime/onboarding/onboarding-state.ts +200 -0
  147. package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
  148. package/src/runtime/services.ts +16 -4
  149. package/src/runtime/terminal-output-guard.ts +7 -0
  150. package/src/runtime/tool-permission-safety.ts +1 -1
  151. package/src/runtime/ui/model-picker/data-provider.ts +1 -1
  152. package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
  153. package/src/runtime/ui/model-picker/types.ts +2 -2
  154. package/src/runtime/ui/provider-health/data-provider.ts +3 -3
  155. package/src/runtime/ui/provider-health/types.ts +2 -2
  156. package/src/runtime/ui-services.ts +0 -2
  157. package/src/shell/agent-workspace-fullscreen.ts +0 -1
  158. package/src/shell/autonomy-surfacing.ts +272 -0
  159. package/src/shell/session-continuity-hints.ts +0 -6
  160. package/src/shell/startup-wiring.ts +221 -0
  161. package/src/shell/ui-openers.ts +18 -29
  162. package/src/tools/agent-context-policy.ts +1 -1
  163. package/src/tools/agent-harness-background-processes.ts +4 -4
  164. package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
  165. package/src/tools/agent-harness-command-runner.ts +6 -1
  166. package/src/tools/agent-harness-document-ops.ts +26 -53
  167. package/src/tools/agent-harness-execution-history.ts +1 -13
  168. package/src/tools/agent-harness-execution-posture.ts +0 -15
  169. package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
  170. package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
  171. package/src/tools/agent-harness-metadata.ts +16 -0
  172. package/src/tools/agent-harness-mode-catalog.ts +2 -9
  173. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  174. package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
  175. package/src/tools/agent-harness-tool-schema.ts +13 -14
  176. package/src/tools/agent-harness-tool.ts +27 -23
  177. package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
  178. package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
  179. package/src/tools/agent-workspace-tool.ts +2 -33
  180. package/src/utils/terminal-width.ts +9 -3
  181. package/src/version.ts +1 -1
  182. package/src/input/agent-workspace-panel-route.ts +0 -44
  183. package/src/input/panel-integration-actions.ts +0 -26
  184. package/src/panels/approval-panel.ts +0 -149
  185. package/src/panels/automation-control-panel.ts +0 -212
  186. package/src/panels/base-panel.ts +0 -254
  187. package/src/panels/builtin/agent.ts +0 -58
  188. package/src/panels/builtin/knowledge.ts +0 -26
  189. package/src/panels/builtin/operations.ts +0 -121
  190. package/src/panels/builtin/session.ts +0 -138
  191. package/src/panels/builtin/shared.ts +0 -275
  192. package/src/panels/builtin/usage.ts +0 -21
  193. package/src/panels/builtin-panels.ts +0 -23
  194. package/src/panels/confirm-state.ts +0 -61
  195. package/src/panels/context-visualizer-panel.ts +0 -204
  196. package/src/panels/cost-tracker-panel.ts +0 -444
  197. package/src/panels/docs-panel.ts +0 -285
  198. package/src/panels/index.ts +0 -25
  199. package/src/panels/knowledge-panel.ts +0 -417
  200. package/src/panels/memory-panel.ts +0 -226
  201. package/src/panels/panel-list-panel.ts +0 -464
  202. package/src/panels/panel-manager.ts +0 -570
  203. package/src/panels/provider-accounts-panel.ts +0 -233
  204. package/src/panels/provider-health-domains.ts +0 -208
  205. package/src/panels/provider-health-panel.ts +0 -720
  206. package/src/panels/provider-health-tracker.ts +0 -115
  207. package/src/panels/provider-stats-panel.ts +0 -366
  208. package/src/panels/qr-panel.ts +0 -207
  209. package/src/panels/schedule-panel.ts +0 -321
  210. package/src/panels/scrollable-list-panel.ts +0 -491
  211. package/src/panels/search-focus.ts +0 -32
  212. package/src/panels/security-panel.ts +0 -295
  213. package/src/panels/session-browser-panel.ts +0 -395
  214. package/src/panels/session-maintenance.ts +0 -125
  215. package/src/panels/subscription-panel.ts +0 -263
  216. package/src/panels/system-messages-panel.ts +0 -230
  217. package/src/panels/tasks-panel.ts +0 -344
  218. package/src/panels/thinking-panel.ts +0 -304
  219. package/src/panels/token-budget-panel.ts +0 -475
  220. package/src/panels/tool-inspector-panel.ts +0 -436
  221. package/src/panels/types.ts +0 -54
  222. package/src/renderer/panel-composite.ts +0 -158
  223. package/src/renderer/panel-tab-bar.ts +0 -69
  224. package/src/renderer/panel-workspace-bar.ts +0 -42
  225. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  226. package/src/tools/agent-harness-panel-metadata.ts +0 -211
  227. /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
@@ -11,9 +11,6 @@ type AgentWorkspaceToolAction =
11
11
  | 'surfaces'
12
12
  | 'surface'
13
13
  | 'open'
14
- | 'panels'
15
- | 'panel'
16
- | 'open_panel'
17
14
  | 'shortcuts'
18
15
  | 'keybindings'
19
16
  | 'keybinding'
@@ -33,7 +30,6 @@ interface AgentWorkspaceToolArgs {
33
30
  readonly actionId?: unknown;
34
31
  readonly workspaceActionId?: unknown;
35
32
  readonly surfaceId?: unknown;
36
- readonly panelId?: unknown;
37
33
  readonly command?: unknown;
38
34
  readonly commandName?: unknown;
39
35
  readonly args?: unknown;
@@ -47,7 +43,6 @@ interface AgentWorkspaceToolArgs {
47
43
  readonly combos?: unknown;
48
44
  readonly key?: unknown;
49
45
  readonly value?: unknown;
50
- readonly pane?: unknown;
51
46
  readonly includeParameters?: unknown;
52
47
  readonly limit?: unknown;
53
48
  readonly confirm?: unknown;
@@ -79,9 +74,6 @@ function normalizeWorkspaceAction(value: unknown): AgentWorkspaceToolAction | nu
79
74
  if (action === 'surfaces' || action === 'ui_surfaces' || action === 'screens' || action === 'views') return 'surfaces';
80
75
  if (action === 'surface' || action === 'ui_surface' || action === 'screen' || action === 'view') return 'surface';
81
76
  if (action === 'open' || action === 'navigate' || action === 'open_surface' || action === 'open_ui_surface') return 'open';
82
- if (action === 'panels' || action === 'panes') return 'panels';
83
- if (action === 'panel' || action === 'pane') return 'panel';
84
- if (action === 'open_panel' || action === 'open_pane') return 'open_panel';
85
77
  if (action === 'shortcuts' || action === 'shortcut_help' || action === 'help') return 'shortcuts';
86
78
  if (action === 'keybindings' || action === 'bindings' || action === 'keys') return 'keybindings';
87
79
  if (action === 'keybinding' || action === 'binding' || action === 'key') return 'keybinding';
@@ -100,7 +92,6 @@ function readAction(args: AgentWorkspaceToolArgs): AgentWorkspaceToolAction {
100
92
  const explicit = normalizeWorkspaceAction(args.action) ?? normalizeWorkspaceAction(args.mode);
101
93
  if (explicit) return explicit;
102
94
  if (readString(args.surfaceId)) return 'surface';
103
- if (readString(args.panelId)) return 'panel';
104
95
  if (readString(args.actionId) || readString(args.workspaceActionId)) return 'action';
105
96
  if (readString(args.command) || readString(args.commandName)) return 'command';
106
97
  if (readString(args.key)) return 'keybinding';
@@ -120,10 +111,6 @@ function surfaceLookup(args: AgentWorkspaceToolArgs): string {
120
111
  return readString(args.surfaceId) || readString(args.id);
121
112
  }
122
113
 
123
- function panelLookup(args: AgentWorkspaceToolArgs): string {
124
- return readString(args.panelId) || readString(args.id);
125
- }
126
-
127
114
  function confirmedArgs(args: AgentWorkspaceToolArgs): Record<string, unknown> {
128
115
  return compactArgs({
129
116
  confirm: args.confirm,
@@ -173,19 +160,6 @@ function surfaceArgs(mode: 'ui_surface' | 'open_ui_surface', args: AgentWorkspac
173
160
  });
174
161
  }
175
162
 
176
- function panelArgs(mode: 'panel' | 'open_panel', args: AgentWorkspaceToolArgs): Record<string, unknown> {
177
- const panelId = panelLookup(args);
178
- return compactArgs({
179
- mode,
180
- panelId,
181
- target: panelId ? undefined : args.target,
182
- query: panelId ? undefined : args.query,
183
- pane: args.pane,
184
- includeParameters: mode === 'panel' ? args.includeParameters : undefined,
185
- ...(mode === 'open_panel' ? confirmedArgs(args) : {}),
186
- });
187
- }
188
-
189
163
  function keybindingArgs(mode: 'keybinding' | 'run_keybinding' | 'set_keybinding' | 'reset_keybinding', args: AgentWorkspaceToolArgs): Record<string, unknown> {
190
164
  const actionId = lookupId(args);
191
165
  return compactArgs({
@@ -232,15 +206,14 @@ export function createAgentWorkspaceTool(deps: AgentWorkspaceToolDeps): Tool {
232
206
  properties: {
233
207
  action: {
234
208
  type: 'string',
235
- enum: ['status', 'actions', 'action', 'run', 'surfaces', 'surface', 'open', 'panels', 'panel', 'open_panel', 'shortcuts', 'keybindings', 'keybinding', 'run_keybinding', 'set_keybinding', 'reset_keybinding', 'commands', 'command', 'run_command', 'cli_commands', 'cli_command'],
209
+ enum: ['status', 'actions', 'action', 'run', 'surfaces', 'surface', 'open', 'shortcuts', 'keybindings', 'keybinding', 'run_keybinding', 'set_keybinding', 'reset_keybinding', 'commands', 'command', 'run_command', 'cli_commands', 'cli_command'],
236
210
  description: 'Inspect catalogs, open UI, or run approved workspace actions.',
237
211
  },
238
212
  mode: { type: 'string', description: 'Alias for action.' },
239
- id: { type: 'string', description: 'Generic action, surface, panel, or keybinding id.' },
213
+ id: { type: 'string', description: 'Generic action, surface, or keybinding id.' },
240
214
  actionId: { type: 'string', description: 'Workspace action or keybinding action id.' },
241
215
  workspaceActionId: { type: 'string', description: 'Workspace action id alias.' },
242
216
  surfaceId: { type: 'string', description: 'UI surface id.' },
243
- panelId: { type: 'string', description: 'Panel id.' },
244
217
  command: { type: 'string', description: 'Slash or CLI command string.' },
245
218
  commandName: { type: 'string', description: 'Slash or CLI command name.' },
246
219
  args: { type: 'array', items: { type: 'string' }, description: 'Command arguments when commandName is used.' },
@@ -254,7 +227,6 @@ export function createAgentWorkspaceTool(deps: AgentWorkspaceToolDeps): Tool {
254
227
  combos: { type: 'array', items: { type: 'object' }, description: 'Multiple keybinding combos.' },
255
228
  key: { type: 'string', description: 'Keybinding lookup key.' },
256
229
  value: { anyOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], description: 'Keybinding value such as Ctrl+G.' },
257
- pane: { type: 'string', enum: ['top', 'bottom'], description: 'Preferred pane for panel open.' },
258
230
  includeParameters: { type: 'boolean', description: 'Include detailed schemas or route metadata.' },
259
231
  limit: { type: 'number', description: 'Maximum rows returned for catalog actions.' },
260
232
  confirm: { type: 'boolean', description: 'Required true for UI opens and side-effecting actions.' },
@@ -276,9 +248,6 @@ export function createAgentWorkspaceTool(deps: AgentWorkspaceToolDeps): Tool {
276
248
  if (action === 'surfaces') return harnessTool.execute(compactArgs({ mode: 'ui_surfaces', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
277
249
  if (action === 'surface') return harnessTool.execute(surfaceArgs('ui_surface', args));
278
250
  if (action === 'open') return harnessTool.execute(surfaceArgs('open_ui_surface', args));
279
- if (action === 'panels') return harnessTool.execute(compactArgs({ mode: 'panels', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
280
- if (action === 'panel') return harnessTool.execute(panelArgs('panel', args));
281
- if (action === 'open_panel') return harnessTool.execute(panelArgs('open_panel', args));
282
251
  if (action === 'shortcuts') return harnessTool.execute(compactArgs({ mode: 'shortcuts', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
283
252
  if (action === 'keybindings') return harnessTool.execute(compactArgs({ mode: 'keybindings', target: args.target, query: args.query, limit: args.limit, includeParameters: args.includeParameters }));
284
253
  if (action === 'keybinding') return harnessTool.execute(keybindingArgs('keybinding', args));
@@ -139,14 +139,20 @@ export function wrapText(text: string, width: number): string[] {
139
139
  if (currentLine) lines.push(currentLine);
140
140
  let remaining = word;
141
141
  while (getDisplayWidth(remaining) > width) {
142
+ const codePoints = [...remaining];
142
143
  let splitIdx = 0;
143
144
  let currentWidth = 0;
144
- for (let i = 0; i < remaining.length; i++) {
145
- const charWidth = getDisplayWidth(remaining[i]!);
145
+ for (const cp of codePoints) {
146
+ const charWidth = getDisplayWidth(cp);
146
147
  if (currentWidth + charWidth > width) break;
147
148
  currentWidth += charWidth;
148
- splitIdx = i + 1;
149
+ splitIdx += cp.length;
149
150
  }
151
+ // Safety: guarantee at least one code point of progress even if a
152
+ // single grapheme is wider than the available width (e.g. CJK/emoji
153
+ // at width===1). Without this guard splitIdx stays 0, remaining
154
+ // never shrinks, and the while-loop hangs.
155
+ if (splitIdx === 0) splitIdx = codePoints[0]!.length;
150
156
  lines.push(remaining.slice(0, splitIdx));
151
157
  remaining = remaining.slice(splitIdx);
152
158
  }
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '1.4.4';
9
+ let _version = '1.5.0';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
12
12
  readonly version?: unknown;
@@ -1,44 +0,0 @@
1
- import type { AgentWorkspaceCategoryId } from './agent-workspace-types.ts';
2
-
3
- const PANEL_CATEGORY_ROUTES = {
4
- knowledge: 'knowledge',
5
- memory: 'memory',
6
- 'work-plan': 'work',
7
- 'project-planning': 'work',
8
- plan: 'work',
9
- approval: 'work',
10
- tasks: 'work',
11
- 'provider-health': 'setup',
12
- automation: 'automation',
13
- schedule: 'automation',
14
- providers: 'setup',
15
- accounts: 'setup',
16
- subscription: 'setup',
17
- cost: 'setup',
18
- tokens: 'setup',
19
- security: 'tools',
20
- policy: 'tools',
21
- tools: 'tools',
22
- 'qr-code': 'channels',
23
- sessions: 'conversation',
24
- context: 'conversation',
25
- thinking: 'conversation',
26
- 'system-messages': 'conversation',
27
- docs: 'home',
28
- 'panel-list': 'home',
29
- } as const satisfies Readonly<Record<string, AgentWorkspaceCategoryId>>;
30
-
31
- type AgentWorkspacePanelRouteId = keyof typeof PANEL_CATEGORY_ROUTES;
32
-
33
- function hasPanelCategoryRoute(panelId: string): panelId is AgentWorkspacePanelRouteId {
34
- return Object.prototype.hasOwnProperty.call(PANEL_CATEGORY_ROUTES, panelId);
35
- }
36
-
37
- export function agentWorkspaceCategoryForPanel(panelId: string): AgentWorkspaceCategoryId {
38
- return hasPanelCategoryRoute(panelId) ? PANEL_CATEGORY_ROUTES[panelId] : 'home';
39
- }
40
-
41
- export function agentWorkspaceCommandForPanel(panelId: string): string {
42
- const category = agentWorkspaceCategoryForPanel(panelId);
43
- return category === 'setup' ? '/agent' : `/agent ${category}`;
44
- }
@@ -1,26 +0,0 @@
1
- import type { CommandContext } from './command-registry.ts';
2
- import { logger } from '@pellux/goodvibes-sdk/platform/utils';
3
- import type { Panel } from '../panels/types.ts';
4
- import type { PanelManager } from '../panels/panel-manager.ts';
5
- import { ApprovalPanel } from '../panels/approval-panel.ts';
6
-
7
- export function handlePanelIntegrationAction(
8
- panelManager: PanelManager,
9
- activePanel: Panel | null,
10
- key: string,
11
- commandContext?: CommandContext,
12
- ): boolean {
13
- if (!activePanel) return false;
14
-
15
- if ((key === 'enter' || key === 'return') && activePanel instanceof ApprovalPanel) {
16
- const command = activePanel.getSelectedCommand();
17
- if (!command || !commandContext?.executeCommand) return false;
18
- const parts = command.replace(/^\//, '').split(/\s+/).filter(Boolean);
19
- const [name, ...args] = parts;
20
- if (!name) return false;
21
- void commandContext.executeCommand(name, args).catch((err) => { logger.debug('approval panel command dispatch failed', { err }); });
22
- return true;
23
- }
24
-
25
- return false;
26
- }
@@ -1,149 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { ScrollableListPanel } from './scrollable-list-panel.ts';
3
- import {
4
- buildGuidanceLine,
5
- buildKeyValueLine,
6
- buildPanelLine,
7
- DEFAULT_PANEL_PALETTE,
8
- } from './polish.ts';
9
- import type { PolicyRuntimeState } from '@/runtime/index.ts';
10
- import { buildPermissionRuleSuggestions } from '@/runtime/index.ts';
11
-
12
- const C = {
13
- ...DEFAULT_PANEL_PALETTE,
14
- headerBg: '#111827',
15
- } as const;
16
-
17
- const APPROVAL_ROWS = [
18
- ['shell', 'why prompted: side effects, destructive ops, secret exposure, escalation', 'review via /approval review shell'],
19
- ['file', 'why prompted: config mutation, notebook edits, secret-bearing paths', 'review via /approval review file'],
20
- ['network', 'why prompted: external hosts, fetch scope, egress policy', 'review via /approval review network'],
21
- ['delegate', 'why prompted: explicit TUI build handoff, write scope, result ownership', 'review via /approval review delegate'],
22
- ['mcp', 'why prompted: trust escalation, host scope, path scope, coherence mismatch', 'review via /approval review mcp'],
23
- ['remote', 'why prompted: build-host trust, remote write scope, artifact requirements', 'review via /approval review remote'],
24
- ['hook', 'why prompted: deny/mutate authority, blocking behavior, execution provenance', 'review via /approval review hook'],
25
- ['plugin', 'why prompted: install/update lifecycle, provenance, capability grants', 'review via /approval review plugin'],
26
- ] as const;
27
-
28
- type ApprovalRow = (typeof APPROVAL_ROWS)[number];
29
-
30
- export class ApprovalPanel extends ScrollableListPanel<ApprovalRow> {
31
- private readonly policyRuntimeState: Pick<PolicyRuntimeState, 'getSnapshot'>;
32
-
33
- public constructor(policyRuntimeState: Pick<PolicyRuntimeState, 'getSnapshot'>) {
34
- super('approval', 'Approval', 'A', 'monitoring');
35
- this.showSelectionGutter = true; // I5: non-color selection affordance
36
- this.policyRuntimeState = policyRuntimeState;
37
- }
38
-
39
- protected override getPalette() { return C; }
40
- protected override getEmptyStateMessage() { return ' No approval lanes defined.'; }
41
-
42
- protected getItems(): readonly ApprovalRow[] {
43
- return APPROVAL_ROWS;
44
- }
45
-
46
- protected renderItem(row: ApprovalRow, index: number, selected: boolean, width: number): Line {
47
- const bg = selected ? C.selectBg : undefined;
48
- return buildPanelLine(width, [
49
- [' ', C.label],
50
- [row[0].padEnd(10), C.info, bg],
51
- [row[1].slice(0, Math.max(0, width - 18)), C.value, bg],
52
- ]);
53
- }
54
-
55
- public handleInput(key: string): boolean {
56
- if (key === 'home') {
57
- this.selectedIndex = 0;
58
- this.markDirty();
59
- return true;
60
- }
61
- if (key === 'end') {
62
- this.selectedIndex = APPROVAL_ROWS.length - 1;
63
- this.markDirty();
64
- return true;
65
- }
66
- if (key === 'enter' || key === 'return') {
67
- return true;
68
- }
69
- return super.handleInput(key);
70
- }
71
-
72
- public getSelectedCommand(): string | null {
73
- const selected = APPROVAL_ROWS[this.selectedIndex] ?? null;
74
- return selected ? selected[2].replace('review via ', '').trim() : null;
75
- }
76
-
77
- public render(width: number, height: number): Line[] {
78
- this.clampSelection();
79
- const policySnapshot = this.policyRuntimeState.getSnapshot();
80
- const approvalCount = policySnapshot.recentPermissionAudit.filter((e) => e.approved === true).length;
81
- const denialCount = policySnapshot.recentPermissionAudit.filter((e) => e.approved === false).length;
82
- const pendingCount = policySnapshot.recentPermissionAudit.filter((e) => e.approved === undefined).length;
83
-
84
- const selected = APPROVAL_ROWS[this.selectedIndex] ?? null;
85
- const detailLines: Line[] = [];
86
- if (selected) {
87
- detailLines.push(buildPanelLine(width, [[' Selected Lane', C.label]]));
88
- detailLines.push(buildKeyValueLine(width, [
89
- { label: 'lane', value: selected[0], valueColor: C.info },
90
- { label: 'next review', value: selected[2], valueColor: C.dim },
91
- ], C));
92
- detailLines.push(buildPanelLine(width, [[` ${selected[1]}`, C.value]]));
93
- detailLines.push(buildGuidanceLine(width, selected[2].replace('review via ', ''), `open the ${selected[0]} review path`, C));
94
- }
95
-
96
- const recentAuditLines: Line[] = [];
97
- for (const entry of policySnapshot.recentPermissionAudit.slice(0, 5)) {
98
- const decision = entry.approved === undefined ? 'pending' : entry.approved ? 'approved' : 'denied';
99
- const decisionColor = entry.approved === undefined ? C.info : entry.approved ? C.good : C.bad;
100
- recentAuditLines.push(buildPanelLine(width, [
101
- [` ${decision.padEnd(8)}`, decisionColor],
102
- [`${entry.tool}`.padEnd(14), C.label],
103
- [entry.summary.slice(0, Math.max(0, width - 28)), C.value],
104
- ]));
105
- if (entry.reasons[0]) {
106
- recentAuditLines.push(buildPanelLine(width, [[` ${entry.reasons[0]}`, C.dim]]));
107
- }
108
- }
109
- if (recentAuditLines.length === 0) {
110
- recentAuditLines.push(buildPanelLine(width, [[` No recent approval pressure. Live requests and decisions will appear here.`, C.dim]]));
111
- }
112
-
113
- const ruleSuggestionLines: Line[] = [];
114
- for (const suggestion of buildPermissionRuleSuggestions(policySnapshot.recentPermissionAudit).slice(0, 3)) {
115
- ruleSuggestionLines.push(buildPanelLine(width, [[` ${suggestion.summary}`, C.info]]));
116
- ruleSuggestionLines.push(buildGuidanceLine(width, suggestion.command, suggestion.reason, C));
117
- }
118
- if (ruleSuggestionLines.length === 0) {
119
- ruleSuggestionLines.push(buildPanelLine(width, [[` No repeated denials currently suggest a durable rule.`, C.dim]]));
120
- }
121
-
122
- const headerLines: Line[] = [
123
- buildPanelLine(width, [[' Approval posture', C.label]]),
124
- buildKeyValueLine(width, [
125
- { label: 'why prompted', value: 'risk summary', valueColor: C.value },
126
- { label: 'what-if', value: '/security review', valueColor: C.info },
127
- { label: 'operator', value: '/approval + /security', valueColor: C.good },
128
- ], C),
129
- buildPanelLine(width, [
130
- [' \u2713 ', C.good],
131
- [`approvals (${approvalCount}) `, C.good],
132
- ['\u2715 ', C.bad],
133
- [`denials (${denialCount}) `, C.bad],
134
- ['\u25cb ', C.info],
135
- [`pending (${pendingCount})`, C.info],
136
- ]),
137
- buildGuidanceLine(width, '/approval review shell', 'inspect the highest-risk approval lane and refine scoped review posture', C),
138
- ...detailLines,
139
- ...recentAuditLines,
140
- ...ruleSuggestionLines,
141
- ];
142
-
143
- return this.renderList(width, height, {
144
- title: 'Approval Control Room',
145
- header: headerLines,
146
- footer: [buildPanelLine(width, [[` Up/Down move Home/End jump selected lane opens the next command path`, C.dim]])],
147
- });
148
- }
149
- }
@@ -1,212 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { createEmptyLine } from '../types/grid.ts';
3
- import { ScrollableListPanel } from './scrollable-list-panel.ts';
4
- import type { UiAutomationSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
5
- import { truncateDisplay } from '../utils/terminal-width.ts';
6
- import {
7
- buildEmptyState,
8
- buildGuidanceLine,
9
- buildKeyValueLine,
10
- buildPanelLine,
11
- buildPanelWorkspace,
12
- DEFAULT_PANEL_PALETTE,
13
- type PanelPalette,
14
- } from './polish.ts';
15
-
16
- const C = {
17
- ...DEFAULT_PANEL_PALETTE,
18
- header: '#94a3b8',
19
- headerBg: '#1e293b',
20
- ok: '#22c55e',
21
- warn: '#eab308',
22
- error: '#ef4444',
23
- info: '#38bdf8',
24
- selectBg: '#0f172a',
25
- } as const;
26
-
27
- function formatTime(value?: number): string {
28
- if (!value) return 'n/a';
29
- return new Date(value).toLocaleString();
30
- }
31
-
32
- function runStatusColor(status: string): string {
33
- if (status === 'completed') return C.ok;
34
- if (status === 'failed' || status === 'dead_lettered') return C.error;
35
- if (status === 'cancelled') return C.warn;
36
- return C.info;
37
- }
38
-
39
- type AutomationRun = UiAutomationSnapshot['runs'][number];
40
- type AutomationJob = UiAutomationSnapshot['jobs'][number];
41
-
42
- export class AutomationControlPanel extends ScrollableListPanel<AutomationRun> {
43
- private readonly readModel?: UiReadModel<UiAutomationSnapshot>;
44
- private readonly unsub: (() => void) | null;
45
-
46
- public constructor(readModel?: UiReadModel<UiAutomationSnapshot>) {
47
- super('automation', 'Automation', 'M', 'monitoring');
48
- this.showSelectionGutter = true; // I5: non-color selection affordance
49
- this.readModel = readModel;
50
- this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
51
- }
52
-
53
- public override onDestroy(): void {
54
- this.unsub?.();
55
- }
56
-
57
- protected override getPalette(): PanelPalette {
58
- return C;
59
- }
60
-
61
- private getJobs(): readonly AutomationJob[] {
62
- if (!this.readModel) return [];
63
- return this.readModel.getSnapshot().jobs;
64
- }
65
-
66
- protected getItems(): readonly AutomationRun[] {
67
- if (!this.readModel) return [];
68
- return this.readModel.getSnapshot().runs;
69
- }
70
-
71
- protected renderItem(run: AutomationRun, _index: number, selected: boolean, width: number): Line {
72
- const bg = selected ? C.selectBg : undefined;
73
- const jobs = this.getJobs();
74
- const name = jobs.find((job) => job.id === run.jobId)?.name ?? run.jobId;
75
- return buildPanelLine(width, [
76
- [' ', C.label, bg],
77
- [run.status.padEnd(11), runStatusColor(run.status), bg],
78
- [` ${truncateDisplay(name, 22).padEnd(22)}`, C.value, bg],
79
- [` ${truncateDisplay(run.target.kind, 12).padEnd(12)}`, C.info, bg],
80
- [` ${truncateDisplay(formatTime(run.queuedAt), Math.max(0, width - 49))}`, C.dim, bg],
81
- ]);
82
- }
83
-
84
- protected override getEmptyStateMessage(): string {
85
- return ' No automation activity recorded.';
86
- }
87
-
88
- protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
89
- return [
90
- { command: '/schedule list', summary: 'inspect jobs and run history without mutating schedules' },
91
- { command: '/schedule receipts', summary: 'review Agent routine promotion receipts' },
92
- ];
93
- }
94
-
95
- public render(width: number, height: number): Line[] {
96
- const intro = 'Automation jobs, active runs, deliveries, and failure posture across the shared runtime.';
97
-
98
- if (!this.readModel) {
99
- const workspace = buildPanelWorkspace(width, height, {
100
- title: 'Automation Control',
101
- intro,
102
- sections: [{
103
- lines: buildEmptyState(
104
- width,
105
- ' Connected-host automation state is unavailable.',
106
- 'This operator view is read-only. Use /schedule list for compact transcript output; automation stays unchanged.',
107
- [{ command: '/schedule list', summary: 'review automation without starting hidden work' }],
108
- C,
109
- ),
110
- }],
111
- palette: C,
112
- });
113
- while (workspace.length < height) workspace.push(createEmptyLine(width));
114
- return workspace;
115
- }
116
-
117
- const snapshot = this.readModel.getSnapshot();
118
- const jobs = [...snapshot.jobs];
119
- const runs = this.getItems();
120
-
121
- const headerLines: Line[] = [
122
- buildKeyValueLine(width, [
123
- { label: 'jobs', value: String(snapshot.totalJobs), valueColor: snapshot.totalJobs > 0 ? C.info : C.dim },
124
- { label: 'runs', value: String(snapshot.totalRuns), valueColor: snapshot.totalRuns > 0 ? C.value : C.dim },
125
- { label: 'active', value: String(snapshot.activeRunIds.length), valueColor: snapshot.activeRunIds.length > 0 ? C.warn : C.dim },
126
- { label: 'failed', value: String(snapshot.totalFailed), valueColor: snapshot.totalFailed > 0 ? C.error : C.dim },
127
- ], C),
128
- buildKeyValueLine(width, [
129
- { label: 'deliveries ok', value: String(snapshot.deliveryTotals.succeeded), valueColor: snapshot.deliveryTotals.succeeded > 0 ? C.ok : C.dim },
130
- { label: 'delivery fail', value: String(snapshot.deliveryTotals.failed), valueColor: snapshot.deliveryTotals.failed > 0 ? C.error : C.dim },
131
- { label: 'dead letters', value: String(snapshot.deliveryTotals.deadLettered), valueColor: snapshot.deliveryTotals.deadLettered > 0 ? C.warn : C.dim },
132
- { label: 'sources', value: String(snapshot.sourceCount), valueColor: snapshot.sourceCount > 0 ? C.info : C.dim },
133
- ], C),
134
- buildGuidanceLine(width, '/schedule list', 'read-only in Agent; schedule mutation/run controls remain blocked here', C),
135
- ];
136
-
137
- if (jobs.length === 0 && runs.length === 0) {
138
- return this.renderList(width, height, {
139
- title: 'Automation Control',
140
- header: headerLines,
141
- emptyMessage: ' No automation activity recorded.',
142
- });
143
- }
144
-
145
- this.clampSelection();
146
- const selectedRun = runs[this.selectedIndex];
147
- const jobName = selectedRun ? (jobs.find((job) => job.id === selectedRun.jobId)?.name ?? selectedRun.jobId) : 'n/a';
148
-
149
- const footerLines: Line[] = [];
150
- if (selectedRun) {
151
- footerLines.push(
152
- buildPanelLine(width, [
153
- [' Run: ', C.label],
154
- [selectedRun.id, C.value],
155
- [' Status: ', C.label],
156
- [selectedRun.status, runStatusColor(selectedRun.status)],
157
- ]),
158
- buildPanelLine(width, [
159
- [' Job: ', C.label],
160
- [jobName, C.value],
161
- [' Agent: ', C.label],
162
- [selectedRun.agentId ?? 'n/a', C.info],
163
- ]),
164
- buildPanelLine(width, [
165
- [' Queue: ', C.label],
166
- [formatTime(selectedRun.queuedAt), C.dim],
167
- [' End: ', C.label],
168
- [formatTime(selectedRun.endedAt), C.dim],
169
- ]),
170
- buildPanelLine(width, [
171
- [' Trigger: ', C.label],
172
- [selectedRun.triggeredBy.kind, C.info],
173
- [' Target: ', C.label],
174
- [selectedRun.target.kind, C.value],
175
- ]),
176
- buildPanelLine(width, [
177
- [' Deliveries: ', C.label],
178
- [String(selectedRun.deliveryIds.length), selectedRun.deliveryIds.length > 0 ? C.info : C.dim],
179
- [' Route: ', C.label],
180
- [selectedRun.routeId ?? 'n/a', C.dim],
181
- ]),
182
- );
183
- if (selectedRun.error) {
184
- footerLines.push(buildPanelLine(width, [
185
- [' Error ', C.label],
186
- [truncateDisplay(selectedRun.error, Math.max(0, width - 10)), C.error],
187
- ]));
188
- }
189
- } else {
190
- footerLines.push(buildPanelLine(width, [[' No run selected.', C.dim]]));
191
- }
192
-
193
- // Jobs quick view
194
- if (jobs.length > 0) {
195
- footerLines.push(
196
- ...jobs.slice(0, 6).map((job) => buildPanelLine(width, [
197
- [' ', C.label],
198
- [job.enabled ? 'ENABLED ' : 'PAUSED ', job.enabled ? C.ok : C.warn],
199
- [truncateDisplay(job.name, 24).padEnd(24), C.value],
200
- [` next ${truncateDisplay(formatTime(job.nextRunAt), Math.max(0, width - 43))}`, C.dim],
201
- ])),
202
- );
203
- }
204
- footerLines.push(buildPanelLine(width, [[' Up/Down move through runs', C.dim]]));
205
-
206
- return this.renderList(width, height, {
207
- title: 'Automation Control',
208
- header: headerLines,
209
- footer: footerLines,
210
- });
211
- }
212
- }