@pellux/goodvibes-agent 1.5.5 → 1.5.6

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 (165) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +1 -1
  3. package/dist/package/main.js +94130 -79335
  4. package/docs/tools-and-commands.md +2 -1
  5. package/package.json +6 -3
  6. package/src/agent/autonomy-schedule-format.ts +1 -1
  7. package/src/agent/autonomy-schedule.ts +3 -3
  8. package/src/agent/calendar/calendar-oauth-service.ts +324 -0
  9. package/src/agent/calendar-subscription-registry.ts +497 -0
  10. package/src/agent/email/email-service.ts +57 -0
  11. package/src/agent/email/smtp-client.ts +30 -10
  12. package/src/agent/memory-prompt.ts +143 -12
  13. package/src/agent/operator-actions.ts +26 -22
  14. package/src/agent/prompt-context-receipts.ts +45 -18
  15. package/src/agent/reminder-schedule-format.ts +1 -1
  16. package/src/agent/reminder-schedule.ts +3 -3
  17. package/src/agent/routine-schedule-format.ts +2 -2
  18. package/src/agent/routine-schedule-promotion.ts +5 -5
  19. package/src/agent/routine-schedule-receipts.ts +2 -2
  20. package/src/agent/schedule-edit-format.ts +1 -1
  21. package/src/agent/schedule-edit.ts +5 -5
  22. package/src/agent/session-registration.ts +281 -0
  23. package/src/agent/vibe-file.ts +50 -0
  24. package/src/cli/local-library-command-shared.ts +146 -0
  25. package/src/cli/management.ts +2 -1
  26. package/src/cli/personas-command.ts +252 -0
  27. package/src/cli/resume-relaunch-notice.ts +151 -0
  28. package/src/cli/routines-command.ts +25 -2
  29. package/src/cli/service-posture.ts +3 -2
  30. package/src/cli/skill-bundle-command.ts +175 -0
  31. package/src/cli/skills-command.ts +309 -0
  32. package/src/cli/status.ts +43 -7
  33. package/src/cli/tui-startup.ts +24 -1
  34. package/src/config/agent-settings-policy.ts +0 -1
  35. package/src/config/secret-config.ts +4 -0
  36. package/src/core/conversation-rendering.ts +20 -11
  37. package/src/core/focus-tracker.ts +41 -0
  38. package/src/core/system-message-noise.ts +108 -0
  39. package/src/core/system-message-router.ts +54 -1
  40. package/src/core/thinking-overlay.ts +83 -0
  41. package/src/input/agent-workspace-access-command-editor-submission.ts +75 -108
  42. package/src/input/agent-workspace-access-command-editors.ts +108 -128
  43. package/src/input/agent-workspace-activation.ts +15 -0
  44. package/src/input/agent-workspace-basic-command-editor-submission.ts +292 -513
  45. package/src/input/agent-workspace-basic-command-editors.ts +433 -564
  46. package/src/input/agent-workspace-calendar-connect-editor.ts +116 -0
  47. package/src/input/agent-workspace-calendar-oauth-editor.ts +152 -0
  48. package/src/input/agent-workspace-calendar-subscribe-editor.ts +128 -0
  49. package/src/input/agent-workspace-categories.ts +7 -2
  50. package/src/input/agent-workspace-channel-command-editor-submission.ts +38 -52
  51. package/src/input/agent-workspace-channel-command-editors.ts +42 -46
  52. package/src/input/agent-workspace-command-editor-engine.ts +133 -0
  53. package/src/input/agent-workspace-command-editor.ts +4 -0
  54. package/src/input/agent-workspace-direct-editor-submission.ts +59 -0
  55. package/src/input/agent-workspace-email-connect-editor.ts +162 -0
  56. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +52 -81
  57. package/src/input/agent-workspace-knowledge-command-editors.ts +71 -71
  58. package/src/input/agent-workspace-library-command-editor-submission.ts +10 -28
  59. package/src/input/agent-workspace-library-command-editors.ts +16 -2
  60. package/src/input/agent-workspace-live-counters.ts +55 -0
  61. package/src/input/agent-workspace-mcp-command-editor-submission.ts +60 -82
  62. package/src/input/agent-workspace-mcp-command-editors.ts +52 -0
  63. package/src/input/agent-workspace-media-command-editor-submission.ts +12 -39
  64. package/src/input/agent-workspace-media-command-editors.ts +10 -8
  65. package/src/input/agent-workspace-memory-command-editor-submission.ts +76 -151
  66. package/src/input/agent-workspace-memory-command-editors.ts +116 -141
  67. package/src/input/agent-workspace-operations-command-editor-submission.ts +131 -184
  68. package/src/input/agent-workspace-operations-command-editors.ts +150 -162
  69. package/src/input/agent-workspace-provider-command-editor-submission.ts +60 -106
  70. package/src/input/agent-workspace-provider-command-editors.ts +58 -68
  71. package/src/input/agent-workspace-search.ts +8 -1
  72. package/src/input/agent-workspace-session-command-editor-submission.ts +104 -132
  73. package/src/input/agent-workspace-session-command-editors.ts +160 -195
  74. package/src/input/agent-workspace-settings.ts +40 -1
  75. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +55 -71
  76. package/src/input/agent-workspace-skill-bundle-command-editors.ts +65 -69
  77. package/src/input/agent-workspace-snapshot-builders.ts +431 -0
  78. package/src/input/agent-workspace-snapshot-config.ts +43 -0
  79. package/src/input/agent-workspace-snapshot.ts +198 -432
  80. package/src/input/agent-workspace-task-command-editor-submission.ts +34 -44
  81. package/src/input/agent-workspace-task-command-editors.ts +27 -28
  82. package/src/input/agent-workspace-types.ts +35 -1
  83. package/src/input/agent-workspace.ts +5 -14
  84. package/src/input/command-registry.ts +23 -1
  85. package/src/input/commands/calendar-connect-runtime.ts +226 -0
  86. package/src/input/commands/calendar-runtime.ts +110 -7
  87. package/src/input/commands/calendar-subscription-runtime.ts +225 -0
  88. package/src/input/commands/email-runtime.ts +100 -40
  89. package/src/input/commands/knowledge.ts +1 -1
  90. package/src/input/commands/network-scan-runtime.ts +75 -0
  91. package/src/input/commands/operator-actions-runtime.ts +9 -6
  92. package/src/input/commands/personas-runtime.ts +1 -1
  93. package/src/input/commands/schedule-runtime.ts +4 -4
  94. package/src/input/commands/session-content.ts +13 -1
  95. package/src/input/commands/session-workflow.ts +15 -20
  96. package/src/input/commands/session.ts +3 -1
  97. package/src/input/commands/shell-core.ts +20 -6
  98. package/src/input/commands.ts +2 -2
  99. package/src/input/delete-key-policy.ts +46 -0
  100. package/src/input/feed-context-factory.ts +10 -0
  101. package/src/input/handler-feed.ts +87 -0
  102. package/src/input/handler-modal-routes.ts +6 -1
  103. package/src/input/handler.ts +5 -0
  104. package/src/input/panel-paste-flood-guard.ts +94 -0
  105. package/src/input/settings-modal-types.ts +5 -3
  106. package/src/input/settings-modal.ts +21 -0
  107. package/src/main.ts +36 -36
  108. package/src/permissions/approval-posture.ts +141 -0
  109. package/src/renderer/agent-workspace-context-lines.ts +10 -2
  110. package/src/renderer/compositor.ts +27 -4
  111. package/src/renderer/diff.ts +61 -18
  112. package/src/renderer/fullscreen-primitives.ts +37 -18
  113. package/src/renderer/markdown.ts +20 -10
  114. package/src/renderer/modal-factory.ts +25 -15
  115. package/src/renderer/overlay-box.ts +23 -12
  116. package/src/renderer/process-indicator.ts +8 -3
  117. package/src/renderer/prompt-content-width.ts +16 -0
  118. package/src/renderer/settings-modal-helpers.ts +2 -0
  119. package/src/renderer/settings-modal.ts +2 -0
  120. package/src/renderer/startup-theme-probe.ts +35 -0
  121. package/src/renderer/status-glyphs.ts +11 -15
  122. package/src/renderer/system-message.ts +17 -2
  123. package/src/renderer/term-caps.ts +318 -0
  124. package/src/renderer/terminal-bg-probe.ts +373 -0
  125. package/src/renderer/terminal-escapes.ts +24 -0
  126. package/src/renderer/theme-mode-config.ts +87 -0
  127. package/src/renderer/theme.ts +241 -0
  128. package/src/renderer/thinking.ts +12 -3
  129. package/src/renderer/tool-call.ts +7 -3
  130. package/src/renderer/ui-factory.ts +92 -36
  131. package/src/renderer/ui-primitives.ts +33 -93
  132. package/src/runtime/bootstrap-core.ts +15 -0
  133. package/src/runtime/bootstrap-hook-bridge.ts +6 -0
  134. package/src/runtime/bootstrap-shell.ts +2 -0
  135. package/src/runtime/bootstrap.ts +68 -5
  136. package/src/runtime/calendar-boot-refresh.ts +105 -0
  137. package/src/runtime/lan-scan-consent.ts +253 -0
  138. package/src/runtime/services.ts +127 -2
  139. package/src/runtime/session-spine-rest-transport.ts +160 -0
  140. package/src/runtime/terminal-output-guard.ts +6 -1
  141. package/src/runtime/ui-services.ts +3 -0
  142. package/src/shell/agent-workspace-fullscreen.ts +5 -0
  143. package/src/shell/terminal-focus-mode.ts +120 -0
  144. package/src/shell/ui-openers.ts +13 -4
  145. package/src/tools/agent-harness-autonomy-intake.ts +5 -5
  146. package/src/tools/agent-harness-autonomy-queue.ts +1 -1
  147. package/src/tools/agent-harness-background-processes.ts +2 -1
  148. package/src/tools/agent-harness-metadata.ts +16 -8
  149. package/src/tools/agent-harness-operator-methods.ts +44 -8
  150. package/src/tools/agent-harness-personal-ops-discovery.ts +57 -10
  151. package/src/tools/agent-harness-personal-ops-lanes.ts +17 -5
  152. package/src/tools/agent-harness-personal-ops-operations.ts +7 -7
  153. package/src/tools/agent-harness-personal-ops-types.ts +6 -0
  154. package/src/tools/agent-harness-prompt-context.ts +26 -12
  155. package/src/tools/agent-harness-workspace-actions.ts +4 -0
  156. package/src/tools/agent-local-registry-args.ts +117 -0
  157. package/src/tools/agent-local-registry-memory.ts +227 -0
  158. package/src/tools/agent-local-registry-tool.ts +19 -237
  159. package/src/tools/agent-operator-briefing-tool.ts +2 -2
  160. package/src/tools/agent-operator-method-tool.ts +13 -0
  161. package/src/tools/agent-policy-explanation.ts +39 -4
  162. package/src/tools/agent-schedule-tool.ts +5 -5
  163. package/src/utils/terminal-width.ts +98 -1
  164. package/src/version.ts +1 -1
  165. package/src/cli/local-library-command.ts +0 -825
@@ -1,96 +1,50 @@
1
- import type { AgentWorkspaceActionResult, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
1
+ import type { AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
2
  import type { AgentWorkspaceKnowledgeCommandEditorKind } from './agent-workspace-knowledge-command-editors.ts';
3
3
  import { isAgentWorkspaceKnowledgeCommandEditorKind } from './agent-workspace-knowledge-command-editors.ts';
4
4
  import { quoteSlashCommandArg } from './slash-command-parser.ts';
5
+ import type { AgentWorkspaceCommandEditorSubmission, AgentWorkspaceCommandSubmissionHandler, AgentWorkspaceFieldReader } from './agent-workspace-command-editor-engine.ts';
6
+ import { buildCommandEditorSubmissionFromTable, dispatchCommandEditorSubmission, editorMessageSubmission, isAffirmative, splitCommaList } from './agent-workspace-command-editor-engine.ts';
5
7
 
6
- type AgentWorkspaceFieldReader = (fieldId: string) => string;
7
-
8
- export type AgentWorkspaceKnowledgeCommandEditorSubmission =
9
- | {
10
- readonly kind: 'editor';
11
- readonly editor: AgentWorkspaceLocalEditor;
12
- readonly status: string;
13
- readonly actionResult?: AgentWorkspaceActionResult;
14
- }
15
- | {
16
- readonly kind: 'dispatch';
17
- readonly command: string;
18
- readonly status: string;
19
- readonly actionResult: AgentWorkspaceActionResult;
20
- };
8
+ export type AgentWorkspaceKnowledgeCommandEditorSubmission = AgentWorkspaceCommandEditorSubmission;
21
9
 
22
10
  export function isAgentWorkspaceKnowledgeCommandSubmissionKind(kind: string): kind is AgentWorkspaceKnowledgeCommandEditorKind {
23
11
  return isAgentWorkspaceKnowledgeCommandEditorKind(kind as AgentWorkspaceKnowledgeCommandEditorKind);
24
12
  }
25
13
 
26
- function isAffirmative(value: string): boolean {
27
- return /^(y|yes|true)$/i.test(value.trim());
28
- }
29
-
30
- function splitCommaList(value: string): readonly string[] {
31
- return value.split(',').map((entry) => entry.trim()).filter(Boolean);
32
- }
33
-
34
- function unconfirmed(editor: AgentWorkspaceLocalEditor, message: string): AgentWorkspaceKnowledgeCommandEditorSubmission {
35
- return {
36
- kind: 'editor',
37
- editor: { ...editor, message },
38
- status: message,
39
- };
40
- }
41
-
42
- function dispatch(command: string, title: string, detail: string, safety: AgentWorkspaceActionResult['safety']): AgentWorkspaceKnowledgeCommandEditorSubmission {
43
- return {
44
- kind: 'dispatch',
45
- command,
46
- status: `${title}.`,
47
- actionResult: { kind: 'dispatched', title, detail, command, safety },
48
- };
49
- }
50
-
51
- export function buildAgentWorkspaceKnowledgeCommandEditorSubmission(
52
- editor: AgentWorkspaceLocalEditor,
53
- readField: AgentWorkspaceFieldReader,
54
- ): AgentWorkspaceKnowledgeCommandEditorSubmission {
55
- if (editor.kind === 'knowledge-get') {
56
- return dispatch(
57
- `/knowledge get ${quoteSlashCommandArg(readField('id'))}`,
58
- 'Opening Agent Knowledge item',
59
- 'The workspace handed a read-only Agent Knowledge item command to the shell-owned command router.',
60
- 'read-only',
61
- );
62
- }
63
- if (editor.kind === 'knowledge-map') {
14
+ const KNOWLEDGE_COMMAND_SUBMISSION_HANDLERS: Readonly<Record<AgentWorkspaceKnowledgeCommandEditorKind, AgentWorkspaceCommandSubmissionHandler>> = {
15
+ 'knowledge-get': (_editor, readField) => dispatchCommandEditorSubmission(
16
+ `/knowledge get ${quoteSlashCommandArg(readField('id'))}`,
17
+ 'Opening Agent Knowledge item',
18
+ 'The workspace handed a read-only Agent Knowledge item command to the shell-owned command router.',
19
+ 'read-only',
20
+ ),
21
+ 'knowledge-map': (_editor, readField) => {
64
22
  const query = readField('query');
65
23
  const limit = readField('limit');
66
24
  const parts = ['/knowledge', 'map'];
67
25
  if (query.length > 0) parts.push(quoteSlashCommandArg(query));
68
26
  if (limit.length > 0) parts.push('--limit', quoteSlashCommandArg(limit));
69
- return dispatch(
27
+ return dispatchCommandEditorSubmission(
70
28
  parts.join(' '),
71
29
  'Opening Agent Knowledge map',
72
30
  'The workspace handed a read-only Agent Knowledge map command to the shell-owned command router.',
73
31
  'read-only',
74
32
  );
75
- }
76
- if (editor.kind === 'knowledge-connector-show') {
77
- return dispatch(
78
- `/knowledge connectors ${quoteSlashCommandArg(readField('connectorId'))}`,
79
- 'Opening Agent Knowledge connector',
80
- 'The workspace handed a read-only Agent Knowledge connector detail command to the shell-owned command router.',
81
- 'read-only',
82
- );
83
- }
84
- if (editor.kind === 'knowledge-connector-doctor') {
85
- return dispatch(
86
- `/knowledge connectors doctor ${quoteSlashCommandArg(readField('connectorId'))}`,
87
- 'Opening Agent Knowledge connector doctor',
88
- 'The workspace handed a read-only Agent Knowledge connector doctor command to the shell-owned command router.',
89
- 'read-only',
90
- );
91
- }
92
- if (editor.kind === 'knowledge-review-issue') {
93
- if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Agent Knowledge issue review not confirmed. Type yes, then press Enter.');
33
+ },
34
+ 'knowledge-connector-show': (_editor, readField) => dispatchCommandEditorSubmission(
35
+ `/knowledge connectors ${quoteSlashCommandArg(readField('connectorId'))}`,
36
+ 'Opening Agent Knowledge connector',
37
+ 'The workspace handed a read-only Agent Knowledge connector detail command to the shell-owned command router.',
38
+ 'read-only',
39
+ ),
40
+ 'knowledge-connector-doctor': (_editor, readField) => dispatchCommandEditorSubmission(
41
+ `/knowledge connectors doctor ${quoteSlashCommandArg(readField('connectorId'))}`,
42
+ 'Opening Agent Knowledge connector doctor',
43
+ 'The workspace handed a read-only Agent Knowledge connector doctor command to the shell-owned command router.',
44
+ 'read-only',
45
+ ),
46
+ 'knowledge-review-issue': (editor, readField) => {
47
+ if (!isAffirmative(readField('confirm'))) return editorMessageSubmission(editor, 'Agent Knowledge issue review not confirmed. Type yes, then press Enter.');
94
48
  const parts = [
95
49
  '/knowledge',
96
50
  'review-issue',
@@ -102,31 +56,48 @@ export function buildAgentWorkspaceKnowledgeCommandEditorSubmission(
102
56
  if (reviewer.length > 0) parts.push('--reviewer', quoteSlashCommandArg(reviewer));
103
57
  if (value.length > 0) parts.push('--value', quoteSlashCommandArg(value));
104
58
  parts.push('--yes');
105
- return dispatch(
59
+ return dispatchCommandEditorSubmission(
106
60
  parts.join(' '),
107
61
  'Opening Agent Knowledge issue review',
108
62
  'The workspace handed a confirmed Agent Knowledge issue review command to the shell-owned command router.',
109
63
  'safe',
110
64
  );
111
- }
112
- if (editor.kind === 'knowledge-consolidate') {
113
- if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Agent Knowledge consolidation not confirmed. Type yes, then press Enter.');
114
- return dispatch(
65
+ },
66
+ 'knowledge-consolidate': (editor, readField) => {
67
+ if (!isAffirmative(readField('confirm'))) return editorMessageSubmission(editor, 'Agent Knowledge consolidation not confirmed. Type yes, then press Enter.');
68
+ return dispatchCommandEditorSubmission(
115
69
  `/knowledge consolidate ${quoteSlashCommandArg(readField('mode'))} --yes`,
116
70
  'Opening Agent Knowledge consolidation',
117
71
  'The workspace handed a confirmed Agent Knowledge consolidation command to the shell-owned command router.',
118
72
  'safe',
119
73
  );
120
- }
74
+ },
75
+ 'knowledge-explain': (editor, readField) => knowledgeExplainOrPacket(editor, readField),
76
+ 'knowledge-packet': (editor, readField) => knowledgeExplainOrPacket(editor, readField),
77
+ };
78
+
79
+ function knowledgeExplainOrPacket(editor: AgentWorkspaceLocalEditor, readField: AgentWorkspaceFieldReader): AgentWorkspaceCommandEditorSubmission {
121
80
  const subcommand = editor.kind === 'knowledge-explain' ? 'explain' : 'packet';
122
81
  const parts = ['/knowledge', subcommand, quoteSlashCommandArg(readField('task'))];
123
82
  for (const scope of splitCommaList(readField('scopes'))) {
124
83
  parts.push('--scope', quoteSlashCommandArg(scope));
125
84
  }
126
- return dispatch(
85
+ return dispatchCommandEditorSubmission(
127
86
  parts.join(' '),
128
87
  subcommand === 'explain' ? 'Opening Agent Knowledge explanation' : 'Opening Agent Knowledge prompt packet',
129
88
  'The workspace handed a read-only Agent Knowledge context command to the shell-owned command router.',
130
89
  'read-only',
131
90
  );
132
91
  }
92
+
93
+ export function buildAgentWorkspaceKnowledgeCommandEditorSubmission(
94
+ editor: AgentWorkspaceLocalEditor,
95
+ readField: AgentWorkspaceFieldReader,
96
+ ): AgentWorkspaceCommandEditorSubmission {
97
+ return buildCommandEditorSubmissionFromTable(
98
+ editor.kind as AgentWorkspaceKnowledgeCommandEditorKind,
99
+ editor,
100
+ readField,
101
+ KNOWLEDGE_COMMAND_SUBMISSION_HANDLERS,
102
+ );
103
+ }
@@ -1,4 +1,6 @@
1
1
  import type { AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
+ import type { AgentWorkspaceEditorSpec, AgentWorkspaceEditorSpecEntry } from './agent-workspace-command-editor-engine.ts';
3
+ import { createAgentWorkspaceEditorFromTable } from './agent-workspace-command-editor-engine.ts';
2
4
 
3
5
  export type AgentWorkspaceKnowledgeCommandEditorKind = Extract<
4
6
  AgentWorkspaceEditorKind,
@@ -23,79 +25,24 @@ export function isAgentWorkspaceKnowledgeCommandEditorKind(kind: AgentWorkspaceE
23
25
  || kind === 'knowledge-explain';
24
26
  }
25
27
 
26
- export function createAgentWorkspaceKnowledgeCommandEditor(kind: AgentWorkspaceKnowledgeCommandEditorKind): AgentWorkspaceLocalEditor {
27
- if (kind === 'knowledge-get') {
28
- return {
29
- kind,
30
- mode: 'create',
31
- title: 'Show Agent Knowledge Item',
32
- selectedFieldIndex: 0,
33
- message: 'Show one source, node, or issue from the isolated Agent Knowledge segment.',
34
- fields: [
35
- { id: 'id', label: 'Item id', value: '', required: true, multiline: false, hint: 'Agent Knowledge source, node, or issue id.' },
36
- ],
37
- };
38
- }
39
- if (kind === 'knowledge-map') {
40
- return {
41
- kind,
42
- mode: 'create',
43
- title: 'Map Agent Knowledge',
44
- selectedFieldIndex: 0,
45
- message: 'Summarize the isolated Agent Knowledge graph map. Leave query blank for a bounded full map summary.',
46
- fields: [
47
- { id: 'query', label: 'Query', value: '', required: false, multiline: false, hint: 'Optional map filter query.' },
48
- { id: 'limit', label: 'Limit', value: '50', required: false, multiline: false, hint: 'Maximum map records to summarize.' },
49
- ],
50
- };
51
- }
52
- if (kind === 'knowledge-connector-show' || kind === 'knowledge-connector-doctor') {
53
- const doctor = kind === 'knowledge-connector-doctor';
54
- return {
55
- kind,
56
- mode: 'create',
57
- title: doctor ? 'Doctor Agent Knowledge Connector' : 'Show Agent Knowledge Connector',
58
- selectedFieldIndex: 0,
59
- message: doctor
60
- ? 'Run a read-only readiness doctor for one Agent Knowledge connector.'
61
- : 'Show one Agent Knowledge connector from the isolated Agent Knowledge connector registry.',
62
- fields: [
63
- { id: 'connectorId', label: 'Connector id', value: '', required: true, multiline: false, hint: 'Connector id from the Agent Knowledge connector inventory.' },
64
- ],
65
- };
66
- }
67
- if (kind === 'knowledge-review-issue') {
68
- return {
69
- kind,
70
- mode: 'update',
71
- title: 'Review Agent Knowledge Issue',
72
- selectedFieldIndex: 0,
73
- message: 'Accept, reject, resolve, reopen, edit, or forget one Agent Knowledge issue. Type yes on the final field to confirm.',
74
- fields: [
75
- { id: 'issueId', label: 'Issue id', value: '', required: true, multiline: false, hint: 'Issue id from the review queue.' },
76
- { id: 'action', label: 'Action', value: 'resolve', required: true, multiline: false, hint: 'accept, reject, resolve, reopen, edit, or forget.' },
77
- { id: 'reviewer', label: 'Reviewer', value: 'agent', required: false, multiline: false, hint: 'Reviewer label. Blank defaults to agent.' },
78
- { id: 'value', label: 'Value JSON', value: '', required: false, multiline: true, hint: 'Optional JSON object for edit actions. Ctrl-J inserts a new line.' },
79
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /knowledge review-issue with --yes.' },
80
- ],
81
- };
82
- }
83
- if (kind === 'knowledge-consolidate') {
84
- return {
85
- kind,
86
- mode: 'update',
87
- title: 'Consolidate Agent Knowledge',
88
- selectedFieldIndex: 0,
89
- message: 'Run isolated Agent Knowledge consolidation. Type yes on the final field to confirm.',
90
- fields: [
91
- { id: 'mode', label: 'Mode', value: 'light', required: true, multiline: false, hint: 'light or deep.' },
92
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /knowledge consolidate with --yes.' },
93
- ],
94
- };
95
- }
28
+ function knowledgeConnectorSpec(kind: AgentWorkspaceKnowledgeCommandEditorKind): AgentWorkspaceEditorSpec {
29
+ const doctor = kind === 'knowledge-connector-doctor';
30
+ return {
31
+ mode: 'create',
32
+ title: doctor ? 'Doctor Agent Knowledge Connector' : 'Show Agent Knowledge Connector',
33
+ selectedFieldIndex: 0,
34
+ message: doctor
35
+ ? 'Run a read-only readiness doctor for one Agent Knowledge connector.'
36
+ : 'Show one Agent Knowledge connector from the isolated Agent Knowledge connector registry.',
37
+ fields: [
38
+ { id: 'connectorId', label: 'Connector id', value: '', required: true, multiline: false, hint: 'Connector id from the Agent Knowledge connector inventory.' },
39
+ ],
40
+ };
41
+ }
42
+
43
+ function knowledgeExplainOrPacketSpec(kind: AgentWorkspaceKnowledgeCommandEditorKind): AgentWorkspaceEditorSpec {
96
44
  const explain = kind === 'knowledge-explain';
97
45
  return {
98
- kind,
99
46
  mode: 'create',
100
47
  title: explain ? 'Explain Memory Selection' : 'Build Prompt Packet',
101
48
  selectedFieldIndex: 0,
@@ -108,3 +55,56 @@ export function createAgentWorkspaceKnowledgeCommandEditor(kind: AgentWorkspaceK
108
55
  ],
109
56
  };
110
57
  }
58
+
59
+ const KNOWLEDGE_COMMAND_EDITOR_SPECS: Readonly<Record<AgentWorkspaceKnowledgeCommandEditorKind, AgentWorkspaceEditorSpecEntry<AgentWorkspaceKnowledgeCommandEditorKind>>> = {
60
+ 'knowledge-get': {
61
+ mode: 'create',
62
+ title: 'Show Agent Knowledge Item',
63
+ selectedFieldIndex: 0,
64
+ message: 'Show one source, node, or issue from the isolated Agent Knowledge segment.',
65
+ fields: [
66
+ { id: 'id', label: 'Item id', value: '', required: true, multiline: false, hint: 'Agent Knowledge source, node, or issue id.' },
67
+ ],
68
+ },
69
+ 'knowledge-map': {
70
+ mode: 'create',
71
+ title: 'Map Agent Knowledge',
72
+ selectedFieldIndex: 0,
73
+ message: 'Summarize the isolated Agent Knowledge graph map. Leave query blank for a bounded full map summary.',
74
+ fields: [
75
+ { id: 'query', label: 'Query', value: '', required: false, multiline: false, hint: 'Optional map filter query.' },
76
+ { id: 'limit', label: 'Limit', value: '50', required: false, multiline: false, hint: 'Maximum map records to summarize.' },
77
+ ],
78
+ },
79
+ 'knowledge-connector-show': knowledgeConnectorSpec,
80
+ 'knowledge-connector-doctor': knowledgeConnectorSpec,
81
+ 'knowledge-review-issue': {
82
+ mode: 'update',
83
+ title: 'Review Agent Knowledge Issue',
84
+ selectedFieldIndex: 0,
85
+ message: 'Accept, reject, resolve, reopen, edit, or forget one Agent Knowledge issue. Type yes on the final field to confirm.',
86
+ fields: [
87
+ { id: 'issueId', label: 'Issue id', value: '', required: true, multiline: false, hint: 'Issue id from the review queue.' },
88
+ { id: 'action', label: 'Action', value: 'resolve', required: true, multiline: false, hint: 'accept, reject, resolve, reopen, edit, or forget.' },
89
+ { id: 'reviewer', label: 'Reviewer', value: 'agent', required: false, multiline: false, hint: 'Reviewer label. Blank defaults to agent.' },
90
+ { id: 'value', label: 'Value JSON', value: '', required: false, multiline: true, hint: 'Optional JSON object for edit actions. Ctrl-J inserts a new line.' },
91
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /knowledge review-issue with --yes.' },
92
+ ],
93
+ },
94
+ 'knowledge-consolidate': {
95
+ mode: 'update',
96
+ title: 'Consolidate Agent Knowledge',
97
+ selectedFieldIndex: 0,
98
+ message: 'Run isolated Agent Knowledge consolidation. Type yes on the final field to confirm.',
99
+ fields: [
100
+ { id: 'mode', label: 'Mode', value: 'light', required: true, multiline: false, hint: 'light or deep.' },
101
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /knowledge consolidate with --yes.' },
102
+ ],
103
+ },
104
+ 'knowledge-explain': knowledgeExplainOrPacketSpec,
105
+ 'knowledge-packet': knowledgeExplainOrPacketSpec,
106
+ };
107
+
108
+ export function createAgentWorkspaceKnowledgeCommandEditor(kind: AgentWorkspaceKnowledgeCommandEditorKind): AgentWorkspaceLocalEditor {
109
+ return createAgentWorkspaceEditorFromTable(kind, KNOWLEDGE_COMMAND_EDITOR_SPECS);
110
+ }
@@ -1,23 +1,11 @@
1
- import type { AgentWorkspaceActionResult, AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
1
+ import type { AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
2
  import type { AgentWorkspaceLibraryCommandEditorKind } from './agent-workspace-library-command-editors.ts';
3
3
  import { isAgentWorkspaceLibraryCommandEditorKind } from './agent-workspace-library-command-editors.ts';
4
4
  import { quoteSlashCommandArg } from './slash-command-parser.ts';
5
+ import type { AgentWorkspaceCommandEditorSubmission, AgentWorkspaceFieldReader } from './agent-workspace-command-editor-engine.ts';
6
+ import { dispatchCommandEditorSubmission } from './agent-workspace-command-editor-engine.ts';
5
7
 
6
- type AgentWorkspaceFieldReader = (fieldId: string) => string;
7
-
8
- export type AgentWorkspaceLibraryCommandEditorSubmission =
9
- | {
10
- readonly kind: 'editor';
11
- readonly editor: AgentWorkspaceLocalEditor;
12
- readonly status: string;
13
- readonly actionResult?: AgentWorkspaceActionResult;
14
- }
15
- | {
16
- readonly kind: 'dispatch';
17
- readonly command: string;
18
- readonly status: string;
19
- readonly actionResult: AgentWorkspaceActionResult;
20
- };
8
+ export type AgentWorkspaceLibraryCommandEditorSubmission = AgentWorkspaceCommandEditorSubmission;
21
9
 
22
10
  export function isAgentWorkspaceLibraryCommandSubmissionKind(kind: AgentWorkspaceEditorKind): kind is AgentWorkspaceLibraryCommandEditorKind {
23
11
  return isAgentWorkspaceLibraryCommandEditorKind(kind);
@@ -26,7 +14,7 @@ export function isAgentWorkspaceLibraryCommandSubmissionKind(kind: AgentWorkspac
26
14
  export function buildAgentWorkspaceLibraryCommandEditorSubmission(
27
15
  editor: AgentWorkspaceLocalEditor,
28
16
  readField: AgentWorkspaceFieldReader,
29
- ): AgentWorkspaceLibraryCommandEditorSubmission {
17
+ ): AgentWorkspaceCommandEditorSubmission {
30
18
  const target = editor.kind.startsWith('persona') ? 'persona' : editor.kind.startsWith('skill') ? 'skill' : 'routine';
31
19
  const root = target === 'persona' ? '/personas' : target === 'skill' ? '/skills' : '/routines';
32
20
  const search = editor.kind.endsWith('search');
@@ -35,16 +23,10 @@ export function buildAgentWorkspaceLibraryCommandEditorSubmission(
35
23
  : `${root} show ${quoteSlashCommandArg(readField('id'))}`;
36
24
  const label = target === 'persona' ? 'persona' : target === 'skill' ? 'skill' : 'routine';
37
25
  const title = search ? `Opening ${label} search` : `Opening ${label} detail`;
38
- return {
39
- kind: 'dispatch',
26
+ return dispatchCommandEditorSubmission(
40
27
  command,
41
- status: `${title}.`,
42
- actionResult: {
43
- kind: 'dispatched',
44
- title,
45
- detail: `The workspace handed read-only local ${label} ${search ? 'search' : 'detail inspection'} to the shell-owned command router.`,
46
- command,
47
- safety: 'read-only',
48
- },
49
- };
28
+ title,
29
+ `The workspace handed read-only local ${label} ${search ? 'search' : 'detail inspection'} to the shell-owned command router.`,
30
+ 'read-only',
31
+ );
50
32
  }
@@ -1,4 +1,6 @@
1
1
  import type { AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
+ import type { AgentWorkspaceEditorSpec } from './agent-workspace-command-editor-engine.ts';
3
+ import { createAgentWorkspaceEditorFromTable } from './agent-workspace-command-editor-engine.ts';
2
4
 
3
5
  export type AgentWorkspaceLibraryCommandEditorKind = Extract<
4
6
  AgentWorkspaceEditorKind,
@@ -14,12 +16,11 @@ export function isAgentWorkspaceLibraryCommandEditorKind(kind: AgentWorkspaceEdi
14
16
  || kind === 'routine-show';
15
17
  }
16
18
 
17
- export function createAgentWorkspaceLibraryCommandEditor(kind: AgentWorkspaceLibraryCommandEditorKind): AgentWorkspaceLocalEditor {
19
+ function librarySpec(kind: AgentWorkspaceLibraryCommandEditorKind): AgentWorkspaceEditorSpec {
18
20
  const target = kind.startsWith('persona') ? 'persona' : kind.startsWith('skill') ? 'skill' : 'routine';
19
21
  const search = kind.endsWith('search');
20
22
  const label = target === 'persona' ? 'Persona' : target === 'skill' ? 'Skill' : 'Routine';
21
23
  return {
22
- kind,
23
24
  mode: 'create',
24
25
  title: search ? `Search ${label}s` : `Show ${label}`,
25
26
  selectedFieldIndex: 0,
@@ -33,3 +34,16 @@ export function createAgentWorkspaceLibraryCommandEditor(kind: AgentWorkspaceLib
33
34
  ],
34
35
  };
35
36
  }
37
+
38
+ const LIBRARY_COMMAND_EDITOR_SPECS: Readonly<Record<AgentWorkspaceLibraryCommandEditorKind, typeof librarySpec>> = {
39
+ 'persona-search': librarySpec,
40
+ 'persona-show': librarySpec,
41
+ 'skill-search': librarySpec,
42
+ 'skill-show': librarySpec,
43
+ 'routine-search': librarySpec,
44
+ 'routine-show': librarySpec,
45
+ };
46
+
47
+ export function createAgentWorkspaceLibraryCommandEditor(kind: AgentWorkspaceLibraryCommandEditorKind): AgentWorkspaceLocalEditor {
48
+ return createAgentWorkspaceEditorFromTable(kind, LIBRARY_COMMAND_EDITOR_SPECS);
49
+ }
@@ -0,0 +1,55 @@
1
+ import type { CommandContext } from './command-registry.ts';
2
+ import { readLiveAgentMemoryCounters, readLiveAgentRoutineCounters } from './agent-workspace-snapshot.ts';
3
+ import type { AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
4
+
5
+ export interface SyncAgentWorkspaceLiveCountersOptions {
6
+ readonly context: CommandContext | null;
7
+ readonly runtimeSnapshot: AgentWorkspaceRuntimeSnapshot | null;
8
+ readonly setRuntimeSnapshot: (snapshot: AgentWorkspaceRuntimeSnapshot) => void;
9
+ readonly clampSelection: () => void;
10
+ }
11
+
12
+ /**
13
+ * Quiet, render-path refresh of the two counters the W4-A6 dogfood finding
14
+ * named as stale: the memory count and the routine counts (incl. each
15
+ * routine's live start count). Call this once per repaint, before rendering,
16
+ * so an external disk mutation (another shell deleting a memory, a CLI
17
+ * `routines start` bumping a start count) shows up on the NEXT paint instead
18
+ * of requiring the user to trigger a workspace action first.
19
+ *
20
+ * Unlike AgentWorkspace.refreshRuntimeSnapshot(), this never touches status
21
+ * or lastActionResult -- it must be safe to call on every passive repaint,
22
+ * including ones triggered by unrelated key presses. It also intentionally
23
+ * does NOT rebuild the entire runtime snapshot (~20 registry/config reads):
24
+ * that full rebuild stays reserved for real action completions elsewhere on
25
+ * AgentWorkspace. Re-deriving just these two counters keeps the render-path
26
+ * cost proportional to "did the memory/routine store change", not to the
27
+ * whole workspace's surface area.
28
+ *
29
+ * On a read failure the previous counters are kept (never overwritten with a
30
+ * fabricated 0) and runtimeSnapshot.liveCountersStale is set so the render
31
+ * path can label them honestly as refreshing rather than asserting a number
32
+ * the disk might already contradict.
33
+ */
34
+ export function syncAgentWorkspaceLiveCounters(options: SyncAgentWorkspaceLiveCountersOptions): void {
35
+ const { context, runtimeSnapshot, setRuntimeSnapshot, clampSelection } = options;
36
+ if (!context || !runtimeSnapshot) return;
37
+ try {
38
+ const memory = readLiveAgentMemoryCounters(context);
39
+ const routines = readLiveAgentRoutineCounters(context);
40
+ setRuntimeSnapshot({
41
+ ...runtimeSnapshot,
42
+ localMemoryCount: memory.count,
43
+ localMemoryReviewQueueCount: memory.reviewQueueCount,
44
+ localMemoryPromptActiveCount: memory.promptActiveCount,
45
+ localMemories: memory.items,
46
+ localRoutineCount: routines.count,
47
+ enabledRoutineCount: routines.enabled,
48
+ localRoutines: routines.items,
49
+ liveCountersStale: false,
50
+ });
51
+ } catch {
52
+ setRuntimeSnapshot({ ...runtimeSnapshot, liveCountersStale: true });
53
+ }
54
+ clampSelection();
55
+ }
@@ -1,95 +1,73 @@
1
- import type { AgentWorkspaceActionResult, AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
1
+ import type { AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
2
  import { quoteSlashCommandArg, tokenizeSlashCommand } from './slash-command-parser.ts';
3
+ import type { AgentWorkspaceMcpCommandEditorKind } from './agent-workspace-mcp-command-editors.ts';
4
+ import { isAgentWorkspaceMcpCommandEditorKind } from './agent-workspace-mcp-command-editors.ts';
5
+ import type { AgentWorkspaceCommandEditorSubmission, AgentWorkspaceCommandSubmissionHandler, AgentWorkspaceFieldReader } from './agent-workspace-command-editor-engine.ts';
6
+ import { buildCommandEditorSubmissionFromTable, dispatchCommandEditorSubmission, editorMessageSubmission, isAffirmative, splitCommaList } from './agent-workspace-command-editor-engine.ts';
3
7
 
4
- type AgentWorkspaceFieldReader = (fieldId: string) => string;
5
-
6
- export type AgentWorkspaceMcpCommandEditorKind = Extract<AgentWorkspaceEditorKind, 'mcp-server' | 'mcp-tools-server' | 'mcp-repair'>;
7
-
8
- export type AgentWorkspaceMcpCommandEditorSubmission =
9
- | {
10
- readonly kind: 'editor';
11
- readonly editor: AgentWorkspaceLocalEditor;
12
- readonly status: string;
13
- readonly actionResult?: AgentWorkspaceActionResult;
14
- }
15
- | {
16
- readonly kind: 'dispatch';
17
- readonly command: string;
18
- readonly status: string;
19
- readonly actionResult: AgentWorkspaceActionResult;
20
- };
8
+ export type { AgentWorkspaceMcpCommandEditorKind } from './agent-workspace-mcp-command-editors.ts';
9
+ export type AgentWorkspaceMcpCommandEditorSubmission = AgentWorkspaceCommandEditorSubmission;
21
10
 
22
11
  export function isAgentWorkspaceMcpCommandSubmissionKind(kind: AgentWorkspaceEditorKind): kind is AgentWorkspaceMcpCommandEditorKind {
23
- return kind === 'mcp-server' || kind === 'mcp-tools-server' || kind === 'mcp-repair';
12
+ return isAgentWorkspaceMcpCommandEditorKind(kind);
24
13
  }
25
14
 
26
- function isAffirmative(value: string): boolean {
27
- return /^(y|yes|true)$/i.test(value.trim());
28
- }
15
+ const MCP_COMMAND_SUBMISSION_HANDLERS: Readonly<Record<AgentWorkspaceMcpCommandEditorKind, AgentWorkspaceCommandSubmissionHandler>> = {
16
+ 'mcp-tools-server': (editor, readField) => mcpToolsOrRepair(editor, readField),
17
+ 'mcp-repair': (editor, readField) => mcpToolsOrRepair(editor, readField),
18
+ 'mcp-server': (editor, readField) => {
19
+ if (!isAffirmative(readField('confirm'))) {
20
+ return editorMessageSubmission(editor, 'MCP server add/update not confirmed. Type yes, then press Enter.', 'MCP server add/update not confirmed.');
21
+ }
22
+ const parts = [
23
+ '/mcp',
24
+ 'add',
25
+ quoteSlashCommandArg(readField('name')),
26
+ quoteSlashCommandArg(readField('command')),
27
+ ...tokenizeSlashCommand(readField('args')).map(quoteSlashCommandArg),
28
+ ];
29
+ const scope = readField('scope');
30
+ const role = readField('role');
31
+ const trust = readField('trust');
32
+ if (scope.length > 0) parts.push('--scope', quoteSlashCommandArg(scope));
33
+ if (role.length > 0) parts.push('--role', quoteSlashCommandArg(role));
34
+ if (trust.length > 0) parts.push('--trust', quoteSlashCommandArg(trust));
35
+ for (const env of splitCommaList(readField('env'))) parts.push('--env', quoteSlashCommandArg(env));
36
+ for (const path of splitCommaList(readField('paths'))) parts.push('--path', quoteSlashCommandArg(path));
37
+ for (const host of splitCommaList(readField('hosts'))) parts.push('--host', quoteSlashCommandArg(host));
38
+ parts.push('--yes');
39
+ return dispatchCommandEditorSubmission(
40
+ parts.join(' '),
41
+ 'Opening MCP server add/update',
42
+ 'The workspace handed a confirmed MCP server add/update command to the shell-owned command router.',
43
+ 'safe',
44
+ );
45
+ },
46
+ };
29
47
 
30
- function splitCommaList(value: string): readonly string[] {
31
- return value.split(',').map((entry) => entry.trim()).filter(Boolean);
48
+ function mcpToolsOrRepair(editor: AgentWorkspaceLocalEditor, readField: AgentWorkspaceFieldReader): AgentWorkspaceCommandEditorSubmission {
49
+ const server = quoteSlashCommandArg(readField('server'));
50
+ const tools = editor.kind === 'mcp-tools-server';
51
+ const command = tools ? `/mcp tools ${server}` : `/mcp repair ${server}`;
52
+ const title = tools ? 'Opening MCP server tools' : 'Opening MCP repair guidance';
53
+ return dispatchCommandEditorSubmission(
54
+ command,
55
+ title,
56
+ tools
57
+ ? 'The workspace handed read-only MCP server tool inspection to the shell-owned command router.'
58
+ : 'The workspace handed read-only MCP repair guidance to the shell-owned command router.',
59
+ 'read-only',
60
+ );
32
61
  }
33
62
 
34
63
  export function buildAgentWorkspaceMcpCommandEditorSubmission(
35
64
  editor: AgentWorkspaceLocalEditor,
36
65
  readField: AgentWorkspaceFieldReader,
37
- ): AgentWorkspaceMcpCommandEditorSubmission {
38
- if (editor.kind === 'mcp-tools-server' || editor.kind === 'mcp-repair') {
39
- const server = quoteSlashCommandArg(readField('server'));
40
- const tools = editor.kind === 'mcp-tools-server';
41
- const command = tools ? `/mcp tools ${server}` : `/mcp repair ${server}`;
42
- const title = tools ? 'Opening MCP server tools' : 'Opening MCP repair guidance';
43
- return {
44
- kind: 'dispatch',
45
- command,
46
- status: `${title}.`,
47
- actionResult: {
48
- kind: 'dispatched',
49
- title,
50
- detail: tools
51
- ? 'The workspace handed read-only MCP server tool inspection to the shell-owned command router.'
52
- : 'The workspace handed read-only MCP repair guidance to the shell-owned command router.',
53
- command,
54
- safety: 'read-only',
55
- },
56
- };
57
- }
58
- if (!isAffirmative(readField('confirm'))) {
59
- return {
60
- kind: 'editor',
61
- editor: { ...editor, message: 'MCP server add/update not confirmed. Type yes, then press Enter.' },
62
- status: 'MCP server add/update not confirmed.',
63
- };
64
- }
65
- const parts = [
66
- '/mcp',
67
- 'add',
68
- quoteSlashCommandArg(readField('name')),
69
- quoteSlashCommandArg(readField('command')),
70
- ...tokenizeSlashCommand(readField('args')).map(quoteSlashCommandArg),
71
- ];
72
- const scope = readField('scope');
73
- const role = readField('role');
74
- const trust = readField('trust');
75
- if (scope.length > 0) parts.push('--scope', quoteSlashCommandArg(scope));
76
- if (role.length > 0) parts.push('--role', quoteSlashCommandArg(role));
77
- if (trust.length > 0) parts.push('--trust', quoteSlashCommandArg(trust));
78
- for (const env of splitCommaList(readField('env'))) parts.push('--env', quoteSlashCommandArg(env));
79
- for (const path of splitCommaList(readField('paths'))) parts.push('--path', quoteSlashCommandArg(path));
80
- for (const host of splitCommaList(readField('hosts'))) parts.push('--host', quoteSlashCommandArg(host));
81
- parts.push('--yes');
82
- const command = parts.join(' ');
83
- return {
84
- kind: 'dispatch',
85
- command,
86
- status: 'Opening MCP server add/update.',
87
- actionResult: {
88
- kind: 'dispatched',
89
- title: 'Opening MCP server add/update',
90
- detail: 'The workspace handed a confirmed MCP server add/update command to the shell-owned command router.',
91
- command,
92
- safety: 'safe',
93
- },
94
- };
66
+ ): AgentWorkspaceCommandEditorSubmission {
67
+ return buildCommandEditorSubmissionFromTable(
68
+ editor.kind as AgentWorkspaceMcpCommandEditorKind,
69
+ editor,
70
+ readField,
71
+ MCP_COMMAND_SUBMISSION_HANDLERS,
72
+ );
95
73
  }