@pellux/goodvibes-agent 0.1.56 → 0.1.58
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.
- package/.goodvibes/GOODVIBES.md +1 -1
- package/CHANGELOG.md +18 -9
- package/README.md +3 -3
- package/docs/README.md +1 -1
- package/docs/getting-started.md +3 -3
- package/docs/release-and-publishing.md +2 -2
- package/package.json +1 -3
- package/src/agent/routine-schedule-args.ts +219 -0
- package/src/agent/routine-schedule-format.ts +173 -0
- package/src/agent/routine-schedule-promotion.ts +3 -811
- package/src/agent/routine-schedule-receipts.ts +502 -0
- package/src/cli/agent-knowledge-command.ts +6 -6
- package/src/cli/help.ts +3 -25
- package/src/cli/package-verification.ts +23 -16
- package/src/cli/redaction.ts +4 -1
- package/src/cli/routines-command.ts +10 -6
- package/src/cli/service-posture.ts +47 -280
- package/src/cli/status.ts +0 -1
- package/src/cli/tui-startup.ts +23 -0
- package/src/config/secret-config.ts +0 -2
- package/src/input/agent-workspace-categories.ts +219 -0
- package/src/input/agent-workspace-editors.ts +143 -0
- package/src/input/agent-workspace-snapshot.ts +265 -0
- package/src/input/agent-workspace-types.ts +142 -0
- package/src/input/agent-workspace.ts +22 -766
- package/src/input/commands/agent-runtime-profile-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -4
- package/src/input/commands/guidance-runtime.ts +1 -2
- package/src/input/commands/health-runtime.ts +3 -65
- package/src/input/commands/knowledge.ts +7 -7
- package/src/input/commands/local-setup-review.ts +0 -61
- package/src/input/commands/local-setup-transfer.ts +0 -3
- package/src/input/commands/local-setup.ts +2 -15
- package/src/input/commands/planning-runtime.ts +4 -1
- package/src/input/commands/platform-access-runtime.ts +1 -10
- package/src/input/commands/platform-services-runtime.ts +0 -1
- package/src/input/commands/recall-query.ts +1 -1
- package/src/input/commands/routines-runtime.ts +10 -6
- package/src/input/commands/schedule-runtime.ts +10 -6
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +1 -14
- package/src/input/commands.ts +0 -4
- package/src/input/handler-onboarding.ts +10 -120
- package/src/input/onboarding/onboarding-wizard-apply.ts +5 -196
- package/src/input/onboarding/onboarding-wizard-constants.ts +8 -119
- package/src/input/onboarding/onboarding-wizard-helpers.ts +2 -53
- package/src/input/onboarding/onboarding-wizard-rules.ts +2 -236
- package/src/input/onboarding/onboarding-wizard-state.ts +1 -69
- package/src/input/onboarding/onboarding-wizard-steps.ts +584 -737
- package/src/input/onboarding/onboarding-wizard-types.ts +8 -26
- package/src/input/onboarding/onboarding-wizard.ts +4 -109
- package/src/input/settings-modal-agent-policy.ts +10 -0
- package/src/input/settings-modal-types.ts +2 -4
- package/src/input/settings-modal.ts +3 -1
- package/src/input/submission-router.ts +0 -1
- package/src/main.ts +13 -12
- package/src/panels/approval-panel.ts +1 -2
- package/src/panels/builtin/operations.ts +1 -2
- package/src/panels/knowledge-panel.ts +2 -2
- package/src/panels/project-planning-panel.ts +4 -1
- package/src/panels/provider-health-domains.ts +0 -22
- package/src/panels/provider-health-panel.ts +1 -5
- package/src/panels/session-browser-panel.ts +0 -5
- package/src/panels/tasks-panel.ts +2 -64
- package/src/renderer/agent-workspace.ts +1 -1
- package/src/renderer/help-overlay.ts +1 -2
- package/src/renderer/semantic-diff.ts +1 -1
- package/src/renderer/settings-modal-helpers.ts +0 -16
- package/src/renderer/settings-modal.ts +3 -5
- package/src/runtime/bootstrap-hook-bridge.ts +0 -3
- package/src/runtime/bootstrap-shell.ts +2 -1
- package/src/runtime/bootstrap.ts +1 -1
- package/src/runtime/index.ts +0 -1
- package/src/runtime/onboarding/derivation.ts +1 -28
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +1 -4
- package/src/runtime/services.ts +4 -23
- package/src/runtime/ui-read-models.ts +4 -3
- package/src/shell/service-settings-sync.ts +15 -244
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/wrfc-agent-guard.ts +3 -3
- package/src/verification/live-verifier.ts +11 -5
- package/src/verification/verification-ledger.ts +3 -6
- package/src/version.ts +1 -1
- package/src/input/commands/agent-externalized-tui.ts +0 -73
- package/src/input/commands/cloudflare-runtime.ts +0 -385
- package/src/input/handler-onboarding-cloudflare.ts +0 -322
- package/src/input/onboarding/onboarding-runtime-status.ts +0 -87
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +0 -494
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +0 -199
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +0 -130
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +0 -762
- package/src/runtime/cloudflare-control-plane.ts +0 -350
- package/src/runtime/sandbox-public-gaps.ts +0 -358
package/.goodvibes/GOODVIBES.md
CHANGED
|
@@ -22,7 +22,7 @@ GoodVibes Agent is a proactive personal operator assistant built on the GoodVibe
|
|
|
22
22
|
## Product Boundaries
|
|
23
23
|
|
|
24
24
|
- Agent connects to an already-running GoodVibes daemon. It does not start, restart, install, or own daemon/listener services.
|
|
25
|
-
- GoodVibes TUI owns coding execution, file edits, git/worktree lifecycle,
|
|
25
|
+
- GoodVibes TUI owns coding execution, file edits, git/worktree lifecycle, runtime-isolation UX, and WRFC owner chains.
|
|
26
26
|
- Agent owns personal operator flow, setup/config surfaces, local memory, local skills, local personas, Agent knowledge, status/approval/automation observability, and explicit delegation receipts.
|
|
27
27
|
|
|
28
28
|
## Engineering Rules
|
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GoodVibes Agent will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.1.58 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
- e137f90 Fail cleanly for non-TTY TUI launch
|
|
8
|
+
|
|
9
|
+
## 0.1.57 - 2026-05-31
|
|
10
|
+
|
|
11
|
+
- 90edab3 Expand Agent onboarding setup surfaces
|
|
12
|
+
- 251ae62 Guard package-facing Agent product language
|
|
13
|
+
|
|
5
14
|
## 0.1.56 - 2026-05-31
|
|
6
15
|
|
|
7
16
|
- a0b54e8 Document Bun global PATH setup
|
|
8
|
-
- f845cca Rename onboarding
|
|
17
|
+
- f845cca Rename onboarding setup step
|
|
9
18
|
- 4462e52 Clarify Bun-only install path
|
|
10
19
|
|
|
11
20
|
## 0.1.55 - 2026-05-31
|
|
@@ -194,7 +203,7 @@ All notable changes to GoodVibes Agent will be recorded here.
|
|
|
194
203
|
|
|
195
204
|
- d20a93e Allow explicit recall review without yes
|
|
196
205
|
- 601f41c Require confirmation for eval execution
|
|
197
|
-
- f41befb Block
|
|
206
|
+
- f41befb Block copied infrastructure onboarding mutations
|
|
198
207
|
- 79071ec Block implicit block file saves
|
|
199
208
|
- 40aca02 Block inline diff file edits in Agent
|
|
200
209
|
- 854eda8 Block MCP workspace config mutations
|
|
@@ -265,24 +274,24 @@ All notable changes to GoodVibes Agent will be recorded here.
|
|
|
265
274
|
## 0.1.7 - 2026-05-31
|
|
266
275
|
|
|
267
276
|
- Replaced active planning-loop output and tests that still described planning as TUI-owned with Agent-owned planning state and planning namespace language.
|
|
268
|
-
- Added `LICENSE` to the explicit package file contract and release verification so
|
|
277
|
+
- Added `LICENSE` to the explicit package file contract and release verification so registry tarballs cannot omit license text.
|
|
269
278
|
- Prevented the operator workspace from dispatching placeholder delegation commands such as `/delegate --wrfc <task>`; those actions now provide guidance until the user supplies real task text.
|
|
270
279
|
- Added local Agent routines with `/routines`: create/list/search/show/enable/disable/start/review/stale/delete, secret-looking value rejection, enabled routine prompt injection, and operator workspace status. Starting a routine stays in the main conversation and does not create hidden background jobs.
|
|
271
|
-
- Removed copied TUI release, UAT, and WRFC artifact docs from the Agent source tree and updated remaining source docs so channel,
|
|
280
|
+
- Removed copied TUI release, UAT, and WRFC artifact docs from the Agent source tree and updated remaining source docs so channel, voice, integration, and panel guidance speaks in Agent/external-daemon terms.
|
|
272
281
|
|
|
273
282
|
## 0.1.6 - 2026-05-31
|
|
274
283
|
|
|
275
284
|
- Made the publish helper use exported `NODE_AUTH_TOKEN` or `NPM_TOKEN` automatically by writing a temporary npm user config for publish commands.
|
|
276
|
-
- Rewrote source docs for tools, commands, knowledge, artifacts, and multimodal behavior so they describe Agent-only Knowledge/Wiki and never teach default Knowledge/Wiki or
|
|
285
|
+
- Rewrote source docs for tools, commands, knowledge, artifacts, and multimodal behavior so they describe Agent-only Knowledge/Wiki and never teach default Knowledge/Wiki or non-Agent graph fallback.
|
|
277
286
|
- Updated `/plan` command and Planning panel language from copied TUI-owned wording to Agent-owned workspace planning state.
|
|
278
287
|
- Added regression tests that keep source docs and active planning surfaces aligned with Agent Knowledge isolation and Agent product language.
|
|
279
288
|
|
|
280
289
|
## 0.1.5 - 2026-05-31
|
|
281
290
|
|
|
282
|
-
- Hardened package-facing release checks so shipped docs and Agent guidance cannot reintroduce default Knowledge/Wiki,
|
|
291
|
+
- Hardened package-facing release checks so shipped docs and Agent guidance cannot reintroduce default Knowledge/Wiki, non-Agent graph, copied daemon, or copied WRFC-first policy text.
|
|
283
292
|
- Removed the generic default `knowledgeApi` client from the active Agent command context so slash commands must use the isolated Agent Knowledge API.
|
|
284
293
|
- Changed CLI `knowledge ingest-url` to post directly to `/api/goodvibes-agent/knowledge/ingest/url` instead of invoking the generic knowledge operator method.
|
|
285
|
-
- Rejected
|
|
294
|
+
- Rejected default-space and broad cross-space query flags in CLI and slash Agent Knowledge commands before any daemon call.
|
|
286
295
|
|
|
287
296
|
## 0.1.4 - 2026-05-31
|
|
288
297
|
|
|
@@ -294,12 +303,12 @@ All notable changes to GoodVibes Agent will be recorded here.
|
|
|
294
303
|
|
|
295
304
|
- Added local Agent personas with `/personas`: create/list/search/show/use/review/stale/delete, secret-looking value rejection, active persona prompt injection, and operator workspace status.
|
|
296
305
|
- Added local Agent skills with `/agent-skills` and `/skills local`: create/list/search/show/enable/disable/review/stale/delete, secret-looking value rejection, enabled skill prompt injection, and operator workspace status.
|
|
297
|
-
- Kept persona and skill state Agent-local with no default Knowledge/Wiki or
|
|
306
|
+
- Kept persona and skill state Agent-local with no default Knowledge/Wiki or non-Agent graph fallback.
|
|
298
307
|
|
|
299
308
|
## 0.1.2 - 2026-05-30
|
|
300
309
|
|
|
301
310
|
- Added `goodvibes-agent compat` for package SDK pin, external daemon version, auth presence, and isolated Agent Knowledge route readiness.
|
|
302
|
-
- Added `goodvibes-agent knowledge ...` commands for the isolated `/api/goodvibes-agent/knowledge/*` environment with no default Knowledge/Wiki or
|
|
311
|
+
- Added `goodvibes-agent knowledge ...` commands for the isolated `/api/goodvibes-agent/knowledge/*` environment with no default Knowledge/Wiki or non-Agent graph fallback.
|
|
303
312
|
- Added explicit GoodVibes TUI build delegation through `goodvibes-agent delegate` and `/delegate`; WRFC is requested only through explicit `--wrfc`, `/wrfc`, or `/review` delegation.
|
|
304
313
|
- Removed the copied WRFC panel from the default Agent panel registry while preserving explicit TUI delegation for build/fix/review work.
|
|
305
314
|
- Hardened the Agent release helper and CLI help output for the current Agent changelog and command set.
|
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ Local Agent behavior is editable from the TUI:
|
|
|
81
81
|
/skills local list
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Starting a routine records local usage and prints its steps; it does not spawn background agents or daemon automation jobs. Promotion to a daemon schedule is separate and explicit: it calls the public `schedules.create` route on the externally managed daemon only after `--yes`, can include explicit delivery targets such as `--delivery-surface slack`, records a redacted local receipt, and the generated scheduled prompt keeps Agent Knowledge isolated from default Knowledge/Wiki and
|
|
84
|
+
Starting a routine records local usage and prints its steps; it does not spawn background agents or daemon automation jobs. Promotion to a daemon schedule is separate and explicit: it calls the public `schedules.create` route on the externally managed daemon only after `--yes`, can include explicit delivery targets such as `--delivery-surface slack`, records a redacted local receipt, and the generated scheduled prompt keeps Agent Knowledge isolated from default Knowledge/Wiki and non-Agent knowledge segments. Use `/schedule reconcile` to compare those local receipts against live externally owned daemon schedules through public `schedules.list`.
|
|
85
85
|
|
|
86
86
|
## Daemon Prerequisite
|
|
87
87
|
|
|
@@ -101,9 +101,9 @@ Those commands should return explicit external-daemon guidance instead of mutati
|
|
|
101
101
|
|
|
102
102
|
GoodVibes Agent owns the operator assistant surface: serial assistant flow, proactive safe actions, local memory/routines/skills/personas, Agent knowledge routes, companion chat, approvals/automation observability, and explicit build delegation.
|
|
103
103
|
|
|
104
|
-
Agent Knowledge/Wiki is its own product segment. Agent uses `/api/goodvibes-agent/knowledge/*` and must not fall back to default Knowledge/Wiki
|
|
104
|
+
Agent Knowledge/Wiki is its own product segment. Agent uses `/api/goodvibes-agent/knowledge/*` and must not fall back to default Knowledge/Wiki or other product-specific knowledge routes.
|
|
105
105
|
|
|
106
|
-
GoodVibes TUI owns coding execution: file edits, git/worktree workflows, coding panels,
|
|
106
|
+
GoodVibes TUI owns coding execution: file edits, git/worktree workflows, coding panels, runtime-isolation UX, and WRFC execution. Agent may delegate explicit build/fix/review work to TUI through public daemon/session contracts; normal assistant chat must not use shared coding sessions.
|
|
107
107
|
|
|
108
108
|
## Package Docs
|
|
109
109
|
|
package/docs/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Important baseline constraints:
|
|
|
17
17
|
- Agent depends on `@pellux/goodvibes-sdk@0.33.35`.
|
|
18
18
|
- Agent connects to an externally managed daemon.
|
|
19
19
|
- Agent does not start, stop, restart, install, uninstall, or own daemon/listener/web/service lifecycle.
|
|
20
|
-
- Agent Knowledge/Wiki uses only `/api/goodvibes-agent/knowledge/*`; there is no default Knowledge/Wiki
|
|
20
|
+
- Agent Knowledge/Wiki uses only `/api/goodvibes-agent/knowledge/*`; there is no default Knowledge/Wiki or non-Agent product fallback.
|
|
21
21
|
- Agent supports isolated runtime homes with `GOODVIBES_AGENT_HOME=<path>` and named profile homes with `goodvibes-agent profiles create <name> --template <starter> --yes` plus `--agent-profile <name>`.
|
|
22
22
|
- Agent ships starter profile templates for household, research, travel, operations, and personal productivity local state; `profiles templates export/import` and `/agent-profile guide` support local custom starters.
|
|
23
23
|
- Local personas, routines, and Agent skills are stored under the Agent surface root and are injected only into the serial Agent conversation.
|
package/docs/getting-started.md
CHANGED
|
@@ -71,7 +71,7 @@ Named profiles isolate Agent-local config, sessions, memory, personas, skills, r
|
|
|
71
71
|
|
|
72
72
|
## Local Personas, Routines, And Skills
|
|
73
73
|
|
|
74
|
-
Personas, routines, and reusable Agent skills are local to GoodVibes Agent. They do not write into default Knowledge/Wiki or
|
|
74
|
+
Personas, routines, and reusable Agent skills are local to GoodVibes Agent. They do not write into default Knowledge/Wiki or non-Agent knowledge segments.
|
|
75
75
|
|
|
76
76
|
```text
|
|
77
77
|
/personas list
|
|
@@ -85,7 +85,7 @@ Personas, routines, and reusable Agent skills are local to GoodVibes Agent. They
|
|
|
85
85
|
/skills local list
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
The active persona plus enabled Agent routines and skills are injected into the main serial assistant conversation. Starting a routine records local usage and prints its steps; it does not spawn background agents or daemon automation jobs. Promoting a routine to a schedule is an explicit `schedules.create` call to the external daemon, requires `--yes`, writes a local redacted promotion receipt, and preserves the rule that Agent Knowledge never falls back to default Knowledge/Wiki or
|
|
88
|
+
The active persona plus enabled Agent routines and skills are injected into the main serial assistant conversation. Starting a routine records local usage and prints its steps; it does not spawn background agents or daemon automation jobs. Promoting a routine to a schedule is an explicit `schedules.create` call to the external daemon, requires `--yes`, writes a local redacted promotion receipt, and preserves the rule that Agent Knowledge never falls back to default Knowledge/Wiki or non-Agent knowledge segments.
|
|
89
89
|
|
|
90
90
|
## External Daemon
|
|
91
91
|
|
|
@@ -97,7 +97,7 @@ Start the daemon from GoodVibes TUI or the daemon host before using daemon-backe
|
|
|
97
97
|
- `/api/goodvibes-agent/knowledge/search`
|
|
98
98
|
- `/api/goodvibes-agent/knowledge/ingest/url`
|
|
99
99
|
|
|
100
|
-
Agent Knowledge/Wiki is an Agent-owned product segment. Agent commands must not fall back to default Knowledge/Wiki
|
|
100
|
+
Agent Knowledge/Wiki is an Agent-owned product segment. Agent commands must not fall back to default Knowledge/Wiki or other product-specific knowledge spaces.
|
|
101
101
|
|
|
102
102
|
Agent lifecycle commands that would start or mutate daemon posture are blocked intentionally. Use `goodvibes-agent status`, `goodvibes-agent doctor`, and read-only surface checks for diagnostics.
|
|
103
103
|
|
|
@@ -18,7 +18,7 @@ bun add -g @pellux/goodvibes-agent
|
|
|
18
18
|
goodvibes-agent --help
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Do not add non-Bun install instructions for this product. The package is hosted on the
|
|
21
|
+
Do not add non-Bun install instructions for this product. The package is hosted on the public package registry, but the supported install and smoke path is Bun.
|
|
22
22
|
|
|
23
23
|
## Required Gates
|
|
24
24
|
|
|
@@ -51,7 +51,7 @@ Also run the package install smoke from a packed artifact. It must prove:
|
|
|
51
51
|
|
|
52
52
|
Do not publish if package-facing docs or install commands refer to another package name, another executable, or Agent-owned daemon lifecycle.
|
|
53
53
|
|
|
54
|
-
Do not publish if Agent Knowledge commands can fall back to default Knowledge/Wiki
|
|
54
|
+
Do not publish if Agent Knowledge commands can fall back to default Knowledge/Wiki or another product-specific knowledge route. Agent Knowledge must use the isolated `/api/goodvibes-agent/knowledge/*` segment.
|
|
55
55
|
|
|
56
56
|
Do not ship daemon binaries from this package. If Agent later gets compiled artifacts, they must use Agent artifact names and remain separate from daemon ownership.
|
|
57
57
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Near-fork GoodVibes operator assistant with the GoodVibes TUI shell, renderer, input, fullscreen workspace, and daemon-connected Agent product brain.",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"!src/test",
|
|
19
19
|
"!src/**/*.test.ts",
|
|
20
20
|
"!src/**/__tests__",
|
|
21
|
-
"!src/daemon/**",
|
|
22
21
|
"!src/panels/diff-panel.ts",
|
|
23
22
|
"!src/panels/file-explorer-panel.ts",
|
|
24
23
|
"!src/panels/file-preview-panel.ts",
|
|
@@ -92,7 +91,6 @@
|
|
|
92
91
|
"access": "public"
|
|
93
92
|
},
|
|
94
93
|
"engines": {
|
|
95
|
-
"node": ">=20",
|
|
96
94
|
"bun": ">=1.3.10"
|
|
97
95
|
},
|
|
98
96
|
"dependencies": {
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ParsedRoutineSchedulePromotionArgs,
|
|
3
|
+
RoutineScheduleDeliverySurfaceKind,
|
|
4
|
+
RoutineScheduleDeliveryTargetSpec,
|
|
5
|
+
} from './routine-schedule-promotion.ts';
|
|
6
|
+
|
|
7
|
+
const DELIVERY_SURFACE_KINDS: readonly RoutineScheduleDeliverySurfaceKind[] = [
|
|
8
|
+
'tui',
|
|
9
|
+
'web',
|
|
10
|
+
'slack',
|
|
11
|
+
'discord',
|
|
12
|
+
'ntfy',
|
|
13
|
+
'webhook',
|
|
14
|
+
'telegram',
|
|
15
|
+
'google-chat',
|
|
16
|
+
'signal',
|
|
17
|
+
'whatsapp',
|
|
18
|
+
'imessage',
|
|
19
|
+
'msteams',
|
|
20
|
+
'bluebubbles',
|
|
21
|
+
'mattermost',
|
|
22
|
+
'matrix',
|
|
23
|
+
'service',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function optionValue(args: readonly string[], index: number, inlineValue: string | undefined): {
|
|
27
|
+
readonly value: string | undefined;
|
|
28
|
+
readonly nextIndex: number;
|
|
29
|
+
} {
|
|
30
|
+
if (inlineValue !== undefined) return { value: inlineValue, nextIndex: index };
|
|
31
|
+
const next = args[index + 1];
|
|
32
|
+
if (next === undefined || next.startsWith('--')) return { value: undefined, nextIndex: index };
|
|
33
|
+
return { value: next, nextIndex: index + 1 };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function isRoutineScheduleDeliverySurfaceKind(value: string): value is RoutineScheduleDeliverySurfaceKind {
|
|
37
|
+
return DELIVERY_SURFACE_KINDS.includes(value as RoutineScheduleDeliverySurfaceKind);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseSurfaceDeliveryTarget(raw: string): RoutineScheduleDeliveryTargetSpec | string {
|
|
41
|
+
const [surfaceKind = '', routeId, label] = raw.split(':');
|
|
42
|
+
if (!isRoutineScheduleDeliverySurfaceKind(surfaceKind)) {
|
|
43
|
+
return `Unsupported delivery surface "${surfaceKind}".`;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
kind: 'surface',
|
|
47
|
+
surfaceKind,
|
|
48
|
+
routeId: routeId?.trim() || undefined,
|
|
49
|
+
label: label?.trim() || undefined,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseRouteDeliveryTarget(raw: string): RoutineScheduleDeliveryTargetSpec | string {
|
|
54
|
+
const [routeId = '', label] = raw.split(':');
|
|
55
|
+
const normalizedRouteId = routeId.trim();
|
|
56
|
+
if (!normalizedRouteId) return '--delivery-route requires a route id.';
|
|
57
|
+
return {
|
|
58
|
+
kind: 'surface',
|
|
59
|
+
routeId: normalizedRouteId,
|
|
60
|
+
label: label?.trim() || undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function parseWebhookDeliveryTarget(raw: string): RoutineScheduleDeliveryTargetSpec | string {
|
|
65
|
+
const normalized = raw.trim();
|
|
66
|
+
if (!normalized) return '--delivery-webhook requires a URL.';
|
|
67
|
+
try {
|
|
68
|
+
const url = new URL(normalized);
|
|
69
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') return '--delivery-webhook must be an http(s) URL.';
|
|
70
|
+
} catch {
|
|
71
|
+
return '--delivery-webhook must be a valid URL.';
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
kind: 'webhook',
|
|
75
|
+
address: normalized,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function parseLinkDeliveryTarget(raw: string): RoutineScheduleDeliveryTargetSpec | string {
|
|
80
|
+
const normalized = raw.trim();
|
|
81
|
+
if (!normalized) return '--delivery-link requires a URL or label.';
|
|
82
|
+
return {
|
|
83
|
+
kind: 'link',
|
|
84
|
+
address: normalized,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateDeliveryTargets(targets: readonly RoutineScheduleDeliveryTargetSpec[]): string | null {
|
|
89
|
+
const kinds = new Set(targets.map((target) => target.kind));
|
|
90
|
+
return kinds.size > 1 ? 'Use one delivery target kind per routine promotion command.' : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function parseRoutineSchedulePromotionArgs(args: readonly string[]): ParsedRoutineSchedulePromotionArgs {
|
|
94
|
+
let routineId: string | null = null;
|
|
95
|
+
let schedule: ParsedRoutineSchedulePromotionArgs['schedule'] = null;
|
|
96
|
+
const deliveryTargets: RoutineScheduleDeliveryTargetSpec[] = [];
|
|
97
|
+
let name: string | undefined;
|
|
98
|
+
let timezone: string | undefined;
|
|
99
|
+
let provider: string | undefined;
|
|
100
|
+
let model: string | undefined;
|
|
101
|
+
let enabled = true;
|
|
102
|
+
let yes = false;
|
|
103
|
+
const errors: string[] = [];
|
|
104
|
+
|
|
105
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
106
|
+
const raw = args[index] ?? '';
|
|
107
|
+
const equals = raw.indexOf('=');
|
|
108
|
+
const optionName = equals >= 0 ? raw.slice(0, equals) : raw;
|
|
109
|
+
const inlineValue = equals >= 0 ? raw.slice(equals + 1) : undefined;
|
|
110
|
+
|
|
111
|
+
if (raw === '--yes') {
|
|
112
|
+
yes = true;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (raw === '--disabled') {
|
|
116
|
+
enabled = false;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (optionName === '--name' || optionName === '--timezone' || optionName === '--provider' || optionName === '--model') {
|
|
120
|
+
const consumed = optionValue(args, index, inlineValue);
|
|
121
|
+
index = consumed.nextIndex;
|
|
122
|
+
const value = consumed.value?.trim();
|
|
123
|
+
if (!value) {
|
|
124
|
+
errors.push(`${optionName} requires a value.`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (optionName === '--name') name = value;
|
|
128
|
+
if (optionName === '--timezone') timezone = value;
|
|
129
|
+
if (optionName === '--provider') provider = value;
|
|
130
|
+
if (optionName === '--model') model = value;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (optionName === '--cron' || optionName === '--every' || optionName === '--at') {
|
|
134
|
+
const consumed = optionValue(args, index, inlineValue);
|
|
135
|
+
index = consumed.nextIndex;
|
|
136
|
+
const value = consumed.value?.trim();
|
|
137
|
+
if (!value) {
|
|
138
|
+
errors.push(`${optionName} requires a value.`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (schedule) {
|
|
142
|
+
errors.push('Choose exactly one schedule selector: --cron, --every, or --at.');
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
schedule = {
|
|
146
|
+
kind: optionName === '--cron' ? 'cron' : optionName === '--every' ? 'every' : 'at',
|
|
147
|
+
value,
|
|
148
|
+
};
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (optionName === '--delivery-surface' || optionName === '--deliver-surface') {
|
|
152
|
+
const consumed = optionValue(args, index, inlineValue);
|
|
153
|
+
index = consumed.nextIndex;
|
|
154
|
+
const value = consumed.value?.trim();
|
|
155
|
+
if (!value) {
|
|
156
|
+
errors.push(`${optionName} requires a value.`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const target = parseSurfaceDeliveryTarget(value);
|
|
160
|
+
if (typeof target === 'string') errors.push(target);
|
|
161
|
+
else deliveryTargets.push(target);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (optionName === '--delivery-route' || optionName === '--deliver-route') {
|
|
165
|
+
const consumed = optionValue(args, index, inlineValue);
|
|
166
|
+
index = consumed.nextIndex;
|
|
167
|
+
const value = consumed.value?.trim();
|
|
168
|
+
if (!value) {
|
|
169
|
+
errors.push(`${optionName} requires a value.`);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const target = parseRouteDeliveryTarget(value);
|
|
173
|
+
if (typeof target === 'string') errors.push(target);
|
|
174
|
+
else deliveryTargets.push(target);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (optionName === '--delivery-webhook' || optionName === '--deliver-webhook') {
|
|
178
|
+
const consumed = optionValue(args, index, inlineValue);
|
|
179
|
+
index = consumed.nextIndex;
|
|
180
|
+
const value = consumed.value?.trim();
|
|
181
|
+
if (!value) {
|
|
182
|
+
errors.push(`${optionName} requires a value.`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const target = parseWebhookDeliveryTarget(value);
|
|
186
|
+
if (typeof target === 'string') errors.push(target);
|
|
187
|
+
else deliveryTargets.push(target);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (optionName === '--delivery-link' || optionName === '--deliver-link') {
|
|
191
|
+
const consumed = optionValue(args, index, inlineValue);
|
|
192
|
+
index = consumed.nextIndex;
|
|
193
|
+
const value = consumed.value?.trim();
|
|
194
|
+
if (!value) {
|
|
195
|
+
errors.push(`${optionName} requires a value.`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const target = parseLinkDeliveryTarget(value);
|
|
199
|
+
if (typeof target === 'string') errors.push(target);
|
|
200
|
+
else deliveryTargets.push(target);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (raw.startsWith('--')) {
|
|
204
|
+
errors.push(`Unknown option: ${raw}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (!routineId) {
|
|
208
|
+
routineId = raw;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
errors.push(`Unexpected argument: ${raw}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!routineId) errors.push('Routine id or name is required.');
|
|
215
|
+
if (!schedule) errors.push('Schedule is required: use --cron <expr>, --every <interval>, or --at <iso-time>.');
|
|
216
|
+
const deliveryError = validateDeliveryTargets(deliveryTargets);
|
|
217
|
+
if (deliveryError) errors.push(deliveryError);
|
|
218
|
+
return { routineId, schedule, deliveryTargets, name, timezone, provider, model, enabled, yes, errors };
|
|
219
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ROUTINE_SCHEDULE_LIST_METHOD,
|
|
3
|
+
ROUTINE_SCHEDULE_METHOD,
|
|
4
|
+
type RoutineScheduleCorrelationResult,
|
|
5
|
+
type RoutineSchedulePromotionFailure,
|
|
6
|
+
type RoutineSchedulePromotionPreview,
|
|
7
|
+
type RoutineSchedulePromotionSuccess,
|
|
8
|
+
type RoutineScheduleReceipt,
|
|
9
|
+
type RoutineScheduleReceiptSnapshot,
|
|
10
|
+
} from './routine-schedule-promotion.ts';
|
|
11
|
+
|
|
12
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
13
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readString(record: Record<string, unknown>, key: string): string | null {
|
|
17
|
+
const value = record[key];
|
|
18
|
+
return typeof value === 'string' ? value : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function formatRoutineSchedulePreview(preview: RoutineSchedulePromotionPreview): string {
|
|
22
|
+
const schedule = preview.payload.kind === 'cron'
|
|
23
|
+
? `${preview.payload.cron}${preview.payload.timezone ? ` [${preview.payload.timezone}]` : ''}`
|
|
24
|
+
: preview.payload.kind === 'every'
|
|
25
|
+
? String(preview.payload.every)
|
|
26
|
+
: String(preview.payload.at);
|
|
27
|
+
const delivery = preview.payload.delivery;
|
|
28
|
+
const deliveryTargetCount = delivery?.targets.length ?? 0;
|
|
29
|
+
return [
|
|
30
|
+
'Daemon schedule preview for Agent routine',
|
|
31
|
+
` routine: ${preview.routineName} (${preview.routineId})`,
|
|
32
|
+
` route: ${preview.method} ${preview.route}`,
|
|
33
|
+
` name: ${String(preview.payload.name ?? '(daemon default)')}`,
|
|
34
|
+
` schedule: ${preview.payload.kind} ${schedule}`,
|
|
35
|
+
` enabled: ${preview.payload.enabled === false ? 'no' : 'yes'}`,
|
|
36
|
+
` delivery: ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
|
|
37
|
+
' target: external daemon service/main conversation route',
|
|
38
|
+
' policy: isolated Agent Knowledge only; no default wiki/non-Agent fallback; no WRFC unless explicitly delegated',
|
|
39
|
+
' next: rerun with --yes to create this daemon schedule',
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function formatRoutineScheduleSuccess(result: RoutineSchedulePromotionSuccess): string {
|
|
44
|
+
const record: Record<string, unknown> = isRecord(result.schedule) ? result.schedule : {};
|
|
45
|
+
const id = readString(record, 'id') ?? '(unknown)';
|
|
46
|
+
const status = readString(record, 'status') ?? (record.enabled === false ? 'paused' : 'enabled');
|
|
47
|
+
return [
|
|
48
|
+
'Created daemon schedule for Agent routine',
|
|
49
|
+
` routine: ${result.routineName} (${result.routineId})`,
|
|
50
|
+
` schedule: ${id}`,
|
|
51
|
+
` status: ${status}`,
|
|
52
|
+
` route: ${result.kind} ${result.route}`,
|
|
53
|
+
' next: inspect with /schedule list or daemon schedule observability',
|
|
54
|
+
].join('\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function formatRoutineScheduleReceipts(snapshot: RoutineScheduleReceiptSnapshot, limit = 10): string {
|
|
58
|
+
const receipts = snapshot.receipts.slice(0, Math.max(1, limit));
|
|
59
|
+
if (snapshot.receipts.length === 0) {
|
|
60
|
+
return [
|
|
61
|
+
'Agent routine schedule receipts',
|
|
62
|
+
` store: ${snapshot.path}`,
|
|
63
|
+
' No routine schedule promotions have been recorded yet.',
|
|
64
|
+
' Create one with /schedule promote-routine <routine-id> --cron <expr> --yes.',
|
|
65
|
+
].join('\n');
|
|
66
|
+
}
|
|
67
|
+
return [
|
|
68
|
+
`Agent routine schedule receipts (${snapshot.receipts.length})`,
|
|
69
|
+
` store: ${snapshot.path}`,
|
|
70
|
+
...receipts.map((receipt) => {
|
|
71
|
+
const schedule = receipt.scheduleId ? ` schedule=${receipt.scheduleId}` : '';
|
|
72
|
+
const failure = receipt.status === 'failed' && receipt.failureKind ? ` failure=${receipt.failureKind}` : '';
|
|
73
|
+
return ` ${receipt.id} ${receipt.status} ${receipt.scheduleKind} ${receipt.scheduleValue} routine=${receipt.routineId}${schedule}${failure}`;
|
|
74
|
+
}),
|
|
75
|
+
snapshot.receipts.length > receipts.length ? ` ...${snapshot.receipts.length - receipts.length} more` : '',
|
|
76
|
+
].filter((line): line is string => Boolean(line)).join('\n');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function formatRoutineScheduleReceipt(receipt: RoutineScheduleReceipt): string {
|
|
80
|
+
return [
|
|
81
|
+
`Agent routine schedule receipt ${receipt.id}`,
|
|
82
|
+
` created: ${receipt.createdAt}`,
|
|
83
|
+
` status: ${receipt.status}`,
|
|
84
|
+
` routine: ${receipt.routineName} (${receipt.routineId})`,
|
|
85
|
+
` route: ${receipt.method} ${receipt.route}`,
|
|
86
|
+
` daemon: ${receipt.daemonBaseUrl}`,
|
|
87
|
+
` schedule: ${receipt.scheduleName}${receipt.scheduleId ? ` (${receipt.scheduleId})` : ''}`,
|
|
88
|
+
receipt.scheduleStatus ? ` schedule status: ${receipt.scheduleStatus}` : '',
|
|
89
|
+
` cadence: ${receipt.scheduleKind} ${receipt.scheduleValue}${receipt.timezone ? ` [${receipt.timezone}]` : ''}`,
|
|
90
|
+
` enabled: ${receipt.enabled ? 'yes' : 'no'}`,
|
|
91
|
+
receipt.provider ? ` provider: ${receipt.provider}` : '',
|
|
92
|
+
receipt.model ? ` model: ${receipt.model}` : '',
|
|
93
|
+
` target: ${receipt.target.kind ?? 'unknown'}${receipt.target.surfaceKind ? `/${receipt.target.surfaceKind}` : ''}`,
|
|
94
|
+
receipt.deliveryMode ? ` delivery: ${receipt.deliveryMode}` : '',
|
|
95
|
+
...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${target.kind}${target.surfaceKind ? `/${target.surfaceKind}` : ''}${target.routeId ? ` route=${target.routeId}` : ''}${target.address ? ` address=${target.address}` : ''}${target.label ? ` label=${target.label}` : ''}`),
|
|
96
|
+
receipt.failureKind ? ` failure: ${receipt.failureKind}` : '',
|
|
97
|
+
receipt.failureError ? ` error: ${receipt.failureError}` : '',
|
|
98
|
+
].filter((line): line is string => Boolean(line)).join('\n');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelationResult, limit = 10): string {
|
|
102
|
+
if (!result.ok) {
|
|
103
|
+
return [
|
|
104
|
+
`Daemon schedule reconciliation error: ${result.kind}`,
|
|
105
|
+
` ${result.error}`,
|
|
106
|
+
result.baseUrl ? ` daemon: ${result.baseUrl}` : null,
|
|
107
|
+
` route: ${ROUTINE_SCHEDULE_LIST_METHOD} ${result.route}`,
|
|
108
|
+
result.kind === 'auth_required'
|
|
109
|
+
? ' next: pair/authenticate with the externally managed GoodVibes daemon, then retry.'
|
|
110
|
+
: null,
|
|
111
|
+
result.kind === 'daemon_unavailable'
|
|
112
|
+
? ' next: start/restart the external GoodVibes daemon from TUI or daemon host tooling; Agent does not own daemon lifecycle.'
|
|
113
|
+
: null,
|
|
114
|
+
result.kind === 'version_mismatch' || result.kind === 'daemon_route_unavailable'
|
|
115
|
+
? ' next: update/restart the external GoodVibes daemon so public schedules.list is available.'
|
|
116
|
+
: null,
|
|
117
|
+
].filter((line): line is string => Boolean(line)).join('\n');
|
|
118
|
+
}
|
|
119
|
+
const correlations = result.correlations.slice(0, Math.max(1, limit));
|
|
120
|
+
if (result.receiptCount === 0) {
|
|
121
|
+
return [
|
|
122
|
+
'Agent routine schedule reconciliation',
|
|
123
|
+
` daemon: ${result.baseUrl}`,
|
|
124
|
+
` route: ${result.kind} ${result.route}`,
|
|
125
|
+
` live schedules: ${result.scheduleCount}`,
|
|
126
|
+
' No local routine promotion receipts exist yet.',
|
|
127
|
+
' Create one with /schedule promote-routine <routine-id> --cron <expr> --yes.',
|
|
128
|
+
].join('\n');
|
|
129
|
+
}
|
|
130
|
+
const matched = result.correlations.filter((entry) => entry.liveStatus === 'matched').length;
|
|
131
|
+
const missing = result.correlations.filter((entry) => entry.liveStatus === 'missing').length;
|
|
132
|
+
const failed = result.correlations.filter((entry) => entry.liveStatus === 'failed-receipt').length;
|
|
133
|
+
return [
|
|
134
|
+
'Agent routine schedule reconciliation',
|
|
135
|
+
` daemon: ${result.baseUrl}`,
|
|
136
|
+
` route: ${result.kind} ${result.route}`,
|
|
137
|
+
` receipts: ${result.receiptCount}; live schedules: ${result.scheduleCount}; matched: ${matched}; missing: ${missing}; failed receipts: ${failed}`,
|
|
138
|
+
...correlations.map((entry) => {
|
|
139
|
+
const receipt = entry.receipt;
|
|
140
|
+
const schedule = entry.schedule;
|
|
141
|
+
const live = schedule
|
|
142
|
+
? ` live=${schedule.id} status=${schedule.status ?? (schedule.enabled === false ? 'paused' : 'enabled')}`
|
|
143
|
+
: '';
|
|
144
|
+
const runs = schedule && schedule.runCount !== undefined
|
|
145
|
+
? ` runs=${schedule.runCount}/${schedule.successCount ?? 0}/${schedule.failureCount ?? 0}`
|
|
146
|
+
: '';
|
|
147
|
+
const next = schedule?.nextRunAt ? ` next=${new Date(schedule.nextRunAt).toISOString()}` : '';
|
|
148
|
+
return ` ${receipt.id} ${entry.liveStatus} reason=${entry.matchReason} routine=${receipt.routineId} receiptSchedule=${receipt.scheduleId ?? '(none)'}${live}${runs}${next}`;
|
|
149
|
+
}),
|
|
150
|
+
result.correlations.length > correlations.length ? ` ...${result.correlations.length - correlations.length} more` : '',
|
|
151
|
+
].filter((line): line is string => Boolean(line)).join('\n');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function formatRoutineScheduleFailure(failure: RoutineSchedulePromotionFailure): string {
|
|
155
|
+
return [
|
|
156
|
+
`Daemon schedule error: ${failure.kind}`,
|
|
157
|
+
` ${failure.error}`,
|
|
158
|
+
failure.baseUrl ? ` daemon: ${failure.baseUrl}` : null,
|
|
159
|
+
` route: ${ROUTINE_SCHEDULE_METHOD} ${failure.route}`,
|
|
160
|
+
failure.kind === 'version_mismatch' && failure.daemonVersion && failure.expectedSdkVersion
|
|
161
|
+
? ` versions: daemon=${failure.daemonVersion} expected=${failure.expectedSdkVersion}`
|
|
162
|
+
: null,
|
|
163
|
+
failure.kind === 'auth_required'
|
|
164
|
+
? ' next: pair/authenticate with the externally managed GoodVibes daemon, then retry with --yes.'
|
|
165
|
+
: null,
|
|
166
|
+
failure.kind === 'daemon_unavailable'
|
|
167
|
+
? ' next: start/restart the external GoodVibes daemon from TUI or daemon host tooling; Agent does not own daemon lifecycle.'
|
|
168
|
+
: null,
|
|
169
|
+
failure.kind === 'version_mismatch' || failure.kind === 'daemon_route_unavailable'
|
|
170
|
+
? ' next: update/restart the external GoodVibes daemon so public schedules.create is available.'
|
|
171
|
+
: null,
|
|
172
|
+
].filter((line): line is string => Boolean(line)).join('\n');
|
|
173
|
+
}
|