@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,421 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
3
|
+
import { readRecord, readString } from './agent-harness-model-routing-utils.ts';
|
|
4
|
+
|
|
5
|
+
export type AgentHarnessInteractiveRuntimeKind = 'process-output' | 'pty-session' | 'sudo-mediation' | 'browser-desktop-receipt';
|
|
6
|
+
export type AgentHarnessInteractiveRuntimeStatus = 'ready' | 'running' | 'completed' | 'blocked' | 'failed' | 'attention' | 'unknown';
|
|
7
|
+
|
|
8
|
+
export interface AgentHarnessInteractiveRuntimeCertification {
|
|
9
|
+
readonly schemaStatus: 'certified' | 'legacy';
|
|
10
|
+
readonly schemaVersion?: string;
|
|
11
|
+
readonly publicationGuarantee?: string;
|
|
12
|
+
readonly publisher?: string;
|
|
13
|
+
readonly provenance?: readonly string[];
|
|
14
|
+
readonly receiptId?: string;
|
|
15
|
+
readonly receiptRoute?: string;
|
|
16
|
+
readonly missingSignals: readonly string[];
|
|
17
|
+
readonly policy: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AgentHarnessInteractiveRuntimeRoute {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly label: string;
|
|
23
|
+
readonly modelRoute: string;
|
|
24
|
+
readonly effect: 'read-only' | 'confirmed' | 'confirmed-admin';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AgentHarnessInteractiveRuntimeRecord {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly kind: AgentHarnessInteractiveRuntimeKind;
|
|
30
|
+
readonly status: AgentHarnessInteractiveRuntimeStatus;
|
|
31
|
+
readonly source: string;
|
|
32
|
+
readonly label: string;
|
|
33
|
+
readonly summary: string;
|
|
34
|
+
readonly processId?: string;
|
|
35
|
+
readonly sessionId?: string;
|
|
36
|
+
readonly pid?: number;
|
|
37
|
+
readonly command?: string;
|
|
38
|
+
readonly surface?: string;
|
|
39
|
+
readonly action?: string;
|
|
40
|
+
readonly receiptId?: string;
|
|
41
|
+
readonly outputChunks?: readonly Record<string, unknown>[];
|
|
42
|
+
readonly routes: readonly AgentHarnessInteractiveRuntimeRoute[];
|
|
43
|
+
readonly updatedAt?: string;
|
|
44
|
+
readonly certification: AgentHarnessInteractiveRuntimeCertification;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const SECRET_PATTERNS: readonly [RegExp, string][] = [
|
|
48
|
+
[/secret:\/\/[^\s,'"}]+/gi, 'secret://<redacted>'],
|
|
49
|
+
[/("?\b(?:api[-_]?key|apikey|token|secret|password|passwd|credential|authorization)\b"?\s*:\s*)("[^"]*"|'[^']*'|[^\s,}]+)/gi, '$1"<redacted>"'],
|
|
50
|
+
[/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTHORIZATION|BEARER)[A-Z0-9_]*)=("[^"]*"|'[^']*'|[^\s]+)/gi, '$1=<redacted>'],
|
|
51
|
+
[/(\b(?:token|secret|password|passwd|api[-_]?key|apikey|authorization|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,}]+)/gi, '$1<redacted>'],
|
|
52
|
+
[/(Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1<redacted>'],
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
function redactedPreview(value: unknown, limit = 180): string {
|
|
56
|
+
const raw = readString(value);
|
|
57
|
+
const redacted = SECRET_PATTERNS.reduce((text, [pattern, replacement]) => text.replace(pattern, replacement), raw);
|
|
58
|
+
return previewHarnessText(redacted, limit);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function readBoolean(value: unknown): boolean | null {
|
|
62
|
+
if (typeof value === 'boolean') return value;
|
|
63
|
+
const raw = readString(value).toLowerCase();
|
|
64
|
+
if (['true', 'yes', '1', 'ready', 'available', 'published', 'certified'].includes(raw)) return true;
|
|
65
|
+
if (['false', 'no', '0', 'blocked', 'missing', 'unavailable'].includes(raw)) return false;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function readNumber(value: unknown): number | undefined {
|
|
70
|
+
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
71
|
+
return typeof parsed === 'number' && Number.isFinite(parsed) ? Math.trunc(parsed) : undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readTimestamp(value: unknown): string | undefined {
|
|
75
|
+
const raw = readString(value);
|
|
76
|
+
const parsed = raw ? Date.parse(raw) : typeof value === 'number' && Number.isFinite(value) ? value : NaN;
|
|
77
|
+
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function stringArray(value: unknown): readonly string[] {
|
|
81
|
+
if (Array.isArray(value)) return [...new Set(value.map((entry) => readString(entry)).filter(Boolean))].slice(0, 10);
|
|
82
|
+
const raw = readString(value);
|
|
83
|
+
return raw ? raw.split(',').map((entry) => entry.trim()).filter(Boolean).slice(0, 10) : [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function firstString(records: readonly Readonly<Record<string, unknown>>[], keys: readonly string[]): string {
|
|
87
|
+
for (const record of records) {
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
const value = readString(record[key]);
|
|
90
|
+
if (value) return value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function certificationRecords(record: Readonly<Record<string, unknown>>): readonly Readonly<Record<string, unknown>>[] {
|
|
97
|
+
return [
|
|
98
|
+
record,
|
|
99
|
+
readRecord(record.schema),
|
|
100
|
+
readRecord(record.contract),
|
|
101
|
+
readRecord(record.publication),
|
|
102
|
+
readRecord(record.receipt),
|
|
103
|
+
readRecord(record.certification),
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function schemaStatus(records: readonly Readonly<Record<string, unknown>>[]): AgentHarnessInteractiveRuntimeCertification['schemaStatus'] {
|
|
108
|
+
const explicit = firstString(records, ['schemaStatus', 'receiptSchemaStatus', 'certificationStatus']).toLowerCase().replace(/[_\s]+/g, '-');
|
|
109
|
+
if (['certified', 'valid', 'verified', 'schema-certified'].includes(explicit)) return 'certified';
|
|
110
|
+
return firstString(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion'])
|
|
111
|
+
&& firstString(records, ['publicationGuarantee', 'hostPublicationGuarantee', 'runtimePublicationGuarantee'])
|
|
112
|
+
&& firstString(records, ['publisher', 'publisherId', 'daemonId', 'hostId', 'methodId', 'sourceTool'])
|
|
113
|
+
? 'certified'
|
|
114
|
+
: 'legacy';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function routeValue(value: unknown): string {
|
|
118
|
+
const direct = readString(value);
|
|
119
|
+
if (direct) return direct;
|
|
120
|
+
const record = readRecord(value);
|
|
121
|
+
return readString(record.modelRoute) || readString(record.route) || readString(record.command) || readString(record.action) || readString(record.href) || readString(record.url);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function routeFrom(record: Readonly<Record<string, unknown>>, aliases: readonly string[]): string {
|
|
125
|
+
const containers = [record, readRecord(record.routes), readRecord(record.modelRoutes), readRecord(record.actions), readRecord(record.commands), readRecord(record.links)];
|
|
126
|
+
for (const alias of aliases) {
|
|
127
|
+
for (const container of containers) {
|
|
128
|
+
const route = routeValue(container[alias]) || routeValue(container[`${alias}Route`]) || routeValue(container[`${alias}Command`]) || routeValue(container[`${alias}Action`]);
|
|
129
|
+
if (route) return route;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function runtimeKind(record: Readonly<Record<string, unknown>>, fallback: AgentHarnessInteractiveRuntimeKind): AgentHarnessInteractiveRuntimeKind {
|
|
136
|
+
const raw = `${readString(record.kind) || readString(record.recordKind) || readString(record.type) || readString(record.capability)}`.toLowerCase();
|
|
137
|
+
if (/pty|terminal-session|interactive-session/.test(raw)) return 'pty-session';
|
|
138
|
+
if (/sudo|credential|privilege/.test(raw)) return 'sudo-mediation';
|
|
139
|
+
if (/browser|desktop|screenshot|screen|computer|control|receipt/.test(raw)) return 'browser-desktop-receipt';
|
|
140
|
+
if (/process|output|chunk|terminal/.test(raw)) return 'process-output';
|
|
141
|
+
return fallback;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeStatus(value: unknown): AgentHarnessInteractiveRuntimeStatus {
|
|
145
|
+
const raw = readString(value).toLowerCase().replace(/_/g, '-');
|
|
146
|
+
if (['ready', 'available', 'published', 'certified', 'ok', 'healthy'].includes(raw)) return 'ready';
|
|
147
|
+
if (['running', 'active', 'in-progress'].includes(raw)) return 'running';
|
|
148
|
+
if (['completed', 'complete', 'succeeded', 'success', 'done'].includes(raw)) return 'completed';
|
|
149
|
+
if (['blocked', 'denied', 'permission-denied'].includes(raw)) return 'blocked';
|
|
150
|
+
if (['failed', 'failure', 'error', 'errored'].includes(raw)) return 'failed';
|
|
151
|
+
if (['attention', 'review', 'stale'].includes(raw)) return 'attention';
|
|
152
|
+
return raw ? 'unknown' : 'unknown';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function readId(record: Readonly<Record<string, unknown>>, kind: AgentHarnessInteractiveRuntimeKind): string {
|
|
156
|
+
return readString(record.id)
|
|
157
|
+
|| readString(record.recordId)
|
|
158
|
+
|| readString(record.processId)
|
|
159
|
+
|| readString(record.sessionId)
|
|
160
|
+
|| readString(record.receiptId)
|
|
161
|
+
|| readString(record.contractId)
|
|
162
|
+
|| `${kind}:${readString(record.sourceId) || readString(record.action) || 'published'}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function routesFor(kind: AgentHarnessInteractiveRuntimeKind, record: Readonly<Record<string, unknown>>): readonly AgentHarnessInteractiveRuntimeRoute[] {
|
|
166
|
+
const routeSpecs: readonly [string, string, readonly string[], AgentHarnessInteractiveRuntimeRoute['effect']][] = [
|
|
167
|
+
['inspect', 'Inspect record', ['inspect', 'status', 'show'], 'read-only'],
|
|
168
|
+
['log', 'Read output', ['log', 'output', 'chunks', 'tail'], 'read-only'],
|
|
169
|
+
['write', 'Write input', ['write', 'stdin', 'input', 'sendInput'], 'confirmed'],
|
|
170
|
+
['resize', 'Resize session', ['resize'], 'confirmed'],
|
|
171
|
+
['close', 'Close session', ['close', 'stop', 'kill', 'cancel'], 'confirmed'],
|
|
172
|
+
['credential', 'Mediated credential prompt', ['credential', 'sudo', 'privilege', 'prompt'], 'confirmed-admin'],
|
|
173
|
+
['execute', kind === 'browser-desktop-receipt' ? 'Execute trusted control' : 'Execute route', ['execute', 'command', 'control', 'run'], 'confirmed'],
|
|
174
|
+
['receipt', 'Inspect receipt', ['receipt', 'receipts', 'history'], 'read-only'],
|
|
175
|
+
];
|
|
176
|
+
return routeSpecs
|
|
177
|
+
.map(([id, label, aliases, effect]) => {
|
|
178
|
+
const modelRoute = routeFrom(record, aliases);
|
|
179
|
+
return modelRoute ? { id, label, modelRoute: redactedPreview(modelRoute, 220), effect } : null;
|
|
180
|
+
})
|
|
181
|
+
.filter((route): route is AgentHarnessInteractiveRuntimeRoute => route !== null);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function outputChunks(record: Readonly<Record<string, unknown>>): readonly Record<string, unknown>[] {
|
|
185
|
+
const output = readRecord(record.output);
|
|
186
|
+
const rawChunks = Array.isArray(record.outputChunks) ? record.outputChunks
|
|
187
|
+
: Array.isArray(record.chunks) ? record.chunks
|
|
188
|
+
: Array.isArray(output.chunks) ? output.chunks
|
|
189
|
+
: [];
|
|
190
|
+
const chunks = rawChunks.flatMap((entry, index): Record<string, unknown>[] => {
|
|
191
|
+
const chunk = readRecord(entry);
|
|
192
|
+
const text = redactedPreview(chunk.text ?? chunk.preview ?? chunk.data ?? chunk.content, 600);
|
|
193
|
+
return text ? [{
|
|
194
|
+
chunkId: readString(chunk.chunkId) || readString(chunk.id) || `${index + 1}`,
|
|
195
|
+
stream: readString(chunk.stream) || readString(chunk.channel) || 'stdout',
|
|
196
|
+
text,
|
|
197
|
+
chars: readNumber(chunk.chars) ?? text.length,
|
|
198
|
+
bytes: readNumber(chunk.bytes) ?? Buffer.byteLength(text),
|
|
199
|
+
truncated: readBoolean(chunk.truncated) ?? false,
|
|
200
|
+
createdAt: readTimestamp(chunk.createdAt ?? chunk.timestamp),
|
|
201
|
+
}] : [];
|
|
202
|
+
});
|
|
203
|
+
if (chunks.length > 0) return chunks.slice(0, 5);
|
|
204
|
+
return ['stdoutTail', 'stderrTail', 'stdout', 'stderr'].flatMap((key) => {
|
|
205
|
+
const text = redactedPreview(record[key] ?? output[key], 600);
|
|
206
|
+
return text ? [{ chunkId: key, stream: key.includes('stderr') ? 'stderr' : 'stdout', text, chars: text.length, bytes: Buffer.byteLength(text), truncated: false }] : [];
|
|
207
|
+
}).slice(0, 5);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function certification(input: {
|
|
211
|
+
readonly record: Readonly<Record<string, unknown>>;
|
|
212
|
+
readonly source: string;
|
|
213
|
+
readonly kind: AgentHarnessInteractiveRuntimeKind;
|
|
214
|
+
readonly id: string;
|
|
215
|
+
readonly routes: readonly AgentHarnessInteractiveRuntimeRoute[];
|
|
216
|
+
readonly chunks: readonly Record<string, unknown>[];
|
|
217
|
+
}): AgentHarnessInteractiveRuntimeCertification {
|
|
218
|
+
const records = certificationRecords(input.record);
|
|
219
|
+
const status = schemaStatus(records);
|
|
220
|
+
const publicationGuarantee = firstString(records, ['publicationGuarantee', 'hostPublicationGuarantee', 'runtimePublicationGuarantee']);
|
|
221
|
+
const publisher = firstString(records, ['publisher', 'publisherId', 'daemonId', 'hostId']);
|
|
222
|
+
const provenance = [...new Set([
|
|
223
|
+
...stringArray(input.record.provenance),
|
|
224
|
+
input.source ? `source ${input.source}` : '',
|
|
225
|
+
firstString(records, ['methodId']) ? `method ${firstString(records, ['methodId'])}` : '',
|
|
226
|
+
firstString(records, ['sourceTool']) ? `sourceTool ${firstString(records, ['sourceTool'])}` : '',
|
|
227
|
+
].map((entry) => redactedPreview(entry, 180)).filter(Boolean))].slice(0, 8);
|
|
228
|
+
const receiptId = firstString(records, ['receiptId', 'operationReceiptId', 'commandReceiptId', 'sessionReceiptId']);
|
|
229
|
+
const hasRoute = (id: string) => input.routes.some((route) => route.id === id);
|
|
230
|
+
const rawSecretReturned = readBoolean(input.record.rawSecretReturned) === true || readBoolean(input.record.rawValueReturned) === true;
|
|
231
|
+
const missingSignals = [
|
|
232
|
+
...(status === 'certified' ? [] : [`Certified ${input.kind} schema is not published.`]),
|
|
233
|
+
...(input.id ? [] : [`Durable ${input.kind} id is not published.`]),
|
|
234
|
+
...(publicationGuarantee ? [] : [`${input.kind} publication guarantee is not published.`]),
|
|
235
|
+
...(publisher ? [] : [`${input.kind} publisher is not published.`]),
|
|
236
|
+
...(provenance.length > 0 ? [] : [`${input.kind} provenance is not published.`]),
|
|
237
|
+
...(input.kind === 'process-output' && input.chunks.length === 0 && !hasRoute('log') ? ['Live process output chunks or log route are not published.'] : []),
|
|
238
|
+
...(input.kind === 'pty-session' && (!hasRoute('write') || (!hasRoute('log') && input.chunks.length === 0)) ? ['PTY input and output routes are not both published.'] : []),
|
|
239
|
+
...(input.kind === 'sudo-mediation' && (!hasRoute('credential') || rawSecretReturned) ? ['Sudo credential mediation route is missing or raw secret exposure was reported.'] : []),
|
|
240
|
+
...(input.kind === 'browser-desktop-receipt' && (!receiptId || !hasRoute('execute') || !hasRoute('receipt')) ? ['Browser/desktop command receipt, execute route, or receipt route is not published.'] : []),
|
|
241
|
+
];
|
|
242
|
+
return {
|
|
243
|
+
schemaStatus: status,
|
|
244
|
+
...(firstString(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion']) ? { schemaVersion: redactedPreview(firstString(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion']), 80) } : {}),
|
|
245
|
+
...(publicationGuarantee ? { publicationGuarantee: redactedPreview(publicationGuarantee, 220) } : {}),
|
|
246
|
+
...(publisher ? { publisher: redactedPreview(publisher, 80) } : {}),
|
|
247
|
+
...(provenance.length > 0 ? { provenance } : {}),
|
|
248
|
+
...(receiptId ? { receiptId: redactedPreview(receiptId, 96) } : {}),
|
|
249
|
+
...(hasRoute('receipt') ? { receiptRoute: input.routes.find((route) => route.id === 'receipt')?.modelRoute } : {}),
|
|
250
|
+
missingSignals,
|
|
251
|
+
policy: 'Interactive runtime records certify release readiness only when the SDK or daemon publishes schema, durable id, publication guarantee, publisher/provenance, bounded output or receipt evidence, exact confirmed control routes, and no raw secret material.',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function normalizeRecord(value: unknown, source: string, fallbackKind: AgentHarnessInteractiveRuntimeKind): AgentHarnessInteractiveRuntimeRecord | null {
|
|
256
|
+
const record = readRecord(value);
|
|
257
|
+
if (Object.keys(record).length === 0) return null;
|
|
258
|
+
const kind = runtimeKind(record, fallbackKind);
|
|
259
|
+
const id = readId(record, kind);
|
|
260
|
+
if (!id) return null;
|
|
261
|
+
const routes = routesFor(kind, record);
|
|
262
|
+
const chunks = outputChunks(record);
|
|
263
|
+
const command = redactedPreview(record.command ?? record.cmd, 180);
|
|
264
|
+
const receiptId = readString(record.receiptId) || readString(record.operationReceiptId) || readString(record.commandReceiptId);
|
|
265
|
+
return {
|
|
266
|
+
id,
|
|
267
|
+
kind,
|
|
268
|
+
status: normalizeStatus(record.status ?? record.state ?? record.outcome),
|
|
269
|
+
source,
|
|
270
|
+
label: redactedPreview(record.label ?? record.title ?? id, 96),
|
|
271
|
+
summary: redactedPreview(record.summary ?? record.description ?? record.detail ?? command ?? id, 240),
|
|
272
|
+
...(readString(record.processId) ? { processId: readString(record.processId) } : {}),
|
|
273
|
+
...(readString(record.sessionId) ? { sessionId: readString(record.sessionId) } : {}),
|
|
274
|
+
...(readNumber(record.pid) !== undefined ? { pid: readNumber(record.pid) } : {}),
|
|
275
|
+
...(command ? { command } : {}),
|
|
276
|
+
...(readString(record.surface) ? { surface: redactedPreview(record.surface, 80) } : {}),
|
|
277
|
+
...(readString(record.action) ? { action: redactedPreview(record.action, 80) } : {}),
|
|
278
|
+
...(receiptId ? { receiptId: redactedPreview(receiptId, 96) } : {}),
|
|
279
|
+
...(chunks.length > 0 ? { outputChunks: chunks } : {}),
|
|
280
|
+
routes,
|
|
281
|
+
...(readTimestamp(record.updatedAt ?? record.createdAt ?? record.timestamp) ? { updatedAt: readTimestamp(record.updatedAt ?? record.createdAt ?? record.timestamp) } : {}),
|
|
282
|
+
certification: certification({ record, source, kind, id, routes, chunks }),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function readSnapshot(source: unknown): unknown {
|
|
287
|
+
if (typeof source === 'function') {
|
|
288
|
+
const value = (source as () => unknown)();
|
|
289
|
+
return value && typeof (value as Promise<unknown>).then === 'function' ? undefined : value;
|
|
290
|
+
}
|
|
291
|
+
const record = readRecord(source);
|
|
292
|
+
for (const methodName of ['getSnapshot', 'snapshot', 'list', 'records']) {
|
|
293
|
+
const method = record[methodName];
|
|
294
|
+
if (typeof method !== 'function') continue;
|
|
295
|
+
const value = (method as () => unknown)();
|
|
296
|
+
return value && typeof (value as Promise<unknown>).then === 'function' ? undefined : value;
|
|
297
|
+
}
|
|
298
|
+
return source;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function recordsFromSnapshot(snapshot: unknown, fallbackKind: AgentHarnessInteractiveRuntimeKind): readonly { readonly kind: AgentHarnessInteractiveRuntimeKind; readonly value: unknown }[] {
|
|
302
|
+
if (Array.isArray(snapshot)) return snapshot.map((value) => ({ kind: fallbackKind, value }));
|
|
303
|
+
const record = readRecord(snapshot);
|
|
304
|
+
const keyed: Array<readonly [string, AgentHarnessInteractiveRuntimeKind]> = [
|
|
305
|
+
['processes', 'process-output'],
|
|
306
|
+
['liveProcesses', 'process-output'],
|
|
307
|
+
['outputChunks', 'process-output'],
|
|
308
|
+
['ptySessions', 'pty-session'],
|
|
309
|
+
['sessions', 'pty-session'],
|
|
310
|
+
['sudoMediation', 'sudo-mediation'],
|
|
311
|
+
['credentials', 'sudo-mediation'],
|
|
312
|
+
['browserDesktopReceipts', 'browser-desktop-receipt'],
|
|
313
|
+
['controlReceipts', 'browser-desktop-receipt'],
|
|
314
|
+
['receipts', 'browser-desktop-receipt'],
|
|
315
|
+
['records', fallbackKind],
|
|
316
|
+
['items', fallbackKind],
|
|
317
|
+
];
|
|
318
|
+
const values: { kind: AgentHarnessInteractiveRuntimeKind; value: unknown }[] = [];
|
|
319
|
+
for (const [key, kind] of keyed) {
|
|
320
|
+
const candidate = record[key];
|
|
321
|
+
if (Array.isArray(candidate)) values.push(...candidate.map((value) => ({ kind, value })));
|
|
322
|
+
else {
|
|
323
|
+
const map = readRecord(candidate);
|
|
324
|
+
if (Object.keys(map).length > 0) values.push(...Object.entries(map).map(([id, value]) => ({ kind, value: { ...readRecord(value), id: readString(readRecord(value).id) || id } })));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return values.length > 0 ? values : Object.keys(record).length > 0 ? [{ kind: fallbackKind, value: record }] : [];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function runtimeSources(context: CommandContext): readonly { readonly path: string; readonly source: unknown; readonly kind: AgentHarnessInteractiveRuntimeKind }[] {
|
|
331
|
+
const platform = context.platform as unknown as Record<string, unknown>;
|
|
332
|
+
const clients = readRecord(context.clients);
|
|
333
|
+
const extensions = context.extensions as unknown as Record<string, unknown>;
|
|
334
|
+
const readModels = readRecord(platform.readModels);
|
|
335
|
+
const execution = readRecord(readModels.execution);
|
|
336
|
+
const computer = readRecord(readModels.computer);
|
|
337
|
+
const browser = readRecord(readModels.browser);
|
|
338
|
+
const operatorSdk = readRecord(clients.operatorSdk);
|
|
339
|
+
const daemonRuntime = readRecord(extensions.daemonRuntime);
|
|
340
|
+
return [
|
|
341
|
+
{ path: 'context.platform.readModels.execution.interactiveRuntime', source: execution.interactiveRuntime, kind: 'process-output' },
|
|
342
|
+
{ path: 'context.platform.readModels.execution.processRuntime', source: execution.processRuntime, kind: 'process-output' },
|
|
343
|
+
{ path: 'context.platform.readModels.execution.ptySessions', source: execution.ptySessions, kind: 'pty-session' },
|
|
344
|
+
{ path: 'context.platform.readModels.execution.sudoMediation', source: execution.sudoMediation, kind: 'sudo-mediation' },
|
|
345
|
+
{ path: 'context.platform.readModels.computer.browserDesktopReceipts', source: computer.browserDesktopReceipts, kind: 'browser-desktop-receipt' },
|
|
346
|
+
{ path: 'context.platform.readModels.browser.controlReceipts', source: browser.controlReceipts, kind: 'browser-desktop-receipt' },
|
|
347
|
+
{ path: 'context.clients.operatorSdk.execution.interactiveRuntime', source: readRecord(operatorSdk.execution).interactiveRuntime, kind: 'process-output' },
|
|
348
|
+
{ path: 'context.extensions.daemonRuntime.interactiveRuntime', source: daemonRuntime.interactiveRuntime, kind: 'process-output' },
|
|
349
|
+
];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export function interactiveRuntimeRecords(context: CommandContext): readonly AgentHarnessInteractiveRuntimeRecord[] {
|
|
353
|
+
const records: AgentHarnessInteractiveRuntimeRecord[] = [];
|
|
354
|
+
for (const source of runtimeSources(context)) {
|
|
355
|
+
if (source.source === undefined || source.source === null) continue;
|
|
356
|
+
try {
|
|
357
|
+
const snapshot = readSnapshot(source.source);
|
|
358
|
+
records.push(...recordsFromSnapshot(snapshot, source.kind)
|
|
359
|
+
.map((entry) => normalizeRecord(entry.value, source.path, entry.kind))
|
|
360
|
+
.filter((entry): entry is AgentHarnessInteractiveRuntimeRecord => entry !== null));
|
|
361
|
+
} catch {
|
|
362
|
+
// Runtime read models are optional; a broken publisher should not hide local execution posture.
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return records.sort((left, right) => {
|
|
366
|
+
const delta = (right.updatedAt ? Date.parse(right.updatedAt) : 0) - (left.updatedAt ? Date.parse(left.updatedAt) : 0);
|
|
367
|
+
return delta || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function complete(record: AgentHarnessInteractiveRuntimeRecord): boolean {
|
|
372
|
+
return record.certification.schemaStatus === 'certified' && record.certification.missingSignals.length === 0;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function kindSummary(records: readonly AgentHarnessInteractiveRuntimeRecord[], kind: AgentHarnessInteractiveRuntimeKind): Record<string, unknown> {
|
|
376
|
+
const matches = records.filter((record) => record.kind === kind);
|
|
377
|
+
const certified = matches.filter(complete);
|
|
378
|
+
return {
|
|
379
|
+
status: certified.length > 0 ? 'certified' : matches.length > 0 ? 'attention' : 'not-published',
|
|
380
|
+
recordCount: matches.length,
|
|
381
|
+
certifiedRecordCount: certified.length,
|
|
382
|
+
...(matches[0] ? { latest: matches[0] } : {}),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function interactiveRuntimeCapabilitySummary(context: CommandContext): Record<string, unknown> {
|
|
387
|
+
const records = interactiveRuntimeRecords(context);
|
|
388
|
+
const certified = records.filter(complete);
|
|
389
|
+
return {
|
|
390
|
+
status: certified.length > 0 ? 'certified-live-runtime' : records.length > 0 ? 'attention' : 'not-published',
|
|
391
|
+
recordCount: records.length,
|
|
392
|
+
certifiedRecordCount: certified.length,
|
|
393
|
+
liveProcessOutput: kindSummary(records, 'process-output'),
|
|
394
|
+
ptySessions: kindSummary(records, 'pty-session'),
|
|
395
|
+
sudoMediation: kindSummary(records, 'sudo-mediation'),
|
|
396
|
+
browserDesktopControl: kindSummary(records, 'browser-desktop-receipt'),
|
|
397
|
+
latestRecords: records.slice(0, 6),
|
|
398
|
+
policy: 'Certified interactive runtime records are read-only evidence until the selected published route is invoked with its own confirmation boundary.',
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function interactiveRuntimeParityStatus(context: CommandContext): {
|
|
403
|
+
readonly stdinWriteContract: boolean;
|
|
404
|
+
readonly ptyContract: boolean;
|
|
405
|
+
readonly sudoMediationContract: boolean;
|
|
406
|
+
readonly browserDesktopControlContract: boolean;
|
|
407
|
+
readonly browserDesktopRoute: string | null;
|
|
408
|
+
readonly browserDesktopRecords: readonly AgentHarnessInteractiveRuntimeRecord[];
|
|
409
|
+
} {
|
|
410
|
+
const certified = interactiveRuntimeRecords(context).filter(complete);
|
|
411
|
+
const hasRoute = (record: AgentHarnessInteractiveRuntimeRecord, id: string) => record.routes.some((route) => route.id === id);
|
|
412
|
+
const browserDesktopRecords = certified.filter((record) => record.kind === 'browser-desktop-receipt');
|
|
413
|
+
return {
|
|
414
|
+
stdinWriteContract: certified.some((record) => (record.kind === 'process-output' || record.kind === 'pty-session') && hasRoute(record, 'write')),
|
|
415
|
+
ptyContract: certified.some((record) => record.kind === 'pty-session'),
|
|
416
|
+
sudoMediationContract: certified.some((record) => record.kind === 'sudo-mediation'),
|
|
417
|
+
browserDesktopControlContract: browserDesktopRecords.length > 0,
|
|
418
|
+
browserDesktopRoute: browserDesktopRecords[0]?.routes.find((route) => route.id === 'execute')?.modelRoute ?? browserDesktopRecords[0]?.routes.find((route) => route.id === 'inspect')?.modelRoute ?? null,
|
|
419
|
+
browserDesktopRecords,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
@@ -250,11 +250,11 @@ function resolveHarnessKeybinding(context: CommandContext, args: HarnessKeybindi
|
|
|
250
250
|
|
|
251
251
|
function keybindingModelRoute(action: KeyAction): string {
|
|
252
252
|
const route = keybindingOperationRoute(action);
|
|
253
|
-
if (route.preferredMode === 'run_keybinding') return `
|
|
254
|
-
if (route.preferredMode === 'open_ui_surface' && route.surfaceId) return `
|
|
255
|
-
if (route.preferredMode === 'open_ui_surface') return '
|
|
256
|
-
if (route.preferredMode === 'run_command' && route.command) return `
|
|
257
|
-
if (route.preferredMode === 'run_command') return '
|
|
253
|
+
if (route.preferredMode === 'run_keybinding') return `workspace action:"run_keybinding" actionId:"${action}"`;
|
|
254
|
+
if (route.preferredMode === 'open_ui_surface' && route.surfaceId) return `workspace action:"open" surfaceId:"${route.surfaceId}"`;
|
|
255
|
+
if (route.preferredMode === 'open_ui_surface') return 'workspace action:"open"';
|
|
256
|
+
if (route.preferredMode === 'run_command' && route.command) return `workspace action:"run_command" command:"${route.command}"`;
|
|
257
|
+
if (route.preferredMode === 'run_command') return 'workspace action:"run_command"';
|
|
258
258
|
return 'direct-user-interaction';
|
|
259
259
|
}
|
|
260
260
|
|
|
@@ -265,6 +265,10 @@ function keybindingModelAccess(action: KeyAction, operation: KeybindingOperation
|
|
|
265
265
|
set: `agent_harness mode:"set_keybinding" actionId:"${action}" combo:{...} confirm:true explicitUserRequest:"..."`,
|
|
266
266
|
reset: `agent_harness mode:"reset_keybinding" actionId:"${action}" confirm:true explicitUserRequest:"..."`,
|
|
267
267
|
preferred: keybindingModelRoute(action),
|
|
268
|
+
directInspect: `workspace action:"keybinding" actionId:"${action}"`,
|
|
269
|
+
directRun: operation.supported ? `workspace action:"run_keybinding" actionId:"${action}" confirm:true explicitUserRequest:"..."` : 'not exposed',
|
|
270
|
+
directSet: `workspace action:"set_keybinding" actionId:"${action}" combo:{...} confirm:true explicitUserRequest:"..."`,
|
|
271
|
+
directReset: `workspace action:"reset_keybinding" actionId:"${action}" confirm:true explicitUserRequest:"..."`,
|
|
268
272
|
};
|
|
269
273
|
}
|
|
270
274
|
|
|
@@ -435,12 +439,12 @@ export function totalHarnessShortcuts(context: CommandContext): number {
|
|
|
435
439
|
}
|
|
436
440
|
|
|
437
441
|
function fixedShortcutModelRoute(shortcut: Record<string, string>): string {
|
|
438
|
-
if (shortcut.key.includes('/shortcuts')) return '
|
|
439
|
-
if (shortcut.key.includes('/keybindings')) return '
|
|
440
|
-
if (shortcut.key.includes('?') || shortcut.key.includes('F1')) return '
|
|
441
|
-
if (shortcut.key.includes('F2')) return '
|
|
442
|
+
if (shortcut.key.includes('/shortcuts')) return 'workspace action:"shortcuts"';
|
|
443
|
+
if (shortcut.key.includes('/keybindings')) return 'workspace action:"keybindings"';
|
|
444
|
+
if (shortcut.key.includes('?') || shortcut.key.includes('F1')) return 'workspace action:"open" surfaceId:"help-overlay"';
|
|
445
|
+
if (shortcut.key.includes('F2')) return 'workspace action:"open" surfaceId:"process-monitor"';
|
|
442
446
|
if (shortcut.key.includes('Esc')) return 'direct-user-interaction';
|
|
443
|
-
if (shortcut.key.includes('Tab')) return '
|
|
447
|
+
if (shortcut.key.includes('Tab')) return 'workspace action:"commands"';
|
|
444
448
|
if (shortcut.key.includes('Enter')) return 'main conversation';
|
|
445
449
|
return 'direct-user-interaction';
|
|
446
450
|
}
|
|
@@ -452,8 +456,8 @@ function describeFixedShortcut(shortcut: Record<string, string>): Record<string,
|
|
|
452
456
|
userEditable: false,
|
|
453
457
|
modelRoute: fixedShortcutModelRoute(shortcut),
|
|
454
458
|
modelAccess: {
|
|
455
|
-
inspectShortcuts: '
|
|
456
|
-
inspectKeybindings: '
|
|
459
|
+
inspectShortcuts: 'workspace action:"shortcuts"',
|
|
460
|
+
inspectKeybindings: 'workspace action:"keybindings"',
|
|
457
461
|
preferred: fixedShortcutModelRoute(shortcut),
|
|
458
462
|
},
|
|
459
463
|
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { AgentWorkspaceLocalLibraryItem } from '../input/agent-workspace-types.ts';
|
|
2
|
+
import type { LearningCandidate, LearningCandidateStatus, LearningScores, LocalLearningCandidateDomain } from './agent-harness-learning-curator-types.ts';
|
|
3
|
+
export function clampScore(value: number): number {
|
|
4
|
+
return Math.max(0, Math.min(100, Math.round(value)));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isReviewed(item: AgentWorkspaceLocalLibraryItem): boolean {
|
|
8
|
+
return item.reviewState === 'reviewed';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function missingRequirementCount(item: AgentWorkspaceLocalLibraryItem): number {
|
|
12
|
+
return item.missingRequirementCount ?? 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function itemUsefulness(item: AgentWorkspaceLocalLibraryItem): number {
|
|
16
|
+
const triggerSignal = Math.min(20, item.triggers.length * 5);
|
|
17
|
+
const tagSignal = Math.min(10, item.tags.length * 2);
|
|
18
|
+
const usageSignal = (item.enabled ? 15 : 0) + (item.active ? 15 : 0) + Math.min(20, (item.startCount ?? 0) * 4);
|
|
19
|
+
const confidenceSignal = item.confidence === undefined ? 10 : Math.max(0, Math.min(20, item.confidence / 5));
|
|
20
|
+
return clampScore(35 + triggerSignal + tagSignal + usageSignal + confidenceSignal);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function itemFreshness(item: AgentWorkspaceLocalLibraryItem): number {
|
|
24
|
+
if (item.reviewState === 'stale') return 15;
|
|
25
|
+
if (item.reviewState === 'fresh') return 55;
|
|
26
|
+
return missingRequirementCount(item) > 0 ? 70 : 92;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function itemSourceQuality(item: AgentWorkspaceLocalLibraryItem): number {
|
|
30
|
+
const source = item.source.toLowerCase();
|
|
31
|
+
const base = source.includes('workspace') || source.includes('import') ? 72 : source.includes('agent') ? 68 : 60;
|
|
32
|
+
return clampScore(base + (isReviewed(item) ? 12 : 0) + Math.min(12, item.tags.length * 2));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function itemRisk(item: AgentWorkspaceLocalLibraryItem): number {
|
|
36
|
+
const reviewRisk = item.reviewState === 'stale' ? 60 : item.reviewState === 'fresh' ? 35 : 10;
|
|
37
|
+
const injectionRisk = item.enabled || item.active ? 20 : 0;
|
|
38
|
+
const setupRisk = Math.min(30, missingRequirementCount(item) * 10);
|
|
39
|
+
const confidenceRisk = item.confidence === undefined ? 0 : Math.max(0, 70 - item.confidence);
|
|
40
|
+
return clampScore(reviewRisk + injectionRisk + setupRisk + confidenceRisk);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function scoresForItem(item: AgentWorkspaceLocalLibraryItem): LearningScores {
|
|
44
|
+
return {
|
|
45
|
+
usefulness: itemUsefulness(item),
|
|
46
|
+
freshness: itemFreshness(item),
|
|
47
|
+
sourceQuality: itemSourceQuality(item),
|
|
48
|
+
risk: itemRisk(item),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function routeDomain(domain: LocalLearningCandidateDomain): string {
|
|
53
|
+
return domain;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function localRegistryRoute(domain: LocalLearningCandidateDomain, action: string, id: string): string {
|
|
57
|
+
return `agent_local_registry domain:"${routeDomain(domain)}" action:"${action}" id:"${id}"`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function localRegistryModelRoute(domain: LocalLearningCandidateDomain): string {
|
|
61
|
+
return `agent_local_registry domain:"${routeDomain(domain)}" action:"get"`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function routeValue(value: string): string {
|
|
65
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function routeList(values: readonly string[]): string {
|
|
69
|
+
return `[${values.map((value) => `"${routeValue(value)}"`).join(',')}]`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function candidateBase(
|
|
73
|
+
domain: LocalLearningCandidateDomain,
|
|
74
|
+
item: AgentWorkspaceLocalLibraryItem,
|
|
75
|
+
status: LearningCandidateStatus,
|
|
76
|
+
priority: number,
|
|
77
|
+
reason: string,
|
|
78
|
+
next: string,
|
|
79
|
+
): LearningCandidate {
|
|
80
|
+
const missingRequirements = item.missingRequirements ?? [];
|
|
81
|
+
return {
|
|
82
|
+
id: `${domain}:${item.id}:${status}`,
|
|
83
|
+
label: item.name,
|
|
84
|
+
domain,
|
|
85
|
+
recordId: item.id,
|
|
86
|
+
status,
|
|
87
|
+
priority: clampScore(priority),
|
|
88
|
+
reason,
|
|
89
|
+
next,
|
|
90
|
+
scores: scoresForItem(item),
|
|
91
|
+
reviewState: item.reviewState,
|
|
92
|
+
...(item.enabled === undefined ? {} : { enabled: item.enabled }),
|
|
93
|
+
...(item.active === undefined ? {} : { active: item.active }),
|
|
94
|
+
...(item.confidence === undefined ? {} : { confidence: item.confidence }),
|
|
95
|
+
...(missingRequirements.length === 0 ? {} : { missingRequirements }),
|
|
96
|
+
inspectRoute: localRegistryRoute(domain, 'get', item.id),
|
|
97
|
+
modelRoute: localRegistryModelRoute(domain),
|
|
98
|
+
reviewRoute: localRegistryRoute(domain, 'review', item.id),
|
|
99
|
+
staleRoute: `${localRegistryRoute(domain, 'stale', item.id)} reason:"..."`,
|
|
100
|
+
deleteRoute: `${localRegistryRoute(domain, 'delete', item.id)} confirm:true explicitUserRequest:"..."`,
|
|
101
|
+
};
|
|
102
|
+
}
|