@pellux/goodvibes-agent 0.1.116 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -661
- package/README.md +64 -57
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +199180 -192185
- package/docs/README.md +15 -8
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +59 -0
- package/docs/getting-started.md +61 -50
- 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 +48 -11
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +3 -6
- package/src/agent/behavior-discovery-summary.ts +1 -3
- 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-discovery.ts +1 -3
- package/src/agent/persona-registry.ts +19 -16
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +27 -26
- package/src/agent/routine-registry.ts +54 -14
- 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 +35 -33
- package/src/agent/routine-schedule-receipts.ts +29 -27
- package/src/agent/runtime-profile-starters.ts +5 -5
- package/src/agent/runtime-profile.ts +91 -12
- package/src/agent/skill-registry.ts +210 -32
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +40 -34
- 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 +14 -9
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/entrypoint.ts +16 -7
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +54 -29
- package/src/cli/local-library-command.ts +203 -83
- package/src/cli/management-commands.ts +105 -68
- package/src/cli/management.ts +55 -74
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -3
- package/src/cli/profiles-command.ts +166 -31
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +176 -40
- package/src/cli/service-posture.ts +16 -16
- package/src/cli/status.ts +56 -135
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
- package/src/input/agent-workspace-access-command-editors.ts +170 -0
- package/src/input/agent-workspace-activation.ts +35 -8
- package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
- package/src/input/agent-workspace-basic-command-editors.ts +502 -9
- package/src/input/agent-workspace-categories.ts +423 -51
- package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
- package/src/input/agent-workspace-channel-command-editors.ts +65 -0
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +41 -3
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
- package/src/input/agent-workspace-editors.ts +166 -1
- package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
- package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +67 -0
- package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
- package/src/input/agent-workspace-library-command-editors.ts +35 -0
- 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-mcp-command-editor-submission.ts +95 -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-editor-submission.ts +232 -0
- package/src/input/agent-workspace-memory-command-editors.ts +180 -0
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +81 -0
- package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
- package/src/input/agent-workspace-operations-command-editors.ts +196 -0
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
- package/src/input/agent-workspace-provider-command-editors.ts +93 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-requirements.ts +11 -0
- 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-secret-editor-submission.ts +153 -0
- package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
- package/src/input/agent-workspace-session-command-editors.ts +249 -0
- package/src/input/agent-workspace-setup.ts +37 -16
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
- package/src/input/agent-workspace-snapshot.ts +162 -21
- package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
- package/src/input/agent-workspace-task-command-editors.ts +47 -0
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +212 -4
- package/src/input/agent-workspace-voice-media.ts +4 -7
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
- package/src/input/agent-workspace.ts +178 -240
- package/src/input/command-registry.ts +5 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
- package/src/input/commands/agent-skills-runtime.ts +149 -72
- package/src/input/commands/agent-workspace-runtime.ts +23 -6
- package/src/input/commands/brief-runtime.ts +55 -25
- package/src/input/commands/channels-runtime.ts +147 -33
- package/src/input/commands/compat-runtime.ts +32 -0
- 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 +149 -126
- package/src/input/commands/knowledge.ts +82 -60
- package/src/input/commands/local-provider-runtime.ts +43 -21
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +10 -20
- package/src/input/commands/mcp-runtime.ts +63 -46
- 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 -76
- package/src/input/commands/personas-runtime.ts +46 -31
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +35 -98
- package/src/input/commands/product-runtime.ts +2 -2
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +50 -9
- 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 +99 -45
- package/src/input/commands/runtime-services.ts +1 -14
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +6 -11
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +134 -104
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +38 -17
- package/src/input/commands/subscription-runtime.ts +54 -185
- package/src/input/commands/support-bundle-runtime.ts +53 -0
- package/src/input/commands/tasks-runtime.ts +21 -21
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +6 -2
- package/src/input/feed-context-factory.ts +2 -2
- 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 -26
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -5
- package/src/input/handler-interactions.ts +9 -36
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +52 -91
- package/src/input/handler-shortcuts.ts +16 -7
- package/src/input/handler.ts +50 -23
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +27 -16
- package/src/input/mcp-workspace.ts +90 -26
- package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
- package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
- package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
- package/src/input/onboarding/onboarding-wizard.ts +87 -2
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-agent-policy.ts +6 -6
- 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 +20 -66
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -10
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +4 -4
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +3 -12
- package/src/panels/builtin/session.ts +32 -24
- package/src/panels/builtin/shared.ts +7 -7
- 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 +19 -12
- 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 +48 -7
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +44 -20
- 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 +6 -6
- package/src/panels/system-messages-panel.ts +3 -3
- package/src/panels/tasks-panel.ts +21 -14
- 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 +3 -3
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +254 -57
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/block-actions.ts +1 -3
- 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 +112 -34
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +176 -43
- package/src/renderer/model-picker-overlay.ts +58 -3
- package/src/renderer/model-workspace.ts +104 -22
- package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +21 -5
- 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 +88 -55
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +129 -0
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +11 -8
- package/src/runtime/bootstrap-core.ts +23 -65
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +46 -8
- package/src/runtime/bootstrap.ts +36 -46
- package/src/runtime/connected-host-auth.ts +47 -0
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -5
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +288 -74
- package/src/runtime/onboarding/derivation.ts +10 -13
- package/src/runtime/onboarding/snapshot.ts +2 -17
- package/src/runtime/onboarding/types.ts +34 -20
- package/src/runtime/onboarding/verify.ts +105 -4
- package/src/runtime/operator-token-cleanup.ts +3 -3
- package/src/runtime/services.ts +95 -11
- 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/service-settings-sync.ts +7 -7
- package/src/shell/ui-openers.ts +17 -38
- 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 +279 -73
- 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 +13 -15
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/docs/connected-services.md +0 -51
- package/src/cli/package-verification.ts +0 -274
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -17
- 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
|
@@ -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,37 +439,37 @@ 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) {
|
|
468
469
|
return {
|
|
469
470
|
ok: false,
|
|
470
471
|
kind: 'auth_required',
|
|
471
|
-
error: `No
|
|
472
|
+
error: `No connected-host operator token found at ${connection.tokenPath}`,
|
|
472
473
|
route: REMINDER_SCHEDULE_ROUTE,
|
|
473
474
|
baseUrl: connection.baseUrl,
|
|
474
475
|
};
|
|
@@ -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,9 +3,19 @@ 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';
|
|
7
|
+
import {
|
|
8
|
+
evaluateAgentSkillReadiness,
|
|
9
|
+
formatAgentSkillRequirement,
|
|
10
|
+
normalizeAgentSkillRequirements,
|
|
11
|
+
type AgentSkillReadiness,
|
|
12
|
+
type AgentSkillRequirement,
|
|
13
|
+
} from './skill-registry.ts';
|
|
6
14
|
|
|
7
15
|
export type AgentRoutineSource = 'user' | 'agent' | 'imported' | 'system';
|
|
8
16
|
export type AgentRoutineReviewState = 'fresh' | 'reviewed' | 'stale';
|
|
17
|
+
export type AgentRoutineRequirement = AgentSkillRequirement;
|
|
18
|
+
export type AgentRoutineReadiness = AgentSkillReadiness;
|
|
9
19
|
|
|
10
20
|
export interface AgentRoutineRecord {
|
|
11
21
|
readonly id: string;
|
|
@@ -14,6 +24,7 @@ export interface AgentRoutineRecord {
|
|
|
14
24
|
readonly steps: string;
|
|
15
25
|
readonly triggers: readonly string[];
|
|
16
26
|
readonly tags: readonly string[];
|
|
27
|
+
readonly requirements: readonly AgentRoutineRequirement[];
|
|
17
28
|
readonly enabled: boolean;
|
|
18
29
|
readonly source: AgentRoutineSource;
|
|
19
30
|
readonly provenance: string;
|
|
@@ -32,6 +43,7 @@ export interface AgentRoutineCreateInput {
|
|
|
32
43
|
readonly steps: string;
|
|
33
44
|
readonly triggers?: readonly string[];
|
|
34
45
|
readonly tags?: readonly string[];
|
|
46
|
+
readonly requirements?: readonly AgentRoutineRequirement[];
|
|
35
47
|
readonly enabled?: boolean;
|
|
36
48
|
readonly source?: AgentRoutineSource;
|
|
37
49
|
readonly provenance?: string;
|
|
@@ -43,6 +55,7 @@ export interface AgentRoutineUpdateInput {
|
|
|
43
55
|
readonly steps?: string;
|
|
44
56
|
readonly triggers?: readonly string[];
|
|
45
57
|
readonly tags?: readonly string[];
|
|
58
|
+
readonly requirements?: readonly AgentRoutineRequirement[];
|
|
46
59
|
readonly provenance?: string;
|
|
47
60
|
}
|
|
48
61
|
|
|
@@ -72,6 +85,18 @@ function readStringArray(value: unknown): string[] {
|
|
|
72
85
|
return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
|
|
73
86
|
}
|
|
74
87
|
|
|
88
|
+
function readRequirementArray(value: unknown): readonly AgentRoutineRequirement[] {
|
|
89
|
+
if (!Array.isArray(value)) return [];
|
|
90
|
+
const requirements = value
|
|
91
|
+
.filter(isRecord)
|
|
92
|
+
.map((entry) => ({
|
|
93
|
+
kind: entry.kind === 'command' ? 'command' as const : 'env' as const,
|
|
94
|
+
name: readString(entry.name).trim(),
|
|
95
|
+
description: readString(entry.description).trim() || undefined,
|
|
96
|
+
}));
|
|
97
|
+
return normalizeAgentSkillRequirements(requirements);
|
|
98
|
+
}
|
|
99
|
+
|
|
75
100
|
function readNonNegativeInteger(value: unknown): number {
|
|
76
101
|
const parsed = typeof value === 'number' ? value : Number(value);
|
|
77
102
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
|
|
@@ -123,6 +148,7 @@ function parseRoutine(value: unknown): AgentRoutineRecord | null {
|
|
|
123
148
|
steps,
|
|
124
149
|
triggers: readStringArray(value.triggers),
|
|
125
150
|
tags: readStringArray(value.tags),
|
|
151
|
+
requirements: readRequirementArray(value.requirements),
|
|
126
152
|
enabled: value.enabled === true,
|
|
127
153
|
source,
|
|
128
154
|
provenance: readString(value.provenance, source).trim() || source,
|
|
@@ -151,14 +177,16 @@ function formatStore(store: RoutineStoreFile): string {
|
|
|
151
177
|
return `${JSON.stringify(store, null, 2)}\n`;
|
|
152
178
|
}
|
|
153
179
|
|
|
154
|
-
|
|
180
|
+
type AgentLocalStorePaths = Pick<ShellPathService, 'resolveUserPath'>;
|
|
181
|
+
|
|
182
|
+
export function routineStorePath(shellPaths: AgentLocalStorePaths): string {
|
|
155
183
|
return shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'routines', 'routines.json');
|
|
156
184
|
}
|
|
157
185
|
|
|
158
186
|
export class AgentRoutineRegistry {
|
|
159
187
|
public constructor(private readonly storePath: string) {}
|
|
160
188
|
|
|
161
|
-
public static fromShellPaths(shellPaths:
|
|
189
|
+
public static fromShellPaths(shellPaths: AgentLocalStorePaths): AgentRoutineRegistry {
|
|
162
190
|
return new AgentRoutineRegistry(routineStorePath(shellPaths));
|
|
163
191
|
}
|
|
164
192
|
|
|
@@ -200,9 +228,10 @@ export class AgentRoutineRegistry {
|
|
|
200
228
|
const description = input.description.trim();
|
|
201
229
|
const steps = input.steps.trim();
|
|
202
230
|
this.validateRequired(name, description, steps);
|
|
203
|
-
|
|
231
|
+
const requirements = normalizeAgentSkillRequirements(input.requirements);
|
|
232
|
+
assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
|
|
204
233
|
const duplicate = store.routines.find((routine) => routine.name.toLowerCase() === name.toLowerCase());
|
|
205
|
-
if (duplicate) throw new Error(`Routine already exists
|
|
234
|
+
if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
|
|
206
235
|
const timestamp = nowIso();
|
|
207
236
|
const routine: AgentRoutineRecord = {
|
|
208
237
|
id: this.nextId(name, store.routines),
|
|
@@ -211,6 +240,7 @@ export class AgentRoutineRegistry {
|
|
|
211
240
|
steps,
|
|
212
241
|
triggers: normalizeList(input.triggers),
|
|
213
242
|
tags: normalizeList(input.tags),
|
|
243
|
+
requirements,
|
|
214
244
|
enabled: input.enabled === true,
|
|
215
245
|
source: input.source ?? 'user',
|
|
216
246
|
provenance: input.provenance?.trim() || input.source || 'user',
|
|
@@ -226,14 +256,15 @@ export class AgentRoutineRegistry {
|
|
|
226
256
|
public update(idOrName: string, input: AgentRoutineUpdateInput): AgentRoutineRecord {
|
|
227
257
|
const store = this.readStore();
|
|
228
258
|
const existing = this.findInStore(store, idOrName);
|
|
229
|
-
if (!existing) throw new Error(`Unknown routine
|
|
259
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
230
260
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
231
261
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
232
262
|
const steps = input.steps === undefined ? existing.steps : input.steps.trim();
|
|
233
263
|
this.validateRequired(name, description, steps);
|
|
234
|
-
|
|
264
|
+
const requirements = input.requirements === undefined ? existing.requirements : normalizeAgentSkillRequirements(input.requirements);
|
|
265
|
+
assertNoSecretLikeText([name, description, steps, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Routines');
|
|
235
266
|
const duplicate = store.routines.find((routine) => routine.id !== existing.id && routine.name.toLowerCase() === name.toLowerCase());
|
|
236
|
-
if (duplicate) throw new Error(`Routine already exists
|
|
267
|
+
if (duplicate) throw new Error(`Routine already exists ${duplicate.id}`);
|
|
237
268
|
const updated: AgentRoutineRecord = {
|
|
238
269
|
...existing,
|
|
239
270
|
name,
|
|
@@ -241,6 +272,7 @@ export class AgentRoutineRegistry {
|
|
|
241
272
|
steps,
|
|
242
273
|
triggers: input.triggers === undefined ? existing.triggers : normalizeList(input.triggers),
|
|
243
274
|
tags: input.tags === undefined ? existing.tags : normalizeList(input.tags),
|
|
275
|
+
requirements,
|
|
244
276
|
provenance: input.provenance === undefined ? existing.provenance : input.provenance.trim() || existing.provenance,
|
|
245
277
|
reviewState: 'fresh',
|
|
246
278
|
staleReason: undefined,
|
|
@@ -257,7 +289,7 @@ export class AgentRoutineRegistry {
|
|
|
257
289
|
public setEnabled(idOrName: string, enabled: boolean): AgentRoutineRecord {
|
|
258
290
|
const store = this.readStore();
|
|
259
291
|
const existing = this.findInStore(store, idOrName);
|
|
260
|
-
if (!existing) throw new Error(`Unknown routine
|
|
292
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
261
293
|
const updated: AgentRoutineRecord = { ...existing, enabled, updatedAt: nowIso() };
|
|
262
294
|
this.writeStore({
|
|
263
295
|
...store,
|
|
@@ -269,7 +301,7 @@ export class AgentRoutineRegistry {
|
|
|
269
301
|
public markStarted(idOrName: string): AgentRoutineRecord {
|
|
270
302
|
const store = this.readStore();
|
|
271
303
|
const existing = this.findInStore(store, idOrName);
|
|
272
|
-
if (!existing) throw new Error(`Unknown routine
|
|
304
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
273
305
|
const timestamp = nowIso();
|
|
274
306
|
const updated: AgentRoutineRecord = {
|
|
275
307
|
...existing,
|
|
@@ -287,7 +319,7 @@ export class AgentRoutineRegistry {
|
|
|
287
319
|
public markReviewed(idOrName: string): AgentRoutineRecord {
|
|
288
320
|
const store = this.readStore();
|
|
289
321
|
const existing = this.findInStore(store, idOrName);
|
|
290
|
-
if (!existing) throw new Error(`Unknown routine
|
|
322
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
291
323
|
const updated: AgentRoutineRecord = {
|
|
292
324
|
...existing,
|
|
293
325
|
reviewState: 'reviewed',
|
|
@@ -305,7 +337,7 @@ export class AgentRoutineRegistry {
|
|
|
305
337
|
public markStale(idOrName: string, reason: string): AgentRoutineRecord {
|
|
306
338
|
const store = this.readStore();
|
|
307
339
|
const existing = this.findInStore(store, idOrName);
|
|
308
|
-
if (!existing) throw new Error(`Unknown routine
|
|
340
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
309
341
|
const updated: AgentRoutineRecord = {
|
|
310
342
|
...existing,
|
|
311
343
|
reviewState: 'stale',
|
|
@@ -322,7 +354,7 @@ export class AgentRoutineRegistry {
|
|
|
322
354
|
public deleteRoutine(idOrName: string): AgentRoutineRecord {
|
|
323
355
|
const store = this.readStore();
|
|
324
356
|
const existing = this.findInStore(store, idOrName);
|
|
325
|
-
if (!existing) throw new Error(`Unknown routine
|
|
357
|
+
if (!existing) throw new Error(`Unknown routine ${idOrName}`);
|
|
326
358
|
this.writeStore({
|
|
327
359
|
...store,
|
|
328
360
|
routines: store.routines.filter((routine) => routine.id !== existing.id),
|
|
@@ -358,7 +390,7 @@ export class AgentRoutineRegistry {
|
|
|
358
390
|
try {
|
|
359
391
|
return parseStore(readFileSync(this.storePath, 'utf-8'));
|
|
360
392
|
} catch (error) {
|
|
361
|
-
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)}`);
|
|
362
394
|
}
|
|
363
395
|
}
|
|
364
396
|
|
|
@@ -370,6 +402,13 @@ export class AgentRoutineRegistry {
|
|
|
370
402
|
}
|
|
371
403
|
}
|
|
372
404
|
|
|
405
|
+
export function evaluateAgentRoutineReadiness(
|
|
406
|
+
routine: Pick<AgentRoutineRecord, 'requirements'>,
|
|
407
|
+
options: Parameters<typeof evaluateAgentSkillReadiness>[1] = {},
|
|
408
|
+
): AgentRoutineReadiness {
|
|
409
|
+
return evaluateAgentSkillReadiness(routine, options);
|
|
410
|
+
}
|
|
411
|
+
|
|
373
412
|
export function buildEnabledRoutinesPrompt(shellPaths: ShellPathService): string | null {
|
|
374
413
|
const enabled = AgentRoutineRegistry.fromShellPaths(shellPaths).snapshot().enabledRoutines;
|
|
375
414
|
if (enabled.length === 0) return null;
|
|
@@ -380,8 +419,9 @@ export function buildEnabledRoutinesPrompt(shellPaths: ShellPathService): string
|
|
|
380
419
|
...enabled.slice(0, 8).flatMap((routine) => [
|
|
381
420
|
`### ${routine.name}`,
|
|
382
421
|
`Description: ${routine.description}`,
|
|
383
|
-
`Review
|
|
422
|
+
`Review: ${formatAgentRecordReviewState(routine.reviewState)}`,
|
|
384
423
|
`Triggers: ${routine.triggers.join(', ') || '(manual)'}`,
|
|
424
|
+
`Readiness: ${evaluateAgentRoutineReadiness(routine).ready ? 'ready' : `missing ${evaluateAgentRoutineReadiness(routine).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
|
|
385
425
|
routine.steps,
|
|
386
426
|
'',
|
|
387
427
|
]),
|
|
@@ -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) {
|
|
@@ -23,6 +23,30 @@ function formatDeliveryTargetKind(target: { readonly kind?: string; readonly sur
|
|
|
23
23
|
return `${target.kind ?? 'unknown'}${target.surfaceKind ? `/${target.surfaceKind}` : ''}`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function formatDeliveryTarget(target: {
|
|
27
|
+
readonly kind?: string;
|
|
28
|
+
readonly surfaceKind?: string;
|
|
29
|
+
readonly routeId?: string;
|
|
30
|
+
readonly address?: string;
|
|
31
|
+
readonly label?: string;
|
|
32
|
+
}): string {
|
|
33
|
+
const details = [
|
|
34
|
+
target.routeId ? `route=${target.routeId}` : '',
|
|
35
|
+
target.address ? `address=${target.address}` : '',
|
|
36
|
+
target.label ? `label=${target.label}` : '',
|
|
37
|
+
].filter(Boolean);
|
|
38
|
+
return `${formatDeliveryTargetKind(target)}${details.length > 0 ? ` ${details.join(' ')}` : ''}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function formatRoutineScheduleFailureKind(kind: string): string {
|
|
42
|
+
if (kind === 'auth_required') return 'authorization required';
|
|
43
|
+
if (kind === 'connected_host_unavailable') return 'connected host unavailable';
|
|
44
|
+
if (kind === 'connected_host_route_unavailable') return 'connected host route unavailable';
|
|
45
|
+
if (kind === 'connected_host_error') return 'connected host error';
|
|
46
|
+
if (kind === 'version_mismatch') return 'version mismatch';
|
|
47
|
+
return kind.replace(/[_-]+/g, ' ');
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
export function formatRoutineSchedulePreview(preview: RoutineSchedulePromotionPreview): string {
|
|
27
51
|
const schedule = preview.payload.kind === 'cron'
|
|
28
52
|
? `${preview.payload.cron}${preview.payload.timezone ? ` [${preview.payload.timezone}]` : ''}`
|
|
@@ -33,15 +57,15 @@ export function formatRoutineSchedulePreview(preview: RoutineSchedulePromotionPr
|
|
|
33
57
|
const deliveryTargetCount = delivery?.targets.length ?? 0;
|
|
34
58
|
return [
|
|
35
59
|
'GoodVibes schedule preview for Agent routine',
|
|
36
|
-
` routine
|
|
37
|
-
` route
|
|
38
|
-
` name
|
|
39
|
-
` schedule
|
|
40
|
-
` enabled
|
|
60
|
+
` routine ${preview.routineName} (${preview.routineId})`,
|
|
61
|
+
` route ${preview.method} ${preview.route}`,
|
|
62
|
+
` name ${String(preview.payload.name ?? '(connected-host default)')}`,
|
|
63
|
+
` schedule ${preview.payload.kind} ${schedule}`,
|
|
64
|
+
` enabled ${preview.payload.enabled === false ? 'no' : 'yes'}`,
|
|
41
65
|
` delivery: ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
|
|
42
|
-
' target
|
|
43
|
-
' policy
|
|
44
|
-
' next
|
|
66
|
+
' target connected GoodVibes host/main conversation route',
|
|
67
|
+
' policy isolated Agent Knowledge only; no default knowledge/non-Agent fallback; no delegated review unless explicitly requested',
|
|
68
|
+
' next rerun with --yes to create this connected schedule',
|
|
45
69
|
].join('\n');
|
|
46
70
|
}
|
|
47
71
|
|
|
@@ -51,11 +75,11 @@ export function formatRoutineScheduleSuccess(result: RoutineSchedulePromotionSuc
|
|
|
51
75
|
const status = readString(record, 'status') ?? (record.enabled === false ? 'paused' : 'enabled');
|
|
52
76
|
return [
|
|
53
77
|
'Created GoodVibes schedule for Agent routine',
|
|
54
|
-
` routine
|
|
55
|
-
` schedule
|
|
56
|
-
` status
|
|
57
|
-
` route
|
|
58
|
-
' next
|
|
78
|
+
` routine ${result.routineName} (${result.routineId})`,
|
|
79
|
+
` schedule ${id}`,
|
|
80
|
+
` status ${status}`,
|
|
81
|
+
` route ${result.kind} ${result.route}`,
|
|
82
|
+
' next inspect with /schedule list or schedule observability',
|
|
59
83
|
].join('\n');
|
|
60
84
|
}
|
|
61
85
|
|
|
@@ -64,18 +88,18 @@ export function formatRoutineScheduleReceipts(snapshot: RoutineScheduleReceiptSn
|
|
|
64
88
|
if (snapshot.receipts.length === 0) {
|
|
65
89
|
return [
|
|
66
90
|
'Agent routine schedule receipts',
|
|
67
|
-
` store
|
|
91
|
+
` store ${snapshot.path}`,
|
|
68
92
|
' No routine schedule promotions have been recorded yet.',
|
|
69
93
|
' Create one with /schedule promote-routine <routine-id> --cron <expr> --yes.',
|
|
70
94
|
].join('\n');
|
|
71
95
|
}
|
|
72
96
|
return [
|
|
73
97
|
`Agent routine schedule receipts (${snapshot.receipts.length})`,
|
|
74
|
-
` store
|
|
98
|
+
` store ${snapshot.path}`,
|
|
75
99
|
...receipts.map((receipt) => {
|
|
76
100
|
const schedule = receipt.scheduleId ? ` schedule=${receipt.scheduleId}` : '';
|
|
77
|
-
const failure = receipt.status === 'failed' && receipt.failureKind ? ` failure
|
|
78
|
-
return ` ${receipt.id} ${receipt.status} ${receipt.scheduleKind} ${receipt.scheduleValue} routine
|
|
101
|
+
const failure = receipt.status === 'failed' && receipt.failureKind ? ` failure ${formatRoutineScheduleFailureKind(receipt.failureKind)}` : '';
|
|
102
|
+
return ` ${receipt.id} ${receipt.status} ${receipt.scheduleKind} ${receipt.scheduleValue} routine ${receipt.routineId}${schedule}${failure}`;
|
|
79
103
|
}),
|
|
80
104
|
snapshot.receipts.length > receipts.length ? ` ...${snapshot.receipts.length - receipts.length} more` : '',
|
|
81
105
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
@@ -84,20 +108,20 @@ export function formatRoutineScheduleReceipts(snapshot: RoutineScheduleReceiptSn
|
|
|
84
108
|
export function formatRoutineScheduleReceipt(receipt: RoutineScheduleReceipt): string {
|
|
85
109
|
return [
|
|
86
110
|
`Agent routine schedule receipt ${receipt.id}`,
|
|
87
|
-
` created
|
|
88
|
-
` status
|
|
89
|
-
` routine
|
|
90
|
-
` route
|
|
91
|
-
`
|
|
92
|
-
` schedule
|
|
93
|
-
receipt.scheduleStatus ? ` schedule status
|
|
111
|
+
` created ${receipt.createdAt}`,
|
|
112
|
+
` status ${receipt.status}`,
|
|
113
|
+
` routine ${receipt.routineName} (${receipt.routineId})`,
|
|
114
|
+
` route ${receipt.method} ${receipt.route}`,
|
|
115
|
+
` connected host: ${receipt.connectedHostBaseUrl}`,
|
|
116
|
+
` schedule ${receipt.scheduleName}${receipt.scheduleId ? ` (${receipt.scheduleId})` : ''}`,
|
|
117
|
+
receipt.scheduleStatus ? ` schedule status ${receipt.scheduleStatus}` : '',
|
|
94
118
|
` cadence: ${receipt.scheduleKind} ${receipt.scheduleValue}${receipt.timezone ? ` [${receipt.timezone}]` : ''}`,
|
|
95
|
-
` enabled
|
|
96
|
-
receipt.provider ? ` provider
|
|
97
|
-
receipt.model ? ` model
|
|
98
|
-
` target
|
|
119
|
+
` enabled ${receipt.enabled ? 'yes' : 'no'}`,
|
|
120
|
+
receipt.provider ? ` provider ${receipt.provider}` : '',
|
|
121
|
+
receipt.model ? ` model ${receipt.model}` : '',
|
|
122
|
+
` target ${formatDeliveryTargetKind(receipt.target)}`,
|
|
99
123
|
receipt.deliveryMode ? ` delivery: ${receipt.deliveryMode}` : '',
|
|
100
|
-
...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${
|
|
124
|
+
...(receipt.deliveryTargets ?? []).map((target) => ` delivery target: ${formatDeliveryTarget(target)}`),
|
|
101
125
|
receipt.failureKind ? ` failure: ${receipt.failureKind}` : '',
|
|
102
126
|
receipt.failureError ? ` error: ${receipt.failureError}` : '',
|
|
103
127
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
@@ -106,18 +130,18 @@ export function formatRoutineScheduleReceipt(receipt: RoutineScheduleReceipt): s
|
|
|
106
130
|
export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelationResult, limit = 10): string {
|
|
107
131
|
if (!result.ok) {
|
|
108
132
|
return [
|
|
109
|
-
`GoodVibes schedule reconciliation error
|
|
133
|
+
`GoodVibes schedule reconciliation error ${formatRoutineScheduleFailureKind(result.kind)}`,
|
|
110
134
|
` ${result.error}`,
|
|
111
|
-
result.baseUrl ? `
|
|
112
|
-
` route
|
|
135
|
+
result.baseUrl ? ` connected host ${result.baseUrl}` : null,
|
|
136
|
+
` route ${ROUTINE_SCHEDULE_LIST_METHOD} ${result.route}`,
|
|
113
137
|
result.kind === 'auth_required'
|
|
114
|
-
? ' next
|
|
138
|
+
? ' next pair/authenticate with the connected GoodVibes host, then retry.'
|
|
115
139
|
: null,
|
|
116
|
-
result.kind === '
|
|
117
|
-
? ' next
|
|
140
|
+
result.kind === 'connected_host_unavailable'
|
|
141
|
+
? ' next make the connected GoodVibes host available outside Agent, then retry.'
|
|
118
142
|
: null,
|
|
119
|
-
result.kind === 'version_mismatch' || result.kind === '
|
|
120
|
-
? ' next
|
|
143
|
+
result.kind === 'version_mismatch' || result.kind === 'connected_host_route_unavailable'
|
|
144
|
+
? ' next update the connected GoodVibes host so public schedules.list is available.'
|
|
121
145
|
: null,
|
|
122
146
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
123
147
|
}
|
|
@@ -125,7 +149,7 @@ export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelat
|
|
|
125
149
|
if (result.receiptCount === 0) {
|
|
126
150
|
return [
|
|
127
151
|
'Agent routine schedule reconciliation',
|
|
128
|
-
`
|
|
152
|
+
` connected host: ${result.baseUrl}`,
|
|
129
153
|
` route: ${result.kind} ${result.route}`,
|
|
130
154
|
` live schedules: ${result.scheduleCount}`,
|
|
131
155
|
' No local routine promotion receipts exist yet.',
|
|
@@ -137,20 +161,20 @@ export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelat
|
|
|
137
161
|
const failed = result.correlations.filter((entry) => entry.liveStatus === 'failed-receipt').length;
|
|
138
162
|
return [
|
|
139
163
|
'Agent routine schedule reconciliation',
|
|
140
|
-
`
|
|
164
|
+
` connected host: ${result.baseUrl}`,
|
|
141
165
|
` route: ${result.kind} ${result.route}`,
|
|
142
166
|
` receipts: ${result.receiptCount}; live schedules: ${result.scheduleCount}; matched: ${matched}; missing: ${missing}; failed receipts: ${failed}`,
|
|
143
167
|
...correlations.map((entry) => {
|
|
144
168
|
const receipt = entry.receipt;
|
|
145
169
|
const schedule = entry.schedule;
|
|
146
170
|
const live = schedule
|
|
147
|
-
? ` live=${schedule.id} status
|
|
171
|
+
? ` live=${schedule.id}; status ${schedule.status ?? (schedule.enabled === false ? 'paused' : 'enabled')}`
|
|
148
172
|
: '';
|
|
149
173
|
const runs = schedule && schedule.runCount !== undefined
|
|
150
|
-
? ` runs
|
|
174
|
+
? ` runs ${schedule.runCount}/${schedule.successCount ?? 0}/${schedule.failureCount ?? 0}`
|
|
151
175
|
: '';
|
|
152
|
-
const next = schedule?.nextRunAt ? ` next
|
|
153
|
-
return ` ${receipt.id} ${entry.liveStatus} reason
|
|
176
|
+
const next = schedule?.nextRunAt ? ` next ${new Date(schedule.nextRunAt).toISOString()}` : '';
|
|
177
|
+
return ` ${receipt.id} ${entry.liveStatus} reason ${entry.matchReason} routine ${receipt.routineId} receipt schedule ${receipt.scheduleId ?? '(none)'}${live}${runs}${next}`;
|
|
154
178
|
}),
|
|
155
179
|
result.correlations.length > correlations.length ? ` ...${result.correlations.length - correlations.length} more` : '',
|
|
156
180
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
@@ -158,21 +182,21 @@ export function formatRoutineScheduleCorrelation(result: RoutineScheduleCorrelat
|
|
|
158
182
|
|
|
159
183
|
export function formatRoutineScheduleFailure(failure: RoutineSchedulePromotionFailure): string {
|
|
160
184
|
return [
|
|
161
|
-
`GoodVibes schedule error
|
|
185
|
+
`GoodVibes schedule error ${formatRoutineScheduleFailureKind(failure.kind)}`,
|
|
162
186
|
` ${failure.error}`,
|
|
163
|
-
failure.baseUrl ? `
|
|
164
|
-
` route
|
|
165
|
-
failure.kind === 'version_mismatch' && failure.
|
|
166
|
-
? ` versions
|
|
187
|
+
failure.baseUrl ? ` connected host ${failure.baseUrl}` : null,
|
|
188
|
+
` route ${ROUTINE_SCHEDULE_METHOD} ${failure.route}`,
|
|
189
|
+
failure.kind === 'version_mismatch' && failure.connectedHostVersion && failure.expectedSdkVersion
|
|
190
|
+
? ` versions connected host ${failure.connectedHostVersion}; expected ${failure.expectedSdkVersion}`
|
|
167
191
|
: null,
|
|
168
192
|
failure.kind === 'auth_required'
|
|
169
|
-
? ' next
|
|
193
|
+
? ' next pair/authenticate with the connected GoodVibes host, then retry with --yes.'
|
|
170
194
|
: null,
|
|
171
|
-
failure.kind === '
|
|
172
|
-
|
|
195
|
+
failure.kind === 'connected_host_unavailable'
|
|
196
|
+
? ' next make the connected GoodVibes host available outside Agent, then retry.'
|
|
173
197
|
: null,
|
|
174
|
-
failure.kind === 'version_mismatch' || failure.kind === '
|
|
175
|
-
? ' next
|
|
198
|
+
failure.kind === 'version_mismatch' || failure.kind === 'connected_host_route_unavailable'
|
|
199
|
+
? ' next update the connected GoodVibes host so public schedules.create is available.'
|
|
176
200
|
: null,
|
|
177
201
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
178
202
|
}
|