@pellux/goodvibes-agent 0.1.114 → 0.1.117

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 (168) hide show
  1. package/CHANGELOG.md +41 -650
  2. package/README.md +40 -50
  3. package/dist/package/main.js +38224 -35673
  4. package/docs/README.md +5 -4
  5. package/docs/{connected-services.md → connected-host.md} +18 -10
  6. package/docs/getting-started.md +43 -42
  7. package/docs/release-and-publishing.md +6 -4
  8. package/package.json +3 -3
  9. package/src/agent/behavior-discovery-summary.ts +165 -0
  10. package/src/agent/persona-discovery.ts +1 -3
  11. package/src/agent/persona-registry.ts +5 -3
  12. package/src/agent/reminder-schedule-format.ts +6 -6
  13. package/src/agent/reminder-schedule.ts +1 -1
  14. package/src/agent/routine-registry.ts +43 -4
  15. package/src/agent/routine-schedule-format.ts +6 -6
  16. package/src/agent/routine-schedule-promotion.ts +1 -1
  17. package/src/agent/routine-schedule-receipts.ts +1 -1
  18. package/src/agent/runtime-profile-starters.ts +331 -0
  19. package/src/agent/runtime-profile.ts +228 -330
  20. package/src/agent/skill-registry.ts +190 -13
  21. package/src/cli/agent-knowledge-command.ts +2 -2
  22. package/src/cli/agent-knowledge-format.ts +2 -2
  23. package/src/cli/agent-knowledge-runtime.ts +1 -1
  24. package/src/cli/bundle-command.ts +1 -1
  25. package/src/cli/entrypoint.ts +16 -7
  26. package/src/cli/help.ts +26 -19
  27. package/src/cli/local-library-command.ts +75 -21
  28. package/src/cli/management-commands.ts +7 -6
  29. package/src/cli/management.ts +6 -51
  30. package/src/cli/parser.ts +23 -2
  31. package/src/cli/profiles-command.ts +247 -1
  32. package/src/cli/routines-command.ts +139 -11
  33. package/src/cli/service-posture.ts +11 -11
  34. package/src/cli/status.ts +21 -25
  35. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  36. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  37. package/src/input/agent-workspace-activation.ts +2 -1
  38. package/src/input/agent-workspace-basic-command-editor-submission.ts +787 -0
  39. package/src/input/agent-workspace-basic-command-editors.ts +524 -459
  40. package/src/input/agent-workspace-categories.ts +177 -14
  41. package/src/input/agent-workspace-channel-command-editor-submission.ts +49 -0
  42. package/src/input/agent-workspace-channel-command-editors.ts +47 -0
  43. package/src/input/agent-workspace-command-editor.ts +25 -1
  44. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  45. package/src/input/agent-workspace-editors.ts +27 -0
  46. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  47. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  48. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  49. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  50. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  51. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  52. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  53. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  54. package/src/input/agent-workspace-navigation.ts +45 -0
  55. package/src/input/agent-workspace-notify-editor-submission.ts +107 -0
  56. package/src/input/agent-workspace-operations-command-editor-submission.ts +146 -0
  57. package/src/input/agent-workspace-operations-command-editors.ts +119 -0
  58. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  59. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  60. package/src/input/agent-workspace-requirements.ts +11 -0
  61. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  62. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  63. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  64. package/src/input/agent-workspace-setup.ts +49 -19
  65. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  66. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  67. package/src/input/agent-workspace-snapshot.ts +56 -8
  68. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  69. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  70. package/src/input/agent-workspace-types.ts +124 -0
  71. package/src/input/agent-workspace-voice-media.ts +1 -1
  72. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  73. package/src/input/agent-workspace.ts +47 -37
  74. package/src/input/command-registry.ts +1 -1
  75. package/src/input/commands/agent-runtime-profile-runtime.ts +160 -6
  76. package/src/input/commands/agent-skills-runtime.ts +76 -12
  77. package/src/input/commands/agent-workspace-runtime.ts +4 -3
  78. package/src/input/commands/brief-runtime.ts +38 -9
  79. package/src/input/commands/channels-runtime.ts +3 -3
  80. package/src/input/commands/compat-runtime.ts +32 -0
  81. package/src/input/commands/health-runtime.ts +22 -24
  82. package/src/input/commands/knowledge.ts +26 -5
  83. package/src/input/commands/local-provider-runtime.ts +7 -3
  84. package/src/input/commands/local-setup-review.ts +4 -14
  85. package/src/input/commands/mcp-runtime.ts +8 -12
  86. package/src/input/commands/operator-runtime.ts +8 -67
  87. package/src/input/commands/personas-runtime.ts +1 -1
  88. package/src/input/commands/platform-access-runtime.ts +7 -70
  89. package/src/input/commands/policy-dispatch.ts +1 -1
  90. package/src/input/commands/policy.ts +1 -5
  91. package/src/input/commands/product-runtime.ts +2 -2
  92. package/src/input/commands/qrcode-runtime.ts +6 -4
  93. package/src/input/commands/routines-runtime.ts +44 -6
  94. package/src/input/commands/runtime-services.ts +1 -14
  95. package/src/input/commands/security-runtime.ts +3 -8
  96. package/src/input/commands/session-content.ts +1 -1
  97. package/src/input/commands/session-workflow.ts +5 -14
  98. package/src/input/commands/shell-core.ts +8 -2
  99. package/src/input/commands/subscription-runtime.ts +1 -6
  100. package/src/input/commands/support-bundle-runtime.ts +53 -0
  101. package/src/input/commands/tasks-runtime.ts +2 -2
  102. package/src/input/commands.ts +4 -0
  103. package/src/input/feed-context-factory.ts +0 -1
  104. package/src/input/handler-content-actions.ts +0 -25
  105. package/src/input/handler-feed.ts +0 -2
  106. package/src/input/handler-interactions.ts +2 -31
  107. package/src/input/handler-onboarding.ts +29 -12
  108. package/src/input/handler-shortcuts.ts +1 -3
  109. package/src/input/handler.ts +4 -7
  110. package/src/input/keybindings.ts +9 -9
  111. package/src/input/mcp-workspace.ts +90 -26
  112. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  113. package/src/input/onboarding/onboarding-wizard-apply.ts +49 -0
  114. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  115. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +126 -11
  116. package/src/input/onboarding/onboarding-wizard-steps.ts +15 -8
  117. package/src/input/onboarding/onboarding-wizard-types.ts +1 -0
  118. package/src/input/onboarding/onboarding-wizard.ts +19 -2
  119. package/src/input/profile-picker-modal.ts +2 -2
  120. package/src/input/settings-modal-agent-policy.ts +6 -6
  121. package/src/input/settings-modal-subscriptions.ts +3 -3
  122. package/src/input/settings-modal.ts +14 -14
  123. package/src/main.ts +0 -2
  124. package/src/panels/automation-control-panel.ts +3 -3
  125. package/src/panels/builtin/agent.ts +1 -1
  126. package/src/panels/builtin/operations.ts +2 -2
  127. package/src/panels/builtin/session.ts +26 -18
  128. package/src/panels/builtin/shared.ts +6 -6
  129. package/src/panels/project-planning-panel.ts +1 -1
  130. package/src/panels/provider-health-domains.ts +3 -3
  131. package/src/panels/qr-panel.ts +44 -20
  132. package/src/panels/subscription-panel.ts +3 -3
  133. package/src/panels/system-messages-panel.ts +2 -2
  134. package/src/panels/tasks-panel.ts +4 -4
  135. package/src/planning/project-planning-coordinator.ts +2 -2
  136. package/src/renderer/agent-workspace.ts +52 -5
  137. package/src/renderer/block-actions.ts +1 -3
  138. package/src/renderer/help-overlay.ts +13 -8
  139. package/src/renderer/mcp-workspace.ts +32 -13
  140. package/src/renderer/model-picker-overlay.ts +0 -1
  141. package/src/renderer/model-workspace.ts +0 -2
  142. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  143. package/src/renderer/panel-picker-overlay.ts +1 -1
  144. package/src/renderer/profile-picker-modal.ts +2 -2
  145. package/src/renderer/settings-modal-helpers.ts +23 -23
  146. package/src/renderer/settings-modal.ts +18 -18
  147. package/src/renderer/system-message.ts +1 -1
  148. package/src/runtime/agent-runtime-events.ts +129 -0
  149. package/src/runtime/bootstrap-command-parts.ts +3 -2
  150. package/src/runtime/bootstrap-core.ts +4 -64
  151. package/src/runtime/bootstrap-shell.ts +42 -4
  152. package/src/runtime/bootstrap.ts +8 -27
  153. package/src/runtime/connected-host-auth.ts +42 -0
  154. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  155. package/src/runtime/index.ts +1 -5
  156. package/src/runtime/onboarding/apply.ts +213 -0
  157. package/src/runtime/onboarding/derivation.ts +25 -6
  158. package/src/runtime/onboarding/snapshot.ts +4 -2
  159. package/src/runtime/onboarding/types.ts +28 -1
  160. package/src/runtime/onboarding/verify.ts +81 -2
  161. package/src/runtime/operator-token-cleanup.ts +2 -2
  162. package/src/runtime/services.ts +27 -12
  163. package/src/shell/service-settings-sync.ts +7 -7
  164. package/src/shell/ui-openers.ts +27 -38
  165. package/src/tools/agent-local-registry-tool.ts +10 -4
  166. package/src/tools/agent-tool-policy-guard.ts +5 -7
  167. package/src/version.ts +1 -1
  168. package/src/cli/package-verification.ts +0 -274
@@ -0,0 +1,153 @@
1
+ import type { AgentWorkspaceActionResult, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
2
+ import { quoteSlashCommandArg } from './slash-command-parser.ts';
3
+
4
+ type AgentWorkspaceFieldReader = (fieldId: string) => string;
5
+
6
+ export type AgentWorkspaceWorkPlanEditorKind = 'workplan-add' | 'workplan-show' | 'workplan-status' | 'workplan-delete' | 'workplan-clear-completed';
7
+
8
+ export type AgentWorkspaceWorkPlanEditorSubmission =
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
+ };
21
+
22
+ const STATUS_COMMANDS: Record<string, string> = {
23
+ pending: 'pending',
24
+ todo: 'pending',
25
+ start: 'start',
26
+ active: 'start',
27
+ progress: 'start',
28
+ in_progress: 'start',
29
+ blocked: 'blocked',
30
+ block: 'blocked',
31
+ done: 'done',
32
+ complete: 'done',
33
+ completed: 'done',
34
+ failed: 'failed',
35
+ fail: 'failed',
36
+ cancelled: 'cancelled',
37
+ canceled: 'cancelled',
38
+ cancel: 'cancelled',
39
+ };
40
+
41
+ export function isAgentWorkspaceWorkPlanEditorKind(kind: string): kind is AgentWorkspaceWorkPlanEditorKind {
42
+ return kind === 'workplan-add'
43
+ || kind === 'workplan-show'
44
+ || kind === 'workplan-status'
45
+ || kind === 'workplan-delete'
46
+ || kind === 'workplan-clear-completed';
47
+ }
48
+
49
+ function isAffirmative(value: string): boolean {
50
+ return /^(y|yes|true)$/i.test(value.trim());
51
+ }
52
+
53
+ function unconfirmed(editor: AgentWorkspaceLocalEditor, message: string, status: string): AgentWorkspaceWorkPlanEditorSubmission {
54
+ return {
55
+ kind: 'editor',
56
+ editor: { ...editor, message },
57
+ status,
58
+ };
59
+ }
60
+
61
+ function dispatch(command: string, title: string, status: string, detail: string, safety: 'safe' | 'read-only' | 'delegates' | 'blocked'): AgentWorkspaceWorkPlanEditorSubmission {
62
+ return {
63
+ kind: 'dispatch',
64
+ command,
65
+ status,
66
+ actionResult: {
67
+ kind: 'dispatched',
68
+ title,
69
+ detail,
70
+ command,
71
+ safety,
72
+ },
73
+ };
74
+ }
75
+
76
+ export function buildAgentWorkspaceWorkPlanEditorSubmission(
77
+ editor: AgentWorkspaceLocalEditor,
78
+ readField: AgentWorkspaceFieldReader,
79
+ ): AgentWorkspaceWorkPlanEditorSubmission {
80
+ if (editor.kind === 'workplan-add') {
81
+ const parts = ['/workplan', 'add', quoteSlashCommandArg(readField('title'))];
82
+ const owner = readField('owner');
83
+ const source = readField('source');
84
+ const notes = readField('notes');
85
+ if (owner.length > 0) parts.push('--owner', quoteSlashCommandArg(owner));
86
+ if (source.length > 0) parts.push('--source', quoteSlashCommandArg(source));
87
+ if (notes.length > 0) parts.push('--notes', quoteSlashCommandArg(notes));
88
+ return dispatch(
89
+ parts.join(' '),
90
+ 'Opening work plan item creation',
91
+ 'Opening work plan item creation.',
92
+ 'The workspace handed a visible work plan item creation command to the shell-owned command router.',
93
+ 'safe',
94
+ );
95
+ }
96
+
97
+ if (editor.kind === 'workplan-show') {
98
+ const format = readField('format').trim().toLowerCase();
99
+ const command = format === 'markdown' ? '/workplan markdown' : '/workplan show';
100
+ return dispatch(
101
+ command,
102
+ 'Opening work plan detail',
103
+ 'Opening work plan detail.',
104
+ 'The workspace handed read-only work plan detail inspection to the shell-owned command router.',
105
+ 'read-only',
106
+ );
107
+ }
108
+
109
+ if (editor.kind === 'workplan-status') {
110
+ const requested = readField('status').trim().toLowerCase().replace(/[\s-]+/g, '_');
111
+ const commandStatus = STATUS_COMMANDS[requested];
112
+ if (!commandStatus) {
113
+ return {
114
+ kind: 'editor',
115
+ editor: { ...editor, message: 'Invalid status. Use pending, start, blocked, done, failed, or cancelled.' },
116
+ status: 'Work plan status update needs a valid status.',
117
+ };
118
+ }
119
+ const command = `/workplan ${commandStatus} ${quoteSlashCommandArg(readField('id'))}`;
120
+ return dispatch(
121
+ command,
122
+ 'Opening work plan status update',
123
+ 'Opening work plan status update.',
124
+ 'The workspace handed a visible work plan status update command to the shell-owned command router.',
125
+ 'safe',
126
+ );
127
+ }
128
+
129
+ if (editor.kind === 'workplan-delete') {
130
+ if (!isAffirmative(readField('confirm'))) {
131
+ return unconfirmed(editor, 'Work plan removal not confirmed. Type yes, then press Enter.', 'Work plan removal not confirmed.');
132
+ }
133
+ const command = `/workplan remove ${quoteSlashCommandArg(readField('id'))} --yes`;
134
+ return dispatch(
135
+ command,
136
+ 'Opening work plan item removal',
137
+ 'Opening work plan item removal.',
138
+ 'The workspace handed a confirmed work plan removal command to the shell-owned command router.',
139
+ 'safe',
140
+ );
141
+ }
142
+
143
+ if (!isAffirmative(readField('confirm'))) {
144
+ return unconfirmed(editor, 'Clear completed work plan items not confirmed. Type yes, then press Enter.', 'Clear completed work plan items not confirmed.');
145
+ }
146
+ return dispatch(
147
+ '/workplan clear-completed --yes',
148
+ 'Opening completed work plan cleanup',
149
+ 'Opening completed work plan cleanup.',
150
+ 'The workspace handed a confirmed work plan cleanup command to the shell-owned command router.',
151
+ 'safe',
152
+ );
153
+ }
@@ -11,24 +11,15 @@ import { AGENT_WORKSPACE_CATEGORIES } from './agent-workspace-categories.ts';
11
11
  import { buildAgentWorkspaceCommandEditorSubmission, isAgentWorkspaceCommandEditorKind } from './agent-workspace-command-editor.ts';
12
12
  import { quoteSlashCommandArg } from './slash-command-parser.ts';
13
13
  import { createDeleteEditor, createMemoryUpdateEditor, createPersonaUpdateEditor, createRoutineUpdateEditor, createSkillUpdateEditor, editorCategoryId, isAffirmative, splitList } from './agent-workspace-editors.ts';
14
+ import { createAgentWorkspaceLearnedBehavior } from './agent-workspace-learned-behavior.ts';
14
15
  import { deleteAgentWorkspaceMemoryEditor, submitAgentWorkspaceMemoryEditor } from './agent-workspace-memory-editor.ts';
16
+ import { jumpAgentWorkspaceSelection, moveAgentWorkspaceSelection, selectAgentWorkspaceCategory } from './agent-workspace-navigation.ts';
17
+ import { buildAgentWorkspaceRequirements } from './agent-workspace-requirements.ts';
15
18
  import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
16
19
  import type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
17
20
 
18
21
  export type { AgentWorkspaceChannelRisk, AgentWorkspaceChannelStatus } from './agent-workspace-channels.ts';
19
- export type {
20
- AgentWorkspaceAction,
21
- AgentWorkspaceActionResult,
22
- AgentWorkspaceCategory,
23
- AgentWorkspaceCommandDispatcher,
24
- AgentWorkspaceEditorField,
25
- AgentWorkspaceFocusPane,
26
- AgentWorkspaceLocalEditor,
27
- AgentWorkspaceLocalEditorKind,
28
- AgentWorkspaceLocalLibraryItem,
29
- AgentWorkspaceLocalOperation,
30
- AgentWorkspaceRuntimeSnapshot,
31
- } from './agent-workspace-types.ts';
22
+ export type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
32
23
  export { AGENT_WORKSPACE_MODAL_NAME } from './agent-workspace-types.ts';
33
24
  export { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
34
25
  export { handleAgentWorkspaceToken } from './agent-workspace-token.ts';
@@ -52,7 +43,7 @@ export class AgentWorkspace {
52
43
  private context: CommandContext | null = null;
53
44
  private dispatchCommand: AgentWorkspaceCommandDispatcher | null = null;
54
45
 
55
- open(context: CommandContext, dispatchCommand: AgentWorkspaceCommandDispatcher): void {
46
+ open(context: CommandContext, dispatchCommand: AgentWorkspaceCommandDispatcher, categoryId?: string): void {
56
47
  this.context = context;
57
48
  this.dispatchCommand = dispatchCommand;
58
49
  this.runtimeSnapshot = buildAgentWorkspaceRuntimeSnapshot(context);
@@ -61,6 +52,16 @@ export class AgentWorkspace {
61
52
  this.status = 'Ready. Choose an operator flow; ordinary assistant work stays in the main conversation.';
62
53
  this.lastActionResult = null;
63
54
  this.localEditor = null;
55
+ if (categoryId && !this.selectCategory(categoryId)) {
56
+ const normalized = categoryId.trim();
57
+ this.status = `Unknown Agent workspace area: ${normalized}.`;
58
+ this.lastActionResult = {
59
+ kind: 'guidance',
60
+ title: 'Unknown Agent workspace area',
61
+ detail: `Use one of: ${this.categories.map((category) => category.id).join(', ')}.`,
62
+ safety: 'safe',
63
+ };
64
+ }
64
65
  this.clampSelection();
65
66
  }
66
67
 
@@ -90,6 +91,10 @@ export class AgentWorkspace {
90
91
  return this.actions[this.selectedActionIndex] ?? null;
91
92
  }
92
93
 
94
+ selectCategory(categoryIdOrLabel: string): boolean {
95
+ return selectAgentWorkspaceCategory(this, categoryIdOrLabel);
96
+ }
97
+
93
98
  selectedLocalLibraryItem(kind: AgentWorkspaceLocalEditorKind): AgentWorkspaceLocalLibraryItem | null {
94
99
  const items = this.localLibraryItems(kind);
95
100
  if (items.length === 0) return null;
@@ -110,35 +115,19 @@ export class AgentWorkspace {
110
115
  }
111
116
 
112
117
  moveUp(): void {
113
- if (this.focusPane === 'categories') {
114
- this.selectedCategoryIndex = Math.max(0, this.selectedCategoryIndex - 1);
115
- this.selectedActionIndex = 0;
116
- } else {
117
- this.selectedActionIndex = Math.max(0, this.selectedActionIndex - 1);
118
- }
119
- this.clampSelection();
118
+ moveAgentWorkspaceSelection(this, -1);
120
119
  }
121
120
 
122
121
  moveDown(): void {
123
- if (this.focusPane === 'categories') {
124
- this.selectedCategoryIndex = Math.min(this.categories.length - 1, this.selectedCategoryIndex + 1);
125
- this.selectedActionIndex = 0;
126
- } else {
127
- this.selectedActionIndex = Math.min(this.actions.length - 1, this.selectedActionIndex + 1);
128
- }
129
- this.clampSelection();
122
+ moveAgentWorkspaceSelection(this, 1);
130
123
  }
131
124
 
132
125
  jumpHome(): void {
133
- if (this.focusPane === 'categories') this.selectedCategoryIndex = 0;
134
- else this.selectedActionIndex = 0;
135
- this.clampSelection();
126
+ jumpAgentWorkspaceSelection(this, 'home');
136
127
  }
137
128
 
138
129
  jumpEnd(): void {
139
- if (this.focusPane === 'categories') this.selectedCategoryIndex = this.categories.length - 1;
140
- else this.selectedActionIndex = this.actions.length - 1;
141
- this.clampSelection();
130
+ jumpAgentWorkspaceSelection(this, 'end');
142
131
  }
143
132
 
144
133
  refreshRuntimeSnapshot(): void {
@@ -184,7 +173,7 @@ export class AgentWorkspace {
184
173
  if (!editor || text.length === 0) return;
185
174
  const field = editor.fields[editor.selectedFieldIndex];
186
175
  if (!field) return;
187
- this.replaceEditorField(editor.selectedFieldIndex, `${field.value}${text}`, editor.message);
176
+ this.replaceEditorField(editor.selectedFieldIndex, `${field.value}${field.multiline ? text.replace(/\r\n?/g, '\n') : text.replace(/[\r\n]+/g, ' ')}`, editor.message);
188
177
  }
189
178
 
190
179
  appendEditorNewline(): void {
@@ -432,6 +421,12 @@ export class AgentWorkspace {
432
421
  return memory;
433
422
  }
434
423
 
424
+ private learnedBehaviorTarget(): Exclude<AgentWorkspaceLocalEditorKind, 'memory' | 'profile'> {
425
+ const target = this.editorField('target').trim().toLowerCase();
426
+ if (target === 'persona' || target === 'skill' || target === 'routine') return target;
427
+ throw new Error('Behavior type must be skill, routine, or persona.');
428
+ }
429
+
435
430
  private finishLocalOperation(kind: AgentWorkspaceLocalEditorKind, title: string, detail: string): void {
436
431
  this.runtimeSnapshot = this.context ? buildAgentWorkspaceRuntimeSnapshot(this.context) : this.runtimeSnapshot;
437
432
  this.clampLocalLibrarySelection(kind);
@@ -528,7 +523,18 @@ export class AgentWorkspace {
528
523
  this.submitLocalDeleteEditor(shellPaths, editor);
529
524
  return;
530
525
  }
531
- if (editor.kind === 'profile') {
526
+ if (editor.kind === 'learned-behavior') {
527
+ const created = createAgentWorkspaceLearnedBehavior(shellPaths, {
528
+ target: this.learnedBehaviorTarget(),
529
+ name: this.editorField('name'),
530
+ description: this.editorField('description'),
531
+ notes: this.editorField('notes'),
532
+ tags: splitList(this.editorField('tags')),
533
+ triggers: splitList(this.editorField('triggers')),
534
+ enable: isAffirmative(this.editorField('enable')),
535
+ });
536
+ this.finishLocalEditor(created.kind, created.id, created.name, 'Created');
537
+ } else if (editor.kind === 'profile') {
532
538
  const template = this.editorField('template');
533
539
  const templateId = template && template.toLowerCase() !== 'none' ? template : undefined;
534
540
  const profile = createAgentRuntimeProfile(shellPaths.homeDirectory, this.editorField('name'), {
@@ -572,6 +578,7 @@ export class AgentWorkspace {
572
578
  procedure: this.editorField('procedure'),
573
579
  triggers: splitList(this.editorField('triggers')),
574
580
  tags: splitList(this.editorField('tags')),
581
+ requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
575
582
  provenance: 'agent-workspace',
576
583
  });
577
584
  registry.setEnabled(updated.id, isAffirmative(this.editorField('enabled')));
@@ -584,6 +591,7 @@ export class AgentWorkspace {
584
591
  procedure: this.editorField('procedure'),
585
592
  triggers: splitList(this.editorField('triggers')),
586
593
  tags: splitList(this.editorField('tags')),
594
+ requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
587
595
  enabled: isAffirmative(this.editorField('enabled')),
588
596
  source: 'user',
589
597
  provenance: 'agent-workspace',
@@ -598,6 +606,7 @@ export class AgentWorkspace {
598
606
  steps: this.editorField('steps'),
599
607
  triggers: splitList(this.editorField('triggers')),
600
608
  tags: splitList(this.editorField('tags')),
609
+ requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
601
610
  provenance: 'agent-workspace',
602
611
  });
603
612
  registry.setEnabled(updated.id, isAffirmative(this.editorField('enabled')));
@@ -610,6 +619,7 @@ export class AgentWorkspace {
610
619
  steps: this.editorField('steps'),
611
620
  triggers: splitList(this.editorField('triggers')),
612
621
  tags: splitList(this.editorField('tags')),
622
+ requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
613
623
  enabled: isAffirmative(this.editorField('enabled')),
614
624
  source: 'user',
615
625
  provenance: 'agent-workspace',
@@ -755,7 +765,7 @@ export class AgentWorkspace {
755
765
  this.lastActionResult = {
756
766
  kind: 'refreshed',
757
767
  title: 'Created Agent profile',
758
- detail: `Created isolated Agent profile ${profile.id}${starter}. Launch it with goodvibes-agent --agent-profile ${profile.id}. The current TUI session was not switched.`,
768
+ detail: `Created isolated Agent profile ${profile.id}${starter}. Launch it with goodvibes-agent --agent-profile ${profile.id}. The current Agent session was not switched.`,
759
769
  safety: 'safe',
760
770
  };
761
771
  this.clampSelection();
@@ -110,7 +110,7 @@ export interface CommandShellUiOpeners {
110
110
  focusPrompt?: () => void;
111
111
  openPolicyPanel?: () => void;
112
112
  openMcpWorkspace?: () => void;
113
- openAgentWorkspace?: () => void;
113
+ openAgentWorkspace?: (categoryId?: string) => void;
114
114
  openSecurityPanel?: () => void;
115
115
  openKnowledgePanel?: () => void;
116
116
  openSubscriptionPanel?: () => void;
@@ -1,6 +1,9 @@
1
1
  import { mkdirSync } from 'node:fs';
2
2
  import { dirname } from 'node:path';
3
3
  import {
4
+ clearAgentRuntimeProfileSelection,
5
+ createAgentRuntimeProfileFromDiscovered,
6
+ createAgentRuntimeProfileTemplateFromDiscovered,
4
7
  createAgentRuntimeProfile,
5
8
  deleteAgentRuntimeProfile,
6
9
  exportAgentRuntimeProfileTemplate,
@@ -8,6 +11,9 @@ import {
8
11
  importAgentRuntimeProfileTemplate,
9
12
  listAgentRuntimeProfiles,
10
13
  listAgentRuntimeProfileTemplates,
14
+ readAgentRuntimeProfileSelection,
15
+ resolveAgentRuntimeProfileHome,
16
+ setAgentRuntimeProfileSelection,
11
17
  type AgentRuntimeProfileInfo,
12
18
  type AgentRuntimeProfileTemplateSummary,
13
19
  } from '../../agent/runtime-profile.ts';
@@ -22,6 +28,12 @@ function parseFlag(args: readonly string[], name: string): string | undefined {
22
28
  return value && !value.startsWith('--') ? value : undefined;
23
29
  }
24
30
 
31
+ function parseCsvFlag(args: readonly string[], name: string): readonly string[] | undefined {
32
+ const raw = parseFlag(args, name);
33
+ if (!raw) return undefined;
34
+ return raw.split(',').map((entry) => entry.trim()).filter(Boolean);
35
+ }
36
+
25
37
  function profileLine(profile: AgentRuntimeProfileInfo): string {
26
38
  const created = profile.createdAt ? ` created=${profile.createdAt}` : '';
27
39
  const starter = profile.starterTemplateId ? ` starter=${profile.starterTemplateId}` : '';
@@ -41,14 +53,29 @@ function templateLine(template: AgentRuntimeProfileTemplateSummary): string {
41
53
 
42
54
  function renderProfiles(homeDirectory: string): string {
43
55
  const profiles = listAgentRuntimeProfiles(homeDirectory);
56
+ const selected = readAgentRuntimeProfileSelection(homeDirectory);
57
+ const defaultLine = selected ? ` default: ${selected.id}${selected.exists ? '' : ' (missing)'}` : ' default: (base Agent home)';
44
58
  if (profiles.length === 0) {
45
59
  return [
46
60
  'Agent Profiles',
61
+ defaultLine,
47
62
  ' No isolated Agent profiles yet.',
48
63
  ' Create one with /agent-profile create <name> --template <id> --yes.',
49
64
  ].join('\n');
50
65
  }
51
- return ['Agent Profiles', ...profiles.map(profileLine)].join('\n');
66
+ return ['Agent Profiles', defaultLine, ...profiles.map(profileLine)].join('\n');
67
+ }
68
+
69
+ function renderProfileDetail(homeDirectory: string, name: string): string {
70
+ const profile = resolveAgentRuntimeProfileHome(homeDirectory, name);
71
+ const info = listAgentRuntimeProfiles(homeDirectory).find((entry) => entry.id === profile.id) ?? { ...profile, createdAt: null };
72
+ const starter = info.starterTemplateId ? [` starter: ${info.starterTemplateId} (${info.starterTemplateName ?? info.starterTemplateId})`] : [];
73
+ return [
74
+ `Agent profile: ${profile.id}`,
75
+ ` home: ${profile.homeDirectory}`,
76
+ ...starter,
77
+ ` use: goodvibes-agent --agent-profile ${profile.id}`,
78
+ ].join('\n');
52
79
  }
53
80
 
54
81
  function renderTemplates(homeDirectory: string): string {
@@ -62,6 +89,7 @@ function renderTemplates(homeDirectory: string): string {
62
89
  ' edit the JSON file',
63
90
  ' /agent-profile template import ./agent-starter.json --yes',
64
91
  ' /agent-profile create <name> --template <imported-id> --yes',
92
+ ' /agent-profile create-from-discovered <name> --yes',
65
93
  ].join('\n');
66
94
  }
67
95
 
@@ -83,7 +111,7 @@ function renderGuide(homeDirectory: string): string {
83
111
  '5. Create an Agent profile from the imported starter:',
84
112
  ' /agent-profile create <name> --template <imported-id> --yes',
85
113
  '',
86
- 'This writes only Agent-local starter/profile state. It does not mutate connected GoodVibes services, default wiki, or non-Agent knowledge segments.',
114
+ 'This writes only Agent-local starter/profile state. It does not mutate connected GoodVibes host, default wiki, or non-Agent knowledge segments.',
87
115
  ].join('\n');
88
116
  }
89
117
 
@@ -108,8 +136,8 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
108
136
  name: 'agent-profile',
109
137
  aliases: ['runtime-profile', 'agent-profiles'],
110
138
  description: 'Manage isolated Agent profiles and starter templates',
111
- usage: '[list|templates|guide|template show|template export|template import|create|delete]',
112
- handler(args, ctx) {
139
+ usage: '[list|show|default|use|templates|guide|template show|template export|template import|template from-discovered|create|create-from-discovered|delete]',
140
+ async handler(args, ctx) {
113
141
  const shellPaths = requireShellPaths(ctx);
114
142
  const homeDirectory = shellPaths.homeDirectory;
115
143
  const parsed = stripYesFlag(args);
@@ -127,6 +155,72 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
127
155
  return;
128
156
  }
129
157
 
158
+ if (sub === 'show' || sub === 'path' || sub === 'home') {
159
+ const name = commandArgs[1];
160
+ if (!name) {
161
+ ctx.print('Usage: /agent-profile show <name>');
162
+ return;
163
+ }
164
+ ctx.print(renderProfileDetail(homeDirectory, name));
165
+ return;
166
+ }
167
+
168
+ if (sub === 'default') {
169
+ const target = commandArgs[1];
170
+ if (target === 'clear' || target === 'none' || target === 'base') {
171
+ if (!parsed.yes) {
172
+ requireYesFlag(ctx, 'clear the default Agent profile', '/agent-profile default clear --yes');
173
+ return;
174
+ }
175
+ clearAgentRuntimeProfileSelection(homeDirectory);
176
+ ctx.print('Default Agent profile cleared. Next launch uses the base Agent home unless --agent-profile is provided.');
177
+ return;
178
+ }
179
+ if (target) {
180
+ if (!parsed.yes) {
181
+ requireYesFlag(ctx, `set default Agent profile ${target}`, '/agent-profile default <name> --yes');
182
+ return;
183
+ }
184
+ const selected = setAgentRuntimeProfileSelection(homeDirectory, target);
185
+ ctx.print([
186
+ `Default Agent profile selected: ${selected.id}`,
187
+ ` home: ${selected.homeDirectory}`,
188
+ ' next launch: goodvibes-agent',
189
+ ' explicit override: goodvibes-agent --agent-profile <name>',
190
+ ].join('\n'));
191
+ return;
192
+ }
193
+ const selected = readAgentRuntimeProfileSelection(homeDirectory);
194
+ ctx.print(selected
195
+ ? [
196
+ `Default Agent profile: ${selected.id}${selected.exists ? '' : ' (missing)'}`,
197
+ ` home: ${selected.homeDirectory}`,
198
+ ' next launch: goodvibes-agent',
199
+ ].join('\n')
200
+ : 'No default Agent profile selected. Next launch uses the base Agent home.');
201
+ return;
202
+ }
203
+
204
+ if (sub === 'use' || sub === 'select' || sub === 'switch') {
205
+ const name = commandArgs[1];
206
+ if (!name) {
207
+ ctx.print('Usage: /agent-profile use <name> --yes');
208
+ return;
209
+ }
210
+ if (!parsed.yes) {
211
+ requireYesFlag(ctx, `set default Agent profile ${name}`, '/agent-profile use <name> --yes');
212
+ return;
213
+ }
214
+ const selected = setAgentRuntimeProfileSelection(homeDirectory, name);
215
+ ctx.print([
216
+ `Default Agent profile selected: ${selected.id}`,
217
+ ` home: ${selected.homeDirectory}`,
218
+ ' next launch: goodvibes-agent',
219
+ ' explicit override: goodvibes-agent --agent-profile <name>',
220
+ ].join('\n'));
221
+ return;
222
+ }
223
+
130
224
  if (sub === 'guide' || sub === 'author') {
131
225
  ctx.print(renderGuide(homeDirectory));
132
226
  return;
@@ -175,7 +269,35 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
175
269
  ctx.print(`Agent starter template imported: ${template.id}\n source: ${template.source}\n use: /agent-profile create <name> --template ${template.id} --yes`);
176
270
  return;
177
271
  }
178
- ctx.print('Usage: /agent-profile template [show <id>|export <id> <path> --yes|import <path> --yes]');
272
+ if (mode === 'from-discovered' || mode === 'import-discovered') {
273
+ const templateId = commandArgs[2];
274
+ if (!templateId) {
275
+ ctx.print('Usage: /agent-profile template from-discovered <id> [--name <name>] [--description <summary>] [--persona <name>] [--skills all|name,name] [--routines all|name,name] [--replace] --yes');
276
+ return;
277
+ }
278
+ if (!parsed.yes) {
279
+ requireYesFlag(ctx, `create Agent starter template ${templateId} from discovered behavior`, '/agent-profile template from-discovered <id> --yes');
280
+ return;
281
+ }
282
+ const template = await createAgentRuntimeProfileTemplateFromDiscovered(shellPaths, {
283
+ id: templateId,
284
+ name: parseFlag(commandArgs, '--name'),
285
+ description: parseFlag(commandArgs, '--description'),
286
+ persona: parseFlag(commandArgs, '--persona'),
287
+ skills: parseCsvFlag(commandArgs, '--skills'),
288
+ routines: parseCsvFlag(commandArgs, '--routines'),
289
+ replace: commandArgs.includes('--replace'),
290
+ });
291
+ ctx.print([
292
+ `Agent starter template created from discovered behavior: ${template.id}`,
293
+ ` persona: ${template.personaName}`,
294
+ ` skills: ${template.skillNames.join(', ')}`,
295
+ ` routines: ${template.routineNames.join(', ')}`,
296
+ ` use: /agent-profile create <name> --template ${template.id} --yes`,
297
+ ].join('\n'));
298
+ return;
299
+ }
300
+ ctx.print('Usage: /agent-profile template [show <id>|export <id> <path> --yes|import <path> --yes|from-discovered <id> --yes]');
179
301
  return;
180
302
  }
181
303
 
@@ -200,6 +322,38 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
200
322
  return;
201
323
  }
202
324
 
325
+ if (sub === 'create-from-discovered' || sub === 'create-discovered') {
326
+ const profileName = commandArgs[1];
327
+ if (!profileName) {
328
+ ctx.print('Usage: /agent-profile create-from-discovered <name> [--template-id <id>] [--profile-name <display>] [--description <summary>] [--persona <name>] [--skills all|name,name] [--routines all|name,name] [--replace] --yes');
329
+ return;
330
+ }
331
+ if (!parsed.yes) {
332
+ requireYesFlag(ctx, `create Agent profile ${profileName} from discovered behavior`, '/agent-profile create-from-discovered <name> --yes');
333
+ return;
334
+ }
335
+ const created = await createAgentRuntimeProfileFromDiscovered(shellPaths, {
336
+ profileName,
337
+ templateId: parseFlag(commandArgs, '--template-id') ?? parseFlag(commandArgs, '--starter-id'),
338
+ name: parseFlag(commandArgs, '--profile-name') ?? parseFlag(commandArgs, '--name'),
339
+ description: parseFlag(commandArgs, '--description'),
340
+ persona: parseFlag(commandArgs, '--persona'),
341
+ skills: parseCsvFlag(commandArgs, '--skills'),
342
+ routines: parseCsvFlag(commandArgs, '--routines'),
343
+ replace: commandArgs.includes('--replace'),
344
+ });
345
+ ctx.print([
346
+ `Agent profile created from discovered behavior: ${created.profile.id}`,
347
+ ` home: ${created.profile.homeDirectory}`,
348
+ ` starter: ${created.template.id}`,
349
+ ` persona: ${created.template.personaName}`,
350
+ ` skills: ${created.template.skillNames.join(', ')}`,
351
+ ` routines: ${created.template.routineNames.join(', ')}`,
352
+ ` launch: goodvibes-agent --agent-profile ${created.profile.id}`,
353
+ ].join('\n'));
354
+ return;
355
+ }
356
+
203
357
  if (sub === 'delete') {
204
358
  const name = commandArgs[1];
205
359
  if (!name) {
@@ -214,7 +368,7 @@ export function registerAgentRuntimeProfileRuntimeCommands(registry: CommandRegi
214
368
  return;
215
369
  }
216
370
 
217
- ctx.print('Usage: /agent-profile [list|templates|guide|template show <id>|template export <id> <path> --yes|template import <path> --yes|create <name> [--template <id>] --yes|delete <name> --yes]');
371
+ ctx.print('Usage: /agent-profile [list|show <name>|default [<name>|clear] --yes|use <name> --yes|templates|guide|template show <id>|template export <id> <path> --yes|template import <path> --yes|create <name> [--template <id>] --yes|create-from-discovered <name> --yes|delete <name> --yes]');
218
372
  } catch (error) {
219
373
  ctx.print(`Error: ${error instanceof Error ? error.message : String(error)}`);
220
374
  }