@pellux/goodvibes-agent 1.5.5 → 1.5.6
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 +9 -0
- package/README.md +1 -1
- package/dist/package/main.js +94130 -79335
- package/docs/tools-and-commands.md +2 -1
- package/package.json +6 -3
- package/src/agent/autonomy-schedule-format.ts +1 -1
- package/src/agent/autonomy-schedule.ts +3 -3
- package/src/agent/calendar/calendar-oauth-service.ts +324 -0
- package/src/agent/calendar-subscription-registry.ts +497 -0
- package/src/agent/email/email-service.ts +57 -0
- package/src/agent/email/smtp-client.ts +30 -10
- package/src/agent/memory-prompt.ts +143 -12
- package/src/agent/operator-actions.ts +26 -22
- package/src/agent/prompt-context-receipts.ts +45 -18
- package/src/agent/reminder-schedule-format.ts +1 -1
- package/src/agent/reminder-schedule.ts +3 -3
- package/src/agent/routine-schedule-format.ts +2 -2
- package/src/agent/routine-schedule-promotion.ts +5 -5
- package/src/agent/routine-schedule-receipts.ts +2 -2
- package/src/agent/schedule-edit-format.ts +1 -1
- package/src/agent/schedule-edit.ts +5 -5
- package/src/agent/session-registration.ts +281 -0
- package/src/agent/vibe-file.ts +50 -0
- package/src/cli/local-library-command-shared.ts +146 -0
- package/src/cli/management.ts +2 -1
- package/src/cli/personas-command.ts +252 -0
- package/src/cli/resume-relaunch-notice.ts +151 -0
- package/src/cli/routines-command.ts +25 -2
- package/src/cli/service-posture.ts +3 -2
- package/src/cli/skill-bundle-command.ts +175 -0
- package/src/cli/skills-command.ts +309 -0
- package/src/cli/status.ts +43 -7
- package/src/cli/tui-startup.ts +24 -1
- package/src/config/agent-settings-policy.ts +0 -1
- package/src/config/secret-config.ts +4 -0
- package/src/core/conversation-rendering.ts +20 -11
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/system-message-noise.ts +108 -0
- package/src/core/system-message-router.ts +54 -1
- package/src/core/thinking-overlay.ts +83 -0
- package/src/input/agent-workspace-access-command-editor-submission.ts +75 -108
- package/src/input/agent-workspace-access-command-editors.ts +108 -128
- package/src/input/agent-workspace-activation.ts +15 -0
- package/src/input/agent-workspace-basic-command-editor-submission.ts +292 -513
- package/src/input/agent-workspace-basic-command-editors.ts +433 -564
- package/src/input/agent-workspace-calendar-connect-editor.ts +116 -0
- package/src/input/agent-workspace-calendar-oauth-editor.ts +152 -0
- package/src/input/agent-workspace-calendar-subscribe-editor.ts +128 -0
- package/src/input/agent-workspace-categories.ts +7 -2
- package/src/input/agent-workspace-channel-command-editor-submission.ts +38 -52
- package/src/input/agent-workspace-channel-command-editors.ts +42 -46
- package/src/input/agent-workspace-command-editor-engine.ts +133 -0
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-direct-editor-submission.ts +59 -0
- package/src/input/agent-workspace-email-connect-editor.ts +162 -0
- package/src/input/agent-workspace-knowledge-command-editor-submission.ts +52 -81
- package/src/input/agent-workspace-knowledge-command-editors.ts +71 -71
- package/src/input/agent-workspace-library-command-editor-submission.ts +10 -28
- package/src/input/agent-workspace-library-command-editors.ts +16 -2
- package/src/input/agent-workspace-live-counters.ts +55 -0
- package/src/input/agent-workspace-mcp-command-editor-submission.ts +60 -82
- package/src/input/agent-workspace-mcp-command-editors.ts +52 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +12 -39
- package/src/input/agent-workspace-media-command-editors.ts +10 -8
- package/src/input/agent-workspace-memory-command-editor-submission.ts +76 -151
- package/src/input/agent-workspace-memory-command-editors.ts +116 -141
- package/src/input/agent-workspace-operations-command-editor-submission.ts +131 -184
- package/src/input/agent-workspace-operations-command-editors.ts +150 -162
- package/src/input/agent-workspace-provider-command-editor-submission.ts +60 -106
- package/src/input/agent-workspace-provider-command-editors.ts +58 -68
- package/src/input/agent-workspace-search.ts +8 -1
- package/src/input/agent-workspace-session-command-editor-submission.ts +104 -132
- package/src/input/agent-workspace-session-command-editors.ts +160 -195
- package/src/input/agent-workspace-settings.ts +40 -1
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +55 -71
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +65 -69
- package/src/input/agent-workspace-snapshot-builders.ts +431 -0
- package/src/input/agent-workspace-snapshot-config.ts +43 -0
- package/src/input/agent-workspace-snapshot.ts +198 -432
- package/src/input/agent-workspace-task-command-editor-submission.ts +34 -44
- package/src/input/agent-workspace-task-command-editors.ts +27 -28
- package/src/input/agent-workspace-types.ts +35 -1
- package/src/input/agent-workspace.ts +5 -14
- package/src/input/command-registry.ts +23 -1
- package/src/input/commands/calendar-connect-runtime.ts +226 -0
- package/src/input/commands/calendar-runtime.ts +110 -7
- package/src/input/commands/calendar-subscription-runtime.ts +225 -0
- package/src/input/commands/email-runtime.ts +100 -40
- package/src/input/commands/knowledge.ts +1 -1
- package/src/input/commands/network-scan-runtime.ts +75 -0
- package/src/input/commands/operator-actions-runtime.ts +9 -6
- package/src/input/commands/personas-runtime.ts +1 -1
- package/src/input/commands/schedule-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +13 -1
- package/src/input/commands/session-workflow.ts +15 -20
- package/src/input/commands/session.ts +3 -1
- package/src/input/commands/shell-core.ts +20 -6
- package/src/input/commands.ts +2 -2
- package/src/input/delete-key-policy.ts +46 -0
- package/src/input/feed-context-factory.ts +10 -0
- package/src/input/handler-feed.ts +87 -0
- package/src/input/handler-modal-routes.ts +6 -1
- package/src/input/handler.ts +5 -0
- package/src/input/panel-paste-flood-guard.ts +94 -0
- package/src/input/settings-modal-types.ts +5 -3
- package/src/input/settings-modal.ts +21 -0
- package/src/main.ts +36 -36
- package/src/permissions/approval-posture.ts +141 -0
- package/src/renderer/agent-workspace-context-lines.ts +10 -2
- package/src/renderer/compositor.ts +27 -4
- package/src/renderer/diff.ts +61 -18
- package/src/renderer/fullscreen-primitives.ts +37 -18
- package/src/renderer/markdown.ts +20 -10
- package/src/renderer/modal-factory.ts +25 -15
- package/src/renderer/overlay-box.ts +23 -12
- package/src/renderer/process-indicator.ts +8 -3
- package/src/renderer/prompt-content-width.ts +16 -0
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +2 -0
- package/src/renderer/startup-theme-probe.ts +35 -0
- package/src/renderer/status-glyphs.ts +11 -15
- package/src/renderer/system-message.ts +17 -2
- package/src/renderer/term-caps.ts +318 -0
- package/src/renderer/terminal-bg-probe.ts +373 -0
- package/src/renderer/terminal-escapes.ts +24 -0
- package/src/renderer/theme-mode-config.ts +87 -0
- package/src/renderer/theme.ts +241 -0
- package/src/renderer/thinking.ts +12 -3
- package/src/renderer/tool-call.ts +7 -3
- package/src/renderer/ui-factory.ts +92 -36
- package/src/renderer/ui-primitives.ts +33 -93
- package/src/runtime/bootstrap-core.ts +15 -0
- package/src/runtime/bootstrap-hook-bridge.ts +6 -0
- package/src/runtime/bootstrap-shell.ts +2 -0
- package/src/runtime/bootstrap.ts +68 -5
- package/src/runtime/calendar-boot-refresh.ts +105 -0
- package/src/runtime/lan-scan-consent.ts +253 -0
- package/src/runtime/services.ts +127 -2
- package/src/runtime/session-spine-rest-transport.ts +160 -0
- package/src/runtime/terminal-output-guard.ts +6 -1
- package/src/runtime/ui-services.ts +3 -0
- package/src/shell/agent-workspace-fullscreen.ts +5 -0
- package/src/shell/terminal-focus-mode.ts +120 -0
- package/src/shell/ui-openers.ts +13 -4
- package/src/tools/agent-harness-autonomy-intake.ts +5 -5
- package/src/tools/agent-harness-autonomy-queue.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +2 -1
- package/src/tools/agent-harness-metadata.ts +16 -8
- package/src/tools/agent-harness-operator-methods.ts +44 -8
- package/src/tools/agent-harness-personal-ops-discovery.ts +57 -10
- package/src/tools/agent-harness-personal-ops-lanes.ts +17 -5
- package/src/tools/agent-harness-personal-ops-operations.ts +7 -7
- package/src/tools/agent-harness-personal-ops-types.ts +6 -0
- package/src/tools/agent-harness-prompt-context.ts +26 -12
- package/src/tools/agent-harness-workspace-actions.ts +4 -0
- package/src/tools/agent-local-registry-args.ts +117 -0
- package/src/tools/agent-local-registry-memory.ts +227 -0
- package/src/tools/agent-local-registry-tool.ts +19 -237
- package/src/tools/agent-operator-briefing-tool.ts +2 -2
- package/src/tools/agent-operator-method-tool.ts +13 -0
- package/src/tools/agent-policy-explanation.ts +39 -4
- package/src/tools/agent-schedule-tool.ts +5 -5
- package/src/utils/terminal-width.ts +98 -1
- package/src/version.ts +1 -1
- package/src/cli/local-library-command.ts +0 -825
|
@@ -36,5 +36,10 @@ export function createAgentWorkspaceFullscreenComposite(
|
|
|
36
36
|
width: number,
|
|
37
37
|
height: number,
|
|
38
38
|
): CompositeRequest {
|
|
39
|
+
// W4-A6: mirror disk state on every repaint of the workspace panel so the
|
|
40
|
+
// memory count and routine start counts never show a stale point-in-time
|
|
41
|
+
// snapshot from the last workspace action (e.g. an external delete, or a
|
|
42
|
+
// routine started from another shell/CLI invocation).
|
|
43
|
+
workspace.syncLiveCountersForRender();
|
|
39
44
|
return createFullscreenCompositeFromLines(renderAgentWorkspace(workspace, width, height), width, height);
|
|
40
45
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal-focus-mode.ts — W4-R3: DECSET ?1004h OS window-focus reporting +
|
|
3
|
+
* the approval-alert wiring that consumes it.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from main.ts (architecture line-count gate: MAX_SOURCE_LINES=800
|
|
6
|
+
* in scripts/check-architecture.ts) — mirrors goodvibes-tui's own precedent of
|
|
7
|
+
* extracting terminal-mode literals out of its entry file (see that repo's
|
|
8
|
+
* src/renderer/terminal-escapes.ts docstring). Behavior is unchanged from an
|
|
9
|
+
* inline version; this is purely a line-count extraction plus the one new
|
|
10
|
+
* behavior (the approval alert) this work order adds.
|
|
11
|
+
*
|
|
12
|
+
* FOCUS_ENABLE/FOCUS_DISABLE: sourced from W4-R2's terminal-escapes.ts (the
|
|
13
|
+
* shared home for the DECSET ?1004 literals — its own docstring names R3 as
|
|
14
|
+
* the importer) and re-exported here so the focus-mode callers (main.ts and
|
|
15
|
+
* the tests) keep one import site for the whole focus-mode surface. This is
|
|
16
|
+
* the R2/R3 convergence the W4-R1 parity matrix pre-ruled: a relocation of
|
|
17
|
+
* the literals, no behavior change.
|
|
18
|
+
*
|
|
19
|
+
* installFocusModeExitGuard(): a last-resort safety net. Node/Bun fire 'exit'
|
|
20
|
+
* listeners synchronously for every process-termination path this app can
|
|
21
|
+
* take — explicit process.exit() calls (normal /exit, double-Ctrl+C via
|
|
22
|
+
* exitApp()) AND the default uncaughtException termination — even though
|
|
23
|
+
* main.ts registers no explicit uncaughtException/SIGTERM handler of its own.
|
|
24
|
+
* Named top-5 risk (W4-R1 matrix): ?1004h MUST be disabled on every exit path
|
|
25
|
+
* or the user's shell inherits focus-reporting escape garbage. The write is
|
|
26
|
+
* harmless even if exitApp() already disabled it moments earlier (?1004l is
|
|
27
|
+
* idempotent). It is GATED on focus mode having actually been enabled
|
|
28
|
+
* (markFocusModeEnabled, called at the TUI's ?1004h-enable site): only the
|
|
29
|
+
* interactive TUI launch turns focus reporting on, so a blocked/scriptable
|
|
30
|
+
* command (serve/status/--help/…) that exits before that must leave stdout
|
|
31
|
+
* pristine — a stray ?1004l on such a command's stdout is escape garbage that
|
|
32
|
+
* would, e.g., corrupt `status --json` for a machine consumer.
|
|
33
|
+
*
|
|
34
|
+
* wrapRequestPermissionWithApprovalAlert(): fires an unfocused-user alert the
|
|
35
|
+
* moment a tool call becomes a real, user-blocking permission prompt. Ported
|
|
36
|
+
* concept from goodvibes-tui's core/approval-alert.ts, scoped down: that
|
|
37
|
+
* module's config-gated alert-class system (alert-gating.ts's
|
|
38
|
+
* behavior.notifyOnApprovalPending / notifyOnlyWhenUnfocused settings, shared
|
|
39
|
+
* with budget-breach/long-task/chain-failure notifiers) is TUI-specific
|
|
40
|
+
* infrastructure the W4-R1 parity matrix does not list as a PORT item — only
|
|
41
|
+
* core/focus-tracker.ts is. This is the minimal, self-contained wiring the
|
|
42
|
+
* matrix's own text calls for ("route focus in/out events ... to drive
|
|
43
|
+
* awaiting-approval alerts"). PRIVACY: message content is tool name + category
|
|
44
|
+
* only — no args, no file contents, no command strings.
|
|
45
|
+
*/
|
|
46
|
+
import { logger, notifyCompletion, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
47
|
+
import type { PermissionRequestHandler, PermissionPromptRequest } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
48
|
+
import type { FocusTracker } from '../core/focus-tracker.ts';
|
|
49
|
+
|
|
50
|
+
import { FOCUS_DISABLE } from '../renderer/terminal-escapes.ts';
|
|
51
|
+
export { FOCUS_ENABLE, FOCUS_DISABLE } from '../renderer/terminal-escapes.ts';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* notifyCompletion (SDK platform/utils) only rings the bell above 5s of
|
|
55
|
+
* "duration" and only pops a desktop notification above 30s — a heuristic
|
|
56
|
+
* tuned for long-running-turn notifications (ported alongside
|
|
57
|
+
* goodvibes-tui's alert-gating.ts FORCE_NOTIFY_DURATION_MS). The
|
|
58
|
+
* approval-alert wire is a point-in-time event with no natural duration, so
|
|
59
|
+
* it passes this constant as durationMs to force both the bell and the
|
|
60
|
+
* desktop popup unconditionally.
|
|
61
|
+
*/
|
|
62
|
+
export const FORCE_APPROVAL_NOTIFY_DURATION_MS = 30_001;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether the TUI actually enabled OS focus reporting (DECSET ?1004h) this run.
|
|
66
|
+
* Set once, at the TUI launch site that writes FOCUS_ENABLE; the process exits
|
|
67
|
+
* without ever clearing it, so the teardown guard fires on every abnormal-exit
|
|
68
|
+
* path once focus mode is on. Left false by every blocked/scriptable command.
|
|
69
|
+
*/
|
|
70
|
+
let focusModeEnabled = false;
|
|
71
|
+
|
|
72
|
+
/** Record that OS focus reporting was enabled; call at the same site that writes FOCUS_ENABLE. */
|
|
73
|
+
export function markFocusModeEnabled(): void {
|
|
74
|
+
focusModeEnabled = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Registers the process-wide ?1004h teardown safety net. Call once at startup.
|
|
79
|
+
* The write only fires when focus mode was actually enabled (isFocusModeEnabled),
|
|
80
|
+
* so non-TUI commands never leak ?1004l onto stdout. The predicate is injectable
|
|
81
|
+
* so tests can drive both branches deterministically without module-global state.
|
|
82
|
+
*/
|
|
83
|
+
export function installFocusModeExitGuard(
|
|
84
|
+
stdout: Pick<NodeJS.WriteStream, 'write'> = process.stdout,
|
|
85
|
+
isFocusModeEnabled: () => boolean = () => focusModeEnabled,
|
|
86
|
+
): void {
|
|
87
|
+
process.on('exit', () => {
|
|
88
|
+
if (!isFocusModeEnabled()) return; // never enabled (blocked/scriptable command) — keep stdout clean
|
|
89
|
+
try { stdout.write(FOCUS_DISABLE); } catch { /* stdout may already be torn down */ }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ApprovalAlertDeps {
|
|
94
|
+
readonly focusTracker: Pick<FocusTracker, 'shouldAlertWhenUnfocused'>;
|
|
95
|
+
readonly notify?: typeof notifyCompletion;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Wrap a PermissionRequestHandler so every call also fires an unfocused-user
|
|
100
|
+
* alert. The wrapped handler's behavior (what it resolves to) is completely
|
|
101
|
+
* unchanged — this only adds a side effect at call time, fired synchronously
|
|
102
|
+
* before the wrapped promise settles (reflects "a prompt just appeared", not
|
|
103
|
+
* "a prompt was just resolved").
|
|
104
|
+
*/
|
|
105
|
+
export function wrapRequestPermissionWithApprovalAlert(
|
|
106
|
+
original: PermissionRequestHandler,
|
|
107
|
+
deps: ApprovalAlertDeps,
|
|
108
|
+
): PermissionRequestHandler {
|
|
109
|
+
const notify = deps.notify ?? notifyCompletion;
|
|
110
|
+
return (request: PermissionPromptRequest) => {
|
|
111
|
+
if (deps.focusTracker.shouldAlertWhenUnfocused()) {
|
|
112
|
+
try {
|
|
113
|
+
notify('GoodVibes — approval needed', `${request.tool} (${request.category}) is waiting for approval`, FORCE_APPROVAL_NOTIFY_DURATION_MS);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
logger.debug('approval-alert: desktop notify error', { error: summarizeError(err) });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return original(request);
|
|
119
|
+
};
|
|
120
|
+
}
|
package/src/shell/ui-openers.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
|
|
|
14
14
|
import type { ModelPickerTargetInfo } from '../input/model-picker.ts';
|
|
15
15
|
import { buildLocalFitRecommendations, buildSignInRow, LOCAL_REC_PROVIDER } from '../input/model-picker-local-fit.ts';
|
|
16
16
|
import { syncServiceSettingToPlatform } from './service-settings-sync.ts';
|
|
17
|
+
import { applyThemeModeSettingChange, THEME_MODE_CONFIG_KEY } from '../renderer/theme-mode-config.ts';
|
|
17
18
|
|
|
18
19
|
type WireShellUiOpenersOptions = {
|
|
19
20
|
commandContext: CommandContext;
|
|
@@ -422,10 +423,18 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
422
423
|
commandContext.openSettingsModal = (target?: string) => {
|
|
423
424
|
input.modalOpened('settings');
|
|
424
425
|
input.settingsModal.open(configManager, featureFlags, subscriptionManager, serviceRegistry, mcpRegistry, secretsManager, {
|
|
425
|
-
onSettingApplied: (change) =>
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
426
|
+
onSettingApplied: (change) => {
|
|
427
|
+
// W4-R4: forced dark/light applies immediately (mode flip + full
|
|
428
|
+
// repaint via clearScreen's resetDiff); auto only re-probes at startup,
|
|
429
|
+
// so it takes effect next launch (stated honestly).
|
|
430
|
+
if (String(change.key) === THEME_MODE_CONFIG_KEY) {
|
|
431
|
+
return applyThemeModeSettingChange(change.value, () => commandContext.clearScreen?.());
|
|
432
|
+
}
|
|
433
|
+
return syncServiceSettingToPlatform(
|
|
434
|
+
{ configManager, workingDirectory, homeDirectory },
|
|
435
|
+
change,
|
|
436
|
+
);
|
|
437
|
+
},
|
|
429
438
|
});
|
|
430
439
|
input.settingsModal.selectTarget(target);
|
|
431
440
|
render();
|
|
@@ -417,8 +417,8 @@ function asksForEventTrigger(lower: string): boolean {
|
|
|
417
417
|
|
|
418
418
|
function buildTriggerWorkflows(request: string, schedule: ScheduleDetection): readonly TriggerWorkflow[] {
|
|
419
419
|
const methodIds = operatorMethodIds();
|
|
420
|
-
const schedulesCreatePublished = methodIds.has('schedules.create');
|
|
421
|
-
const schedulesListPublished = methodIds.has('schedules.list');
|
|
420
|
+
const schedulesCreatePublished = methodIds.has('automation.schedules.create');
|
|
421
|
+
const schedulesListPublished = methodIds.has('automation.schedules.list');
|
|
422
422
|
const watcherCreatePublished = methodIds.has('watchers.create');
|
|
423
423
|
const watcherListPublished = methodIds.has('watchers.list');
|
|
424
424
|
const watcherRunPublished = methodIds.has('watchers.run');
|
|
@@ -437,10 +437,10 @@ function buildTriggerWorkflows(request: string, schedule: ScheduleDetection): re
|
|
|
437
437
|
status: scheduleReady ? 'ready' : schedulesCreatePublished ? 'attention' : 'setup-needed',
|
|
438
438
|
userOutcome: 'Run autonomous work on an exact at/every/cron cadence without making the user understand scheduler internals.',
|
|
439
439
|
summary: scheduleReady
|
|
440
|
-
? 'The request includes an exact cadence and the connected host publishes schedules.create.'
|
|
440
|
+
? 'The request includes an exact cadence and the connected host publishes automation.schedules.create.'
|
|
441
441
|
: schedulesCreatePublished
|
|
442
442
|
? 'The schedule route is published, but this request still needs an exact cadence before creation.'
|
|
443
|
-
: 'The connected host does not publish schedules.create in the current SDK contract.',
|
|
443
|
+
: 'The connected host does not publish automation.schedules.create in the current SDK contract.',
|
|
444
444
|
nextStep: scheduleReady
|
|
445
445
|
? 'Create through schedule action:"create" with confirm:true and explicit success criteria.'
|
|
446
446
|
: 'Ask for the exact ISO time, every interval, or cron expression before offering schedule creation.',
|
|
@@ -449,7 +449,7 @@ function buildTriggerWorkflows(request: string, schedule: ScheduleDetection): re
|
|
|
449
449
|
modelRoute: `schedule action:"create" task:"${shortRequest}" successCriteria:"..." scheduleKind:"${schedule.kind ?? 'at|every|cron'}" scheduleValue:"${schedule.value ?? '...'}" confirm:true explicitUserRequest:"..."`,
|
|
450
450
|
inspectRoute: 'autonomy action:"item" queueItemId:"connected-schedules"',
|
|
451
451
|
setupRoutes: [
|
|
452
|
-
'host action:"method" methodId:"schedules.create"',
|
|
452
|
+
'host action:"method" methodId:"automation.schedules.create"',
|
|
453
453
|
'autonomy action:"item" queueItemId:"connected-schedules"',
|
|
454
454
|
],
|
|
455
455
|
evidence: {
|
|
@@ -381,7 +381,7 @@ function buildQueueItems(context: CommandContext): readonly AutonomyQueueItem[]
|
|
|
381
381
|
current: `${scheduleMethods.length} schedule/reminder daemon method(s); autonomous schedules require explicit task, cadence, success criteria, and user request provenance.`,
|
|
382
382
|
next: scheduleMethods.length > 0
|
|
383
383
|
? 'Create one visible autonomous schedule only after the user gives exact timing and success criteria.'
|
|
384
|
-
: 'Update the connected GoodVibes host until schedules.create is available.',
|
|
384
|
+
: 'Update the connected GoodVibes host until automation.schedules.create is available.',
|
|
385
385
|
inspectRoute: 'autonomy action:"intake" query:"..."',
|
|
386
386
|
modelRoute: 'schedule action:"create"',
|
|
387
387
|
createRoute: 'schedule action:"create" task:"..." successCriteria:"..." scheduleKind:"..." scheduleValue:"..." confirm:true explicitUserRequest:"..."',
|
|
@@ -104,7 +104,8 @@ function matchingOperatorMethodRoutes(tokens: readonly string[]): readonly Recor
|
|
|
104
104
|
return operatorContractMethods()
|
|
105
105
|
.filter((method) => {
|
|
106
106
|
const text = operatorMethodSearchText(method);
|
|
107
|
-
|
|
107
|
+
// \b-anchored: a raw text.includes(token) false-matches short tokens like 'pty' inside an unrelated word ('empty').
|
|
108
|
+
return tokens.some((token) => new RegExp(`\\b${token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(text));
|
|
108
109
|
})
|
|
109
110
|
.map((method) => ({
|
|
110
111
|
methodId: method.id,
|
|
@@ -86,6 +86,14 @@ export function describeCommandPolicy(commandName: string): CommandExecutionPoli
|
|
|
86
86
|
boundary: 'Provider selection and custom provider files belong to Agent provider configuration. Adding, removing, or switching providers requires explicit user intent.',
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
+
if (root === 'network-scan' || root === 'discover-lan') {
|
|
90
|
+
return {
|
|
91
|
+
effect: 'local-state',
|
|
92
|
+
confirmation,
|
|
93
|
+
preferredModelTool: settingsActions('get', 'set'),
|
|
94
|
+
boundary: 'Turns the local-network model-server scan on or off and persists that decision to a local consent file; it never probes the network itself. Enabling it requires the user\'s explicit request — do not turn it on unprompted.',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
89
97
|
if (root === 'refresh-models') {
|
|
90
98
|
return {
|
|
91
99
|
effect: 'external-network',
|
|
@@ -387,14 +395,14 @@ export function connectedHostCapabilityMap(toolRegistry: ToolRegistry): readonly
|
|
|
387
395
|
'approvals.deny',
|
|
388
396
|
'approvals.cancel',
|
|
389
397
|
'automation.jobs.run',
|
|
390
|
-
'automation.jobs.
|
|
391
|
-
'automation.jobs.
|
|
398
|
+
'automation.jobs.disable',
|
|
399
|
+
'automation.jobs.enable',
|
|
392
400
|
'automation.runs.cancel',
|
|
393
401
|
'automation.runs.retry',
|
|
394
|
-
'schedules.delete',
|
|
395
|
-
'schedules.disable',
|
|
396
|
-
'schedules.enable',
|
|
397
|
-
'schedules.run',
|
|
402
|
+
'automation.schedules.delete',
|
|
403
|
+
'automation.schedules.disable',
|
|
404
|
+
'automation.schedules.enable',
|
|
405
|
+
'automation.schedules.run',
|
|
398
406
|
],
|
|
399
407
|
purpose: 'Perform one explicit allowlisted approval, automation, run, or schedule action.',
|
|
400
408
|
}),
|
|
@@ -404,7 +412,7 @@ export function connectedHostCapabilityMap(toolRegistry: ToolRegistry): readonly
|
|
|
404
412
|
modelTools: ['schedule', 'agent_schedule_edit'],
|
|
405
413
|
workspaceCategories: ['automation', 'personal-ops'],
|
|
406
414
|
slashCommandFamilies: ['schedule'],
|
|
407
|
-
methodIds: ['automation.jobs.
|
|
415
|
+
methodIds: ['automation.jobs.update'],
|
|
408
416
|
purpose: 'Edit one explicit connected schedule name, cadence, or prompt.',
|
|
409
417
|
}),
|
|
410
418
|
withAvailability({
|
|
@@ -737,6 +745,6 @@ export function settingsPolicySummary(): Record<string, unknown> {
|
|
|
737
745
|
mutation: 'Use settings action:"set" or action:"reset" with key, target, or query plus confirm:true and explicitUserRequest; ambiguous setting lookups are refused.',
|
|
738
746
|
secretHandling: 'Raw secret values are persisted through the secret manager; config receives only a secret reference and tool output is redacted.',
|
|
739
747
|
writablePolicy: 'Each setting descriptor includes writable, visibleInWorkspace, and lockReason when applicable.',
|
|
740
|
-
protectedRawDangerKeys: ['danger.
|
|
748
|
+
protectedRawDangerKeys: ['danger.httpListener'],
|
|
741
749
|
};
|
|
742
750
|
}
|
|
@@ -43,9 +43,35 @@ interface OperatorMethodDescriptor {
|
|
|
43
43
|
readonly category: string;
|
|
44
44
|
readonly access: string;
|
|
45
45
|
readonly scopes: readonly string[];
|
|
46
|
+
readonly available: boolean;
|
|
46
47
|
readonly parameters?: readonly Record<string, unknown>[];
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* W4-A3 (capability-advertisement honesty): the SDK operator contract marks
|
|
52
|
+
* a method `invokable: false` when it is cataloged but not backed by a real
|
|
53
|
+
* daemon route (see @pellux/goodvibes-sdk's method-catalog-route-reconcile —
|
|
54
|
+
* email.inbox.list/read, email.draft.create, email.send are the dogfood
|
|
55
|
+
* case: they advertise /api/email/* paths no router dispatch chain serves).
|
|
56
|
+
* Before this, `invokable` was declared on the local contract-method shape
|
|
57
|
+
* but never read — an unavailable method looked identical to a live one in
|
|
58
|
+
* this catalog, so the model (or a human skimming host action:"methods")
|
|
59
|
+
* had no way to tell the ad from the reality short of trying the call and
|
|
60
|
+
* getting a 404. Degrading the listing here means the ad itself — not just
|
|
61
|
+
* the eventual answer — tells the truth.
|
|
62
|
+
*/
|
|
63
|
+
function methodIsAvailable(method: OperatorContractMethod): boolean {
|
|
64
|
+
return method.invokable !== false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const UNAVAILABLE_MODEL_TOOL = 'none — unavailable; do not call this method';
|
|
68
|
+
const UNAVAILABLE_CONFIRMATION = 'Unavailable: cataloged by the daemon but not backed by a served route. Do not call it; it will not succeed.';
|
|
69
|
+
const UNAVAILABLE_BOUNDARY = 'This method is advertised in the operator contract but the daemon does not currently serve its route (invokable:false). Treat it as absent — do not attempt to call it, and tell the user the capability is not wired up rather than guessing at a workaround.';
|
|
70
|
+
|
|
71
|
+
function unavailableLabel(method: OperatorContractMethod): string {
|
|
72
|
+
return `${method.id} — unavailable (route not served by this daemon)`;
|
|
73
|
+
}
|
|
74
|
+
|
|
49
75
|
type OperatorMethodResolution =
|
|
50
76
|
| { readonly status: 'found'; readonly method: Record<string, unknown> }
|
|
51
77
|
| { readonly status: 'ambiguous'; readonly input: string; readonly candidates: readonly Record<string, unknown>[] }
|
|
@@ -121,22 +147,26 @@ function toDescriptor(method: OperatorContractMethod): OperatorMethodDescriptor
|
|
|
121
147
|
const httpMethod = method.http?.method?.toUpperCase() ?? 'GET';
|
|
122
148
|
const path = method.http?.path ?? '/';
|
|
123
149
|
const effect = methodEffect(method);
|
|
124
|
-
const
|
|
150
|
+
const available = methodIsAvailable(method);
|
|
151
|
+
const label = available ? (method.title ?? method.description ?? method.id) : unavailableLabel(method);
|
|
125
152
|
return {
|
|
126
153
|
id: method.id,
|
|
127
154
|
label,
|
|
128
155
|
route: `${httpMethod} ${path}`,
|
|
129
156
|
effect,
|
|
130
157
|
owner: 'goodvibes-daemon',
|
|
131
|
-
preferredModelTool: preferredToolFor(effect),
|
|
132
|
-
confirmation: confirmationFor(effect),
|
|
133
|
-
boundary:
|
|
134
|
-
?
|
|
135
|
-
|
|
158
|
+
preferredModelTool: available ? preferredToolFor(effect) : UNAVAILABLE_MODEL_TOOL,
|
|
159
|
+
confirmation: available ? confirmationFor(effect) : UNAVAILABLE_CONFIRMATION,
|
|
160
|
+
boundary: available
|
|
161
|
+
? (effect === 'read-only-network'
|
|
162
|
+
? 'Reads the connected GoodVibes daemon operator route and returns a redacted response.'
|
|
163
|
+
: 'Runs a confirmed connected GoodVibes daemon operator route. The model must keep the action visible, reversible when possible, and tied to the user request.')
|
|
164
|
+
: UNAVAILABLE_BOUNDARY,
|
|
136
165
|
category: method.category ?? 'uncategorized',
|
|
137
166
|
access: method.access ?? 'authenticated',
|
|
138
167
|
scopes: method.scopes ?? [],
|
|
139
|
-
|
|
168
|
+
available,
|
|
169
|
+
parameters: available ? parametersFromInputSchema(method) : [],
|
|
140
170
|
};
|
|
141
171
|
}
|
|
142
172
|
|
|
@@ -170,6 +200,7 @@ function describeMethod(
|
|
|
170
200
|
effect: method.effect,
|
|
171
201
|
owner: method.owner,
|
|
172
202
|
route: method.route,
|
|
203
|
+
available: method.available,
|
|
173
204
|
modelRoute: previewHarnessText(method.preferredModelTool),
|
|
174
205
|
scopes: method.scopes,
|
|
175
206
|
confirmation: method.confirmation,
|
|
@@ -190,6 +221,7 @@ function describeCandidate(method: OperatorMethodDescriptor): Record<string, unk
|
|
|
190
221
|
category: method.category,
|
|
191
222
|
effect: method.effect,
|
|
192
223
|
route: method.route,
|
|
224
|
+
available: method.available,
|
|
193
225
|
modelRoute: previewHarnessText(method.preferredModelTool),
|
|
194
226
|
};
|
|
195
227
|
}
|
|
@@ -209,14 +241,18 @@ export function operatorMethodCatalogStatus(): Record<string, unknown> {
|
|
|
209
241
|
acc[method.category] = (acc[method.category] ?? 0) + 1;
|
|
210
242
|
return acc;
|
|
211
243
|
}, {});
|
|
244
|
+
const unavailableMethods = methods.filter((method) => !method.available).length;
|
|
212
245
|
return {
|
|
213
246
|
modes: ['operator_methods', 'operator_method'],
|
|
214
247
|
methods: methods.length,
|
|
215
248
|
readOnlyMethods: methods.filter((method) => method.effect === 'read-only-network').length,
|
|
216
249
|
confirmedMethods: methods.filter((method) => method.effect !== 'read-only-network').length,
|
|
217
250
|
adminMethods: methods.filter((method) => method.effect === 'confirmed-admin-connected-host-state').length,
|
|
251
|
+
unavailableMethods,
|
|
218
252
|
categories,
|
|
219
|
-
policy:
|
|
253
|
+
policy: unavailableMethods > 0
|
|
254
|
+
? `Full GoodVibes SDK operator contract. Read-only routes can run through agent_operator_method; write/admin routes require confirm:true and explicitUserRequest. ${unavailableMethods} cataloged method(s) are currently unavailable (no served route) — check each method's "available" field before treating it as callable.`
|
|
255
|
+
: 'Full GoodVibes SDK operator contract. Read-only routes can run through agent_operator_method; write/admin routes require confirm:true and explicitUserRequest.',
|
|
220
256
|
};
|
|
221
257
|
}
|
|
222
258
|
|
|
@@ -35,17 +35,56 @@ export function methodSearchText(method: OperatorContractMethod): string {
|
|
|
35
35
|
].filter(Boolean).join('\n').toLowerCase();
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* W4-A3/W4-A5 capability honesty: a method whose contract entry carries
|
|
40
|
+
* invokable:false is cataloged but NOT dispatchable — no daemon route or
|
|
41
|
+
* handler serves it. Counting it toward "workflow ready" would repeat the
|
|
42
|
+
* dogfood finding (a lane card claiming readiness backed only by methods a
|
|
43
|
+
* caller cannot actually invoke). Absent flag = invokable (older contracts).
|
|
44
|
+
*/
|
|
45
|
+
export function isInvokableContractMethod(method: OperatorContractMethod): boolean {
|
|
46
|
+
return method.invokable !== false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function matchingContractMethods(tokens: readonly string[]): readonly OperatorContractMethod[] {
|
|
39
50
|
if (tokens.length === 0) return [];
|
|
40
|
-
return operatorContractMethods()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
return operatorContractMethods().filter((method) => {
|
|
52
|
+
const text = methodSearchText(method);
|
|
53
|
+
return tokens.some((token) => text.includes(token));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Method ids matching the tokens that are actually DISPATCHABLE. Readiness
|
|
59
|
+
* (workflowStatus, lane status, capability counts) must be computed from
|
|
60
|
+
* this list only — advertised-but-unavailable methods are returned
|
|
61
|
+
* separately by unavailableMethodIdsMatching for honest degraded wording.
|
|
62
|
+
*/
|
|
63
|
+
export function methodIdsMatching(tokens: readonly string[]): readonly string[] {
|
|
64
|
+
return matchingContractMethods(tokens)
|
|
65
|
+
.filter(isInvokableContractMethod)
|
|
45
66
|
.map((method) => method.id)
|
|
46
67
|
.sort((left, right) => left.localeCompare(right));
|
|
47
68
|
}
|
|
48
69
|
|
|
70
|
+
/** Method ids matching the tokens that are cataloged but NOT dispatchable (invokable:false). */
|
|
71
|
+
export function unavailableMethodIdsMatching(tokens: readonly string[]): readonly string[] {
|
|
72
|
+
return matchingContractMethods(tokens)
|
|
73
|
+
.filter((method) => !isInvokableContractMethod(method))
|
|
74
|
+
.map((method) => method.id)
|
|
75
|
+
.sort((left, right) => left.localeCompare(right));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* One honest line naming advertised-but-undispatchable methods, matching the
|
|
80
|
+
* W4-A3 degraded-ad pattern ("unavailable (route not served by this daemon)").
|
|
81
|
+
* Returns null when nothing is degraded.
|
|
82
|
+
*/
|
|
83
|
+
export function unavailableMethodsNote(unavailableMethodIds: readonly string[]): string | null {
|
|
84
|
+
if (unavailableMethodIds.length === 0) return null;
|
|
85
|
+
return `${unavailableMethodIds.length} advertised method(s) are unavailable (route not served by this daemon) and cannot be dispatched: ${unavailableMethodIds.join(', ')}.`;
|
|
86
|
+
}
|
|
87
|
+
|
|
49
88
|
export function mcpServerRecords(context: CommandContext): readonly {
|
|
50
89
|
readonly name: string;
|
|
51
90
|
readonly connected: boolean;
|
|
@@ -337,14 +376,18 @@ export function workflowInspectRoutes(methodIds: readonly string[], connectors:
|
|
|
337
376
|
return routes.length > 0 ? routes : [`personal_ops action:"lane" laneId:"${fallbackQuery}"`];
|
|
338
377
|
}
|
|
339
378
|
|
|
340
|
-
export function inboxWorkflows(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[]): readonly PersonalOpsWorkflow[] {
|
|
379
|
+
export function inboxWorkflows(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[], unavailableMethodIds: readonly string[] = []): readonly PersonalOpsWorkflow[] {
|
|
341
380
|
const status = workflowStatus(methodIds, connectors);
|
|
342
381
|
const inspectRoutes = workflowInspectRoutes(methodIds, connectors, 'inbox');
|
|
343
382
|
const modelRoute = workflowModelRoute('email', connectors);
|
|
344
383
|
const readToolCount = connectorToolCount(connectors, 'read-only');
|
|
345
384
|
const writeToolCount = connectorToolCount(connectors, 'confirmed-effect');
|
|
385
|
+
const degradedAdNote = unavailableMethodsNote(unavailableMethodIds);
|
|
346
386
|
const setupPrerequisite = status === 'needs-setup'
|
|
347
|
-
? [
|
|
387
|
+
? [
|
|
388
|
+
'Install or configure an email-capable daemon method, MCP server, or plugin first.',
|
|
389
|
+
...(degradedAdNote ? [degradedAdNote] : []),
|
|
390
|
+
]
|
|
348
391
|
: status === 'attention'
|
|
349
392
|
? ['Review connector trust/schema freshness before reading inbox data.']
|
|
350
393
|
: [
|
|
@@ -389,14 +432,18 @@ export function inboxWorkflows(methodIds: readonly string[], connectors: readonl
|
|
|
389
432
|
];
|
|
390
433
|
}
|
|
391
434
|
|
|
392
|
-
export function calendarWorkflows(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[]): readonly PersonalOpsWorkflow[] {
|
|
435
|
+
export function calendarWorkflows(methodIds: readonly string[], connectors: readonly PersonalOpsConnectorSignal[], unavailableMethodIds: readonly string[] = []): readonly PersonalOpsWorkflow[] {
|
|
393
436
|
const status = workflowStatus(methodIds, connectors);
|
|
394
437
|
const inspectRoutes = workflowInspectRoutes(methodIds, connectors, 'calendar');
|
|
395
438
|
const modelRoute = workflowModelRoute('calendar', connectors);
|
|
396
439
|
const readToolCount = connectorToolCount(connectors, 'read-only');
|
|
397
440
|
const writeToolCount = connectorToolCount(connectors, 'confirmed-effect');
|
|
441
|
+
const degradedAdNote = unavailableMethodsNote(unavailableMethodIds);
|
|
398
442
|
const setupPrerequisite = status === 'needs-setup'
|
|
399
|
-
? [
|
|
443
|
+
? [
|
|
444
|
+
'Install or configure a calendar-capable daemon method, CalDAV MCP server, or plugin first.',
|
|
445
|
+
...(degradedAdNote ? [degradedAdNote] : []),
|
|
446
|
+
]
|
|
400
447
|
: status === 'attention'
|
|
401
448
|
? ['Review connector trust/schema freshness before reading calendar data.']
|
|
402
449
|
: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
2
|
import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
|
|
3
|
-
import { calendarWorkflows, connectorSignalsMatching, inboxWorkflows, methodIdsMatching, reminderWorkflows, taskWorkflows } from './agent-harness-personal-ops-discovery.ts';
|
|
3
|
+
import { calendarWorkflows, connectorSignalsMatching, inboxWorkflows, methodIdsMatching, reminderWorkflows, taskWorkflows, unavailableMethodIdsMatching } from './agent-harness-personal-ops-discovery.ts';
|
|
4
4
|
import { providerBackedQueueRecords } from './agent-harness-personal-ops-provider-records.ts';
|
|
5
5
|
import { providerBackedReminderRecords, providerBackedTaskRecords } from './agent-harness-personal-ops-provider-task-records.ts';
|
|
6
6
|
import { channelRecords, connectorRecords, localRecord, refreshableSavedRecordCount, reminderOperationRecords, routineReceiptRecord, savedProviderEffectReceiptRecords, savedReviewArtifactRecords, savedReviewQueueRecords, taskOperationRecords } from './agent-harness-personal-ops-records.ts';
|
|
@@ -15,8 +15,14 @@ export function buildLanes(
|
|
|
15
15
|
} = {},
|
|
16
16
|
): readonly PersonalOpsLane[] {
|
|
17
17
|
const snapshot = buildAgentWorkspaceRuntimeSnapshot(context);
|
|
18
|
+
// Readiness counts only DISPATCHABLE methods (W4-A3/W4-A5 honesty): a
|
|
19
|
+
// method the contract marks invokable:false has no serving route, so it
|
|
20
|
+
// must not make a lane read "ready"/"partial". The unavailable lists feed
|
|
21
|
+
// honest degraded wording instead.
|
|
18
22
|
const emailMethods = methodIdsMatching(['email', 'mail', 'imap', 'smtp']);
|
|
23
|
+
const unavailableEmailMethods = unavailableMethodIdsMatching(['email', 'mail', 'imap', 'smtp']);
|
|
19
24
|
const calendarMethods = methodIdsMatching(['calendar', 'caldav', 'agenda']);
|
|
25
|
+
const unavailableCalendarMethods = unavailableMethodIdsMatching(['calendar', 'caldav', 'agenda']);
|
|
20
26
|
const toolsByName = options.toolsByServer ?? new Map<string, readonly McpToolRecord[]>();
|
|
21
27
|
const schemasByQualifiedName = options.schemasByQualifiedName ?? new Map<string, McpToolSchema>();
|
|
22
28
|
const emailConnectors = connectorSignalsMatching(context, ['email', 'mail', 'imap', 'smtp', 'gmail'], { lane: 'inbox', toolsByServer: toolsByName, schemasByQualifiedName });
|
|
@@ -66,6 +72,8 @@ export function buildLanes(
|
|
|
66
72
|
? 'Saved inbox review artifacts and thread queue items are available for recap, local draft review, or promotion; no fresh email connector is currently ready.'
|
|
67
73
|
: inboxEffectReceiptRecords.length > 0
|
|
68
74
|
? 'Saved inbox provider-effect receipts are available for audit and follow-up; no fresh email connector is currently ready.'
|
|
75
|
+
: unavailableEmailMethods.length > 0
|
|
76
|
+
? `The operator contract advertises ${unavailableEmailMethods.length} email method(s), but they are unavailable (route not served by this daemon) and cannot be dispatched.`
|
|
69
77
|
: 'No email/IMAP/SMTP methods are present in the current GoodVibes SDK operator contract.',
|
|
70
78
|
next: inboxProviderRecords.length > 0
|
|
71
79
|
? 'Inspect one fresh thread record, summarize or draft locally, and use only the published confirmed follow-up routes for replies, sends, labels, or archive.'
|
|
@@ -82,7 +90,8 @@ export function buildLanes(
|
|
|
82
90
|
modelRoute: emailConnectors.length > 0 ? 'agent_harness mode:"mcp_servers" query:"email"' : 'host action:"methods" query:"email"',
|
|
83
91
|
signals: [
|
|
84
92
|
`${inboxProviderRecords.length} fresh provider-backed inbox thread record(s)`,
|
|
85
|
-
`${emailMethods.length} email-like daemon method(s)`,
|
|
93
|
+
`${emailMethods.length} dispatchable email-like daemon method(s)`,
|
|
94
|
+
...(unavailableEmailMethods.length > 0 ? [`${unavailableEmailMethods.length} advertised-but-unavailable email method(s) (route not served by this daemon)`] : []),
|
|
86
95
|
`${emailConnectors.length} email-like MCP connector(s)`,
|
|
87
96
|
`${inboxArtifactRecords.length} saved inbox review artifact(s)`,
|
|
88
97
|
`${inboxReviewQueueRecords.length} saved inbox thread queue item(s)`,
|
|
@@ -92,7 +101,7 @@ export function buildLanes(
|
|
|
92
101
|
],
|
|
93
102
|
methodIds: emailMethods,
|
|
94
103
|
connectorSignals: emailConnectors,
|
|
95
|
-
workflows: [...inboxWorkflows(emailMethods, emailConnectors), styleReplyAdditions.workflow],
|
|
104
|
+
workflows: [...inboxWorkflows(emailMethods, emailConnectors, unavailableEmailMethods), styleReplyAdditions.workflow],
|
|
96
105
|
liveRecords: [
|
|
97
106
|
...inboxProviderRecords,
|
|
98
107
|
...inboxReviewQueueRecords,
|
|
@@ -119,6 +128,8 @@ export function buildLanes(
|
|
|
119
128
|
? 'Saved calendar review artifacts and event queue items are available for agenda recap, reminder creation, or follow-up planning; no fresh calendar connector is currently ready.'
|
|
120
129
|
: calendarEffectReceiptRecords.length > 0
|
|
121
130
|
? 'Saved calendar provider-effect receipts are available for audit and follow-up; no fresh calendar connector is currently ready.'
|
|
131
|
+
: unavailableCalendarMethods.length > 0
|
|
132
|
+
? `The operator contract advertises ${unavailableCalendarMethods.length} calendar method(s), but they are unavailable (route not served by this daemon) and cannot be dispatched. The local Agent calendar (/calendar) remains available.`
|
|
122
133
|
: 'No calendar/CalDAV/agenda methods are present in the current GoodVibes SDK operator contract.',
|
|
123
134
|
next: calendarProviderRecords.length > 0
|
|
124
135
|
? 'Inspect one fresh event record, brief the agenda or conflicts locally, and use only the published confirmed follow-up routes for edits, RSVP, reschedule, or delete.'
|
|
@@ -135,7 +146,8 @@ export function buildLanes(
|
|
|
135
146
|
modelRoute: calendarConnectors.length > 0 ? 'agent_harness mode:"mcp_servers" query:"calendar"' : 'host action:"methods" query:"calendar"',
|
|
136
147
|
signals: [
|
|
137
148
|
`${calendarProviderRecords.length} fresh provider-backed calendar event record(s)`,
|
|
138
|
-
`${calendarMethods.length} calendar-like daemon method(s)`,
|
|
149
|
+
`${calendarMethods.length} dispatchable calendar-like daemon method(s)`,
|
|
150
|
+
...(unavailableCalendarMethods.length > 0 ? [`${unavailableCalendarMethods.length} advertised-but-unavailable calendar method(s) (route not served by this daemon)`] : []),
|
|
139
151
|
`${calendarConnectors.length} calendar-like MCP connector(s)`,
|
|
140
152
|
`${calendarArtifactRecords.length} saved calendar review artifact(s)`,
|
|
141
153
|
`${calendarReviewQueueRecords.length} saved calendar event queue item(s)`,
|
|
@@ -145,7 +157,7 @@ export function buildLanes(
|
|
|
145
157
|
],
|
|
146
158
|
methodIds: calendarMethods,
|
|
147
159
|
connectorSignals: calendarConnectors,
|
|
148
|
-
workflows: calendarWorkflows(calendarMethods, calendarConnectors),
|
|
160
|
+
workflows: calendarWorkflows(calendarMethods, calendarConnectors, unavailableCalendarMethods),
|
|
149
161
|
liveRecords: [
|
|
150
162
|
...calendarProviderRecords,
|
|
151
163
|
...calendarReviewQueueRecords,
|
|
@@ -107,7 +107,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
107
107
|
{
|
|
108
108
|
id: 'reminder-create',
|
|
109
109
|
label: 'Create confirmed reminder',
|
|
110
|
-
status: hasMethod(methodIds, 'schedules.create') ? deliveryConfigured ? 'ready' : 'attention' : 'needs-setup',
|
|
110
|
+
status: hasMethod(methodIds, 'automation.schedules.create') ? deliveryConfigured ? 'ready' : 'attention' : 'needs-setup',
|
|
111
111
|
summary: deliveryConfigured
|
|
112
112
|
? 'Create one connected reminder schedule with real timing and a visible delivery path.'
|
|
113
113
|
: 'Create one reminder only after confirming timing and delivery scope; no configured delivery target was detected.',
|
|
@@ -123,7 +123,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
123
123
|
{
|
|
124
124
|
id: 'autonomous-schedule-create',
|
|
125
125
|
label: 'Create autonomous schedule',
|
|
126
|
-
status: hasMethod(methodIds, 'schedules.create') ? 'ready' : 'needs-setup',
|
|
126
|
+
status: hasMethod(methodIds, 'automation.schedules.create') ? 'ready' : 'needs-setup',
|
|
127
127
|
summary: 'Create one visible autonomous schedule only when task, cadence, success criteria, and user request provenance are explicit.',
|
|
128
128
|
userRoute: 'Agent Workspace -> Automation -> Create schedule',
|
|
129
129
|
modelRoute: 'schedule action:"create" task:"..." successCriteria:"..." scheduleKind:"..." scheduleValue:"..." confirm:true explicitUserRequest:"..."',
|
|
@@ -134,7 +134,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
134
134
|
confirmationRequired: true,
|
|
135
135
|
},
|
|
136
136
|
];
|
|
137
|
-
if (hasMethod(methodIds, 'schedules.list')) {
|
|
137
|
+
if (hasMethod(methodIds, 'automation.schedules.list')) {
|
|
138
138
|
records.push({
|
|
139
139
|
id: 'schedule-list',
|
|
140
140
|
label: 'List connected schedules',
|
|
@@ -161,7 +161,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
161
161
|
confirmationRequired: true,
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
-
if (hasMethod(methodIds, 'schedules.run')) {
|
|
164
|
+
if (hasMethod(methodIds, 'automation.schedules.run')) {
|
|
165
165
|
records.push({
|
|
166
166
|
id: 'schedule-run-now',
|
|
167
167
|
label: 'Run schedule now',
|
|
@@ -176,7 +176,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
176
176
|
confirmationRequired: true,
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
|
-
if (hasMethod(methodIds, 'schedules.disable')) {
|
|
179
|
+
if (hasMethod(methodIds, 'automation.schedules.disable')) {
|
|
180
180
|
records.push({
|
|
181
181
|
id: 'schedule-pause',
|
|
182
182
|
label: 'Pause connected schedule',
|
|
@@ -191,7 +191,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
191
191
|
confirmationRequired: true,
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
-
if (hasMethod(methodIds, 'schedules.enable')) {
|
|
194
|
+
if (hasMethod(methodIds, 'automation.schedules.enable')) {
|
|
195
195
|
records.push({
|
|
196
196
|
id: 'schedule-resume',
|
|
197
197
|
label: 'Resume connected schedule',
|
|
@@ -206,7 +206,7 @@ export function reminderOperationRecords(methodIds: readonly string[], deliveryC
|
|
|
206
206
|
confirmationRequired: true,
|
|
207
207
|
});
|
|
208
208
|
}
|
|
209
|
-
if (hasMethod(methodIds, 'schedules.delete')) {
|
|
209
|
+
if (hasMethod(methodIds, 'automation.schedules.delete')) {
|
|
210
210
|
records.push({
|
|
211
211
|
id: 'schedule-delete',
|
|
212
212
|
label: 'Delete connected schedule',
|
|
@@ -28,6 +28,12 @@ export interface OperatorContractMethod {
|
|
|
28
28
|
readonly title?: string;
|
|
29
29
|
readonly description?: string;
|
|
30
30
|
readonly category?: string;
|
|
31
|
+
/**
|
|
32
|
+
* W4-A3 capability-advertisement honesty: false means the method is
|
|
33
|
+
* cataloged but NOT dispatchable (no route/handler serves it). Absent
|
|
34
|
+
* means invokable (older contracts predate the flag).
|
|
35
|
+
*/
|
|
36
|
+
readonly invokable?: boolean;
|
|
31
37
|
readonly http?: {
|
|
32
38
|
readonly method?: string;
|
|
33
39
|
readonly path?: string;
|