@pellux/goodvibes-agent 1.1.7 → 1.2.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 (255) hide show
  1. package/CHANGELOG.md +170 -0
  2. package/README.md +46 -30
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
  5. package/dist/package/main.js +132577 -91483
  6. package/docs/README.md +14 -7
  7. package/docs/channels-remote-and-api.md +8 -5
  8. package/docs/connected-host.md +14 -12
  9. package/docs/getting-started.md +56 -30
  10. package/docs/knowledge-artifacts-and-multimodal.md +7 -5
  11. package/docs/project-planning.md +2 -2
  12. package/docs/providers-and-routing.md +11 -3
  13. package/docs/tools-and-commands.md +114 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +130 -5
  17. package/release/release-readiness.json +120 -90
  18. package/src/agent/assistant-cockpit.ts +247 -0
  19. package/src/agent/autonomy-schedule-format.ts +96 -0
  20. package/src/agent/autonomy-schedule.ts +514 -0
  21. package/src/agent/channel-delivery-receipts.ts +292 -0
  22. package/src/agent/competitive-feature-inventory.ts +296 -0
  23. package/src/agent/document-registry-types.ts +106 -0
  24. package/src/agent/document-registry.ts +734 -0
  25. package/src/agent/harness-control.ts +2 -2
  26. package/src/agent/memory-prompt.ts +2 -2
  27. package/src/agent/operator-actions.ts +50 -6
  28. package/src/agent/persona-registry.ts +8 -0
  29. package/src/agent/project-context-files.ts +273 -0
  30. package/src/agent/prompt-context-receipts.ts +502 -0
  31. package/src/agent/reminder-schedule-format.ts +16 -2
  32. package/src/agent/research-run-registry.ts +582 -0
  33. package/src/agent/research-source-registry.ts +441 -0
  34. package/src/agent/routine-registry.ts +19 -3
  35. package/src/agent/routine-schedule-format.ts +27 -2
  36. package/src/agent/runtime-profile-starters.ts +7 -0
  37. package/src/agent/runtime-profile.ts +106 -4
  38. package/src/agent/schedule-edit-format.ts +129 -0
  39. package/src/agent/schedule-edit.ts +496 -0
  40. package/src/agent/schedule-next-routes.ts +42 -0
  41. package/src/agent/setup-wizard-checkpoint.ts +140 -0
  42. package/src/agent/setup-wizard.ts +447 -0
  43. package/src/agent/skill-registry-types.ts +102 -0
  44. package/src/agent/skill-registry.ts +81 -107
  45. package/src/agent/vibe-confirmation-routes.ts +62 -0
  46. package/src/agent/vibe-file.ts +285 -0
  47. package/src/cli/agent-knowledge-command.ts +45 -2
  48. package/src/cli/help.ts +3 -0
  49. package/src/cli/profiles-command.ts +25 -11
  50. package/src/config/agent-settings-policy.ts +1 -18
  51. package/src/input/agent-workspace-activation.ts +53 -0
  52. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  53. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  54. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  55. package/src/input/agent-workspace-categories.ts +153 -155
  56. package/src/input/agent-workspace-channel-triage.ts +481 -0
  57. package/src/input/agent-workspace-channels.ts +263 -0
  58. package/src/input/agent-workspace-command-editor.ts +152 -0
  59. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  60. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  61. package/src/input/agent-workspace-document-editor.ts +502 -0
  62. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  63. package/src/input/agent-workspace-host-category.ts +1 -1
  64. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  65. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  66. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  67. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  68. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  69. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  70. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  71. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  72. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  73. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  74. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  75. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  76. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  77. package/src/input/agent-workspace-settings.ts +262 -30
  78. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  79. package/src/input/agent-workspace-setup-snapshot.ts +198 -0
  80. package/src/input/agent-workspace-setup.ts +34 -1
  81. package/src/input/agent-workspace-snapshot.ts +162 -147
  82. package/src/input/agent-workspace-types.ts +287 -2
  83. package/src/input/agent-workspace.ts +18 -0
  84. package/src/input/command-registry.ts +9 -2
  85. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  86. package/src/input/commands/channels-runtime.ts +102 -114
  87. package/src/input/commands/delegation-runtime.ts +87 -24
  88. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  89. package/src/input/commands/knowledge.ts +28 -14
  90. package/src/input/commands/operator-actions-runtime.ts +6 -3
  91. package/src/input/commands/schedule-runtime.ts +71 -14
  92. package/src/input/commands/session-content.ts +2 -2
  93. package/src/input/commands/shell-core.ts +2 -0
  94. package/src/input/commands/vibe-runtime.ts +140 -0
  95. package/src/input/commands.ts +2 -0
  96. package/src/input/connected-host-routes.ts +123 -0
  97. package/src/input/settings-modal-types.ts +14 -0
  98. package/src/main.ts +15 -15
  99. package/src/panels/qr-panel.ts +2 -2
  100. package/src/renderer/agent-workspace-context-lines.ts +686 -0
  101. package/src/renderer/agent-workspace.ts +30 -419
  102. package/src/renderer/settings-modal-helpers.ts +12 -0
  103. package/src/renderer/settings-modal.ts +12 -0
  104. package/src/runtime/bootstrap-command-context.ts +11 -1
  105. package/src/runtime/bootstrap-command-parts.ts +14 -3
  106. package/src/runtime/bootstrap-core.ts +39 -1
  107. package/src/runtime/bootstrap-shell.ts +6 -0
  108. package/src/runtime/bootstrap.ts +113 -30
  109. package/src/runtime/connected-host-auth.ts +3 -2
  110. package/src/runtime/execution-ledger.ts +231 -0
  111. package/src/runtime/services.ts +88 -10
  112. package/src/runtime/tool-permission-safety.ts +199 -5
  113. package/src/shell/session-continuity-hints.ts +39 -0
  114. package/src/tools/agent-artifacts-tool.ts +606 -0
  115. package/src/tools/agent-audit-tool.ts +155 -0
  116. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  117. package/src/tools/agent-autonomy-tool.ts +140 -0
  118. package/src/tools/agent-channel-send-tool.ts +24 -2
  119. package/src/tools/agent-channels-tool.ts +140 -0
  120. package/src/tools/agent-computer-tool.ts +168 -0
  121. package/src/tools/agent-context-policy.ts +137 -7
  122. package/src/tools/agent-context-tool.ts +143 -0
  123. package/src/tools/agent-delegation-tool.ts +128 -0
  124. package/src/tools/agent-device-tool.ts +240 -0
  125. package/src/tools/agent-documents-tool.ts +684 -0
  126. package/src/tools/agent-execution-tool.ts +230 -0
  127. package/src/tools/agent-harness-agent-orchestration.ts +703 -0
  128. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  129. package/src/tools/agent-harness-autonomy-live-records.ts +588 -0
  130. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  131. package/src/tools/agent-harness-autonomy-queue.ts +560 -0
  132. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  133. package/src/tools/agent-harness-background-processes.ts +794 -0
  134. package/src/tools/agent-harness-browser-cockpit-route.ts +219 -0
  135. package/src/tools/agent-harness-browser-control.ts +338 -0
  136. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  137. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  138. package/src/tools/agent-harness-command-catalog.ts +4 -2
  139. package/src/tools/agent-harness-command-runner.ts +42 -0
  140. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  141. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  142. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  143. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  144. package/src/tools/agent-harness-document-ops.ts +671 -0
  145. package/src/tools/agent-harness-execution-history.ts +489 -0
  146. package/src/tools/agent-harness-execution-posture.ts +382 -0
  147. package/src/tools/agent-harness-file-recovery.ts +135 -0
  148. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  149. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  150. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  151. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  152. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  153. package/src/tools/agent-harness-learning-curator.ts +417 -0
  154. package/src/tools/agent-harness-local-model-benchmarks.ts +199 -0
  155. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  156. package/src/tools/agent-harness-local-model-endpoints.ts +673 -0
  157. package/src/tools/agent-harness-media-posture.ts +208 -2
  158. package/src/tools/agent-harness-memory-posture.ts +556 -0
  159. package/src/tools/agent-harness-metadata.ts +100 -168
  160. package/src/tools/agent-harness-mode-catalog.ts +84 -33
  161. package/src/tools/agent-harness-model-catalog.ts +162 -0
  162. package/src/tools/agent-harness-model-provider-health.ts +186 -0
  163. package/src/tools/agent-harness-model-readiness.ts +406 -0
  164. package/src/tools/agent-harness-model-routing-types.ts +266 -0
  165. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  166. package/src/tools/agent-harness-model-routing.ts +137 -190
  167. package/src/tools/agent-harness-operator-methods.ts +115 -133
  168. package/src/tools/agent-harness-pairing-posture.ts +431 -18
  169. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  170. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  171. package/src/tools/agent-harness-personal-ops-lanes.ts +204 -0
  172. package/src/tools/agent-harness-personal-ops-records.ts +732 -0
  173. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  174. package/src/tools/agent-harness-personal-ops-types.ts +222 -0
  175. package/src/tools/agent-harness-personal-ops.ts +606 -0
  176. package/src/tools/agent-harness-project-context.ts +144 -0
  177. package/src/tools/agent-harness-prompt-context.ts +589 -0
  178. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  179. package/src/tools/agent-harness-release-evidence.ts +9 -7
  180. package/src/tools/agent-harness-release-readiness.ts +9 -7
  181. package/src/tools/agent-harness-research-briefing.ts +427 -0
  182. package/src/tools/agent-harness-research-queue.ts +250 -0
  183. package/src/tools/agent-harness-research-runs.ts +295 -0
  184. package/src/tools/agent-harness-research-workflow.ts +322 -0
  185. package/src/tools/agent-harness-security-posture.ts +2 -2
  186. package/src/tools/agent-harness-service-posture.ts +9 -9
  187. package/src/tools/agent-harness-session-metadata.ts +3 -3
  188. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  189. package/src/tools/agent-harness-setup-describe.ts +557 -0
  190. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  191. package/src/tools/agent-harness-setup-model-helpers.ts +101 -0
  192. package/src/tools/agent-harness-setup-plan.ts +253 -0
  193. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  194. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  195. package/src/tools/agent-harness-setup-posture.ts +449 -221
  196. package/src/tools/agent-harness-setup-smoke.ts +508 -0
  197. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  198. package/src/tools/agent-harness-tool-schema.ts +120 -7
  199. package/src/tools/agent-harness-tool-types.ts +78 -0
  200. package/src/tools/agent-harness-tool-utils.ts +43 -0
  201. package/src/tools/agent-harness-tool.ts +311 -370
  202. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  203. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  204. package/src/tools/agent-harness-vibe-health.ts +105 -0
  205. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  206. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  207. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  208. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  209. package/src/tools/agent-host-tool.ts +159 -0
  210. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  211. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  212. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  213. package/src/tools/agent-memory-tool.ts +214 -0
  214. package/src/tools/agent-model-compare-export.ts +315 -0
  215. package/src/tools/agent-model-compare-handoff.ts +592 -0
  216. package/src/tools/agent-model-compare-judgment.ts +633 -0
  217. package/src/tools/agent-model-compare-run.ts +722 -0
  218. package/src/tools/agent-model-compare-tool.ts +698 -0
  219. package/src/tools/agent-model-compare-types.ts +191 -0
  220. package/src/tools/agent-model-compare-utils.ts +93 -0
  221. package/src/tools/agent-models-tool.ts +208 -0
  222. package/src/tools/agent-operator-action-tool.ts +1 -1
  223. package/src/tools/agent-operator-method-tool.ts +643 -0
  224. package/src/tools/agent-personal-ops-tool.ts +197 -0
  225. package/src/tools/agent-policy-explanation.ts +415 -0
  226. package/src/tools/agent-research-report-tool.ts +608 -0
  227. package/src/tools/agent-research-runs-tool.ts +434 -0
  228. package/src/tools/agent-research-sources-tool.ts +443 -0
  229. package/src/tools/agent-research-tool.ts +687 -0
  230. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  231. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  232. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  233. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  234. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  235. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  236. package/src/tools/agent-route-planner-helpers.ts +667 -0
  237. package/src/tools/agent-route-planner.ts +185 -0
  238. package/src/tools/agent-route-tool.ts +105 -0
  239. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  240. package/src/tools/agent-schedule-tool.ts +282 -0
  241. package/src/tools/agent-security-tool.ts +145 -0
  242. package/src/tools/agent-sessions-tool.ts +122 -0
  243. package/src/tools/agent-settings-import-tool.ts +104 -0
  244. package/src/tools/agent-settings-tool.ts +176 -0
  245. package/src/tools/agent-setup-tool.ts +226 -0
  246. package/src/tools/agent-support-tool.ts +122 -0
  247. package/src/tools/agent-terminal-process-tools.ts +167 -0
  248. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  249. package/src/tools/agent-tool-policy-guard.ts +106 -85
  250. package/src/tools/agent-vibe-tool.ts +297 -0
  251. package/src/tools/agent-work-plan-tool.ts +265 -6
  252. package/src/tools/agent-workspace-tool.ts +305 -0
  253. package/src/tools/artifact-archive.ts +169 -0
  254. package/src/tools/tool-definition-compaction.ts +36 -0
  255. package/src/version.ts +1 -1
@@ -0,0 +1,496 @@
1
+ import { createBrowserGoodVibesSdk } from '@pellux/goodvibes-sdk/browser';
2
+ import type { OperatorMethodInput, OperatorMethodOutput } from '@pellux/goodvibes-sdk/contracts';
3
+ import {
4
+ formatEveryInterval,
5
+ normalizeAtSchedule,
6
+ normalizeCronSchedule,
7
+ normalizeEverySchedule,
8
+ type AutomationScheduleDefinition,
9
+ } from '@pellux/goodvibes-sdk/platform/automation';
10
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
11
+ import {
12
+ buildAutonomySchedulePrompt,
13
+ } from './autonomy-schedule.ts';
14
+ import {
15
+ resolveAgentConnectedHostConnection,
16
+ ROUTINE_SCHEDULE_LIST_METHOD,
17
+ ROUTINE_SCHEDULE_ROUTE,
18
+ type AgentConnectedHostConfigReader,
19
+ type AgentConnectedHostConnection,
20
+ type RoutineScheduleKind,
21
+ type RoutineScheduleSpec,
22
+ } from './routine-schedule-promotion.ts';
23
+
24
+ type SchedulePatchInput = OperatorMethodInput<'automation.jobs.patch'>;
25
+ type SchedulePatchOutput = OperatorMethodOutput<'automation.jobs.patch'>;
26
+ type ScheduleListOutput = OperatorMethodOutput<'schedules.list'>;
27
+
28
+ export const SCHEDULE_EDIT_METHOD = 'automation.jobs.patch';
29
+ export const SCHEDULE_EDIT_ROUTE = '/api/automation/jobs/{jobId}';
30
+
31
+ export interface ParsedScheduleEditArgs {
32
+ readonly scheduleId: string | null;
33
+ readonly schedule: RoutineScheduleSpec | null;
34
+ readonly name?: string;
35
+ readonly prompt?: string;
36
+ readonly task?: string;
37
+ readonly successCriteria?: string;
38
+ readonly timezone?: string;
39
+ readonly staggerMs?: number;
40
+ readonly yes: boolean;
41
+ readonly explicitUserRequest: string | null;
42
+ readonly errors: readonly string[];
43
+ }
44
+
45
+ export interface ParseScheduleEditOptions {
46
+ readonly defaultExplicitUserRequest?: string;
47
+ }
48
+
49
+ export interface ScheduleEditPreview {
50
+ readonly scheduleId: string;
51
+ readonly route: typeof SCHEDULE_EDIT_ROUTE;
52
+ readonly method: typeof SCHEDULE_EDIT_METHOD;
53
+ readonly explicitUserRequest: string;
54
+ readonly changes: readonly string[];
55
+ readonly payload: SchedulePatchInput;
56
+ readonly current?: ScheduleEditCurrentSchedule;
57
+ }
58
+
59
+ export interface ScheduleEditFieldDiff {
60
+ readonly field: 'name' | 'schedule' | 'prompt';
61
+ readonly before: string;
62
+ readonly after: string;
63
+ readonly changed: boolean;
64
+ }
65
+
66
+ export interface ScheduleEditCurrentSchedule {
67
+ readonly method: typeof ROUTINE_SCHEDULE_LIST_METHOD;
68
+ readonly route: typeof ROUTINE_SCHEDULE_ROUTE;
69
+ readonly scheduleId: string;
70
+ readonly found: boolean;
71
+ readonly status?: string;
72
+ readonly diffs: readonly ScheduleEditFieldDiff[];
73
+ readonly note?: string;
74
+ }
75
+
76
+ export interface ScheduleEditSuccess {
77
+ readonly ok: true;
78
+ readonly kind: typeof SCHEDULE_EDIT_METHOD;
79
+ readonly route: typeof SCHEDULE_EDIT_ROUTE;
80
+ readonly scheduleId: string;
81
+ readonly schedule: SchedulePatchOutput;
82
+ readonly request: SchedulePatchInput;
83
+ }
84
+
85
+ export interface ScheduleEditFailure {
86
+ readonly ok: false;
87
+ readonly kind:
88
+ | 'confirmation_required'
89
+ | 'auth_required'
90
+ | 'connected_host_unavailable'
91
+ | 'connected_host_incompatible'
92
+ | 'connected_host_route_unavailable'
93
+ | 'connected_host_error';
94
+ readonly error: string;
95
+ readonly route: typeof SCHEDULE_EDIT_ROUTE;
96
+ readonly baseUrl?: string;
97
+ }
98
+
99
+ export type ScheduleEditResult = ScheduleEditSuccess | ScheduleEditFailure;
100
+
101
+ function optionValue(args: readonly string[], index: number, inlineValue: string | undefined): {
102
+ readonly value: string | undefined;
103
+ readonly nextIndex: number;
104
+ } {
105
+ if (inlineValue !== undefined) return { value: inlineValue, nextIndex: index };
106
+ const next = args[index + 1];
107
+ if (next === undefined || next.startsWith('--')) return { value: undefined, nextIndex: index };
108
+ return { value: next, nextIndex: index + 1 };
109
+ }
110
+
111
+ function parseOptionalNumber(raw: string, flag: string): number | string {
112
+ const value = Number(raw);
113
+ if (!Number.isFinite(value) || value < 0) return `${flag} must be a finite number greater than or equal to 0.`;
114
+ return value;
115
+ }
116
+
117
+ function scheduleDefinition(schedule: RoutineScheduleSpec, timezone?: string, staggerMs?: number): AutomationScheduleDefinition {
118
+ if (schedule.kind === 'cron') return normalizeCronSchedule(schedule.value, timezone, staggerMs);
119
+ if (timezone) throw new Error('--timezone is only valid with --cron.');
120
+ if (staggerMs !== undefined) throw new Error('--stagger-ms is only valid with --cron.');
121
+ if (schedule.kind === 'every') return normalizeEverySchedule(schedule.value);
122
+ const at = Date.parse(schedule.value);
123
+ if (!Number.isFinite(at)) throw new Error(`Invalid --at timestamp: ${schedule.value}`);
124
+ return normalizeAtSchedule(at);
125
+ }
126
+
127
+ function isRecord(value: unknown): value is Record<string, unknown> {
128
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
129
+ }
130
+
131
+ function readString(record: Record<string, unknown>, key: string): string | null {
132
+ const value = record[key];
133
+ return typeof value === 'string' ? value : null;
134
+ }
135
+
136
+ function scheduleValue(schedule: unknown): string {
137
+ if (!isRecord(schedule)) return 'unknown';
138
+ if (schedule.kind === 'cron') {
139
+ return [
140
+ readString(schedule, 'expression') ?? '',
141
+ readString(schedule, 'timezone') ? `[${readString(schedule, 'timezone')}]` : '',
142
+ typeof schedule.staggerMs === 'number' ? `[stagger ${schedule.staggerMs}ms]` : '',
143
+ ].filter(Boolean).join(' ') || 'cron';
144
+ }
145
+ if (schedule.kind === 'every' && typeof schedule.intervalMs === 'number') return formatEveryInterval(schedule.intervalMs);
146
+ if (schedule.kind === 'at' && typeof schedule.at === 'number') return new Date(schedule.at).toISOString();
147
+ return String(schedule.kind ?? 'unknown');
148
+ }
149
+
150
+ function promptState(job: Record<string, unknown>): string {
151
+ const execution = isRecord(job.execution) ? job.execution : {};
152
+ const prompt = readString(execution, 'prompt') ?? readString(job, 'prompt');
153
+ return prompt?.trim() ? 'existing prompt present' : 'no existing prompt';
154
+ }
155
+
156
+ function readScheduleJob(output: ScheduleListOutput, scheduleId: string): Record<string, unknown> | null {
157
+ const record: Record<string, unknown> = isRecord(output as unknown) ? output as Record<string, unknown> : {};
158
+ const jobs = Array.isArray(record.jobs) ? record.jobs : [];
159
+ for (const job of jobs) {
160
+ if (!isRecord(job)) continue;
161
+ if (readString(job, 'id') === scheduleId) return job;
162
+ }
163
+ return null;
164
+ }
165
+
166
+ function diffChanged(before: string, after: string): boolean {
167
+ return before.trim() !== after.trim();
168
+ }
169
+
170
+ function buildCurrentScheduleContext(
171
+ preview: ScheduleEditPreview,
172
+ output: ScheduleListOutput,
173
+ ): ScheduleEditCurrentSchedule {
174
+ const job = readScheduleJob(output, preview.scheduleId);
175
+ if (!job) {
176
+ return {
177
+ method: ROUTINE_SCHEDULE_LIST_METHOD,
178
+ route: ROUTINE_SCHEDULE_ROUTE,
179
+ scheduleId: preview.scheduleId,
180
+ found: false,
181
+ diffs: [],
182
+ note: 'No current schedule record matched this id; review the requested patch before confirming.',
183
+ };
184
+ }
185
+
186
+ const diffs: ScheduleEditFieldDiff[] = [];
187
+ if (preview.payload.name) {
188
+ const before = readString(job, 'name') ?? 'unknown';
189
+ const after = preview.payload.name;
190
+ diffs.push({ field: 'name', before, after, changed: diffChanged(before, after) });
191
+ }
192
+ if (preview.payload.schedule) {
193
+ const before = scheduleValue(job.schedule);
194
+ const after = scheduleValue(preview.payload.schedule);
195
+ diffs.push({ field: 'schedule', before, after, changed: diffChanged(before, after) });
196
+ }
197
+ if (preview.payload.prompt) {
198
+ diffs.push({
199
+ field: 'prompt',
200
+ before: promptState(job),
201
+ after: 'replacement prompt prepared',
202
+ changed: true,
203
+ });
204
+ }
205
+
206
+ return {
207
+ method: ROUTINE_SCHEDULE_LIST_METHOD,
208
+ route: ROUTINE_SCHEDULE_ROUTE,
209
+ scheduleId: preview.scheduleId,
210
+ found: true,
211
+ status: readString(job, 'status') ?? undefined,
212
+ diffs,
213
+ };
214
+ }
215
+
216
+ function pushRequiredPairErrors(parsed: {
217
+ readonly prompt?: string;
218
+ readonly task?: string;
219
+ readonly successCriteria?: string;
220
+ }, errors: string[]): void {
221
+ const hasTaskPrompt = Boolean(parsed.task || parsed.successCriteria);
222
+ if (parsed.prompt && hasTaskPrompt) {
223
+ errors.push('Use either --prompt or --task with --success-criteria, not both.');
224
+ }
225
+ if (hasTaskPrompt && (!parsed.task || !parsed.successCriteria)) {
226
+ errors.push('Use --task and --success-criteria together when rebuilding an autonomous schedule prompt.');
227
+ }
228
+ }
229
+
230
+ export function parseScheduleEditArgs(
231
+ args: readonly string[],
232
+ options: ParseScheduleEditOptions = {},
233
+ ): ParsedScheduleEditArgs {
234
+ let scheduleId: string | null = null;
235
+ let schedule: RoutineScheduleSpec | null = null;
236
+ let name: string | undefined;
237
+ let prompt: string | undefined;
238
+ let task: string | undefined;
239
+ let successCriteria: string | undefined;
240
+ let timezone: string | undefined;
241
+ let staggerMs: number | undefined;
242
+ let yes = false;
243
+ let explicitUserRequest: string | null = options.defaultExplicitUserRequest ?? null;
244
+ const errors: string[] = [];
245
+
246
+ for (let index = 0; index < args.length; index += 1) {
247
+ const raw = args[index] ?? '';
248
+ const equals = raw.indexOf('=');
249
+ const optionName = equals >= 0 ? raw.slice(0, equals) : raw;
250
+ const inlineValue = equals >= 0 ? raw.slice(equals + 1) : undefined;
251
+
252
+ if (raw === '--yes') {
253
+ yes = true;
254
+ continue;
255
+ }
256
+ if (
257
+ optionName === '--schedule-id'
258
+ || optionName === '--job-id'
259
+ || optionName === '--name'
260
+ || optionName === '--prompt'
261
+ || optionName === '--task'
262
+ || optionName === '--success-criteria'
263
+ || optionName === '--timezone'
264
+ || optionName === '--explicit-user-request'
265
+ ) {
266
+ const consumed = optionValue(args, index, inlineValue);
267
+ index = consumed.nextIndex;
268
+ const value = consumed.value?.trim();
269
+ if (!value) {
270
+ errors.push(`${optionName} requires a value.`);
271
+ continue;
272
+ }
273
+ if (optionName === '--schedule-id' || optionName === '--job-id') scheduleId = value;
274
+ if (optionName === '--name') name = value;
275
+ if (optionName === '--prompt') prompt = value;
276
+ if (optionName === '--task') task = value;
277
+ if (optionName === '--success-criteria') successCriteria = value;
278
+ if (optionName === '--timezone') timezone = value;
279
+ if (optionName === '--explicit-user-request') explicitUserRequest = value;
280
+ continue;
281
+ }
282
+ if (optionName === '--cron' || optionName === '--every' || optionName === '--at') {
283
+ const consumed = optionValue(args, index, inlineValue);
284
+ index = consumed.nextIndex;
285
+ const value = consumed.value?.trim();
286
+ if (!value) {
287
+ errors.push(`${optionName} requires a value.`);
288
+ continue;
289
+ }
290
+ if (schedule) {
291
+ errors.push('Choose exactly one schedule selector: --cron, --every, or --at.');
292
+ continue;
293
+ }
294
+ schedule = {
295
+ kind: optionName === '--cron' ? 'cron' : optionName === '--every' ? 'every' : 'at',
296
+ value,
297
+ };
298
+ continue;
299
+ }
300
+ if (optionName === '--stagger-ms') {
301
+ const consumed = optionValue(args, index, inlineValue);
302
+ index = consumed.nextIndex;
303
+ const value = consumed.value?.trim();
304
+ if (!value) {
305
+ errors.push('--stagger-ms requires a value.');
306
+ continue;
307
+ }
308
+ const parsed = parseOptionalNumber(value, '--stagger-ms');
309
+ if (typeof parsed === 'string') errors.push(parsed);
310
+ else staggerMs = parsed;
311
+ continue;
312
+ }
313
+ if (raw.startsWith('--')) {
314
+ errors.push(`Unknown option ${raw}`);
315
+ continue;
316
+ }
317
+ if (!scheduleId) {
318
+ scheduleId = raw;
319
+ continue;
320
+ }
321
+ errors.push(`Unexpected argument: ${raw}`);
322
+ }
323
+
324
+ if (!scheduleId) errors.push('Schedule id is required.');
325
+ if (!explicitUserRequest) errors.push('Explicit user request is required: use --explicit-user-request <authorizing request>.');
326
+ pushRequiredPairErrors({ prompt, task, successCriteria }, errors);
327
+ if (!schedule && !name && !prompt && !task && !successCriteria) {
328
+ errors.push('At least one edit is required: --cron, --every, --at, --name, --prompt, or --task with --success-criteria.');
329
+ }
330
+ if (!schedule && (timezone || staggerMs !== undefined)) {
331
+ errors.push('--timezone and --stagger-ms require a schedule selector.');
332
+ }
333
+ if (schedule && schedule.kind !== 'cron' && (timezone || staggerMs !== undefined)) {
334
+ errors.push('--timezone and --stagger-ms are only valid with --cron.');
335
+ }
336
+ return { scheduleId, schedule, name, prompt, task, successCriteria, timezone, staggerMs, yes, explicitUserRequest, errors };
337
+ }
338
+
339
+ function schedulePatchPrompt(parsed: ParsedScheduleEditArgs): string | undefined {
340
+ if (parsed.prompt) return parsed.prompt;
341
+ if (!parsed.task || !parsed.successCriteria || !parsed.explicitUserRequest) return undefined;
342
+ return buildAutonomySchedulePrompt({
343
+ task: parsed.task,
344
+ successCriteria: parsed.successCriteria,
345
+ explicitUserRequest: parsed.explicitUserRequest,
346
+ });
347
+ }
348
+
349
+ function changeList(parsed: ParsedScheduleEditArgs): readonly string[] {
350
+ return [
351
+ parsed.name ? 'name' : '',
352
+ parsed.schedule ? 'schedule' : '',
353
+ parsed.prompt ? 'prompt' : '',
354
+ parsed.task ? 'autonomous-task-prompt' : '',
355
+ ].filter((value): value is string => value.length > 0);
356
+ }
357
+
358
+ export function buildScheduleEditPayload(parsed: ParsedScheduleEditArgs): SchedulePatchInput {
359
+ if (!parsed.scheduleId) throw new Error('Schedule id is required.');
360
+ const payload: SchedulePatchInput = { jobId: parsed.scheduleId };
361
+ if (parsed.name) payload.name = parsed.name;
362
+ const prompt = schedulePatchPrompt(parsed);
363
+ if (prompt) payload.prompt = prompt;
364
+ if (parsed.schedule) payload.schedule = scheduleDefinition(parsed.schedule, parsed.timezone, parsed.staggerMs);
365
+ return payload;
366
+ }
367
+
368
+ export function buildScheduleEditPreview(parsed: ParsedScheduleEditArgs): ScheduleEditPreview {
369
+ if (!parsed.scheduleId) throw new Error('Schedule id is required.');
370
+ if (!parsed.explicitUserRequest) throw new Error('Explicit user request is required.');
371
+ return {
372
+ scheduleId: parsed.scheduleId,
373
+ route: SCHEDULE_EDIT_ROUTE,
374
+ method: SCHEDULE_EDIT_METHOD,
375
+ explicitUserRequest: parsed.explicitUserRequest,
376
+ changes: changeList(parsed),
377
+ payload: buildScheduleEditPayload(parsed),
378
+ };
379
+ }
380
+
381
+ export async function enrichScheduleEditPreviewFromConnectedHost(
382
+ connection: AgentConnectedHostConnection,
383
+ preview: ScheduleEditPreview,
384
+ ): Promise<ScheduleEditPreview> {
385
+ if (!connection.token) return preview;
386
+ try {
387
+ const sdk = createBrowserGoodVibesSdk({ baseUrl: connection.baseUrl, authToken: connection.token });
388
+ const output = await sdk.operator.invoke(ROUTINE_SCHEDULE_LIST_METHOD, {});
389
+ return {
390
+ ...preview,
391
+ current: buildCurrentScheduleContext(preview, output),
392
+ };
393
+ } catch (error) {
394
+ return {
395
+ ...preview,
396
+ current: {
397
+ method: ROUTINE_SCHEDULE_LIST_METHOD,
398
+ route: ROUTINE_SCHEDULE_ROUTE,
399
+ scheduleId: preview.scheduleId,
400
+ found: false,
401
+ diffs: [],
402
+ note: `Could not read current schedule state: ${summarizeError(error)}`,
403
+ },
404
+ };
405
+ }
406
+ }
407
+
408
+ async function fetchConnectedHostStatus(connection: AgentConnectedHostConnection): Promise<{
409
+ readonly ok: boolean;
410
+ readonly status: number;
411
+ readonly body: unknown;
412
+ }> {
413
+ try {
414
+ const response = await fetch(`${connection.baseUrl}/status`, {
415
+ headers: connection.token ? { authorization: `Bearer ${connection.token}` } : undefined,
416
+ });
417
+ const text = await response.text();
418
+ let body: unknown = text;
419
+ try {
420
+ body = text.trim() ? JSON.parse(text) as unknown : {};
421
+ } catch {
422
+ body = text;
423
+ }
424
+ return { ok: response.ok, status: response.status, body };
425
+ } catch (error) {
426
+ return { ok: false, status: 0, body: summarizeError(error) };
427
+ }
428
+ }
429
+
430
+ async function classifyScheduleEditError(
431
+ error: unknown,
432
+ connection: AgentConnectedHostConnection,
433
+ ): Promise<ScheduleEditFailure> {
434
+ const message = summarizeError(error);
435
+ const lower = message.toLowerCase();
436
+ if (lower.includes('401') || lower.includes('unauthorized') || lower.includes('auth')) {
437
+ return { ok: false, kind: 'auth_required', error: message, route: SCHEDULE_EDIT_ROUTE, baseUrl: connection.baseUrl };
438
+ }
439
+ if (lower.includes('404') || lower.includes('not found')) {
440
+ const connectedHost = await fetchConnectedHostStatus(connection);
441
+ if (connectedHost.ok) {
442
+ return {
443
+ ok: false,
444
+ kind: 'connected_host_incompatible',
445
+ error: 'Connected GoodVibes host compatibility does not satisfy Agent schedule edit requirements; automation.jobs.patch is unavailable.',
446
+ route: SCHEDULE_EDIT_ROUTE,
447
+ baseUrl: connection.baseUrl,
448
+ };
449
+ }
450
+ return { ok: false, kind: 'connected_host_route_unavailable', error: message, route: SCHEDULE_EDIT_ROUTE, baseUrl: connection.baseUrl };
451
+ }
452
+ if (lower.includes('fetch') || lower.includes('connect') || lower.includes('econnrefused')) {
453
+ return { ok: false, kind: 'connected_host_unavailable', error: message, route: SCHEDULE_EDIT_ROUTE, baseUrl: connection.baseUrl };
454
+ }
455
+ return { ok: false, kind: 'connected_host_error', error: message, route: SCHEDULE_EDIT_ROUTE, baseUrl: connection.baseUrl };
456
+ }
457
+
458
+ export async function editConnectedSchedule(
459
+ connection: AgentConnectedHostConnection,
460
+ preview: ScheduleEditPreview,
461
+ ): Promise<ScheduleEditResult> {
462
+ if (!connection.token) {
463
+ return {
464
+ ok: false,
465
+ kind: 'auth_required',
466
+ error: `No connected-host operator token found at ${connection.tokenPath}`,
467
+ route: SCHEDULE_EDIT_ROUTE,
468
+ baseUrl: connection.baseUrl,
469
+ };
470
+ }
471
+ try {
472
+ const sdk = createBrowserGoodVibesSdk({ baseUrl: connection.baseUrl, authToken: connection.token });
473
+ const schedule = await sdk.operator.invoke(SCHEDULE_EDIT_METHOD, preview.payload);
474
+ return {
475
+ ok: true,
476
+ kind: SCHEDULE_EDIT_METHOD,
477
+ route: SCHEDULE_EDIT_ROUTE,
478
+ scheduleId: preview.scheduleId,
479
+ schedule,
480
+ request: preview.payload,
481
+ };
482
+ } catch (error) {
483
+ return classifyScheduleEditError(error, connection);
484
+ }
485
+ }
486
+
487
+ export function resolveScheduleEditConnectedHostConnection(
488
+ configManager: AgentConnectedHostConfigReader,
489
+ homeDirectory: string,
490
+ ): AgentConnectedHostConnection {
491
+ return resolveAgentConnectedHostConnection(configManager, homeDirectory);
492
+ }
493
+
494
+ export function scheduleEditKindFromValue(value: unknown): RoutineScheduleKind | null {
495
+ return value === 'at' || value === 'every' || value === 'cron' ? value : null;
496
+ }
@@ -0,0 +1,42 @@
1
+ export function scheduleRouteArg(value: string): string {
2
+ return JSON.stringify(value);
3
+ }
4
+
5
+ function routeLine(id: string, route: string): string {
6
+ return ` ${id} ${route}`;
7
+ }
8
+
9
+ export function scheduleNextRouteLines(scheduleId: string, options: { readonly deleted?: boolean } = {}): readonly string[] {
10
+ const id = scheduleId && scheduleId !== '(unknown)' ? scheduleId : '...';
11
+ const query = id === '...' ? '' : ` query:${scheduleRouteArg(id)}`;
12
+ const scheduleIdArg = `scheduleId:${scheduleRouteArg(id)}`;
13
+ const lines = [
14
+ ' nextRoutes',
15
+ routeLine('listSchedules', `schedule action:"list"${query}`),
16
+ routeLine('autonomyQueue', 'autonomy action:"item" queueItemId:"connected-schedules" includeParameters:true'),
17
+ ];
18
+ if (options.deleted) {
19
+ lines.push(
20
+ routeLine('createAutonomousSchedule', 'schedule action:"create" task:"..." successCriteria:"..." every:"..." confirm:true explicitUserRequest:"..."'),
21
+ routeLine('createReminder', 'schedule action:"remind" message:"..." at:"..." confirm:true explicitUserRequest:"..."'),
22
+ );
23
+ return lines;
24
+ }
25
+ lines.push(
26
+ routeLine('runNow', `schedule action:"run" ${scheduleIdArg} confirm:true explicitUserRequest:"..."`),
27
+ routeLine('editSchedule', `schedule action:"edit" ${scheduleIdArg} confirm:true explicitUserRequest:"..."`),
28
+ routeLine('pauseSchedule', `schedule action:"pause" ${scheduleIdArg} confirm:true explicitUserRequest:"..."`),
29
+ routeLine('resumeSchedule', `schedule action:"resume" ${scheduleIdArg} confirm:true explicitUserRequest:"..."`),
30
+ routeLine('deleteSchedule', `schedule action:"delete" ${scheduleIdArg} confirm:true explicitUserRequest:"..."`),
31
+ );
32
+ return lines;
33
+ }
34
+
35
+ export function scheduleConfirmationRouteLines(routes: Record<string, string>): readonly string[] {
36
+ const entries = Object.entries(routes).filter(([, route]) => route.trim() !== '');
37
+ if (entries.length === 0) return [];
38
+ return [
39
+ ' confirmationRoutes',
40
+ ...entries.map(([id, route]) => routeLine(id, route)),
41
+ ];
42
+ }
@@ -0,0 +1,140 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import type { ShellPathService } from '@/runtime/index.ts';
4
+ import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
5
+
6
+ export type AgentSetupWizardCheckpointSource = 'workspace' | 'harness';
7
+
8
+ export interface AgentSetupWizardCheckpointRecord {
9
+ readonly version: 1;
10
+ readonly currentStepId: string;
11
+ readonly currentStepLabel: string;
12
+ readonly savedAt: string;
13
+ readonly source: AgentSetupWizardCheckpointSource;
14
+ readonly note?: string;
15
+ }
16
+
17
+ export interface AgentSetupWizardCheckpointSnapshot {
18
+ readonly path: string;
19
+ readonly exists: boolean;
20
+ readonly checkpoint: AgentSetupWizardCheckpointRecord | null;
21
+ readonly parseError?: string;
22
+ }
23
+
24
+ export interface SaveAgentSetupWizardCheckpointInput {
25
+ readonly currentStepId: string;
26
+ readonly currentStepLabel: string;
27
+ readonly source: AgentSetupWizardCheckpointSource;
28
+ readonly note?: string;
29
+ }
30
+
31
+ type AgentSetupWizardCheckpointShellPaths = Pick<ShellPathService, 'resolveUserPath'>;
32
+
33
+ const CHECKPOINT_VERSION = 1;
34
+
35
+ function isRecord(value: unknown): value is Record<string, unknown> {
36
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
37
+ }
38
+
39
+ function readString(value: unknown): string {
40
+ return typeof value === 'string' ? value.trim() : '';
41
+ }
42
+
43
+ function isCheckpointSource(value: unknown): value is AgentSetupWizardCheckpointSource {
44
+ return value === 'workspace' || value === 'harness';
45
+ }
46
+
47
+ function normalizeStepId(value: string): string {
48
+ return value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^[._-]+|[._-]+$/g, '');
49
+ }
50
+
51
+ function parseCheckpoint(value: unknown): AgentSetupWizardCheckpointRecord | null {
52
+ if (!isRecord(value) || value.version !== CHECKPOINT_VERSION) return null;
53
+ const currentStepId = normalizeStepId(readString(value.currentStepId));
54
+ const currentStepLabel = readString(value.currentStepLabel).replace(/\s+/g, ' ');
55
+ const savedAt = readString(value.savedAt);
56
+ if (!currentStepId || !currentStepLabel || !savedAt || Number.isNaN(Date.parse(savedAt))) return null;
57
+ const source = isCheckpointSource(value.source) ? value.source : 'workspace';
58
+ const note = readString(value.note);
59
+ return {
60
+ version: CHECKPOINT_VERSION,
61
+ currentStepId,
62
+ currentStepLabel,
63
+ savedAt,
64
+ source,
65
+ ...(note ? { note } : {}),
66
+ };
67
+ }
68
+
69
+ function formatCheckpoint(checkpoint: AgentSetupWizardCheckpointRecord): string {
70
+ return `${JSON.stringify(checkpoint, null, 2)}\n`;
71
+ }
72
+
73
+ export function setupWizardCheckpointPath(shellPaths: AgentSetupWizardCheckpointShellPaths): string {
74
+ return shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'setup', 'wizard-checkpoint.json');
75
+ }
76
+
77
+ export function readSetupWizardCheckpoint(shellPaths: AgentSetupWizardCheckpointShellPaths): AgentSetupWizardCheckpointSnapshot {
78
+ const path = setupWizardCheckpointPath(shellPaths);
79
+ if (!existsSync(path)) {
80
+ return {
81
+ path,
82
+ exists: false,
83
+ checkpoint: null,
84
+ };
85
+ }
86
+ try {
87
+ const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
88
+ const checkpoint = parseCheckpoint(parsed);
89
+ if (!checkpoint) {
90
+ return {
91
+ path,
92
+ exists: true,
93
+ checkpoint: null,
94
+ parseError: 'Invalid setup wizard checkpoint payload.',
95
+ };
96
+ }
97
+ return {
98
+ path,
99
+ exists: true,
100
+ checkpoint,
101
+ };
102
+ } catch (error) {
103
+ return {
104
+ path,
105
+ exists: true,
106
+ checkpoint: null,
107
+ parseError: error instanceof Error ? error.message : String(error),
108
+ };
109
+ }
110
+ }
111
+
112
+ export function saveSetupWizardCheckpoint(
113
+ shellPaths: AgentSetupWizardCheckpointShellPaths,
114
+ input: SaveAgentSetupWizardCheckpointInput,
115
+ ): AgentSetupWizardCheckpointSnapshot {
116
+ const currentStepId = normalizeStepId(input.currentStepId);
117
+ const currentStepLabel = input.currentStepLabel.trim().replace(/\s+/g, ' ');
118
+ if (!currentStepId) throw new Error('Setup wizard checkpoint requires a current step id.');
119
+ if (!currentStepLabel) throw new Error('Setup wizard checkpoint requires a current step label.');
120
+ const path = setupWizardCheckpointPath(shellPaths);
121
+ const checkpoint: AgentSetupWizardCheckpointRecord = {
122
+ version: CHECKPOINT_VERSION,
123
+ currentStepId,
124
+ currentStepLabel,
125
+ savedAt: new Date().toISOString(),
126
+ source: input.source,
127
+ ...(input.note?.trim() ? { note: input.note.trim().replace(/\s+/g, ' ') } : {}),
128
+ };
129
+ mkdirSync(dirname(path), { recursive: true });
130
+ const tempPath = `${path}.tmp`;
131
+ writeFileSync(tempPath, formatCheckpoint(checkpoint), 'utf-8');
132
+ renameSync(tempPath, path);
133
+ return readSetupWizardCheckpoint(shellPaths);
134
+ }
135
+
136
+ export function clearSetupWizardCheckpoint(shellPaths: AgentSetupWizardCheckpointShellPaths): AgentSetupWizardCheckpointSnapshot {
137
+ const path = setupWizardCheckpointPath(shellPaths);
138
+ if (existsSync(path)) rmSync(path, { force: true });
139
+ return readSetupWizardCheckpoint(shellPaths);
140
+ }