@pellux/goodvibes-agent 1.1.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +176 -0
- package/README.md +46 -30
- package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
- package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
- package/dist/package/main.js +132662 -91399
- package/docs/README.md +14 -7
- package/docs/channels-remote-and-api.md +8 -5
- package/docs/connected-host.md +14 -12
- package/docs/getting-started.md +56 -30
- package/docs/knowledge-artifacts-and-multimodal.md +7 -5
- package/docs/project-planning.md +2 -2
- package/docs/providers-and-routing.md +11 -3
- package/docs/tools-and-commands.md +114 -45
- package/docs/voice-and-live-tts.md +3 -1
- package/package.json +3 -2
- package/release/release-notes.md +130 -5
- package/release/release-readiness.json +120 -90
- package/src/agent/assistant-cockpit.ts +247 -0
- package/src/agent/autonomy-schedule-format.ts +96 -0
- package/src/agent/autonomy-schedule.ts +514 -0
- package/src/agent/channel-delivery-receipts.ts +292 -0
- package/src/agent/competitive-feature-inventory.ts +296 -0
- package/src/agent/document-registry-types.ts +106 -0
- package/src/agent/document-registry.ts +734 -0
- package/src/agent/harness-control.ts +2 -2
- package/src/agent/memory-prompt.ts +2 -2
- package/src/agent/operator-actions.ts +50 -6
- package/src/agent/persona-registry.ts +8 -0
- package/src/agent/project-context-files.ts +273 -0
- package/src/agent/prompt-context-receipts.ts +502 -0
- package/src/agent/reminder-schedule-format.ts +16 -2
- package/src/agent/research-run-registry.ts +582 -0
- package/src/agent/research-source-registry.ts +441 -0
- package/src/agent/routine-registry.ts +19 -3
- package/src/agent/routine-schedule-format.ts +27 -2
- package/src/agent/runtime-profile-starters.ts +7 -0
- package/src/agent/runtime-profile.ts +106 -4
- package/src/agent/schedule-edit-format.ts +129 -0
- package/src/agent/schedule-edit.ts +496 -0
- package/src/agent/schedule-next-routes.ts +42 -0
- package/src/agent/setup-wizard-checkpoint.ts +140 -0
- package/src/agent/setup-wizard.ts +447 -0
- package/src/agent/skill-registry-types.ts +102 -0
- package/src/agent/skill-registry.ts +81 -107
- package/src/agent/vibe-confirmation-routes.ts +62 -0
- package/src/agent/vibe-file.ts +285 -0
- package/src/cli/agent-knowledge-command.ts +45 -2
- package/src/cli/help.ts +3 -0
- package/src/cli/profiles-command.ts +25 -11
- package/src/config/agent-settings-policy.ts +1 -18
- package/src/input/agent-workspace-activation.ts +53 -0
- package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
- package/src/input/agent-workspace-artifact-metadata.ts +17 -0
- package/src/input/agent-workspace-basic-command-editors.ts +5 -1
- package/src/input/agent-workspace-categories.ts +153 -155
- package/src/input/agent-workspace-channel-triage.ts +481 -0
- package/src/input/agent-workspace-channels.ts +263 -0
- package/src/input/agent-workspace-command-editor.ts +152 -0
- package/src/input/agent-workspace-context-snapshot.ts +349 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
- package/src/input/agent-workspace-document-editor.ts +502 -0
- package/src/input/agent-workspace-document-ops-editor.ts +523 -0
- package/src/input/agent-workspace-host-category.ts +1 -1
- package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
- package/src/input/agent-workspace-model-compare-editor.ts +376 -0
- package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
- package/src/input/agent-workspace-model-compare-types.ts +82 -0
- package/src/input/agent-workspace-model-compare-utils.ts +35 -0
- package/src/input/agent-workspace-onboarding-categories.ts +141 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
- package/src/input/agent-workspace-operations-command-editors.ts +22 -0
- package/src/input/agent-workspace-research-report-editor.ts +212 -0
- package/src/input/agent-workspace-research-run-editor.ts +144 -0
- package/src/input/agent-workspace-research-source-editor.ts +167 -0
- package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
- package/src/input/agent-workspace-review-packet-utils.ts +265 -0
- package/src/input/agent-workspace-settings.ts +262 -30
- package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
- package/src/input/agent-workspace-setup-snapshot.ts +198 -0
- package/src/input/agent-workspace-setup.ts +34 -1
- package/src/input/agent-workspace-snapshot.ts +162 -147
- package/src/input/agent-workspace-types.ts +287 -2
- package/src/input/agent-workspace.ts +18 -0
- package/src/input/command-registry.ts +9 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
- package/src/input/commands/channels-runtime.ts +102 -114
- package/src/input/commands/delegation-runtime.ts +87 -24
- package/src/input/commands/knowledge-browser-flags.ts +12 -0
- package/src/input/commands/knowledge.ts +28 -14
- package/src/input/commands/operator-actions-runtime.ts +6 -3
- package/src/input/commands/schedule-runtime.ts +71 -14
- package/src/input/commands/session-content.ts +2 -2
- package/src/input/commands/shell-core.ts +2 -0
- package/src/input/commands/vibe-runtime.ts +140 -0
- package/src/input/commands.ts +2 -0
- package/src/input/connected-host-routes.ts +123 -0
- package/src/input/settings-modal-types.ts +14 -0
- package/src/main.ts +15 -15
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/agent-workspace-context-lines.ts +686 -0
- package/src/renderer/agent-workspace.ts +30 -419
- package/src/renderer/settings-modal-helpers.ts +12 -0
- package/src/renderer/settings-modal.ts +12 -0
- package/src/runtime/bootstrap-command-context.ts +11 -1
- package/src/runtime/bootstrap-command-parts.ts +14 -3
- package/src/runtime/bootstrap-core.ts +43 -1
- package/src/runtime/bootstrap-shell.ts +6 -0
- package/src/runtime/bootstrap.ts +113 -30
- package/src/runtime/connected-host-auth.ts +3 -2
- package/src/runtime/execution-ledger.ts +231 -0
- package/src/runtime/services.ts +88 -10
- package/src/runtime/tool-permission-safety.ts +287 -0
- package/src/shell/session-continuity-hints.ts +39 -0
- package/src/tools/agent-artifacts-tool.ts +606 -0
- package/src/tools/agent-audit-tool.ts +155 -0
- package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
- package/src/tools/agent-autonomy-tool.ts +140 -0
- package/src/tools/agent-channel-send-tool.ts +24 -2
- package/src/tools/agent-channels-tool.ts +140 -0
- package/src/tools/agent-computer-tool.ts +168 -0
- package/src/tools/agent-context-policy.ts +267 -10
- package/src/tools/agent-context-tool.ts +143 -0
- package/src/tools/agent-delegation-tool.ts +128 -0
- package/src/tools/agent-device-tool.ts +240 -0
- package/src/tools/agent-documents-tool.ts +684 -0
- package/src/tools/agent-execution-tool.ts +230 -0
- package/src/tools/agent-harness-agent-orchestration.ts +703 -0
- package/src/tools/agent-harness-autonomy-intake.ts +785 -0
- package/src/tools/agent-harness-autonomy-live-records.ts +588 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
- package/src/tools/agent-harness-autonomy-queue.ts +560 -0
- package/src/tools/agent-harness-background-processes-types.ts +35 -0
- package/src/tools/agent-harness-background-processes.ts +794 -0
- package/src/tools/agent-harness-browser-cockpit-route.ts +219 -0
- package/src/tools/agent-harness-browser-control.ts +338 -0
- package/src/tools/agent-harness-channel-metadata.ts +168 -10
- package/src/tools/agent-harness-cli-command-policy.ts +110 -0
- package/src/tools/agent-harness-command-catalog.ts +4 -2
- package/src/tools/agent-harness-command-runner.ts +42 -0
- package/src/tools/agent-harness-connected-host-status.ts +7 -4
- package/src/tools/agent-harness-delegation-posture.ts +234 -19
- package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
- package/src/tools/agent-harness-document-ops-types.ts +72 -0
- package/src/tools/agent-harness-document-ops.ts +671 -0
- package/src/tools/agent-harness-execution-history.ts +489 -0
- package/src/tools/agent-harness-execution-posture.ts +382 -0
- package/src/tools/agent-harness-file-recovery.ts +135 -0
- package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
- package/src/tools/agent-harness-learning-curator-common.ts +102 -0
- package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
- package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
- package/src/tools/agent-harness-learning-curator-types.ts +151 -0
- package/src/tools/agent-harness-learning-curator.ts +417 -0
- package/src/tools/agent-harness-local-model-benchmarks.ts +199 -0
- package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
- package/src/tools/agent-harness-local-model-endpoints.ts +673 -0
- package/src/tools/agent-harness-media-posture.ts +208 -2
- package/src/tools/agent-harness-memory-posture.ts +556 -0
- package/src/tools/agent-harness-metadata.ts +100 -168
- package/src/tools/agent-harness-mode-catalog.ts +84 -33
- package/src/tools/agent-harness-model-catalog.ts +162 -0
- package/src/tools/agent-harness-model-provider-health.ts +186 -0
- package/src/tools/agent-harness-model-readiness.ts +406 -0
- package/src/tools/agent-harness-model-routing-types.ts +266 -0
- package/src/tools/agent-harness-model-routing-utils.ts +30 -0
- package/src/tools/agent-harness-model-routing.ts +137 -190
- package/src/tools/agent-harness-operator-methods.ts +115 -133
- package/src/tools/agent-harness-pairing-posture.ts +431 -18
- package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
- package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
- package/src/tools/agent-harness-personal-ops-lanes.ts +204 -0
- package/src/tools/agent-harness-personal-ops-records.ts +732 -0
- package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
- package/src/tools/agent-harness-personal-ops-types.ts +222 -0
- package/src/tools/agent-harness-personal-ops.ts +606 -0
- package/src/tools/agent-harness-project-context.ts +144 -0
- package/src/tools/agent-harness-prompt-context.ts +589 -0
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-release-evidence.ts +9 -7
- package/src/tools/agent-harness-release-readiness.ts +9 -7
- package/src/tools/agent-harness-research-briefing.ts +427 -0
- package/src/tools/agent-harness-research-queue.ts +250 -0
- package/src/tools/agent-harness-research-runs.ts +295 -0
- package/src/tools/agent-harness-research-workflow.ts +322 -0
- package/src/tools/agent-harness-security-posture.ts +2 -2
- package/src/tools/agent-harness-service-posture.ts +9 -9
- package/src/tools/agent-harness-session-metadata.ts +3 -3
- package/src/tools/agent-harness-setup-connected-host.ts +501 -0
- package/src/tools/agent-harness-setup-describe.ts +557 -0
- package/src/tools/agent-harness-setup-handoffs.ts +502 -0
- package/src/tools/agent-harness-setup-model-helpers.ts +101 -0
- package/src/tools/agent-harness-setup-plan.ts +253 -0
- package/src/tools/agent-harness-setup-posture-types.ts +218 -0
- package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
- package/src/tools/agent-harness-setup-posture.ts +449 -221
- package/src/tools/agent-harness-setup-smoke.ts +508 -0
- package/src/tools/agent-harness-sudo-posture.ts +114 -0
- package/src/tools/agent-harness-tool-schema.ts +120 -7
- package/src/tools/agent-harness-tool-types.ts +78 -0
- package/src/tools/agent-harness-tool-utils.ts +43 -0
- package/src/tools/agent-harness-tool.ts +311 -370
- package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
- package/src/tools/agent-harness-ui-surface-types.ts +46 -0
- package/src/tools/agent-harness-vibe-health.ts +105 -0
- package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
- package/src/tools/agent-harness-workspace-actions.ts +114 -8
- package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
- package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
- package/src/tools/agent-host-tool.ts +159 -0
- package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
- package/src/tools/agent-learning-consolidation-core.ts +327 -0
- package/src/tools/agent-learning-consolidation-tool.ts +758 -0
- package/src/tools/agent-memory-tool.ts +214 -0
- package/src/tools/agent-model-compare-export.ts +315 -0
- package/src/tools/agent-model-compare-handoff.ts +592 -0
- package/src/tools/agent-model-compare-judgment.ts +633 -0
- package/src/tools/agent-model-compare-run.ts +722 -0
- package/src/tools/agent-model-compare-tool.ts +698 -0
- package/src/tools/agent-model-compare-types.ts +191 -0
- package/src/tools/agent-model-compare-utils.ts +93 -0
- package/src/tools/agent-models-tool.ts +208 -0
- package/src/tools/agent-operator-action-tool.ts +1 -1
- package/src/tools/agent-operator-method-tool.ts +643 -0
- package/src/tools/agent-personal-ops-tool.ts +197 -0
- package/src/tools/agent-policy-explanation.ts +415 -0
- package/src/tools/agent-research-report-tool.ts +608 -0
- package/src/tools/agent-research-runs-tool.ts +434 -0
- package/src/tools/agent-research-sources-tool.ts +443 -0
- package/src/tools/agent-research-tool.ts +687 -0
- package/src/tools/agent-review-packet-presets-core.ts +757 -0
- package/src/tools/agent-review-packet-presets-tool.ts +466 -0
- package/src/tools/agent-review-packet-share-tool.ts +305 -0
- package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
- package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
- package/src/tools/agent-route-planner-candidates-work.ts +251 -0
- package/src/tools/agent-route-planner-helpers.ts +667 -0
- package/src/tools/agent-route-planner.ts +185 -0
- package/src/tools/agent-route-tool.ts +105 -0
- package/src/tools/agent-schedule-edit-tool.ts +210 -0
- package/src/tools/agent-schedule-tool.ts +282 -0
- package/src/tools/agent-security-tool.ts +145 -0
- package/src/tools/agent-sessions-tool.ts +122 -0
- package/src/tools/agent-settings-import-tool.ts +104 -0
- package/src/tools/agent-settings-tool.ts +176 -0
- package/src/tools/agent-setup-tool.ts +226 -0
- package/src/tools/agent-support-tool.ts +122 -0
- package/src/tools/agent-terminal-process-tools.ts +167 -0
- package/src/tools/agent-tool-policy-guard-types.ts +77 -0
- package/src/tools/agent-tool-policy-guard.ts +110 -89
- package/src/tools/agent-vibe-tool.ts +297 -0
- package/src/tools/agent-work-plan-tool.ts +265 -6
- package/src/tools/agent-workspace-tool.ts +305 -0
- package/src/tools/artifact-archive.ts +169 -0
- package/src/tools/tool-definition-compaction.ts +36 -0
- package/src/tools/tool-execution-safety.ts +41 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
2
|
+
import { hasAny } from './agent-harness-personal-ops-discovery.ts';
|
|
3
|
+
import { laneById, liveRecordById, operationSummary, recordOperationSummary, workflowById, workflowExecutionPlan, workflowMissingFields } from './agent-harness-personal-ops-runner.ts';
|
|
4
|
+
import type { PersonalOpsConnectorSignal, PersonalOpsConnectorTool, PersonalOpsIntakeCandidate, PersonalOpsLane, PersonalOpsLiveRecord, PersonalOpsWorkflowStatus } from './agent-harness-personal-ops-types.ts';
|
|
5
|
+
|
|
6
|
+
export function toolPreferenceScore(tool: PersonalOpsConnectorTool, preferredTokens: readonly string[]): number {
|
|
7
|
+
const text = [tool.name, tool.description ?? '', tool.capability].join('\n').toLowerCase();
|
|
8
|
+
const preferred = preferredTokens.findIndex((token) => text.includes(token));
|
|
9
|
+
const preferredScore = preferred >= 0 ? 1_000 - preferred * 50 : 0;
|
|
10
|
+
const schemaScore = tool.schemaRoute ? 100 : 0;
|
|
11
|
+
const requiredFieldPenalty = (tool.requiredFields?.length ?? 0) * 5;
|
|
12
|
+
return preferredScore + schemaScore - requiredFieldPenalty;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function selectConnectorTool(
|
|
16
|
+
lane: PersonalOpsLane,
|
|
17
|
+
effect: PersonalOpsConnectorTool['effect'],
|
|
18
|
+
capability: string,
|
|
19
|
+
preferredTokens: readonly string[],
|
|
20
|
+
): { readonly signal: PersonalOpsConnectorSignal; readonly tool: PersonalOpsConnectorTool } | undefined {
|
|
21
|
+
const candidates = (lane.connectorSignals ?? []).flatMap((signal) => {
|
|
22
|
+
const tools = effect === 'read-only' ? signal.readTools ?? [] : signal.writeTools ?? [];
|
|
23
|
+
return tools
|
|
24
|
+
.filter((tool) => tool.capability === capability)
|
|
25
|
+
.map((tool) => ({ signal, tool }));
|
|
26
|
+
});
|
|
27
|
+
return candidates
|
|
28
|
+
.sort((left, right) => {
|
|
29
|
+
const statusDelta = (right.signal.status === 'ready' ? 1 : 0) - (left.signal.status === 'ready' ? 1 : 0);
|
|
30
|
+
if (statusDelta !== 0) return statusDelta;
|
|
31
|
+
return toolPreferenceScore(right.tool, preferredTokens) - toolPreferenceScore(left.tool, preferredTokens)
|
|
32
|
+
|| left.tool.name.localeCompare(right.tool.name);
|
|
33
|
+
})[0];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function workflowCandidate(options: {
|
|
37
|
+
readonly lane: PersonalOpsLane;
|
|
38
|
+
readonly workflowId: string;
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly label: string;
|
|
41
|
+
readonly confidence: PersonalOpsIntakeCandidate['confidence'];
|
|
42
|
+
readonly why: string;
|
|
43
|
+
readonly operation?: { readonly signal: PersonalOpsConnectorSignal; readonly tool: PersonalOpsConnectorTool };
|
|
44
|
+
readonly followUpOperation?: { readonly signal: PersonalOpsConnectorSignal; readonly tool: PersonalOpsConnectorTool };
|
|
45
|
+
readonly includeParameters: boolean;
|
|
46
|
+
readonly readOnlyNext: string;
|
|
47
|
+
readonly mutationBoundary: string;
|
|
48
|
+
}): PersonalOpsIntakeCandidate | null {
|
|
49
|
+
const workflow = workflowById(options.lane, options.workflowId);
|
|
50
|
+
if (!workflow) return null;
|
|
51
|
+
const operation = options.operation?.tool;
|
|
52
|
+
const modelRoute = operation?.schemaRoute ?? workflow.modelRoute;
|
|
53
|
+
const inspectRoutes = [
|
|
54
|
+
...(operation?.schemaRoute ? [operation.schemaRoute] : []),
|
|
55
|
+
...workflow.inspectRoutes,
|
|
56
|
+
];
|
|
57
|
+
const missingFields = workflowMissingFields(options.lane, workflow, operation);
|
|
58
|
+
const nextSteps = workflow.status === 'ready'
|
|
59
|
+
? [
|
|
60
|
+
`Inspect ${operation?.schemaRoute ? 'the selected connector schema' : 'the exact route'} before using live personal data.`,
|
|
61
|
+
options.readOnlyNext,
|
|
62
|
+
options.mutationBoundary,
|
|
63
|
+
]
|
|
64
|
+
: workflow.status === 'attention'
|
|
65
|
+
? [
|
|
66
|
+
'Repair connector trust, connection, or schema freshness before using live personal data.',
|
|
67
|
+
'Re-run this intake request after the connector reports ready.',
|
|
68
|
+
]
|
|
69
|
+
: [
|
|
70
|
+
`Set up a ${options.lane.id === 'inbox' ? 'mail' : options.lane.id} connector or daemon method first.`,
|
|
71
|
+
'Re-run this intake request after setup so the route can bind to a concrete tool.',
|
|
72
|
+
];
|
|
73
|
+
return {
|
|
74
|
+
id: options.id,
|
|
75
|
+
label: options.label,
|
|
76
|
+
laneId: options.lane.id,
|
|
77
|
+
workflowId: workflow.id,
|
|
78
|
+
status: workflow.status,
|
|
79
|
+
confidence: options.confidence,
|
|
80
|
+
why: options.why,
|
|
81
|
+
modelRoute,
|
|
82
|
+
inspectRoutes: [...new Set(inspectRoutes)],
|
|
83
|
+
requiresConfirmation: false,
|
|
84
|
+
safetyBoundary: workflow.runBoundary,
|
|
85
|
+
nextSteps,
|
|
86
|
+
...(operation ? { operation: operationSummary(operation, options.operation?.signal, options.includeParameters) } : {}),
|
|
87
|
+
...(options.followUpOperation ? { followUpOperation: operationSummary(options.followUpOperation.tool, options.followUpOperation.signal, options.includeParameters) } : {}),
|
|
88
|
+
executionPlan: workflowExecutionPlan({
|
|
89
|
+
lane: options.lane,
|
|
90
|
+
workflow,
|
|
91
|
+
operation: options.operation,
|
|
92
|
+
followUpOperation: options.followUpOperation,
|
|
93
|
+
includeParameters: options.includeParameters,
|
|
94
|
+
readOnlyNext: options.readOnlyNext,
|
|
95
|
+
mutationBoundary: options.mutationBoundary,
|
|
96
|
+
}),
|
|
97
|
+
...(operation?.requiredFields && operation.requiredFields.length > 0 ? { requiredFields: operation.requiredFields } : {}),
|
|
98
|
+
...(missingFields && missingFields.length > 0 ? { missingFields } : {}),
|
|
99
|
+
...(workflow.status === 'needs-setup' ? { userQuestion: `Which ${options.lane.id === 'inbox' ? 'email' : options.lane.id} connector should GoodVibes use?` } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function recordStatusAsWorkflowStatus(status: string): PersonalOpsWorkflowStatus {
|
|
104
|
+
if (status === 'ready') return 'ready';
|
|
105
|
+
if (status === 'needs-setup') return 'needs-setup';
|
|
106
|
+
return 'attention';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function recordCandidate(options: {
|
|
110
|
+
readonly lane: PersonalOpsLane;
|
|
111
|
+
readonly recordId: string;
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly label: string;
|
|
114
|
+
readonly confidence: PersonalOpsIntakeCandidate['confidence'];
|
|
115
|
+
readonly why: string;
|
|
116
|
+
readonly includeParameters: boolean;
|
|
117
|
+
readonly requiresConfirmation: boolean;
|
|
118
|
+
readonly missingFields?: readonly string[];
|
|
119
|
+
readonly userQuestion?: string;
|
|
120
|
+
readonly nextSteps: readonly string[];
|
|
121
|
+
readonly safetyBoundary: string;
|
|
122
|
+
}): PersonalOpsIntakeCandidate | null {
|
|
123
|
+
const record = liveRecordById(options.lane, options.recordId);
|
|
124
|
+
if (!record) return null;
|
|
125
|
+
return {
|
|
126
|
+
id: options.id,
|
|
127
|
+
label: options.label,
|
|
128
|
+
laneId: options.lane.id,
|
|
129
|
+
status: recordStatusAsWorkflowStatus(record.status),
|
|
130
|
+
confidence: options.confidence,
|
|
131
|
+
why: options.why,
|
|
132
|
+
modelRoute: record.modelRoute,
|
|
133
|
+
inspectRoutes: [record.modelRoute, `personal_ops action:"lane" laneId:"${options.lane.id}"`],
|
|
134
|
+
requiresConfirmation: options.requiresConfirmation,
|
|
135
|
+
safetyBoundary: options.safetyBoundary,
|
|
136
|
+
nextSteps: options.nextSteps,
|
|
137
|
+
operation: recordOperationSummary(record, options.includeParameters),
|
|
138
|
+
...(record.requiredFields && record.requiredFields.length > 0 ? { requiredFields: record.requiredFields } : {}),
|
|
139
|
+
...(options.missingFields && options.missingFields.length > 0 ? { missingFields: options.missingFields } : {}),
|
|
140
|
+
...(options.userQuestion ? { userQuestion: options.userQuestion } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function setupCandidate(lane: PersonalOpsLane, request: string): PersonalOpsIntakeCandidate {
|
|
145
|
+
return {
|
|
146
|
+
id: 'personal-ops-map-first',
|
|
147
|
+
label: 'Map Personal Ops readiness first',
|
|
148
|
+
laneId: lane.id,
|
|
149
|
+
status: lane.status === 'gap' || lane.status === 'needs-setup' ? 'needs-setup' : lane.status === 'ready' ? 'ready' : 'attention',
|
|
150
|
+
confidence: 'low',
|
|
151
|
+
why: `The request "${previewHarnessText(request, 80)}" does not clearly name one personal operation, so the safest next step is a readiness map.`,
|
|
152
|
+
modelRoute: 'personal_ops action:"status"',
|
|
153
|
+
inspectRoutes: ['personal_ops action:"status"'],
|
|
154
|
+
requiresConfirmation: false,
|
|
155
|
+
safetyBoundary: 'Readiness inspection is read-only; personal-data reads and all sends or mutations remain on their owning routes.',
|
|
156
|
+
nextSteps: [
|
|
157
|
+
'Inspect Personal Ops readiness.',
|
|
158
|
+
'Choose one lane: inbox, calendar, notes, tasks, reminders, routines, or delivery.',
|
|
159
|
+
'Re-run personal_ops action:"intake" with the specific user request.',
|
|
160
|
+
],
|
|
161
|
+
missingFields: ['specific personal operation goal'],
|
|
162
|
+
userQuestion: 'Should this be inbox, calendar, notes, tasks, reminders, routines, or delivery work?',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function candidatePriority(candidate: PersonalOpsIntakeCandidate): number {
|
|
167
|
+
const confidence = candidate.confidence === 'high' ? 300 : candidate.confidence === 'medium' ? 200 : 100;
|
|
168
|
+
const readiness = candidate.status === 'ready' ? 30 : candidate.status === 'attention' ? 15 : 0;
|
|
169
|
+
return confidence + readiness;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function buildPersonalOpsIntakeCandidates(
|
|
173
|
+
request: string,
|
|
174
|
+
lanes: readonly PersonalOpsLane[],
|
|
175
|
+
includeParameters: boolean,
|
|
176
|
+
): readonly PersonalOpsIntakeCandidate[] {
|
|
177
|
+
const lower = request.toLowerCase();
|
|
178
|
+
const inboxLane = laneById(lanes, 'inbox');
|
|
179
|
+
const calendarLane = laneById(lanes, 'calendar');
|
|
180
|
+
const taskLane = laneById(lanes, 'tasks');
|
|
181
|
+
const reminderLane = laneById(lanes, 'reminders');
|
|
182
|
+
const routineLane = laneById(lanes, 'routines');
|
|
183
|
+
const deliveryLane = laneById(lanes, 'delivery');
|
|
184
|
+
const candidates: PersonalOpsIntakeCandidate[] = [];
|
|
185
|
+
|
|
186
|
+
const asksInbox = hasAny(lower, ['inbox', 'email', 'mail', 'gmail', 'imap', 'message', 'thread']);
|
|
187
|
+
const asksReply = hasAny(lower, ['draft', 'reply', 'respond', 'compose']);
|
|
188
|
+
const asksCalendar = hasAny(lower, ['calendar', 'agenda', 'caldav', 'event', 'meeting', 'availability', 'freebusy', 'free busy']);
|
|
189
|
+
const asksConflict = hasAny(lower, ['conflict', 'overlap', 'double-book', 'double booked', 'availability', 'freebusy', 'free busy']);
|
|
190
|
+
const asksReminder = hasAny(lower, ['remind', 'reminder', 'follow up', 'follow-up', 'ping me', 'notify me']);
|
|
191
|
+
const asksTask = hasAny(lower, ['task', 'todo', 'to-do', 'work item', 'work plan', 'host task']);
|
|
192
|
+
const asksNote = hasAny(lower, ['note', 'scratchpad', 'capture', 'jot down']);
|
|
193
|
+
const asksRoutine = hasAny(lower, ['routine', 'checklist', 'repeatable']);
|
|
194
|
+
const asksDelivery = !asksReminder && hasAny(lower, ['deliver', 'send', 'channel', 'slack', 'discord', 'telegram', 'sms', 'notification']);
|
|
195
|
+
|
|
196
|
+
if (asksInbox && asksReply) {
|
|
197
|
+
const readTool = selectConnectorTool(inboxLane, 'read-only', 'inbox-read', ['get_thread', 'thread', 'read', 'message', 'fetch', 'get']);
|
|
198
|
+
const writeTool = selectConnectorTool(inboxLane, 'confirmed-effect', 'inbox-write', ['send_reply', 'reply', 'draft', 'compose', 'send']);
|
|
199
|
+
const candidate = workflowCandidate({
|
|
200
|
+
lane: inboxLane,
|
|
201
|
+
workflowId: 'inbox-draft-reply',
|
|
202
|
+
id: 'inbox-draft-reply',
|
|
203
|
+
label: 'Draft an inbox reply without sending',
|
|
204
|
+
confidence: 'high',
|
|
205
|
+
why: 'The request mentions drafting or replying to inbox content.',
|
|
206
|
+
operation: readTool,
|
|
207
|
+
followUpOperation: writeTool,
|
|
208
|
+
includeParameters,
|
|
209
|
+
readOnlyNext: 'Read the selected thread through the reviewed connector, then draft the reply in chat.',
|
|
210
|
+
mutationBoundary: 'Sending, labeling, archiving, moving, or deleting remains a separate confirmed connector action.',
|
|
211
|
+
});
|
|
212
|
+
if (candidate) candidates.push(candidate);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (asksInbox) {
|
|
216
|
+
const readTool = selectConnectorTool(inboxLane, 'read-only', 'inbox-read', ['search', 'list', 'unread', 'query', 'find', 'messages', 'inbox']);
|
|
217
|
+
const candidate = workflowCandidate({
|
|
218
|
+
lane: inboxLane,
|
|
219
|
+
workflowId: 'inbox-triage-briefing',
|
|
220
|
+
id: 'inbox-triage-briefing',
|
|
221
|
+
label: 'Triage inbox messages',
|
|
222
|
+
confidence: asksReply ? 'medium' : 'high',
|
|
223
|
+
why: 'The request asks for inbox, email, message, or thread triage.',
|
|
224
|
+
operation: readTool,
|
|
225
|
+
includeParameters,
|
|
226
|
+
readOnlyNext: 'Run only a bounded read/list/search route, then summarize priorities, risks, and suggested next actions in chat.',
|
|
227
|
+
mutationBoundary: 'Reply, send, label, archive, move, and delete actions require a separate explicit confirmation.',
|
|
228
|
+
});
|
|
229
|
+
if (candidate) candidates.push(candidate);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (asksCalendar) {
|
|
233
|
+
const workflowId = asksConflict ? 'calendar-conflict-scan' : 'calendar-agenda-briefing';
|
|
234
|
+
const readTool = selectConnectorTool(calendarLane, 'read-only', 'calendar-read', asksConflict
|
|
235
|
+
? ['freebusy', 'availability', 'list', 'events', 'upcoming']
|
|
236
|
+
: ['list', 'upcoming', 'agenda', 'events', 'search']);
|
|
237
|
+
const writeTool = selectConnectorTool(calendarLane, 'confirmed-effect', 'calendar-write', ['create', 'update', 'reschedule', 'edit', 'delete']);
|
|
238
|
+
const candidate = workflowCandidate({
|
|
239
|
+
lane: calendarLane,
|
|
240
|
+
workflowId,
|
|
241
|
+
id: workflowId,
|
|
242
|
+
label: asksConflict ? 'Scan calendar conflicts' : 'Brief calendar agenda',
|
|
243
|
+
confidence: 'high',
|
|
244
|
+
why: asksConflict ? 'The request asks about conflicts, overlap, or availability.' : 'The request asks for agenda, event, meeting, or calendar context.',
|
|
245
|
+
operation: readTool,
|
|
246
|
+
followUpOperation: writeTool,
|
|
247
|
+
includeParameters,
|
|
248
|
+
readOnlyNext: asksConflict
|
|
249
|
+
? 'Read a bounded calendar window and report overlaps, prep gaps, and reminder suggestions.'
|
|
250
|
+
: 'Read a bounded calendar window and summarize agenda context, prep items, and risks.',
|
|
251
|
+
mutationBoundary: 'Creating, editing, deleting, RSVP, or rescheduling events requires a separate explicit confirmation.',
|
|
252
|
+
});
|
|
253
|
+
if (candidate) candidates.push(candidate);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (asksReminder) {
|
|
257
|
+
const candidate = recordCandidate({
|
|
258
|
+
lane: reminderLane,
|
|
259
|
+
recordId: 'reminder-create',
|
|
260
|
+
id: 'confirmed-reminder-request',
|
|
261
|
+
label: 'Create one confirmed reminder',
|
|
262
|
+
confidence: 'high',
|
|
263
|
+
why: 'The request asks GoodVibes to remind, notify, ping, or follow up with the user.',
|
|
264
|
+
includeParameters,
|
|
265
|
+
requiresConfirmation: true,
|
|
266
|
+
missingFields: ['title', 'scheduleKind', 'scheduleValue', 'explicitUserRequest'],
|
|
267
|
+
userQuestion: 'What exact reminder title and time should GoodVibes use?',
|
|
268
|
+
safetyBoundary: 'Reminder creation requires confirm:true and explicitUserRequest; vague follow-up ideas stay as notes or work-plan items.',
|
|
269
|
+
nextSteps: [
|
|
270
|
+
'Collect the reminder title, exact timing, timezone/cadence, and delivery scope.',
|
|
271
|
+
'Inspect delivery channel readiness when the reminder must reach the user outside the terminal.',
|
|
272
|
+
'Create exactly one reminder through the confirmed route.',
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
if (candidate) candidates.push(candidate);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (asksTask) {
|
|
279
|
+
const recordId = hasAny(lower, ['host task', 'running task', 'task status', 'inspect task']) ? 'host-tasks-list' : 'workplan-add';
|
|
280
|
+
const candidate = recordCandidate({
|
|
281
|
+
lane: taskLane,
|
|
282
|
+
recordId,
|
|
283
|
+
id: recordId === 'host-tasks-list' ? 'host-task-review' : 'visible-work-item',
|
|
284
|
+
label: recordId === 'host-tasks-list' ? 'Review connected-host tasks' : 'Create a visible work item',
|
|
285
|
+
confidence: 'high',
|
|
286
|
+
why: recordId === 'host-tasks-list'
|
|
287
|
+
? 'The request asks about host task state.'
|
|
288
|
+
: 'The request asks for task or work-plan tracking.',
|
|
289
|
+
includeParameters,
|
|
290
|
+
requiresConfirmation: false,
|
|
291
|
+
missingFields: recordId === 'workplan-add' ? ['title'] : undefined,
|
|
292
|
+
userQuestion: recordId === 'workplan-add' ? 'What short title should this visible work item use?' : undefined,
|
|
293
|
+
safetyBoundary: 'Agent-owned work-plan edits stay local and visible; connected-host task controls require exact ids and confirmation.',
|
|
294
|
+
nextSteps: recordId === 'host-tasks-list'
|
|
295
|
+
? ['List host tasks.', 'Inspect one exact host task id before considering retry or cancel.', 'Use confirmed host controls only when the user asks.']
|
|
296
|
+
: ['Create a concise visible work-plan item.', 'Keep status changes visible as the work proceeds.', 'Use host tasks only when execution needs connected-host ownership.'],
|
|
297
|
+
});
|
|
298
|
+
if (candidate) candidates.push(candidate);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (asksNote) {
|
|
302
|
+
const candidate: PersonalOpsIntakeCandidate = {
|
|
303
|
+
id: 'capture-scratchpad-note',
|
|
304
|
+
label: 'Capture a scratchpad note',
|
|
305
|
+
laneId: 'notes' as const,
|
|
306
|
+
status: 'ready' as const,
|
|
307
|
+
confidence: 'medium' as const,
|
|
308
|
+
why: 'The request asks to capture or note working context.',
|
|
309
|
+
modelRoute: 'agent_local_registry domain:"notes" action:"create"',
|
|
310
|
+
inspectRoutes: ['personal_ops action:"lane" laneId:"notes"'],
|
|
311
|
+
requiresConfirmation: false,
|
|
312
|
+
safetyBoundary: 'Notes are Agent-local scratchpad records; promotion to memory or Knowledge stays separate and reviewed.',
|
|
313
|
+
nextSteps: ['Create a scratchpad note with a short title and body.', 'Review or promote the note only when it proves useful.'],
|
|
314
|
+
requiredFields: ['title', 'body'],
|
|
315
|
+
missingFields: ['title', 'body'],
|
|
316
|
+
userQuestion: 'What should the note say?',
|
|
317
|
+
};
|
|
318
|
+
candidates.push(candidate);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (asksRoutine) {
|
|
322
|
+
candidates.push({
|
|
323
|
+
id: 'routine-review-or-promotion',
|
|
324
|
+
label: 'Review routines before reuse',
|
|
325
|
+
laneId: 'routines',
|
|
326
|
+
status: routineLane.status === 'ready' ? 'ready' : routineLane.status === 'needs-setup' ? 'needs-setup' : 'attention',
|
|
327
|
+
confidence: 'medium',
|
|
328
|
+
why: 'The request asks about a routine, checklist, or repeatable workflow.',
|
|
329
|
+
modelRoute: 'personal_ops action:"lane" laneId:"routines"',
|
|
330
|
+
inspectRoutes: [
|
|
331
|
+
'personal_ops action:"lane" laneId:"routines"',
|
|
332
|
+
'workspace action:"actions" categoryId:"routines"',
|
|
333
|
+
],
|
|
334
|
+
requiresConfirmation: true,
|
|
335
|
+
safetyBoundary: 'Routine creation/review is Agent-local; schedule promotion requires explicit cadence and confirmation.',
|
|
336
|
+
nextSteps: [
|
|
337
|
+
'Inspect routine readiness and setup gaps.',
|
|
338
|
+
'Create or review the routine locally.',
|
|
339
|
+
'Promote to a connected schedule only after the user confirms cadence and delivery expectations.',
|
|
340
|
+
],
|
|
341
|
+
missingFields: ['routineId or routine goal'],
|
|
342
|
+
userQuestion: 'Which routine or repeatable workflow should GoodVibes use?',
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (asksDelivery) {
|
|
347
|
+
candidates.push({
|
|
348
|
+
id: 'delivery-channel-review',
|
|
349
|
+
label: 'Review delivery channels before sending',
|
|
350
|
+
laneId: 'delivery',
|
|
351
|
+
status: deliveryLane.status === 'ready' ? 'ready' : deliveryLane.status === 'needs-setup' ? 'needs-setup' : 'attention',
|
|
352
|
+
confidence: 'medium',
|
|
353
|
+
why: 'The request asks to deliver, send, notify, or use a communication channel.',
|
|
354
|
+
modelRoute: 'channels action:"status"',
|
|
355
|
+
inspectRoutes: [
|
|
356
|
+
'personal_ops action:"lane" laneId:"delivery"',
|
|
357
|
+
'channels action:"triage"',
|
|
358
|
+
'channels action:"deliveries"',
|
|
359
|
+
],
|
|
360
|
+
requiresConfirmation: true,
|
|
361
|
+
safetyBoundary: 'External sends require an explicit target, reviewed message, and confirmed channel send route.',
|
|
362
|
+
nextSteps: [
|
|
363
|
+
'Inspect channel readiness and recent delivery receipts.',
|
|
364
|
+
'Choose one configured target and message.',
|
|
365
|
+
'Send only through agent_channel_send or the confirmed workspace send flow.',
|
|
366
|
+
],
|
|
367
|
+
missingFields: ['channel target', 'reviewed message', 'explicitUserRequest'],
|
|
368
|
+
userQuestion: 'Which configured channel target and reviewed message should GoodVibes send?',
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (candidates.length === 0) candidates.push(setupCandidate(laneById(lanes, 'tasks'), request));
|
|
373
|
+
return candidates
|
|
374
|
+
.sort((left, right) => candidatePriority(right) - candidatePriority(left) || left.id.localeCompare(right.id));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function nextActions(lanes: readonly PersonalOpsLane[]): readonly string[] {
|
|
378
|
+
const urgent = lanes
|
|
379
|
+
.filter((lane) => lane.status === 'gap' || lane.status === 'needs-setup')
|
|
380
|
+
.map((lane) => `${lane.label}: ${lane.next}`);
|
|
381
|
+
const partial = lanes
|
|
382
|
+
.filter((lane) => lane.status === 'partial')
|
|
383
|
+
.map((lane) => `${lane.label}: ${lane.next}`);
|
|
384
|
+
return [...urgent, ...partial].slice(0, 5);
|
|
385
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
|
|
3
|
+
import { calendarWorkflows, connectorSignalsMatching, inboxWorkflows, methodIdsMatching, reminderWorkflows, taskWorkflows } from './agent-harness-personal-ops-discovery.ts';
|
|
4
|
+
import { channelRecords, connectorRecords, localRecord, refreshableSavedRecordCount, reminderOperationRecords, routineReceiptRecord, savedReviewArtifactRecords, savedReviewQueueRecords, taskOperationRecords } from './agent-harness-personal-ops-records.ts';
|
|
5
|
+
import type { McpToolRecord, McpToolSchema, PersonalOpsLane, PersonalOpsLiveRecord } from './agent-harness-personal-ops-types.ts';
|
|
6
|
+
|
|
7
|
+
export function buildLanes(
|
|
8
|
+
context: CommandContext,
|
|
9
|
+
options: {
|
|
10
|
+
readonly toolsByServer?: ReadonlyMap<string, readonly McpToolRecord[]>;
|
|
11
|
+
readonly schemasByQualifiedName?: ReadonlyMap<string, McpToolSchema>;
|
|
12
|
+
} = {},
|
|
13
|
+
): readonly PersonalOpsLane[] {
|
|
14
|
+
const snapshot = buildAgentWorkspaceRuntimeSnapshot(context);
|
|
15
|
+
const emailMethods = methodIdsMatching(['email', 'mail', 'imap', 'smtp']);
|
|
16
|
+
const calendarMethods = methodIdsMatching(['calendar', 'caldav', 'agenda']);
|
|
17
|
+
const toolsByName = options.toolsByServer ?? new Map<string, readonly McpToolRecord[]>();
|
|
18
|
+
const schemasByQualifiedName = options.schemasByQualifiedName ?? new Map<string, McpToolSchema>();
|
|
19
|
+
const emailConnectors = connectorSignalsMatching(context, ['email', 'mail', 'imap', 'smtp', 'gmail'], { lane: 'inbox', toolsByServer: toolsByName, schemasByQualifiedName });
|
|
20
|
+
const calendarConnectors = connectorSignalsMatching(context, ['calendar', 'caldav', 'agenda'], { lane: 'calendar', toolsByServer: toolsByName, schemasByQualifiedName });
|
|
21
|
+
const inboxArtifactRecords = savedReviewArtifactRecords(context, 'inbox', emailConnectors);
|
|
22
|
+
const calendarArtifactRecords = savedReviewArtifactRecords(context, 'calendar', calendarConnectors);
|
|
23
|
+
const inboxReviewQueueRecords = savedReviewQueueRecords(context, 'inbox', emailConnectors);
|
|
24
|
+
const calendarReviewQueueRecords = savedReviewQueueRecords(context, 'calendar', calendarConnectors);
|
|
25
|
+
const refreshableInboxQueueRecords = refreshableSavedRecordCount(inboxReviewQueueRecords);
|
|
26
|
+
const refreshableCalendarQueueRecords = refreshableSavedRecordCount(calendarReviewQueueRecords);
|
|
27
|
+
const taskMethods = methodIdsMatching(['task', 'work-plan', 'workplan']);
|
|
28
|
+
const scheduleMethods = methodIdsMatching(['schedule', 'reminder']);
|
|
29
|
+
const readyChannels = snapshot.channels.filter((channel) => channel.ready).length;
|
|
30
|
+
const enabledChannels = snapshot.channels.filter((channel) => channel.enabled).length;
|
|
31
|
+
const configuredTargets = snapshot.channels.filter((channel) => channel.defaultTarget === 'configured').length;
|
|
32
|
+
const scheduleReadyRoutines = snapshot.localRoutines.filter((routine) => (
|
|
33
|
+
routine.enabled === true
|
|
34
|
+
&& routine.reviewState === 'reviewed'
|
|
35
|
+
&& (routine.missingRequirementCount ?? 0) === 0
|
|
36
|
+
)).length;
|
|
37
|
+
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
id: 'inbox',
|
|
41
|
+
label: 'Inbox',
|
|
42
|
+
status: emailMethods.length > 0 || emailConnectors.length > 0 || inboxArtifactRecords.length > 0 ? 'partial' : 'gap',
|
|
43
|
+
outcome: 'Triage inbound email or message inboxes, summarize threads, draft replies, and send only after confirmation.',
|
|
44
|
+
current: emailMethods.length > 0
|
|
45
|
+
? 'The daemon contract exposes email-like methods; Personal Ops workflow cards now guide inbox triage and draft boundaries around exact methods.'
|
|
46
|
+
: emailConnectors.length > 0
|
|
47
|
+
? 'A configured MCP connector looks email-capable; Personal Ops workflow cards now guide inbox triage, schema-derived operation records, and draft boundaries around its exact tools.'
|
|
48
|
+
: inboxArtifactRecords.length > 0
|
|
49
|
+
? 'Saved inbox review artifacts and thread queue items are available for recap, local draft review, or promotion; no fresh email connector is currently ready.'
|
|
50
|
+
: 'No email/IMAP/SMTP methods are present in the current GoodVibes SDK operator contract.',
|
|
51
|
+
next: emailMethods.length > 0
|
|
52
|
+
? 'Use the inbox workflow cards to inspect exact methods, read selected threads, summarize priorities, and keep send as a separate confirmation.'
|
|
53
|
+
: emailConnectors.length > 0
|
|
54
|
+
? 'Use the inbox workflow cards and operation records to inspect matching MCP connector schemas, then route triage only through reviewed connector actions.'
|
|
55
|
+
: inboxArtifactRecords.length > 0
|
|
56
|
+
? 'Use saved thread queue records for local draft review or recap, then repair an email connector before reading fresh inbox data or sending.'
|
|
57
|
+
: 'Install or build an email connector/MCP/plugin, then expose triage and draft-reply actions here.',
|
|
58
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Channels or connector setup',
|
|
59
|
+
modelRoute: emailConnectors.length > 0 ? 'agent_harness mode:"mcp_servers" query:"email"' : 'host action:"methods" query:"email"',
|
|
60
|
+
signals: [
|
|
61
|
+
`${emailMethods.length} email-like daemon method(s)`,
|
|
62
|
+
`${emailConnectors.length} email-like MCP connector(s)`,
|
|
63
|
+
`${inboxArtifactRecords.length} saved inbox review artifact(s)`,
|
|
64
|
+
`${inboxReviewQueueRecords.length} saved inbox thread queue item(s)`,
|
|
65
|
+
`${refreshableInboxQueueRecords} refreshable saved inbox queue item(s)`,
|
|
66
|
+
`${readyChannels}/${snapshot.channels.length} channel(s) ready for delivery`,
|
|
67
|
+
],
|
|
68
|
+
methodIds: emailMethods,
|
|
69
|
+
connectorSignals: emailConnectors,
|
|
70
|
+
workflows: inboxWorkflows(emailMethods, emailConnectors),
|
|
71
|
+
liveRecords: [
|
|
72
|
+
...inboxReviewQueueRecords,
|
|
73
|
+
...inboxArtifactRecords,
|
|
74
|
+
...connectorRecords(emailConnectors, 'Inbox', 'inbox'),
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'calendar',
|
|
79
|
+
label: 'Calendar',
|
|
80
|
+
status: calendarMethods.length > 0 || calendarConnectors.length > 0 || calendarArtifactRecords.length > 0 ? 'partial' : 'gap',
|
|
81
|
+
outcome: 'Read agenda context, identify conflicts, prepare briefings, and create reminders for calendar-driven work.',
|
|
82
|
+
current: calendarMethods.length > 0
|
|
83
|
+
? 'The daemon contract exposes calendar-like methods; Personal Ops workflow cards now guide agenda briefing and conflict-scan boundaries.'
|
|
84
|
+
: calendarConnectors.length > 0
|
|
85
|
+
? 'A configured MCP connector looks calendar-capable; Personal Ops workflow cards now guide agenda briefing, schema-derived operation records, and conflict-scan boundaries around its exact tools.'
|
|
86
|
+
: calendarArtifactRecords.length > 0
|
|
87
|
+
? '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.'
|
|
88
|
+
: 'No calendar/CalDAV/agenda methods are present in the current GoodVibes SDK operator contract.',
|
|
89
|
+
next: calendarMethods.length > 0
|
|
90
|
+
? 'Use the calendar workflow cards to inspect exact methods, fetch a bounded agenda window, and propose reminders or follow-ups.'
|
|
91
|
+
: calendarConnectors.length > 0
|
|
92
|
+
? 'Use the calendar workflow cards and operation records to inspect matching MCP connector schemas, then route agenda work only through reviewed connector actions.'
|
|
93
|
+
: calendarArtifactRecords.length > 0
|
|
94
|
+
? 'Use saved event queue records for recap or reminder creation, then repair a calendar connector before reading fresh agenda data or editing events.'
|
|
95
|
+
: 'Add a CalDAV/calendar connector and route agenda briefing, conflicts, and reminders through this lane.',
|
|
96
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Create reminder',
|
|
97
|
+
modelRoute: calendarConnectors.length > 0 ? 'agent_harness mode:"mcp_servers" query:"calendar"' : 'host action:"methods" query:"calendar"',
|
|
98
|
+
signals: [
|
|
99
|
+
`${calendarMethods.length} calendar-like daemon method(s)`,
|
|
100
|
+
`${calendarConnectors.length} calendar-like MCP connector(s)`,
|
|
101
|
+
`${calendarArtifactRecords.length} saved calendar review artifact(s)`,
|
|
102
|
+
`${calendarReviewQueueRecords.length} saved calendar event queue item(s)`,
|
|
103
|
+
`${refreshableCalendarQueueRecords} refreshable saved calendar queue item(s)`,
|
|
104
|
+
`${scheduleMethods.length} schedule/reminder method(s) available for follow-up`,
|
|
105
|
+
],
|
|
106
|
+
methodIds: calendarMethods,
|
|
107
|
+
connectorSignals: calendarConnectors,
|
|
108
|
+
workflows: calendarWorkflows(calendarMethods, calendarConnectors),
|
|
109
|
+
liveRecords: [
|
|
110
|
+
...calendarReviewQueueRecords,
|
|
111
|
+
...calendarArtifactRecords,
|
|
112
|
+
...connectorRecords(calendarConnectors, 'Calendar', 'calendar'),
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'notes',
|
|
117
|
+
label: 'Notes',
|
|
118
|
+
status: 'ready',
|
|
119
|
+
outcome: 'Capture working context, source triage, decisions, and handoff notes without polluting durable memory.',
|
|
120
|
+
current: `Agent-local notes are available: ${snapshot.localNoteCount} note(s), ${snapshot.localNoteReviewQueueCount} needing review.`,
|
|
121
|
+
next: snapshot.localNoteCount > 0
|
|
122
|
+
? 'Review or promote selected notes into memory, skills, routines, or Agent Knowledge when useful.'
|
|
123
|
+
: 'Create a note from the Personal Ops or Notes workspace when the next decision or source needs tracking.',
|
|
124
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Create note',
|
|
125
|
+
modelRoute: 'agent_local_registry domain:"notes" action:"create"',
|
|
126
|
+
signals: [
|
|
127
|
+
`${snapshot.localNoteCount} local note(s)`,
|
|
128
|
+
`${snapshot.localNoteReviewQueueCount} note(s) in review queue`,
|
|
129
|
+
],
|
|
130
|
+
liveRecords: snapshot.localNotes.slice(0, 5).map((note) => localRecord('note', note)),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'tasks',
|
|
134
|
+
label: 'Tasks',
|
|
135
|
+
status: taskMethods.length > 0 ? 'ready' : 'partial',
|
|
136
|
+
outcome: 'Track user-visible work items, inspect host task state, and update work plan status without hidden jobs.',
|
|
137
|
+
current: `Agent has work-plan actions and ${taskMethods.length} task/work-plan daemon method(s) in the SDK contract.`,
|
|
138
|
+
next: 'Use work plans for user-visible task tracking; inspect runtime host tasks separately before mutating anything.',
|
|
139
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Add work item',
|
|
140
|
+
modelRoute: 'agent_work_plan action:"create"',
|
|
141
|
+
signals: [
|
|
142
|
+
`${taskMethods.length} task/work-plan daemon method(s)`,
|
|
143
|
+
'Work plan add/show/status/delete actions are available',
|
|
144
|
+
],
|
|
145
|
+
methodIds: taskMethods,
|
|
146
|
+
workflows: taskWorkflows(taskMethods),
|
|
147
|
+
liveRecords: taskOperationRecords(taskMethods),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'reminders',
|
|
151
|
+
label: 'Reminders',
|
|
152
|
+
status: scheduleMethods.length > 0 ? 'ready' : 'partial',
|
|
153
|
+
outcome: 'Turn a user request into a visible reminder or autonomous schedule with delivery target and cancellation path.',
|
|
154
|
+
current: `Reminder and autonomous schedule creation are available through Agent tools; ${scheduleMethods.length} schedule/reminder daemon method(s) are discoverable.`,
|
|
155
|
+
next: 'Create one confirmed reminder or autonomous schedule with title, time, scope, delivery target, success criteria, and explicit user request.',
|
|
156
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Create reminder',
|
|
157
|
+
modelRoute: 'schedule action:"remind|create"',
|
|
158
|
+
signals: [
|
|
159
|
+
`${scheduleMethods.length} schedule/reminder daemon method(s)`,
|
|
160
|
+
`${configuredTargets} configured delivery target(s)`,
|
|
161
|
+
],
|
|
162
|
+
methodIds: scheduleMethods,
|
|
163
|
+
workflows: reminderWorkflows(scheduleMethods, configuredTargets > 0 || readyChannels > 0),
|
|
164
|
+
liveRecords: reminderOperationRecords(scheduleMethods, configuredTargets > 0 || readyChannels > 0),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'routines',
|
|
168
|
+
label: 'Routines',
|
|
169
|
+
status: scheduleReadyRoutines > 0 ? 'ready' : snapshot.localRoutineCount > 0 ? 'partial' : 'needs-setup',
|
|
170
|
+
outcome: 'Reuse repeatable local workflows and promote reviewed routines to connected schedules only when useful.',
|
|
171
|
+
current: `${snapshot.localRoutineCount} routine(s), ${snapshot.enabledRoutineCount} enabled, ${scheduleReadyRoutines} schedule-ready, ${snapshot.routineScheduleReceiptCount} promotion receipt(s).`,
|
|
172
|
+
next: scheduleReadyRoutines > 0
|
|
173
|
+
? 'Promote a reviewed routine to a connected schedule when the user asks for recurrence.'
|
|
174
|
+
: 'Create or review a routine, resolve setup gaps, then promote only with explicit schedule confirmation.',
|
|
175
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Routine library',
|
|
176
|
+
modelRoute: 'workspace action:"actions" categoryId:"routines"',
|
|
177
|
+
signals: [
|
|
178
|
+
`${scheduleReadyRoutines} schedule-ready routine(s)`,
|
|
179
|
+
`${snapshot.failedRoutineScheduleReceiptCount} failed promotion receipt(s)`,
|
|
180
|
+
],
|
|
181
|
+
liveRecords: [
|
|
182
|
+
...snapshot.localRoutines.slice(0, 5).map((routine) => localRecord('routine', routine)),
|
|
183
|
+
...[routineReceiptRecord(snapshot.latestRoutineScheduleReceipt)].filter((record): record is PersonalOpsLiveRecord => record !== null),
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 'delivery',
|
|
188
|
+
label: 'Delivery',
|
|
189
|
+
status: readyChannels > 0 ? 'ready' : enabledChannels > 0 ? 'needs-setup' : 'partial',
|
|
190
|
+
outcome: 'Reach the user through configured channels and send messages or notifications only after explicit confirmation.',
|
|
191
|
+
current: `${readyChannels}/${snapshot.channels.length} channel(s) ready, ${enabledChannels} enabled, ${configuredTargets} configured default target(s).`,
|
|
192
|
+
next: readyChannels > 0
|
|
193
|
+
? 'Use confirmed channel send or notification tools when the user asks for delivery.'
|
|
194
|
+
: 'Enable and configure one delivery channel so personal-ops reminders and summaries can reach the user.',
|
|
195
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Channels',
|
|
196
|
+
modelRoute: 'channels action:"status"',
|
|
197
|
+
signals: [
|
|
198
|
+
`${readyChannels} ready channel(s)`,
|
|
199
|
+
`${configuredTargets} configured default target(s)`,
|
|
200
|
+
],
|
|
201
|
+
liveRecords: channelRecords(snapshot),
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
}
|