@pellux/goodvibes-agent 1.1.7 → 1.3.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 +329 -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 +138903 -91477
- 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 +58 -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 +116 -45
- package/docs/voice-and-live-tts.md +3 -1
- package/package.json +3 -2
- package/release/release-notes.md +142 -5
- package/release/release-readiness.json +124 -94
- 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 +294 -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-artifact-receipts.ts +366 -0
- package/src/agent/setup-wizard-checkpoint.ts +140 -0
- package/src/agent/setup-wizard.ts +683 -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 +313 -30
- package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
- package/src/input/agent-workspace-setup-snapshot.ts +217 -0
- package/src/input/agent-workspace-setup.ts +61 -1
- package/src/input/agent-workspace-snapshot.ts +184 -149
- package/src/input/agent-workspace-types.ts +287 -2
- package/src/input/agent-workspace.ts +23 -1
- 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/input/setup-wizard-live-receipts.ts +76 -0
- package/src/main.ts +15 -15
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/agent-workspace-context-lines.ts +701 -0
- package/src/renderer/agent-workspace.ts +68 -421
- 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 +39 -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 +199 -5
- 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 +137 -7
- 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-policy.ts +75 -0
- package/src/tools/agent-harness-agent-orchestration.ts +791 -0
- package/src/tools/agent-harness-autonomy-intake.ts +785 -0
- package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
- package/src/tools/agent-harness-autonomy-queue.ts +571 -0
- package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
- package/src/tools/agent-harness-background-processes-types.ts +35 -0
- package/src/tools/agent-harness-background-processes.ts +798 -0
- package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
- package/src/tools/agent-harness-browser-control.ts +346 -0
- package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -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-device-live-read-models.ts +366 -0
- 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 +385 -0
- package/src/tools/agent-harness-file-recovery.ts +135 -0
- package/src/tools/agent-harness-interactive-runtime-records.ts +421 -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 +269 -0
- package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
- package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
- package/src/tools/agent-harness-local-model-smoke.ts +277 -0
- package/src/tools/agent-harness-local-model-url.ts +78 -0
- package/src/tools/agent-harness-media-posture.ts +223 -2
- package/src/tools/agent-harness-memory-external-providers.ts +796 -0
- package/src/tools/agent-harness-memory-posture.ts +672 -0
- package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
- package/src/tools/agent-harness-memory-refinement.ts +340 -0
- package/src/tools/agent-harness-metadata.ts +100 -168
- package/src/tools/agent-harness-mode-catalog.ts +86 -33
- package/src/tools/agent-harness-model-catalog.ts +162 -0
- package/src/tools/agent-harness-model-provider-health.ts +283 -0
- package/src/tools/agent-harness-model-readiness.ts +432 -0
- package/src/tools/agent-harness-model-routing-types.ts +327 -0
- package/src/tools/agent-harness-model-routing-utils.ts +30 -0
- package/src/tools/agent-harness-model-routing.ts +165 -193
- package/src/tools/agent-harness-operator-methods.ts +115 -133
- package/src/tools/agent-harness-pairing-posture.ts +452 -18
- package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
- 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 +270 -0
- package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
- package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
- package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
- package/src/tools/agent-harness-personal-ops-records.ts +684 -0
- package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
- package/src/tools/agent-harness-personal-ops-types.ts +240 -0
- package/src/tools/agent-harness-personal-ops.ts +613 -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-remote-read-models.ts +541 -0
- package/src/tools/agent-harness-research-briefing.ts +446 -0
- package/src/tools/agent-harness-research-live-read-models.ts +500 -0
- package/src/tools/agent-harness-research-queue.ts +250 -0
- package/src/tools/agent-harness-research-runs.ts +307 -0
- package/src/tools/agent-harness-research-workflow.ts +395 -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 +102 -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 +533 -0
- package/src/tools/agent-harness-sudo-posture.ts +114 -0
- package/src/tools/agent-harness-tool-schema.ts +142 -7
- package/src/tools/agent-harness-tool-types.ts +83 -0
- package/src/tools/agent-harness-tool-utils.ts +43 -0
- package/src/tools/agent-harness-tool.ts +319 -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 +253 -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 +735 -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-runner.ts +367 -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 +515 -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 +106 -85
- 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/version.ts +1 -1
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
2
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
3
|
+
import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
|
|
4
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
5
|
+
import { redactedPersonalOpsText } from './agent-harness-personal-ops-runner.ts';
|
|
6
|
+
import { personalOpsRecordCertification } from './agent-harness-personal-ops-certification.ts';
|
|
7
|
+
import type { PersonalOpsConnectorSignal, PersonalOpsConnectorTool, PersonalOpsLane, PersonalOpsLaneId, PersonalOpsLiveRecord, PersonalOpsRecordFreshness, PersonalOpsStatus, PersonalOpsWorkflow, PersonalOpsWorkflowStatus } from './agent-harness-personal-ops-types.ts';
|
|
8
|
+
|
|
9
|
+
type PersonalOpsEffectReceiptLaneId = Extract<PersonalOpsLaneId, 'inbox' | 'calendar' | 'tasks' | 'reminders'>;
|
|
10
|
+
|
|
11
|
+
export function laneStatusRank(status: PersonalOpsStatus): number {
|
|
12
|
+
if (status === 'ready') return 4;
|
|
13
|
+
if (status === 'partial') return 3;
|
|
14
|
+
if (status === 'needs-setup') return 2;
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function searchText(lane: PersonalOpsLane): string {
|
|
19
|
+
return [
|
|
20
|
+
lane.id,
|
|
21
|
+
lane.label,
|
|
22
|
+
lane.status,
|
|
23
|
+
lane.outcome,
|
|
24
|
+
lane.current,
|
|
25
|
+
lane.next,
|
|
26
|
+
lane.userRoute,
|
|
27
|
+
lane.modelRoute,
|
|
28
|
+
lane.signals.join('\n'),
|
|
29
|
+
lane.connectorSignals?.flatMap((signal) => [
|
|
30
|
+
signal.id,
|
|
31
|
+
signal.label,
|
|
32
|
+
signal.status,
|
|
33
|
+
signal.summary,
|
|
34
|
+
signal.modelRoute,
|
|
35
|
+
String(signal.toolCount),
|
|
36
|
+
signal.capabilityTags.join('\n'),
|
|
37
|
+
signal.readTools?.map((tool) => `${tool.name} ${tool.description ?? ''} ${tool.capability}`).join('\n') ?? '',
|
|
38
|
+
signal.writeTools?.map((tool) => `${tool.name} ${tool.description ?? ''} ${tool.capability}`).join('\n') ?? '',
|
|
39
|
+
]).join('\n') ?? '',
|
|
40
|
+
lane.workflows?.flatMap((workflow) => [
|
|
41
|
+
workflow.id,
|
|
42
|
+
workflow.label,
|
|
43
|
+
workflow.status,
|
|
44
|
+
workflow.summary,
|
|
45
|
+
workflow.next,
|
|
46
|
+
workflow.modelRoute,
|
|
47
|
+
workflow.inspectRoutes.join('\n'),
|
|
48
|
+
workflow.prerequisites.join('\n'),
|
|
49
|
+
workflow.runBoundary,
|
|
50
|
+
]).join('\n') ?? '',
|
|
51
|
+
lane.liveRecords?.flatMap((record) => [
|
|
52
|
+
record.id,
|
|
53
|
+
record.label,
|
|
54
|
+
record.status,
|
|
55
|
+
record.summary,
|
|
56
|
+
record.userRoute,
|
|
57
|
+
record.modelRoute,
|
|
58
|
+
record.tags?.join('\n') ?? '',
|
|
59
|
+
record.freshness ? [
|
|
60
|
+
record.freshness.status,
|
|
61
|
+
record.freshness.source,
|
|
62
|
+
record.freshness.sourceTool ?? '',
|
|
63
|
+
record.freshness.refreshRoute ?? '',
|
|
64
|
+
record.freshness.policy,
|
|
65
|
+
].join('\n') : '',
|
|
66
|
+
record.certification ? [
|
|
67
|
+
record.certification.schemaStatus,
|
|
68
|
+
record.certification.schemaVersion ?? '',
|
|
69
|
+
record.certification.publicationGuarantee ?? '',
|
|
70
|
+
record.certification.publisher ?? '',
|
|
71
|
+
record.certification.provenance?.join('\n') ?? '',
|
|
72
|
+
record.certification.receiptIds?.join('\n') ?? '',
|
|
73
|
+
record.certification.missingSignals.join('\n'),
|
|
74
|
+
].join('\n') : '',
|
|
75
|
+
]).join('\n') ?? '',
|
|
76
|
+
].join('\n').toLowerCase();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function describeLiveRecord(record: PersonalOpsLiveRecord, includeParameters: boolean): Record<string, unknown> {
|
|
80
|
+
return {
|
|
81
|
+
id: record.id,
|
|
82
|
+
label: record.label,
|
|
83
|
+
status: record.status,
|
|
84
|
+
summary: previewHarnessText(record.summary, includeParameters ? 240 : 96),
|
|
85
|
+
userRoute: previewHarnessText(record.userRoute, includeParameters ? 140 : 96),
|
|
86
|
+
modelRoute: previewHarnessText(record.modelRoute, includeParameters ? 140 : 96),
|
|
87
|
+
...(record.tags && record.tags.length > 0 ? { tags: record.tags.slice(0, includeParameters ? 12 : 4) } : {}),
|
|
88
|
+
...(includeParameters && record.effect ? { effect: record.effect } : {}),
|
|
89
|
+
...(includeParameters && record.capability ? { capability: record.capability } : {}),
|
|
90
|
+
...(includeParameters && record.qualifiedName ? { qualifiedName: record.qualifiedName } : {}),
|
|
91
|
+
...(includeParameters && record.requiredFields ? { requiredFields: record.requiredFields } : {}),
|
|
92
|
+
...(includeParameters && record.optionalFields ? { optionalFields: record.optionalFields.slice(0, 12) } : {}),
|
|
93
|
+
...(includeParameters && record.sampleInput ? { sampleInput: record.sampleInput } : {}),
|
|
94
|
+
...(includeParameters && typeof record.confirmationRequired === 'boolean' ? { confirmationRequired: record.confirmationRequired } : {}),
|
|
95
|
+
...(includeParameters && record.artifactId ? { artifactId: record.artifactId } : {}),
|
|
96
|
+
...(includeParameters && typeof record.reviewRecordCount === 'number' ? { reviewRecordCount: record.reviewRecordCount } : {}),
|
|
97
|
+
...(includeParameters && record.reviewLabels && record.reviewLabels.length > 0 ? { reviewLabels: record.reviewLabels } : {}),
|
|
98
|
+
...(includeParameters && record.sourceTool ? { sourceTool: record.sourceTool } : {}),
|
|
99
|
+
...(includeParameters && record.certification ? { certification: record.certification } : {}),
|
|
100
|
+
...(includeParameters && record.freshness ? { freshness: record.freshness } : {}),
|
|
101
|
+
...(includeParameters && record.followUpRoutes && record.followUpRoutes.length > 0 ? { followUpRoutes: record.followUpRoutes } : {}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function describeConnectorSignal(signal: PersonalOpsConnectorSignal, includeParameters: boolean): Record<string, unknown> {
|
|
106
|
+
return {
|
|
107
|
+
id: signal.id,
|
|
108
|
+
kind: signal.kind,
|
|
109
|
+
label: signal.label,
|
|
110
|
+
status: signal.status,
|
|
111
|
+
summary: previewHarnessText(signal.summary, includeParameters ? 180 : 96),
|
|
112
|
+
modelRoute: previewHarnessText(signal.modelRoute, includeParameters ? 140 : 96),
|
|
113
|
+
toolCount: signal.toolCount,
|
|
114
|
+
...(signal.capabilityTags.length > 0 ? { capabilityTags: signal.capabilityTags } : {}),
|
|
115
|
+
...(includeParameters && signal.readTools && signal.readTools.length > 0 ? { readTools: signal.readTools } : {}),
|
|
116
|
+
...(includeParameters && signal.writeTools && signal.writeTools.length > 0 ? { writeTools: signal.writeTools } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function describeWorkflow(workflow: PersonalOpsWorkflow, includeParameters: boolean): Record<string, unknown> {
|
|
121
|
+
return {
|
|
122
|
+
id: workflow.id,
|
|
123
|
+
label: workflow.label,
|
|
124
|
+
status: workflow.status,
|
|
125
|
+
summary: previewHarnessText(workflow.summary, includeParameters ? 180 : 96),
|
|
126
|
+
next: previewHarnessText(workflow.next, includeParameters ? 180 : 96),
|
|
127
|
+
modelRoute: previewHarnessText(workflow.modelRoute, 96),
|
|
128
|
+
...(includeParameters ? {
|
|
129
|
+
inspectRoutes: workflow.inspectRoutes,
|
|
130
|
+
prerequisites: workflow.prerequisites,
|
|
131
|
+
runBoundary: workflow.runBoundary,
|
|
132
|
+
} : {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function describeLane(lane: PersonalOpsLane, includeParameters: boolean): Record<string, unknown> {
|
|
137
|
+
return {
|
|
138
|
+
id: lane.id,
|
|
139
|
+
label: lane.label,
|
|
140
|
+
status: lane.status,
|
|
141
|
+
outcome: lane.outcome,
|
|
142
|
+
current: lane.current,
|
|
143
|
+
next: lane.next,
|
|
144
|
+
userRoute: previewHarnessText(lane.userRoute, 96),
|
|
145
|
+
modelRoute: previewHarnessText(lane.modelRoute, 96),
|
|
146
|
+
signals: lane.signals,
|
|
147
|
+
...(lane.connectorSignals && lane.connectorSignals.length > 0 ? { connectorSignals: lane.connectorSignals.slice(0, includeParameters ? 8 : 3).map((signal) => describeConnectorSignal(signal, includeParameters)) } : {}),
|
|
148
|
+
...(lane.workflows && lane.workflows.length > 0 ? { workflows: lane.workflows.slice(0, includeParameters ? 8 : 3).map((workflow) => describeWorkflow(workflow, includeParameters)) } : {}),
|
|
149
|
+
...(lane.liveRecords && lane.liveRecords.length > 0 ? { liveRecords: lane.liveRecords.slice(0, includeParameters ? 8 : 3).map((record) => describeLiveRecord(record, includeParameters)) } : {}),
|
|
150
|
+
...(includeParameters ? {
|
|
151
|
+
routes: {
|
|
152
|
+
user: lane.userRoute,
|
|
153
|
+
model: lane.modelRoute,
|
|
154
|
+
},
|
|
155
|
+
methodIds: lane.methodIds ?? [],
|
|
156
|
+
safety: 'Writes, sends, schedules, and operator calls require explicit user request and confirmation through the owning tool.',
|
|
157
|
+
} : {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function localRecord(domain: 'note' | 'routine', item: ReturnType<typeof buildAgentWorkspaceRuntimeSnapshot>['localNotes'][number]): PersonalOpsLiveRecord {
|
|
162
|
+
return {
|
|
163
|
+
id: item.id,
|
|
164
|
+
label: item.name,
|
|
165
|
+
status: item.reviewState,
|
|
166
|
+
summary: item.description,
|
|
167
|
+
userRoute: domain === 'note' ? 'Agent Workspace -> Notes' : 'Agent Workspace -> Routines',
|
|
168
|
+
modelRoute: `agent_local_registry domain:"${domain}" action:"get" id:"${item.id}"`,
|
|
169
|
+
tags: item.tags,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function routineReceiptRecord(receipt: ReturnType<typeof buildAgentWorkspaceRuntimeSnapshot>['latestRoutineScheduleReceipt']): PersonalOpsLiveRecord | null {
|
|
174
|
+
if (!receipt) return null;
|
|
175
|
+
return {
|
|
176
|
+
id: receipt.id,
|
|
177
|
+
label: receipt.scheduleName,
|
|
178
|
+
status: receipt.status,
|
|
179
|
+
summary: `${receipt.routineName} -> ${receipt.scheduleKind} ${receipt.scheduleValue}`,
|
|
180
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Routine schedule receipts',
|
|
181
|
+
modelRoute: 'autonomy action:"item" queueItemId:"routine-schedule-promotions"',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function channelRecords(snapshot: ReturnType<typeof buildAgentWorkspaceRuntimeSnapshot>): readonly PersonalOpsLiveRecord[] {
|
|
186
|
+
return snapshot.channels.map((channel) => ({
|
|
187
|
+
id: channel.id,
|
|
188
|
+
label: channel.label,
|
|
189
|
+
status: channel.setupState,
|
|
190
|
+
summary: `${channel.delivery}; ${channel.riskLabel}. ${channel.nextStep}`,
|
|
191
|
+
userRoute: 'Agent Workspace -> Channels',
|
|
192
|
+
modelRoute: `channels action:"channel" channelId:"${channel.id}"`,
|
|
193
|
+
tags: [channel.risk, channel.delivery],
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function personalOpsReadRunRoute(laneId: PersonalOpsLaneId, recordId: string): string {
|
|
198
|
+
return `personal_ops action:"read" laneId:"${laneId}" recordId:"${recordId}" fields:{...} confirm:true explicitUserRequest:"..."`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function connectorReadFreshness(signal: PersonalOpsConnectorSignal, tool: PersonalOpsConnectorTool, laneId: PersonalOpsLaneId): PersonalOpsRecordFreshness | undefined {
|
|
202
|
+
if (tool.effect !== 'read-only') return undefined;
|
|
203
|
+
const recordId = tool.qualifiedName ?? `${signal.id}:${tool.name}`;
|
|
204
|
+
return {
|
|
205
|
+
status: signal.status === 'ready' ? 'fresh-provider-route-ready' : 'connector-attention',
|
|
206
|
+
source: 'connector-read',
|
|
207
|
+
...(tool.qualifiedName ? { sourceTool: tool.qualifiedName } : {}),
|
|
208
|
+
...(signal.status === 'ready' ? { refreshRoute: personalOpsReadRunRoute(laneId, recordId) } : {}),
|
|
209
|
+
...(tool.requiredFields ? { requiredFields: tool.requiredFields } : {}),
|
|
210
|
+
...(tool.sampleInput ? { sampleInput: tool.sampleInput } : {}),
|
|
211
|
+
policy: signal.status === 'ready'
|
|
212
|
+
? 'Reads fresh provider data only through the confirmed Personal Ops read route; provider mutations stay on separate confirmed-effect routes.'
|
|
213
|
+
: 'Repair connector connection, trust, or schema freshness before reading provider data.',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function connectorRecords(signals: readonly PersonalOpsConnectorSignal[], laneLabel: string, laneId: PersonalOpsLaneId): readonly PersonalOpsLiveRecord[] {
|
|
218
|
+
return signals.flatMap((signal) => {
|
|
219
|
+
const summaryRecord: PersonalOpsLiveRecord = {
|
|
220
|
+
id: signal.id,
|
|
221
|
+
label: `${laneLabel} connector: ${signal.label}`,
|
|
222
|
+
status: signal.status,
|
|
223
|
+
summary: [
|
|
224
|
+
signal.summary,
|
|
225
|
+
signal.capabilityTags.length > 0 ? `capabilities ${signal.capabilityTags.join(', ')}` : '',
|
|
226
|
+
].filter(Boolean).join('; '),
|
|
227
|
+
userRoute: 'Agent Workspace -> Tools & MCP',
|
|
228
|
+
modelRoute: signal.modelRoute,
|
|
229
|
+
tags: ['connector', signal.kind, ...signal.capabilityTags],
|
|
230
|
+
};
|
|
231
|
+
const operationTools = [...(signal.readTools ?? []), ...(signal.writeTools ?? [])];
|
|
232
|
+
const operationRecords: PersonalOpsLiveRecord[] = operationTools
|
|
233
|
+
.slice(0, 8)
|
|
234
|
+
.map((tool) => {
|
|
235
|
+
const freshness = connectorReadFreshness(signal, tool, laneId);
|
|
236
|
+
return {
|
|
237
|
+
id: tool.qualifiedName ?? `${signal.id}:${tool.name}`,
|
|
238
|
+
label: `${laneLabel} ${tool.effect === 'read-only' ? 'read' : 'confirmed action'}: ${tool.name}`,
|
|
239
|
+
status: signal.status,
|
|
240
|
+
summary: [
|
|
241
|
+
`${tool.effect === 'read-only' ? 'Read-only' : 'Write-like'} ${tool.capability} MCP route.`,
|
|
242
|
+
tool.requiredFields && tool.requiredFields.length > 0 ? `required fields ${tool.requiredFields.join(', ')}` : 'schema fields unknown until inspected',
|
|
243
|
+
tool.description ?? '',
|
|
244
|
+
].filter(Boolean).join(' '),
|
|
245
|
+
userRoute: 'Agent Workspace -> Tools & MCP -> Tool schema',
|
|
246
|
+
modelRoute: tool.schemaRoute ?? signal.modelRoute,
|
|
247
|
+
tags: ['connector-operation', signal.kind, tool.capability, tool.effect],
|
|
248
|
+
effect: tool.effect,
|
|
249
|
+
capability: tool.capability,
|
|
250
|
+
...(tool.qualifiedName ? { qualifiedName: tool.qualifiedName } : {}),
|
|
251
|
+
...(tool.requiredFields ? { requiredFields: tool.requiredFields } : {}),
|
|
252
|
+
...(tool.optionalFields ? { optionalFields: tool.optionalFields } : {}),
|
|
253
|
+
...(tool.sampleInput ? { sampleInput: tool.sampleInput } : {}),
|
|
254
|
+
confirmationRequired: tool.effect === 'confirmed-effect',
|
|
255
|
+
...(freshness ? { freshness } : {}),
|
|
256
|
+
};
|
|
257
|
+
});
|
|
258
|
+
return [summaryRecord, ...operationRecords];
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function artifactMetadata(artifact: ArtifactDescriptor): Readonly<Record<string, unknown>> {
|
|
263
|
+
return artifact.metadata && typeof artifact.metadata === 'object' && !Array.isArray(artifact.metadata)
|
|
264
|
+
? artifact.metadata
|
|
265
|
+
: {};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function artifactMetadataString(artifact: ArtifactDescriptor, key: string): string {
|
|
269
|
+
const value = artifactMetadata(artifact)[key];
|
|
270
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function artifactMetadataNumber(artifact: ArtifactDescriptor, key: string): number | null {
|
|
274
|
+
const value = artifactMetadata(artifact)[key];
|
|
275
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
276
|
+
if (typeof value === 'string' && value.trim()) {
|
|
277
|
+
const parsed = Number(value);
|
|
278
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function artifactMetadataStringArray(artifact: ArtifactDescriptor, key: string): readonly string[] {
|
|
284
|
+
const value = artifactMetadata(artifact)[key];
|
|
285
|
+
if (Array.isArray(value)) {
|
|
286
|
+
return value
|
|
287
|
+
.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)
|
|
288
|
+
.map((entry) => previewHarnessText(redactedPersonalOpsText(entry.trim()), 120));
|
|
289
|
+
}
|
|
290
|
+
if (typeof value === 'string' && value.trim()) return [previewHarnessText(redactedPersonalOpsText(value.trim()), 120)];
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const PERSONAL_OPS_EFFECT_RECEIPT_PURPOSES = new Set([
|
|
295
|
+
'personal-ops-provider-effect-receipt',
|
|
296
|
+
'personal-ops-effect-receipt',
|
|
297
|
+
'personal-ops-connector-effect-receipt',
|
|
298
|
+
'agent-personal-ops-effect-receipt',
|
|
299
|
+
'connected-host-personal-ops-effect-receipt',
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
export function safeRecordIdPart(value: string): string {
|
|
303
|
+
return value
|
|
304
|
+
.toLowerCase()
|
|
305
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
306
|
+
.replace(/^-+|-+$/g, '')
|
|
307
|
+
.slice(0, 80) || 'item';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function artifactCreatedAtIso(artifact: ArtifactDescriptor): string {
|
|
311
|
+
return typeof artifact.createdAt === 'number' && Number.isFinite(artifact.createdAt)
|
|
312
|
+
? new Date(artifact.createdAt).toISOString()
|
|
313
|
+
: '';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function metadataCreatedAtIso(artifact: ArtifactDescriptor): string {
|
|
317
|
+
const explicit = artifactMetadataString(artifact, 'createdAt')
|
|
318
|
+
|| artifactMetadataString(artifact, 'recordedAt')
|
|
319
|
+
|| artifactMetadataString(artifact, 'completedAt')
|
|
320
|
+
|| artifactMetadataString(artifact, 'timestamp');
|
|
321
|
+
if (explicit) {
|
|
322
|
+
const parsed = Date.parse(explicit);
|
|
323
|
+
if (Number.isFinite(parsed)) return new Date(parsed).toISOString();
|
|
324
|
+
}
|
|
325
|
+
return artifactCreatedAtIso(artifact);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function savedReviewArtifacts(context: CommandContext, laneId: 'inbox' | 'calendar'): readonly ArtifactDescriptor[] {
|
|
329
|
+
const store = context.platform.artifactStore;
|
|
330
|
+
if (!store?.list) return [];
|
|
331
|
+
try {
|
|
332
|
+
return store.list(100)
|
|
333
|
+
.filter((artifact) => artifactMetadataString(artifact, 'purpose') === 'personal-ops-review-cards')
|
|
334
|
+
.filter((artifact) => artifactMetadataString(artifact, 'laneId') === laneId)
|
|
335
|
+
.sort((left, right) => {
|
|
336
|
+
const leftCreated = typeof left.createdAt === 'number' ? left.createdAt : 0;
|
|
337
|
+
const rightCreated = typeof right.createdAt === 'number' ? right.createdAt : 0;
|
|
338
|
+
return rightCreated - leftCreated;
|
|
339
|
+
})
|
|
340
|
+
.slice(0, 5);
|
|
341
|
+
} catch {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function savedProviderEffectReceiptArtifacts(context: CommandContext, laneId: PersonalOpsEffectReceiptLaneId): readonly ArtifactDescriptor[] {
|
|
347
|
+
const store = context.platform.artifactStore;
|
|
348
|
+
if (!store?.list) return [];
|
|
349
|
+
try {
|
|
350
|
+
return store.list(100)
|
|
351
|
+
.filter((artifact) => PERSONAL_OPS_EFFECT_RECEIPT_PURPOSES.has(artifactMetadataString(artifact, 'purpose')))
|
|
352
|
+
.filter((artifact) => artifactMetadataString(artifact, 'laneId') === laneId)
|
|
353
|
+
.sort((left, right) => {
|
|
354
|
+
const leftCreated = typeof left.createdAt === 'number' ? left.createdAt : 0;
|
|
355
|
+
const rightCreated = typeof right.createdAt === 'number' ? right.createdAt : 0;
|
|
356
|
+
return rightCreated - leftCreated;
|
|
357
|
+
})
|
|
358
|
+
.slice(0, 10);
|
|
359
|
+
} catch {
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function normalizedEffectReceiptStatus(artifact: ArtifactDescriptor): string {
|
|
365
|
+
const raw = (artifactMetadataString(artifact, 'status')
|
|
366
|
+
|| artifactMetadataString(artifact, 'outcome')
|
|
367
|
+
|| artifactMetadataString(artifact, 'result')).toLowerCase();
|
|
368
|
+
if (!raw) return 'unknown';
|
|
369
|
+
if (['ok', 'ready', 'success', 'succeeded', 'complete', 'completed', 'sent', 'archived', 'labeled', 'updated', 'accepted', 'declined'].includes(raw)) return 'succeeded';
|
|
370
|
+
if (['blocked', 'needs-review', 'needs_setup', 'needs-setup'].includes(raw)) return 'blocked';
|
|
371
|
+
if (['fail', 'failed', 'error', 'errored'].includes(raw)) return 'failed';
|
|
372
|
+
if (['running', 'pending', 'in-progress', 'in_progress'].includes(raw)) return 'running';
|
|
373
|
+
return raw;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function providerEffectSubjectId(artifact: ArtifactDescriptor): string {
|
|
377
|
+
return artifactMetadataString(artifact, 'subjectId')
|
|
378
|
+
|| artifactMetadataString(artifact, 'threadId')
|
|
379
|
+
|| artifactMetadataString(artifact, 'messageId')
|
|
380
|
+
|| artifactMetadataString(artifact, 'eventId')
|
|
381
|
+
|| artifactMetadataString(artifact, 'taskId')
|
|
382
|
+
|| artifactMetadataString(artifact, 'reminderId')
|
|
383
|
+
|| artifactMetadataString(artifact, 'scheduleId')
|
|
384
|
+
|| artifactMetadataString(artifact, 'providerRecordId')
|
|
385
|
+
|| artifactMetadataString(artifact, 'targetId');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function effectReceiptLaneLabel(laneId: PersonalOpsEffectReceiptLaneId): string {
|
|
389
|
+
if (laneId === 'inbox') return 'Inbox';
|
|
390
|
+
if (laneId === 'calendar') return 'Calendar';
|
|
391
|
+
if (laneId === 'tasks') return 'Task';
|
|
392
|
+
return 'Reminder';
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function effectReceiptCapability(laneId: PersonalOpsEffectReceiptLaneId): string {
|
|
396
|
+
if (laneId === 'inbox') return 'inbox-effect-receipt';
|
|
397
|
+
if (laneId === 'calendar') return 'calendar-effect-receipt';
|
|
398
|
+
if (laneId === 'tasks') return 'task-effect-receipt';
|
|
399
|
+
return 'reminder-effect-receipt';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function savedProviderEffectReceiptRecords(
|
|
403
|
+
context: CommandContext,
|
|
404
|
+
laneId: PersonalOpsEffectReceiptLaneId,
|
|
405
|
+
): readonly PersonalOpsLiveRecord[] {
|
|
406
|
+
return savedProviderEffectReceiptArtifacts(context, laneId).map((artifact) => {
|
|
407
|
+
const laneLabel = effectReceiptLaneLabel(laneId);
|
|
408
|
+
const lowerLaneLabel = laneLabel.toLowerCase();
|
|
409
|
+
const providerId = artifactMetadataString(artifact, 'providerId') || artifactMetadataString(artifact, 'provider') || 'provider';
|
|
410
|
+
const operation = artifactMetadataString(artifact, 'operation') || artifactMetadataString(artifact, 'action') || `${laneId}-effect`;
|
|
411
|
+
const status = normalizedEffectReceiptStatus(artifact);
|
|
412
|
+
const sourceTool = artifactMetadataString(artifact, 'sourceTool') || artifactMetadataString(artifact, 'qualifiedName');
|
|
413
|
+
const subjectId = providerEffectSubjectId(artifact);
|
|
414
|
+
const receiptId = artifactMetadataString(artifact, 'receiptId') || artifact.id;
|
|
415
|
+
const createdAt = metadataCreatedAtIso(artifact);
|
|
416
|
+
const failureReason = artifactMetadataString(artifact, 'failureReason') || artifactMetadataString(artifact, 'error');
|
|
417
|
+
const redaction = artifactMetadataString(artifact, 'redaction') || artifactMetadataString(artifact, 'redactionPolicy') || 'metadata-only';
|
|
418
|
+
const nextRoute = artifactMetadataString(artifact, 'nextRoute')
|
|
419
|
+
|| `personal_ops action:"lane" laneId:"${laneId}" includeParameters:true`;
|
|
420
|
+
const artifactRoute = `agent_artifacts show artifactId:"${artifact.id}" includeContent:false`;
|
|
421
|
+
const operationLabel = operation.replace(/[-_]+/g, ' ');
|
|
422
|
+
const certification = personalOpsRecordCertification({
|
|
423
|
+
record: artifactMetadata(artifact),
|
|
424
|
+
sourcePath: sourceTool || artifactRoute,
|
|
425
|
+
durableId: subjectId || receiptId,
|
|
426
|
+
recordKind: `${lowerLaneLabel} provider-effect receipt`,
|
|
427
|
+
hasConfirmedEffectRoute: true,
|
|
428
|
+
requireReceipt: true,
|
|
429
|
+
});
|
|
430
|
+
return {
|
|
431
|
+
id: `provider-effect-receipt:${artifact.id}`,
|
|
432
|
+
label: `${laneLabel} effect receipt: ${operationLabel}`,
|
|
433
|
+
status,
|
|
434
|
+
summary: [
|
|
435
|
+
`Confirmed ${lowerLaneLabel} provider-effect receipt ${receiptId} reports ${operation} ${status}.`,
|
|
436
|
+
providerId ? `Provider ${providerId}.` : '',
|
|
437
|
+
subjectId ? `Subject ${previewHarnessText(redactedPersonalOpsText(subjectId), 96)}.` : '',
|
|
438
|
+
`Redaction ${redaction}.`,
|
|
439
|
+
failureReason ? `Failure ${previewHarnessText(redactedPersonalOpsText(failureReason), 140)}.` : '',
|
|
440
|
+
sourceTool ? `Source ${sourceTool}.` : '',
|
|
441
|
+
createdAt ? `Recorded ${createdAt}.` : '',
|
|
442
|
+
].filter(Boolean).join(' '),
|
|
443
|
+
userRoute: `Agent Workspace -> Personal Ops -> ${laneLabel} effect receipts`,
|
|
444
|
+
modelRoute: artifactRoute,
|
|
445
|
+
tags: [
|
|
446
|
+
'provider-effect-receipt',
|
|
447
|
+
'artifact',
|
|
448
|
+
`${laneId}-effect`,
|
|
449
|
+
operation,
|
|
450
|
+
status,
|
|
451
|
+
providerId,
|
|
452
|
+
].filter(Boolean),
|
|
453
|
+
effect: 'read-only',
|
|
454
|
+
capability: effectReceiptCapability(laneId),
|
|
455
|
+
confirmationRequired: false,
|
|
456
|
+
artifactId: artifact.id,
|
|
457
|
+
...(sourceTool ? { sourceTool } : {}),
|
|
458
|
+
certification,
|
|
459
|
+
followUpRoutes: [
|
|
460
|
+
{
|
|
461
|
+
id: 'inspect-effect-receipt',
|
|
462
|
+
label: 'Inspect provider-effect receipt',
|
|
463
|
+
effect: 'read-only',
|
|
464
|
+
modelRoute: artifactRoute,
|
|
465
|
+
requiresConfirmation: false,
|
|
466
|
+
policy: 'Receipt inspection is read-only and uses redacted artifact metadata/content boundaries.',
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
id: 'continue-provider-lane',
|
|
470
|
+
label: `Continue ${lowerLaneLabel} lane review`,
|
|
471
|
+
effect: 'read-only',
|
|
472
|
+
modelRoute: nextRoute,
|
|
473
|
+
requiresConfirmation: false,
|
|
474
|
+
policy: 'Use the lane posture to decide whether a fresh connector read or separate confirmed provider effect is appropriate.',
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
};
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function matchingReadTool(
|
|
482
|
+
connectors: readonly PersonalOpsConnectorSignal[],
|
|
483
|
+
sourceTool: string,
|
|
484
|
+
): { readonly signal: PersonalOpsConnectorSignal; readonly tool: PersonalOpsConnectorTool } | null {
|
|
485
|
+
if (!sourceTool) return null;
|
|
486
|
+
for (const signal of connectors) {
|
|
487
|
+
const tool = (signal.readTools ?? []).find((entry) => entry.qualifiedName === sourceTool || entry.name === sourceTool);
|
|
488
|
+
if (tool) return { signal, tool };
|
|
489
|
+
}
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export function savedReviewFreshness(options: {
|
|
494
|
+
readonly laneId: 'inbox' | 'calendar';
|
|
495
|
+
readonly createdAt: string;
|
|
496
|
+
readonly sourceTool: string;
|
|
497
|
+
readonly connectors: readonly PersonalOpsConnectorSignal[];
|
|
498
|
+
}): PersonalOpsRecordFreshness {
|
|
499
|
+
const match = matchingReadTool(options.connectors, options.sourceTool);
|
|
500
|
+
if (!options.sourceTool) {
|
|
501
|
+
return {
|
|
502
|
+
status: 'source-tool-missing',
|
|
503
|
+
source: 'saved-review-artifact',
|
|
504
|
+
...(options.createdAt ? { lastReviewedAt: options.createdAt } : {}),
|
|
505
|
+
policy: 'This saved review artifact did not preserve a connector source tool, so Agent can only reopen the redacted artifact and cannot offer a precise refresh route.',
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
if (!match) {
|
|
509
|
+
return {
|
|
510
|
+
status: 'provider-contract-missing',
|
|
511
|
+
source: 'saved-review-artifact',
|
|
512
|
+
sourceTool: options.sourceTool,
|
|
513
|
+
...(options.createdAt ? { lastReviewedAt: options.createdAt } : {}),
|
|
514
|
+
policy: 'The saved review names a source tool, but the current runtime does not expose a matching read-only connector route. Reconnect or repair the provider before refreshing.',
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
status: match.signal.status === 'ready' ? 'saved-review-refreshable' : 'connector-attention',
|
|
519
|
+
source: 'saved-review-artifact',
|
|
520
|
+
sourceTool: options.sourceTool,
|
|
521
|
+
...(options.createdAt ? { lastReviewedAt: options.createdAt } : {}),
|
|
522
|
+
...(match.signal.status === 'ready' ? { refreshRoute: personalOpsReadRunRoute(options.laneId, options.sourceTool) } : {}),
|
|
523
|
+
...(match.tool.requiredFields ? { requiredFields: match.tool.requiredFields } : {}),
|
|
524
|
+
...(match.tool.sampleInput ? { sampleInput: match.tool.sampleInput } : {}),
|
|
525
|
+
policy: match.signal.status === 'ready'
|
|
526
|
+
? 'Saved review data is stale by default. Refresh requires the user to supply current connector fields and confirm the read; saved artifacts do not store raw prior input values.'
|
|
527
|
+
: 'A matching connector route exists, but it needs connection, trust, or schema freshness repair before refreshing saved review data.',
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export function refreshableSavedRecordCount(records: readonly PersonalOpsLiveRecord[]): number {
|
|
532
|
+
return records.filter((record) => record.freshness?.status === 'saved-review-refreshable').length;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export function savedReviewQueueRecords(
|
|
536
|
+
context: CommandContext,
|
|
537
|
+
laneId: 'inbox' | 'calendar',
|
|
538
|
+
connectors: readonly PersonalOpsConnectorSignal[],
|
|
539
|
+
): readonly PersonalOpsLiveRecord[] {
|
|
540
|
+
return savedReviewArtifacts(context, laneId)
|
|
541
|
+
.flatMap((artifact) => {
|
|
542
|
+
const reviewLabels = artifactMetadataStringArray(artifact, 'reviewLabels').slice(0, 5);
|
|
543
|
+
const reviewRecordIds = artifactMetadataStringArray(artifact, 'reviewRecordIds').slice(0, reviewLabels.length);
|
|
544
|
+
const sourceTool = artifactMetadataString(artifact, 'sourceTool') || artifactMetadataString(artifact, 'sourceRecordId');
|
|
545
|
+
const createdAt = typeof artifact.createdAt === 'number' && Number.isFinite(artifact.createdAt)
|
|
546
|
+
? new Date(artifact.createdAt).toISOString()
|
|
547
|
+
: '';
|
|
548
|
+
const freshness = savedReviewFreshness({ laneId, createdAt, sourceTool, connectors });
|
|
549
|
+
return reviewLabels.map((label, index): PersonalOpsLiveRecord => {
|
|
550
|
+
const recordId = reviewRecordIds[index] || label || `${index + 1}`;
|
|
551
|
+
const artifactRoute = `agent_artifacts show artifactId:"${artifact.id}" includeContent:true`;
|
|
552
|
+
const calendar = laneId === 'calendar';
|
|
553
|
+
return {
|
|
554
|
+
id: `${calendar ? 'review-event' : 'review-thread'}:${artifact.id}:${safeRecordIdPart(recordId)}`,
|
|
555
|
+
label: `${calendar ? 'Saved event' : 'Saved thread'}: ${label}`,
|
|
556
|
+
status: calendar ? 'ready-for-reminder' : 'ready-for-draft',
|
|
557
|
+
summary: [
|
|
558
|
+
`Derived from saved redacted ${calendar ? 'calendar' : 'inbox'} review artifact ${artifact.id}.`,
|
|
559
|
+
sourceTool ? `Source ${sourceTool}.` : '',
|
|
560
|
+
createdAt ? `Saved ${createdAt}.` : '',
|
|
561
|
+
calendar
|
|
562
|
+
? 'Inspect the artifact before creating reminders or proposing calendar edits.'
|
|
563
|
+
: 'Inspect the artifact before drafting; sending remains a separate confirmed connector action.',
|
|
564
|
+
].filter(Boolean).join(' '),
|
|
565
|
+
userRoute: calendar
|
|
566
|
+
? 'Agent Workspace -> Personal Ops -> Calendar review queue'
|
|
567
|
+
: 'Agent Workspace -> Personal Ops -> Inbox review queue',
|
|
568
|
+
modelRoute: artifactRoute,
|
|
569
|
+
tags: [
|
|
570
|
+
'saved-review',
|
|
571
|
+
'artifact',
|
|
572
|
+
calendar ? 'calendar-event' : 'inbox-thread',
|
|
573
|
+
calendar ? 'reminder-ready' : 'draft-ready',
|
|
574
|
+
],
|
|
575
|
+
effect: 'read-only',
|
|
576
|
+
capability: calendar ? 'calendar-event-review' : 'inbox-thread-review',
|
|
577
|
+
confirmationRequired: false,
|
|
578
|
+
artifactId: artifact.id,
|
|
579
|
+
reviewRecordCount: 1,
|
|
580
|
+
reviewLabels: [label],
|
|
581
|
+
...(sourceTool ? { sourceTool } : {}),
|
|
582
|
+
freshness,
|
|
583
|
+
followUpRoutes: calendar
|
|
584
|
+
? [
|
|
585
|
+
...(freshness.refreshRoute ? [{
|
|
586
|
+
id: 'refresh-saved-event',
|
|
587
|
+
label: 'Refresh saved event from provider',
|
|
588
|
+
effect: 'read-only' as const,
|
|
589
|
+
modelRoute: freshness.refreshRoute,
|
|
590
|
+
requiresConfirmation: true,
|
|
591
|
+
policy: 'Refresh reads current provider data only after the user supplies required fields and confirms the bounded read.',
|
|
592
|
+
}] : []),
|
|
593
|
+
{
|
|
594
|
+
id: 'create-reminder-from-event',
|
|
595
|
+
label: 'Create reminder from saved event',
|
|
596
|
+
effect: 'confirmed-effect',
|
|
597
|
+
modelRoute: 'schedule action:"remind" message:"..." at:"..." confirm:true explicitUserRequest:"..."',
|
|
598
|
+
requiresConfirmation: true,
|
|
599
|
+
policy: 'Create one reminder only after the user reviews the saved event and confirms exact timing.',
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
id: 'calendar-edit-boundary',
|
|
603
|
+
label: 'Inspect calendar edit route',
|
|
604
|
+
effect: 'confirmed-effect',
|
|
605
|
+
modelRoute: 'personal_ops action:"intake" query:"edit saved calendar event" includeParameters:true',
|
|
606
|
+
requiresConfirmation: true,
|
|
607
|
+
policy: 'Calendar edits, RSVP, reschedule, and deletes require a separate inspected connector route and explicit confirmation.',
|
|
608
|
+
},
|
|
609
|
+
]
|
|
610
|
+
: [
|
|
611
|
+
...(freshness.refreshRoute ? [{
|
|
612
|
+
id: 'refresh-saved-thread',
|
|
613
|
+
label: 'Refresh saved thread from provider',
|
|
614
|
+
effect: 'read-only' as const,
|
|
615
|
+
modelRoute: freshness.refreshRoute,
|
|
616
|
+
requiresConfirmation: true,
|
|
617
|
+
policy: 'Refresh reads current provider data only after the user supplies required fields and confirms the bounded read.',
|
|
618
|
+
}] : []),
|
|
619
|
+
{
|
|
620
|
+
id: 'draft-local-reply',
|
|
621
|
+
label: 'Draft local reply from saved thread',
|
|
622
|
+
effect: 'read-only',
|
|
623
|
+
modelRoute: artifactRoute,
|
|
624
|
+
requiresConfirmation: false,
|
|
625
|
+
policy: 'Drafting stays local in the Agent transcript and does not send, label, archive, move, or delete provider records.',
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
id: 'send-reviewed-reply-boundary',
|
|
629
|
+
label: 'Inspect send route for reviewed reply',
|
|
630
|
+
effect: 'confirmed-effect',
|
|
631
|
+
modelRoute: 'personal_ops action:"intake" query:"send reviewed reply from saved inbox review" includeParameters:true',
|
|
632
|
+
requiresConfirmation: true,
|
|
633
|
+
policy: 'Send only after a write-like inbox connector route is inspected and the user confirms exact recipients and body.',
|
|
634
|
+
},
|
|
635
|
+
],
|
|
636
|
+
};
|
|
637
|
+
});
|
|
638
|
+
})
|
|
639
|
+
.slice(0, 10);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export function savedReviewArtifactRecords(
|
|
643
|
+
context: CommandContext,
|
|
644
|
+
laneId: 'inbox' | 'calendar',
|
|
645
|
+
connectors: readonly PersonalOpsConnectorSignal[],
|
|
646
|
+
): readonly PersonalOpsLiveRecord[] {
|
|
647
|
+
return savedReviewArtifacts(context, laneId).map((artifact) => {
|
|
648
|
+
const reviewRecordCount = artifactMetadataNumber(artifact, 'reviewRecordCount') ?? 0;
|
|
649
|
+
const reviewLabels = artifactMetadataStringArray(artifact, 'reviewLabels').slice(0, 5);
|
|
650
|
+
const sourceTool = artifactMetadataString(artifact, 'sourceTool') || artifactMetadataString(artifact, 'sourceRecordId');
|
|
651
|
+
const createdAt = typeof artifact.createdAt === 'number' && Number.isFinite(artifact.createdAt)
|
|
652
|
+
? new Date(artifact.createdAt).toISOString()
|
|
653
|
+
: '';
|
|
654
|
+
const freshness = savedReviewFreshness({ laneId, createdAt, sourceTool, connectors });
|
|
655
|
+
const countText = reviewRecordCount > 0
|
|
656
|
+
? `${reviewRecordCount} normalized review card${reviewRecordCount === 1 ? '' : 's'}`
|
|
657
|
+
: 'normalized review cards';
|
|
658
|
+
return {
|
|
659
|
+
id: `review-artifact:${artifact.id}`,
|
|
660
|
+
label: `${laneId === 'calendar' ? 'Saved agenda review' : 'Saved inbox review'}: ${artifact.filename ?? artifact.id}`,
|
|
661
|
+
status: 'ready',
|
|
662
|
+
summary: [
|
|
663
|
+
`${countText} saved for later review.`,
|
|
664
|
+
reviewLabels.length > 0 ? `Items ${reviewLabels.slice(0, 3).join('; ')}.` : '',
|
|
665
|
+
sourceTool ? `Source ${sourceTool}.` : '',
|
|
666
|
+
createdAt ? `Saved ${createdAt}.` : '',
|
|
667
|
+
'Use the artifact route to reopen redacted cards before summary, draft, or promotion work.',
|
|
668
|
+
].filter(Boolean).join(' '),
|
|
669
|
+
userRoute: 'Agent Workspace -> Artifacts -> Browse artifacts',
|
|
670
|
+
modelRoute: `agent_artifacts show artifactId:"${artifact.id}" includeContent:true`,
|
|
671
|
+
tags: ['saved-review', 'artifact', laneId === 'calendar' ? 'calendar-read' : 'inbox-read'],
|
|
672
|
+
effect: 'read-only',
|
|
673
|
+
capability: laneId === 'calendar' ? 'calendar-review-artifact' : 'inbox-review-artifact',
|
|
674
|
+
confirmationRequired: false,
|
|
675
|
+
artifactId: artifact.id,
|
|
676
|
+
reviewRecordCount,
|
|
677
|
+
...(reviewLabels.length > 0 ? { reviewLabels } : {}),
|
|
678
|
+
...(sourceTool ? { sourceTool } : {}),
|
|
679
|
+
freshness,
|
|
680
|
+
};
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export { reminderOperationRecords, taskOperationRecords } from './agent-harness-personal-ops-operations.ts';
|