@pellux/goodvibes-agent 0.1.117 → 1.0.1
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/CHANGELOG.md +20 -4
- package/README.md +35 -14
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +176093 -171065
- package/docs/README.md +11 -5
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +3 -3
- package/docs/getting-started.md +29 -15
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +44 -9
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +2 -5
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-registry.ts +15 -14
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +26 -25
- package/src/agent/routine-registry.ts +13 -12
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +34 -32
- package/src/agent/routine-schedule-receipts.ts +28 -26
- package/src/agent/runtime-profile-starters.ts +2 -2
- package/src/agent/runtime-profile.ts +18 -17
- package/src/agent/skill-registry.ts +25 -24
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +39 -33
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +13 -8
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +29 -11
- package/src/cli/local-library-command.ts +159 -73
- package/src/cli/management-commands.ts +98 -62
- package/src/cli/management.ts +52 -26
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -24
- package/src/cli/profiles-command.ts +52 -35
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +59 -39
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +46 -121
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-activation.ts +33 -7
- package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
- package/src/input/agent-workspace-basic-command-editors.ts +30 -10
- package/src/input/agent-workspace-categories.ts +276 -64
- package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
- package/src/input/agent-workspace-channel-command-editors.ts +23 -5
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +18 -2
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
- package/src/input/agent-workspace-editors.ts +140 -2
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-knowledge-url-editor.ts +4 -11
- package/src/input/agent-workspace-learned-behavior.ts +2 -2
- package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
- package/src/input/agent-workspace-library-command-editors.ts +2 -2
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editors.ts +1 -1
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +38 -2
- package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
- package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
- package/src/input/agent-workspace-operations-command-editors.ts +80 -3
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-setup.ts +22 -11
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
- package/src/input/agent-workspace-snapshot.ts +112 -13
- package/src/input/agent-workspace-task-command-editors.ts +4 -4
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +92 -4
- package/src/input/agent-workspace-voice-media.ts +3 -6
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace.ts +136 -208
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-local-library-args.ts +52 -0
- package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
- package/src/input/commands/agent-skills-runtime.ts +87 -99
- package/src/input/commands/agent-workspace-runtime.ts +23 -7
- package/src/input/commands/brief-runtime.ts +25 -24
- package/src/input/commands/channels-runtime.ts +145 -31
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +140 -115
- package/src/input/commands/knowledge.ts +57 -56
- package/src/input/commands/local-provider-runtime.ts +36 -18
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +7 -7
- package/src/input/commands/mcp-runtime.ts +118 -50
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -17
- package/src/input/commands/personas-runtime.ts +49 -59
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +29 -29
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +45 -6
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +63 -72
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +132 -93
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +32 -17
- package/src/input/commands/subscription-runtime.ts +53 -179
- package/src/input/commands/tasks-runtime.ts +20 -20
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +2 -2
- package/src/input/feed-context-factory.ts +2 -1
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -1
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -3
- package/src/input/handler-interactions.ts +7 -5
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +24 -80
- package/src/input/handler-shortcuts.ts +15 -4
- package/src/input/handler.ts +47 -17
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +22 -11
- package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
- package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/onboarding/onboarding-wizard.ts +68 -0
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +22 -4
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +6 -52
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -8
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +2 -2
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +1 -10
- package/src/panels/builtin/session.ts +9 -9
- package/src/panels/builtin/shared.ts +2 -2
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +18 -11
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +45 -4
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +1 -1
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +20 -13
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +1 -1
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +228 -52
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +106 -33
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +164 -50
- package/src/renderer/model-picker-overlay.ts +58 -2
- package/src/renderer/model-workspace.ts +104 -20
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +20 -4
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +83 -50
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +6 -6
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +9 -7
- package/src/runtime/bootstrap-core.ts +19 -1
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +4 -4
- package/src/runtime/bootstrap.ts +31 -22
- package/src/runtime/connected-host-auth.ts +7 -2
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -1
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +80 -79
- package/src/runtime/onboarding/derivation.ts +5 -8
- package/src/runtime/onboarding/snapshot.ts +0 -15
- package/src/runtime/onboarding/types.ts +8 -19
- package/src/runtime/onboarding/verify.ts +32 -10
- package/src/runtime/operator-token-cleanup.ts +1 -1
- package/src/runtime/services.ts +91 -22
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/ui-openers.ts +3 -13
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +269 -69
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +8 -8
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -13
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from '
|
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
4
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
5
|
+
import { formatAgentRecordReviewState } from './record-labels.ts';
|
|
5
6
|
|
|
6
7
|
export type AgentPersonaSource = 'user' | 'agent' | 'imported' | 'system';
|
|
7
8
|
export type AgentPersonaReviewState = 'fresh' | 'reviewed' | 'stale';
|
|
@@ -106,9 +107,9 @@ function containsSecretLikeText(text: string): boolean {
|
|
|
106
107
|
return SECRET_PATTERNS.some((pattern) => pattern.test(text));
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
export function assertNoSecretLikeText(fields: readonly string[]): void {
|
|
110
|
+
export function assertNoSecretLikeText(fields: readonly string[], ownerLabel = 'Agent local records'): void {
|
|
110
111
|
if (fields.some((field) => containsSecretLikeText(field))) {
|
|
111
|
-
throw new Error(
|
|
112
|
+
throw new Error(`${ownerLabel} cannot store secret-looking values. Store a secret reference or remove the sensitive text.`);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -213,9 +214,9 @@ export class AgentPersonaRegistry {
|
|
|
213
214
|
const description = input.description.trim();
|
|
214
215
|
const body = input.body.trim();
|
|
215
216
|
this.validateRequired(name, description, body);
|
|
216
|
-
assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])]);
|
|
217
|
+
assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])], 'Personas');
|
|
217
218
|
const duplicate = store.personas.find((persona) => persona.name.toLowerCase() === name.toLowerCase());
|
|
218
|
-
if (duplicate) throw new Error(`Persona already exists
|
|
219
|
+
if (duplicate) throw new Error(`Persona already exists ${duplicate.id}`);
|
|
219
220
|
const timestamp = nowIso();
|
|
220
221
|
const persona: AgentPersonaRecord = {
|
|
221
222
|
id: this.nextId(name, store.personas),
|
|
@@ -237,14 +238,14 @@ export class AgentPersonaRegistry {
|
|
|
237
238
|
public update(idOrName: string, input: AgentPersonaUpdateInput): AgentPersonaRecord {
|
|
238
239
|
const store = this.readStore();
|
|
239
240
|
const existing = this.findInStore(store, idOrName);
|
|
240
|
-
if (!existing) throw new Error(`Unknown persona
|
|
241
|
+
if (!existing) throw new Error(`Unknown persona ${idOrName}`);
|
|
241
242
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
242
243
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
243
244
|
const body = input.body === undefined ? existing.body : input.body.trim();
|
|
244
245
|
this.validateRequired(name, description, body);
|
|
245
|
-
assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])]);
|
|
246
|
+
assertNoSecretLikeText([name, description, body, ...(input.tags ?? []), ...(input.triggers ?? [])], 'Personas');
|
|
246
247
|
const duplicate = store.personas.find((persona) => persona.id !== existing.id && persona.name.toLowerCase() === name.toLowerCase());
|
|
247
|
-
if (duplicate) throw new Error(`Persona already exists
|
|
248
|
+
if (duplicate) throw new Error(`Persona already exists ${duplicate.id}`);
|
|
248
249
|
const updated: AgentPersonaRecord = {
|
|
249
250
|
...existing,
|
|
250
251
|
name,
|
|
@@ -268,7 +269,7 @@ export class AgentPersonaRegistry {
|
|
|
268
269
|
public setActive(idOrName: string): AgentPersonaRecord {
|
|
269
270
|
const store = this.readStore();
|
|
270
271
|
const persona = this.findInStore(store, idOrName);
|
|
271
|
-
if (!persona) throw new Error(`Unknown persona
|
|
272
|
+
if (!persona) throw new Error(`Unknown persona ${idOrName}`);
|
|
272
273
|
this.writeStore({ ...store, activePersonaId: persona.id });
|
|
273
274
|
return persona;
|
|
274
275
|
}
|
|
@@ -281,7 +282,7 @@ export class AgentPersonaRegistry {
|
|
|
281
282
|
public markReviewed(idOrName: string): AgentPersonaRecord {
|
|
282
283
|
const store = this.readStore();
|
|
283
284
|
const existing = this.findInStore(store, idOrName);
|
|
284
|
-
if (!existing) throw new Error(`Unknown persona
|
|
285
|
+
if (!existing) throw new Error(`Unknown persona ${idOrName}`);
|
|
285
286
|
const updated: AgentPersonaRecord = {
|
|
286
287
|
...existing,
|
|
287
288
|
reviewState: 'reviewed',
|
|
@@ -299,7 +300,7 @@ export class AgentPersonaRegistry {
|
|
|
299
300
|
public markStale(idOrName: string, reason: string): AgentPersonaRecord {
|
|
300
301
|
const store = this.readStore();
|
|
301
302
|
const existing = this.findInStore(store, idOrName);
|
|
302
|
-
if (!existing) throw new Error(`Unknown persona
|
|
303
|
+
if (!existing) throw new Error(`Unknown persona ${idOrName}`);
|
|
303
304
|
const updated: AgentPersonaRecord = {
|
|
304
305
|
...existing,
|
|
305
306
|
reviewState: 'stale',
|
|
@@ -316,7 +317,7 @@ export class AgentPersonaRegistry {
|
|
|
316
317
|
public deletePersona(idOrName: string): AgentPersonaRecord {
|
|
317
318
|
const store = this.readStore();
|
|
318
319
|
const existing = this.findInStore(store, idOrName);
|
|
319
|
-
if (!existing) throw new Error(`Unknown persona
|
|
320
|
+
if (!existing) throw new Error(`Unknown persona ${idOrName}`);
|
|
320
321
|
this.writeStore({
|
|
321
322
|
...store,
|
|
322
323
|
activePersonaId: store.activePersonaId === existing.id ? null : store.activePersonaId,
|
|
@@ -353,7 +354,7 @@ export class AgentPersonaRegistry {
|
|
|
353
354
|
try {
|
|
354
355
|
return parseStore(readFileSync(this.storePath, 'utf-8'));
|
|
355
356
|
} catch (error) {
|
|
356
|
-
throw new Error(`Could not read Agent persona store
|
|
357
|
+
throw new Error(`Could not read Agent persona store ${error instanceof Error ? error.message : String(error)}`);
|
|
357
358
|
}
|
|
358
359
|
}
|
|
359
360
|
|
|
@@ -372,10 +373,10 @@ export function buildActivePersonaPrompt(shellPaths: ShellPathService): string |
|
|
|
372
373
|
'## Active GoodVibes Agent Persona',
|
|
373
374
|
`Name: ${active.name}`,
|
|
374
375
|
`Description: ${active.description}`,
|
|
375
|
-
`Review
|
|
376
|
+
`Review: ${formatAgentRecordReviewState(active.reviewState)}`,
|
|
376
377
|
'',
|
|
377
378
|
active.body,
|
|
378
379
|
'',
|
|
379
|
-
'Apply this persona inside the same serial assistant conversation. Do not
|
|
380
|
+
'Apply this persona inside the same serial assistant conversation. Do not spawn separate Agent jobs because a persona is active.',
|
|
380
381
|
].join('\n');
|
|
381
382
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const SOURCE_LABELS = {
|
|
2
|
+
user: 'User',
|
|
3
|
+
agent: 'Agent',
|
|
4
|
+
imported: 'Imported',
|
|
5
|
+
system: 'Built-in',
|
|
6
|
+
builtin: 'Built-in',
|
|
7
|
+
local: 'Local',
|
|
8
|
+
'agent-memory': 'Agent memory',
|
|
9
|
+
} as const satisfies Readonly<Record<string, string>>;
|
|
10
|
+
|
|
11
|
+
const USER_PROVENANCE_LABELS = new Set<string>([
|
|
12
|
+
'Workspace',
|
|
13
|
+
'Workspace capture',
|
|
14
|
+
'Command',
|
|
15
|
+
'Manual',
|
|
16
|
+
'Main conversation',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const REVIEW_STATE_LABELS = {
|
|
20
|
+
fresh: 'Needs review',
|
|
21
|
+
reviewed: 'Reviewed',
|
|
22
|
+
stale: 'Needs refresh',
|
|
23
|
+
contradicted: 'Contradicted',
|
|
24
|
+
} as const satisfies Readonly<Record<string, string>>;
|
|
25
|
+
|
|
26
|
+
type SourceLabelKey = keyof typeof SOURCE_LABELS;
|
|
27
|
+
type ReviewStateLabelKey = keyof typeof REVIEW_STATE_LABELS;
|
|
28
|
+
|
|
29
|
+
export interface AgentRecordReference {
|
|
30
|
+
readonly kind: string;
|
|
31
|
+
readonly ref: string;
|
|
32
|
+
readonly label?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function titleCaseWords(value: string): string {
|
|
36
|
+
return value
|
|
37
|
+
.trim()
|
|
38
|
+
.replace(/[_-]+/g, ' ')
|
|
39
|
+
.replace(/\s+/g, ' ')
|
|
40
|
+
.replace(/\b\w/g, (match) => match.toUpperCase());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function formatAgentRecordSource(source: string | undefined): string {
|
|
44
|
+
const normalized = (source ?? '').trim().toLowerCase();
|
|
45
|
+
if (!normalized) return 'Unknown';
|
|
46
|
+
if (normalized in SOURCE_LABELS) return SOURCE_LABELS[normalized as SourceLabelKey];
|
|
47
|
+
return titleCaseWords(source ?? '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function formatAgentRecordReviewState(reviewState: string | undefined): string {
|
|
51
|
+
const normalized = (reviewState ?? '').trim().toLowerCase();
|
|
52
|
+
if (!normalized) return REVIEW_STATE_LABELS.fresh;
|
|
53
|
+
if (normalized in REVIEW_STATE_LABELS) return REVIEW_STATE_LABELS[normalized as ReviewStateLabelKey];
|
|
54
|
+
return titleCaseWords(reviewState ?? '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function formatAgentRecordProvenance(provenance: string | undefined): string {
|
|
58
|
+
const raw = (provenance ?? '').trim();
|
|
59
|
+
const normalized = raw.toLowerCase();
|
|
60
|
+
if (!raw) return '';
|
|
61
|
+
if (normalized === 'agent-workspace') return 'Workspace';
|
|
62
|
+
if (normalized === 'agent-workspace-learned-behavior') return 'Workspace capture';
|
|
63
|
+
if (normalized === 'workspace capture') return 'Workspace capture';
|
|
64
|
+
if (normalized === 'slash-command') return 'Command';
|
|
65
|
+
if (normalized === 'agent-memory') return 'Agent memory';
|
|
66
|
+
if (normalized === 'manual') return 'Manual';
|
|
67
|
+
if (normalized === 'main-conversation') return 'Main conversation';
|
|
68
|
+
if (normalized === 'plan-command') return 'Planning command';
|
|
69
|
+
if (normalized === 'agent-media-generation') return 'Media generation';
|
|
70
|
+
if (normalized === 'agent-channel-send') return 'Channel delivery';
|
|
71
|
+
if (normalized === 'project-memory') return 'Project memory';
|
|
72
|
+
|
|
73
|
+
const discovered = raw.match(/^discovered:([^:]+):(.+)$/i);
|
|
74
|
+
if (discovered) {
|
|
75
|
+
return `Imported file (${discovered[1]}): ${discovered[2]}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const starter = raw.match(/^goodvibes-agent starter:(.+)$/i);
|
|
79
|
+
if (starter) {
|
|
80
|
+
return `Starter profile: ${starter[1]}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return titleCaseWords(raw);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function formatAgentRecordOrigin(source: string | undefined, provenance: string | undefined): string {
|
|
87
|
+
const sourceLabel = formatAgentRecordSource(source);
|
|
88
|
+
const provenanceLabel = formatAgentRecordProvenance(provenance);
|
|
89
|
+
if (!provenanceLabel || provenanceLabel === sourceLabel) return sourceLabel;
|
|
90
|
+
if (sourceLabel === 'User' && USER_PROVENANCE_LABELS.has(provenanceLabel)) {
|
|
91
|
+
return provenanceLabel;
|
|
92
|
+
}
|
|
93
|
+
if (sourceLabel === 'Imported' && provenanceLabel.startsWith('Imported file')) {
|
|
94
|
+
return provenanceLabel;
|
|
95
|
+
}
|
|
96
|
+
return `${sourceLabel} (${provenanceLabel})`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function formatAgentRecordReference(reference: AgentRecordReference): string {
|
|
100
|
+
const kind = titleCaseWords(reference.kind);
|
|
101
|
+
const label = reference.label?.trim();
|
|
102
|
+
return `${kind}: ${reference.ref}${label ? ` (${label})` : ''}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function formatAgentRecordReferences(references: readonly AgentRecordReference[]): string {
|
|
106
|
+
return references.map(formatAgentRecordReference).join(', ');
|
|
107
|
+
}
|
|
@@ -22,20 +22,29 @@ function scheduleValue(preview: ReminderSchedulePreview): string {
|
|
|
22
22
|
return String(preview.payload.at);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function formatReminderScheduleFailureKind(kind: string): string {
|
|
26
|
+
if (kind === 'auth_required') return 'authorization required';
|
|
27
|
+
if (kind === 'connected_host_unavailable') return 'connected host unavailable';
|
|
28
|
+
if (kind === 'connected_host_route_unavailable') return 'connected host route unavailable';
|
|
29
|
+
if (kind === 'connected_host_error') return 'connected host error';
|
|
30
|
+
if (kind === 'version_mismatch') return 'version mismatch';
|
|
31
|
+
return kind.replace(/[_-]+/g, ' ');
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
export function formatReminderSchedulePreview(preview: ReminderSchedulePreview): string {
|
|
26
35
|
const delivery = preview.payload.delivery;
|
|
27
36
|
const deliveryTargetCount = delivery?.targets.length ?? 0;
|
|
28
37
|
return [
|
|
29
38
|
'GoodVibes schedule preview for Agent reminder',
|
|
30
|
-
` route
|
|
31
|
-
` name
|
|
32
|
-
` reminder
|
|
33
|
-
` schedule
|
|
34
|
-
` enabled
|
|
35
|
-
` delivery
|
|
36
|
-
' target
|
|
37
|
-
' policy
|
|
38
|
-
' next
|
|
39
|
+
` route ${preview.method} ${preview.route}`,
|
|
40
|
+
` name ${String(preview.payload.name ?? '(service default)')}`,
|
|
41
|
+
` reminder ${preview.message}`,
|
|
42
|
+
` schedule ${preview.payload.kind} ${scheduleValue(preview)}`,
|
|
43
|
+
` enabled ${preview.payload.enabled === false ? 'no' : 'yes'}`,
|
|
44
|
+
` delivery ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
|
|
45
|
+
' target connected GoodVibes host/main conversation route',
|
|
46
|
+
' policy reminder delivery only; isolated Agent Knowledge only; no default knowledge/non-Agent fallback',
|
|
47
|
+
' next rerun with --yes to create this connected reminder schedule',
|
|
39
48
|
].join('\n');
|
|
40
49
|
}
|
|
41
50
|
|
|
@@ -45,31 +54,31 @@ export function formatReminderScheduleSuccess(result: ReminderScheduleSuccess):
|
|
|
45
54
|
const status = readString(record, 'status') ?? (record.enabled === false ? 'paused' : 'enabled');
|
|
46
55
|
return [
|
|
47
56
|
'Created GoodVibes schedule for Agent reminder',
|
|
48
|
-
` schedule
|
|
49
|
-
` status
|
|
50
|
-
` route
|
|
51
|
-
` reminder
|
|
52
|
-
' next
|
|
57
|
+
` schedule ${id}`,
|
|
58
|
+
` status ${status}`,
|
|
59
|
+
` route ${result.kind} ${result.route}`,
|
|
60
|
+
` reminder ${result.message}`,
|
|
61
|
+
' next inspect with /schedule list',
|
|
53
62
|
].join('\n');
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
export function formatReminderScheduleFailure(failure: ReminderScheduleFailure): string {
|
|
57
66
|
return [
|
|
58
|
-
`GoodVibes reminder schedule error
|
|
67
|
+
`GoodVibes reminder schedule error ${formatReminderScheduleFailureKind(failure.kind)}`,
|
|
59
68
|
` ${failure.error}`,
|
|
60
|
-
failure.baseUrl ? ` host
|
|
61
|
-
` route
|
|
62
|
-
failure.kind === 'version_mismatch' && failure.
|
|
63
|
-
? ` versions
|
|
69
|
+
failure.baseUrl ? ` connected host ${failure.baseUrl}` : null,
|
|
70
|
+
` route ${REMINDER_SCHEDULE_METHOD} ${failure.route}`,
|
|
71
|
+
failure.kind === 'version_mismatch' && failure.connectedHostVersion && failure.expectedSdkVersion
|
|
72
|
+
? ` versions connected host ${failure.connectedHostVersion}; expected ${failure.expectedSdkVersion}`
|
|
64
73
|
: null,
|
|
65
74
|
failure.kind === 'auth_required'
|
|
66
|
-
? ' next
|
|
75
|
+
? ' next pair/authenticate with the connected GoodVibes host, then retry with --yes.'
|
|
67
76
|
: null,
|
|
68
|
-
failure.kind === '
|
|
69
|
-
? ' next
|
|
77
|
+
failure.kind === 'connected_host_unavailable'
|
|
78
|
+
? ' next make the connected GoodVibes host available outside Agent, then retry.'
|
|
70
79
|
: null,
|
|
71
|
-
failure.kind === 'version_mismatch' || failure.kind === '
|
|
72
|
-
? ' next
|
|
80
|
+
failure.kind === 'version_mismatch' || failure.kind === 'connected_host_route_unavailable'
|
|
81
|
+
? ' next update the connected GoodVibes host so public schedules.create is available.'
|
|
73
82
|
: null,
|
|
74
83
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
75
84
|
}
|
|
@@ -4,11 +4,11 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
4
4
|
import { getModelIdFromProviderModel, getProviderIdFromModel } from '../config/provider-model.ts';
|
|
5
5
|
import { SDK_VERSION } from '../version.ts';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
resolveAgentConnectedHostConnection,
|
|
8
8
|
ROUTINE_SCHEDULE_METHOD,
|
|
9
9
|
ROUTINE_SCHEDULE_ROUTE,
|
|
10
|
-
type
|
|
11
|
-
type
|
|
10
|
+
type AgentConnectedHostConfigReader,
|
|
11
|
+
type AgentConnectedHostConnection,
|
|
12
12
|
type RoutineScheduleDeliveryKind,
|
|
13
13
|
type RoutineScheduleDeliverySurfaceKind,
|
|
14
14
|
type RoutineScheduleDeliveryTargetSpec,
|
|
@@ -58,14 +58,14 @@ export interface ReminderScheduleFailure {
|
|
|
58
58
|
readonly kind:
|
|
59
59
|
| 'confirmation_required'
|
|
60
60
|
| 'auth_required'
|
|
61
|
-
| '
|
|
61
|
+
| 'connected_host_unavailable'
|
|
62
62
|
| 'version_mismatch'
|
|
63
|
-
| '
|
|
64
|
-
| '
|
|
63
|
+
| 'connected_host_route_unavailable'
|
|
64
|
+
| 'connected_host_error';
|
|
65
65
|
readonly error: string;
|
|
66
66
|
readonly route: typeof REMINDER_SCHEDULE_ROUTE;
|
|
67
67
|
readonly baseUrl?: string;
|
|
68
|
-
readonly
|
|
68
|
+
readonly connectedHostVersion?: string;
|
|
69
69
|
readonly expectedSdkVersion?: string;
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -82,6 +82,7 @@ const DELIVERY_SURFACE_KINDS: readonly RoutineScheduleDeliverySurfaceKind[] = [
|
|
|
82
82
|
'google-chat',
|
|
83
83
|
'signal',
|
|
84
84
|
'whatsapp',
|
|
85
|
+
'telephony',
|
|
85
86
|
'imessage',
|
|
86
87
|
'msteams',
|
|
87
88
|
'bluebubbles',
|
|
@@ -186,7 +187,7 @@ function deliveryModeFromTargets(targets: readonly RoutineScheduleDeliveryTarget
|
|
|
186
187
|
function toDeliveryTargetInput(target: RoutineScheduleDeliveryTargetSpec): ScheduleDeliveryTargetInput {
|
|
187
188
|
return {
|
|
188
189
|
kind: target.kind,
|
|
189
|
-
surfaceKind: target.surfaceKind,
|
|
190
|
+
surfaceKind: target.surfaceKind as ScheduleDeliveryTargetInput['surfaceKind'],
|
|
190
191
|
address: target.address,
|
|
191
192
|
routeId: target.routeId,
|
|
192
193
|
label: target.label,
|
|
@@ -312,7 +313,7 @@ export function parseReminderScheduleArgs(args: readonly string[]): ParsedRemind
|
|
|
312
313
|
continue;
|
|
313
314
|
}
|
|
314
315
|
if (raw.startsWith('--')) {
|
|
315
|
-
errors.push(`Unknown option
|
|
316
|
+
errors.push(`Unknown option ${raw}`);
|
|
316
317
|
continue;
|
|
317
318
|
}
|
|
318
319
|
positional.push(raw);
|
|
@@ -335,9 +336,9 @@ export function buildReminderSchedulePrompt(message: string): string {
|
|
|
335
336
|
'',
|
|
336
337
|
'Operator policy:',
|
|
337
338
|
'- Treat this as a reminder delivery, not an autonomous hidden workflow.',
|
|
338
|
-
'- Use isolated Agent Knowledge routes only when lookup is needed; never use default
|
|
339
|
+
'- Use isolated Agent Knowledge routes only when lookup is needed; never use default knowledge or non-Agent knowledge spaces as fallback.',
|
|
339
340
|
'- Do not perform destructive, costly, externally visible, or secret-handling actions from this reminder without explicit approval.',
|
|
340
|
-
'- Do not request
|
|
341
|
+
'- Do not request GoodVibes TUI delegation from a reminder. If build/fix/review work is needed, ask the user to delegate it explicitly to GoodVibes TUI.',
|
|
341
342
|
'- Keep the reminder concise and state any next action the user can take.',
|
|
342
343
|
].join('\n');
|
|
343
344
|
}
|
|
@@ -406,7 +407,7 @@ export function buildReminderSchedulePreview(parsed: ParsedReminderScheduleArgs)
|
|
|
406
407
|
};
|
|
407
408
|
}
|
|
408
409
|
|
|
409
|
-
async function
|
|
410
|
+
async function fetchConnectedHostStatus(connection: AgentConnectedHostConnection): Promise<{
|
|
410
411
|
readonly ok: boolean;
|
|
411
412
|
readonly status: number;
|
|
412
413
|
readonly body: unknown;
|
|
@@ -430,7 +431,7 @@ async function fetchDaemonStatus(connection: AgentDaemonConnection): Promise<{
|
|
|
430
431
|
|
|
431
432
|
async function classifyReminderScheduleError(
|
|
432
433
|
error: unknown,
|
|
433
|
-
connection:
|
|
434
|
+
connection: AgentConnectedHostConnection,
|
|
434
435
|
): Promise<ReminderScheduleFailure> {
|
|
435
436
|
const message = summarizeError(error);
|
|
436
437
|
const lower = message.toLowerCase();
|
|
@@ -438,30 +439,30 @@ async function classifyReminderScheduleError(
|
|
|
438
439
|
return { ok: false, kind: 'auth_required', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
|
|
439
440
|
}
|
|
440
441
|
if (lower.includes('404') || lower.includes('not found')) {
|
|
441
|
-
const
|
|
442
|
-
const record = isRecord(
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
442
|
+
const connectedHost = await fetchConnectedHostStatus(connection);
|
|
443
|
+
const record = isRecord(connectedHost.body) ? connectedHost.body : {};
|
|
444
|
+
const connectedHostVersion = readString(record, 'version') ?? 'unknown';
|
|
445
|
+
if (connectedHost.ok && connectedHostVersion !== SDK_VERSION) {
|
|
445
446
|
return {
|
|
446
447
|
ok: false,
|
|
447
448
|
kind: 'version_mismatch',
|
|
448
|
-
error: `Connected GoodVibes
|
|
449
|
+
error: `Connected GoodVibes host SDK version ${connectedHostVersion} does not match Agent SDK pin ${SDK_VERSION}; schedules.create is unavailable.`,
|
|
449
450
|
route: REMINDER_SCHEDULE_ROUTE,
|
|
450
451
|
baseUrl: connection.baseUrl,
|
|
451
|
-
|
|
452
|
+
connectedHostVersion,
|
|
452
453
|
expectedSdkVersion: SDK_VERSION,
|
|
453
454
|
};
|
|
454
455
|
}
|
|
455
|
-
return { ok: false, kind: '
|
|
456
|
+
return { ok: false, kind: 'connected_host_route_unavailable', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
|
|
456
457
|
}
|
|
457
458
|
if (lower.includes('fetch') || lower.includes('connect') || lower.includes('econnrefused')) {
|
|
458
|
-
return { ok: false, kind: '
|
|
459
|
+
return { ok: false, kind: 'connected_host_unavailable', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
|
|
459
460
|
}
|
|
460
|
-
return { ok: false, kind: '
|
|
461
|
+
return { ok: false, kind: 'connected_host_error', error: message, route: REMINDER_SCHEDULE_ROUTE, baseUrl: connection.baseUrl };
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
export async function createReminderSchedule(
|
|
464
|
-
connection:
|
|
465
|
+
connection: AgentConnectedHostConnection,
|
|
465
466
|
preview: ReminderSchedulePreview,
|
|
466
467
|
): Promise<ReminderScheduleResult> {
|
|
467
468
|
if (!connection.token) {
|
|
@@ -489,6 +490,6 @@ export async function createReminderSchedule(
|
|
|
489
490
|
}
|
|
490
491
|
}
|
|
491
492
|
|
|
492
|
-
export function
|
|
493
|
-
return
|
|
493
|
+
export function resolveReminderConnectedHostConnection(configManager: AgentConnectedHostConfigReader, homeDirectory: string): AgentConnectedHostConnection {
|
|
494
|
+
return resolveAgentConnectedHostConnection(configManager, homeDirectory);
|
|
494
495
|
}
|
|
@@ -3,6 +3,7 @@ import { dirname } from 'node:path';
|
|
|
3
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
4
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
5
5
|
import { assertNoSecretLikeText } from './persona-registry.ts';
|
|
6
|
+
import { formatAgentRecordReviewState } from './record-labels.ts';
|
|
6
7
|
import {
|
|
7
8
|
evaluateAgentSkillReadiness,
|
|
8
9
|
formatAgentSkillRequirement,
|
|
@@ -228,9 +229,9 @@ export class AgentRoutineRegistry {
|
|
|
228
229
|
const steps = input.steps.trim();
|
|
229
230
|
this.validateRequired(name, description, steps);
|
|
230
231
|
const requirements = normalizeAgentSkillRequirements(input.requirements);
|
|
231
|
-
assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
|
|
232
|
+
assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
|
|
232
233
|
const duplicate = store.routines.find((routine) => routine.name.toLowerCase() === name.toLowerCase());
|
|
233
|
-
if (duplicate) throw new Error(`Routine already exists
|
|
234
|
+
if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
|
|
234
235
|
const timestamp = nowIso();
|
|
235
236
|
const routine: AgentRoutineRecord = {
|
|
236
237
|
id: this.nextId(name, store.routines),
|
|
@@ -255,15 +256,15 @@ export class AgentRoutineRegistry {
|
|
|
255
256
|
public update(idOrName: string, input: AgentRoutineUpdateInput): AgentRoutineRecord {
|
|
256
257
|
const store = this.readStore();
|
|
257
258
|
const existing = this.findInStore(store, idOrName);
|
|
258
|
-
if (!existing) throw new Error(`Unknown routine
|
|
259
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
259
260
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
260
261
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
261
262
|
const steps = input.steps === undefined ? existing.steps : input.steps.trim();
|
|
262
263
|
this.validateRequired(name, description, steps);
|
|
263
264
|
const requirements = input.requirements === undefined ? existing.requirements : normalizeAgentSkillRequirements(input.requirements);
|
|
264
|
-
assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
|
|
265
|
+
assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
|
|
265
266
|
const duplicate = store.routines.find((routine) => routine.id !== existing.id && routine.name.toLowerCase() === name.toLowerCase());
|
|
266
|
-
if (duplicate) throw new Error(`Routine already exists
|
|
267
|
+
if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
|
|
267
268
|
const updated: AgentRoutineRecord = {
|
|
268
269
|
...existing,
|
|
269
270
|
name,
|
|
@@ -288,7 +289,7 @@ export class AgentRoutineRegistry {
|
|
|
288
289
|
public setEnabled(idOrName: string, enabled: boolean): AgentRoutineRecord {
|
|
289
290
|
const store = this.readStore();
|
|
290
291
|
const existing = this.findInStore(store, idOrName);
|
|
291
|
-
if (!existing) throw new Error(`Unknown routine
|
|
292
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
292
293
|
const updated: AgentRoutineRecord = { ...existing, enabled, updatedAt: nowIso() };
|
|
293
294
|
this.writeStore({
|
|
294
295
|
...store,
|
|
@@ -300,7 +301,7 @@ export class AgentRoutineRegistry {
|
|
|
300
301
|
public markStarted(idOrName: string): AgentRoutineRecord {
|
|
301
302
|
const store = this.readStore();
|
|
302
303
|
const existing = this.findInStore(store, idOrName);
|
|
303
|
-
if (!existing) throw new Error(`Unknown routine
|
|
304
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
304
305
|
const timestamp = nowIso();
|
|
305
306
|
const updated: AgentRoutineRecord = {
|
|
306
307
|
...existing,
|
|
@@ -318,7 +319,7 @@ export class AgentRoutineRegistry {
|
|
|
318
319
|
public markReviewed(idOrName: string): AgentRoutineRecord {
|
|
319
320
|
const store = this.readStore();
|
|
320
321
|
const existing = this.findInStore(store, idOrName);
|
|
321
|
-
if (!existing) throw new Error(`Unknown routine
|
|
322
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
322
323
|
const updated: AgentRoutineRecord = {
|
|
323
324
|
...existing,
|
|
324
325
|
reviewState: 'reviewed',
|
|
@@ -336,7 +337,7 @@ export class AgentRoutineRegistry {
|
|
|
336
337
|
public markStale(idOrName: string, reason: string): AgentRoutineRecord {
|
|
337
338
|
const store = this.readStore();
|
|
338
339
|
const existing = this.findInStore(store, idOrName);
|
|
339
|
-
if (!existing) throw new Error(`Unknown routine
|
|
340
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
340
341
|
const updated: AgentRoutineRecord = {
|
|
341
342
|
...existing,
|
|
342
343
|
reviewState: 'stale',
|
|
@@ -353,7 +354,7 @@ export class AgentRoutineRegistry {
|
|
|
353
354
|
public deleteRoutine(idOrName: string): AgentRoutineRecord {
|
|
354
355
|
const store = this.readStore();
|
|
355
356
|
const existing = this.findInStore(store, idOrName);
|
|
356
|
-
if (!existing) throw new Error(`Unknown routine
|
|
357
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
357
358
|
this.writeStore({
|
|
358
359
|
...store,
|
|
359
360
|
routines: store.routines.filter((routine) => routine.id !== existing.id),
|
|
@@ -389,7 +390,7 @@ export class AgentRoutineRegistry {
|
|
|
389
390
|
try {
|
|
390
391
|
return parseStore(readFileSync(this.storePath, 'utf-8'));
|
|
391
392
|
} catch (error) {
|
|
392
|
-
throw new Error(`Could not read Agent routine store
|
|
393
|
+
throw new Error(`Could not read Agent routine store ${error instanceof Error ? error.message : String(error)}`);
|
|
393
394
|
}
|
|
394
395
|
}
|
|
395
396
|
|
|
@@ -418,7 +419,7 @@ export function buildEnabledRoutinesPrompt(shellPaths: ShellPathService): string
|
|
|
418
419
|
...enabled.slice(0, 8).flatMap((routine) => [
|
|
419
420
|
`### ${routine.name}`,
|
|
420
421
|
`Description: ${routine.description}`,
|
|
421
|
-
`Review
|
|
422
|
+
`Review: ${formatAgentRecordReviewState(routine.reviewState)}`,
|
|
422
423
|
`Triggers: ${routine.triggers.join(', ') || '(manual)'}`,
|
|
423
424
|
`Readiness: ${evaluateAgentRoutineReadiness(routine).ready ? 'ready' : `missing ${evaluateAgentRoutineReadiness(routine).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
|
|
424
425
|
routine.steps,
|
|
@@ -15,6 +15,7 @@ const DELIVERY_SURFACE_KINDS: readonly RoutineScheduleDeliverySurfaceKind[] = [
|
|
|
15
15
|
'google-chat',
|
|
16
16
|
'signal',
|
|
17
17
|
'whatsapp',
|
|
18
|
+
'telephony',
|
|
18
19
|
'imessage',
|
|
19
20
|
'msteams',
|
|
20
21
|
'bluebubbles',
|
|
@@ -204,7 +205,7 @@ export function parseRoutineSchedulePromotionArgs(args: readonly string[]): Pars
|
|
|
204
205
|
continue;
|
|
205
206
|
}
|
|
206
207
|
if (raw.startsWith('--')) {
|
|
207
|
-
errors.push(`Unknown option
|
|
208
|
+
errors.push(`Unknown option ${raw}`);
|
|
208
209
|
continue;
|
|
209
210
|
}
|
|
210
211
|
if (!routineId) {
|