@pellux/goodvibes-agent 0.1.117 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176073 -170980
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +134 -68
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +44 -36
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  66. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  67. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  68. package/src/input/agent-workspace-local-operations.ts +218 -0
  69. package/src/input/agent-workspace-local-selection.ts +75 -0
  70. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  71. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  72. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  73. package/src/input/agent-workspace-memory-editor.ts +2 -2
  74. package/src/input/agent-workspace-navigation.ts +38 -2
  75. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  76. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  77. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  78. package/src/input/agent-workspace-panel-route.ts +43 -0
  79. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  80. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  81. package/src/input/agent-workspace-search.ts +169 -0
  82. package/src/input/agent-workspace-setup.ts +22 -11
  83. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  84. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  85. package/src/input/agent-workspace-snapshot.ts +112 -13
  86. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  87. package/src/input/agent-workspace-token.ts +18 -2
  88. package/src/input/agent-workspace-types.ts +92 -4
  89. package/src/input/agent-workspace-voice-media.ts +3 -6
  90. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  91. package/src/input/agent-workspace.ts +136 -208
  92. package/src/input/command-registry.ts +4 -1
  93. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  94. package/src/input/commands/agent-skills-runtime.ts +83 -70
  95. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  96. package/src/input/commands/brief-runtime.ts +25 -24
  97. package/src/input/commands/channels-runtime.ts +145 -31
  98. package/src/input/commands/delegation-runtime.ts +29 -23
  99. package/src/input/commands/experience-runtime.ts +6 -4
  100. package/src/input/commands/guidance-runtime.ts +4 -4
  101. package/src/input/commands/health-runtime.ts +140 -115
  102. package/src/input/commands/knowledge.ts +57 -56
  103. package/src/input/commands/local-provider-runtime.ts +36 -18
  104. package/src/input/commands/local-runtime.ts +138 -16
  105. package/src/input/commands/local-setup-review.ts +7 -7
  106. package/src/input/commands/mcp-runtime.ts +56 -35
  107. package/src/input/commands/notify-runtime.ts +38 -9
  108. package/src/input/commands/operator-actions-runtime.ts +138 -0
  109. package/src/input/commands/operator-runtime.ts +6 -17
  110. package/src/input/commands/personas-runtime.ts +45 -30
  111. package/src/input/commands/planning-runtime.ts +1 -1
  112. package/src/input/commands/platform-access-runtime.ts +29 -29
  113. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  114. package/src/input/commands/qrcode-runtime.ts +45 -6
  115. package/src/input/commands/recall-bundle.ts +11 -11
  116. package/src/input/commands/recall-capture.ts +13 -11
  117. package/src/input/commands/recall-query.ts +29 -21
  118. package/src/input/commands/recall-review.ts +2 -1
  119. package/src/input/commands/routines-runtime.ts +59 -43
  120. package/src/input/commands/schedule-runtime.ts +33 -20
  121. package/src/input/commands/security-runtime.ts +4 -4
  122. package/src/input/commands/session-content.ts +80 -78
  123. package/src/input/commands/session-workflow.ts +132 -93
  124. package/src/input/commands/session.ts +3 -174
  125. package/src/input/commands/shell-core.ts +32 -17
  126. package/src/input/commands/subscription-runtime.ts +53 -179
  127. package/src/input/commands/tasks-runtime.ts +20 -20
  128. package/src/input/commands/work-plan-runtime.ts +33 -8
  129. package/src/input/commands.ts +2 -2
  130. package/src/input/feed-context-factory.ts +2 -1
  131. package/src/input/file-picker.ts +3 -2
  132. package/src/input/handler-command-route.ts +4 -7
  133. package/src/input/handler-content-actions.ts +89 -1
  134. package/src/input/handler-feed-routes.ts +19 -12
  135. package/src/input/handler-feed.ts +6 -3
  136. package/src/input/handler-interactions.ts +7 -5
  137. package/src/input/handler-modal-stack.ts +3 -3
  138. package/src/input/handler-onboarding.ts +24 -80
  139. package/src/input/handler-shortcuts.ts +15 -4
  140. package/src/input/handler.ts +47 -17
  141. package/src/input/input-history.ts +5 -6
  142. package/src/input/keybindings.ts +22 -11
  143. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  144. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  145. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  146. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  147. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  148. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  149. package/src/input/profile-picker-modal.ts +31 -12
  150. package/src/input/session-picker-modal.ts +21 -4
  151. package/src/input/settings-modal-behavior.ts +0 -3
  152. package/src/input/settings-modal-subscriptions.ts +3 -3
  153. package/src/input/settings-modal-types.ts +2 -13
  154. package/src/input/settings-modal.ts +6 -52
  155. package/src/input/submission-intent.ts +0 -1
  156. package/src/input/submission-router.ts +3 -3
  157. package/src/main.ts +18 -8
  158. package/src/panels/approval-panel.ts +8 -8
  159. package/src/panels/automation-control-panel.ts +2 -2
  160. package/src/panels/base-panel.ts +1 -1
  161. package/src/panels/builtin/agent.ts +1 -1
  162. package/src/panels/builtin/operations.ts +1 -10
  163. package/src/panels/builtin/session.ts +9 -9
  164. package/src/panels/builtin/shared.ts +2 -2
  165. package/src/panels/cost-tracker-panel.ts +1 -1
  166. package/src/panels/docs-panel.ts +5 -3
  167. package/src/panels/index.ts +0 -1
  168. package/src/panels/knowledge-panel.ts +6 -5
  169. package/src/panels/memory-panel.ts +7 -6
  170. package/src/panels/panel-list-panel.ts +36 -80
  171. package/src/panels/project-planning-panel.ts +18 -11
  172. package/src/panels/provider-account-snapshot.ts +51 -25
  173. package/src/panels/provider-accounts-panel.ts +33 -18
  174. package/src/panels/provider-health-domains.ts +45 -4
  175. package/src/panels/provider-health-panel.ts +5 -4
  176. package/src/panels/qr-panel.ts +1 -1
  177. package/src/panels/schedule-panel.ts +9 -17
  178. package/src/panels/security-panel.ts +8 -8
  179. package/src/panels/session-browser-panel.ts +10 -10
  180. package/src/panels/subscription-panel.ts +3 -3
  181. package/src/panels/system-messages-panel.ts +1 -1
  182. package/src/panels/tasks-panel.ts +20 -13
  183. package/src/panels/tool-inspector-panel.ts +19 -12
  184. package/src/panels/work-plan-panel.ts +5 -5
  185. package/src/planning/project-planning-coordinator.ts +1 -1
  186. package/src/provider-auth-route-display.ts +9 -0
  187. package/src/renderer/agent-workspace-style.ts +34 -0
  188. package/src/renderer/agent-workspace.ts +228 -52
  189. package/src/renderer/autocomplete-overlay.ts +25 -6
  190. package/src/renderer/bookmark-modal.ts +19 -4
  191. package/src/renderer/buffer.ts +4 -2
  192. package/src/renderer/context-inspector.ts +50 -13
  193. package/src/renderer/diff.ts +1 -1
  194. package/src/renderer/file-picker-overlay.ts +19 -6
  195. package/src/renderer/help-overlay.ts +106 -33
  196. package/src/renderer/history-search-overlay.ts +19 -4
  197. package/src/renderer/live-tail-modal.ts +27 -5
  198. package/src/renderer/mcp-workspace.ts +164 -50
  199. package/src/renderer/model-picker-overlay.ts +58 -2
  200. package/src/renderer/model-workspace.ts +104 -20
  201. package/src/renderer/process-modal.ts +27 -6
  202. package/src/renderer/profile-picker-modal.ts +20 -4
  203. package/src/renderer/search-overlay.ts +25 -5
  204. package/src/renderer/selection-modal-overlay.ts +46 -14
  205. package/src/renderer/session-picker-modal.ts +18 -1
  206. package/src/renderer/settings-modal-helpers.ts +2 -40
  207. package/src/renderer/settings-modal.ts +83 -50
  208. package/src/renderer/tool-call.ts +20 -11
  209. package/src/runtime/agent-runtime-events.ts +6 -6
  210. package/src/runtime/bootstrap-command-context.ts +7 -1
  211. package/src/runtime/bootstrap-command-parts.ts +9 -7
  212. package/src/runtime/bootstrap-core.ts +19 -1
  213. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  214. package/src/runtime/bootstrap-shell.ts +4 -4
  215. package/src/runtime/bootstrap.ts +31 -22
  216. package/src/runtime/connected-host-auth.ts +7 -2
  217. package/src/runtime/diagnostics/panels/index.ts +2 -2
  218. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  219. package/src/runtime/index.ts +2 -1
  220. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  221. package/src/runtime/onboarding/apply.ts +80 -79
  222. package/src/runtime/onboarding/derivation.ts +5 -8
  223. package/src/runtime/onboarding/snapshot.ts +0 -15
  224. package/src/runtime/onboarding/types.ts +8 -19
  225. package/src/runtime/onboarding/verify.ts +32 -10
  226. package/src/runtime/operator-token-cleanup.ts +1 -1
  227. package/src/runtime/services.ts +91 -22
  228. package/src/runtime/store/selectors/index.ts +3 -3
  229. package/src/runtime/store/state.ts +1 -4
  230. package/src/runtime/surface-feature-flags.ts +41 -6
  231. package/src/runtime/ui-read-models.ts +3 -4
  232. package/src/runtime/ui-services.ts +6 -6
  233. package/src/shell/blocking-input.ts +2 -1
  234. package/src/shell/ui-openers.ts +3 -13
  235. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  236. package/src/tools/agent-channel-send-tool.ts +133 -0
  237. package/src/tools/agent-context-policy.ts +1 -1
  238. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  239. package/src/tools/agent-knowledge-tool.ts +170 -0
  240. package/src/tools/agent-local-registry-tool.ts +269 -69
  241. package/src/tools/agent-media-generate-tool.ts +133 -0
  242. package/src/tools/agent-notify-tool.ts +143 -0
  243. package/src/tools/agent-operator-action-tool.ts +137 -0
  244. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  245. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  246. package/src/tools/agent-tool-policy-guard.ts +8 -8
  247. package/src/tools/agent-work-plan-tool.ts +256 -0
  248. package/src/version.ts +1 -1
  249. package/src/input/commands/policy-dispatch.ts +0 -339
  250. package/src/input/commands/policy.ts +0 -13
  251. package/src/panels/panel-picker.ts +0 -105
  252. package/src/panels/policy-panel.ts +0 -308
  253. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -0,0 +1,66 @@
1
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+
4
+ export type RollbackAction = () => Promise<void> | void;
5
+
6
+ export function isPlainObject(value: unknown): value is Record<string, unknown> {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
9
+
10
+ export function readJsonObject(path: string): Record<string, unknown> {
11
+ if (!existsSync(path)) return {};
12
+ const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
13
+ if (!isPlainObject(parsed)) throw new Error(`Expected an object JSON payload at ${path}.`);
14
+ return parsed;
15
+ }
16
+
17
+ export function writeJsonObject(path: string, payload: Record<string, unknown>): void {
18
+ mkdirSync(dirname(path), { recursive: true });
19
+ writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
20
+ }
21
+
22
+ export function setNestedValue(root: Record<string, unknown>, key: string, value: unknown): Record<string, unknown> {
23
+ const parts = key.split('.');
24
+ const next = structuredClone(root);
25
+ let cursor: Record<string, unknown> = next;
26
+
27
+ for (let index = 0; index < parts.length - 1; index += 1) {
28
+ const part = parts[index]!;
29
+ let existing = cursor[part];
30
+ if (!isPlainObject(existing)) {
31
+ existing = {};
32
+ cursor[part] = existing;
33
+ }
34
+ cursor = existing as Record<string, unknown>;
35
+ }
36
+
37
+ cursor[parts[parts.length - 1]!] = structuredClone(value);
38
+ return next;
39
+ }
40
+
41
+ export function restoreFile(path: string, previous: string | null, reload?: () => void): void {
42
+ if (previous === null) {
43
+ if (existsSync(path)) unlinkSync(path);
44
+ } else {
45
+ mkdirSync(dirname(path), { recursive: true });
46
+ writeFileSync(path, previous, 'utf-8');
47
+ }
48
+ reload?.();
49
+ }
50
+
51
+ export function snapshotFileRollback(path: string, reload?: () => void): RollbackAction {
52
+ const previous = existsSync(path) ? readFileSync(path, 'utf-8') : null;
53
+ return () => restoreFile(path, previous, reload);
54
+ }
55
+
56
+ export async function runRollbacks(rollbacks: readonly RollbackAction[]): Promise<readonly string[]> {
57
+ const errors: string[] = [];
58
+ for (const rollback of [...rollbacks].reverse()) {
59
+ try {
60
+ await rollback();
61
+ } catch (error) {
62
+ errors.push(error instanceof Error ? error.message : String(error));
63
+ }
64
+ }
65
+ return errors;
66
+ }
@@ -1,5 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
2
- import { dirname } from 'node:path';
1
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
3
2
  import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
4
3
  import { CONFIG_SCHEMA, DEFAULT_CONFIG } from '../../config/index.ts';
5
4
  import {
@@ -10,10 +9,16 @@ import {
10
9
  resolveAgentRuntimeProfileHome,
11
10
  setAgentRuntimeProfileSelection,
12
11
  } from '../../agent/runtime-profile.ts';
12
+ import { AgentNoteRegistry } from '../../agent/note-registry.ts';
13
13
  import { AgentPersonaRegistry, assertNoSecretLikeText } from '../../agent/persona-registry.ts';
14
14
  import { AgentRoutineRegistry } from '../../agent/routine-registry.ts';
15
15
  import { AgentSkillRegistry } from '../../agent/skill-registry.ts';
16
- import type { FeatureFlagConfigKey } from '../surface-feature-flags.ts';
16
+ import {
17
+ isFeatureFlagConfigKey,
18
+ mergeFeatureFlagConfigValue,
19
+ readFeatureFlagConfigValue,
20
+ type FeatureFlagConfigKey,
21
+ } from '../surface-feature-flags.ts';
17
22
  import {
18
23
  getOnboardingRuntimeStatePath,
19
24
  readOnboardingRuntimeState,
@@ -29,72 +34,12 @@ import type {
29
34
  OnboardingApplyRequest,
30
35
  OnboardingApplyResult,
31
36
  } from './types.ts';
37
+ import { isPlainObject, readJsonObject, restoreFile, runRollbacks, setNestedValue, snapshotFileRollback, writeJsonObject, type RollbackAction } from './apply-file-helpers.ts';
32
38
 
33
39
  function getNow(deps: Pick<OnboardingApplyDependencies, 'clock'>): number {
34
40
  return deps.clock?.() ?? Date.now();
35
41
  }
36
42
 
37
- function isPlainObject(value: unknown): value is Record<string, unknown> {
38
- return typeof value === 'object' && value !== null && !Array.isArray(value);
39
- }
40
-
41
- function readJsonObject(path: string): Record<string, unknown> {
42
- if (!existsSync(path)) return {};
43
- const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
44
- if (!isPlainObject(parsed)) throw new Error(`Expected an object JSON payload at ${path}.`);
45
- return parsed;
46
- }
47
-
48
- function writeJsonObject(path: string, payload: Record<string, unknown>): void {
49
- mkdirSync(dirname(path), { recursive: true });
50
- writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
51
- }
52
-
53
- function setNestedValue(root: Record<string, unknown>, key: string, value: unknown): Record<string, unknown> {
54
- const parts = key.split('.');
55
- const next = structuredClone(root);
56
- let cursor: Record<string, unknown> = next;
57
-
58
- for (let index = 0; index < parts.length - 1; index += 1) {
59
- const part = parts[index]!;
60
- const existing = cursor[part];
61
- if (!isPlainObject(existing)) cursor[part] = {};
62
- cursor = cursor[part] as Record<string, unknown>;
63
- }
64
-
65
- cursor[parts[parts.length - 1]!] = structuredClone(value);
66
- return next;
67
- }
68
-
69
- type RollbackAction = () => Promise<void> | void;
70
-
71
- function restoreFile(path: string, previous: string | null, reload?: () => void): void {
72
- if (previous === null) {
73
- if (existsSync(path)) unlinkSync(path);
74
- } else {
75
- mkdirSync(dirname(path), { recursive: true });
76
- writeFileSync(path, previous, 'utf-8');
77
- }
78
- reload?.();
79
- }
80
-
81
- function snapshotFileRollback(path: string, reload?: () => void): RollbackAction {
82
- const previous = existsSync(path) ? readFileSync(path, 'utf-8') : null;
83
- return () => restoreFile(path, previous, reload);
84
- }
85
-
86
- async function runRollbacks(rollbacks: readonly RollbackAction[]): Promise<readonly string[]> {
87
- const errors: string[] = [];
88
- for (const rollback of [...rollbacks].reverse()) {
89
- try {
90
- await rollback();
91
- } catch (error) {
92
- errors.push(error instanceof Error ? error.message : String(error));
93
- }
94
- }
95
- return errors;
96
- }
97
-
98
43
  function isGoodVibesSecretReferenceValue(value: string): boolean {
99
44
  const normalized = value.trim();
100
45
  return normalized.startsWith('goodvibes://secrets/') && isSecretRefInput(normalized);
@@ -105,10 +50,6 @@ function isMalformedGoodVibesSecretReferenceValue(value: string): boolean {
105
50
  return normalized.startsWith('goodvibes://') && !isGoodVibesSecretReferenceValue(normalized);
106
51
  }
107
52
 
108
- function isFeatureFlagConfigKey(key: string): key is FeatureFlagConfigKey {
109
- return key === 'featureFlags' || key.startsWith('featureFlags.');
110
- }
111
-
112
53
  function validateFeatureFlagConfigValue(operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>): boolean {
113
54
  if (!isFeatureFlagConfigKey(operation.key)) return false;
114
55
 
@@ -143,7 +84,7 @@ function validateConfigValue(operation: Extract<OnboardingApplyOperation, { kind
143
84
  const defaultValue = operation.key.split('.').reduce<unknown>((cursor, part) => (
144
85
  isPlainObject(cursor) ? cursor[part] : undefined
145
86
  ), DEFAULT_CONFIG);
146
- if (defaultValue === undefined) throw new Error(`Unknown config key: ${operation.key}`);
87
+ if (defaultValue === undefined) throw new Error(`Unknown config key ${operation.key}`);
147
88
  if (typeof defaultValue === 'boolean' && typeof operation.value !== 'boolean') {
148
89
  throw new Error(`Config key ${operation.key} expects a boolean value.`);
149
90
  }
@@ -170,11 +111,11 @@ function validateConfigValue(operation: Extract<OnboardingApplyOperation, { kind
170
111
  }
171
112
 
172
113
  if (schema.type === 'enum' && schema.enumValues && stringValue !== null && !schema.enumValues.includes(stringValue)) {
173
- throw new Error(`Invalid value for ${operation.key}: ${String(operation.value)}.`);
114
+ throw new Error(`Invalid value for ${operation.key} ${String(operation.value)}.`);
174
115
  }
175
116
 
176
117
  if (schema.validate && !schema.validate(operation.value)) {
177
- throw new Error(`Invalid value for ${operation.key}: ${String(operation.value)}.`);
118
+ throw new Error(`Invalid value for ${operation.key} ${String(operation.value)}.`);
178
119
  }
179
120
  }
180
121
 
@@ -287,6 +228,21 @@ function validateCreateLocalRoutineOperation(
287
228
  if (duplicate) throw new Error(`Routine already exists: ${duplicate.id}`);
288
229
  }
289
230
 
231
+ function validateCreateLocalNoteOperation(
232
+ deps: OnboardingApplyDependencies,
233
+ operation: Extract<OnboardingApplyOperation, { kind: 'create-local-note' }>,
234
+ ): void {
235
+ const registry = AgentNoteRegistry.fromShellPaths(deps.shellPaths);
236
+ const title = operation.title.trim();
237
+ const body = operation.body.trim();
238
+ const tags = operation.tags ?? [];
239
+ if (!title) throw new Error('Note title is required.');
240
+ if (!body) throw new Error('Note body is required.');
241
+ assertNoSecretLikeText([title, body, ...tags]);
242
+ const duplicate = registry.get(title);
243
+ if (duplicate) throw new Error(`Note already exists: ${duplicate.id}`);
244
+ }
245
+
290
246
  function applyConfigOperation(
291
247
  deps: OnboardingApplyDependencies,
292
248
  operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>,
@@ -306,7 +262,11 @@ function applyConfigOperation(
306
262
  };
307
263
  }
308
264
 
309
- deps.config.setDynamic(operation.key as never, operation.value);
265
+ if (isFeatureFlagConfigKey(operation.key)) {
266
+ mergeFeatureFlagConfigValue(deps.config, operation.key, operation.value);
267
+ } else {
268
+ deps.config.setDynamic(operation.key, operation.value);
269
+ }
310
270
  return {
311
271
  kind: operation.kind,
312
272
  summary: `Updated ${operation.key} in global onboarding settings.`,
@@ -359,9 +319,15 @@ async function buildRollbackAction(
359
319
  );
360
320
  }
361
321
 
362
- const previous = deps.config.get(operation.key as never);
322
+ const previous = isFeatureFlagConfigKey(operation.key)
323
+ ? readFeatureFlagConfigValue(deps.config, operation.key)
324
+ : deps.config.get(operation.key);
363
325
  return () => {
364
- deps.config.setDynamic(operation.key as never, previous);
326
+ if (isFeatureFlagConfigKey(operation.key)) {
327
+ mergeFeatureFlagConfigValue(deps.config, operation.key, previous ?? 'disabled');
328
+ } else {
329
+ deps.config.setDynamic(operation.key, previous);
330
+ }
365
331
  };
366
332
  }
367
333
 
@@ -392,6 +358,11 @@ async function buildRollbackAction(
392
358
  return snapshotFileRollback(path);
393
359
  }
394
360
 
361
+ if (operation.kind === 'create-local-note') {
362
+ const registry = AgentNoteRegistry.fromShellPaths(deps.shellPaths);
363
+ return snapshotFileRollback(registry.snapshot().path);
364
+ }
365
+
395
366
  if (operation.kind === 'create-local-persona') {
396
367
  const registry = AgentPersonaRegistry.fromShellPaths(deps.shellPaths);
397
368
  return snapshotFileRollback(registry.snapshot().path);
@@ -457,7 +428,7 @@ function applySelectAgentProfileOperation(
457
428
  const selection = setAgentRuntimeProfileSelection(deps.shellPaths.homeDirectory, operation.name);
458
429
  return {
459
430
  kind: operation.kind,
460
- summary: `Selected Agent profile ${selection.id} for future launches.`,
431
+ summary: `Selected Agent profile ${selection.id} for later plain goodvibes-agent runs.`,
461
432
  };
462
433
  }
463
434
 
@@ -476,7 +447,7 @@ function applyCreateLocalPersonaOperation(
476
447
  if (operation.activate !== false) registry.setActive(persona.id);
477
448
  return {
478
449
  kind: operation.kind,
479
- summary: `Created local Agent persona ${persona.id}.`,
450
+ summary: `Created Agent-local persona ${persona.id}.`,
480
451
  };
481
452
  }
482
453
 
@@ -494,7 +465,7 @@ function applyCreateLocalSkillOperation(
494
465
  });
495
466
  return {
496
467
  kind: operation.kind,
497
- summary: `Created local Agent skill ${skill.id}.`,
468
+ summary: `Created Agent-local skill ${skill.id}.`,
498
469
  };
499
470
  }
500
471
 
@@ -512,7 +483,24 @@ function applyCreateLocalRoutineOperation(
512
483
  });
513
484
  return {
514
485
  kind: operation.kind,
515
- summary: `Created local Agent routine ${routine.id}.`,
486
+ summary: `Created Agent-local routine ${routine.id}.`,
487
+ };
488
+ }
489
+
490
+ function applyCreateLocalNoteOperation(
491
+ deps: OnboardingApplyDependencies,
492
+ operation: Extract<OnboardingApplyOperation, { kind: 'create-local-note' }>,
493
+ ): OnboardingAppliedOperation {
494
+ const note = AgentNoteRegistry.fromShellPaths(deps.shellPaths).create({
495
+ title: operation.title,
496
+ body: operation.body,
497
+ tags: operation.tags ?? [],
498
+ source: 'user',
499
+ provenance: 'onboarding',
500
+ });
501
+ return {
502
+ kind: operation.kind,
503
+ summary: `Created Agent-local note ${note.id}.`,
516
504
  };
517
505
  }
518
506
 
@@ -530,7 +518,8 @@ function orderApplyOperations(
530
518
  const agentProfileOperations = operations.filter((operation) => operation.kind === 'create-agent-profile');
531
519
  const agentProfileSelectionOperations = operations.filter((operation) => operation.kind === 'select-agent-profile');
532
520
  const localBehaviorOperations = operations.filter((operation) => (
533
- operation.kind === 'create-local-persona'
521
+ operation.kind === 'create-local-note'
522
+ || operation.kind === 'create-local-persona'
534
523
  || operation.kind === 'create-local-skill'
535
524
  || operation.kind === 'create-local-routine'
536
525
  ));
@@ -602,6 +591,11 @@ function prevalidateApplyRequest(
602
591
  continue;
603
592
  }
604
593
 
594
+ if (operation.kind === 'create-local-note') {
595
+ validateCreateLocalNoteOperation(deps, operation);
596
+ continue;
597
+ }
598
+
605
599
  if (operation.kind === 'create-local-skill') {
606
600
  validateCreateLocalSkillOperation(deps, operation);
607
601
  continue;
@@ -632,6 +626,7 @@ function getVerificationFailureKind(itemId: string): OnboardingApplyOperation['k
632
626
  if (itemId.startsWith('acknowledge:')) return 'acknowledge';
633
627
  if (itemId.startsWith('agent-profile:')) return 'create-agent-profile';
634
628
  if (itemId.startsWith('selected-agent-profile:')) return 'select-agent-profile';
629
+ if (itemId.startsWith('local-note:')) return 'create-local-note';
635
630
  if (itemId.startsWith('local-persona:')) return 'create-local-persona';
636
631
  if (itemId.startsWith('local-skill:')) return 'create-local-skill';
637
632
  if (itemId.startsWith('local-routine:')) return 'create-local-routine';
@@ -703,6 +698,12 @@ export async function applyOnboardingRequest(
703
698
  continue;
704
699
  }
705
700
 
701
+ if (operation.kind === 'create-local-note') {
702
+ applied.push(applyCreateLocalNoteOperation(deps, operation));
703
+ rollbacks.push(rollback);
704
+ continue;
705
+ }
706
+
706
707
  if (operation.kind === 'create-local-skill') {
707
708
  applied.push(applyCreateLocalSkillOperation(deps, operation));
708
709
  rollbacks.push(rollback);
@@ -83,7 +83,7 @@ function isDeepEqual(left: unknown, right: unknown): boolean {
83
83
  const rightEntries = Object.entries(right);
84
84
  if (leftEntries.length !== rightEntries.length) return false;
85
85
 
86
- return leftEntries.every(([key, value]) => isDeepEqual(value, (right as Record<string, unknown>)[key]));
86
+ return leftEntries.every(([key, value]) => isDeepEqual(value, Reflect.get(right, key)));
87
87
  }
88
88
 
89
89
  return false;
@@ -250,7 +250,7 @@ function describeLocalBehavior(snapshot: OnboardingSnapshotState): string {
250
250
  ...discovery.routines.names,
251
251
  ].slice(0, 4);
252
252
  const sampleText = samples.length > 0 ? ` Found: ${samples.join(', ')}.` : '';
253
- return `Import ${discoveredCount} discovered Agent persona/skill/routine file(s) from local Agent folders before creating blank behavior.${sampleText}`;
253
+ return `Import ${discoveredCount} discovered Agent persona/skill/routine file(s) from Agent-local folders before creating blank behavior.${sampleText}`;
254
254
  }
255
255
 
256
256
  if (!hasLocalBehaviorCustomization(snapshot)) {
@@ -288,7 +288,7 @@ function describeAutomationReview(snapshot: OnboardingSnapshotState): string {
288
288
  }
289
289
 
290
290
  function describeTuiDelegation(): string {
291
- return 'Delegate explicit build, fix, implementation, and review work to GoodVibes TUI; WRFC is requested only when the user explicitly asks for it.';
291
+ return 'Delegate explicit build, fix, implementation, and review work to GoodVibes TUI; delegated review is requested only when the user explicitly asks for it.';
292
292
  }
293
293
 
294
294
  function getAcknowledgementAccepted(
@@ -391,10 +391,7 @@ export function deriveStep1CapabilityFlags(
391
391
  };
392
392
  }
393
393
 
394
- export function deriveStep1_5NetworkMode(
395
- bindSettings: Pick<OnboardingSnapshotState, 'bindSettings'>['bindSettings'],
396
- ): OnboardingNetworkMode {
397
- void bindSettings;
394
+ export function deriveStep1_5NetworkMode(): OnboardingNetworkMode {
398
395
  return 'local-network-default';
399
396
  }
400
397
 
@@ -488,7 +485,7 @@ export function deriveOnboardingStepState(
488
485
  ): OnboardingStepDerivationState {
489
486
  return {
490
487
  step1Capabilities: deriveStep1Capabilities(snapshot),
491
- step1_5NetworkMode: deriveStep1_5NetworkMode(snapshot.bindSettings),
488
+ step1_5NetworkMode: deriveStep1_5NetworkMode(),
492
489
  reopenEditAcknowledgements: deriveReopenEditAcknowledgementState(snapshot),
493
490
  };
494
491
  }
@@ -35,15 +35,7 @@ function buildConfigSnapshot(
35
35
  llmProvider: config.get('tools.llmProvider'),
36
36
  llmModel: config.get('tools.llmModel'),
37
37
  },
38
- danger: config.getCategory('danger'),
39
- controlPlane: config.getCategory('controlPlane'),
40
- httpListener: config.getCategory('httpListener'),
41
- web: config.getCategory('web'),
42
- network: config.getCategory('network'),
43
38
  surfaces: config.getCategory('surfaces'),
44
- service: config.getCategory('service'),
45
- featureFlags: config.getCategory('featureFlags'),
46
- batch: config.getCategory('batch'),
47
39
  };
48
40
  }
49
41
 
@@ -389,13 +381,6 @@ export async function collectOnboardingSnapshot(
389
381
  auth: {
390
382
  snapshot: authSnapshotResult.value,
391
383
  },
392
- bindSettings: {
393
- daemonEnabled: Boolean(config.danger.daemon),
394
- httpListenerEnabled: Boolean(config.danger.httpListener),
395
- controlPlane: config.controlPlane,
396
- httpListener: config.httpListener,
397
- web: config.web,
398
- },
399
384
  surfaces: {
400
385
  configuredEnabledKinds: buildConfiguredSurfaceKinds(config.surfaces),
401
386
  records: sortSurfaceRecords(surfaceResult.value),
@@ -40,15 +40,7 @@ export interface OnboardingConfigSnapshot {
40
40
  readonly permissions: GoodVibesConfig['permissions'];
41
41
  readonly helper: GoodVibesConfig['helper'];
42
42
  readonly tools: Pick<GoodVibesConfig['tools'], 'llmEnabled' | 'llmProvider' | 'llmModel'>;
43
- readonly danger: GoodVibesConfig['danger'];
44
- readonly controlPlane: GoodVibesConfig['controlPlane'];
45
- readonly httpListener: GoodVibesConfig['httpListener'];
46
- readonly web: GoodVibesConfig['web'];
47
- readonly network: GoodVibesConfig['network'];
48
43
  readonly surfaces: GoodVibesConfig['surfaces'];
49
- readonly service: GoodVibesConfig['service'];
50
- readonly featureFlags: GoodVibesConfig['featureFlags'];
51
- readonly batch: GoodVibesConfig['batch'];
52
44
  }
53
45
 
54
46
  export interface OnboardingProviderRoutingSnapshot {
@@ -102,14 +94,6 @@ export interface OnboardingAuthSnapshot {
102
94
  readonly snapshot: LocalAuthSnapshot;
103
95
  }
104
96
 
105
- export interface OnboardingBindSettingsSnapshot {
106
- readonly daemonEnabled: boolean;
107
- readonly httpListenerEnabled: boolean;
108
- readonly controlPlane: GoodVibesConfig['controlPlane'];
109
- readonly httpListener: GoodVibesConfig['httpListener'];
110
- readonly web: GoodVibesConfig['web'];
111
- }
112
-
113
97
  export interface OnboardingSurfaceRecord {
114
98
  readonly id: string;
115
99
  readonly kind: string;
@@ -187,7 +171,6 @@ export interface OnboardingSnapshotState {
187
171
  readonly subscriptions: OnboardingSubscriptionsSnapshot;
188
172
  readonly secrets: OnboardingSecretsSnapshot;
189
173
  readonly auth: OnboardingAuthSnapshot;
190
- readonly bindSettings: OnboardingBindSettingsSnapshot;
191
174
  readonly surfaces: OnboardingSurfacesSnapshot;
192
175
  readonly providerAccounts: OnboardingProviderAccountsSnapshot | null;
193
176
  readonly localBehaviorDiscovery: AgentBehaviorDiscoverySnapshot;
@@ -276,6 +259,12 @@ export type OnboardingApplyOperation =
276
259
  readonly kind: 'select-agent-profile';
277
260
  readonly name: string;
278
261
  }
262
+ | {
263
+ readonly kind: 'create-local-note';
264
+ readonly title: string;
265
+ readonly body: string;
266
+ readonly tags?: readonly string[];
267
+ }
279
268
  | {
280
269
  readonly kind: 'create-local-persona';
281
270
  readonly name: string;
@@ -408,7 +397,7 @@ export interface OnboardingSnapshotDependencies {
408
397
 
409
398
  export interface OnboardingApplyDependencies {
410
399
  readonly clock?: () => number;
411
- readonly config: Pick<ConfigManager, 'get' | 'getRaw' | 'load' | 'setDynamic'>;
400
+ readonly config: Pick<ConfigManager, 'get' | 'getCategory' | 'getRaw' | 'load' | 'mergeCategory' | 'setDynamic'>;
412
401
  readonly secrets?: Pick<SecretsManager, 'delete' | 'get' | 'inspect' | 'set'>;
413
402
  readonly auth?: Pick<UserAuthManager, 'inspect'>;
414
403
  readonly shellPaths: OnboardingShellPaths;
@@ -417,7 +406,7 @@ export interface OnboardingApplyDependencies {
417
406
 
418
407
  export interface OnboardingVerificationDependencies {
419
408
  readonly clock?: () => number;
420
- readonly config: Pick<ConfigManager, 'get'>;
409
+ readonly config: Pick<ConfigManager, 'get' | 'getCategory'>;
421
410
  readonly secrets?: Pick<SecretsManager, 'get'>;
422
411
  readonly auth?: Pick<UserAuthManager, 'inspect'>;
423
412
  readonly shellPaths: OnboardingShellPaths;
@@ -1,10 +1,12 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { isSecretRefInput } from '@pellux/goodvibes-sdk/platform/config';
4
+ import { AgentNoteRegistry } from '../../agent/note-registry.ts';
4
5
  import { AgentPersonaRegistry } from '../../agent/persona-registry.ts';
5
6
  import { AgentRoutineRegistry } from '../../agent/routine-registry.ts';
6
7
  import { readAgentRuntimeProfileSelection, resolveAgentRuntimeProfileHome } from '../../agent/runtime-profile.ts';
7
8
  import { AgentSkillRegistry } from '../../agent/skill-registry.ts';
9
+ import { isFeatureFlagConfigKey, readFeatureFlagConfigValue } from '../surface-feature-flags.ts';
8
10
  import { readOnboardingRuntimeState } from './state.ts';
9
11
  import type {
10
12
  OnboardingApplyOperation,
@@ -34,7 +36,7 @@ function isDeepEqual(left: unknown, right: unknown): boolean {
34
36
  const rightEntries = Object.entries(right);
35
37
  if (leftEntries.length !== rightEntries.length) return false;
36
38
 
37
- return leftEntries.every(([key, value]) => isDeepEqual(value, (right as Record<string, unknown>)[key]));
39
+ return leftEntries.every(([key, value]) => isDeepEqual(value, Reflect.get(right, key)));
38
40
  }
39
41
 
40
42
  return false;
@@ -49,7 +51,9 @@ function verifyConfigOperation(
49
51
  deps: OnboardingVerificationDependencies,
50
52
  operation: Extract<OnboardingApplyOperation, { kind: 'set-config' }>,
51
53
  ): OnboardingVerificationItem {
52
- const actual = deps.config.get(operation.key as never);
54
+ const actual = isFeatureFlagConfigKey(operation.key)
55
+ ? readFeatureFlagConfigValue(deps.config, operation.key)
56
+ : deps.config.get(operation.key);
53
57
  const ok = isDeepEqual(actual, operation.value);
54
58
 
55
59
  return {
@@ -158,8 +162,8 @@ function verifySelectAgentProfileOperation(
158
162
  id: `selected-agent-profile:${resolution.id}`,
159
163
  status: ok ? 'pass' : 'fail',
160
164
  message: ok
161
- ? `${resolution.id} Agent profile is selected for future launches.`
162
- : `${resolution.id} Agent profile is not selected for future launches.`,
165
+ ? `${resolution.id} Agent profile is selected for later plain goodvibes-agent runs.`
166
+ : `${resolution.id} Agent profile is not selected for later plain goodvibes-agent runs.`,
163
167
  target: resolution.id,
164
168
  };
165
169
  }
@@ -177,8 +181,8 @@ function verifyCreateLocalPersonaOperation(
177
181
  id: `local-persona:${target}`,
178
182
  status: ok ? 'pass' : 'fail',
179
183
  message: ok
180
- ? `${target} local Agent persona exists${operation.activate === false ? '' : ' and is active'}.`
181
- : `${target} local Agent persona was not created${operation.activate === false ? '' : ' or activated'}.`,
184
+ ? `${target} Agent-local persona exists${operation.activate === false ? '' : ' and is active'}.`
185
+ : `${target} Agent-local persona was not created${operation.activate === false ? '' : ' or activated'}.`,
182
186
  target,
183
187
  };
184
188
  }
@@ -195,8 +199,8 @@ function verifyCreateLocalSkillOperation(
195
199
  id: `local-skill:${target}`,
196
200
  status: ok ? 'pass' : 'fail',
197
201
  message: ok
198
- ? `${target} local Agent skill exists${operation.enabled === false ? '' : ' and is enabled'}.`
199
- : `${target} local Agent skill was not created${operation.enabled === false ? '' : ' or enabled'}.`,
202
+ ? `${target} Agent-local skill exists${operation.enabled === false ? '' : ' and is enabled'}.`
203
+ : `${target} Agent-local skill was not created${operation.enabled === false ? '' : ' or enabled'}.`,
200
204
  target,
201
205
  };
202
206
  }
@@ -213,8 +217,25 @@ function verifyCreateLocalRoutineOperation(
213
217
  id: `local-routine:${target}`,
214
218
  status: ok ? 'pass' : 'fail',
215
219
  message: ok
216
- ? `${target} local Agent routine exists${operation.enabled === false ? '' : ' and is enabled'}.`
217
- : `${target} local Agent routine was not created${operation.enabled === false ? '' : ' or enabled'}.`,
220
+ ? `${target} Agent-local routine exists${operation.enabled === false ? '' : ' and is enabled'}.`
221
+ : `${target} Agent-local routine was not created${operation.enabled === false ? '' : ' or enabled'}.`,
222
+ target,
223
+ };
224
+ }
225
+
226
+ function verifyCreateLocalNoteOperation(
227
+ deps: OnboardingVerificationDependencies,
228
+ operation: Extract<OnboardingApplyOperation, { kind: 'create-local-note' }>,
229
+ ): OnboardingVerificationItem {
230
+ const note = AgentNoteRegistry.fromShellPaths(deps.shellPaths).get(operation.title);
231
+ const ok = note !== null && note.body.trim() === operation.body.trim();
232
+ const target = note?.id ?? operation.title.trim();
233
+ return {
234
+ id: `local-note:${target}`,
235
+ status: ok ? 'pass' : 'fail',
236
+ message: ok
237
+ ? `${target} Agent-local note exists.`
238
+ : `${target} Agent-local note was not created.`,
218
239
  target,
219
240
  };
220
241
  }
@@ -229,6 +250,7 @@ async function verifyOperation(
229
250
  if (operation.kind === 'acknowledge') return verifyAcknowledgementOperation(deps, operation);
230
251
  if (operation.kind === 'create-agent-profile') return verifyCreateAgentProfileOperation(deps, operation);
231
252
  if (operation.kind === 'select-agent-profile') return verifySelectAgentProfileOperation(deps, operation);
253
+ if (operation.kind === 'create-local-note') return verifyCreateLocalNoteOperation(deps, operation);
232
254
  if (operation.kind === 'create-local-persona') return verifyCreateLocalPersonaOperation(deps, operation);
233
255
  if (operation.kind === 'create-local-skill') return verifyCreateLocalSkillOperation(deps, operation);
234
256
  return verifyCreateLocalRoutineOperation(deps, operation);
@@ -16,7 +16,7 @@ import { join } from 'node:path';
16
16
  * Return the list of absolute operator-tokens.json paths the TUI may have written
17
17
  * at legacy (pre-0.21.28) workspace-scoped locations under `workingDirectory`.
18
18
  *
19
- * The canonical, current-SDK location is `<daemonHomeDir>/operator-tokens.json`;
19
+ * The canonical, current-SDK location is `<connectedHostTokenDir>/operator-tokens.json`;
20
20
  * this helper is strictly for legacy-cleanup candidates.
21
21
  */
22
22
  export function workspaceOperatorTokenCandidates(workingDirectory: string): readonly string[] {