@ornexus/neocortex 4.60.73 → 4.60.75
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/dist/release-source.json +1 -1
- package/dist/sbom.cdx.json +1216 -1210
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +1 -108
- package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
- package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
- package/packages/client/dist/adapters/codex-adapter.js +2 -80
- package/packages/client/dist/adapters/cursor-adapter.js +4 -115
- package/packages/client/dist/adapters/gemini-adapter.js +2 -71
- package/packages/client/dist/adapters/grok-adapter.js +2 -42
- package/packages/client/dist/adapters/index.js +1 -22
- package/packages/client/dist/adapters/platform-detector.js +1 -212
- package/packages/client/dist/adapters/target-adapter.js +0 -12
- package/packages/client/dist/adapters/vscode-adapter.js +2 -72
- package/packages/client/dist/agent/refresh-stubs.js +2 -417
- package/packages/client/dist/agent/update-agent-yaml.js +1 -102
- package/packages/client/dist/agent/update-description.js +1 -251
- package/packages/client/dist/cache/crypto-utils.js +1 -78
- package/packages/client/dist/cache/encrypted-cache.d.ts +27 -0
- package/packages/client/dist/cache/encrypted-cache.js +1 -108
- package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
- package/packages/client/dist/cache/index.js +1 -13
- package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
- package/packages/client/dist/checkpoint/index.js +1 -11
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
- package/packages/client/dist/cli.js +5 -358
- package/packages/client/dist/commands/activate.js +8 -394
- package/packages/client/dist/commands/architecture-answers.js +7 -274
- package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
- package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
- package/packages/client/dist/commands/architecture-root-binding.js +1 -51
- package/packages/client/dist/commands/cache-status.js +2 -112
- package/packages/client/dist/commands/config.js +1 -88
- package/packages/client/dist/commands/doctor.js +2 -635
- package/packages/client/dist/commands/invoke.d.ts +38 -1
- package/packages/client/dist/commands/invoke.js +143 -7200
- package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
- package/packages/client/dist/commands/legacy-story-branch.js +1 -96
- package/packages/client/dist/commands/legacy-story-pr.js +1 -276
- package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
- package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
- package/packages/client/dist/commands/mutation-qa-git.js +1 -41
- package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
- package/packages/client/dist/commands/mutation-qa-run.js +3 -219
- package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
- package/packages/client/dist/commands/mutation-qa.js +1 -220
- package/packages/client/dist/commands/native-agent.js +2 -136
- package/packages/client/dist/commands/planning-diagnosis.js +1 -43
- package/packages/client/dist/commands/planning-generation.js +1 -53
- package/packages/client/dist/commands/planning-memory.js +1 -40
- package/packages/client/dist/commands/planning-research.js +1 -42
- package/packages/client/dist/commands/private-session.js +1 -75
- package/packages/client/dist/commands/property-qa.js +1 -139
- package/packages/client/dist/commands/qa-control.js +1 -16
- package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
- package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
- package/packages/client/dist/commands/qa-scan-capture.js +5 -663
- package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
- package/packages/client/dist/commands/qa-scan-review.js +5 -826
- package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
- package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
- package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
- package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
- package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
- package/packages/client/dist/commands/refactor-characterization.js +21 -671
- package/packages/client/dist/commands/refactor-commit.js +13 -399
- package/packages/client/dist/commands/refactor-control.js +3 -142
- package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
- package/packages/client/dist/commands/refactor-review.js +5 -283
- package/packages/client/dist/commands/refactor-root.js +7 -588
- package/packages/client/dist/commands/refresh-memory.js +3 -156
- package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
- package/packages/client/dist/commands/story-checkpoint.js +1 -171
- package/packages/client/dist/config/host-auth-broker.d.ts +31 -0
- package/packages/client/dist/config/host-auth-broker.js +1 -0
- package/packages/client/dist/config/host-auth-reference.d.ts +32 -0
- package/packages/client/dist/config/host-auth-reference.js +1 -0
- package/packages/client/dist/config/profile-directory.js +1 -13
- package/packages/client/dist/config/resolver-selection.js +1 -278
- package/packages/client/dist/config/secure-config.js +12 -281
- package/packages/client/dist/config.js +2 -223
- package/packages/client/dist/constants.js +1 -25
- package/packages/client/dist/context/context-collector.js +2 -222
- package/packages/client/dist/context/context-sanitizer.js +1 -145
- package/packages/client/dist/continuity/autonomous-governance.js +1 -285
- package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
- package/packages/client/dist/continuity/invoke-hooks.js +1 -129
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
- package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
- package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
- package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
- package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
- package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
- package/packages/client/dist/continuity/native-agent-store.js +1 -69
- package/packages/client/dist/continuity/native-agent-types.d.ts +11 -0
- package/packages/client/dist/continuity/native-agent-types.js +1 -90
- package/packages/client/dist/continuity/outbox.js +1 -117
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
- package/packages/client/dist/continuity/sqlite-store.js +81 -2612
- package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
- package/packages/client/dist/errors/error-messages.d.ts +3 -0
- package/packages/client/dist/errors/error-messages.js +2 -228
- package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
- package/packages/client/dist/evidence/collector.js +1 -347
- package/packages/client/dist/evidence/index.js +1 -1
- package/packages/client/dist/evidence/types.js +0 -2
- package/packages/client/dist/fs/atomic-state-replace.js +1 -152
- package/packages/client/dist/generated/command-registry.generated.js +1 -4076
- package/packages/client/dist/generated/public-projections.generated.js +1 -149
- package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
- package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +11 -1
- package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
- package/packages/client/dist/git/epic-operational-state.js +2 -672
- package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
- package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
- package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
- package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
- package/packages/client/dist/git/legacy-story-pr.js +70 -3538
- package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
- package/packages/client/dist/i18n/first-run.js +2 -60
- package/packages/client/dist/index.d.ts +2 -0
- package/packages/client/dist/index.js +1 -47
- package/packages/client/dist/invocation/invoke-input.js +1 -24
- package/packages/client/dist/license/host-auth-recovery.d.ts +158 -0
- package/packages/client/dist/license/host-auth-recovery.js +1 -0
- package/packages/client/dist/license/index.js +1 -5
- package/packages/client/dist/license/license-client.d.ts +10 -1
- package/packages/client/dist/license/license-client.js +1 -304
- package/packages/client/dist/machine/fingerprint.js +2 -163
- package/packages/client/dist/machine/index.js +1 -5
- package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
- package/packages/client/dist/memory/project-memory-writer.js +40 -1007
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
- package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
- package/packages/client/dist/migrations/project-identity-capability.js +1 -50
- package/packages/client/dist/migrations/project-identity.js +7 -493
- package/packages/client/dist/migrations/state-json.js +4 -393
- package/packages/client/dist/policy/architecture-policy.js +2 -184
- package/packages/client/dist/policy/index.js +1 -7
- package/packages/client/dist/policy/shared-policy-types.js +0 -38
- package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
- package/packages/client/dist/private-execution/clean-baseline.js +2 -279
- package/packages/client/dist/private-execution/codex-executor.js +4 -574
- package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
- package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
- package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
- package/packages/client/dist/private-execution/delivery.js +13 -410
- package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
- package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
- package/packages/client/dist/private-execution/implementation-validation.js +2 -106
- package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
- package/packages/client/dist/private-execution/isolated-session.js +5 -725
- package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
- package/packages/client/dist/private-execution/pact-test-source.js +1 -258
- package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
- package/packages/client/dist/private-execution/pact-write-set.js +1 -172
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
- package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
- package/packages/client/dist/private-execution/private-report-store.js +1 -42
- package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
- package/packages/client/dist/private-execution/property-test-choices.js +3 -147
- package/packages/client/dist/private-execution/property-test-plan.js +7 -271
- package/packages/client/dist/private-execution/property-test-review.js +4 -258
- package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
- package/packages/client/dist/private-execution/property-test-source.js +1 -139
- package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
- package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
- package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
- package/packages/client/dist/private-execution/root-memory-request.js +1 -29
- package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
- package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
- package/packages/client/dist/private-execution/session-lease.js +1 -63
- package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
- package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
- package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
- package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
- package/packages/client/dist/private-execution/ux-write-set.js +2 -191
- package/packages/client/dist/registry/registry-reconciliation.js +1 -234
- package/packages/client/dist/resilience/circuit-breaker.js +1 -170
- package/packages/client/dist/resilience/degradation-manager.js +1 -164
- package/packages/client/dist/resilience/freshness-indicator.js +1 -100
- package/packages/client/dist/resilience/index.js +1 -8
- package/packages/client/dist/resilience/recovery-detector.js +1 -74
- package/packages/client/dist/resolvers/asset-resolver.js +0 -13
- package/packages/client/dist/resolvers/local-resolver.js +8 -255
- package/packages/client/dist/resolvers/remote-resolver.js +1 -233
- package/packages/client/dist/runner/binary.js +1 -404
- package/packages/client/dist/runner/cli.js +17 -1571
- package/packages/client/dist/runner/epic-background-dispatcher.d.ts +61 -0
- package/packages/client/dist/runner/epic-background-dispatcher.js +1 -0
- package/packages/client/dist/runner/headless/adapters.js +1 -49
- package/packages/client/dist/runner/headless/background-root.d.ts +65 -0
- package/packages/client/dist/runner/headless/background-root.js +1 -0
- package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +27 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
- package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
- package/packages/client/dist/runner/headless/managed-environment-cleanup.d.ts +95 -0
- package/packages/client/dist/runner/headless/managed-environment-cleanup.js +1 -0
- package/packages/client/dist/runner/headless/managed-profile.js +1 -132
- package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
- package/packages/client/dist/runner/headless/resolver.js +1 -231
- package/packages/client/dist/runner/headless/run-yolo.js +1 -885
- package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
- package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
- package/packages/client/dist/runner/headless/types.js +2 -574
- package/packages/client/dist/runner/local-console.js +13 -396
- package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
- package/packages/client/dist/runner/project-identity.js +1 -106
- package/packages/client/dist/runner/scheduler.js +7 -995
- package/packages/client/dist/runner-cli.js +2 -18
- package/packages/client/dist/state/invocation-shape.js +1 -13
- package/packages/client/dist/state/project-state-snapshot.d.ts +7 -0
- package/packages/client/dist/state/project-state-snapshot.js +1 -1731
- package/packages/client/dist/state/state-json-repair.js +3 -785
- package/packages/client/dist/state/structural-dag-projection.js +1 -118
- package/packages/client/dist/telemetry/index.js +1 -5
- package/packages/client/dist/telemetry/offline-queue.js +1 -131
- package/packages/client/dist/tier/index.js +1 -5
- package/packages/client/dist/tier/tier-aware-client.js +1 -405
- package/packages/client/dist/types/index.js +1 -38
- package/packages/client/dist/version.js +1 -27
- package/packages/client/dist/yolo/change-evidence-collector.d.ts +21 -1
- package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
- package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
- package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
- package/packages/client/dist/yoloop/discovery-hook.js +2 -300
- package/packages/client/dist/yoloop/index.js +1 -8
- package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
- package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
- package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
- package/packages/client/dist/yoloop/target-normalization.js +1 -180
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
- package/packages/shared/dist/invocation/explicit-intent-recovery.d.ts +83 -0
- package/packages/shared/dist/invocation/explicit-intent-recovery.js +187 -0
- package/packages/shared/dist/invocation/index.d.ts +7 -0
- package/packages/shared/dist/invocation/index.js +22 -0
- package/packages/shared/dist/yolo/change-evidence.js +1 -1
- package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +24 -21
- package/targets-stubs/codex/README.md +6 -1
- package/targets-stubs/codex/neocortex.toml +7 -2
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +16 -1
- package/targets-stubs/opencode/neocortex.md +6 -0
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1,1587 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license FSL-1.1
|
|
3
|
-
* Copyright (c) 2026 OrNexus AI
|
|
4
|
-
*
|
|
5
|
-
* P180.05 -- Thin-client continuity state snapshots and operation-log apply.
|
|
6
|
-
*/
|
|
7
|
-
import { existsSync } from 'node:fs';
|
|
8
|
-
import { createHash } from 'node:crypto';
|
|
9
|
-
import { deriveProjectIdentity } from '../runner/project-identity.js';
|
|
10
|
-
import { recordAutonomousGovernance, } from './autonomous-governance.js';
|
|
11
|
-
import { CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE, ContinuitySqliteStoreError, continuitySqliteStorePath, isContinuityPublicStage, normalizeContinuityStageIteration, openContinuitySqliteStore, sanitizeContinuityStageHistory, } from './sqlite-store.js';
|
|
12
|
-
export const CONTINUITY_CLIENT_SCHEMA_VERSION = 1;
|
|
13
|
-
export const CONTINUITY_CLIENT_CONTRACT_VERSION = 'continuity-v1';
|
|
14
|
-
export const CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_LIMIT = 10;
|
|
15
|
-
export const CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
16
|
-
export const CONTINUITY_INVOKE_WATCH_DEFAULT_LIMIT = CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_LIMIT;
|
|
17
|
-
export const CONTINUITY_INVOKE_WATCH_DEFAULT_POLL_INTERVAL_MS = CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_POLL_INTERVAL_MS;
|
|
18
|
-
export const CONTINUITY_INVOKE_WATCH_DEFAULT_MAX_IDLE_POLLS = 3;
|
|
19
|
-
export const CONTINUITY_INVOKE_WATCH_DEFAULT_MAX_POLLS = 1_800;
|
|
20
|
-
export const CONTINUITY_CLIENT_PORTAL_SYNC_ENVELOPE_VERSION = 1;
|
|
21
|
-
export const CONTINUITY_CLIENT_REASON_CODES = Object.freeze({
|
|
22
|
-
OK: 'continuity-ok',
|
|
23
|
-
INVALID_SCHEMA_VERSION: 'invalid-schema-version',
|
|
24
|
-
INVALID_CONTRACT_VERSION: 'invalid-contract-version',
|
|
25
|
-
UNKNOWN_OPERATION_REQUIRED: 'unknown-operation-required',
|
|
26
|
-
UNKNOWN_OPERATION_IGNORED: 'unknown-operation-ignored',
|
|
27
|
-
DUPLICATE_OPERATION_ID: 'duplicate-operation-id',
|
|
28
|
-
IDEMPOTENCY_REPLAY: 'idempotency-replay',
|
|
29
|
-
UNSAFE_FIELD_REJECTED: 'unsafe-field-rejected',
|
|
30
|
-
SQLITE_ADAPTER_UNAVAILABLE: 'sqlite-adapter-unavailable',
|
|
31
|
-
SQLITE_INTEGRITY_FAILED: 'sqlite-integrity-failed',
|
|
32
|
-
SQLITE_MUTATION_FAILED: 'sqlite-mutation-failed',
|
|
33
|
-
});
|
|
34
|
-
const KNOWN_OPERATION_TYPES = new Set([
|
|
35
|
-
'create_job',
|
|
36
|
-
'update_job_status',
|
|
37
|
-
'append_event',
|
|
38
|
-
'append_receipt',
|
|
39
|
-
'upsert_goal_campaign',
|
|
40
|
-
'upsert_goal_ledger_item',
|
|
41
|
-
'upsert_verifier',
|
|
42
|
-
'upsert_review_gate',
|
|
43
|
-
'schedule_next_tick',
|
|
44
|
-
'acquire_lease',
|
|
45
|
-
'release_lease',
|
|
46
|
-
'compact_context',
|
|
47
|
-
'enqueue_outbox',
|
|
48
|
-
'append_outbox',
|
|
49
|
-
'enqueue_execution_attempt',
|
|
50
|
-
'claim_execution_attempt',
|
|
51
|
-
'heartbeat_execution_attempt',
|
|
52
|
-
'release_execution_attempt',
|
|
53
|
-
'complete_execution_attempt',
|
|
54
|
-
'append_execution_receipt',
|
|
55
|
-
'append_execution_summary',
|
|
56
|
-
]);
|
|
57
|
-
const ALLOWED_METADATA_KEYS = new Set([
|
|
58
|
-
'schemaVersion',
|
|
59
|
-
'contractVersion',
|
|
60
|
-
'eventType',
|
|
61
|
-
'executionId',
|
|
62
|
-
'jobId',
|
|
63
|
-
'operationId',
|
|
64
|
-
'idempotencyKey',
|
|
65
|
-
'goalId',
|
|
66
|
-
'loopId',
|
|
67
|
-
'campaignId',
|
|
68
|
-
'itemId',
|
|
69
|
-
'ledgerItemIds',
|
|
70
|
-
'verifierId',
|
|
71
|
-
'verifierIds',
|
|
72
|
-
'gateId',
|
|
73
|
-
'reviewGateIds',
|
|
74
|
-
'runnerId',
|
|
75
|
-
'leaseId',
|
|
76
|
-
'status',
|
|
77
|
-
'reasonCode',
|
|
78
|
-
'reasonCodes',
|
|
79
|
-
'runtimeId',
|
|
80
|
-
'modelId',
|
|
81
|
-
'runtimeSource',
|
|
82
|
-
'headlessRuntime',
|
|
83
|
-
'headlessModel',
|
|
84
|
-
'headlessRuntimeReasonCode',
|
|
85
|
-
'originRuntime',
|
|
86
|
-
'originModel',
|
|
87
|
-
'targetRef',
|
|
88
|
-
'attemptId',
|
|
89
|
-
'attemptStatus',
|
|
90
|
-
'directiveHash',
|
|
91
|
-
'timeoutMs',
|
|
92
|
-
'leaseExpiresAt',
|
|
93
|
-
'heartbeatAt',
|
|
94
|
-
'notBeforeAt',
|
|
95
|
-
'maxAttempts',
|
|
96
|
-
'counts',
|
|
97
|
-
'hashes',
|
|
98
|
-
'relativePaths',
|
|
99
|
-
'artifactRefs',
|
|
100
|
-
'evidenceRefs',
|
|
101
|
-
'validationRefs',
|
|
102
|
-
'registryRefs',
|
|
103
|
-
'storyIds',
|
|
104
|
-
'epicIds',
|
|
105
|
-
'debtIds',
|
|
106
|
-
'iteration',
|
|
107
|
-
'currentStage',
|
|
108
|
-
'stageHistory',
|
|
109
|
-
'durationMs',
|
|
110
|
-
'retryCount',
|
|
111
|
-
'nextTickAt',
|
|
112
|
-
'processingOutputSource',
|
|
113
|
-
'publicSummary',
|
|
114
|
-
'redactionEvidence',
|
|
115
|
-
'selectorKind',
|
|
116
|
-
'targetId',
|
|
117
|
-
'targetJobId',
|
|
118
|
-
'relatedJobIds',
|
|
119
|
-
'serverAccessToLocalhost',
|
|
120
|
-
'serverHeldStream',
|
|
121
|
-
'initialCursor',
|
|
122
|
-
'targetResolutionState',
|
|
123
|
-
'targetKind',
|
|
124
|
-
'relatedJobCount',
|
|
125
|
-
'snapshotStatus',
|
|
126
|
-
'snapshotGeneratedAt',
|
|
127
|
-
'renderedSummaryCount',
|
|
128
|
-
'cursorState',
|
|
129
|
-
'currentCursor',
|
|
130
|
-
'nextCursor',
|
|
131
|
-
'cursorAdvanced',
|
|
132
|
-
'eventId',
|
|
133
|
-
'latestEventId',
|
|
134
|
-
'watchStatus',
|
|
135
|
-
'stopReason',
|
|
136
|
-
'syncVersion',
|
|
137
|
-
'transportKind',
|
|
138
|
-
'envelopeId',
|
|
139
|
-
'workspaceId',
|
|
140
|
-
'workspaceLabel',
|
|
141
|
-
'projectId',
|
|
142
|
-
'projectLabel',
|
|
143
|
-
'snapshotId',
|
|
144
|
-
'syncCursor',
|
|
145
|
-
'previousSyncCursor',
|
|
146
|
-
'nextSyncCursor',
|
|
147
|
-
'commandId',
|
|
148
|
-
'receiptId',
|
|
149
|
-
'acknowledgedAt',
|
|
150
|
-
'observedAt',
|
|
151
|
-
'generatedAt',
|
|
152
|
-
'syncStatus',
|
|
153
|
-
'commandStatus',
|
|
154
|
-
'receiptStatus',
|
|
155
|
-
'actorType',
|
|
156
|
-
'actorId',
|
|
157
|
-
'tenantId',
|
|
158
|
-
'commandKind',
|
|
159
|
-
'auditStatus',
|
|
160
|
-
'redactionCount',
|
|
161
|
-
]);
|
|
162
|
-
const UNSAFE_METADATA_KEY_PATTERN = /(raw[_-]?(?:logs?|payloads?)|std(?:out|err)|stack(?:[_-]?trace)?|transcript|prompt(?:[_-]?(?:body|text))?|private[_-]?(?:urls?|prompts?)|secret|token|password|protected(?:[_-]?(?:field|body|bodies|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|\bpii\b|authorization|api[_-]?key|license[_-]?key|argv|args|executable(?:[_-]?path)?|binary(?:[_-]?path)?|shell|cwd|command[_-]?path|vendor(?:[_-]?(?:payload|corpus|response|output)))/i;
|
|
163
|
-
const ABSOLUTE_URL_VALUE_PATTERN = /\bhttps?:\/\/\S+/i;
|
|
164
|
-
const LOCAL_ABSOLUTE_PATH_VALUE_PATTERN = /(?:^|\s)(?:\/(?:home|Users|media|mnt|tmp|var|etc|private)\/\S+|[A-Za-z]:\\\S+)/i;
|
|
165
|
-
const SECRET_VALUE_PATTERN = /(ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|authorization\s*:|password\s*=|token\s*=|api[_-]?key\s*=|license[_-]?key\s*=)/i;
|
|
166
|
-
const RAW_LOG_VALUE_PATTERN = /(raw[_-]?(?:logs?)\s*[:=]|transcript|stack trace|stderr:|stdout:)/i;
|
|
167
|
-
const PROMPT_VALUE_PATTERN = /(prompt[_-]?body|system prompt|workflow body|step body)/i;
|
|
168
|
-
const PROTECTED_VALUE_PATTERN = /(PROTECTED_|_PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|\bPII\b|VENDOR_CORPUS|P180[_-]?SYNTHETIC[_-]?CONTINUITY|P185[_-]?SYNTHETIC[_-]?CONTINUITY|P187[_-]?SYNTHETIC[_-]?(?:CONTINUITY|RUNNER|WATCH)|P190[_-]?SYNTHETIC[_-]?(?:HEADLESS|CONTINUITY)|P192[_-]?SYNTHETIC[_-]?(?:PORTAL|RUNNER|SYNC))/i;
|
|
169
|
-
const PUBLIC_TEXT_MAX_CHARS = 240;
|
|
170
|
-
const COMPACTION_SUMMARY_MAX_CHARS = 2_400;
|
|
171
|
-
const COMPACTION_RETAINED_REF_LIMIT = 40;
|
|
172
|
-
const COMPACTION_ARTIFACT_REF_LIMIT = 20;
|
|
173
|
-
const COMPACTION_EVIDENCE_REF_LIMIT = 40;
|
|
174
|
-
const COMPACTION_DECISION_LOG_REF_LIMIT = 20;
|
|
175
|
-
const COMPACTION_RELATIVE_PATH_LIMIT = 80;
|
|
176
|
-
export class ContinuityClientStateStoreError extends Error {
|
|
177
|
-
reasonCodes;
|
|
178
|
-
redactions;
|
|
179
|
-
mutationBlocked;
|
|
180
|
-
constructor(message, reasonCodes, options = {}) {
|
|
181
|
-
super(message);
|
|
182
|
-
this.name = 'ContinuityClientStateStoreError';
|
|
183
|
-
this.reasonCodes = unique(reasonCodes);
|
|
184
|
-
this.redactions = options.redactions ?? [];
|
|
185
|
-
this.mutationBlocked = options.mutationBlocked ?? false;
|
|
186
|
-
this.cause = options.cause;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
export function continuitySqliteExists(projectRoot) {
|
|
190
|
-
return existsSync(continuitySqliteStorePath(projectRoot));
|
|
191
|
-
}
|
|
192
|
-
export function createContinuityHardStopSnapshot(reasonCodes, nowIso = new Date().toISOString()) {
|
|
193
|
-
return {
|
|
194
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
195
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
196
|
-
generatedAt: nowIso,
|
|
197
|
-
hardStop: true,
|
|
198
|
-
reasonCodes: unique(reasonCodes),
|
|
199
|
-
publicSafe: true,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
export async function openContinuityClientStateStore(options) {
|
|
203
|
-
const store = await openContinuitySqliteStore({
|
|
204
|
-
projectRoot: options.projectRoot,
|
|
205
|
-
adapter: options.adapter,
|
|
206
|
-
adapterLoader: options.adapterLoader,
|
|
207
|
-
now: options.now,
|
|
208
|
-
strictReadOnly: options.strictReadOnly,
|
|
209
|
-
});
|
|
210
|
-
return new ContinuityClientStateStore(store, options.now ?? (() => new Date().toISOString()), deriveProjectIdentity(options.projectRoot));
|
|
211
|
-
}
|
|
212
|
-
export class ContinuityClientStateStore {
|
|
213
|
-
store;
|
|
214
|
-
now;
|
|
215
|
-
projectIdentity;
|
|
216
|
-
constructor(store, now = () => new Date().toISOString(), projectIdentity) {
|
|
217
|
-
this.store = store;
|
|
218
|
-
this.now = now;
|
|
219
|
-
this.projectIdentity = projectIdentity;
|
|
220
|
-
}
|
|
221
|
-
close() {
|
|
222
|
-
this.store.close();
|
|
223
|
-
}
|
|
224
|
-
recordAutonomousGovernance(envelope, nowIso) {
|
|
225
|
-
try {
|
|
226
|
-
return recordAutonomousGovernance(this.store, envelope, nowIso);
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
throw mapContinuityError(error, 'Failed to record autonomous governance envelope.');
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
collectSnapshot(options = {}) {
|
|
233
|
-
try {
|
|
234
|
-
const nowIso = options.nowIso ?? this.now();
|
|
235
|
-
const rows = this.store.collectSnapshotRows({
|
|
236
|
-
nowIso,
|
|
237
|
-
jobLimit: options.jobLimit ?? 20,
|
|
238
|
-
staleLeaseLimit: options.staleLeaseLimit ?? 10,
|
|
239
|
-
receiptLimit: options.receiptLimit ?? 10,
|
|
240
|
-
eventLimit: options.eventLimit ?? 10,
|
|
241
|
-
snapshotLimit: options.snapshotLimit ?? 5,
|
|
242
|
-
});
|
|
243
|
-
const dueRows = this.store.listDueJobs({
|
|
244
|
-
nowIso,
|
|
245
|
-
limit: options.dueJobLimit ?? 10,
|
|
246
|
-
});
|
|
247
|
-
const processingSummaryRows = this.store.listProcessingOutputSummaries({
|
|
248
|
-
limit: options.processingSummaryLimit ?? CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_LIMIT,
|
|
249
|
-
});
|
|
250
|
-
const redactions = collectReceiptRedactions(rows.receipts).slice(0, 20);
|
|
251
|
-
const portalSync = toPortalSyncSnapshotSummary({
|
|
252
|
-
generatedAt: nowIso,
|
|
253
|
-
counts: rows.counts,
|
|
254
|
-
jobCount: rows.jobs.length,
|
|
255
|
-
dueJobCount: dueRows.length,
|
|
256
|
-
outboxPending: rows.outboxCounts.pending ?? 0,
|
|
257
|
-
redactions,
|
|
258
|
-
});
|
|
259
|
-
return {
|
|
260
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
261
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
262
|
-
generatedAt: nowIso,
|
|
263
|
-
store: {
|
|
264
|
-
schemaVersion: rows.schemaVersion,
|
|
265
|
-
readable: true,
|
|
266
|
-
},
|
|
267
|
-
jobs: rows.jobs.map(toSnapshotJobSummary),
|
|
268
|
-
counts: rows.counts,
|
|
269
|
-
dueJobCount: rows.counts.dueJobs ?? 0,
|
|
270
|
-
dueJobs: dueRows.map(toSnapshotJobSummary),
|
|
271
|
-
staleLeases: rows.staleLeases.map((lease) => ({
|
|
272
|
-
leaseId: boundPublicText(lease.lease_id, 100),
|
|
273
|
-
jobId: boundPublicText(lease.job_id, 100),
|
|
274
|
-
runnerId: boundPublicText(lease.runner_id, 100),
|
|
275
|
-
expiresAt: boundPublicText(lease.expires_at, 80),
|
|
276
|
-
publicSafe: true,
|
|
277
|
-
})),
|
|
278
|
-
outbox: {
|
|
279
|
-
total: rows.counts.outbox ?? 0,
|
|
280
|
-
pending: rows.outboxCounts.pending ?? 0,
|
|
281
|
-
failed: rows.outboxCounts.failed ?? 0,
|
|
282
|
-
deadLetter: rows.outboxCounts.dead_letter ?? 0,
|
|
283
|
-
sent: rows.outboxCounts.sent ?? 0,
|
|
284
|
-
acked: rows.outboxCounts.acked ?? 0,
|
|
285
|
-
},
|
|
286
|
-
queue: {
|
|
287
|
-
depth: rows.queue.depth,
|
|
288
|
-
queued: rows.queue.queued,
|
|
289
|
-
running: rows.queue.running,
|
|
290
|
-
retrying: rows.queue.retrying,
|
|
291
|
-
deadLetter: rows.queue.deadLetter,
|
|
292
|
-
capacity: rows.queue.capacity,
|
|
293
|
-
available: rows.queue.available,
|
|
294
|
-
backpressure: rows.queue.backpressure,
|
|
295
|
-
generatedAt: boundPublicText(rows.queue.generatedAt, 80),
|
|
296
|
-
...(rows.queue.nextRetryAt ? { nextRetryAt: boundPublicText(rows.queue.nextRetryAt, 80) } : {}),
|
|
297
|
-
...(rows.queue.reasonCode ? { reasonCode: boundPublicText(rows.queue.reasonCode, 80) } : {}),
|
|
298
|
-
publicSafe: true,
|
|
299
|
-
},
|
|
300
|
-
lastReceipts: rows.receipts.map((receipt) => ({
|
|
301
|
-
receiptId: boundPublicText(receipt.receipt_id, 100),
|
|
302
|
-
jobId: boundPublicText(receipt.job_id, 100),
|
|
303
|
-
...(receipt.operation_id ? { operationId: boundPublicText(receipt.operation_id, 100) } : {}),
|
|
304
|
-
status: boundPublicText(receipt.status, 40),
|
|
305
|
-
...(receipt.reason_code ? { reasonCode: boundPublicText(receipt.reason_code, 80) } : {}),
|
|
306
|
-
createdAt: boundPublicText(receipt.created_at, 80),
|
|
307
|
-
publicSafe: true,
|
|
308
|
-
})),
|
|
309
|
-
lastEvents: rows.events.map((event) => {
|
|
310
|
-
const metadata = normalizeSnapshotRecord(parsePublicMetadata(event.metadata_json));
|
|
311
|
-
return {
|
|
312
|
-
eventId: boundPublicText(event.event_id, 100),
|
|
313
|
-
jobId: boundPublicText(event.job_id, 100),
|
|
314
|
-
operationId: boundPublicText(event.operation_id, 100),
|
|
315
|
-
type: boundPublicText(event.type, 80),
|
|
316
|
-
publicSummary: boundPublicText(event.summary, PUBLIC_TEXT_MAX_CHARS),
|
|
317
|
-
...(event.reason_code ? { reasonCode: boundPublicText(event.reason_code, 80) } : {}),
|
|
318
|
-
createdAt: boundPublicText(event.created_at, 80),
|
|
319
|
-
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
320
|
-
publicSafe: true,
|
|
321
|
-
};
|
|
322
|
-
}),
|
|
323
|
-
processingSummaries: processingSummaryRows.map(toProcessingSummaryRef),
|
|
324
|
-
headlessExecutionAttempts: rows.headlessExecutionAttempts.map(toHeadlessExecutionAttemptRef),
|
|
325
|
-
compactContext: toCompactContextSnapshot(rows.stateSnapshots),
|
|
326
|
-
portalSync,
|
|
327
|
-
redactions,
|
|
328
|
-
publicSafe: true,
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
catch (error) {
|
|
332
|
-
throw mapContinuityError(error, 'Failed to collect continuity snapshot.');
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
collectProcessingOutputSummaryCursor(options = {}) {
|
|
336
|
-
try {
|
|
337
|
-
const result = this.store.pollProcessingOutputSummaries(options);
|
|
338
|
-
return {
|
|
339
|
-
summaries: result.summaries.map(toProcessingSummaryRef),
|
|
340
|
-
...(result.nextCursor ? { nextCursor: result.nextCursor } : {}),
|
|
341
|
-
hasMore: result.hasMore,
|
|
342
|
-
publicSafe: true,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
catch (error) {
|
|
346
|
-
throw mapContinuityError(error, 'Failed to collect processing summary cursor.');
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
applyStateUpdate(update) {
|
|
350
|
-
const logs = normalizeStateUpdate(update);
|
|
351
|
-
const appliedOperationIds = [];
|
|
352
|
-
const ignoredOperationIds = [];
|
|
353
|
-
const reasonCodes = [];
|
|
354
|
-
const redactions = [];
|
|
355
|
-
for (const log of logs) {
|
|
356
|
-
const result = this.applyOperationLog(log);
|
|
357
|
-
appliedOperationIds.push(...result.appliedOperationIds);
|
|
358
|
-
ignoredOperationIds.push(...result.ignoredOperationIds);
|
|
359
|
-
reasonCodes.push(...result.reasonCodes);
|
|
360
|
-
redactions.push(...result.redactions);
|
|
361
|
-
}
|
|
362
|
-
return {
|
|
363
|
-
ok: true,
|
|
364
|
-
appliedOperationIds: unique(appliedOperationIds),
|
|
365
|
-
ignoredOperationIds: unique(ignoredOperationIds),
|
|
366
|
-
reasonCodes: unique(reasonCodes.length > 0 ? reasonCodes : [CONTINUITY_CLIENT_REASON_CODES.OK]),
|
|
367
|
-
redactions,
|
|
368
|
-
publicSafe: true,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
applyOperationLog(log) {
|
|
372
|
-
const planned = planOperationLog(log);
|
|
373
|
-
const appliedOperationIds = [];
|
|
374
|
-
const ignoredOperationIds = [...planned.ignoredOperationIds];
|
|
375
|
-
const reasonCodes = [...planned.reasonCodes];
|
|
376
|
-
const redactions = [...planned.redactions];
|
|
377
|
-
try {
|
|
378
|
-
this.store.runInTransaction(() => {
|
|
379
|
-
for (const operation of planned.operations) {
|
|
380
|
-
if (this.store.hasOperationRef(operation.operationId, operation.idempotencyKey)) {
|
|
381
|
-
ignoredOperationIds.push(operation.operationId);
|
|
382
|
-
reasonCodes.push(CONTINUITY_CLIENT_REASON_CODES.IDEMPOTENCY_REPLAY);
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
this.applyKnownOperation(operation, planned.logMetadata);
|
|
386
|
-
appliedOperationIds.push(operation.operationId);
|
|
387
|
-
if (operation.type !== 'append_receipt') {
|
|
388
|
-
this.recordAppliedAudit(operation);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
for (const skipped of planned.skippedOperations) {
|
|
392
|
-
this.recordIgnoredAuditIfPossible(skipped, CONTINUITY_CLIENT_REASON_CODES.UNKNOWN_OPERATION_IGNORED);
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
throw mapContinuityError(error, 'Failed to apply continuity operation log atomically.');
|
|
398
|
-
}
|
|
399
|
-
return {
|
|
400
|
-
ok: true,
|
|
401
|
-
appliedOperationIds: unique(appliedOperationIds),
|
|
402
|
-
ignoredOperationIds: unique(ignoredOperationIds),
|
|
403
|
-
reasonCodes: unique(reasonCodes.length > 0 ? reasonCodes : [CONTINUITY_CLIENT_REASON_CODES.OK]),
|
|
404
|
-
redactions,
|
|
405
|
-
publicSafe: true,
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
applyKnownOperation(operation, logMetadata) {
|
|
409
|
-
switch (operation.type) {
|
|
410
|
-
case 'create_job':
|
|
411
|
-
this.applyCreateJob(operation, logMetadata);
|
|
412
|
-
return;
|
|
413
|
-
case 'update_job_status':
|
|
414
|
-
this.applyUpdateJobStatus(operation);
|
|
415
|
-
return;
|
|
416
|
-
case 'append_event':
|
|
417
|
-
this.applyAppendEvent(operation);
|
|
418
|
-
return;
|
|
419
|
-
case 'append_receipt':
|
|
420
|
-
this.applyAppendReceipt(operation);
|
|
421
|
-
return;
|
|
422
|
-
case 'upsert_goal_campaign':
|
|
423
|
-
this.applyUpsertGoalCampaign(operation);
|
|
424
|
-
return;
|
|
425
|
-
case 'schedule_next_tick':
|
|
426
|
-
this.store.scheduleNextTick({
|
|
427
|
-
jobId: operation.jobId,
|
|
428
|
-
scheduledAt: requiredString(operation, 'scheduledAt'),
|
|
429
|
-
updatedAt: operation.createdAt,
|
|
430
|
-
reasonCode: optionalString(operation, 'reasonCode'),
|
|
431
|
-
});
|
|
432
|
-
return;
|
|
433
|
-
case 'acquire_lease':
|
|
434
|
-
this.store.acquireLease({
|
|
435
|
-
leaseId: requiredString(operation, 'leaseId'),
|
|
436
|
-
jobId: operation.jobId,
|
|
437
|
-
runnerId: requiredString(operation, 'runnerId'),
|
|
438
|
-
acquiredAt: requiredString(operation, 'acquiredAt'),
|
|
439
|
-
expiresAt: requiredString(operation, 'expiresAt'),
|
|
440
|
-
operationId: operation.operationId,
|
|
441
|
-
idempotencyKey: operation.idempotencyKey,
|
|
442
|
-
metadata: sanitizeMetadataStrict(operation.metadata, 'operation.metadata'),
|
|
443
|
-
});
|
|
444
|
-
return;
|
|
445
|
-
case 'release_lease':
|
|
446
|
-
this.store.releaseLease({
|
|
447
|
-
leaseId: requiredString(operation, 'leaseId'),
|
|
448
|
-
releasedAt: requiredString(operation, 'releasedAt'),
|
|
449
|
-
reasonCode: optionalString(operation, 'reasonCode'),
|
|
450
|
-
});
|
|
451
|
-
return;
|
|
452
|
-
case 'compact_context':
|
|
453
|
-
this.applyCompactContext(operation);
|
|
454
|
-
return;
|
|
455
|
-
case 'upsert_goal_ledger_item':
|
|
456
|
-
this.applyUpsertGoalLedgerItem(operation);
|
|
457
|
-
return;
|
|
458
|
-
case 'upsert_verifier':
|
|
459
|
-
this.applyUpsertVerifier(operation);
|
|
460
|
-
return;
|
|
461
|
-
case 'upsert_review_gate':
|
|
462
|
-
this.applyUpsertReviewGate(operation);
|
|
463
|
-
return;
|
|
464
|
-
case 'enqueue_outbox':
|
|
465
|
-
case 'append_outbox':
|
|
466
|
-
this.applyOutbox(operation);
|
|
467
|
-
return;
|
|
468
|
-
case 'enqueue_execution_attempt':
|
|
469
|
-
this.applyEnqueueExecutionAttempt(operation);
|
|
470
|
-
return;
|
|
471
|
-
case 'claim_execution_attempt':
|
|
472
|
-
this.store.claimHeadlessExecutionAttempt({
|
|
473
|
-
attemptId: requiredString(operation, 'attemptId'),
|
|
474
|
-
leaseId: requiredString(operation, 'leaseId'),
|
|
475
|
-
runnerId: requiredString(operation, 'runnerId'),
|
|
476
|
-
claimedAt: optionalString(operation, 'claimedAt') ?? operation.createdAt,
|
|
477
|
-
leaseExpiresAt: requiredString(operation, 'leaseExpiresAt'),
|
|
478
|
-
reasonCode: optionalString(operation, 'reasonCode'),
|
|
479
|
-
});
|
|
480
|
-
return;
|
|
481
|
-
case 'heartbeat_execution_attempt':
|
|
482
|
-
this.store.heartbeatHeadlessExecutionAttempt({
|
|
483
|
-
attemptId: requiredString(operation, 'attemptId'),
|
|
484
|
-
leaseId: requiredString(operation, 'leaseId'),
|
|
485
|
-
runnerId: requiredString(operation, 'runnerId'),
|
|
486
|
-
heartbeatAt: optionalString(operation, 'heartbeatAt') ?? operation.createdAt,
|
|
487
|
-
leaseExpiresAt: optionalString(operation, 'leaseExpiresAt'),
|
|
488
|
-
});
|
|
489
|
-
return;
|
|
490
|
-
case 'release_execution_attempt':
|
|
491
|
-
this.store.releaseHeadlessExecutionAttempt({
|
|
492
|
-
attemptId: requiredString(operation, 'attemptId'),
|
|
493
|
-
leaseId: requiredString(operation, 'leaseId'),
|
|
494
|
-
releasedAt: optionalString(operation, 'releasedAt') ?? operation.createdAt,
|
|
495
|
-
status: optionalString(operation, 'status'),
|
|
496
|
-
reasonCode: optionalString(operation, 'reasonCode'),
|
|
497
|
-
notBeforeAt: optionalString(operation, 'notBeforeAt'),
|
|
498
|
-
retryCount: optionalNumber(operation, 'retryCount'),
|
|
499
|
-
});
|
|
500
|
-
return;
|
|
501
|
-
case 'complete_execution_attempt':
|
|
502
|
-
this.applyCompleteExecutionAttempt(operation);
|
|
503
|
-
return;
|
|
504
|
-
case 'append_execution_receipt':
|
|
505
|
-
this.applyAppendExecutionReceipt(operation);
|
|
506
|
-
return;
|
|
507
|
-
case 'append_execution_summary':
|
|
508
|
-
this.applyAppendExecutionSummary(operation);
|
|
509
|
-
return;
|
|
510
|
-
default:
|
|
511
|
-
throw new ContinuityClientStateStoreError(`Unknown continuity operation ${operation.type}.`, [CONTINUITY_CLIENT_REASON_CODES.UNKNOWN_OPERATION_REQUIRED], { mutationBlocked: true });
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
applyCompactContext(operation) {
|
|
515
|
-
const summaryHash = normalizeCompactionHash(requiredString(operation, 'summaryHash'), 'compact_context.summaryHash');
|
|
516
|
-
const retainedRefs = normalizeCompactionPaths(operation.retainedRefs, 'compact_context.retainedRefs', COMPACTION_RETAINED_REF_LIMIT);
|
|
517
|
-
const summary = normalizeCompactionSummary(optionalString(operation, 'publicSummary')
|
|
518
|
-
?? `Context compacted with ${retainedRefs.length} retained public refs.`);
|
|
519
|
-
const artifactRefs = normalizeCompactionArtifactRefs(operation);
|
|
520
|
-
const evidenceRefs = normalizeCompactionEvidenceRefs(operation);
|
|
521
|
-
const decisionLogRefs = normalizeCompactionPaths(operation.decisionLogRefs, 'compact_context.decisionLogRefs', COMPACTION_DECISION_LOG_REF_LIMIT);
|
|
522
|
-
const checkpoint = normalizeCompactionCheckpoint(operation, retainedRefs, artifactRefs, evidenceRefs);
|
|
523
|
-
const relativePaths = unique([
|
|
524
|
-
...retainedRefs,
|
|
525
|
-
...decisionLogRefs,
|
|
526
|
-
...artifactRefs.map((ref) => ref.relativePath),
|
|
527
|
-
...evidenceRefs.map((ref) => ref.relativePath),
|
|
528
|
-
]).slice(0, COMPACTION_RELATIVE_PATH_LIMIT);
|
|
529
|
-
const computedSummaryHash = sha256Hex(summary);
|
|
530
|
-
const reasonCode = optionalString(operation, 'reasonCode') ?? 'context-compacted';
|
|
531
|
-
const metadata = {
|
|
532
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
533
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
534
|
-
jobId: operation.jobId,
|
|
535
|
-
operationId: operation.operationId,
|
|
536
|
-
reasonCode,
|
|
537
|
-
publicSummary: summary,
|
|
538
|
-
counts: {
|
|
539
|
-
retainedRefs: retainedRefs.length,
|
|
540
|
-
artifactRefs: artifactRefs.length,
|
|
541
|
-
evidenceRefs: evidenceRefs.length,
|
|
542
|
-
decisionLogRefs: decisionLogRefs.length,
|
|
543
|
-
...(optionalNumber(operation, 'inputTokens') !== undefined ? { inputContextUnits: optionalNumber(operation, 'inputTokens') } : {}),
|
|
544
|
-
...(optionalNumber(operation, 'outputTokens') !== undefined ? { outputContextUnits: optionalNumber(operation, 'outputTokens') } : {}),
|
|
545
|
-
summaryChars: summary.length,
|
|
546
|
-
},
|
|
547
|
-
hashes: {
|
|
548
|
-
summary: summaryHash,
|
|
549
|
-
persistedSummary: computedSummaryHash,
|
|
550
|
-
},
|
|
551
|
-
relativePaths,
|
|
552
|
-
artifactRefs,
|
|
553
|
-
evidenceRefs,
|
|
554
|
-
validationRefs: decisionLogRefs,
|
|
555
|
-
redactionEvidence: operation.redactions ?? [],
|
|
556
|
-
checkpoint,
|
|
557
|
-
};
|
|
558
|
-
for (const [index, artifact] of artifactRefs.entries()) {
|
|
559
|
-
this.store.recordArtifact({
|
|
560
|
-
artifactId: `artifact-${safeId(operation.operationId)}-${index + 1}`,
|
|
561
|
-
jobId: operation.jobId,
|
|
562
|
-
kind: artifact.kind,
|
|
563
|
-
relativePath: artifact.relativePath,
|
|
564
|
-
sha256: artifact.sha256,
|
|
565
|
-
summary: artifact.publicSummary,
|
|
566
|
-
createdAt: operation.createdAt,
|
|
567
|
-
operationId: `${operation.operationId}:artifact:${index + 1}`,
|
|
568
|
-
idempotencyKey: operation.idempotencyKey ? `${operation.idempotencyKey}:artifact:${index + 1}` : undefined,
|
|
569
|
-
metadata: {
|
|
570
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
571
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
572
|
-
jobId: operation.jobId,
|
|
573
|
-
operationId: operation.operationId,
|
|
574
|
-
reasonCode,
|
|
575
|
-
hashes: artifact.sha256 ? { artifact: artifact.sha256 } : {},
|
|
576
|
-
relativePaths: [artifact.relativePath],
|
|
577
|
-
publicSummary: artifact.publicSummary,
|
|
578
|
-
},
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
this.store.recordStateSnapshot({
|
|
582
|
-
snapshotId: `snapshot-${safeId(operation.operationId)}`,
|
|
583
|
-
jobId: operation.jobId,
|
|
584
|
-
kind: 'context_compaction',
|
|
585
|
-
relativePath: artifactRefs.find((ref) => ref.kind === 'context_summary')?.relativePath,
|
|
586
|
-
sha256: summaryHash,
|
|
587
|
-
summary,
|
|
588
|
-
capturedAt: operation.createdAt,
|
|
589
|
-
operationId: operation.operationId,
|
|
590
|
-
idempotencyKey: operation.idempotencyKey,
|
|
591
|
-
metadata,
|
|
592
|
-
});
|
|
593
|
-
this.store.appendEvent({
|
|
594
|
-
eventId: `event-${safeId(operation.operationId)}-context-compacted`,
|
|
595
|
-
jobId: operation.jobId,
|
|
596
|
-
operationId: `${operation.operationId}:event`,
|
|
597
|
-
idempotencyKey: operation.idempotencyKey ? `${operation.idempotencyKey}:event` : undefined,
|
|
598
|
-
sequence: operation.sequence,
|
|
599
|
-
type: 'context_compacted',
|
|
600
|
-
summary: `Context compaction checkpoint ${checkpoint.checkpointId} persisted with ${relativePaths.length} public refs.`,
|
|
601
|
-
reasonCode,
|
|
602
|
-
createdAt: operation.createdAt,
|
|
603
|
-
metadata: {
|
|
604
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
605
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
606
|
-
jobId: operation.jobId,
|
|
607
|
-
operationId: operation.operationId,
|
|
608
|
-
reasonCode,
|
|
609
|
-
counts: metadata.counts,
|
|
610
|
-
hashes: metadata.hashes,
|
|
611
|
-
relativePaths,
|
|
612
|
-
artifactRefs: artifactRefs.map((ref) => ref.relativePath),
|
|
613
|
-
evidenceRefs,
|
|
614
|
-
publicSummary: summary,
|
|
615
|
-
},
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
applyCreateJob(operation, logMetadata) {
|
|
619
|
-
const job = requiredRecord(operation, 'job');
|
|
620
|
-
this.store.recordJob({
|
|
621
|
-
jobId: requiredString(job, 'jobId'),
|
|
622
|
-
kind: requiredString(job, 'kind'),
|
|
623
|
-
status: requiredString(job, 'status'),
|
|
624
|
-
createdAt: requiredString(job, 'createdAt'),
|
|
625
|
-
updatedAt: requiredString(job, 'updatedAt'),
|
|
626
|
-
summary: optionalString(job, 'summary'),
|
|
627
|
-
reasonCode: optionalString(job, 'reasonCode'),
|
|
628
|
-
nextRunAt: optionalString(job, 'nextRunAt'),
|
|
629
|
-
goalId: optionalString(job, 'goalId'),
|
|
630
|
-
loopId: optionalString(job, 'loopId'),
|
|
631
|
-
campaignId: optionalString(job, 'campaignId'),
|
|
632
|
-
operationId: operation.operationId,
|
|
633
|
-
idempotencyKey: operation.idempotencyKey,
|
|
634
|
-
metadata: sanitizeMetadataStrict({
|
|
635
|
-
...(optionalRecord(job, 'metadata') ?? optionalRecord(operation, 'metadata') ?? { ...logMetadata }),
|
|
636
|
-
...projectIdentityMetadata(this.projectIdentity),
|
|
637
|
-
}, 'job.metadata'),
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
applyUpdateJobStatus(operation) {
|
|
641
|
-
this.store.updateJobStatus({
|
|
642
|
-
jobId: operation.jobId,
|
|
643
|
-
status: requiredString(operation, 'status'),
|
|
644
|
-
updatedAt: operation.createdAt,
|
|
645
|
-
reasonCode: optionalString(operation, 'reasonCode'),
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
applyAppendEvent(operation) {
|
|
649
|
-
const event = requiredRecord(operation, 'event');
|
|
650
|
-
const eventType = requiredString(event, 'type');
|
|
651
|
-
if (eventType === CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE) {
|
|
652
|
-
this.applyProcessingOutputSummaryEvent(operation, event);
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
this.store.appendEvent({
|
|
656
|
-
eventId: requiredString(event, 'eventId'),
|
|
657
|
-
jobId: requiredString(event, 'jobId'),
|
|
658
|
-
operationId: operation.operationId,
|
|
659
|
-
idempotencyKey: operation.idempotencyKey,
|
|
660
|
-
sequence: operation.sequence,
|
|
661
|
-
type: eventType,
|
|
662
|
-
summary: requiredString(event, 'summary'),
|
|
663
|
-
reasonCode: optionalString(event, 'reasonCode'),
|
|
664
|
-
createdAt: requiredString(event, 'createdAt'),
|
|
665
|
-
metadata: sanitizeMetadataStrict(optionalRecord(event, 'metadata'), 'event.metadata'),
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
applyProcessingOutputSummaryEvent(operation, event) {
|
|
669
|
-
const summary = optionalRecord(event, 'processingOutputSummary') ?? event;
|
|
670
|
-
const executionId = optionalString(summary, 'executionId') ?? operation.operationId;
|
|
671
|
-
const jobId = optionalString(summary, 'jobId') ?? optionalString(event, 'jobId') ?? operation.jobId;
|
|
672
|
-
const publicSummary = optionalString(summary, 'publicSummary') ?? requiredString(event, 'summary');
|
|
673
|
-
const createdAt = optionalString(summary, 'createdAt') ?? optionalString(event, 'createdAt') ?? operation.createdAt;
|
|
674
|
-
const summaryMetadata = optionalRecord(summary, 'metadata');
|
|
675
|
-
const eventMetadata = optionalRecord(event, 'metadata');
|
|
676
|
-
this.store.recordProcessingOutputSummary({
|
|
677
|
-
eventId: optionalString(event, 'eventId'),
|
|
678
|
-
executionId,
|
|
679
|
-
jobId,
|
|
680
|
-
idempotencyKey: operation.idempotencyKey,
|
|
681
|
-
goalId: optionalString(summary, 'goalId') ?? optionalString(event, 'goalId'),
|
|
682
|
-
loopId: optionalString(summary, 'loopId') ?? optionalString(event, 'loopId'),
|
|
683
|
-
publicSummary,
|
|
684
|
-
reasonCode: optionalString(summary, 'reasonCode') ?? optionalString(event, 'reasonCode'),
|
|
685
|
-
createdAt,
|
|
686
|
-
source: optionalString(summary, 'source') ?? optionalString(event, 'source'),
|
|
687
|
-
metadata: {
|
|
688
|
-
...(eventMetadata ?? {}),
|
|
689
|
-
...(summaryMetadata ?? {}),
|
|
690
|
-
},
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
applyAppendReceipt(operation) {
|
|
694
|
-
const receipt = requiredRecord(operation, 'receipt');
|
|
695
|
-
const receiptOperationId = optionalString(receipt, 'operationId') ?? operation.operationId;
|
|
696
|
-
this.store.appendReceipt({
|
|
697
|
-
receiptId: requiredString(receipt, 'receiptId'),
|
|
698
|
-
jobId: requiredString(receipt, 'jobId'),
|
|
699
|
-
operationId: receiptOperationId,
|
|
700
|
-
idempotencyKey: operation.idempotencyKey,
|
|
701
|
-
status: requiredString(receipt, 'status'),
|
|
702
|
-
reasonCode: optionalString(receipt, 'reasonCode'),
|
|
703
|
-
appliedOperationIds: arrayOfStrings(receipt.appliedOperationIds),
|
|
704
|
-
ignoredOperationIds: arrayOfStrings(receipt.ignoredOperationIds),
|
|
705
|
-
createdAt: requiredString(receipt, 'createdAt'),
|
|
706
|
-
metadata: sanitizeMetadataStrict(optionalRecord(receipt, 'metadata'), 'receipt.metadata'),
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
applyUpsertGoalCampaign(operation) {
|
|
710
|
-
const campaign = requiredRecord(operation, 'campaign');
|
|
711
|
-
this.store.upsertGoalCampaign({
|
|
712
|
-
campaignId: requiredString(campaign, 'campaignId'),
|
|
713
|
-
jobId: requiredString(campaign, 'jobId'),
|
|
714
|
-
status: requiredString(campaign, 'status'),
|
|
715
|
-
title: optionalString(campaign, 'title'),
|
|
716
|
-
publicSummary: optionalString(campaign, 'publicSummary'),
|
|
717
|
-
reasonCode: optionalString(campaign, 'reasonCode'),
|
|
718
|
-
createdAt: requiredString(campaign, 'createdAt'),
|
|
719
|
-
updatedAt: requiredString(campaign, 'updatedAt'),
|
|
720
|
-
operationId: operation.operationId,
|
|
721
|
-
idempotencyKey: operation.idempotencyKey,
|
|
722
|
-
metadata: sanitizeMetadataStrict(optionalRecord(campaign, 'metadata'), 'campaign.metadata'),
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
applyUpsertGoalLedgerItem(operation) {
|
|
726
|
-
const item = requiredRecord(operation, 'item');
|
|
727
|
-
this.store.upsertGoalLedgerItem({
|
|
728
|
-
itemId: requiredString(item, 'itemId'),
|
|
729
|
-
campaignId: requiredString(item, 'campaignId'),
|
|
730
|
-
jobId: requiredString(item, 'jobId'),
|
|
731
|
-
itemType: requiredString(item, 'type'),
|
|
732
|
-
status: requiredString(item, 'status'),
|
|
733
|
-
summary: requiredString(item, 'publicSummary'),
|
|
734
|
-
reasonCode: optionalString(item, 'reasonCode'),
|
|
735
|
-
createdAt: operation.createdAt,
|
|
736
|
-
updatedAt: requiredString(item, 'updatedAt'),
|
|
737
|
-
operationId: operation.operationId,
|
|
738
|
-
idempotencyKey: operation.idempotencyKey,
|
|
739
|
-
metadata: sanitizeMetadataStrict(optionalRecord(item, 'metadata'), 'item.metadata'),
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
applyUpsertVerifier(operation) {
|
|
743
|
-
const verifier = requiredRecord(operation, 'verifier');
|
|
744
|
-
this.store.upsertGoalVerifier({
|
|
745
|
-
verifierId: requiredString(verifier, 'verifierId'),
|
|
746
|
-
campaignId: requiredString(verifier, 'campaignId'),
|
|
747
|
-
jobId: requiredString(verifier, 'jobId'),
|
|
748
|
-
kind: requiredString(verifier, 'kind'),
|
|
749
|
-
status: requiredString(verifier, 'status'),
|
|
750
|
-
summary: optionalString(verifier, 'publicSummary'),
|
|
751
|
-
reasonCode: optionalString(verifier, 'reasonCode'),
|
|
752
|
-
lastCheckedAt: optionalString(verifier, 'lastCheckedAt'),
|
|
753
|
-
operationId: operation.operationId,
|
|
754
|
-
idempotencyKey: operation.idempotencyKey,
|
|
755
|
-
metadata: sanitizeMetadataStrict(optionalRecord(verifier, 'metadata'), 'verifier.metadata'),
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
applyUpsertReviewGate(operation) {
|
|
759
|
-
const reviewGate = requiredRecord(operation, 'reviewGate');
|
|
760
|
-
this.store.upsertGoalReviewGate({
|
|
761
|
-
gateId: requiredString(reviewGate, 'gateId'),
|
|
762
|
-
campaignId: requiredString(reviewGate, 'campaignId'),
|
|
763
|
-
jobId: requiredString(reviewGate, 'jobId'),
|
|
764
|
-
status: requiredString(reviewGate, 'status'),
|
|
765
|
-
summary: optionalString(reviewGate, 'publicSummary'),
|
|
766
|
-
required: optionalBoolean(reviewGate, 'required') ?? true,
|
|
767
|
-
reasonCode: optionalString(reviewGate, 'reasonCode'),
|
|
768
|
-
operationId: operation.operationId,
|
|
769
|
-
idempotencyKey: operation.idempotencyKey,
|
|
770
|
-
metadata: sanitizeMetadataStrict(optionalRecord(reviewGate, 'metadata'), 'reviewGate.metadata'),
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
applyOutbox(operation) {
|
|
774
|
-
const outbox = optionalRecord(operation, 'outbox') ?? operation;
|
|
775
|
-
this.store.enqueueOutbox({
|
|
776
|
-
outboxId: optionalString(outbox, 'outboxId') ?? `outbox-${safeId(operation.operationId)}`,
|
|
777
|
-
jobId: optionalString(outbox, 'jobId') ?? operation.jobId,
|
|
778
|
-
operationId: operation.operationId,
|
|
779
|
-
idempotencyKey: operation.idempotencyKey,
|
|
780
|
-
type: optionalString(outbox, 'type') === operation.type ? 'continuity_event' : requiredString(outbox, 'type'),
|
|
781
|
-
status: optionalString(outbox, 'status'),
|
|
782
|
-
payload: sanitizeMetadataStrict(optionalRecord(outbox, 'payload') ?? optionalRecord(operation, 'metadata') ?? {}, 'outbox.payload'),
|
|
783
|
-
retryCount: optionalNumber(outbox, 'retryCount'),
|
|
784
|
-
nextAttemptAt: optionalString(outbox, 'nextAttemptAt'),
|
|
785
|
-
lastErrorCode: optionalString(outbox, 'lastErrorCode'),
|
|
786
|
-
createdAt: optionalString(outbox, 'createdAt') ?? operation.createdAt,
|
|
787
|
-
updatedAt: optionalString(outbox, 'updatedAt') ?? operation.createdAt,
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
applyEnqueueExecutionAttempt(operation) {
|
|
791
|
-
const attempt = optionalRecord(operation, 'attempt') ?? operation;
|
|
792
|
-
const metadata = sanitizeMetadataStrict(optionalRecord(attempt, 'metadata') ?? optionalRecord(operation, 'metadata'), 'executionAttempt.metadata');
|
|
793
|
-
this.store.enqueueHeadlessExecutionAttempt({
|
|
794
|
-
attemptId: requiredString(attempt, 'attemptId'),
|
|
795
|
-
executionId: requiredString(attempt, 'executionId'),
|
|
796
|
-
jobId: optionalString(attempt, 'jobId') ?? operation.jobId,
|
|
797
|
-
directiveHash: requiredString(attempt, 'directiveHash'),
|
|
798
|
-
directiveKind: requiredString(attempt, 'directiveKind'),
|
|
799
|
-
commandClass: requiredString(attempt, 'commandClass'),
|
|
800
|
-
targetKind: requiredString(attempt, 'targetKind'),
|
|
801
|
-
targetRef: requiredString(attempt, 'targetRef'),
|
|
802
|
-
storyId: optionalString(attempt, 'storyId'),
|
|
803
|
-
epicId: optionalString(attempt, 'epicId'),
|
|
804
|
-
runtimeId: optionalString(attempt, 'runtimeId'),
|
|
805
|
-
modelId: optionalString(attempt, 'modelId'),
|
|
806
|
-
runtimeSource: optionalString(attempt, 'runtimeSource'),
|
|
807
|
-
status: optionalString(attempt, 'status'),
|
|
808
|
-
reasonCode: optionalString(attempt, 'reasonCode'),
|
|
809
|
-
publicSummary: optionalString(attempt, 'publicSummary'),
|
|
810
|
-
enqueuedAt: optionalString(attempt, 'enqueuedAt') ?? operation.createdAt,
|
|
811
|
-
notBeforeAt: optionalString(attempt, 'notBeforeAt'),
|
|
812
|
-
retryCount: optionalNumber(attempt, 'retryCount'),
|
|
813
|
-
maxAttempts: optionalNumber(attempt, 'maxAttempts'),
|
|
814
|
-
operationId: operation.operationId,
|
|
815
|
-
idempotencyKey: operation.idempotencyKey,
|
|
816
|
-
counts: optionalRecord(attempt, 'counts'),
|
|
817
|
-
hashes: optionalRecord(attempt, 'hashes'),
|
|
818
|
-
redactionSummary: Array.isArray(attempt.redactionSummary) ? attempt.redactionSummary : undefined,
|
|
819
|
-
relativePaths: arrayOfStrings(attempt.relativePaths),
|
|
820
|
-
metadata,
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
applyCompleteExecutionAttempt(operation) {
|
|
824
|
-
const completion = optionalRecord(operation, 'completion') ?? operation;
|
|
825
|
-
this.store.completeHeadlessExecutionAttempt({
|
|
826
|
-
attemptId: requiredString(completion, 'attemptId'),
|
|
827
|
-
executionId: optionalString(completion, 'executionId'),
|
|
828
|
-
jobId: optionalString(completion, 'jobId') ?? operation.jobId,
|
|
829
|
-
leaseId: optionalString(completion, 'leaseId'),
|
|
830
|
-
runnerId: optionalString(completion, 'runnerId'),
|
|
831
|
-
status: requiredString(completion, 'status'),
|
|
832
|
-
completedAt: optionalString(completion, 'completedAt') ?? operation.createdAt,
|
|
833
|
-
durationMs: optionalNumber(completion, 'durationMs'),
|
|
834
|
-
reasonCode: optionalString(completion, 'reasonCode'),
|
|
835
|
-
publicSummary: optionalString(completion, 'publicSummary'),
|
|
836
|
-
notBeforeAt: optionalString(completion, 'notBeforeAt'),
|
|
837
|
-
retryCount: optionalNumber(completion, 'retryCount'),
|
|
838
|
-
counts: optionalRecord(completion, 'counts'),
|
|
839
|
-
hashes: optionalRecord(completion, 'hashes'),
|
|
840
|
-
redactionSummary: Array.isArray(completion.redactionSummary) ? completion.redactionSummary : undefined,
|
|
841
|
-
relativePaths: arrayOfStrings(completion.relativePaths),
|
|
842
|
-
metadata: sanitizeMetadataStrict(optionalRecord(completion, 'metadata'), 'executionCompletion.metadata'),
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
applyAppendExecutionReceipt(operation) {
|
|
846
|
-
const receipt = requiredRecord(operation, 'receipt');
|
|
847
|
-
this.store.recordHeadlessExecutionReceipt({
|
|
848
|
-
receiptId: requiredString(receipt, 'receiptId'),
|
|
849
|
-
attemptId: requiredString(receipt, 'attemptId'),
|
|
850
|
-
executionId: requiredString(receipt, 'executionId'),
|
|
851
|
-
jobId: optionalString(receipt, 'jobId') ?? operation.jobId,
|
|
852
|
-
status: requiredString(receipt, 'status'),
|
|
853
|
-
reasonCode: optionalString(receipt, 'reasonCode'),
|
|
854
|
-
createdAt: optionalString(receipt, 'createdAt') ?? operation.createdAt,
|
|
855
|
-
operationId: operation.operationId,
|
|
856
|
-
idempotencyKey: operation.idempotencyKey,
|
|
857
|
-
counts: optionalRecord(receipt, 'counts'),
|
|
858
|
-
hashes: optionalRecord(receipt, 'hashes'),
|
|
859
|
-
redactionSummary: Array.isArray(receipt.redactionSummary) ? receipt.redactionSummary : undefined,
|
|
860
|
-
relativePaths: arrayOfStrings(receipt.relativePaths),
|
|
861
|
-
metadata: sanitizeMetadataStrict(optionalRecord(receipt, 'metadata'), 'executionReceipt.metadata'),
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
applyAppendExecutionSummary(operation) {
|
|
865
|
-
const summary = optionalRecord(operation, 'summary') ?? operation;
|
|
866
|
-
this.store.recordHeadlessExecutionSummary({
|
|
867
|
-
eventId: optionalString(summary, 'eventId'),
|
|
868
|
-
attemptId: requiredString(summary, 'attemptId'),
|
|
869
|
-
executionId: requiredString(summary, 'executionId'),
|
|
870
|
-
jobId: optionalString(summary, 'jobId') ?? operation.jobId,
|
|
871
|
-
publicSummary: requiredString(summary, 'publicSummary'),
|
|
872
|
-
reasonCode: optionalString(summary, 'reasonCode'),
|
|
873
|
-
createdAt: optionalString(summary, 'createdAt') ?? operation.createdAt,
|
|
874
|
-
runtimeId: optionalString(summary, 'runtimeId'),
|
|
875
|
-
modelId: optionalString(summary, 'modelId'),
|
|
876
|
-
durationMs: optionalNumber(summary, 'durationMs'),
|
|
877
|
-
counts: optionalRecord(summary, 'counts'),
|
|
878
|
-
hashes: optionalRecord(summary, 'hashes'),
|
|
879
|
-
redactionSummary: Array.isArray(summary.redactionSummary) ? summary.redactionSummary : undefined,
|
|
880
|
-
relativePaths: arrayOfStrings(summary.relativePaths),
|
|
881
|
-
metadata: sanitizeMetadataStrict(optionalRecord(summary, 'metadata'), 'executionSummary.metadata'),
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
recordAppliedAudit(operation) {
|
|
885
|
-
this.store.recordOperationAudit({
|
|
886
|
-
receiptId: `receipt-${safeId(operation.operationId)}`,
|
|
887
|
-
jobId: operation.jobId,
|
|
888
|
-
operationId: operation.operationId,
|
|
889
|
-
idempotencyKey: `audit-${operation.idempotencyKey ?? operation.operationId}`,
|
|
890
|
-
status: 'applied',
|
|
891
|
-
reasonCode: CONTINUITY_CLIENT_REASON_CODES.OK,
|
|
892
|
-
appliedOperationIds: [operation.operationId],
|
|
893
|
-
createdAt: operation.createdAt,
|
|
894
|
-
metadata: {
|
|
895
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
896
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
897
|
-
jobId: operation.jobId,
|
|
898
|
-
operationId: operation.operationId,
|
|
899
|
-
status: 'applied',
|
|
900
|
-
reasonCode: CONTINUITY_CLIENT_REASON_CODES.OK,
|
|
901
|
-
},
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
recordIgnoredAuditIfPossible(operation, reasonCode) {
|
|
905
|
-
if (!this.store.jobExists(operation.jobId))
|
|
906
|
-
return;
|
|
907
|
-
this.store.recordOperationAudit({
|
|
908
|
-
receiptId: `receipt-${safeId(operation.operationId)}-ignored`,
|
|
909
|
-
jobId: operation.jobId,
|
|
910
|
-
operationId: operation.operationId,
|
|
911
|
-
idempotencyKey: `audit-${operation.idempotencyKey ?? operation.operationId}-ignored`,
|
|
912
|
-
status: 'ignored',
|
|
913
|
-
reasonCode,
|
|
914
|
-
ignoredOperationIds: [operation.operationId],
|
|
915
|
-
createdAt: operation.createdAt,
|
|
916
|
-
metadata: {
|
|
917
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
918
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
919
|
-
jobId: operation.jobId,
|
|
920
|
-
operationId: operation.operationId,
|
|
921
|
-
status: 'ignored',
|
|
922
|
-
reasonCode,
|
|
923
|
-
},
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
function toSnapshotJobSummary(job) {
|
|
928
|
-
const counts = extractPublicJobCounts(job.metadata_json, job.iteration);
|
|
929
|
-
const rawMetadata = parsePublicMetadata(job.metadata_json);
|
|
930
|
-
const metadata = normalizeSnapshotRecord(rawMetadata);
|
|
931
|
-
// P195.04: read the additive stage projection from the raw metadata so a bounded
|
|
932
|
-
// `stageHistory` array survives the 20-key snapshot normalization; values are
|
|
933
|
-
// re-validated against the public stage allow-list and re-bounded here.
|
|
934
|
-
const currentStage = isContinuityPublicStage(rawMetadata.currentStage) ? rawMetadata.currentStage : undefined;
|
|
935
|
-
const stageHistory = sanitizeContinuityStageHistory(rawMetadata.stageHistory);
|
|
936
|
-
const iteration = normalizeContinuityStageIteration(rawMetadata.iteration)
|
|
937
|
-
?? normalizeContinuityStageIteration(job.iteration);
|
|
938
|
-
addFiniteCount(counts, 'goalLedgerItems', 'goal_ledger_item_count' in job ? job.goal_ledger_item_count : undefined);
|
|
939
|
-
addFiniteCount(counts, 'blockedGoalLedgerItems', 'blocked_goal_ledger_item_count' in job ? job.blocked_goal_ledger_item_count : undefined);
|
|
940
|
-
addFiniteCount(counts, 'securityIssuesBlocked', 'security_issue_blocked_count' in job ? job.security_issue_blocked_count : undefined);
|
|
941
|
-
addFiniteCount(counts, 'goalVerifiers', 'goal_verifier_count' in job ? job.goal_verifier_count : undefined);
|
|
942
|
-
addFiniteCount(counts, 'failedVerifiers', 'failed_verifier_count' in job ? job.failed_verifier_count : undefined);
|
|
943
|
-
addFiniteCount(counts, 'unavailableVerifiers', 'unavailable_verifier_count' in job ? job.unavailable_verifier_count : undefined);
|
|
944
|
-
addFiniteCount(counts, 'goalReviewGates', 'goal_review_gate_count' in job ? job.goal_review_gate_count : undefined);
|
|
945
|
-
addFiniteCount(counts, 'failedRequiredReviewGates', 'failed_required_review_gate_count' in job ? job.failed_required_review_gate_count : undefined);
|
|
946
|
-
return {
|
|
947
|
-
jobId: boundPublicText(job.job_id, 80),
|
|
948
|
-
kind: boundPublicText(job.kind, 40),
|
|
949
|
-
status: boundPublicText(job.status, 40),
|
|
950
|
-
publicSummary: boundPublicText(job.summary ?? 'No public summary recorded.', PUBLIC_TEXT_MAX_CHARS),
|
|
951
|
-
createdAt: boundPublicText(job.created_at, 80),
|
|
952
|
-
updatedAt: boundPublicText(job.updated_at, 80),
|
|
953
|
-
...(job.next_run_at ? { nextRunAt: boundPublicText(job.next_run_at, 80) } : {}),
|
|
954
|
-
...(job.reason_code ? { reasonCode: boundPublicText(job.reason_code, 80) } : {}),
|
|
955
|
-
...(Object.keys(counts).length > 0 ? { counts } : {}),
|
|
956
|
-
...(currentStage ? { currentStage } : {}),
|
|
957
|
-
...(stageHistory.length > 0 ? { stageHistory } : {}),
|
|
958
|
-
...(iteration !== undefined ? { iteration } : {}),
|
|
959
|
-
...extractHeadlessJobMetadata(metadata),
|
|
960
|
-
publicSafe: true,
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
function toPortalSyncSnapshotSummary(input) {
|
|
964
|
-
const counts = {
|
|
965
|
-
jobs: input.counts.jobs ?? input.jobCount,
|
|
966
|
-
dueJobs: input.counts.dueJobs ?? input.dueJobCount,
|
|
967
|
-
outboxPending: input.outboxPending,
|
|
968
|
-
redactions: input.redactions.length,
|
|
969
|
-
};
|
|
970
|
-
const hashInput = stableStringify({ generatedAt: input.generatedAt, counts });
|
|
971
|
-
return {
|
|
972
|
-
syncVersion: CONTINUITY_CLIENT_PORTAL_SYNC_ENVELOPE_VERSION,
|
|
973
|
-
transportKind: 'client_snapshot',
|
|
974
|
-
snapshotId: `snapshot-${safeId(sha256Hex(hashInput).replace(/^sha256:/, '').slice(0, 16))}`,
|
|
975
|
-
generatedAt: boundPublicText(input.generatedAt, 80),
|
|
976
|
-
publicSummary: `Client snapshot contains ${counts.jobs} public jobs and ${counts.outboxPending} pending outbox records.`,
|
|
977
|
-
counts,
|
|
978
|
-
hashes: { snapshot: sha256Hex(hashInput) },
|
|
979
|
-
redactionEvidence: input.redactions.slice(0, 20),
|
|
980
|
-
publicSafe: true,
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
function extractHeadlessJobMetadata(metadata) {
|
|
984
|
-
const runtimeSource = metadata.runtimeSource === 'explicit'
|
|
985
|
-
|| metadata.runtimeSource === 'origin'
|
|
986
|
-
|| metadata.runtimeSource === 'default'
|
|
987
|
-
|| metadata.runtimeSource === 'fallback'
|
|
988
|
-
|| metadata.runtimeSource === 'unspecified'
|
|
989
|
-
? metadata.runtimeSource
|
|
990
|
-
: undefined;
|
|
991
|
-
return {
|
|
992
|
-
...safeMetadataString(metadata, 'runtimeId', 80),
|
|
993
|
-
...safeMetadataString(metadata, 'modelId', 120),
|
|
994
|
-
...(runtimeSource ? { runtimeSource } : {}),
|
|
995
|
-
...safeMetadataString(metadata, 'headlessRuntime', 80),
|
|
996
|
-
...safeMetadataString(metadata, 'headlessModel', 120),
|
|
997
|
-
...safeMetadataString(metadata, 'headlessRuntimeReasonCode', 100),
|
|
998
|
-
...safeMetadataString(metadata, 'originRuntime', 80),
|
|
999
|
-
...safeMetadataString(metadata, 'originModel', 120),
|
|
1000
|
-
...safeMetadataString(metadata, 'workspaceId', 120),
|
|
1001
|
-
...safeMetadataString(metadata, 'workspaceLabel', 80),
|
|
1002
|
-
...safeMetadataString(metadata, 'projectId', 120),
|
|
1003
|
-
...safeMetadataString(metadata, 'projectLabel', 80),
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
function projectIdentityMetadata(identity) {
|
|
1007
|
-
return identity
|
|
1008
|
-
? {
|
|
1009
|
-
workspaceId: identity.workspaceId,
|
|
1010
|
-
workspaceLabel: identity.workspaceLabel,
|
|
1011
|
-
projectId: identity.projectId,
|
|
1012
|
-
projectLabel: identity.projectLabel,
|
|
1013
|
-
}
|
|
1014
|
-
: {};
|
|
1015
|
-
}
|
|
1016
|
-
function safeMetadataString(metadata, key, maxChars) {
|
|
1017
|
-
const value = metadata[key];
|
|
1018
|
-
return typeof value === 'string'
|
|
1019
|
-
? { [key]: boundPublicText(value, maxChars) }
|
|
1020
|
-
: {};
|
|
1021
|
-
}
|
|
1022
|
-
function toProcessingSummaryRef(row) {
|
|
1023
|
-
const metadata = normalizeSnapshotRecord(parsePublicMetadata(row.metadata_json));
|
|
1024
|
-
const goalId = typeof metadata.goalId === 'string' ? boundPublicText(metadata.goalId, 100) : undefined;
|
|
1025
|
-
const loopId = typeof metadata.loopId === 'string' ? boundPublicText(metadata.loopId, 100) : undefined;
|
|
1026
|
-
return {
|
|
1027
|
-
eventId: boundPublicText(row.event_id, 120),
|
|
1028
|
-
jobId: boundPublicText(row.job_id, 100),
|
|
1029
|
-
...(goalId ? { goalId } : {}),
|
|
1030
|
-
...(loopId ? { loopId } : {}),
|
|
1031
|
-
executionId: boundPublicText(row.execution_id, 120),
|
|
1032
|
-
publicSummary: boundPublicText(row.public_summary, PUBLIC_TEXT_MAX_CHARS),
|
|
1033
|
-
...(row.reason_code ? { reasonCode: boundPublicText(row.reason_code, 80) } : {}),
|
|
1034
|
-
createdAt: boundPublicText(row.created_at, 80),
|
|
1035
|
-
cursor: boundPublicText(row.cursor, 260),
|
|
1036
|
-
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
1037
|
-
publicSafe: true,
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
function toHeadlessExecutionAttemptRef(row) {
|
|
1041
|
-
return {
|
|
1042
|
-
attemptId: boundPublicText(row.attempt_id, 120),
|
|
1043
|
-
executionId: boundPublicText(row.execution_id, 120),
|
|
1044
|
-
jobId: boundPublicText(row.job_id, 100),
|
|
1045
|
-
status: boundPublicText(row.status, 40),
|
|
1046
|
-
...(row.reason_code ? { reasonCode: boundPublicText(row.reason_code, 80) } : {}),
|
|
1047
|
-
...(row.runtime_id ? { runtimeId: boundPublicText(row.runtime_id, 80) } : {}),
|
|
1048
|
-
...(row.model_id ? { modelId: boundPublicText(row.model_id, 120) } : {}),
|
|
1049
|
-
retryCount: Math.max(0, Math.floor(row.retry_count)),
|
|
1050
|
-
attemptCount: Math.max(0, Math.floor(row.attempt_count)),
|
|
1051
|
-
maxAttempts: Math.max(1, Math.floor(row.max_attempts)),
|
|
1052
|
-
queuePriority: Math.max(-100, Math.min(100, Math.floor(row.queue_priority))),
|
|
1053
|
-
queueScope: row.queue_scope === 'workspace' ? 'workspace' : 'project',
|
|
1054
|
-
...(typeof row.duration_ms === 'number' && Number.isFinite(row.duration_ms) ? { durationMs: Math.max(0, Math.floor(row.duration_ms)) } : {}),
|
|
1055
|
-
...optionalPublicRecord('counts', parsePublicMetadata(row.counts_json)),
|
|
1056
|
-
...optionalPublicRecord('hashes', parsePublicMetadata(row.hashes_json)),
|
|
1057
|
-
...optionalPublicArray('redactionSummary', row.redaction_summary_json),
|
|
1058
|
-
...(row.lease_id ? { leaseId: boundPublicText(row.lease_id, 120) } : {}),
|
|
1059
|
-
...(row.runner_id ? { runnerId: boundPublicText(row.runner_id, 120) } : {}),
|
|
1060
|
-
...(row.lease_expires_at ? { leaseExpiresAt: boundPublicText(row.lease_expires_at, 80) } : {}),
|
|
1061
|
-
...(row.heartbeat_at ? { heartbeatAt: boundPublicText(row.heartbeat_at, 80) } : {}),
|
|
1062
|
-
publicSummary: boundPublicText(row.public_summary ?? 'No public headless execution summary recorded.', PUBLIC_TEXT_MAX_CHARS),
|
|
1063
|
-
enqueuedAt: boundPublicText(row.enqueued_at, 80),
|
|
1064
|
-
...(row.not_before_at ? { notBeforeAt: boundPublicText(row.not_before_at, 80) } : {}),
|
|
1065
|
-
...(row.next_retry_at ? { nextRetryAt: boundPublicText(row.next_retry_at, 80) } : {}),
|
|
1066
|
-
...(row.dead_lettered_at ? { deadLetteredAt: boundPublicText(row.dead_lettered_at, 80) } : {}),
|
|
1067
|
-
...(row.dead_letter_reason_code ? { deadLetterReasonCode: boundPublicText(row.dead_letter_reason_code, 80) } : {}),
|
|
1068
|
-
publicSafe: true,
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
|
-
function addFiniteCount(counts, key, value) {
|
|
1072
|
-
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
1073
|
-
counts[key] = Math.floor(value);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
function extractPublicJobCounts(metadataJson, iteration) {
|
|
1077
|
-
const counts = {};
|
|
1078
|
-
if (metadataJson) {
|
|
1079
|
-
try {
|
|
1080
|
-
const parsed = JSON.parse(metadataJson);
|
|
1081
|
-
const metadataCounts = isRecord(parsed) && isRecord(parsed.counts) ? parsed.counts : undefined;
|
|
1082
|
-
if (metadataCounts) {
|
|
1083
|
-
for (const [key, value] of Object.entries(metadataCounts)) {
|
|
1084
|
-
if (typeof value === 'number' && Number.isFinite(value))
|
|
1085
|
-
counts[key] = Math.max(0, Math.floor(value));
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
catch {
|
|
1090
|
-
// Corrupt metadata JSON is already guarded by SQLite json_valid; ignore any
|
|
1091
|
-
// unexpected parse failure so snapshots remain bounded and public-safe.
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
if (typeof iteration === 'number' && Number.isFinite(iteration))
|
|
1095
|
-
counts.iteration = Math.max(0, Math.floor(iteration));
|
|
1096
|
-
return counts;
|
|
1097
|
-
}
|
|
1098
|
-
function toCompactContextSnapshot(rows) {
|
|
1099
|
-
const summaries = rows.map(toCompactContextRef).filter((item) => Boolean(item));
|
|
1100
|
-
return {
|
|
1101
|
-
preferredSource: summaries.length > 0 ? 'compact_summary' : 'recent_events',
|
|
1102
|
-
summaries,
|
|
1103
|
-
publicSafe: true,
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
function toCompactContextRef(row) {
|
|
1107
|
-
const metadata = parsePublicMetadata(row.metadata_json);
|
|
1108
|
-
const hashes = isRecord(metadata.hashes) ? metadata.hashes : {};
|
|
1109
|
-
const summaryHash = typeof hashes.summary === 'string' ? hashes.summary : row.sha256 ?? undefined;
|
|
1110
|
-
const reasonCode = typeof metadata.reasonCode === 'string' ? boundPublicText(metadata.reasonCode, 80) : undefined;
|
|
1111
|
-
return {
|
|
1112
|
-
snapshotId: boundPublicText(row.snapshot_id, 120),
|
|
1113
|
-
...(row.job_id ? { jobId: boundPublicText(row.job_id, 100) } : {}),
|
|
1114
|
-
...(row.operation_id ? { operationId: boundPublicText(row.operation_id, 120) } : {}),
|
|
1115
|
-
capturedAt: boundPublicText(row.captured_at, 80),
|
|
1116
|
-
publicSummary: boundPublicText(row.summary ?? 'Context compacted with public-safe refs.', COMPACTION_SUMMARY_MAX_CHARS),
|
|
1117
|
-
...(summaryHash ? { summaryHash: boundPublicText(summaryHash, 120) } : {}),
|
|
1118
|
-
...(row.relative_path ? { relativePath: boundPublicText(row.relative_path, 240) } : {}),
|
|
1119
|
-
retainedRefs: normalizeSnapshotRefArray(metadata.relativePaths),
|
|
1120
|
-
artifactRefs: normalizeSnapshotUnknownArray(metadata.artifactRefs),
|
|
1121
|
-
evidenceRefs: normalizeSnapshotUnknownArray(metadata.evidenceRefs),
|
|
1122
|
-
...(isRecord(metadata.checkpoint) ? { checkpoint: normalizeSnapshotRecord(metadata.checkpoint) } : {}),
|
|
1123
|
-
...(reasonCode ? { reasonCode } : {}),
|
|
1124
|
-
publicSafe: true,
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
function parsePublicMetadata(metadataJson) {
|
|
1128
|
-
if (!metadataJson)
|
|
1129
|
-
return {};
|
|
1130
|
-
try {
|
|
1131
|
-
const parsed = JSON.parse(metadataJson);
|
|
1132
|
-
return isRecord(parsed) ? parsed : {};
|
|
1133
|
-
}
|
|
1134
|
-
catch {
|
|
1135
|
-
return {};
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
function optionalPublicRecord(key, value) {
|
|
1139
|
-
const normalized = normalizeSnapshotRecord(value);
|
|
1140
|
-
return Object.keys(normalized).length > 0 ? { [key]: normalized } : {};
|
|
1141
|
-
}
|
|
1142
|
-
function optionalPublicArray(key, json) {
|
|
1143
|
-
if (!json)
|
|
1144
|
-
return {};
|
|
1145
|
-
try {
|
|
1146
|
-
const parsed = JSON.parse(json);
|
|
1147
|
-
const normalized = normalizeSnapshotUnknownArray(parsed);
|
|
1148
|
-
return normalized.length > 0 ? { [key]: normalized } : {};
|
|
1149
|
-
}
|
|
1150
|
-
catch {
|
|
1151
|
-
return {};
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
function normalizeSnapshotRefArray(value) {
|
|
1155
|
-
return Array.isArray(value)
|
|
1156
|
-
? value.filter((item) => typeof item === 'string' && item.length > 0).map((item) => boundPublicText(item, 240)).slice(0, COMPACTION_RELATIVE_PATH_LIMIT)
|
|
1157
|
-
: [];
|
|
1158
|
-
}
|
|
1159
|
-
function normalizeSnapshotUnknownArray(value) {
|
|
1160
|
-
if (!Array.isArray(value))
|
|
1161
|
-
return [];
|
|
1162
|
-
return value.map(normalizeSnapshotValue).filter((item) => item !== undefined).slice(0, COMPACTION_RELATIVE_PATH_LIMIT);
|
|
1163
|
-
}
|
|
1164
|
-
function normalizeSnapshotRecord(value) {
|
|
1165
|
-
const output = {};
|
|
1166
|
-
for (const [key, child] of Object.entries(value).slice(0, 20)) {
|
|
1167
|
-
if (UNSAFE_METADATA_KEY_PATTERN.test(key))
|
|
1168
|
-
continue;
|
|
1169
|
-
const normalized = normalizeSnapshotValue(child);
|
|
1170
|
-
if (normalized !== undefined)
|
|
1171
|
-
output[boundPublicText(key, 80)] = normalized;
|
|
1172
|
-
}
|
|
1173
|
-
return output;
|
|
1174
|
-
}
|
|
1175
|
-
function normalizeSnapshotValue(value) {
|
|
1176
|
-
const reason = getUnsafeMetadataValueReason(value);
|
|
1177
|
-
if (reason)
|
|
1178
|
-
return undefined;
|
|
1179
|
-
if (typeof value === 'string')
|
|
1180
|
-
return boundPublicText(value, 240);
|
|
1181
|
-
if (typeof value === 'number' && Number.isFinite(value))
|
|
1182
|
-
return Math.floor(value);
|
|
1183
|
-
if (typeof value === 'boolean' || value === null)
|
|
1184
|
-
return value;
|
|
1185
|
-
if (Array.isArray(value))
|
|
1186
|
-
return value.map(normalizeSnapshotValue).filter((item) => item !== undefined).slice(0, 20);
|
|
1187
|
-
if (isRecord(value))
|
|
1188
|
-
return normalizeSnapshotRecord(value);
|
|
1189
|
-
return undefined;
|
|
1190
|
-
}
|
|
1191
|
-
function normalizeStateUpdate(update) {
|
|
1192
|
-
if (!isRecord(update))
|
|
1193
|
-
return [];
|
|
1194
|
-
if (Array.isArray(update.operationLogs))
|
|
1195
|
-
return update.operationLogs.filter(isRecord);
|
|
1196
|
-
if (isRecord(update.operationLog))
|
|
1197
|
-
return [update.operationLog];
|
|
1198
|
-
if (Array.isArray(update.operations))
|
|
1199
|
-
return [update];
|
|
1200
|
-
return [];
|
|
1201
|
-
}
|
|
1202
|
-
function planOperationLog(log) {
|
|
1203
|
-
const reasonCodes = [];
|
|
1204
|
-
const redactions = [];
|
|
1205
|
-
const skippedOperations = [];
|
|
1206
|
-
const ignoredOperationIds = [];
|
|
1207
|
-
const accepted = [];
|
|
1208
|
-
const seen = new Map();
|
|
1209
|
-
if (log.schemaVersion !== CONTINUITY_CLIENT_SCHEMA_VERSION) {
|
|
1210
|
-
throw new ContinuityClientStateStoreError('Invalid continuity operation log schema version.', [CONTINUITY_CLIENT_REASON_CODES.INVALID_SCHEMA_VERSION], { mutationBlocked: true });
|
|
1211
|
-
}
|
|
1212
|
-
if (log.contractVersion !== CONTINUITY_CLIENT_CONTRACT_VERSION) {
|
|
1213
|
-
throw new ContinuityClientStateStoreError('Invalid continuity operation log contract version.', [CONTINUITY_CLIENT_REASON_CODES.INVALID_CONTRACT_VERSION], { mutationBlocked: true });
|
|
1214
|
-
}
|
|
1215
|
-
if (log.publicSafe !== true) {
|
|
1216
|
-
throw new ContinuityClientStateStoreError('Continuity operation log is not marked public-safe.', [CONTINUITY_CLIENT_REASON_CODES.UNSAFE_FIELD_REJECTED], { mutationBlocked: true });
|
|
1217
|
-
}
|
|
1218
|
-
const logMetadata = sanitizeMetadataStrict(optionalRecord(log, 'metadata'), 'log.metadata');
|
|
1219
|
-
if (!Array.isArray(log.operations))
|
|
1220
|
-
return { operations: [], skippedOperations, ignoredOperationIds, reasonCodes, redactions, logMetadata };
|
|
1221
|
-
for (const rawOperation of [...log.operations].filter(isRecord).sort(compareOperationOrder)) {
|
|
1222
|
-
const operation = normalizeOperation(rawOperation);
|
|
1223
|
-
const canonical = stableStringify(operation);
|
|
1224
|
-
const duplicatePayload = seen.get(operation.operationId);
|
|
1225
|
-
if (duplicatePayload !== undefined) {
|
|
1226
|
-
if (duplicatePayload === canonical) {
|
|
1227
|
-
ignoredOperationIds.push(operation.operationId);
|
|
1228
|
-
reasonCodes.push(CONTINUITY_CLIENT_REASON_CODES.IDEMPOTENCY_REPLAY);
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1231
|
-
redactions.push({
|
|
1232
|
-
field: 'operationId',
|
|
1233
|
-
reason: 'unknown_required_operation',
|
|
1234
|
-
evidence: 'operation-denied',
|
|
1235
|
-
publicSafe: true,
|
|
1236
|
-
});
|
|
1237
|
-
throw new ContinuityClientStateStoreError('Duplicate continuity operation ID with conflicting payload.', [CONTINUITY_CLIENT_REASON_CODES.DUPLICATE_OPERATION_ID], { redactions, mutationBlocked: true });
|
|
1238
|
-
}
|
|
1239
|
-
seen.set(operation.operationId, canonical);
|
|
1240
|
-
sanitizeMetadataStrict(operation.metadata, 'operation.metadata');
|
|
1241
|
-
if (!KNOWN_OPERATION_TYPES.has(operation.type)) {
|
|
1242
|
-
const required = rawOperation.required === true;
|
|
1243
|
-
if (rawOperation.forwardCompatible === true && !required) {
|
|
1244
|
-
skippedOperations.push(operation);
|
|
1245
|
-
ignoredOperationIds.push(operation.operationId);
|
|
1246
|
-
reasonCodes.push(CONTINUITY_CLIENT_REASON_CODES.UNKNOWN_OPERATION_IGNORED);
|
|
1247
|
-
continue;
|
|
1248
|
-
}
|
|
1249
|
-
redactions.push({
|
|
1250
|
-
field: operation.type,
|
|
1251
|
-
reason: 'unknown_required_operation',
|
|
1252
|
-
evidence: 'operation-denied',
|
|
1253
|
-
publicSafe: true,
|
|
1254
|
-
});
|
|
1255
|
-
throw new ContinuityClientStateStoreError(`Unknown required continuity operation ${operation.type}.`, [CONTINUITY_CLIENT_REASON_CODES.UNKNOWN_OPERATION_REQUIRED], { redactions, mutationBlocked: true });
|
|
1256
|
-
}
|
|
1257
|
-
accepted.push(operation);
|
|
1258
|
-
}
|
|
1259
|
-
return { operations: accepted, skippedOperations, ignoredOperationIds, reasonCodes, redactions, logMetadata };
|
|
1260
|
-
}
|
|
1261
|
-
function normalizeOperation(raw) {
|
|
1262
|
-
if (raw.schemaVersion !== CONTINUITY_CLIENT_SCHEMA_VERSION) {
|
|
1263
|
-
throw new ContinuityClientStateStoreError('Invalid continuity operation schema version.', [CONTINUITY_CLIENT_REASON_CODES.INVALID_SCHEMA_VERSION], { mutationBlocked: true });
|
|
1264
|
-
}
|
|
1265
|
-
if (raw.contractVersion !== CONTINUITY_CLIENT_CONTRACT_VERSION) {
|
|
1266
|
-
throw new ContinuityClientStateStoreError('Invalid continuity operation contract version.', [CONTINUITY_CLIENT_REASON_CODES.INVALID_CONTRACT_VERSION], { mutationBlocked: true });
|
|
1267
|
-
}
|
|
1268
|
-
if (raw.publicSafe !== true) {
|
|
1269
|
-
throw new ContinuityClientStateStoreError('Continuity operation is not marked public-safe.', [CONTINUITY_CLIENT_REASON_CODES.UNSAFE_FIELD_REJECTED], { mutationBlocked: true });
|
|
1270
|
-
}
|
|
1271
|
-
const type = requiredString(raw, 'type');
|
|
1272
|
-
const operationId = requiredString(raw, 'operationId');
|
|
1273
|
-
const jobId = requiredString(raw, 'jobId');
|
|
1274
|
-
const sequence = requiredNumber(raw, 'sequence');
|
|
1275
|
-
const createdAt = requiredString(raw, 'createdAt');
|
|
1276
|
-
return {
|
|
1277
|
-
...raw,
|
|
1278
|
-
type,
|
|
1279
|
-
operationId,
|
|
1280
|
-
jobId,
|
|
1281
|
-
schemaVersion: CONTINUITY_CLIENT_SCHEMA_VERSION,
|
|
1282
|
-
contractVersion: CONTINUITY_CLIENT_CONTRACT_VERSION,
|
|
1283
|
-
sequence,
|
|
1284
|
-
createdAt,
|
|
1285
|
-
idempotencyKey: optionalString(raw, 'idempotencyKey'),
|
|
1286
|
-
metadata: optionalRecord(raw, 'metadata'),
|
|
1287
|
-
publicSafe: true,
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
function sanitizeMetadataStrict(input, path) {
|
|
1291
|
-
if (!input)
|
|
1292
|
-
return {};
|
|
1293
|
-
const metadata = {};
|
|
1294
|
-
const redactions = [];
|
|
1295
|
-
for (const [key, value] of Object.entries(input)) {
|
|
1296
|
-
const keyReason = getUnsafeMetadataKeyReason(key);
|
|
1297
|
-
const valueReason = getUnsafeMetadataValueReason(value);
|
|
1298
|
-
const reason = keyReason ?? valueReason;
|
|
1299
|
-
if (reason) {
|
|
1300
|
-
redactions.push({
|
|
1301
|
-
field: `${path}.${key}`,
|
|
1302
|
-
reason,
|
|
1303
|
-
evidence: keyReason ? 'key-denied' : 'value-denied',
|
|
1304
|
-
publicSafe: true,
|
|
1305
|
-
});
|
|
1306
|
-
continue;
|
|
1307
|
-
}
|
|
1308
|
-
metadata[key] = value;
|
|
1309
|
-
}
|
|
1310
|
-
if (redactions.length > 0) {
|
|
1311
|
-
throw new ContinuityClientStateStoreError(`Continuity metadata rejected unsafe public fields at ${path}.`, [CONTINUITY_CLIENT_REASON_CODES.UNSAFE_FIELD_REJECTED], { redactions, mutationBlocked: true });
|
|
1312
|
-
}
|
|
1313
|
-
return metadata;
|
|
1314
|
-
}
|
|
1315
|
-
function getUnsafeMetadataKeyReason(key) {
|
|
1316
|
-
if (!ALLOWED_METADATA_KEYS.has(key) || UNSAFE_METADATA_KEY_PATTERN.test(key))
|
|
1317
|
-
return 'unsafe_key';
|
|
1318
|
-
return null;
|
|
1319
|
-
}
|
|
1320
|
-
function getUnsafeMetadataValueReason(value) {
|
|
1321
|
-
if (typeof value === 'string') {
|
|
1322
|
-
if (ABSOLUTE_URL_VALUE_PATTERN.test(value))
|
|
1323
|
-
return 'private_url';
|
|
1324
|
-
if (LOCAL_ABSOLUTE_PATH_VALUE_PATTERN.test(value))
|
|
1325
|
-
return 'local_absolute_path';
|
|
1326
|
-
if (SECRET_VALUE_PATTERN.test(value))
|
|
1327
|
-
return 'secret';
|
|
1328
|
-
if (PROMPT_VALUE_PATTERN.test(value))
|
|
1329
|
-
return 'prompt_body';
|
|
1330
|
-
if (RAW_LOG_VALUE_PATTERN.test(value))
|
|
1331
|
-
return 'raw_log';
|
|
1332
|
-
if (PROTECTED_VALUE_PATTERN.test(value))
|
|
1333
|
-
return 'protected_field';
|
|
1334
|
-
return null;
|
|
1335
|
-
}
|
|
1336
|
-
if (value === null || typeof value === 'number' || typeof value === 'boolean')
|
|
1337
|
-
return null;
|
|
1338
|
-
if (Array.isArray(value)) {
|
|
1339
|
-
for (const item of value) {
|
|
1340
|
-
const reason = getUnsafeMetadataValueReason(item);
|
|
1341
|
-
if (reason)
|
|
1342
|
-
return reason;
|
|
1343
|
-
}
|
|
1344
|
-
return null;
|
|
1345
|
-
}
|
|
1346
|
-
if (isRecord(value)) {
|
|
1347
|
-
for (const [key, child] of Object.entries(value)) {
|
|
1348
|
-
if (UNSAFE_METADATA_KEY_PATTERN.test(key))
|
|
1349
|
-
return 'unsafe_key';
|
|
1350
|
-
const reason = getUnsafeMetadataValueReason(child);
|
|
1351
|
-
if (reason)
|
|
1352
|
-
return reason;
|
|
1353
|
-
}
|
|
1354
|
-
return null;
|
|
1355
|
-
}
|
|
1356
|
-
return 'unsafe_key';
|
|
1357
|
-
}
|
|
1358
|
-
function collectReceiptRedactions(receipts) {
|
|
1359
|
-
const redactions = [];
|
|
1360
|
-
for (const receipt of receipts) {
|
|
1361
|
-
if (!receipt.metadata_json)
|
|
1362
|
-
continue;
|
|
1363
|
-
try {
|
|
1364
|
-
const metadata = JSON.parse(receipt.metadata_json);
|
|
1365
|
-
const evidence = metadata.redactionEvidence;
|
|
1366
|
-
if (!Array.isArray(evidence))
|
|
1367
|
-
continue;
|
|
1368
|
-
for (const item of evidence) {
|
|
1369
|
-
if (!isRecord(item))
|
|
1370
|
-
continue;
|
|
1371
|
-
if (typeof item.field !== 'string' || typeof item.reason !== 'string' || typeof item.evidence !== 'string')
|
|
1372
|
-
continue;
|
|
1373
|
-
redactions.push({
|
|
1374
|
-
field: boundPublicText(item.field, 120),
|
|
1375
|
-
reason: item.reason,
|
|
1376
|
-
evidence: item.evidence,
|
|
1377
|
-
publicSafe: true,
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
catch {
|
|
1382
|
-
// Ignore malformed metadata; SQLite json_valid already guards new rows.
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
return redactions;
|
|
1386
|
-
}
|
|
1387
|
-
function compareOperationOrder(left, right) {
|
|
1388
|
-
const leftSequence = typeof left.sequence === 'number' ? left.sequence : 0;
|
|
1389
|
-
const rightSequence = typeof right.sequence === 'number' ? right.sequence : 0;
|
|
1390
|
-
if (leftSequence !== rightSequence)
|
|
1391
|
-
return leftSequence - rightSequence;
|
|
1392
|
-
const createdAt = String(left.createdAt ?? '').localeCompare(String(right.createdAt ?? ''));
|
|
1393
|
-
if (createdAt !== 0)
|
|
1394
|
-
return createdAt;
|
|
1395
|
-
return String(left.operationId ?? '').localeCompare(String(right.operationId ?? ''));
|
|
1396
|
-
}
|
|
1397
|
-
function isRecord(value) {
|
|
1398
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1399
|
-
}
|
|
1400
|
-
function requiredRecord(input, key) {
|
|
1401
|
-
const value = input[key];
|
|
1402
|
-
if (!isRecord(value)) {
|
|
1403
|
-
throw new ContinuityClientStateStoreError(`Continuity operation missing object field ${key}.`, [CONTINUITY_CLIENT_REASON_CODES.INVALID_SCHEMA_VERSION], { mutationBlocked: true });
|
|
1404
|
-
}
|
|
1405
|
-
return value;
|
|
1406
|
-
}
|
|
1407
|
-
function optionalRecord(input, key) {
|
|
1408
|
-
const value = input[key];
|
|
1409
|
-
return isRecord(value) ? value : undefined;
|
|
1410
|
-
}
|
|
1411
|
-
function requiredString(input, key) {
|
|
1412
|
-
const value = input[key];
|
|
1413
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
1414
|
-
throw new ContinuityClientStateStoreError(`Continuity operation missing string field ${key}.`, [CONTINUITY_CLIENT_REASON_CODES.INVALID_SCHEMA_VERSION], { mutationBlocked: true });
|
|
1415
|
-
}
|
|
1416
|
-
return value;
|
|
1417
|
-
}
|
|
1418
|
-
function optionalString(input, key) {
|
|
1419
|
-
const value = input[key];
|
|
1420
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
1421
|
-
}
|
|
1422
|
-
function requiredNumber(input, key) {
|
|
1423
|
-
const value = input[key];
|
|
1424
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
1425
|
-
throw new ContinuityClientStateStoreError(`Continuity operation missing numeric field ${key}.`, [CONTINUITY_CLIENT_REASON_CODES.INVALID_SCHEMA_VERSION], { mutationBlocked: true });
|
|
1426
|
-
}
|
|
1427
|
-
return value;
|
|
1428
|
-
}
|
|
1429
|
-
function optionalNumber(input, key) {
|
|
1430
|
-
const value = input[key];
|
|
1431
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
1432
|
-
}
|
|
1433
|
-
function optionalBoolean(input, key) {
|
|
1434
|
-
const value = input[key];
|
|
1435
|
-
return typeof value === 'boolean' ? value : undefined;
|
|
1436
|
-
}
|
|
1437
|
-
function arrayOfStrings(value) {
|
|
1438
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === 'string' && item.length > 0) : [];
|
|
1439
|
-
}
|
|
1440
|
-
function arrayOfRecords(value) {
|
|
1441
|
-
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
1442
|
-
}
|
|
1443
|
-
function normalizeCompactionSummary(value) {
|
|
1444
|
-
return normalizeSafePublicText(value, COMPACTION_SUMMARY_MAX_CHARS, 'compact_context.publicSummary');
|
|
1445
|
-
}
|
|
1446
|
-
function normalizeSafePublicText(value, maxChars, path) {
|
|
1447
|
-
const trimmed = value.trim();
|
|
1448
|
-
if (!trimmed) {
|
|
1449
|
-
throw new ContinuityClientStateStoreError(`Continuity compaction missing public text at ${path}.`, [CONTINUITY_CLIENT_REASON_CODES.INVALID_SCHEMA_VERSION], { mutationBlocked: true });
|
|
1450
|
-
}
|
|
1451
|
-
const reason = getUnsafeMetadataValueReason(trimmed);
|
|
1452
|
-
if (reason) {
|
|
1453
|
-
throw new ContinuityClientStateStoreError(`Continuity compaction rejected unsafe public text at ${path}.`, [CONTINUITY_CLIENT_REASON_CODES.UNSAFE_FIELD_REJECTED], { redactions: [{ field: path, reason, evidence: 'value-denied', publicSafe: true }], mutationBlocked: true });
|
|
1454
|
-
}
|
|
1455
|
-
return trimmed.length <= maxChars ? trimmed : `${trimmed.slice(0, maxChars)}…[TRUNCATED:${trimmed.length - maxChars}]`;
|
|
1456
|
-
}
|
|
1457
|
-
function normalizeCompactionHash(value, path) {
|
|
1458
|
-
return normalizeSafePublicText(value, 120, path);
|
|
1459
|
-
}
|
|
1460
|
-
function normalizeCompactionPaths(value, path, limit) {
|
|
1461
|
-
return unique(arrayOfStrings(value).map((item, index) => normalizeRepoRelativeRef(item, `${path}[${index}]`))).slice(0, limit);
|
|
1462
|
-
}
|
|
1463
|
-
function normalizeRepoRelativeRef(value, path) {
|
|
1464
|
-
const normalized = normalizeSafePublicText(value, 240, path).replace(/\\/g, '/');
|
|
1465
|
-
const segments = normalized.split('/').filter(Boolean);
|
|
1466
|
-
const absoluteWindows = /^[A-Za-z]:\//.test(normalized);
|
|
1467
|
-
if (normalized.startsWith('/') ||
|
|
1468
|
-
normalized.startsWith('~') ||
|
|
1469
|
-
normalized.includes('://') ||
|
|
1470
|
-
absoluteWindows ||
|
|
1471
|
-
segments.includes('..')) {
|
|
1472
|
-
throw new ContinuityClientStateStoreError(`Continuity compaction rejected non-relative ref at ${path}.`, [CONTINUITY_CLIENT_REASON_CODES.UNSAFE_FIELD_REJECTED], { redactions: [{ field: path, reason: 'private_url', evidence: 'value-denied', publicSafe: true }], mutationBlocked: true });
|
|
1473
|
-
}
|
|
1474
|
-
return normalized;
|
|
1475
|
-
}
|
|
1476
|
-
function normalizeCompactionArtifactRefs(operation) {
|
|
1477
|
-
const refs = [];
|
|
1478
|
-
const summaryRef = optionalRecord(operation, 'summaryRef');
|
|
1479
|
-
if (summaryRef)
|
|
1480
|
-
refs.push(normalizeCompactionArtifactRef(summaryRef, 'compact_context.summaryRef', 'context_summary'));
|
|
1481
|
-
for (const [index, ref] of arrayOfRecords(operation.artifactRefs).entries()) {
|
|
1482
|
-
refs.push(normalizeCompactionArtifactRef(ref, `compact_context.artifactRefs[${index}]`));
|
|
1483
|
-
}
|
|
1484
|
-
const seen = new Set();
|
|
1485
|
-
return refs.filter((ref) => {
|
|
1486
|
-
const key = `${ref.kind}:${ref.relativePath}`;
|
|
1487
|
-
if (seen.has(key))
|
|
1488
|
-
return false;
|
|
1489
|
-
seen.add(key);
|
|
1490
|
-
return true;
|
|
1491
|
-
}).slice(0, COMPACTION_ARTIFACT_REF_LIMIT);
|
|
1492
|
-
}
|
|
1493
|
-
function normalizeCompactionArtifactRef(input, path, fallbackKind = 'context_summary') {
|
|
1494
|
-
const kind = normalizeSafePublicText(optionalString(input, 'kind') ?? fallbackKind, 60, `${path}.kind`);
|
|
1495
|
-
const relativePath = normalizeRepoRelativeRef(requiredString(input, 'relativePath'), `${path}.relativePath`);
|
|
1496
|
-
const sha = optionalString(input, 'sha256');
|
|
1497
|
-
const publicSummary = optionalString(input, 'publicSummary');
|
|
1498
|
-
return {
|
|
1499
|
-
kind,
|
|
1500
|
-
relativePath,
|
|
1501
|
-
...(sha ? { sha256: normalizeCompactionHash(sha, `${path}.sha256`) } : {}),
|
|
1502
|
-
...(publicSummary ? { publicSummary: normalizeSafePublicText(publicSummary, PUBLIC_TEXT_MAX_CHARS, `${path}.publicSummary`) } : {}),
|
|
1503
|
-
publicSafe: true,
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
function normalizeCompactionEvidenceRefs(operation) {
|
|
1507
|
-
return arrayOfRecords(operation.evidenceIndex).map((ref, index) => {
|
|
1508
|
-
const path = `compact_context.evidenceIndex[${index}]`;
|
|
1509
|
-
const relativePath = normalizeRepoRelativeRef(requiredString(ref, 'relativePath'), `${path}.relativePath`);
|
|
1510
|
-
const refId = optionalString(ref, 'refId') ?? `evidence-${safeId(relativePath)}`;
|
|
1511
|
-
const reasonCode = optionalString(ref, 'reasonCode') ?? optionalString(operation, 'reasonCode') ?? 'context-compacted';
|
|
1512
|
-
const sha = optionalString(ref, 'sha256');
|
|
1513
|
-
const publicSummary = optionalString(ref, 'publicSummary');
|
|
1514
|
-
return {
|
|
1515
|
-
refId: normalizeSafePublicText(refId, 120, `${path}.refId`),
|
|
1516
|
-
relativePath,
|
|
1517
|
-
reasonCode: normalizeSafePublicText(reasonCode, 80, `${path}.reasonCode`),
|
|
1518
|
-
...(sha ? { sha256: normalizeCompactionHash(sha, `${path}.sha256`) } : {}),
|
|
1519
|
-
...(publicSummary ? { publicSummary: normalizeSafePublicText(publicSummary, PUBLIC_TEXT_MAX_CHARS, `${path}.publicSummary`) } : {}),
|
|
1520
|
-
publicSafe: true,
|
|
1521
|
-
};
|
|
1522
|
-
}).slice(0, COMPACTION_EVIDENCE_REF_LIMIT);
|
|
1523
|
-
}
|
|
1524
|
-
function normalizeCompactionCheckpoint(operation, retainedRefs, artifactRefs, evidenceRefs) {
|
|
1525
|
-
const checkpoint = optionalRecord(operation, 'checkpoint') ?? {};
|
|
1526
|
-
const checkpointId = optionalString(checkpoint, 'checkpointId') ?? `checkpoint-${safeId(operation.operationId)}`;
|
|
1527
|
-
const capturedAt = optionalString(checkpoint, 'capturedAt') ?? operation.createdAt;
|
|
1528
|
-
const normalized = {
|
|
1529
|
-
checkpointId: normalizeSafePublicText(checkpointId, 120, 'compact_context.checkpoint.checkpointId'),
|
|
1530
|
-
capturedAt: normalizeSafePublicText(capturedAt, 80, 'compact_context.checkpoint.capturedAt'),
|
|
1531
|
-
retainedRefCount: retainedRefs.length,
|
|
1532
|
-
artifactRefCount: artifactRefs.length,
|
|
1533
|
-
evidenceRefCount: evidenceRefs.length,
|
|
1534
|
-
publicSafe: true,
|
|
1535
|
-
};
|
|
1536
|
-
for (const key of ['eventCursor', 'receiptCursor']) {
|
|
1537
|
-
const value = optionalString(checkpoint, key);
|
|
1538
|
-
if (value)
|
|
1539
|
-
normalized[key] = normalizeSafePublicText(value, 120, `compact_context.checkpoint.${key}`);
|
|
1540
|
-
}
|
|
1541
|
-
for (const key of ['recentEventCount', 'recentReceiptCount']) {
|
|
1542
|
-
const value = optionalNumber(checkpoint, key);
|
|
1543
|
-
if (value !== undefined)
|
|
1544
|
-
normalized[key] = Math.max(0, Math.floor(value));
|
|
1545
|
-
}
|
|
1546
|
-
return normalized;
|
|
1547
|
-
}
|
|
1548
|
-
function sha256Hex(value) {
|
|
1549
|
-
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
1550
|
-
}
|
|
1551
|
-
function unique(values) {
|
|
1552
|
-
return Array.from(new Set(values));
|
|
1553
|
-
}
|
|
1554
|
-
function safeId(value) {
|
|
1555
|
-
return value.replace(/[^a-z0-9_.:-]+/gi, '-').slice(0, 120);
|
|
1556
|
-
}
|
|
1557
|
-
function boundPublicText(value, maxChars) {
|
|
1558
|
-
const redacted = redactPublicText(value);
|
|
1559
|
-
return redacted.length <= maxChars ? redacted : `${redacted.slice(0, maxChars)}…[TRUNCATED:${redacted.length - maxChars}]`;
|
|
1560
|
-
}
|
|
1561
|
-
function redactPublicText(value) {
|
|
1562
|
-
if (ABSOLUTE_URL_VALUE_PATTERN.test(value) ||
|
|
1563
|
-
SECRET_VALUE_PATTERN.test(value) ||
|
|
1564
|
-
RAW_LOG_VALUE_PATTERN.test(value) ||
|
|
1565
|
-
PROMPT_VALUE_PATTERN.test(value) ||
|
|
1566
|
-
PROTECTED_VALUE_PATTERN.test(value)) {
|
|
1567
|
-
return '[REDACTED]';
|
|
1568
|
-
}
|
|
1569
|
-
return value;
|
|
1570
|
-
}
|
|
1571
|
-
function stableStringify(value) {
|
|
1572
|
-
if (Array.isArray(value))
|
|
1573
|
-
return `[${value.map(stableStringify).join(',')}]`;
|
|
1574
|
-
if (isRecord(value)) {
|
|
1575
|
-
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
|
|
1576
|
-
}
|
|
1577
|
-
return JSON.stringify(value);
|
|
1578
|
-
}
|
|
1579
|
-
function mapContinuityError(error, fallbackMessage) {
|
|
1580
|
-
if (error instanceof ContinuityClientStateStoreError)
|
|
1581
|
-
return error;
|
|
1582
|
-
if (error instanceof ContinuitySqliteStoreError) {
|
|
1583
|
-
return new ContinuityClientStateStoreError(error.message, [error.reasonCode], { mutationBlocked: error.mutationBlocked, cause: error });
|
|
1584
|
-
}
|
|
1585
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1586
|
-
return new ContinuityClientStateStoreError(`${fallbackMessage} ${message}`, [CONTINUITY_CLIENT_REASON_CODES.SQLITE_MUTATION_FAILED], { cause: error });
|
|
1587
|
-
}
|
|
1
|
+
import{existsSync as se}from"node:fs";import{createHash as ie}from"node:crypto";import{deriveProjectIdentity as de}from"../runner/project-identity.js";import{recordAutonomousGovernance as ce}from"./autonomous-governance.js";import{CONTINUITY_PROCESSING_OUTPUT_SUMMARY_EVENT_TYPE as ue,ContinuitySqliteStoreError as pe,continuitySqliteStorePath as me,isContinuityPublicStage as le,normalizeContinuityStageIteration as Y,openContinuitySqliteStore as Ie,sanitizeContinuityStageHistory as _e}from"./sqlite-store.js";const S=1,g="continuity-v1",z=10,ye=2e3,Ze=z,et=ye,tt=3,nt=1800,fe=1,I=Object.freeze({OK:"continuity-ok",INVALID_SCHEMA_VERSION:"invalid-schema-version",INVALID_CONTRACT_VERSION:"invalid-contract-version",UNKNOWN_OPERATION_REQUIRED:"unknown-operation-required",UNKNOWN_OPERATION_IGNORED:"unknown-operation-ignored",DUPLICATE_OPERATION_ID:"duplicate-operation-id",IDEMPOTENCY_REPLAY:"idempotency-replay",UNSAFE_FIELD_REJECTED:"unsafe-field-rejected",SQLITE_ADAPTER_UNAVAILABLE:"sqlite-adapter-unavailable",SQLITE_INTEGRITY_FAILED:"sqlite-integrity-failed",SQLITE_MUTATION_FAILED:"sqlite-mutation-failed"}),he=new Set(["create_job","update_job_status","append_event","append_receipt","upsert_goal_campaign","upsert_goal_ledger_item","upsert_verifier","upsert_review_gate","schedule_next_tick","acquire_lease","release_lease","compact_context","enqueue_outbox","append_outbox","enqueue_execution_attempt","claim_execution_attempt","heartbeat_execution_attempt","release_execution_attempt","complete_execution_attempt","append_execution_receipt","append_execution_summary"]),Ae=new Set(["schemaVersion","contractVersion","eventType","executionId","jobId","operationId","idempotencyKey","goalId","loopId","campaignId","itemId","ledgerItemIds","verifierId","verifierIds","gateId","reviewGateIds","runnerId","leaseId","status","reasonCode","reasonCodes","runtimeId","modelId","runtimeSource","headlessRuntime","headlessModel","headlessRuntimeReasonCode","originRuntime","originModel","targetRef","attemptId","attemptStatus","directiveHash","timeoutMs","leaseExpiresAt","heartbeatAt","notBeforeAt","maxAttempts","counts","hashes","relativePaths","artifactRefs","evidenceRefs","validationRefs","registryRefs","storyIds","epicIds","debtIds","iteration","currentStage","stageHistory","durationMs","retryCount","nextTickAt","processingOutputSource","publicSummary","redactionEvidence","selectorKind","targetId","targetJobId","relatedJobIds","serverAccessToLocalhost","serverHeldStream","initialCursor","targetResolutionState","targetKind","relatedJobCount","snapshotStatus","snapshotGeneratedAt","renderedSummaryCount","cursorState","currentCursor","nextCursor","cursorAdvanced","eventId","latestEventId","watchStatus","stopReason","syncVersion","transportKind","envelopeId","workspaceId","workspaceLabel","projectId","projectLabel","snapshotId","syncCursor","previousSyncCursor","nextSyncCursor","commandId","receiptId","acknowledgedAt","observedAt","generatedAt","syncStatus","commandStatus","receiptStatus","actorType","actorId","tenantId","commandKind","auditStatus","redactionCount"]),K=/(raw[_-]?(?:logs?|payloads?)|std(?:out|err)|stack(?:[_-]?trace)?|transcript|prompt(?:[_-]?(?:body|text))?|private[_-]?(?:urls?|prompts?)|secret|token|password|protected(?:[_-]?(?:field|body|bodies|internals))?|workflow(?:[_-]?body)?|step(?:[_-]?body)?|customer(?:[_-]?(?:pii|data))?|\bpii\b|authorization|api[_-]?key|license[_-]?key|argv|args|executable(?:[_-]?path)?|binary(?:[_-]?path)?|shell|cwd|command[_-]?path|vendor(?:[_-]?(?:payload|corpus|response|output)))/i,G=/\bhttps?:\/\/\S+/i,be=/(?:^|\s)(?:\/(?:home|Users|media|mnt|tmp|var|etc|private)\/\S+|[A-Za-z]:\\\S+)/i,J=/(ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|authorization\s*:|password\s*=|token\s*=|api[_-]?key\s*=|license[_-]?key\s*=)/i,W=/(raw[_-]?(?:logs?)\s*[:=]|transcript|stack trace|stderr:|stdout:)/i,Q=/(prompt[_-]?body|system prompt|workflow body|step body)/i,X=/(PROTECTED_|_PROTECTED_|PRIVATE_INTERNALS|CUSTOMER_PII|\bPII\b|VENDOR_CORPUS|P180[_-]?SYNTHETIC[_-]?CONTINUITY|P185[_-]?SYNTHETIC[_-]?CONTINUITY|P187[_-]?SYNTHETIC[_-]?(?:CONTINUITY|RUNNER|WATCH)|P190[_-]?SYNTHETIC[_-]?(?:HEADLESS|CONTINUITY)|P192[_-]?SYNTHETIC[_-]?(?:PORTAL|RUNNER|SYNC))/i,k=240,Z=2400,Ce=40,Se=20,ge=40,Ee=20,D=80;class _ extends Error{reasonCodes;redactions;mutationBlocked;constructor(e,n,a={}){super(e),this.name="ContinuityClientStateStoreError",this.reasonCodes=E(n),this.redactions=a.redactions??[],this.mutationBlocked=a.mutationBlocked??!1,this.cause=a.cause}}function at(t){return se(me(t))}function ot(t,e=new Date().toISOString()){return{schemaVersion:S,contractVersion:g,generatedAt:e,hardStop:!0,reasonCodes:E(t),publicSafe:!0}}async function rt(t){const e=await Ie({projectRoot:t.projectRoot,adapter:t.adapter,adapterLoader:t.adapterLoader,now:t.now,strictReadOnly:t.strictReadOnly});return new Re(e,t.now??(()=>new Date().toISOString()),de(t.projectRoot))}class Re{store;now;projectIdentity;constructor(e,n=()=>new Date().toISOString(),a){this.store=e,this.now=n,this.projectIdentity=a}close(){this.store.close()}recordAutonomousGovernance(e,n){try{return ce(this.store,e,n)}catch(a){throw q(a,"Failed to record autonomous governance envelope.")}}collectSnapshot(e={}){try{const n=e.nowIso??this.now(),a=this.store.collectSnapshotRows({nowIso:n,jobLimit:e.jobLimit??20,staleLeaseLimit:e.staleLeaseLimit??10,receiptLimit:e.receiptLimit??10,eventLimit:e.eventLimit??10,snapshotLimit:e.snapshotLimit??5}),i=this.store.listDueJobs({nowIso:n,limit:e.dueJobLimit??10}),d=this.store.listProcessingOutputSummaries({limit:e.processingSummaryLimit??z}),u=Ke(a.receipts).slice(0,20),m=xe({generatedAt:n,counts:a.counts,jobCount:a.jobs.length,dueJobCount:i.length,outboxPending:a.outboxCounts.pending??0,redactions:u});return{schemaVersion:S,contractVersion:g,generatedAt:n,store:{schemaVersion:a.schemaVersion,readable:!0},jobs:a.jobs.map(ee),counts:a.counts,dueJobCount:a.counts.dueJobs??0,dueJobs:i.map(ee),staleLeases:a.staleLeases.map(c=>({leaseId:s(c.lease_id,100),jobId:s(c.job_id,100),runnerId:s(c.runner_id,100),expiresAt:s(c.expires_at,80),publicSafe:!0})),outbox:{total:a.counts.outbox??0,pending:a.outboxCounts.pending??0,failed:a.outboxCounts.failed??0,deadLetter:a.outboxCounts.dead_letter??0,sent:a.outboxCounts.sent??0,acked:a.outboxCounts.acked??0},queue:{depth:a.queue.depth,queued:a.queue.queued,running:a.queue.running,retrying:a.queue.retrying,deadLetter:a.queue.deadLetter,capacity:a.queue.capacity,available:a.queue.available,backpressure:a.queue.backpressure,generatedAt:s(a.queue.generatedAt,80),...a.queue.nextRetryAt?{nextRetryAt:s(a.queue.nextRetryAt,80)}:{},...a.queue.reasonCode?{reasonCode:s(a.queue.reasonCode,80)}:{},publicSafe:!0},lastReceipts:a.receipts.map(c=>({receiptId:s(c.receipt_id,100),jobId:s(c.job_id,100),...c.operation_id?{operationId:s(c.operation_id,100)}:{},status:s(c.status,40),...c.reason_code?{reasonCode:s(c.reason_code,80)}:{},createdAt:s(c.created_at,80),publicSafe:!0})),lastEvents:a.events.map(c=>{const l=P(v(c.metadata_json));return{eventId:s(c.event_id,100),jobId:s(c.job_id,100),operationId:s(c.operation_id,100),type:s(c.type,80),publicSummary:s(c.summary,k),...c.reason_code?{reasonCode:s(c.reason_code,80)}:{},createdAt:s(c.created_at,80),...Object.keys(l).length>0?{metadata:l}:{},publicSafe:!0}}),processingSummaries:d.map(te),headlessExecutionAttempts:a.headlessExecutionAttempts.map(Ne),compactContext:ke(a.stateSnapshots),portalSync:m,redactions:u,publicSafe:!0}}catch(n){throw q(n,"Failed to collect continuity snapshot.")}}collectProcessingOutputSummaryCursor(e={}){try{const n=this.store.pollProcessingOutputSummaries(e);return{summaries:n.summaries.map(te),...n.nextCursor?{nextCursor:n.nextCursor}:{},hasMore:n.hasMore,publicSafe:!0}}catch(n){throw q(n,"Failed to collect processing summary cursor.")}}applyStateUpdate(e){const n=Me(e),a=[],i=[],d=[],u=[];for(const m of n){const c=this.applyOperationLog(m);a.push(...c.appliedOperationIds),i.push(...c.ignoredOperationIds),d.push(...c.reasonCodes),u.push(...c.redactions)}return{ok:!0,appliedOperationIds:E(a),ignoredOperationIds:E(i),reasonCodes:E(d.length>0?d:[I.OK]),redactions:u,publicSafe:!0}}applyOperationLog(e){const n=Ue(e),a=[],i=[...n.ignoredOperationIds],d=[...n.reasonCodes],u=[...n.redactions];try{this.store.runInTransaction(()=>{for(const m of n.operations){if(this.store.hasOperationRef(m.operationId,m.idempotencyKey)){i.push(m.operationId),d.push(I.IDEMPOTENCY_REPLAY);continue}this.applyKnownOperation(m,n.logMetadata),a.push(m.operationId),m.type!=="append_receipt"&&this.recordAppliedAudit(m)}for(const m of n.skippedOperations)this.recordIgnoredAuditIfPossible(m,I.UNKNOWN_OPERATION_IGNORED)})}catch(m){throw q(m,"Failed to apply continuity operation log atomically.")}return{ok:!0,appliedOperationIds:E(a),ignoredOperationIds:E(i),reasonCodes:E(d.length>0?d:[I.OK]),redactions:u,publicSafe:!0}}applyKnownOperation(e,n){switch(e.type){case"create_job":this.applyCreateJob(e,n);return;case"update_job_status":this.applyUpdateJobStatus(e);return;case"append_event":this.applyAppendEvent(e);return;case"append_receipt":this.applyAppendReceipt(e);return;case"upsert_goal_campaign":this.applyUpsertGoalCampaign(e);return;case"schedule_next_tick":this.store.scheduleNextTick({jobId:e.jobId,scheduledAt:r(e,"scheduledAt"),updatedAt:e.createdAt,reasonCode:o(e,"reasonCode")});return;case"acquire_lease":this.store.acquireLease({leaseId:r(e,"leaseId"),jobId:e.jobId,runnerId:r(e,"runnerId"),acquiredAt:r(e,"acquiredAt"),expiresAt:r(e,"expiresAt"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:f(e.metadata,"operation.metadata")});return;case"release_lease":this.store.releaseLease({leaseId:r(e,"leaseId"),releasedAt:r(e,"releasedAt"),reasonCode:o(e,"reasonCode")});return;case"compact_context":this.applyCompactContext(e);return;case"upsert_goal_ledger_item":this.applyUpsertGoalLedgerItem(e);return;case"upsert_verifier":this.applyUpsertVerifier(e);return;case"upsert_review_gate":this.applyUpsertReviewGate(e);return;case"enqueue_outbox":case"append_outbox":this.applyOutbox(e);return;case"enqueue_execution_attempt":this.applyEnqueueExecutionAttempt(e);return;case"claim_execution_attempt":this.store.claimHeadlessExecutionAttempt({attemptId:r(e,"attemptId"),leaseId:r(e,"leaseId"),runnerId:r(e,"runnerId"),claimedAt:o(e,"claimedAt")??e.createdAt,leaseExpiresAt:r(e,"leaseExpiresAt"),reasonCode:o(e,"reasonCode")});return;case"heartbeat_execution_attempt":this.store.heartbeatHeadlessExecutionAttempt({attemptId:r(e,"attemptId"),leaseId:r(e,"leaseId"),runnerId:r(e,"runnerId"),heartbeatAt:o(e,"heartbeatAt")??e.createdAt,leaseExpiresAt:o(e,"leaseExpiresAt")});return;case"release_execution_attempt":this.store.releaseHeadlessExecutionAttempt({attemptId:r(e,"attemptId"),leaseId:r(e,"leaseId"),releasedAt:o(e,"releasedAt")??e.createdAt,status:o(e,"status"),reasonCode:o(e,"reasonCode"),notBeforeAt:o(e,"notBeforeAt"),retryCount:h(e,"retryCount")});return;case"complete_execution_attempt":this.applyCompleteExecutionAttempt(e);return;case"append_execution_receipt":this.applyAppendExecutionReceipt(e);return;case"append_execution_summary":this.applyAppendExecutionSummary(e);return;default:throw new _(`Unknown continuity operation ${e.type}.`,[I.UNKNOWN_OPERATION_REQUIRED],{mutationBlocked:!0})}}applyCompactContext(e){const n=w(r(e,"summaryHash"),"compact_context.summaryHash"),a=oe(e.retainedRefs,"compact_context.retainedRefs",Ce),i=we(o(e,"publicSummary")??`Context compacted with ${a.length} retained public refs.`),d=Fe(e),u=Be(e),m=oe(e.decisionLogRefs,"compact_context.decisionLogRefs",Ee),c=Ye(e,a,d,u),l=E([...a,...m,...d.map(A=>A.relativePath),...u.map(A=>A.relativePath)]).slice(0,D),U=B(i),N=o(e,"reasonCode")??"context-compacted",j={schemaVersion:S,contractVersion:g,jobId:e.jobId,operationId:e.operationId,reasonCode:N,publicSummary:i,counts:{retainedRefs:a.length,artifactRefs:d.length,evidenceRefs:u.length,decisionLogRefs:m.length,...h(e,"inputTokens")!==void 0?{inputContextUnits:h(e,"inputTokens")}:{},...h(e,"outputTokens")!==void 0?{outputContextUnits:h(e,"outputTokens")}:{},summaryChars:i.length},hashes:{summary:n,persistedSummary:U},relativePaths:l,artifactRefs:d,evidenceRefs:u,validationRefs:m,redactionEvidence:e.redactions??[],checkpoint:c};for(const[A,x]of d.entries())this.store.recordArtifact({artifactId:`artifact-${R(e.operationId)}-${A+1}`,jobId:e.jobId,kind:x.kind,relativePath:x.relativePath,sha256:x.sha256,summary:x.publicSummary,createdAt:e.createdAt,operationId:`${e.operationId}:artifact:${A+1}`,idempotencyKey:e.idempotencyKey?`${e.idempotencyKey}:artifact:${A+1}`:void 0,metadata:{schemaVersion:S,contractVersion:g,jobId:e.jobId,operationId:e.operationId,reasonCode:N,hashes:x.sha256?{artifact:x.sha256}:{},relativePaths:[x.relativePath],publicSummary:x.publicSummary}});this.store.recordStateSnapshot({snapshotId:`snapshot-${R(e.operationId)}`,jobId:e.jobId,kind:"context_compaction",relativePath:d.find(A=>A.kind==="context_summary")?.relativePath,sha256:n,summary:i,capturedAt:e.createdAt,operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:j}),this.store.appendEvent({eventId:`event-${R(e.operationId)}-context-compacted`,jobId:e.jobId,operationId:`${e.operationId}:event`,idempotencyKey:e.idempotencyKey?`${e.idempotencyKey}:event`:void 0,sequence:e.sequence,type:"context_compacted",summary:`Context compaction checkpoint ${c.checkpointId} persisted with ${l.length} public refs.`,reasonCode:N,createdAt:e.createdAt,metadata:{schemaVersion:S,contractVersion:g,jobId:e.jobId,operationId:e.operationId,reasonCode:N,counts:j.counts,hashes:j.hashes,relativePaths:l,artifactRefs:d.map(A=>A.relativePath),evidenceRefs:u,publicSummary:i}})}applyCreateJob(e,n){const a=T(e,"job");this.store.recordJob({jobId:r(a,"jobId"),kind:r(a,"kind"),status:r(a,"status"),createdAt:r(a,"createdAt"),updatedAt:r(a,"updatedAt"),summary:o(a,"summary"),reasonCode:o(a,"reasonCode"),nextRunAt:o(a,"nextRunAt"),goalId:o(a,"goalId"),loopId:o(a,"loopId"),campaignId:o(a,"campaignId"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:f({...p(a,"metadata")??p(e,"metadata")??{...n},...Te(this.projectIdentity)},"job.metadata")})}applyUpdateJobStatus(e){this.store.updateJobStatus({jobId:e.jobId,status:r(e,"status"),updatedAt:e.createdAt,reasonCode:o(e,"reasonCode")})}applyAppendEvent(e){const n=T(e,"event"),a=r(n,"type");if(a===ue){this.applyProcessingOutputSummaryEvent(e,n);return}this.store.appendEvent({eventId:r(n,"eventId"),jobId:r(n,"jobId"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,sequence:e.sequence,type:a,summary:r(n,"summary"),reasonCode:o(n,"reasonCode"),createdAt:r(n,"createdAt"),metadata:f(p(n,"metadata"),"event.metadata")})}applyProcessingOutputSummaryEvent(e,n){const a=p(n,"processingOutputSummary")??n,i=o(a,"executionId")??e.operationId,d=o(a,"jobId")??o(n,"jobId")??e.jobId,u=o(a,"publicSummary")??r(n,"summary"),m=o(a,"createdAt")??o(n,"createdAt")??e.createdAt,c=p(a,"metadata"),l=p(n,"metadata");this.store.recordProcessingOutputSummary({eventId:o(n,"eventId"),executionId:i,jobId:d,idempotencyKey:e.idempotencyKey,goalId:o(a,"goalId")??o(n,"goalId"),loopId:o(a,"loopId")??o(n,"loopId"),publicSummary:u,reasonCode:o(a,"reasonCode")??o(n,"reasonCode"),createdAt:m,source:o(a,"source")??o(n,"source"),metadata:{...l??{},...c??{}}})}applyAppendReceipt(e){const n=T(e,"receipt"),a=o(n,"operationId")??e.operationId;this.store.appendReceipt({receiptId:r(n,"receiptId"),jobId:r(n,"jobId"),operationId:a,idempotencyKey:e.idempotencyKey,status:r(n,"status"),reasonCode:o(n,"reasonCode"),appliedOperationIds:L(n.appliedOperationIds),ignoredOperationIds:L(n.ignoredOperationIds),createdAt:r(n,"createdAt"),metadata:f(p(n,"metadata"),"receipt.metadata")})}applyUpsertGoalCampaign(e){const n=T(e,"campaign");this.store.upsertGoalCampaign({campaignId:r(n,"campaignId"),jobId:r(n,"jobId"),status:r(n,"status"),title:o(n,"title"),publicSummary:o(n,"publicSummary"),reasonCode:o(n,"reasonCode"),createdAt:r(n,"createdAt"),updatedAt:r(n,"updatedAt"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:f(p(n,"metadata"),"campaign.metadata")})}applyUpsertGoalLedgerItem(e){const n=T(e,"item");this.store.upsertGoalLedgerItem({itemId:r(n,"itemId"),campaignId:r(n,"campaignId"),jobId:r(n,"jobId"),itemType:r(n,"type"),status:r(n,"status"),summary:r(n,"publicSummary"),reasonCode:o(n,"reasonCode"),createdAt:e.createdAt,updatedAt:r(n,"updatedAt"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:f(p(n,"metadata"),"item.metadata")})}applyUpsertVerifier(e){const n=T(e,"verifier");this.store.upsertGoalVerifier({verifierId:r(n,"verifierId"),campaignId:r(n,"campaignId"),jobId:r(n,"jobId"),kind:r(n,"kind"),status:r(n,"status"),summary:o(n,"publicSummary"),reasonCode:o(n,"reasonCode"),lastCheckedAt:o(n,"lastCheckedAt"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:f(p(n,"metadata"),"verifier.metadata")})}applyUpsertReviewGate(e){const n=T(e,"reviewGate");this.store.upsertGoalReviewGate({gateId:r(n,"gateId"),campaignId:r(n,"campaignId"),jobId:r(n,"jobId"),status:r(n,"status"),summary:o(n,"publicSummary"),required:He(n,"required")??!0,reasonCode:o(n,"reasonCode"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,metadata:f(p(n,"metadata"),"reviewGate.metadata")})}applyOutbox(e){const n=p(e,"outbox")??e;this.store.enqueueOutbox({outboxId:o(n,"outboxId")??`outbox-${R(e.operationId)}`,jobId:o(n,"jobId")??e.jobId,operationId:e.operationId,idempotencyKey:e.idempotencyKey,type:o(n,"type")===e.type?"continuity_event":r(n,"type"),status:o(n,"status"),payload:f(p(n,"payload")??p(e,"metadata")??{},"outbox.payload"),retryCount:h(n,"retryCount"),nextAttemptAt:o(n,"nextAttemptAt"),lastErrorCode:o(n,"lastErrorCode"),createdAt:o(n,"createdAt")??e.createdAt,updatedAt:o(n,"updatedAt")??e.createdAt})}applyEnqueueExecutionAttempt(e){const n=p(e,"attempt")??e,a=f(p(n,"metadata")??p(e,"metadata"),"executionAttempt.metadata");this.store.enqueueHeadlessExecutionAttempt({attemptId:r(n,"attemptId"),executionId:r(n,"executionId"),jobId:o(n,"jobId")??e.jobId,directiveHash:r(n,"directiveHash"),directiveKind:r(n,"directiveKind"),commandClass:r(n,"commandClass"),targetKind:r(n,"targetKind"),targetRef:r(n,"targetRef"),storyId:o(n,"storyId"),epicId:o(n,"epicId"),runtimeId:o(n,"runtimeId"),modelId:o(n,"modelId"),runtimeSource:o(n,"runtimeSource"),status:o(n,"status"),reasonCode:o(n,"reasonCode"),publicSummary:o(n,"publicSummary"),enqueuedAt:o(n,"enqueuedAt")??e.createdAt,notBeforeAt:o(n,"notBeforeAt"),retryCount:h(n,"retryCount"),maxAttempts:h(n,"maxAttempts"),operationId:e.operationId,idempotencyKey:e.idempotencyKey,counts:p(n,"counts"),hashes:p(n,"hashes"),redactionSummary:Array.isArray(n.redactionSummary)?n.redactionSummary:void 0,relativePaths:L(n.relativePaths),metadata:a})}applyCompleteExecutionAttempt(e){const n=p(e,"completion")??e;this.store.completeHeadlessExecutionAttempt({attemptId:r(n,"attemptId"),executionId:o(n,"executionId"),jobId:o(n,"jobId")??e.jobId,leaseId:o(n,"leaseId"),runnerId:o(n,"runnerId"),status:r(n,"status"),completedAt:o(n,"completedAt")??e.createdAt,durationMs:h(n,"durationMs"),reasonCode:o(n,"reasonCode"),publicSummary:o(n,"publicSummary"),notBeforeAt:o(n,"notBeforeAt"),retryCount:h(n,"retryCount"),counts:p(n,"counts"),hashes:p(n,"hashes"),redactionSummary:Array.isArray(n.redactionSummary)?n.redactionSummary:void 0,relativePaths:L(n.relativePaths),metadata:f(p(n,"metadata"),"executionCompletion.metadata")})}applyAppendExecutionReceipt(e){const n=T(e,"receipt");this.store.recordHeadlessExecutionReceipt({receiptId:r(n,"receiptId"),attemptId:r(n,"attemptId"),executionId:r(n,"executionId"),jobId:o(n,"jobId")??e.jobId,status:r(n,"status"),reasonCode:o(n,"reasonCode"),createdAt:o(n,"createdAt")??e.createdAt,operationId:e.operationId,idempotencyKey:e.idempotencyKey,counts:p(n,"counts"),hashes:p(n,"hashes"),redactionSummary:Array.isArray(n.redactionSummary)?n.redactionSummary:void 0,relativePaths:L(n.relativePaths),metadata:f(p(n,"metadata"),"executionReceipt.metadata")})}applyAppendExecutionSummary(e){const n=p(e,"summary")??e;this.store.recordHeadlessExecutionSummary({eventId:o(n,"eventId"),attemptId:r(n,"attemptId"),executionId:r(n,"executionId"),jobId:o(n,"jobId")??e.jobId,publicSummary:r(n,"publicSummary"),reasonCode:o(n,"reasonCode"),createdAt:o(n,"createdAt")??e.createdAt,runtimeId:o(n,"runtimeId"),modelId:o(n,"modelId"),durationMs:h(n,"durationMs"),counts:p(n,"counts"),hashes:p(n,"hashes"),redactionSummary:Array.isArray(n.redactionSummary)?n.redactionSummary:void 0,relativePaths:L(n.relativePaths),metadata:f(p(n,"metadata"),"executionSummary.metadata")})}recordAppliedAudit(e){this.store.recordOperationAudit({receiptId:`receipt-${R(e.operationId)}`,jobId:e.jobId,operationId:e.operationId,idempotencyKey:`audit-${e.idempotencyKey??e.operationId}`,status:"applied",reasonCode:I.OK,appliedOperationIds:[e.operationId],createdAt:e.createdAt,metadata:{schemaVersion:S,contractVersion:g,jobId:e.jobId,operationId:e.operationId,status:"applied",reasonCode:I.OK}})}recordIgnoredAuditIfPossible(e,n){this.store.jobExists(e.jobId)&&this.store.recordOperationAudit({receiptId:`receipt-${R(e.operationId)}-ignored`,jobId:e.jobId,operationId:e.operationId,idempotencyKey:`audit-${e.idempotencyKey??e.operationId}-ignored`,status:"ignored",reasonCode:n,ignoredOperationIds:[e.operationId],createdAt:e.createdAt,metadata:{schemaVersion:S,contractVersion:g,jobId:e.jobId,operationId:e.operationId,status:"ignored",reasonCode:n}})}}function ee(t){const e=Le(t.metadata_json,t.iteration),n=v(t.metadata_json),a=P(n),i=le(n.currentStage)?n.currentStage:void 0,d=_e(n.stageHistory),u=Y(n.iteration)??Y(t.iteration);return O(e,"goalLedgerItems","goal_ledger_item_count"in t?t.goal_ledger_item_count:void 0),O(e,"blockedGoalLedgerItems","blocked_goal_ledger_item_count"in t?t.blocked_goal_ledger_item_count:void 0),O(e,"securityIssuesBlocked","security_issue_blocked_count"in t?t.security_issue_blocked_count:void 0),O(e,"goalVerifiers","goal_verifier_count"in t?t.goal_verifier_count:void 0),O(e,"failedVerifiers","failed_verifier_count"in t?t.failed_verifier_count:void 0),O(e,"unavailableVerifiers","unavailable_verifier_count"in t?t.unavailable_verifier_count:void 0),O(e,"goalReviewGates","goal_review_gate_count"in t?t.goal_review_gate_count:void 0),O(e,"failedRequiredReviewGates","failed_required_review_gate_count"in t?t.failed_required_review_gate_count:void 0),{jobId:s(t.job_id,80),kind:s(t.kind,40),status:s(t.status,40),publicSummary:s(t.summary??"No public summary recorded.",k),createdAt:s(t.created_at,80),updatedAt:s(t.updated_at,80),...t.next_run_at?{nextRunAt:s(t.next_run_at,80)}:{},...t.reason_code?{reasonCode:s(t.reason_code,80)}:{},...Object.keys(e).length>0?{counts:e}:{},...i?{currentStage:i}:{},...d.length>0?{stageHistory:d}:{},...u!==void 0?{iteration:u}:{},...Oe(a),publicSafe:!0}}function xe(t){const e={jobs:t.counts.jobs??t.jobCount,dueJobs:t.counts.dueJobs??t.dueJobCount,outboxPending:t.outboxPending,redactions:t.redactions.length},n=V({generatedAt:t.generatedAt,counts:e});return{syncVersion:fe,transportKind:"client_snapshot",snapshotId:`snapshot-${R(B(n).replace(/^sha256:/,"").slice(0,16))}`,generatedAt:s(t.generatedAt,80),publicSummary:`Client snapshot contains ${e.jobs} public jobs and ${e.outboxPending} pending outbox records.`,counts:e,hashes:{snapshot:B(n)},redactionEvidence:t.redactions.slice(0,20),publicSafe:!0}}function Oe(t){const e=t.runtimeSource==="explicit"||t.runtimeSource==="origin"||t.runtimeSource==="default"||t.runtimeSource==="fallback"||t.runtimeSource==="unspecified"?t.runtimeSource:void 0;return{...b(t,"runtimeId",80),...b(t,"modelId",120),...e?{runtimeSource:e}:{},...b(t,"headlessRuntime",80),...b(t,"headlessModel",120),...b(t,"headlessRuntimeReasonCode",100),...b(t,"originRuntime",80),...b(t,"originModel",120),...b(t,"workspaceId",120),...b(t,"workspaceLabel",80),...b(t,"projectId",120),...b(t,"projectLabel",80)}}function Te(t){return t?{workspaceId:t.workspaceId,workspaceLabel:t.workspaceLabel,projectId:t.projectId,projectLabel:t.projectLabel}:{}}function b(t,e,n){const a=t[e];return typeof a=="string"?{[e]:s(a,n)}:{}}function te(t){const e=P(v(t.metadata_json)),n=typeof e.goalId=="string"?s(e.goalId,100):void 0,a=typeof e.loopId=="string"?s(e.loopId,100):void 0;return{eventId:s(t.event_id,120),jobId:s(t.job_id,100),...n?{goalId:n}:{},...a?{loopId:a}:{},executionId:s(t.execution_id,120),publicSummary:s(t.public_summary,k),...t.reason_code?{reasonCode:s(t.reason_code,80)}:{},createdAt:s(t.created_at,80),cursor:s(t.cursor,260),...Object.keys(e).length>0?{metadata:e}:{},publicSafe:!0}}function Ne(t){return{attemptId:s(t.attempt_id,120),executionId:s(t.execution_id,120),jobId:s(t.job_id,100),status:s(t.status,40),...t.reason_code?{reasonCode:s(t.reason_code,80)}:{},...t.runtime_id?{runtimeId:s(t.runtime_id,80)}:{},...t.model_id?{modelId:s(t.model_id,120)}:{},retryCount:Math.max(0,Math.floor(t.retry_count)),attemptCount:Math.max(0,Math.floor(t.attempt_count)),maxAttempts:Math.max(1,Math.floor(t.max_attempts)),queuePriority:Math.max(-100,Math.min(100,Math.floor(t.queue_priority))),queueScope:t.queue_scope==="workspace"?"workspace":"project",...typeof t.duration_ms=="number"&&Number.isFinite(t.duration_ms)?{durationMs:Math.max(0,Math.floor(t.duration_ms))}:{},...ne("counts",v(t.counts_json)),...ne("hashes",v(t.hashes_json)),...Pe("redactionSummary",t.redaction_summary_json),...t.lease_id?{leaseId:s(t.lease_id,120)}:{},...t.runner_id?{runnerId:s(t.runner_id,120)}:{},...t.lease_expires_at?{leaseExpiresAt:s(t.lease_expires_at,80)}:{},...t.heartbeat_at?{heartbeatAt:s(t.heartbeat_at,80)}:{},publicSummary:s(t.public_summary??"No public headless execution summary recorded.",k),enqueuedAt:s(t.enqueued_at,80),...t.not_before_at?{notBeforeAt:s(t.not_before_at,80)}:{},...t.next_retry_at?{nextRetryAt:s(t.next_retry_at,80)}:{},...t.dead_lettered_at?{deadLetteredAt:s(t.dead_lettered_at,80)}:{},...t.dead_letter_reason_code?{deadLetterReasonCode:s(t.dead_letter_reason_code,80)}:{},publicSafe:!0}}function O(t,e,n){typeof n=="number"&&Number.isFinite(n)&&n>0&&(t[e]=Math.floor(n))}function Le(t,e){const n={};if(t)try{const a=JSON.parse(t),i=y(a)&&y(a.counts)?a.counts:void 0;if(i)for(const[d,u]of Object.entries(i))typeof u=="number"&&Number.isFinite(u)&&(n[d]=Math.max(0,Math.floor(u)))}catch{}return typeof e=="number"&&Number.isFinite(e)&&(n.iteration=Math.max(0,Math.floor(e))),n}function ke(t){const e=t.map(ve).filter(n=>!!n);return{preferredSource:e.length>0?"compact_summary":"recent_events",summaries:e,publicSafe:!0}}function ve(t){const e=v(t.metadata_json),n=y(e.hashes)?e.hashes:{},a=typeof n.summary=="string"?n.summary:t.sha256??void 0,i=typeof e.reasonCode=="string"?s(e.reasonCode,80):void 0;return{snapshotId:s(t.snapshot_id,120),...t.job_id?{jobId:s(t.job_id,100)}:{},...t.operation_id?{operationId:s(t.operation_id,120)}:{},capturedAt:s(t.captured_at,80),publicSummary:s(t.summary??"Context compacted with public-safe refs.",Z),...a?{summaryHash:s(a,120)}:{},...t.relative_path?{relativePath:s(t.relative_path,240)}:{},retainedRefs:je(e.relativePaths),artifactRefs:$(e.artifactRefs),evidenceRefs:$(e.evidenceRefs),...y(e.checkpoint)?{checkpoint:P(e.checkpoint)}:{},...i?{reasonCode:i}:{},publicSafe:!0}}function v(t){if(!t)return{};try{const e=JSON.parse(t);return y(e)?e:{}}catch{return{}}}function ne(t,e){const n=P(e);return Object.keys(n).length>0?{[t]:n}:{}}function Pe(t,e){if(!e)return{};try{const n=JSON.parse(e),a=$(n);return a.length>0?{[t]:a}:{}}catch{return{}}}function je(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"&&e.length>0).map(e=>s(e,240)).slice(0,D):[]}function $(t){return Array.isArray(t)?t.map(H).filter(e=>e!==void 0).slice(0,D):[]}function P(t){const e={};for(const[n,a]of Object.entries(t).slice(0,20)){if(K.test(n))continue;const i=H(a);i!==void 0&&(e[s(n,80)]=i)}return e}function H(t){if(!M(t)){if(typeof t=="string")return s(t,240);if(typeof t=="number"&&Number.isFinite(t))return Math.floor(t);if(typeof t=="boolean"||t===null)return t;if(Array.isArray(t))return t.map(H).filter(n=>n!==void 0).slice(0,20);if(y(t))return P(t)}}function Me(t){return y(t)?Array.isArray(t.operationLogs)?t.operationLogs.filter(y):y(t.operationLog)?[t.operationLog]:Array.isArray(t.operations)?[t]:[]:[]}function Ue(t){const e=[],n=[],a=[],i=[],d=[],u=new Map;if(t.schemaVersion!==S)throw new _("Invalid continuity operation log schema version.",[I.INVALID_SCHEMA_VERSION],{mutationBlocked:!0});if(t.contractVersion!==g)throw new _("Invalid continuity operation log contract version.",[I.INVALID_CONTRACT_VERSION],{mutationBlocked:!0});if(t.publicSafe!==!0)throw new _("Continuity operation log is not marked public-safe.",[I.UNSAFE_FIELD_REJECTED],{mutationBlocked:!0});const m=f(p(t,"metadata"),"log.metadata");if(!Array.isArray(t.operations))return{operations:[],skippedOperations:a,ignoredOperationIds:i,reasonCodes:e,redactions:n,logMetadata:m};for(const c of[...t.operations].filter(y).sort(De)){const l=Ve(c),U=V(l),N=u.get(l.operationId);if(N!==void 0){if(N===U){i.push(l.operationId),e.push(I.IDEMPOTENCY_REPLAY);continue}throw n.push({field:"operationId",reason:"unknown_required_operation",evidence:"operation-denied",publicSafe:!0}),new _("Duplicate continuity operation ID with conflicting payload.",[I.DUPLICATE_OPERATION_ID],{redactions:n,mutationBlocked:!0})}if(u.set(l.operationId,U),f(l.metadata,"operation.metadata"),!he.has(l.type)){const j=c.required===!0;if(c.forwardCompatible===!0&&!j){a.push(l),i.push(l.operationId),e.push(I.UNKNOWN_OPERATION_IGNORED);continue}throw n.push({field:l.type,reason:"unknown_required_operation",evidence:"operation-denied",publicSafe:!0}),new _(`Unknown required continuity operation ${l.type}.`,[I.UNKNOWN_OPERATION_REQUIRED],{redactions:n,mutationBlocked:!0})}d.push(l)}return{operations:d,skippedOperations:a,ignoredOperationIds:i,reasonCodes:e,redactions:n,logMetadata:m}}function Ve(t){if(t.schemaVersion!==S)throw new _("Invalid continuity operation schema version.",[I.INVALID_SCHEMA_VERSION],{mutationBlocked:!0});if(t.contractVersion!==g)throw new _("Invalid continuity operation contract version.",[I.INVALID_CONTRACT_VERSION],{mutationBlocked:!0});if(t.publicSafe!==!0)throw new _("Continuity operation is not marked public-safe.",[I.UNSAFE_FIELD_REJECTED],{mutationBlocked:!0});const e=r(t,"type"),n=r(t,"operationId"),a=r(t,"jobId"),i=$e(t,"sequence"),d=r(t,"createdAt");return{...t,type:e,operationId:n,jobId:a,schemaVersion:S,contractVersion:g,sequence:i,createdAt:d,idempotencyKey:o(t,"idempotencyKey"),metadata:p(t,"metadata"),publicSafe:!0}}function f(t,e){if(!t)return{};const n={},a=[];for(const[i,d]of Object.entries(t)){const u=qe(i),m=M(d),c=u??m;if(c){a.push({field:`${e}.${i}`,reason:c,evidence:u?"key-denied":"value-denied",publicSafe:!0});continue}n[i]=d}if(a.length>0)throw new _(`Continuity metadata rejected unsafe public fields at ${e}.`,[I.UNSAFE_FIELD_REJECTED],{redactions:a,mutationBlocked:!0});return n}function qe(t){return!Ae.has(t)||K.test(t)?"unsafe_key":null}function M(t){if(typeof t=="string")return G.test(t)?"private_url":be.test(t)?"local_absolute_path":J.test(t)?"secret":Q.test(t)?"prompt_body":W.test(t)?"raw_log":X.test(t)?"protected_field":null;if(t===null||typeof t=="number"||typeof t=="boolean")return null;if(Array.isArray(t)){for(const e of t){const n=M(e);if(n)return n}return null}if(y(t)){for(const[e,n]of Object.entries(t)){if(K.test(e))return"unsafe_key";const a=M(n);if(a)return a}return null}return"unsafe_key"}function Ke(t){const e=[];for(const n of t)if(n.metadata_json)try{const i=JSON.parse(n.metadata_json).redactionEvidence;if(!Array.isArray(i))continue;for(const d of i)y(d)&&(typeof d.field!="string"||typeof d.reason!="string"||typeof d.evidence!="string"||e.push({field:s(d.field,120),reason:d.reason,evidence:d.evidence,publicSafe:!0}))}catch{}return e}function De(t,e){const n=typeof t.sequence=="number"?t.sequence:0,a=typeof e.sequence=="number"?e.sequence:0;if(n!==a)return n-a;const i=String(t.createdAt??"").localeCompare(String(e.createdAt??""));return i!==0?i:String(t.operationId??"").localeCompare(String(e.operationId??""))}function y(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function T(t,e){const n=t[e];if(!y(n))throw new _(`Continuity operation missing object field ${e}.`,[I.INVALID_SCHEMA_VERSION],{mutationBlocked:!0});return n}function p(t,e){const n=t[e];return y(n)?n:void 0}function r(t,e){const n=t[e];if(typeof n!="string"||n.length===0)throw new _(`Continuity operation missing string field ${e}.`,[I.INVALID_SCHEMA_VERSION],{mutationBlocked:!0});return n}function o(t,e){const n=t[e];return typeof n=="string"&&n.length>0?n:void 0}function $e(t,e){const n=t[e];if(typeof n!="number"||!Number.isFinite(n))throw new _(`Continuity operation missing numeric field ${e}.`,[I.INVALID_SCHEMA_VERSION],{mutationBlocked:!0});return n}function h(t,e){const n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:void 0}function He(t,e){const n=t[e];return typeof n=="boolean"?n:void 0}function L(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"&&e.length>0):[]}function ae(t){return Array.isArray(t)?t.filter(y):[]}function we(t){return C(t,Z,"compact_context.publicSummary")}function C(t,e,n){const a=t.trim();if(!a)throw new _(`Continuity compaction missing public text at ${n}.`,[I.INVALID_SCHEMA_VERSION],{mutationBlocked:!0});const i=M(a);if(i)throw new _(`Continuity compaction rejected unsafe public text at ${n}.`,[I.UNSAFE_FIELD_REJECTED],{redactions:[{field:n,reason:i,evidence:"value-denied",publicSafe:!0}],mutationBlocked:!0});return a.length<=e?a:`${a.slice(0,e)}\u2026[TRUNCATED:${a.length-e}]`}function w(t,e){return C(t,120,e)}function oe(t,e,n){return E(L(t).map((a,i)=>F(a,`${e}[${i}]`))).slice(0,n)}function F(t,e){const n=C(t,240,e).replace(/\\/g,"/"),a=n.split("/").filter(Boolean),i=/^[A-Za-z]:\//.test(n);if(n.startsWith("/")||n.startsWith("~")||n.includes("://")||i||a.includes(".."))throw new _(`Continuity compaction rejected non-relative ref at ${e}.`,[I.UNSAFE_FIELD_REJECTED],{redactions:[{field:e,reason:"private_url",evidence:"value-denied",publicSafe:!0}],mutationBlocked:!0});return n}function Fe(t){const e=[],n=p(t,"summaryRef");n&&e.push(re(n,"compact_context.summaryRef","context_summary"));for(const[i,d]of ae(t.artifactRefs).entries())e.push(re(d,`compact_context.artifactRefs[${i}]`));const a=new Set;return e.filter(i=>{const d=`${i.kind}:${i.relativePath}`;return a.has(d)?!1:(a.add(d),!0)}).slice(0,Se)}function re(t,e,n="context_summary"){const a=C(o(t,"kind")??n,60,`${e}.kind`),i=F(r(t,"relativePath"),`${e}.relativePath`),d=o(t,"sha256"),u=o(t,"publicSummary");return{kind:a,relativePath:i,...d?{sha256:w(d,`${e}.sha256`)}:{},...u?{publicSummary:C(u,k,`${e}.publicSummary`)}:{},publicSafe:!0}}function Be(t){return ae(t.evidenceIndex).map((e,n)=>{const a=`compact_context.evidenceIndex[${n}]`,i=F(r(e,"relativePath"),`${a}.relativePath`),d=o(e,"refId")??`evidence-${R(i)}`,u=o(e,"reasonCode")??o(t,"reasonCode")??"context-compacted",m=o(e,"sha256"),c=o(e,"publicSummary");return{refId:C(d,120,`${a}.refId`),relativePath:i,reasonCode:C(u,80,`${a}.reasonCode`),...m?{sha256:w(m,`${a}.sha256`)}:{},...c?{publicSummary:C(c,k,`${a}.publicSummary`)}:{},publicSafe:!0}}).slice(0,ge)}function Ye(t,e,n,a){const i=p(t,"checkpoint")??{},d=o(i,"checkpointId")??`checkpoint-${R(t.operationId)}`,u=o(i,"capturedAt")??t.createdAt,m={checkpointId:C(d,120,"compact_context.checkpoint.checkpointId"),capturedAt:C(u,80,"compact_context.checkpoint.capturedAt"),retainedRefCount:e.length,artifactRefCount:n.length,evidenceRefCount:a.length,publicSafe:!0};for(const c of["eventCursor","receiptCursor"]){const l=o(i,c);l&&(m[c]=C(l,120,`compact_context.checkpoint.${c}`))}for(const c of["recentEventCount","recentReceiptCount"]){const l=h(i,c);l!==void 0&&(m[c]=Math.max(0,Math.floor(l)))}return m}function B(t){return`sha256:${ie("sha256").update(t).digest("hex")}`}function E(t){return Array.from(new Set(t))}function R(t){return t.replace(/[^a-z0-9_.:-]+/gi,"-").slice(0,120)}function s(t,e){const n=ze(t);return n.length<=e?n:`${n.slice(0,e)}\u2026[TRUNCATED:${n.length-e}]`}function ze(t){return G.test(t)||J.test(t)||W.test(t)||Q.test(t)||X.test(t)?"[REDACTED]":t}function V(t){return Array.isArray(t)?`[${t.map(V).join(",")}]`:y(t)?`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${V(t[e])}`).join(",")}}`:JSON.stringify(t)}function q(t,e){if(t instanceof _)return t;if(t instanceof pe)return new _(t.message,[t.reasonCode],{mutationBlocked:t.mutationBlocked,cause:t});const n=t instanceof Error?t.message:String(t);return new _(`${e} ${n}`,[I.SQLITE_MUTATION_FAILED],{cause:t})}export{g as CONTINUITY_CLIENT_CONTRACT_VERSION,fe as CONTINUITY_CLIENT_PORTAL_SYNC_ENVELOPE_VERSION,I as CONTINUITY_CLIENT_REASON_CODES,S as CONTINUITY_CLIENT_SCHEMA_VERSION,Ze as CONTINUITY_INVOKE_WATCH_DEFAULT_LIMIT,tt as CONTINUITY_INVOKE_WATCH_DEFAULT_MAX_IDLE_POLLS,nt as CONTINUITY_INVOKE_WATCH_DEFAULT_MAX_POLLS,et as CONTINUITY_INVOKE_WATCH_DEFAULT_POLL_INTERVAL_MS,z as CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_LIMIT,ye as CONTINUITY_PROCESSING_SUMMARY_WATCH_DEFAULT_POLL_INTERVAL_MS,Re as ContinuityClientStateStore,_ as ContinuityClientStateStoreError,at as continuitySqliteExists,ot as createContinuityHardStopSnapshot,rt as openContinuityClientStateStore};
|