@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
|
@@ -104,8 +104,8 @@ function itemSearchText(item: Record<string, unknown>): string {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
function releaseReadinessModelRoute(item?: Record<string, unknown>): string {
|
|
107
|
-
if (item) return '
|
|
108
|
-
return '
|
|
107
|
+
if (item) return 'audit action:"item"';
|
|
108
|
+
return 'audit action:"readiness" or action:"item"';
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
function releaseReadinessPolicy(): Record<string, unknown> {
|
|
@@ -153,8 +153,9 @@ function summarizeItem(item: Record<string, unknown>, options: { readonly includ
|
|
|
153
153
|
...(options.includeQuality ? {
|
|
154
154
|
policy: releaseReadinessPolicy(),
|
|
155
155
|
modelAccess: {
|
|
156
|
-
listItems: '
|
|
157
|
-
inspectItem: `
|
|
156
|
+
listItems: 'audit action:"readiness"',
|
|
157
|
+
inspectItem: `audit action:"item" itemId:"${readString(item.id)}"`,
|
|
158
|
+
lowerLevelItem: `agent_harness mode:"release_readiness_item" itemId:"${readString(item.id)}" includeParameters:true`,
|
|
158
159
|
},
|
|
159
160
|
} : {}),
|
|
160
161
|
};
|
|
@@ -230,8 +231,9 @@ export function releaseReadinessSummary(args: ReleaseReadinessArgs): Record<stri
|
|
|
230
231
|
policy: loaded.root.policy,
|
|
231
232
|
operatorAuditPolicy: releaseReadinessPolicy(),
|
|
232
233
|
modelAccess: {
|
|
233
|
-
listItems: '
|
|
234
|
-
inspectItem: '
|
|
234
|
+
listItems: 'audit action:"readiness"',
|
|
235
|
+
inspectItem: 'audit action:"item" with itemId, target, or query',
|
|
236
|
+
lowerLevelItem: 'agent_harness mode:"release_readiness_item" itemId:"..." includeParameters:true',
|
|
235
237
|
},
|
|
236
238
|
totals: {
|
|
237
239
|
items: items.length,
|
|
@@ -245,7 +247,7 @@ export function releaseReadinessSummary(args: ReleaseReadinessArgs): Record<stri
|
|
|
245
247
|
},
|
|
246
248
|
sources: includeQuality ? sources : sources.map(summarizeSource),
|
|
247
249
|
items: filtered.slice(0, limit).map((item) => summarizeItem(item, { includeQuality })),
|
|
248
|
-
itemLookup: 'Use
|
|
250
|
+
itemLookup: 'Use audit action:"item" with itemId, target, or query to inspect one readiness item; lower-level agent_harness mode:"release_readiness_item" remains available.',
|
|
249
251
|
};
|
|
250
252
|
}
|
|
251
253
|
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
3
|
+
|
|
4
|
+
export interface RemoteCaptureOutcomeRecord {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly runnerId: string;
|
|
7
|
+
readonly status: string;
|
|
8
|
+
readonly kind: string;
|
|
9
|
+
readonly task: string | null;
|
|
10
|
+
readonly summary: string | null;
|
|
11
|
+
readonly captureId: string | null;
|
|
12
|
+
readonly exportId: string | null;
|
|
13
|
+
readonly artifactId: string | null;
|
|
14
|
+
readonly sourcePath: string;
|
|
15
|
+
readonly source: 'daemon-read-model' | 'sdk-read-model';
|
|
16
|
+
readonly redaction: string;
|
|
17
|
+
readonly startedAt: string | null;
|
|
18
|
+
readonly completedAt: string | null;
|
|
19
|
+
readonly modelRoute: string;
|
|
20
|
+
readonly certification: RemoteRuntimeRecordCertification;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RemoteWorkspaceEvidenceRecord {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly runnerId: string;
|
|
26
|
+
readonly status: string;
|
|
27
|
+
readonly workspaceId: string | null;
|
|
28
|
+
readonly isolationKind: string;
|
|
29
|
+
readonly label: string | null;
|
|
30
|
+
readonly worktreeRef: string | null;
|
|
31
|
+
readonly branch: string | null;
|
|
32
|
+
readonly sourcePath: string;
|
|
33
|
+
readonly source: 'daemon-read-model' | 'sdk-read-model';
|
|
34
|
+
readonly redaction: string;
|
|
35
|
+
readonly modelRoute: string;
|
|
36
|
+
readonly certification: RemoteRuntimeRecordCertification;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface RemoteReadModelSnapshot {
|
|
40
|
+
readonly outcomes: readonly RemoteCaptureOutcomeRecord[];
|
|
41
|
+
readonly workspaces: readonly RemoteWorkspaceEvidenceRecord[];
|
|
42
|
+
readonly sourceCounts: Readonly<Record<string, number>>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface RemoteRuntimeRecordCertification {
|
|
46
|
+
readonly schemaStatus: 'certified' | 'legacy';
|
|
47
|
+
readonly schemaVersion?: string;
|
|
48
|
+
readonly publicationGuarantee?: string;
|
|
49
|
+
readonly publisher?: string;
|
|
50
|
+
readonly provenance?: readonly string[];
|
|
51
|
+
readonly receiptId?: string;
|
|
52
|
+
readonly cursor?: string;
|
|
53
|
+
readonly missingSignals: readonly string[];
|
|
54
|
+
readonly policy: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface SourceCandidate {
|
|
58
|
+
readonly path: string;
|
|
59
|
+
readonly source: unknown;
|
|
60
|
+
readonly kind: 'daemon-read-model' | 'sdk-read-model';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface CollectedRecord {
|
|
64
|
+
readonly path: string;
|
|
65
|
+
readonly kind: 'daemon-read-model' | 'sdk-read-model';
|
|
66
|
+
readonly record: Record<string, unknown>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const OUTCOME_WRAPPER_KEYS = [
|
|
70
|
+
'records',
|
|
71
|
+
'items',
|
|
72
|
+
'outcomes',
|
|
73
|
+
'captureOutcomes',
|
|
74
|
+
'captures',
|
|
75
|
+
'captureRecords',
|
|
76
|
+
'exports',
|
|
77
|
+
'exportOutcomes',
|
|
78
|
+
'closeouts',
|
|
79
|
+
'closeoutOutcomes',
|
|
80
|
+
'artifacts',
|
|
81
|
+
] as const;
|
|
82
|
+
|
|
83
|
+
const WORKSPACE_WRAPPER_KEYS = [
|
|
84
|
+
'records',
|
|
85
|
+
'items',
|
|
86
|
+
'workspaces',
|
|
87
|
+
'workspaceRecords',
|
|
88
|
+
'workspaceIsolation',
|
|
89
|
+
'workspaceEvidence',
|
|
90
|
+
'worktrees',
|
|
91
|
+
'worktreeRecords',
|
|
92
|
+
] as const;
|
|
93
|
+
|
|
94
|
+
const SNAPSHOT_METHODS = [
|
|
95
|
+
'getSnapshot',
|
|
96
|
+
'snapshot',
|
|
97
|
+
'toJSON',
|
|
98
|
+
] as const;
|
|
99
|
+
|
|
100
|
+
const OUTCOME_METHODS = [
|
|
101
|
+
'listCaptureOutcomes',
|
|
102
|
+
'listOutcomes',
|
|
103
|
+
'listCaptures',
|
|
104
|
+
'listExports',
|
|
105
|
+
'listCloseouts',
|
|
106
|
+
'listArtifacts',
|
|
107
|
+
'list',
|
|
108
|
+
] as const;
|
|
109
|
+
|
|
110
|
+
const WORKSPACE_METHODS = [
|
|
111
|
+
'listWorkspaceEvidence',
|
|
112
|
+
'listWorkspaceIsolation',
|
|
113
|
+
'listWorkspaces',
|
|
114
|
+
'listWorktrees',
|
|
115
|
+
'list',
|
|
116
|
+
] as const;
|
|
117
|
+
|
|
118
|
+
function readObject(value: unknown): Record<string, unknown> {
|
|
119
|
+
return value && typeof value === 'object' ? value as Record<string, unknown> : {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function readString(value: unknown): string {
|
|
123
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function readNumber(value: unknown): number {
|
|
127
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function readMetadata(record: Record<string, unknown>): Record<string, unknown> {
|
|
131
|
+
return readObject(record.metadata);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function certificationRecords(record: Record<string, unknown>): readonly Record<string, unknown>[] {
|
|
135
|
+
return [
|
|
136
|
+
record,
|
|
137
|
+
readMetadata(record),
|
|
138
|
+
readObject(record.schema),
|
|
139
|
+
readObject(record.contract),
|
|
140
|
+
readObject(record.publication),
|
|
141
|
+
readObject(record.receipt),
|
|
142
|
+
readObject(record.certification),
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function nestedString(record: Record<string, unknown>, key: string): string {
|
|
147
|
+
return readString(record[key])
|
|
148
|
+
|| readString(readMetadata(record)[key])
|
|
149
|
+
|| readString(readObject(record.task)[key])
|
|
150
|
+
|| readString(readObject(record.evidence)[key])
|
|
151
|
+
|| readString(readObject(record.workspace)[key]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function firstAcross(records: readonly Record<string, unknown>[], keys: readonly string[]): string {
|
|
155
|
+
for (const record of records) {
|
|
156
|
+
for (const key of keys) {
|
|
157
|
+
const value = readString(record[key]);
|
|
158
|
+
if (value) return value;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function stringArray(value: unknown): readonly string[] {
|
|
165
|
+
if (Array.isArray(value)) return [...new Set(value.map((entry) => readString(entry)).filter(Boolean))].slice(0, 10);
|
|
166
|
+
const text = readString(value);
|
|
167
|
+
return text ? text.split(',').map((entry) => entry.trim()).filter(Boolean).slice(0, 10) : [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function redactText(value: string): string {
|
|
171
|
+
return value
|
|
172
|
+
.replace(/\b(bearer)\s+[a-z0-9._~+/=-]+/gi, '$1 <redacted>')
|
|
173
|
+
.replace(/\b(token|secret|password|api[_-]?key|authorization)\s*[:=]\s*[^,\s;/]+/gi, '$1=<redacted>');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function safePreview(value: string, limit: number): string {
|
|
177
|
+
return previewHarnessText(redactText(value), limit);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function safeNullablePreview(value: string, limit: number): string | null {
|
|
181
|
+
return value ? safePreview(value, limit) : null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function certificationSchemaStatus(records: readonly Record<string, unknown>[]): RemoteRuntimeRecordCertification['schemaStatus'] {
|
|
185
|
+
const explicit = firstAcross(records, ['schemaStatus', 'receiptSchemaStatus', 'certificationStatus']).toLowerCase().replace(/[_\s]+/g, '-');
|
|
186
|
+
if (['certified', 'valid', 'verified', 'schema-certified'].includes(explicit)) return 'certified';
|
|
187
|
+
return firstAcross(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion'])
|
|
188
|
+
&& firstAcross(records, ['publicationGuarantee', 'hostPublicationGuarantee', 'remoteRuntimePublicationGuarantee'])
|
|
189
|
+
&& firstAcross(records, ['publisher', 'publisherId', 'daemonId', 'hostId', 'methodId', 'sourceTool'])
|
|
190
|
+
? 'certified'
|
|
191
|
+
: 'legacy';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function remoteCertification(input: {
|
|
195
|
+
readonly record: Record<string, unknown>;
|
|
196
|
+
readonly sourcePath: string;
|
|
197
|
+
readonly recordKind: 'remote outcome' | 'remote workspace';
|
|
198
|
+
readonly durableId: string;
|
|
199
|
+
readonly runnerId: string;
|
|
200
|
+
readonly modelRoute: string;
|
|
201
|
+
readonly hasOutcomeEvidence?: boolean;
|
|
202
|
+
readonly hasWorkspaceEvidence?: boolean;
|
|
203
|
+
readonly status: string;
|
|
204
|
+
}): RemoteRuntimeRecordCertification {
|
|
205
|
+
const records = certificationRecords(input.record);
|
|
206
|
+
const schemaStatus = certificationSchemaStatus(records);
|
|
207
|
+
const schemaVersion = firstAcross(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion']);
|
|
208
|
+
const publicationGuarantee = firstAcross(records, ['publicationGuarantee', 'hostPublicationGuarantee', 'remoteRuntimePublicationGuarantee']);
|
|
209
|
+
const publisher = firstAcross(records, ['publisher', 'publisherId', 'daemonId', 'hostId']);
|
|
210
|
+
const receiptId = firstAcross(records, ['receiptId', 'operationReceiptId', 'captureReceiptId', 'exportReceiptId', 'closeoutReceiptId', 'workspaceReceiptId']);
|
|
211
|
+
const cursor = firstAcross(records, ['cursor', 'freshnessCursor', 'sequence', 'checkpoint']);
|
|
212
|
+
const provenance = [...new Set([
|
|
213
|
+
...stringArray(input.record.provenance),
|
|
214
|
+
input.sourcePath ? `source ${input.sourcePath}` : '',
|
|
215
|
+
firstAcross(records, ['methodId']) ? `method ${firstAcross(records, ['methodId'])}` : '',
|
|
216
|
+
firstAcross(records, ['sourceTool']) ? `sourceTool ${firstAcross(records, ['sourceTool'])}` : '',
|
|
217
|
+
].map((entry) => safePreview(entry, 180)).filter(Boolean))].slice(0, 8);
|
|
218
|
+
const missingSignals = [
|
|
219
|
+
...(schemaStatus === 'certified' ? [] : [`Certified ${input.recordKind} schema is not published.`]),
|
|
220
|
+
...(input.durableId ? [] : [`Durable ${input.recordKind} id is not published.`]),
|
|
221
|
+
...(input.runnerId ? [] : [`${input.recordKind} runner id is not published.`]),
|
|
222
|
+
...(publicationGuarantee ? [] : [`${input.recordKind} publication guarantee is not published.`]),
|
|
223
|
+
...(publisher ? [] : [`${input.recordKind} publisher is not published.`]),
|
|
224
|
+
...(provenance.length > 0 ? [] : [`${input.recordKind} provenance is not published.`]),
|
|
225
|
+
...(cursor ? [] : [`${input.recordKind} freshness cursor is not published.`]),
|
|
226
|
+
...(input.modelRoute ? [] : [`${input.recordKind} inspect route is not published.`]),
|
|
227
|
+
...(input.status ? [] : [`${input.recordKind} status is not published.`]),
|
|
228
|
+
...(input.hasOutcomeEvidence === false ? ['Remote capture/export/closeout/artifact evidence id is not published.'] : []),
|
|
229
|
+
...(input.hasWorkspaceEvidence === false ? ['Remote workspace/worktree isolation evidence is not published.'] : []),
|
|
230
|
+
];
|
|
231
|
+
return {
|
|
232
|
+
schemaStatus,
|
|
233
|
+
...(schemaVersion ? { schemaVersion: safePreview(schemaVersion, 80) } : {}),
|
|
234
|
+
...(publicationGuarantee ? { publicationGuarantee: safePreview(publicationGuarantee, 220) } : {}),
|
|
235
|
+
...(publisher ? { publisher: safePreview(publisher, 80) } : {}),
|
|
236
|
+
...(provenance.length > 0 ? { provenance } : {}),
|
|
237
|
+
...(receiptId ? { receiptId: safePreview(receiptId, 96) } : {}),
|
|
238
|
+
...(cursor ? { cursor: safePreview(cursor, 96) } : {}),
|
|
239
|
+
missingSignals,
|
|
240
|
+
policy: 'Remote runtime read models certify release readiness only when the SDK or daemon publishes schema, durable runner/evidence ids, publication guarantee, publisher/provenance, freshness cursor, exact inspect route, and redacted outcome or workspace isolation evidence.',
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function safePathRef(value: string): string | null {
|
|
245
|
+
const redacted = redactText(value);
|
|
246
|
+
if (!redacted) return null;
|
|
247
|
+
const parts = redacted.split(/[\\/]+/).filter(Boolean);
|
|
248
|
+
if (parts.length <= 2) return safePreview(redacted, 120);
|
|
249
|
+
return safePreview(`.../${parts.slice(-2).join('/')}`, 120);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function timestampString(value: unknown): string | null {
|
|
253
|
+
const raw = readString(value);
|
|
254
|
+
if (raw) return safePreview(raw, 80);
|
|
255
|
+
const numeric = readNumber(value);
|
|
256
|
+
if (!numeric) return null;
|
|
257
|
+
const date = new Date(numeric);
|
|
258
|
+
return Number.isNaN(date.getTime()) ? String(numeric) : date.toISOString();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function callMethod(source: Record<string, unknown>, method: string): unknown {
|
|
262
|
+
const fn = source[method];
|
|
263
|
+
if (typeof fn !== 'function') return undefined;
|
|
264
|
+
try {
|
|
265
|
+
return (fn as () => unknown).call(source);
|
|
266
|
+
} catch {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function collectFromSource(
|
|
272
|
+
source: unknown,
|
|
273
|
+
path: string,
|
|
274
|
+
kind: 'daemon-read-model' | 'sdk-read-model',
|
|
275
|
+
wrapperKeys: readonly string[],
|
|
276
|
+
methods: readonly string[],
|
|
277
|
+
visited = new WeakSet<object>(),
|
|
278
|
+
): readonly CollectedRecord[] {
|
|
279
|
+
if (!source) return [];
|
|
280
|
+
if (Array.isArray(source)) {
|
|
281
|
+
return source
|
|
282
|
+
.flatMap((entry, index) => collectFromSource(entry, `${path}[${index}]`, kind, wrapperKeys, methods, visited));
|
|
283
|
+
}
|
|
284
|
+
if (source instanceof Map) {
|
|
285
|
+
return Array.from(source.entries())
|
|
286
|
+
.flatMap(([key, value]) => collectFromSource(value, `${path}.${String(key)}`, kind, wrapperKeys, methods, visited));
|
|
287
|
+
}
|
|
288
|
+
if (typeof source !== 'object') return [];
|
|
289
|
+
if (visited.has(source)) return [];
|
|
290
|
+
visited.add(source);
|
|
291
|
+
|
|
292
|
+
const record = source as Record<string, unknown>;
|
|
293
|
+
const fromSnapshots = SNAPSHOT_METHODS.flatMap((method) => {
|
|
294
|
+
const snapshot = callMethod(record, method);
|
|
295
|
+
return snapshot === undefined ? [] : collectFromSource(snapshot, `${path}.${method}()`, kind, wrapperKeys, methods, visited);
|
|
296
|
+
});
|
|
297
|
+
const fromMethods = methods.flatMap((method) => {
|
|
298
|
+
const snapshot = callMethod(record, method);
|
|
299
|
+
return snapshot === undefined ? [] : collectFromSource(snapshot, `${path}.${method}()`, kind, wrapperKeys, methods, visited);
|
|
300
|
+
});
|
|
301
|
+
const fromWrappers = wrapperKeys.flatMap((key) => {
|
|
302
|
+
if (!(key in record)) return [];
|
|
303
|
+
return collectFromSource(record[key], `${path}.${key}`, kind, wrapperKeys, methods, visited);
|
|
304
|
+
});
|
|
305
|
+
if (fromSnapshots.length > 0 || fromMethods.length > 0 || fromWrappers.length > 0) {
|
|
306
|
+
return [...fromSnapshots, ...fromMethods, ...fromWrappers];
|
|
307
|
+
}
|
|
308
|
+
return [{ path, kind, record }];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function runnerId(record: Record<string, unknown>): string {
|
|
312
|
+
return nestedString(record, 'runnerId')
|
|
313
|
+
|| nestedString(record, 'agentId')
|
|
314
|
+
|| nestedString(record, 'linkedAgentId')
|
|
315
|
+
|| nestedString(record, 'remoteRunnerId')
|
|
316
|
+
|| nestedString(record, 'runner');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function routeForRunner(record: Record<string, unknown>, runner: string): string {
|
|
320
|
+
return nestedString(record, 'modelRoute')
|
|
321
|
+
|| nestedString(record, 'inspectRoute')
|
|
322
|
+
|| nestedString(record, 'reviewRoute')
|
|
323
|
+
|| (runner ? `agent_harness mode:"agent_orchestration_agent" agentId:"${runner}" includeParameters:true` : 'agent_harness mode:"agent_orchestration" includeParameters:true');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function outcomeKind(record: Record<string, unknown>): string {
|
|
327
|
+
const raw = nestedString(record, 'kind')
|
|
328
|
+
|| nestedString(record, 'type')
|
|
329
|
+
|| nestedString(record, 'purpose')
|
|
330
|
+
|| nestedString(record, 'operation');
|
|
331
|
+
const normalized = raw.toLowerCase();
|
|
332
|
+
if (normalized.includes('export')) return 'export';
|
|
333
|
+
if (normalized.includes('closeout')) return 'closeout';
|
|
334
|
+
if (normalized.includes('artifact')) return 'artifact';
|
|
335
|
+
if (normalized.includes('capture')) return 'capture';
|
|
336
|
+
return raw || 'remote-outcome';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function outcomeStatus(record: Record<string, unknown>): string {
|
|
340
|
+
return nestedString(record, 'status')
|
|
341
|
+
|| nestedString(record, 'outcome')
|
|
342
|
+
|| nestedString(record, 'result')
|
|
343
|
+
|| nestedString(record, 'state')
|
|
344
|
+
|| 'unknown';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function outcomeId(record: Record<string, unknown>, runner: string, kind: string, index: number): string {
|
|
348
|
+
return nestedString(record, 'id')
|
|
349
|
+
|| nestedString(record, 'outcomeId')
|
|
350
|
+
|| nestedString(record, 'captureId')
|
|
351
|
+
|| nestedString(record, 'exportId')
|
|
352
|
+
|| nestedString(record, 'artifactId')
|
|
353
|
+
|| `remote-outcome:${runner || 'unknown'}:${kind}:${index}`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function normalizeOutcome(entry: CollectedRecord, index: number): RemoteCaptureOutcomeRecord | null {
|
|
357
|
+
const runner = runnerId(entry.record);
|
|
358
|
+
if (!runner) return null;
|
|
359
|
+
const kind = outcomeKind(entry.record);
|
|
360
|
+
const task = nestedString(entry.record, 'task') || nestedString(entry.record, 'title') || nestedString(entry.record, 'request');
|
|
361
|
+
const summary = nestedString(entry.record, 'summary')
|
|
362
|
+
|| nestedString(entry.record, 'description')
|
|
363
|
+
|| nestedString(entry.record, 'logTail')
|
|
364
|
+
|| nestedString(entry.record, 'message');
|
|
365
|
+
const id = outcomeId(entry.record, runner, kind, index);
|
|
366
|
+
const status = outcomeStatus(entry.record);
|
|
367
|
+
const captureId = nestedString(entry.record, 'captureId') || null;
|
|
368
|
+
const exportId = nestedString(entry.record, 'exportId') || null;
|
|
369
|
+
const artifactId = nestedString(entry.record, 'artifactId') || nestedString(entry.record, 'sourceArtifactId') || null;
|
|
370
|
+
const modelRoute = routeForRunner(entry.record, runner);
|
|
371
|
+
return {
|
|
372
|
+
id,
|
|
373
|
+
runnerId: runner,
|
|
374
|
+
status,
|
|
375
|
+
kind,
|
|
376
|
+
task: safeNullablePreview(task, 140),
|
|
377
|
+
summary: safeNullablePreview(summary, 220),
|
|
378
|
+
captureId,
|
|
379
|
+
exportId,
|
|
380
|
+
artifactId,
|
|
381
|
+
sourcePath: entry.path,
|
|
382
|
+
source: entry.kind,
|
|
383
|
+
redaction: nestedString(entry.record, 'redaction') || nestedString(entry.record, 'redactionPolicy') || 'bounded-redacted-read-model',
|
|
384
|
+
startedAt: timestampString(entry.record.startedAt ?? readMetadata(entry.record).startedAt),
|
|
385
|
+
completedAt: timestampString(entry.record.completedAt ?? entry.record.finishedAt ?? readMetadata(entry.record).completedAt),
|
|
386
|
+
modelRoute,
|
|
387
|
+
certification: remoteCertification({
|
|
388
|
+
record: entry.record,
|
|
389
|
+
sourcePath: entry.path,
|
|
390
|
+
recordKind: 'remote outcome',
|
|
391
|
+
durableId: id,
|
|
392
|
+
runnerId: runner,
|
|
393
|
+
modelRoute,
|
|
394
|
+
hasOutcomeEvidence: Boolean(captureId || exportId || artifactId || kind === 'closeout'),
|
|
395
|
+
status,
|
|
396
|
+
}),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function workspaceId(record: Record<string, unknown>): string {
|
|
401
|
+
return nestedString(record, 'workspaceId')
|
|
402
|
+
|| nestedString(record, 'worktreeId')
|
|
403
|
+
|| nestedString(record, 'id');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function workspaceStatus(record: Record<string, unknown>): string {
|
|
407
|
+
return nestedString(record, 'status')
|
|
408
|
+
|| nestedString(record, 'state')
|
|
409
|
+
|| nestedString(record, 'outcome')
|
|
410
|
+
|| 'unknown';
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function normalizeWorkspace(entry: CollectedRecord, index: number): RemoteWorkspaceEvidenceRecord | null {
|
|
414
|
+
const runner = runnerId(entry.record);
|
|
415
|
+
if (!runner) return null;
|
|
416
|
+
const id = workspaceId(entry.record) || `remote-workspace:${runner}:${index}`;
|
|
417
|
+
const path = nestedString(entry.record, 'worktreePath')
|
|
418
|
+
|| nestedString(entry.record, 'workspacePath')
|
|
419
|
+
|| nestedString(entry.record, 'path')
|
|
420
|
+
|| nestedString(entry.record, 'root')
|
|
421
|
+
|| nestedString(entry.record, 'directory');
|
|
422
|
+
const status = workspaceStatus(entry.record);
|
|
423
|
+
const modelRoute = routeForRunner(entry.record, runner);
|
|
424
|
+
const workspace = workspaceId(entry.record) || null;
|
|
425
|
+
const worktreeRef = safePathRef(path);
|
|
426
|
+
const branch = safeNullablePreview(nestedString(entry.record, 'branch') || nestedString(entry.record, 'ref') || nestedString(entry.record, 'baseRef'), 100);
|
|
427
|
+
return {
|
|
428
|
+
id,
|
|
429
|
+
runnerId: runner,
|
|
430
|
+
status,
|
|
431
|
+
workspaceId: workspace,
|
|
432
|
+
isolationKind: nestedString(entry.record, 'isolationKind')
|
|
433
|
+
|| nestedString(entry.record, 'workspaceKind')
|
|
434
|
+
|| nestedString(entry.record, 'kind')
|
|
435
|
+
|| 'workspace',
|
|
436
|
+
label: safeNullablePreview(nestedString(entry.record, 'label') || nestedString(entry.record, 'name') || nestedString(entry.record, 'summary'), 120),
|
|
437
|
+
worktreeRef,
|
|
438
|
+
branch,
|
|
439
|
+
sourcePath: entry.path,
|
|
440
|
+
source: entry.kind,
|
|
441
|
+
redaction: nestedString(entry.record, 'redaction') || nestedString(entry.record, 'redactionPolicy') || 'path-bounded-read-model',
|
|
442
|
+
modelRoute,
|
|
443
|
+
certification: remoteCertification({
|
|
444
|
+
record: entry.record,
|
|
445
|
+
sourcePath: entry.path,
|
|
446
|
+
recordKind: 'remote workspace',
|
|
447
|
+
durableId: id,
|
|
448
|
+
runnerId: runner,
|
|
449
|
+
modelRoute,
|
|
450
|
+
hasWorkspaceEvidence: Boolean(workspace || worktreeRef || branch),
|
|
451
|
+
status,
|
|
452
|
+
}),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function dedupeById<T extends { readonly id: string; readonly runnerId: string }>(records: readonly T[]): readonly T[] {
|
|
457
|
+
const seen = new Set<string>();
|
|
458
|
+
const output: T[] = [];
|
|
459
|
+
for (const record of records) {
|
|
460
|
+
const key = `${record.runnerId}:${record.id}`;
|
|
461
|
+
if (seen.has(key)) continue;
|
|
462
|
+
seen.add(key);
|
|
463
|
+
output.push(record);
|
|
464
|
+
}
|
|
465
|
+
return output;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function sourceCounts(entries: readonly CollectedRecord[]): Readonly<Record<string, number>> {
|
|
469
|
+
const counts: Record<string, number> = {};
|
|
470
|
+
for (const entry of entries) counts[entry.path] = (counts[entry.path] ?? 0) + 1;
|
|
471
|
+
return counts;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function outcomeSources(context: CommandContext): readonly SourceCandidate[] {
|
|
475
|
+
const platform = readObject(context.platform);
|
|
476
|
+
const readModels = readObject(platform.readModels);
|
|
477
|
+
const remoteRuntime = readObject(readModels.remoteRuntime);
|
|
478
|
+
const remoteRunners = readObject(readModels.remoteRunners);
|
|
479
|
+
const remote = readObject(readModels.remote);
|
|
480
|
+
const opsRemote = readObject(readObject(context.ops).remoteRuntime);
|
|
481
|
+
const opsReadModels = readObject(opsRemote.readModels);
|
|
482
|
+
const clients = readObject(context.clients);
|
|
483
|
+
const operator = readObject(clients.operator);
|
|
484
|
+
const operatorRemote = readObject(operator.remoteRuntime);
|
|
485
|
+
return [
|
|
486
|
+
{ path: 'context.platform.readModels.remoteRuntime.captureOutcomes', source: remoteRuntime.captureOutcomes, kind: 'daemon-read-model' },
|
|
487
|
+
{ path: 'context.platform.readModels.remoteRuntime.outcomes', source: remoteRuntime.outcomes, kind: 'daemon-read-model' },
|
|
488
|
+
{ path: 'context.platform.readModels.remoteRuntime.captures', source: remoteRuntime.captures, kind: 'daemon-read-model' },
|
|
489
|
+
{ path: 'context.platform.readModels.remoteRuntime.exports', source: remoteRuntime.exports, kind: 'daemon-read-model' },
|
|
490
|
+
{ path: 'context.platform.readModels.remoteRuntime.closeouts', source: remoteRuntime.closeouts, kind: 'daemon-read-model' },
|
|
491
|
+
{ path: 'context.platform.readModels.remoteRunners.captureOutcomes', source: remoteRunners.captureOutcomes, kind: 'daemon-read-model' },
|
|
492
|
+
{ path: 'context.platform.readModels.remote.captureOutcomes', source: remote.captureOutcomes, kind: 'daemon-read-model' },
|
|
493
|
+
{ path: 'context.platform.readModels.remoteCaptureOutcomes', source: readModels.remoteCaptureOutcomes, kind: 'daemon-read-model' },
|
|
494
|
+
{ path: 'context.platform.readModels.remoteRunnerOutcomes', source: readModels.remoteRunnerOutcomes, kind: 'daemon-read-model' },
|
|
495
|
+
{ path: 'context.ops.remoteRuntime.readModels.captureOutcomes', source: opsReadModels.captureOutcomes, kind: 'sdk-read-model' },
|
|
496
|
+
{ path: 'context.clients.operator.remoteRuntime.captureOutcomes', source: operatorRemote.captureOutcomes, kind: 'sdk-read-model' },
|
|
497
|
+
];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function workspaceSources(context: CommandContext): readonly SourceCandidate[] {
|
|
501
|
+
const platform = readObject(context.platform);
|
|
502
|
+
const readModels = readObject(platform.readModels);
|
|
503
|
+
const remoteRuntime = readObject(readModels.remoteRuntime);
|
|
504
|
+
const remoteRunners = readObject(readModels.remoteRunners);
|
|
505
|
+
const remote = readObject(readModels.remote);
|
|
506
|
+
const worktrees = readObject(readModels.worktrees);
|
|
507
|
+
const opsRemote = readObject(readObject(context.ops).remoteRuntime);
|
|
508
|
+
const opsReadModels = readObject(opsRemote.readModels);
|
|
509
|
+
const clients = readObject(context.clients);
|
|
510
|
+
const operator = readObject(clients.operator);
|
|
511
|
+
const operatorRemote = readObject(operator.remoteRuntime);
|
|
512
|
+
return [
|
|
513
|
+
{ path: 'context.platform.readModels.remoteRuntime.workspaces', source: remoteRuntime.workspaces, kind: 'daemon-read-model' },
|
|
514
|
+
{ path: 'context.platform.readModels.remoteRuntime.workspaceIsolation', source: remoteRuntime.workspaceIsolation, kind: 'daemon-read-model' },
|
|
515
|
+
{ path: 'context.platform.readModels.remoteRuntime.worktrees', source: remoteRuntime.worktrees, kind: 'daemon-read-model' },
|
|
516
|
+
{ path: 'context.platform.readModels.remoteRunners.workspaces', source: remoteRunners.workspaces, kind: 'daemon-read-model' },
|
|
517
|
+
{ path: 'context.platform.readModels.remote.workspaceIsolation', source: remote.workspaceIsolation, kind: 'daemon-read-model' },
|
|
518
|
+
{ path: 'context.platform.readModels.remoteWorkspaces', source: readModels.remoteWorkspaces, kind: 'daemon-read-model' },
|
|
519
|
+
{ path: 'context.platform.readModels.remoteWorkspaceIsolation', source: readModels.remoteWorkspaceIsolation, kind: 'daemon-read-model' },
|
|
520
|
+
{ path: 'context.platform.readModels.worktrees.remote', source: worktrees.remote, kind: 'daemon-read-model' },
|
|
521
|
+
{ path: 'context.ops.remoteRuntime.readModels.workspaces', source: opsReadModels.workspaces, kind: 'sdk-read-model' },
|
|
522
|
+
{ path: 'context.clients.operator.remoteRuntime.workspaces', source: operatorRemote.workspaces, kind: 'sdk-read-model' },
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function remoteReadModelSnapshot(context: CommandContext): RemoteReadModelSnapshot {
|
|
527
|
+
const outcomeEntries = outcomeSources(context).flatMap((candidate) =>
|
|
528
|
+
collectFromSource(candidate.source, candidate.path, candidate.kind, OUTCOME_WRAPPER_KEYS, OUTCOME_METHODS)
|
|
529
|
+
);
|
|
530
|
+
const workspaceEntries = workspaceSources(context).flatMap((candidate) =>
|
|
531
|
+
collectFromSource(candidate.source, candidate.path, candidate.kind, WORKSPACE_WRAPPER_KEYS, WORKSPACE_METHODS)
|
|
532
|
+
);
|
|
533
|
+
return {
|
|
534
|
+
outcomes: dedupeById(outcomeEntries.map(normalizeOutcome).filter((entry): entry is RemoteCaptureOutcomeRecord => entry !== null)),
|
|
535
|
+
workspaces: dedupeById(workspaceEntries.map(normalizeWorkspace).filter((entry): entry is RemoteWorkspaceEvidenceRecord => entry !== null)),
|
|
536
|
+
sourceCounts: {
|
|
537
|
+
...sourceCounts(outcomeEntries),
|
|
538
|
+
...sourceCounts(workspaceEntries),
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
}
|