@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,742 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
3
|
+
import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
|
|
4
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
5
|
+
import type { UiAutomationSnapshot, UiTasksSnapshot } from '../runtime/ui-read-models.ts';
|
|
6
|
+
import type { AutonomyQueueLiveRecord, AutonomyQueueRecordControl, AutonomyQueueRecordOutput, SnapshotReader } from './agent-harness-autonomy-queue-types.ts';
|
|
7
|
+
function readSnapshot<TSnapshot>(readModel: SnapshotReader<TSnapshot> | undefined): TSnapshot | null {
|
|
8
|
+
if (!readModel) return null;
|
|
9
|
+
try {
|
|
10
|
+
return readModel.getSnapshot();
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function formatEpochMs(value: number | undefined): string | undefined {
|
|
17
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return undefined;
|
|
18
|
+
return new Date(value).toISOString();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatTimeFragment(label: string, value: number | undefined): string {
|
|
22
|
+
const formatted = formatEpochMs(value);
|
|
23
|
+
return formatted ? `${label} ${formatted}` : '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readString(value: unknown): string {
|
|
27
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SENSITIVE_TEXT_PATTERNS: readonly [RegExp, string][] = [
|
|
31
|
+
[/("?\b(?:api[-_]?key|apikey|token|secret|password|passwd|credential|authorization)\b"?\s*:\s*)("[^"]*"|'[^']*'|[^\s,}]+)/gi, '$1"<redacted>"'],
|
|
32
|
+
[/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTHORIZATION|BEARER)[A-Z0-9_]*)=("[^"]*"|'[^']*'|[^\s]+)/gi, '$1=<redacted>'],
|
|
33
|
+
[/(\b(?:token|secret|password|passwd|api[-_]?key|apikey|authorization|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,}]+)/gi, '$1<redacted>'],
|
|
34
|
+
[/(Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1<redacted>'],
|
|
35
|
+
[/(\s--(?:token|password|secret|api-key|api_key)\s+)("[^"]*"|'[^']*'|[^\s]+)/gi, '$1<redacted>'],
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const WATCHER_RECEIPT_PURPOSES = new Set([
|
|
39
|
+
'watcher-receipt',
|
|
40
|
+
'watcher-run-receipt',
|
|
41
|
+
'automation-watcher-receipt',
|
|
42
|
+
'automation-run-history-receipt',
|
|
43
|
+
'durable-run-history-receipt',
|
|
44
|
+
'agent-watcher-receipt',
|
|
45
|
+
'agent-watcher-run-receipt',
|
|
46
|
+
'connected-host-watcher-receipt',
|
|
47
|
+
'connected-host-watcher-run-receipt',
|
|
48
|
+
'connected-host-automation-run-history-receipt',
|
|
49
|
+
'provider-source-record-receipt',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
function redactHarnessOutputText(value: string): string {
|
|
53
|
+
return SENSITIVE_TEXT_PATTERNS.reduce((text, [pattern, replacement]) => text.replace(pattern, replacement), value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function compactUnknown(value: unknown): string {
|
|
57
|
+
if (value === undefined || value === null) return '';
|
|
58
|
+
if (typeof value === 'string') return redactHarnessOutputText(value.replace(/\s+/g, ' ').trim());
|
|
59
|
+
if (typeof value === 'number' || typeof value === 'boolean') return redactHarnessOutputText(String(value));
|
|
60
|
+
try {
|
|
61
|
+
return redactHarnessOutputText(JSON.stringify(value).replace(/\s+/g, ' ').trim());
|
|
62
|
+
} catch {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function artifactMetadata(artifact: ArtifactDescriptor): Readonly<Record<string, unknown>> {
|
|
68
|
+
return artifact.metadata && typeof artifact.metadata === 'object'
|
|
69
|
+
? artifact.metadata as Readonly<Record<string, unknown>>
|
|
70
|
+
: {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function artifactMetadataString(artifact: ArtifactDescriptor, key: string): string {
|
|
74
|
+
return readString(artifactMetadata(artifact)[key]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function artifactMetadataBoolean(artifact: ArtifactDescriptor, key: string): boolean | null {
|
|
78
|
+
const value = artifactMetadata(artifact)[key];
|
|
79
|
+
if (typeof value === 'boolean') return value;
|
|
80
|
+
if (typeof value === 'string') {
|
|
81
|
+
const normalized = value.trim().toLowerCase();
|
|
82
|
+
if (normalized === 'true') return true;
|
|
83
|
+
if (normalized === 'false') return false;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function artifactTimestamp(artifact: ArtifactDescriptor): number {
|
|
89
|
+
const explicit = artifactMetadataString(artifact, 'createdAt')
|
|
90
|
+
|| artifactMetadataString(artifact, 'recordedAt')
|
|
91
|
+
|| artifactMetadataString(artifact, 'completedAt')
|
|
92
|
+
|| artifactMetadataString(artifact, 'timestamp');
|
|
93
|
+
if (explicit) {
|
|
94
|
+
const parsed = Date.parse(explicit);
|
|
95
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
96
|
+
}
|
|
97
|
+
return typeof artifact.createdAt === 'number' && Number.isFinite(artifact.createdAt) ? artifact.createdAt : 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function normalizedReceiptStatus(artifact: ArtifactDescriptor): string {
|
|
101
|
+
const raw = (artifactMetadataString(artifact, 'status')
|
|
102
|
+
|| artifactMetadataString(artifact, 'outcome')
|
|
103
|
+
|| artifactMetadataString(artifact, 'result')).toLowerCase();
|
|
104
|
+
if (!raw) return 'unknown';
|
|
105
|
+
if (['ok', 'ready', 'success', 'succeeded', 'complete', 'completed', 'delivered', 'captured', 'recorded'].includes(raw)) return 'succeeded';
|
|
106
|
+
if (['queued', 'scheduled', 'running', 'pending', 'in-progress', 'in_progress'].includes(raw)) return raw.replace('_', '-');
|
|
107
|
+
if (['blocked', 'needs-review', 'needs_setup', 'needs-setup'].includes(raw)) return 'blocked';
|
|
108
|
+
if (['fail', 'failed', 'error', 'errored'].includes(raw)) return 'failed';
|
|
109
|
+
return raw;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function watcherReceiptArtifacts(context: CommandContext): readonly ArtifactDescriptor[] {
|
|
113
|
+
const store = context.platform.artifactStore;
|
|
114
|
+
if (!store?.list) return [];
|
|
115
|
+
try {
|
|
116
|
+
return store.list(100)
|
|
117
|
+
.filter((artifact) => WATCHER_RECEIPT_PURPOSES.has(artifactMetadataString(artifact, 'purpose')))
|
|
118
|
+
.sort((left, right) => artifactTimestamp(right) - artifactTimestamp(left))
|
|
119
|
+
.slice(0, 20);
|
|
120
|
+
} catch {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function formatIntervalMs(value: number): string {
|
|
126
|
+
if (!Number.isFinite(value) || value <= 0) return `${value}ms`;
|
|
127
|
+
const units: ReadonlyArray<readonly [number, string]> = [
|
|
128
|
+
[86_400_000, 'd'],
|
|
129
|
+
[3_600_000, 'h'],
|
|
130
|
+
[60_000, 'm'],
|
|
131
|
+
[1_000, 's'],
|
|
132
|
+
];
|
|
133
|
+
for (const [size, suffix] of units) {
|
|
134
|
+
if (value >= size && value % size === 0) return `${value / size}${suffix}`;
|
|
135
|
+
}
|
|
136
|
+
return `${value}ms`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function summarizeSchedule(schedule: UiAutomationSnapshot['jobs'][number]['schedule']): string {
|
|
140
|
+
if (schedule.kind === 'cron') {
|
|
141
|
+
return [
|
|
142
|
+
`cron ${schedule.expression}`,
|
|
143
|
+
schedule.timezone ? `timezone ${schedule.timezone}` : '',
|
|
144
|
+
schedule.staggerMs !== undefined ? `stagger ${schedule.staggerMs}ms` : '',
|
|
145
|
+
].filter(Boolean).join(', ');
|
|
146
|
+
}
|
|
147
|
+
if (schedule.kind === 'every') return `every ${formatIntervalMs(schedule.intervalMs)}`;
|
|
148
|
+
return `at ${formatEpochMs(schedule.at) ?? schedule.at}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function quoteRouteValue(value: string): string {
|
|
152
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function operatorMethodRoute(methodId: string, input: Record<string, string>): string {
|
|
156
|
+
const payload = JSON.stringify(input);
|
|
157
|
+
return `agent_operator_method methodId:"${quoteRouteValue(methodId)}" input:${payload} confirm:true explicitUserRequest:"..."`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function availableControl(
|
|
161
|
+
id: string,
|
|
162
|
+
label: string,
|
|
163
|
+
effect: AutonomyQueueRecordControl['effect'],
|
|
164
|
+
modelRoute: string,
|
|
165
|
+
): AutonomyQueueRecordControl {
|
|
166
|
+
return {
|
|
167
|
+
id,
|
|
168
|
+
label,
|
|
169
|
+
state: 'available',
|
|
170
|
+
effect,
|
|
171
|
+
confirmationRequired: effect === 'confirmed-effect',
|
|
172
|
+
modelRoute,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function unavailableControl(id: string, label: string, reason: string): AutonomyQueueRecordControl {
|
|
177
|
+
return {
|
|
178
|
+
id,
|
|
179
|
+
label,
|
|
180
|
+
state: 'unavailable',
|
|
181
|
+
effect: 'confirmed-effect',
|
|
182
|
+
confirmationRequired: true,
|
|
183
|
+
reason,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function controlAvailable(record: AutonomyQueueLiveRecord, id: string): boolean {
|
|
188
|
+
return record.controls?.some((control) => control.id === id && control.state === 'available') === true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function availableControlRoute(record: AutonomyQueueLiveRecord, id: string): string | undefined {
|
|
192
|
+
return record.controls?.find((control) => control.id === id && control.state === 'available')?.modelRoute;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function firstAvailableControlRoute(records: readonly AutonomyQueueLiveRecord[], id: string): string | undefined {
|
|
196
|
+
for (const record of records) {
|
|
197
|
+
const route = availableControlRoute(record, id);
|
|
198
|
+
if (route) return route;
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function taskOutputDescriptor(task: UiTasksSnapshot['tasks'][number]): AutonomyQueueRecordOutput {
|
|
204
|
+
const route = `/tasks output ${task.id}`;
|
|
205
|
+
const resultPreview = task.result !== undefined ? compactUnknown(task.result) : '';
|
|
206
|
+
if (resultPreview.length > 0) {
|
|
207
|
+
return {
|
|
208
|
+
status: 'preview',
|
|
209
|
+
route,
|
|
210
|
+
source: 'runtime-task-result',
|
|
211
|
+
preview: previewHarnessText(resultPreview, 240),
|
|
212
|
+
policy: 'Bounded preview from the connected-host task read model; secret-looking text is redacted. Use the route for the full host-owned output view when available.',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const errorPreview = task.error ? compactUnknown(task.error) : '';
|
|
216
|
+
if (errorPreview.length > 0) {
|
|
217
|
+
return {
|
|
218
|
+
status: 'preview',
|
|
219
|
+
route,
|
|
220
|
+
source: 'runtime-task-error',
|
|
221
|
+
preview: previewHarnessText(errorPreview, 240),
|
|
222
|
+
policy: 'Bounded error preview from the connected-host task read model; secret-looking text is redacted. Use the route for the full host-owned output view when available.',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
status: 'route-only',
|
|
227
|
+
route,
|
|
228
|
+
source: 'not-published',
|
|
229
|
+
policy: 'The connected host has not published task result/error text in the task read model. Use the route for host-owned output if that host exposes it.',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function taskDiagnostics(task: UiTasksSnapshot['tasks'][number]): readonly string[] {
|
|
234
|
+
const retry = task.retryPolicy
|
|
235
|
+
? `retry attempt ${task.retryPolicy.currentAttempt}/${task.retryPolicy.maxAttempts} backoff ${task.retryPolicy.backoff} delay ${task.retryPolicy.delayMs}ms categories ${task.retryPolicy.retryOn.join(',')}`
|
|
236
|
+
: '';
|
|
237
|
+
return [
|
|
238
|
+
retry,
|
|
239
|
+
task.retryAt ? `retry eligible ${formatEpochMs(task.retryAt)}` : '',
|
|
240
|
+
task.exitCode !== undefined ? `exit code ${task.exitCode}` : '',
|
|
241
|
+
task.parentTaskId ? `parent ${task.parentTaskId}` : '',
|
|
242
|
+
task.childTaskIds.length > 0 ? `children ${task.childTaskIds.join(',')}` : '',
|
|
243
|
+
task.correlationId ? `correlation ${task.correlationId}` : '',
|
|
244
|
+
task.turnId ? `turn ${task.turnId}` : '',
|
|
245
|
+
task.error ? `error ${compactUnknown(task.error)}` : '',
|
|
246
|
+
task.result !== undefined ? `result ${compactUnknown(task.result)}` : '',
|
|
247
|
+
`output route /tasks output ${task.id}`,
|
|
248
|
+
].filter((value): value is string => value.length > 0);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function automationRunDiagnostics(run: UiAutomationSnapshot['runs'][number]): readonly string[] {
|
|
252
|
+
const telemetry = run.telemetry;
|
|
253
|
+
const usage = telemetry?.usage;
|
|
254
|
+
const usageSummary = usage
|
|
255
|
+
? [
|
|
256
|
+
`telemetry usage input ${usage.inputTokens}`,
|
|
257
|
+
`output ${usage.outputTokens}`,
|
|
258
|
+
`cache-read ${usage.cacheReadTokens}`,
|
|
259
|
+
`cache-write ${usage.cacheWriteTokens}`,
|
|
260
|
+
usage.reasoningTokens !== undefined ? `reasoning ${usage.reasoningTokens}` : '',
|
|
261
|
+
].filter(Boolean).join(' ')
|
|
262
|
+
: '';
|
|
263
|
+
const callSummary = telemetry
|
|
264
|
+
? [
|
|
265
|
+
telemetry.llmCallCount !== undefined ? `llm ${telemetry.llmCallCount}` : '',
|
|
266
|
+
telemetry.toolCallCount !== undefined ? `tool ${telemetry.toolCallCount}` : '',
|
|
267
|
+
telemetry.turnCount !== undefined ? `turns ${telemetry.turnCount}` : '',
|
|
268
|
+
telemetry.reasoningSummaryPresent !== undefined ? `reasoning-summary ${telemetry.reasoningSummaryPresent ? 'yes' : 'no'}` : '',
|
|
269
|
+
].filter(Boolean).join(' ')
|
|
270
|
+
: '';
|
|
271
|
+
const route = run.route
|
|
272
|
+
? [
|
|
273
|
+
`route ${run.route.id}`,
|
|
274
|
+
run.route.surfaceKind,
|
|
275
|
+
run.route.kind,
|
|
276
|
+
run.route.title,
|
|
277
|
+
].filter(Boolean).join(' ')
|
|
278
|
+
: '';
|
|
279
|
+
const deliveries = (run.deliveryAttempts ?? []).slice(-3).map((delivery) => [
|
|
280
|
+
`delivery ${delivery.id}`,
|
|
281
|
+
delivery.status,
|
|
282
|
+
delivery.responseId ? `response ${delivery.responseId}` : '',
|
|
283
|
+
delivery.endedAt ? `ended ${formatEpochMs(delivery.endedAt)}` : '',
|
|
284
|
+
].filter(Boolean).join(' '));
|
|
285
|
+
return [
|
|
286
|
+
run.continuationMode ? `continuation ${run.continuationMode}` : '',
|
|
287
|
+
run.executionIntent ? `intent ${compactUnknown(run.executionIntent)}` : '',
|
|
288
|
+
telemetry?.source ? `telemetry source ${telemetry.source}` : '',
|
|
289
|
+
telemetry?.modelId ? `telemetry model ${telemetry.modelId}` : '',
|
|
290
|
+
telemetry?.providerId ? `telemetry provider ${telemetry.providerId}` : '',
|
|
291
|
+
usageSummary,
|
|
292
|
+
callSummary ? `telemetry calls ${callSummary}` : '',
|
|
293
|
+
route,
|
|
294
|
+
...deliveries,
|
|
295
|
+
run.result !== undefined ? `result ${compactUnknown(run.result)}` : '',
|
|
296
|
+
run.cancelledReason ? `cancelled ${run.cancelledReason}` : '',
|
|
297
|
+
].filter((value): value is string => value.length > 0);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function scheduleEditRoute(job: UiAutomationSnapshot['jobs'][number]): string {
|
|
301
|
+
const schedule = job.schedule;
|
|
302
|
+
const value = schedule.kind === 'cron'
|
|
303
|
+
? schedule.expression
|
|
304
|
+
: schedule.kind === 'every'
|
|
305
|
+
? formatIntervalMs(schedule.intervalMs)
|
|
306
|
+
: formatEpochMs(schedule.at) ?? String(schedule.at);
|
|
307
|
+
const timezone = schedule.kind === 'cron' && schedule.timezone
|
|
308
|
+
? ` timezone:"${quoteRouteValue(schedule.timezone)}"`
|
|
309
|
+
: '';
|
|
310
|
+
return `schedule action:"edit" scheduleId:"${quoteRouteValue(job.id)}" scheduleKind:"${schedule.kind}" scheduleValue:"${quoteRouteValue(value)}"${timezone} confirm:true explicitUserRequest:"..."`;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function taskStatusRank(status: UiTasksSnapshot['tasks'][number]['status']): number {
|
|
314
|
+
if (status === 'running') return 0;
|
|
315
|
+
if (status === 'queued') return 1;
|
|
316
|
+
if (status === 'blocked') return 2;
|
|
317
|
+
if (status === 'failed') return 3;
|
|
318
|
+
if (status === 'completed') return 4;
|
|
319
|
+
return 5;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function taskLiveRecords(context: CommandContext): readonly AutonomyQueueLiveRecord[] {
|
|
323
|
+
const snapshot = readSnapshot(context.platform.readModels?.tasks);
|
|
324
|
+
const tasks = [...(snapshot?.tasks ?? [])].sort((left, right) => {
|
|
325
|
+
const rankDelta = taskStatusRank(left.status) - taskStatusRank(right.status);
|
|
326
|
+
if (rankDelta !== 0) return rankDelta;
|
|
327
|
+
const leftTime = left.endedAt ?? left.startedAt ?? left.queuedAt;
|
|
328
|
+
const rightTime = right.endedAt ?? right.startedAt ?? right.queuedAt;
|
|
329
|
+
return rightTime - leftTime || left.title.localeCompare(right.title);
|
|
330
|
+
});
|
|
331
|
+
return tasks.slice(0, 20).map((task) => {
|
|
332
|
+
const active = task.status === 'queued' || task.status === 'running' || task.status === 'blocked';
|
|
333
|
+
const canCancel = task.cancellable && active;
|
|
334
|
+
const canRetry = task.status === 'failed' || task.status === 'cancelled';
|
|
335
|
+
const cancelRoute = operatorMethodRoute('tasks.cancel', { taskId: task.id });
|
|
336
|
+
const retryRoute = operatorMethodRoute('tasks.retry', { taskId: task.id });
|
|
337
|
+
const retry = task.retryPolicy
|
|
338
|
+
? `attempt ${task.retryPolicy.currentAttempt}/${task.retryPolicy.maxAttempts}`
|
|
339
|
+
: '';
|
|
340
|
+
const summary = [
|
|
341
|
+
`${task.status} ${task.kind}`,
|
|
342
|
+
`owner ${task.owner}`,
|
|
343
|
+
task.cancellable ? 'host-cancellable' : 'not cancellable',
|
|
344
|
+
formatTimeFragment('queued', task.queuedAt),
|
|
345
|
+
formatTimeFragment('started', task.startedAt),
|
|
346
|
+
formatTimeFragment('ended', task.endedAt),
|
|
347
|
+
retry,
|
|
348
|
+
task.retryAt ? `retry ${formatEpochMs(task.retryAt)}` : '',
|
|
349
|
+
task.error ? `error ${compactUnknown(task.error)}` : '',
|
|
350
|
+
task.result !== undefined ? `result ${compactUnknown(task.result)}` : compactUnknown(task.description),
|
|
351
|
+
].filter(Boolean).join(' | ');
|
|
352
|
+
const output = taskOutputDescriptor(task);
|
|
353
|
+
return {
|
|
354
|
+
id: task.id,
|
|
355
|
+
label: task.title,
|
|
356
|
+
status: task.status,
|
|
357
|
+
phase: task.kind,
|
|
358
|
+
updatedAt: formatEpochMs(task.endedAt ?? task.startedAt ?? task.queuedAt),
|
|
359
|
+
summary,
|
|
360
|
+
inspectRoute: `/tasks show ${task.id}`,
|
|
361
|
+
...(canCancel ? { cancelRoute } : {}),
|
|
362
|
+
nextSteps: [
|
|
363
|
+
`/tasks show ${task.id}`,
|
|
364
|
+
`/tasks output ${task.id}`,
|
|
365
|
+
...(canCancel ? [cancelRoute] : []),
|
|
366
|
+
...(canRetry ? [retryRoute] : []),
|
|
367
|
+
'Use /workplan for Agent-owned visible task changes; host task mutation requires exact confirmed daemon methods.',
|
|
368
|
+
],
|
|
369
|
+
sourceIds: [
|
|
370
|
+
task.parentTaskId,
|
|
371
|
+
...task.childTaskIds,
|
|
372
|
+
task.correlationId,
|
|
373
|
+
task.turnId,
|
|
374
|
+
].filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
375
|
+
...(task.error ? { logTail: [compactUnknown(task.error)] } : {}),
|
|
376
|
+
output,
|
|
377
|
+
diagnostics: taskDiagnostics(task),
|
|
378
|
+
controls: [
|
|
379
|
+
availableControl('inspect', 'Inspect task', 'read-only', `/tasks show ${task.id}`),
|
|
380
|
+
availableControl('output', 'Show output', 'read-only', `/tasks output ${task.id}`),
|
|
381
|
+
canCancel
|
|
382
|
+
? availableControl('cancel', 'Cancel task', 'confirmed-effect', cancelRoute)
|
|
383
|
+
: unavailableControl('cancel', 'Cancel task', task.cancellable ? `Task is ${task.status}; cancel is only useful before terminal completion.` : 'Task owner did not mark this task cancellable.'),
|
|
384
|
+
canRetry
|
|
385
|
+
? availableControl('retry', 'Retry task', 'confirmed-effect', retryRoute)
|
|
386
|
+
: unavailableControl('retry', 'Retry task', `Task is ${task.status}; retry is only offered for failed or cancelled tasks.`),
|
|
387
|
+
],
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function approvalLiveRecords(context: CommandContext): readonly AutonomyQueueLiveRecord[] {
|
|
393
|
+
const snapshot = readSnapshot(context.platform.readModels?.controlPlane);
|
|
394
|
+
const statusRank = new Map([
|
|
395
|
+
['pending', 0],
|
|
396
|
+
['claimed', 1],
|
|
397
|
+
['denied', 2],
|
|
398
|
+
['cancelled', 3],
|
|
399
|
+
['expired', 4],
|
|
400
|
+
['approved', 5],
|
|
401
|
+
]);
|
|
402
|
+
return [...(snapshot?.approvals ?? [])]
|
|
403
|
+
.sort((left, right) => {
|
|
404
|
+
const rankDelta = (statusRank.get(left.status) ?? 99) - (statusRank.get(right.status) ?? 99);
|
|
405
|
+
if (rankDelta !== 0) return rankDelta;
|
|
406
|
+
return right.updatedAt - left.updatedAt || left.id.localeCompare(right.id);
|
|
407
|
+
})
|
|
408
|
+
.slice(0, 20)
|
|
409
|
+
.map((approval) => {
|
|
410
|
+
const active = approval.status === 'pending' || approval.status === 'claimed';
|
|
411
|
+
const label = `${approval.request.tool}: ${approval.request.analysis.summary}`;
|
|
412
|
+
const approveRoute = `agent_operator_action action:"approvals.approve" approvalId:"${approval.id}" confirm:true explicitUserRequest:"..."`;
|
|
413
|
+
const denyRoute = `agent_operator_action action:"approvals.deny" approvalId:"${approval.id}" confirm:true explicitUserRequest:"..."`;
|
|
414
|
+
const cancelRoute = `agent_operator_action action:"approvals.cancel" approvalId:"${approval.id}" confirm:true explicitUserRequest:"..."`;
|
|
415
|
+
return {
|
|
416
|
+
id: approval.id,
|
|
417
|
+
label,
|
|
418
|
+
status: approval.status,
|
|
419
|
+
phase: approval.request.analysis.riskLevel,
|
|
420
|
+
updatedAt: formatEpochMs(approval.updatedAt),
|
|
421
|
+
summary: [
|
|
422
|
+
`${approval.request.category}/${approval.request.analysis.riskLevel}`,
|
|
423
|
+
approval.request.analysis.classification,
|
|
424
|
+
`call ${approval.callId}`,
|
|
425
|
+
approval.sessionId ? `session ${approval.sessionId}` : '',
|
|
426
|
+
approval.routeId ? `route ${approval.routeId}` : '',
|
|
427
|
+
approval.claimedBy ? `claimed by ${approval.claimedBy}` : '',
|
|
428
|
+
approval.resolvedBy ? `resolved by ${approval.resolvedBy}` : '',
|
|
429
|
+
compactUnknown(approval.request.args),
|
|
430
|
+
].filter(Boolean).join(' | '),
|
|
431
|
+
inspectRoute: '/approval matrix',
|
|
432
|
+
...(active ? {
|
|
433
|
+
cancelRoute,
|
|
434
|
+
} : {}),
|
|
435
|
+
nextSteps: active ? [
|
|
436
|
+
approveRoute,
|
|
437
|
+
denyRoute,
|
|
438
|
+
cancelRoute,
|
|
439
|
+
] : [`/approval matrix`],
|
|
440
|
+
sourceIds: [
|
|
441
|
+
approval.callId,
|
|
442
|
+
approval.sessionId,
|
|
443
|
+
approval.routeId,
|
|
444
|
+
...approval.audit.map((entry) => entry.id),
|
|
445
|
+
].filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
446
|
+
logTail: approval.audit.slice(-3).map((entry) => [
|
|
447
|
+
entry.action,
|
|
448
|
+
entry.actor,
|
|
449
|
+
formatEpochMs(entry.createdAt),
|
|
450
|
+
entry.note,
|
|
451
|
+
].filter(Boolean).join(' ')),
|
|
452
|
+
controls: [
|
|
453
|
+
availableControl('inspect', 'Inspect approval matrix', 'read-only', '/approval matrix'),
|
|
454
|
+
active
|
|
455
|
+
? availableControl('approve', 'Approve approval', 'confirmed-effect', approveRoute)
|
|
456
|
+
: unavailableControl('approve', 'Approve approval', `Approval is ${approval.status}; only pending or claimed approvals can be approved.`),
|
|
457
|
+
active
|
|
458
|
+
? availableControl('deny', 'Deny approval', 'confirmed-effect', denyRoute)
|
|
459
|
+
: unavailableControl('deny', 'Deny approval', `Approval is ${approval.status}; only pending or claimed approvals can be denied.`),
|
|
460
|
+
active
|
|
461
|
+
? availableControl('cancel', 'Cancel approval', 'confirmed-effect', cancelRoute)
|
|
462
|
+
: unavailableControl('cancel', 'Cancel approval', `Approval is ${approval.status}; only pending or claimed approvals can be cancelled.`),
|
|
463
|
+
],
|
|
464
|
+
};
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export function automationRunLiveRecords(context: CommandContext): readonly AutonomyQueueLiveRecord[] {
|
|
469
|
+
const snapshot = readSnapshot(context.platform.readModels?.automation);
|
|
470
|
+
return [...(snapshot?.runs ?? [])]
|
|
471
|
+
.sort((left, right) => {
|
|
472
|
+
const activeDelta = Number(right.status === 'queued' || right.status === 'running') - Number(left.status === 'queued' || left.status === 'running');
|
|
473
|
+
if (activeDelta !== 0) return activeDelta;
|
|
474
|
+
return right.queuedAt - left.queuedAt || left.id.localeCompare(right.id);
|
|
475
|
+
})
|
|
476
|
+
.slice(0, 20)
|
|
477
|
+
.map((run) => {
|
|
478
|
+
const active = run.status === 'queued' || run.status === 'running';
|
|
479
|
+
const failed = run.status === 'failed';
|
|
480
|
+
const cancelRoute = `agent_operator_action action:"automation.runs.cancel" runId:"${run.id}" confirm:true explicitUserRequest:"..."`;
|
|
481
|
+
const retryRoute = `agent_operator_action action:"automation.runs.retry" runId:"${run.id}" confirm:true explicitUserRequest:"..."`;
|
|
482
|
+
const timing = [
|
|
483
|
+
formatTimeFragment('queued', run.queuedAt),
|
|
484
|
+
formatTimeFragment('started', run.startedAt),
|
|
485
|
+
formatTimeFragment('ended', run.endedAt),
|
|
486
|
+
run.durationMs !== undefined ? `duration ${run.durationMs}ms` : '',
|
|
487
|
+
].filter(Boolean).join(', ');
|
|
488
|
+
return {
|
|
489
|
+
id: run.id,
|
|
490
|
+
label: `${run.jobId} -> ${run.target.kind}`,
|
|
491
|
+
status: run.status,
|
|
492
|
+
phase: run.scheduleKind ?? run.triggeredBy.kind,
|
|
493
|
+
updatedAt: formatEpochMs(run.updatedAt),
|
|
494
|
+
summary: [
|
|
495
|
+
`job ${run.jobId}`,
|
|
496
|
+
`target ${run.target.kind}`,
|
|
497
|
+
`attempt ${run.attempt}`,
|
|
498
|
+
run.agentId ? `agent ${run.agentId}` : '',
|
|
499
|
+
run.sessionId ? `session ${run.sessionId}` : '',
|
|
500
|
+
run.routeId ? `route ${run.routeId}` : '',
|
|
501
|
+
run.modelId ? `model ${run.modelId}` : '',
|
|
502
|
+
run.providerId ? `provider ${run.providerId}` : '',
|
|
503
|
+
timing,
|
|
504
|
+
run.error ? `error ${run.error}` : '',
|
|
505
|
+
run.result !== undefined ? `result ${compactUnknown(run.result)}` : '',
|
|
506
|
+
].filter(Boolean).join(' | '),
|
|
507
|
+
inspectRoute: 'workspace action:"action" actionId:"schedule-list"',
|
|
508
|
+
...(active ? {
|
|
509
|
+
cancelRoute,
|
|
510
|
+
} : {}),
|
|
511
|
+
nextSteps: [
|
|
512
|
+
...(active ? [cancelRoute] : []),
|
|
513
|
+
...(failed ? [retryRoute] : []),
|
|
514
|
+
`workspace action:"action" actionId:"schedule-list"`,
|
|
515
|
+
],
|
|
516
|
+
sourceIds: [
|
|
517
|
+
run.jobId,
|
|
518
|
+
run.agentId,
|
|
519
|
+
run.sessionId,
|
|
520
|
+
run.routeId,
|
|
521
|
+
run.triggeredBy.id,
|
|
522
|
+
...run.deliveryIds,
|
|
523
|
+
].filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
524
|
+
logTail: [
|
|
525
|
+
run.error,
|
|
526
|
+
run.cancelledReason,
|
|
527
|
+
].filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
528
|
+
diagnostics: automationRunDiagnostics(run),
|
|
529
|
+
controls: [
|
|
530
|
+
availableControl('inspect', 'Inspect schedule list', 'read-only', 'workspace action:"action" actionId:"schedule-list"'),
|
|
531
|
+
active
|
|
532
|
+
? availableControl('cancel', 'Cancel automation run', 'confirmed-effect', cancelRoute)
|
|
533
|
+
: unavailableControl('cancel', 'Cancel automation run', `Run is ${run.status}; cancel is only offered for queued or running runs.`),
|
|
534
|
+
failed
|
|
535
|
+
? availableControl('retry', 'Retry automation run', 'confirmed-effect', retryRoute)
|
|
536
|
+
: unavailableControl('retry', 'Retry automation run', `Run is ${run.status}; retry is only offered for failed runs.`),
|
|
537
|
+
],
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export function watcherReceiptLiveRecords(context: CommandContext): readonly AutonomyQueueLiveRecord[] {
|
|
543
|
+
return watcherReceiptArtifacts(context).map((artifact) => {
|
|
544
|
+
const purpose = artifactMetadataString(artifact, 'purpose');
|
|
545
|
+
const operation = artifactMetadataString(artifact, 'operation')
|
|
546
|
+
|| artifactMetadataString(artifact, 'action')
|
|
547
|
+
|| artifactMetadataString(artifact, 'event')
|
|
548
|
+
|| 'watcher-run';
|
|
549
|
+
const status = normalizedReceiptStatus(artifact);
|
|
550
|
+
const watcherId = artifactMetadataString(artifact, 'watcherId')
|
|
551
|
+
|| artifactMetadataString(artifact, 'sourceId')
|
|
552
|
+
|| artifactMetadataString(artifact, 'providerSourceId');
|
|
553
|
+
const runId = artifactMetadataString(artifact, 'runId')
|
|
554
|
+
|| artifactMetadataString(artifact, 'jobRunId')
|
|
555
|
+
|| artifactMetadataString(artifact, 'automationRunId');
|
|
556
|
+
const provider = artifactMetadataString(artifact, 'providerId')
|
|
557
|
+
|| artifactMetadataString(artifact, 'provider')
|
|
558
|
+
|| artifactMetadataString(artifact, 'sourceProvider');
|
|
559
|
+
const trigger = artifactMetadataString(artifact, 'trigger')
|
|
560
|
+
|| artifactMetadataString(artifact, 'triggerKind')
|
|
561
|
+
|| artifactMetadataString(artifact, 'eventKind');
|
|
562
|
+
const correlationId = artifactMetadataString(artifact, 'correlationId')
|
|
563
|
+
|| artifactMetadataString(artifact, 'turnId')
|
|
564
|
+
|| artifactMetadataString(artifact, 'sessionId');
|
|
565
|
+
const redaction = artifactMetadataString(artifact, 'redaction')
|
|
566
|
+
|| artifactMetadataString(artifact, 'redactionPolicy')
|
|
567
|
+
|| 'metadata-only';
|
|
568
|
+
const failureReason = artifactMetadataString(artifact, 'failureReason')
|
|
569
|
+
|| artifactMetadataString(artifact, 'error');
|
|
570
|
+
const sourceTool = artifactMetadataString(artifact, 'sourceTool')
|
|
571
|
+
|| artifactMetadataString(artifact, 'qualifiedName');
|
|
572
|
+
const payloadRedacted = artifactMetadataBoolean(artifact, 'payloadRedacted');
|
|
573
|
+
const artifactRoute = `agent_artifacts show artifactId:"${artifact.id}" includeContent:false`;
|
|
574
|
+
const queueRoute = 'autonomy action:"item" queueItemId:"automation-runs" includeParameters:true';
|
|
575
|
+
return {
|
|
576
|
+
id: `watcher-receipt:${artifact.id}`,
|
|
577
|
+
label: `Watcher receipt: ${operation.replace(/[-_]+/g, ' ')}`,
|
|
578
|
+
status,
|
|
579
|
+
phase: trigger || operation,
|
|
580
|
+
updatedAt: formatEpochMs(artifactTimestamp(artifact)),
|
|
581
|
+
summary: [
|
|
582
|
+
`Durable watcher/run receipt ${artifact.id} reports ${operation} ${status}.`,
|
|
583
|
+
`Redaction ${redaction}.`,
|
|
584
|
+
payloadRedacted === true ? 'Payload redacted.' : payloadRedacted === false ? 'Payload redaction not asserted.' : '',
|
|
585
|
+
provider ? `Provider ${provider}.` : '',
|
|
586
|
+
watcherId ? `Watcher ${compactUnknown(watcherId)}.` : '',
|
|
587
|
+
runId ? `Run ${compactUnknown(runId)}.` : '',
|
|
588
|
+
trigger ? `Trigger ${compactUnknown(trigger)}.` : '',
|
|
589
|
+
failureReason ? `Failure ${compactUnknown(failureReason)}.` : '',
|
|
590
|
+
sourceTool ? `Source ${sourceTool}.` : '',
|
|
591
|
+
].filter(Boolean).join(' '),
|
|
592
|
+
inspectRoute: artifactRoute,
|
|
593
|
+
nextSteps: [
|
|
594
|
+
artifactRoute,
|
|
595
|
+
queueRoute,
|
|
596
|
+
],
|
|
597
|
+
sourceIds: [
|
|
598
|
+
watcherId,
|
|
599
|
+
runId,
|
|
600
|
+
provider,
|
|
601
|
+
trigger,
|
|
602
|
+
correlationId,
|
|
603
|
+
].filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
604
|
+
diagnostics: [
|
|
605
|
+
`purpose ${purpose}`,
|
|
606
|
+
`receipt artifact ${artifact.id}`,
|
|
607
|
+
`redaction ${redaction}`,
|
|
608
|
+
failureReason ? `failure ${compactUnknown(failureReason)}` : '',
|
|
609
|
+
].filter((value): value is string => value.length > 0),
|
|
610
|
+
...(failureReason ? { logTail: [compactUnknown(failureReason)] } : {}),
|
|
611
|
+
controls: [
|
|
612
|
+
availableControl('inspect', 'Inspect watcher receipt', 'read-only', artifactRoute),
|
|
613
|
+
availableControl('queue', 'Inspect automation queue', 'read-only', queueRoute),
|
|
614
|
+
],
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export function scheduleLiveRecords(context: CommandContext): readonly AutonomyQueueLiveRecord[] {
|
|
620
|
+
const snapshot = readSnapshot(context.platform.readModels?.automation);
|
|
621
|
+
return [...(snapshot?.jobs ?? [])]
|
|
622
|
+
.sort((left, right) => {
|
|
623
|
+
const errorDelta = Number(right.status === 'error') - Number(left.status === 'error');
|
|
624
|
+
if (errorDelta !== 0) return errorDelta;
|
|
625
|
+
return (right.nextRunAt ?? 0) - (left.nextRunAt ?? 0) || left.name.localeCompare(right.name);
|
|
626
|
+
})
|
|
627
|
+
.slice(0, 20)
|
|
628
|
+
.map((job) => {
|
|
629
|
+
const enabled = job.enabled && job.status === 'enabled';
|
|
630
|
+
const paused = job.status === 'paused' || !job.enabled;
|
|
631
|
+
const toggleRoute = enabled
|
|
632
|
+
? `schedule action:"pause" scheduleId:"${job.id}" confirm:true explicitUserRequest:"..."`
|
|
633
|
+
: `schedule action:"resume" scheduleId:"${job.id}" confirm:true explicitUserRequest:"..."`;
|
|
634
|
+
const runRoute = `schedule action:"run" scheduleId:"${job.id}" confirm:true explicitUserRequest:"..."`;
|
|
635
|
+
const editRoute = scheduleEditRoute(job);
|
|
636
|
+
const deleteRoute = `schedule action:"delete" scheduleId:"${job.id}" confirm:true explicitUserRequest:"..."`;
|
|
637
|
+
return {
|
|
638
|
+
id: job.id,
|
|
639
|
+
label: job.name,
|
|
640
|
+
status: job.status,
|
|
641
|
+
phase: job.schedule.kind,
|
|
642
|
+
updatedAt: formatEpochMs(job.updatedAt),
|
|
643
|
+
summary: [
|
|
644
|
+
enabled ? 'enabled' : paused ? 'paused' : job.status,
|
|
645
|
+
summarizeSchedule(job.schedule),
|
|
646
|
+
`runs ${job.runCount}`,
|
|
647
|
+
`success ${job.successCount}`,
|
|
648
|
+
`failed ${job.failureCount}`,
|
|
649
|
+
formatTimeFragment('next', job.nextRunAt),
|
|
650
|
+
formatTimeFragment('last', job.lastRunAt),
|
|
651
|
+
job.lastRunId ? `last run ${job.lastRunId}` : '',
|
|
652
|
+
job.description ?? '',
|
|
653
|
+
job.pausedReason ? `paused ${job.pausedReason}` : '',
|
|
654
|
+
].filter(Boolean).join(' | '),
|
|
655
|
+
inspectRoute: '/schedule list',
|
|
656
|
+
...(enabled ? { cancelRoute: toggleRoute } : {}),
|
|
657
|
+
...(enabled ? { pauseRoute: toggleRoute } : { resumeRoute: toggleRoute }),
|
|
658
|
+
nextSteps: [
|
|
659
|
+
runRoute,
|
|
660
|
+
editRoute,
|
|
661
|
+
toggleRoute,
|
|
662
|
+
deleteRoute,
|
|
663
|
+
`workspace action:"action" actionId:"schedule-list"`,
|
|
664
|
+
],
|
|
665
|
+
sourceIds: [
|
|
666
|
+
job.source.id,
|
|
667
|
+
job.lastRunId,
|
|
668
|
+
...job.labels,
|
|
669
|
+
].filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
670
|
+
...(job.pausedReason || job.status === 'error' ? {
|
|
671
|
+
logTail: [job.pausedReason ?? `Schedule status ${job.status}`],
|
|
672
|
+
} : {}),
|
|
673
|
+
controls: [
|
|
674
|
+
availableControl('inspect', 'Inspect schedules', 'read-only', '/schedule list'),
|
|
675
|
+
availableControl('run', 'Run schedule now', 'confirmed-effect', runRoute),
|
|
676
|
+
availableControl('edit', 'Edit schedule', 'confirmed-effect', editRoute),
|
|
677
|
+
availableControl(enabled ? 'pause' : 'resume', enabled ? 'Pause schedule' : 'Resume schedule', 'confirmed-effect', toggleRoute),
|
|
678
|
+
availableControl(enabled ? 'disable' : 'enable', enabled ? 'Disable schedule' : 'Enable schedule', 'confirmed-effect', toggleRoute),
|
|
679
|
+
availableControl('delete', 'Delete schedule', 'confirmed-effect', deleteRoute),
|
|
680
|
+
],
|
|
681
|
+
};
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export function researchRunLiveRecords(snapshot: ReturnType<typeof buildAgentWorkspaceRuntimeSnapshot>): readonly AutonomyQueueLiveRecord[] {
|
|
686
|
+
return snapshot.researchRuns.map((run) => {
|
|
687
|
+
const terminal = run.status === 'cancelled' || run.status === 'completed' || run.status === 'failed';
|
|
688
|
+
const canPause = run.status === 'running' || run.status === 'blocked';
|
|
689
|
+
const canResume = run.status === 'paused' || run.status === 'planned' || run.status === 'blocked';
|
|
690
|
+
const inspectRoute = `research action:"run" runId="${run.id}"`;
|
|
691
|
+
const cancelRoute = `research action:"cancel" id="${run.id}" note="..." confirm:true explicitUserRequest:"..."`;
|
|
692
|
+
const checkpointRoute = `research action:"checkpoint" id="${run.id}" note="..." progress:${run.progress} confirm:true explicitUserRequest:"..."`;
|
|
693
|
+
const pauseRoute = `research action:"pause" id="${run.id}" note="..." confirm:true explicitUserRequest:"..."`;
|
|
694
|
+
const resumeRoute = `research action:"resume" id="${run.id}" note="..." confirm:true explicitUserRequest:"..."`;
|
|
695
|
+
const availableNextRoutes = [
|
|
696
|
+
checkpointRoute,
|
|
697
|
+
...(canPause ? [pauseRoute] : []),
|
|
698
|
+
...(canResume ? [resumeRoute] : []),
|
|
699
|
+
cancelRoute,
|
|
700
|
+
];
|
|
701
|
+
return {
|
|
702
|
+
id: run.id,
|
|
703
|
+
label: run.title,
|
|
704
|
+
status: run.status,
|
|
705
|
+
phase: run.phase,
|
|
706
|
+
progress: run.progress,
|
|
707
|
+
updatedAt: run.updatedAt,
|
|
708
|
+
summary: [
|
|
709
|
+
`${run.status}/${run.phase} ${run.progress}%`,
|
|
710
|
+
`${run.checkpointCount} checkpoint(s)`,
|
|
711
|
+
`${run.sourceIds.length} source(s)`,
|
|
712
|
+
run.reportArtifactId ? `report ${run.reportArtifactId}` : '',
|
|
713
|
+
run.note ?? '',
|
|
714
|
+
].filter(Boolean).join(' | '),
|
|
715
|
+
inspectRoute,
|
|
716
|
+
...(terminal ? {} : {
|
|
717
|
+
cancelRoute,
|
|
718
|
+
checkpointRoute,
|
|
719
|
+
...(canPause ? { pauseRoute } : {}),
|
|
720
|
+
...(canResume ? { resumeRoute } : {}),
|
|
721
|
+
}),
|
|
722
|
+
nextSteps: terminal ? run.nextSteps : [...run.nextSteps, ...availableNextRoutes],
|
|
723
|
+
sourceIds: run.sourceIds,
|
|
724
|
+
logTail: run.logTail,
|
|
725
|
+
controls: [
|
|
726
|
+
availableControl('inspect', 'Inspect research run', 'read-only', inspectRoute),
|
|
727
|
+
terminal
|
|
728
|
+
? unavailableControl('checkpoint', 'Checkpoint research run', `Research run is ${run.status}; checkpoint is only offered before terminal completion.`)
|
|
729
|
+
: availableControl('checkpoint', 'Checkpoint research run', 'confirmed-effect', checkpointRoute),
|
|
730
|
+
canPause
|
|
731
|
+
? availableControl('pause', 'Pause research run', 'confirmed-effect', pauseRoute)
|
|
732
|
+
: unavailableControl('pause', 'Pause research run', terminal ? `Research run is ${run.status}; pause is only offered before terminal completion.` : `Research run is ${run.status}; pause is offered for running or blocked runs.`),
|
|
733
|
+
canResume
|
|
734
|
+
? availableControl('resume', 'Resume research run', 'confirmed-effect', resumeRoute)
|
|
735
|
+
: unavailableControl('resume', 'Resume research run', terminal ? `Research run is ${run.status}; resume is only offered before terminal completion.` : `Research run is ${run.status}; resume is offered for planned, paused, or blocked runs.`),
|
|
736
|
+
terminal
|
|
737
|
+
? unavailableControl('cancel', 'Cancel research run', `Research run is ${run.status}; cancel is only offered before terminal completion.`)
|
|
738
|
+
: availableControl('cancel', 'Cancel research run', 'confirmed-effect', cancelRoute),
|
|
739
|
+
],
|
|
740
|
+
};
|
|
741
|
+
});
|
|
742
|
+
}
|