@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,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 AgentWorkspaceSessionCommandEditorKind = Extract<
4
6
  AgentWorkspaceEditorKind,
@@ -41,201 +43,160 @@ export function isAgentWorkspaceSessionCommandEditorKind(kind: AgentWorkspaceEdi
41
43
  || kind === 'mode-domain';
42
44
  }
43
45
 
44
- export function createAgentWorkspaceSessionCommandEditor(kind: AgentWorkspaceSessionCommandEditorKind): AgentWorkspaceLocalEditor {
45
- if (kind === 'conversation-export') {
46
- return {
47
- kind,
48
- mode: 'create',
49
- title: 'Export Conversation',
50
- selectedFieldIndex: 0,
51
- message: 'Export the current conversation to a workspace file. Type yes on the final field to confirm.',
52
- fields: [
53
- { id: 'format', label: 'Format', value: 'markdown', required: true, multiline: false, hint: 'markdown or text.' },
54
- { id: 'path', label: 'Output path', value: './conversation.md', required: true, multiline: false, hint: 'Workspace-relative output path.' },
55
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /export with --yes.' },
56
- ],
57
- };
58
- }
59
- if (kind === 'conversation-events' || kind === 'conversation-groups') {
60
- const groups = kind === 'conversation-groups';
61
- return {
62
- kind,
63
- mode: 'create',
64
- title: groups ? 'Show Transcript Groups' : 'Show Transcript Events',
65
- selectedFieldIndex: 0,
66
- message: groups
67
- ? 'Inspect grouped transcript structure from the Agent workspace.'
68
- : 'Inspect transcript events from the Agent workspace.',
69
- fields: [
70
- { id: 'kind', label: 'Event kind', value: '', required: false, multiline: false, hint: 'Optional transcript event kind. Blank shows all.' },
71
- ],
72
- };
73
- }
74
- if (kind === 'conversation-find') {
75
- return {
76
- kind,
77
- mode: 'create',
78
- title: 'Find Transcript Text',
79
- selectedFieldIndex: 0,
80
- message: 'Search the current Agent transcript from the workspace.',
81
- fields: [
82
- { id: 'query', label: 'Search query', value: '', required: true, multiline: false, hint: 'Text to find in the current transcript.' },
83
- { id: 'kind', label: 'Event kind', value: '', required: false, multiline: false, hint: 'Optional transcript event kind.' },
84
- ],
85
- };
86
- }
87
- if (kind === 'effort-level') {
88
- return {
89
- kind,
90
- mode: 'update',
91
- title: 'Set Reasoning Effort',
92
- selectedFieldIndex: 0,
93
- message: 'Set the reasoning effort used by normal Agent chat turns when the selected model supports it.',
94
- fields: [
95
- { id: 'level', label: 'Effort level', value: 'medium', required: true, multiline: false, hint: 'instant, low, medium, or high.' },
96
- ],
97
- };
98
- }
99
- if (kind === 'session-save') {
100
- return {
101
- kind,
102
- mode: 'create',
103
- title: 'Save Session',
104
- selectedFieldIndex: 0,
105
- message: 'Save the current Agent session under a reviewable name. Type yes on the final field to confirm.',
106
- fields: [
107
- { id: 'name', label: 'Session name', value: '', required: true, multiline: false, hint: 'Local saved-session name.' },
108
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /save.' },
109
- ],
110
- };
111
- }
112
- if (kind === 'session-load') {
113
- return {
114
- kind,
115
- mode: 'update',
116
- title: 'Load Session',
117
- selectedFieldIndex: 0,
118
- message: 'Load a saved Agent session into the current conversation. Type yes on the final field to confirm.',
119
- fields: [
120
- { id: 'name', label: 'Session name', value: '', required: true, multiline: false, hint: 'Existing saved-session name from /sessions.' },
121
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /load.' },
122
- ],
123
- };
124
- }
125
- if (kind === 'session-rename') {
126
- return {
127
- kind,
128
- mode: 'update',
129
- title: 'Rename Current Session',
130
- selectedFieldIndex: 0,
131
- message: 'Rename the active Agent session from the workspace.',
132
- fields: [
133
- { id: 'name', label: 'New session name', value: '', required: true, multiline: false, hint: 'New reviewable name for the current session.' },
134
- ],
135
- };
136
- }
137
- if (kind === 'session-resume') {
138
- return {
139
- kind,
140
- mode: 'update',
141
- title: 'Resume Saved Session',
142
- selectedFieldIndex: 0,
143
- message: 'Resume one saved Agent session by id, prefix, or title.',
144
- fields: [
145
- { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Use /session list or the Saved sessions action to find ids.' },
146
- ],
147
- };
148
- }
149
- if (kind === 'session-info') {
150
- return {
151
- kind,
152
- mode: 'create',
153
- title: 'Inspect Saved Session',
154
- selectedFieldIndex: 0,
155
- message: 'Inspect saved session metadata without changing the current conversation.',
156
- fields: [
157
- { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Saved session id, prefix, or name.' },
158
- ],
159
- };
160
- }
161
- if (kind === 'session-export-saved') {
162
- return {
163
- kind,
164
- mode: 'create',
165
- title: 'Export Saved Session',
166
- selectedFieldIndex: 0,
167
- message: 'Print one saved session transcript as markdown or text.',
168
- fields: [
169
- { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Use . for the current session, or a saved session id/name.' },
170
- { id: 'format', label: 'Format', value: 'markdown', required: true, multiline: false, hint: 'markdown or text.' },
171
- ],
172
- };
173
- }
174
- if (kind === 'session-search') {
175
- return {
176
- kind,
177
- mode: 'create',
178
- title: 'Search Saved Sessions',
179
- selectedFieldIndex: 0,
180
- message: 'Search saved Agent sessions from the workspace.',
181
- fields: [
182
- { id: 'query', label: 'Search query', value: '', required: true, multiline: false, hint: 'Keyword or phrase to find in saved sessions.' },
183
- ],
184
- };
185
- }
186
- if (kind === 'session-delete') {
187
- return {
188
- kind,
189
- mode: 'delete',
190
- title: 'Delete Saved Session',
191
- selectedFieldIndex: 0,
192
- message: 'Delete one saved Agent session. Type yes on the final field to confirm.',
193
- fields: [
194
- { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Saved session id or prefix. The active session cannot be deleted.' },
195
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /session delete with --yes.' },
196
- ],
197
- };
198
- }
199
- if (kind === 'session-fork') {
200
- return {
201
- kind,
202
- mode: 'create',
203
- title: 'Fork Current Session',
204
- selectedFieldIndex: 0,
205
- message: 'Fork the current Agent session into a new local saved session.',
206
- fields: [
207
- { id: 'name', label: 'Fork name', value: '', required: false, multiline: false, hint: 'Optional new session name. Blank uses the default fork name.' },
208
- ],
209
- };
210
- }
211
- if (kind === 'session-graph') {
212
- return {
213
- kind,
214
- mode: 'create',
215
- title: 'Inspect Session Graph',
216
- selectedFieldIndex: 0,
217
- message: 'Inspect the read-only cross-session graph. Mutating graph actions remain blocked in Agent.',
218
- fields: [
219
- { id: 'sessionId', label: 'Session id', value: '', required: false, multiline: false, hint: 'Optional session id filter.' },
220
- { id: 'format', label: 'Format', value: 'text', required: false, multiline: false, hint: 'text or json.' },
221
- ],
222
- };
223
- }
224
- if (kind === 'mode-preset') {
225
- return {
226
- kind,
227
- mode: 'update',
228
- title: 'Set Interaction Mode',
229
- selectedFieldIndex: 0,
230
- message: 'Set the Agent interaction noise level. Type yes on the final field to confirm.',
231
- fields: [
232
- { id: 'preset', label: 'Preset', value: 'balanced', required: true, multiline: false, hint: 'quiet, balanced, or operator.' },
233
- { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /mode <preset> with --yes.' },
234
- ],
235
- };
236
- }
46
+ function conversationEventsOrGroupsSpec(kind: AgentWorkspaceSessionCommandEditorKind): AgentWorkspaceEditorSpec {
47
+ const groups = kind === 'conversation-groups';
237
48
  return {
238
- kind,
49
+ mode: 'create',
50
+ title: groups ? 'Show Transcript Groups' : 'Show Transcript Events',
51
+ selectedFieldIndex: 0,
52
+ message: groups
53
+ ? 'Inspect grouped transcript structure from the Agent workspace.'
54
+ : 'Inspect transcript events from the Agent workspace.',
55
+ fields: [
56
+ { id: 'kind', label: 'Event kind', value: '', required: false, multiline: false, hint: 'Optional transcript event kind. Blank shows all.' },
57
+ ],
58
+ };
59
+ }
60
+
61
+ const SESSION_COMMAND_EDITOR_SPECS: Readonly<Record<AgentWorkspaceSessionCommandEditorKind, AgentWorkspaceEditorSpecEntry<AgentWorkspaceSessionCommandEditorKind>>> = {
62
+ 'conversation-export': {
63
+ mode: 'create',
64
+ title: 'Export Conversation',
65
+ selectedFieldIndex: 0,
66
+ message: 'Export the current conversation to a workspace file. Type yes on the final field to confirm.',
67
+ fields: [
68
+ { id: 'format', label: 'Format', value: 'markdown', required: true, multiline: false, hint: 'markdown or text.' },
69
+ { id: 'path', label: 'Output path', value: './conversation.md', required: true, multiline: false, hint: 'Workspace-relative output path.' },
70
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /export with --yes.' },
71
+ ],
72
+ },
73
+ 'conversation-events': conversationEventsOrGroupsSpec,
74
+ 'conversation-groups': conversationEventsOrGroupsSpec,
75
+ 'conversation-find': {
76
+ mode: 'create',
77
+ title: 'Find Transcript Text',
78
+ selectedFieldIndex: 0,
79
+ message: 'Search the current Agent transcript from the workspace.',
80
+ fields: [
81
+ { id: 'query', label: 'Search query', value: '', required: true, multiline: false, hint: 'Text to find in the current transcript.' },
82
+ { id: 'kind', label: 'Event kind', value: '', required: false, multiline: false, hint: 'Optional transcript event kind.' },
83
+ ],
84
+ },
85
+ 'effort-level': {
86
+ mode: 'update',
87
+ title: 'Set Reasoning Effort',
88
+ selectedFieldIndex: 0,
89
+ message: 'Set the reasoning effort used by normal Agent chat turns when the selected model supports it.',
90
+ fields: [
91
+ { id: 'level', label: 'Effort level', value: 'medium', required: true, multiline: false, hint: 'instant, low, medium, or high.' },
92
+ ],
93
+ },
94
+ 'session-save': {
95
+ mode: 'create',
96
+ title: 'Save Session',
97
+ selectedFieldIndex: 0,
98
+ message: 'Save the current Agent session under a reviewable name. Type yes on the final field to confirm.',
99
+ fields: [
100
+ { id: 'name', label: 'Session name', value: '', required: true, multiline: false, hint: 'Local saved-session name.' },
101
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /save.' },
102
+ ],
103
+ },
104
+ 'session-load': {
105
+ mode: 'update',
106
+ title: 'Load Session',
107
+ selectedFieldIndex: 0,
108
+ message: 'Load a saved Agent session into the current conversation. Type yes on the final field to confirm.',
109
+ fields: [
110
+ { id: 'name', label: 'Session name', value: '', required: true, multiline: false, hint: 'Existing saved-session name from /sessions.' },
111
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /load.' },
112
+ ],
113
+ },
114
+ 'session-rename': {
115
+ mode: 'update',
116
+ title: 'Rename Current Session',
117
+ selectedFieldIndex: 0,
118
+ message: 'Rename the active Agent session from the workspace.',
119
+ fields: [
120
+ { id: 'name', label: 'New session name', value: '', required: true, multiline: false, hint: 'New reviewable name for the current session.' },
121
+ ],
122
+ },
123
+ 'session-resume': {
124
+ mode: 'update',
125
+ title: 'Resume Saved Session',
126
+ selectedFieldIndex: 0,
127
+ message: 'Resume one saved Agent session by id, prefix, or title.',
128
+ fields: [
129
+ { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Use /session list or the Saved sessions action to find ids.' },
130
+ ],
131
+ },
132
+ 'session-info': {
133
+ mode: 'create',
134
+ title: 'Inspect Saved Session',
135
+ selectedFieldIndex: 0,
136
+ message: 'Inspect saved session metadata without changing the current conversation.',
137
+ fields: [
138
+ { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Saved session id, prefix, or name.' },
139
+ ],
140
+ },
141
+ 'session-export-saved': {
142
+ mode: 'create',
143
+ title: 'Export Saved Session',
144
+ selectedFieldIndex: 0,
145
+ message: 'Print one saved session transcript as markdown or text.',
146
+ fields: [
147
+ { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Use . for the current session, or a saved session id/name.' },
148
+ { id: 'format', label: 'Format', value: 'markdown', required: true, multiline: false, hint: 'markdown or text.' },
149
+ ],
150
+ },
151
+ 'session-search': {
152
+ mode: 'create',
153
+ title: 'Search Saved Sessions',
154
+ selectedFieldIndex: 0,
155
+ message: 'Search saved Agent sessions from the workspace.',
156
+ fields: [
157
+ { id: 'query', label: 'Search query', value: '', required: true, multiline: false, hint: 'Keyword or phrase to find in saved sessions.' },
158
+ ],
159
+ },
160
+ 'session-delete': {
161
+ mode: 'delete',
162
+ title: 'Delete Saved Session',
163
+ selectedFieldIndex: 0,
164
+ message: 'Delete one saved Agent session. Type yes on the final field to confirm.',
165
+ fields: [
166
+ { id: 'target', label: 'Session id or name', value: '', required: true, multiline: false, hint: 'Saved session id or prefix. The active session cannot be deleted.' },
167
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /session delete with --yes.' },
168
+ ],
169
+ },
170
+ 'session-fork': {
171
+ mode: 'create',
172
+ title: 'Fork Current Session',
173
+ selectedFieldIndex: 0,
174
+ message: 'Fork the current Agent session into a new local saved session.',
175
+ fields: [
176
+ { id: 'name', label: 'Fork name', value: '', required: false, multiline: false, hint: 'Optional new session name. Blank uses the default fork name.' },
177
+ ],
178
+ },
179
+ 'session-graph': {
180
+ mode: 'create',
181
+ title: 'Inspect Session Graph',
182
+ selectedFieldIndex: 0,
183
+ message: 'Inspect the read-only cross-session graph. Mutating graph actions remain blocked in Agent.',
184
+ fields: [
185
+ { id: 'sessionId', label: 'Session id', value: '', required: false, multiline: false, hint: 'Optional session id filter.' },
186
+ { id: 'format', label: 'Format', value: 'text', required: false, multiline: false, hint: 'text or json.' },
187
+ ],
188
+ },
189
+ 'mode-preset': {
190
+ mode: 'update',
191
+ title: 'Set Interaction Mode',
192
+ selectedFieldIndex: 0,
193
+ message: 'Set the Agent interaction noise level. Type yes on the final field to confirm.',
194
+ fields: [
195
+ { id: 'preset', label: 'Preset', value: 'balanced', required: true, multiline: false, hint: 'quiet, balanced, or operator.' },
196
+ { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /mode <preset> with --yes.' },
197
+ ],
198
+ },
199
+ 'mode-domain': {
239
200
  mode: 'update',
240
201
  title: 'Set Domain Verbosity',
241
202
  selectedFieldIndex: 0,
@@ -245,5 +206,9 @@ export function createAgentWorkspaceSessionCommandEditor(kind: AgentWorkspaceSes
245
206
  { id: 'verbosity', label: 'Verbosity', value: 'normal', required: true, multiline: false, hint: 'minimal, normal, or verbose.' },
246
207
  { id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /mode set-domain.' },
247
208
  ],
248
- };
209
+ },
210
+ };
211
+
212
+ export function createAgentWorkspaceSessionCommandEditor(kind: AgentWorkspaceSessionCommandEditorKind): AgentWorkspaceLocalEditor {
213
+ return createAgentWorkspaceEditorFromTable(kind, SESSION_COMMAND_EDITOR_SPECS);
249
214
  }
@@ -3,6 +3,7 @@ import type { ConfigKey, ConfigSetting } from '@pellux/goodvibes-sdk/platform/co
3
3
  import type { PendingSubscriptionLogin, ProviderSubscription } from '@pellux/goodvibes-sdk/platform/config';
4
4
  import { setHarnessSetting } from '../agent/harness-control.ts';
5
5
  import { isExternalHostOwnedSettingKey } from '../config/agent-settings-policy.ts';
6
+ import { applyThemeModeSettingChange, THEME_MODE_CONFIG_KEY, THEME_MODE_SYNTHETIC_SETTING } from '../renderer/theme-mode-config.ts';
6
7
  import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
7
8
  import type { CommandContext } from './command-registry.ts';
8
9
  import type {
@@ -325,9 +326,15 @@ function applyTuiSubscriptions(context: CommandContext, parseErrors: string[]):
325
326
  }
326
327
 
327
328
  export function agentWorkspaceSettingSchema(context: CommandContext | null, key: string): ConfigSetting | null {
328
- return context?.platform?.configManager
329
+ const fromSchema = context?.platform?.configManager
329
330
  ?.getSchema()
330
331
  .find((setting) => setting.key === key) ?? null;
332
+ if (fromSchema) return fromSchema;
333
+ // display.themeMode is a TUI-local synthetic setting (W4-R4) that never
334
+ // joined the SDK ConfigKey schema — the classic settings-modal injects the
335
+ // same descriptor into its own groups map rather than the schema. Mirror
336
+ // that fallback here so the workspace surface can discover and cycle it too.
337
+ return key === THEME_MODE_CONFIG_KEY ? THEME_MODE_SYNTHETIC_SETTING : null;
331
338
  }
332
339
 
333
340
  export function isAgentWorkspaceActionVisible(context: CommandContext | null, action: AgentWorkspaceAction): boolean {
@@ -432,6 +439,38 @@ export async function applyAgentWorkspaceSettingValue(
432
439
  },
433
440
  };
434
441
  }
442
+ if (String(setting.key) === THEME_MODE_CONFIG_KEY) {
443
+ try {
444
+ // display.themeMode is agent-local and never joined the SDK ConfigKey
445
+ // schema, so setHarnessSetting's schema lookup would reject it. Persist
446
+ // directly (the same setDynamic round-trip the classic settings-modal
447
+ // uses for this key), then run the ONE shared apply hook — forced
448
+ // dark/light flips the active mode now with a full repaint; auto only
449
+ // re-probes at startup, stated honestly in the returned message.
450
+ configManager.setDynamic(setting.key as ConfigKey, value);
451
+ const { message } = applyThemeModeSettingChange(value, () => context?.clearScreen?.());
452
+ return {
453
+ status: message,
454
+ result: {
455
+ kind: 'refreshed',
456
+ title: `${setting.key} updated`,
457
+ detail: message,
458
+ safety: 'safe',
459
+ },
460
+ };
461
+ } catch (error) {
462
+ const detail = error instanceof Error ? error.message : String(error);
463
+ return {
464
+ status: detail,
465
+ result: {
466
+ kind: 'error',
467
+ title: `${setting.key} update failed`,
468
+ detail,
469
+ safety: 'safe',
470
+ },
471
+ };
472
+ }
473
+ }
435
474
  try {
436
475
  const result = await setHarnessSetting(configManager, context?.platform?.secretsManager, setting.key, value);
437
476
  return {
@@ -1,67 +1,33 @@
1
- import type { AgentWorkspaceActionResult, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
1
+ import type { AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
2
  import type { AgentWorkspaceSkillBundleCommandEditorKind } from './agent-workspace-skill-bundle-command-editors.ts';
3
3
  import { isAgentWorkspaceSkillBundleCommandEditorKind } from './agent-workspace-skill-bundle-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 } from './agent-workspace-command-editor-engine.ts';
5
7
 
6
- type AgentWorkspaceFieldReader = (fieldId: string) => string;
7
-
8
- export type AgentWorkspaceSkillBundleCommandEditorSubmission =
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 AgentWorkspaceSkillBundleCommandEditorSubmission = AgentWorkspaceCommandEditorSubmission;
21
9
 
22
10
  export function isAgentWorkspaceSkillBundleCommandSubmissionKind(kind: string): kind is AgentWorkspaceSkillBundleCommandEditorKind {
23
11
  return isAgentWorkspaceSkillBundleCommandEditorKind(kind as AgentWorkspaceSkillBundleCommandEditorKind);
24
12
  }
25
13
 
26
- function isAffirmative(value: string): boolean {
27
- return /^(y|yes|true)$/i.test(value.trim());
28
- }
29
-
30
- function unconfirmed(editor: AgentWorkspaceLocalEditor, message: string): AgentWorkspaceSkillBundleCommandEditorSubmission {
31
- return {
32
- kind: 'editor',
33
- editor: { ...editor, message },
34
- status: message,
35
- };
36
- }
37
-
38
- function dispatch(command: string, title: string, detail: string, safety: AgentWorkspaceActionResult['safety']): AgentWorkspaceSkillBundleCommandEditorSubmission {
39
- return {
40
- kind: 'dispatch',
41
- command,
42
- status: `${title}.`,
43
- actionResult: { kind: 'dispatched', title, detail, command, safety },
44
- };
14
+ function unconfirmed(editor: AgentWorkspaceLocalEditor, message: string): AgentWorkspaceCommandEditorSubmission {
15
+ return editorMessageSubmission(editor, message);
45
16
  }
46
17
 
47
- export function buildAgentWorkspaceSkillBundleCommandEditorSubmission(
48
- editor: AgentWorkspaceLocalEditor,
49
- readField: AgentWorkspaceFieldReader,
50
- ): AgentWorkspaceSkillBundleCommandEditorSubmission {
51
- if (editor.kind === 'skill-bundle-search') {
18
+ const SKILL_BUNDLE_COMMAND_SUBMISSION_HANDLERS: Readonly<Record<AgentWorkspaceSkillBundleCommandEditorKind, AgentWorkspaceCommandSubmissionHandler>> = {
19
+ 'skill-bundle-search': (_editor, readField) => {
52
20
  const query = readField('query').trim();
53
21
  const command = query.length > 0 ? `/skills bundle search ${quoteSlashCommandArg(query)}` : '/skills bundle search';
54
- return dispatch(command, 'Opening skill bundle search', 'The workspace handed a read-only skill bundle search command to the shell-owned command router.', 'read-only');
55
- }
56
- if (editor.kind === 'skill-bundle-show') {
57
- return dispatch(
58
- `/skills bundle show ${quoteSlashCommandArg(readField('id'))}`,
59
- 'Opening skill bundle detail',
60
- 'The workspace handed a read-only skill bundle detail command to the shell-owned command router.',
61
- 'read-only',
62
- );
63
- }
64
- if (editor.kind === 'skill-bundle-update') {
22
+ return dispatchCommandEditorSubmission(command, 'Opening skill bundle search', 'The workspace handed a read-only skill bundle search command to the shell-owned command router.', 'read-only');
23
+ },
24
+ 'skill-bundle-show': (_editor, readField) => dispatchCommandEditorSubmission(
25
+ `/skills bundle show ${quoteSlashCommandArg(readField('id'))}`,
26
+ 'Opening skill bundle detail',
27
+ 'The workspace handed a read-only skill bundle detail command to the shell-owned command router.',
28
+ 'read-only',
29
+ ),
30
+ 'skill-bundle-update': (editor, readField) => {
65
31
  if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Skill bundle update not confirmed. Type yes, then press Enter.');
66
32
  const parts = ['/skills', 'bundle', 'update', quoteSlashCommandArg(readField('id'))];
67
33
  const name = readField('name');
@@ -70,32 +36,50 @@ export function buildAgentWorkspaceSkillBundleCommandEditorSubmission(
70
36
  if (name.length > 0) parts.push('--name', quoteSlashCommandArg(name));
71
37
  if (description.length > 0) parts.push('--description', quoteSlashCommandArg(description));
72
38
  if (skills.length > 0) parts.push('--skills', quoteSlashCommandArg(skills));
73
- return dispatch(parts.join(' '), 'Opening skill bundle update', 'The workspace handed a confirmed skill bundle update command to the shell-owned command router.', 'safe');
74
- }
75
- if (editor.kind === 'skill-bundle-enable' || editor.kind === 'skill-bundle-disable' || editor.kind === 'skill-bundle-review') {
76
- if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Skill bundle action not confirmed. Type yes, then press Enter.');
77
- const verb = editor.kind.replace('skill-bundle-', '');
78
- return dispatch(
79
- `/skills bundle ${verb} ${quoteSlashCommandArg(readField('id'))}`,
80
- `Opening skill bundle ${verb}`,
81
- `The workspace handed a confirmed skill bundle ${verb} command to the shell-owned command router.`,
82
- 'safe',
83
- );
84
- }
85
- if (editor.kind === 'skill-bundle-stale') {
39
+ return dispatchCommandEditorSubmission(parts.join(' '), 'Opening skill bundle update', 'The workspace handed a confirmed skill bundle update command to the shell-owned command router.', 'safe');
40
+ },
41
+ 'skill-bundle-enable': (editor, readField) => skillBundleAction(editor, readField),
42
+ 'skill-bundle-disable': (editor, readField) => skillBundleAction(editor, readField),
43
+ 'skill-bundle-review': (editor, readField) => skillBundleAction(editor, readField),
44
+ 'skill-bundle-stale': (editor, readField) => {
86
45
  if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Skill bundle stale action not confirmed. Type yes, then press Enter.');
87
- return dispatch(
46
+ return dispatchCommandEditorSubmission(
88
47
  `/skills bundle stale ${quoteSlashCommandArg(readField('id'))} ${quoteSlashCommandArg(readField('reason'))}`,
89
48
  'Opening skill bundle stale review',
90
49
  'The workspace handed a confirmed skill bundle stale command to the shell-owned command router.',
91
50
  'safe',
92
51
  );
93
- }
94
- if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Skill bundle delete not confirmed. Type yes, then press Enter.');
95
- return dispatch(
96
- `/skills bundle delete ${quoteSlashCommandArg(readField('id'))} --yes`,
97
- 'Opening skill bundle delete',
98
- 'The workspace handed a confirmed skill bundle delete command to the shell-owned command router.',
52
+ },
53
+ 'skill-bundle-delete': (editor, readField) => {
54
+ if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Skill bundle delete not confirmed. Type yes, then press Enter.');
55
+ return dispatchCommandEditorSubmission(
56
+ `/skills bundle delete ${quoteSlashCommandArg(readField('id'))} --yes`,
57
+ 'Opening skill bundle delete',
58
+ 'The workspace handed a confirmed skill bundle delete command to the shell-owned command router.',
59
+ 'safe',
60
+ );
61
+ },
62
+ };
63
+
64
+ function skillBundleAction(editor: AgentWorkspaceLocalEditor, readField: AgentWorkspaceFieldReader): AgentWorkspaceCommandEditorSubmission {
65
+ if (!isAffirmative(readField('confirm'))) return unconfirmed(editor, 'Skill bundle action not confirmed. Type yes, then press Enter.');
66
+ const verb = editor.kind.replace('skill-bundle-', '');
67
+ return dispatchCommandEditorSubmission(
68
+ `/skills bundle ${verb} ${quoteSlashCommandArg(readField('id'))}`,
69
+ `Opening skill bundle ${verb}`,
70
+ `The workspace handed a confirmed skill bundle ${verb} command to the shell-owned command router.`,
99
71
  'safe',
100
72
  );
101
73
  }
74
+
75
+ export function buildAgentWorkspaceSkillBundleCommandEditorSubmission(
76
+ editor: AgentWorkspaceLocalEditor,
77
+ readField: AgentWorkspaceFieldReader,
78
+ ): AgentWorkspaceCommandEditorSubmission {
79
+ return buildCommandEditorSubmissionFromTable(
80
+ editor.kind as AgentWorkspaceSkillBundleCommandEditorKind,
81
+ editor,
82
+ readField,
83
+ SKILL_BUNDLE_COMMAND_SUBMISSION_HANDLERS,
84
+ );
85
+ }