@ornexus/neocortex 4.60.71 → 4.60.73
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 +1211 -1217
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +2 -2
- package/packages/client/dist/adapters/adapter-registry.js +108 -1
- package/packages/client/dist/adapters/antigravity-adapter.js +77 -2
- package/packages/client/dist/adapters/claude-code-adapter.js +79 -3
- package/packages/client/dist/adapters/codex-adapter.js +80 -2
- package/packages/client/dist/adapters/cursor-adapter.js +115 -4
- package/packages/client/dist/adapters/gemini-adapter.js +71 -2
- package/packages/client/dist/adapters/grok-adapter.js +42 -2
- package/packages/client/dist/adapters/index.js +22 -1
- package/packages/client/dist/adapters/platform-detector.js +212 -1
- package/packages/client/dist/adapters/target-adapter.js +12 -0
- package/packages/client/dist/adapters/vscode-adapter.js +72 -2
- package/packages/client/dist/agent/refresh-stubs.js +417 -2
- package/packages/client/dist/agent/update-agent-yaml.js +102 -1
- package/packages/client/dist/agent/update-description.js +251 -1
- package/packages/client/dist/cache/crypto-utils.js +78 -1
- package/packages/client/dist/cache/encrypted-cache.js +108 -1
- package/packages/client/dist/cache/in-memory-asset-cache.js +73 -1
- package/packages/client/dist/cache/index.js +13 -1
- package/packages/client/dist/cache/protected-pi-boundary.js +44 -1
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +127 -2
- package/packages/client/dist/checkpoint/index.js +11 -1
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +32 -1
- package/packages/client/dist/cli.js +358 -5
- package/packages/client/dist/commands/activate.js +394 -8
- package/packages/client/dist/commands/architecture-answers.js +274 -7
- package/packages/client/dist/commands/architecture-memory-terminal.js +253 -4
- package/packages/client/dist/commands/architecture-non-ui-continuation.js +100 -1
- package/packages/client/dist/commands/architecture-root-binding.js +51 -1
- package/packages/client/dist/commands/cache-status.js +112 -2
- package/packages/client/dist/commands/config.js +88 -1
- package/packages/client/dist/commands/doctor.js +635 -2
- package/packages/client/dist/commands/invoke.js +7200 -143
- package/packages/client/dist/commands/legacy-epic-terminal.js +71 -1
- package/packages/client/dist/commands/legacy-story-branch.js +96 -1
- package/packages/client/dist/commands/legacy-story-pr.js +276 -1
- package/packages/client/dist/commands/mutation-qa-checkpoint.js +285 -5
- package/packages/client/dist/commands/mutation-qa-gate.js +54 -1
- package/packages/client/dist/commands/mutation-qa-git.js +41 -1
- package/packages/client/dist/commands/mutation-qa-policy.js +102 -1
- package/packages/client/dist/commands/mutation-qa-run.js +219 -3
- package/packages/client/dist/commands/mutation-qa-verdict.js +149 -1
- package/packages/client/dist/commands/mutation-qa.js +220 -1
- package/packages/client/dist/commands/native-agent.js +136 -2
- package/packages/client/dist/commands/planning-diagnosis.js +43 -1
- package/packages/client/dist/commands/planning-generation.js +53 -1
- package/packages/client/dist/commands/planning-memory.js +40 -1
- package/packages/client/dist/commands/planning-research.js +42 -1
- package/packages/client/dist/commands/private-session.js +75 -1
- package/packages/client/dist/commands/property-qa.js +139 -1
- package/packages/client/dist/commands/qa-control.js +16 -1
- package/packages/client/dist/commands/qa-scan-browser-launch.js +55 -1
- package/packages/client/dist/commands/qa-scan-browser-route.js +28 -1
- package/packages/client/dist/commands/qa-scan-capture.js +663 -5
- package/packages/client/dist/commands/qa-scan-discovery.js +376 -3
- package/packages/client/dist/commands/qa-scan-review.js +826 -5
- package/packages/client/dist/commands/qa-synthetic-backend-capture.js +266 -1
- package/packages/client/dist/commands/qa-synthetic-backend-review.js +297 -1
- package/packages/client/dist/commands/qa-synthetic-backend.js +340 -2
- package/packages/client/dist/commands/qa-synthetic-broker-port.js +85 -1
- package/packages/client/dist/commands/qa-synthetic-browser.js +39 -1
- package/packages/client/dist/commands/refactor-characterization.js +671 -21
- package/packages/client/dist/commands/refactor-commit.js +399 -13
- package/packages/client/dist/commands/refactor-control.js +142 -3
- package/packages/client/dist/commands/refactor-cpp-assertions.js +93 -3
- package/packages/client/dist/commands/refactor-review.js +283 -5
- package/packages/client/dist/commands/refactor-root.js +588 -7
- package/packages/client/dist/commands/refresh-memory.js +156 -3
- package/packages/client/dist/commands/shared-architecture-non-ui.js +24 -1
- package/packages/client/dist/commands/story-checkpoint.js +171 -1
- package/packages/client/dist/config/profile-directory.js +13 -1
- package/packages/client/dist/config/resolver-selection.js +278 -1
- package/packages/client/dist/config/secure-config.js +281 -12
- package/packages/client/dist/config.js +223 -2
- package/packages/client/dist/constants.js +25 -1
- package/packages/client/dist/context/context-collector.js +222 -2
- package/packages/client/dist/context/context-sanitizer.js +145 -1
- package/packages/client/dist/continuity/autonomous-governance.js +285 -1
- package/packages/client/dist/continuity/continuity-client-state-store.js +1587 -1
- package/packages/client/dist/continuity/invoke-hooks.js +129 -1
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +12 -2
- package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +12 -2
- package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +12 -2
- package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +12 -2
- package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +12 -2
- package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +10 -2
- package/packages/client/dist/continuity/native-agent-store.js +69 -1
- package/packages/client/dist/continuity/native-agent-types.js +90 -1
- package/packages/client/dist/continuity/outbox.js +117 -1
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +172 -1
- package/packages/client/dist/continuity/sqlite-store.d.ts +22 -1
- package/packages/client/dist/continuity/sqlite-store.js +2612 -81
- package/packages/client/dist/effects/invoke-effect-gateways.js +62 -1
- package/packages/client/dist/errors/error-messages.js +228 -2
- package/packages/client/dist/errors/frontmatter-diagnostics.js +125 -1
- package/packages/client/dist/evidence/collector.js +347 -1
- package/packages/client/dist/evidence/index.js +1 -1
- package/packages/client/dist/evidence/types.js +2 -0
- package/packages/client/dist/fs/atomic-state-replace.js +152 -1
- package/packages/client/dist/generated/command-registry.generated.js +4076 -1
- package/packages/client/dist/generated/public-projections.generated.js +149 -1
- package/packages/client/dist/git/direct-yolo-c01-retry.js +138 -4
- package/packages/client/dist/git/direct-yolo-checkpoint.js +1234 -4
- package/packages/client/dist/git/epic-operational-state.d.ts +198 -0
- package/packages/client/dist/git/epic-operational-state.js +672 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.js +14 -1
- package/packages/client/dist/git/git-lifecycle-store.d.ts +9 -1
- package/packages/client/dist/git/git-lifecycle-store.js +1981 -9
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +259 -9
- package/packages/client/dist/git/legacy-story-branch-setup.js +348 -4
- package/packages/client/dist/git/legacy-story-pr.js +3538 -70
- package/packages/client/dist/git/secure-filesystem-boundary.js +182 -5
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +284 -1
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +237 -1
- package/packages/client/dist/i18n/first-run.js +60 -2
- package/packages/client/dist/index.js +47 -1
- package/packages/client/dist/invocation/invoke-input.js +24 -1
- package/packages/client/dist/license/index.js +5 -1
- package/packages/client/dist/license/license-client.js +304 -1
- package/packages/client/dist/machine/fingerprint.js +163 -2
- package/packages/client/dist/machine/index.js +5 -1
- package/packages/client/dist/memory/project-memory-path-scope.js +62 -1
- package/packages/client/dist/memory/project-memory-writer.js +1007 -40
- package/packages/client/dist/memory/shared-project-memory-types.js +253 -2
- package/packages/client/dist/migrations/epic-lifecycle-backfill.d.ts +97 -0
- package/packages/client/dist/migrations/epic-lifecycle-backfill.js +1060 -0
- package/packages/client/dist/migrations/project-identity-capability.js +50 -1
- package/packages/client/dist/migrations/project-identity.js +493 -7
- package/packages/client/dist/migrations/state-json.d.ts +22 -0
- package/packages/client/dist/migrations/state-json.js +393 -4
- package/packages/client/dist/policy/architecture-policy.js +184 -2
- package/packages/client/dist/policy/index.js +7 -1
- package/packages/client/dist/policy/shared-policy-types.js +38 -0
- package/packages/client/dist/policy/subagent-approval-policy.js +55 -1
- package/packages/client/dist/private-execution/clean-baseline.js +279 -2
- package/packages/client/dist/private-execution/codex-executor.js +574 -4
- package/packages/client/dist/private-execution/codex-host-runtime.js +137 -1
- package/packages/client/dist/private-execution/codex-lifecycle-guard.js +97 -1
- package/packages/client/dist/private-execution/codex-scoped-permissions.js +128 -1
- package/packages/client/dist/private-execution/delivery.js +410 -13
- package/packages/client/dist/private-execution/diagnosis-root-review.js +294 -2
- package/packages/client/dist/private-execution/frozen-handoff.js +19 -6
- package/packages/client/dist/private-execution/implementation-validation.js +106 -2
- package/packages/client/dist/private-execution/isolated-admission.js +1432 -23
- package/packages/client/dist/private-execution/isolated-session.js +725 -5
- package/packages/client/dist/private-execution/isolated-workspace.js +122 -1
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +409 -3
- package/packages/client/dist/private-execution/pact-test-source.js +258 -1
- package/packages/client/dist/private-execution/pact-toolchain.js +290 -3
- package/packages/client/dist/private-execution/pact-write-set.js +172 -1
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +406 -5
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +462 -5
- package/packages/client/dist/private-execution/planning-memory-checkpoint.js +139 -3
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +325 -4
- package/packages/client/dist/private-execution/private-report-store.js +42 -1
- package/packages/client/dist/private-execution/property-test-candidate.js +103 -1
- package/packages/client/dist/private-execution/property-test-choices.js +147 -3
- package/packages/client/dist/private-execution/property-test-plan.js +271 -7
- package/packages/client/dist/private-execution/property-test-review.js +258 -4
- package/packages/client/dist/private-execution/property-test-runtime.js +372 -9
- package/packages/client/dist/private-execution/property-test-source.js +139 -1
- package/packages/client/dist/private-execution/root-checkpoint.js +80 -1
- package/packages/client/dist/private-execution/root-memory-git-evidence.js +135 -3
- package/packages/client/dist/private-execution/root-memory-recovery.js +93 -1
- package/packages/client/dist/private-execution/root-memory-request.js +29 -1
- package/packages/client/dist/private-execution/root-memory-scope.js +178 -1
- package/packages/client/dist/private-execution/root-memory-writer.js +102 -1
- package/packages/client/dist/private-execution/session-lease.js +63 -1
- package/packages/client/dist/private-execution/shared-private-admission-types.js +50 -1
- package/packages/client/dist/private-execution/shared-project-validation.js +66 -1
- package/packages/client/dist/private-execution/shared-root-memory-types.js +61 -1
- package/packages/client/dist/private-execution/ux-source-validation.js +372 -1
- package/packages/client/dist/private-execution/ux-write-set.js +191 -2
- package/packages/client/dist/registry/registry-reconciliation.js +234 -1
- package/packages/client/dist/resilience/circuit-breaker.js +170 -1
- package/packages/client/dist/resilience/degradation-manager.js +164 -1
- package/packages/client/dist/resilience/freshness-indicator.js +100 -1
- package/packages/client/dist/resilience/index.js +8 -1
- package/packages/client/dist/resilience/recovery-detector.js +74 -1
- package/packages/client/dist/resolvers/asset-resolver.js +13 -0
- package/packages/client/dist/resolvers/local-resolver.js +255 -8
- package/packages/client/dist/resolvers/remote-resolver.js +233 -1
- package/packages/client/dist/runner/binary.js +404 -1
- package/packages/client/dist/runner/cli.js +1571 -17
- package/packages/client/dist/runner/headless/adapters.js +49 -1
- package/packages/client/dist/runner/headless/claude-code-adapter.js +79 -1
- package/packages/client/dist/runner/headless/continuous-story-workspace.js +1628 -11
- package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +69 -1
- package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +110 -2
- package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +1236 -8
- package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +318 -2
- package/packages/client/dist/runner/headless/fake-adapter.js +62 -1
- package/packages/client/dist/runner/headless/managed-profile.js +132 -1
- package/packages/client/dist/runner/headless/opencode-adapter.js +145 -2
- package/packages/client/dist/runner/headless/prepared-story-bundle.js +43 -1
- package/packages/client/dist/runner/headless/resolver.js +231 -1
- package/packages/client/dist/runner/headless/run-yolo.js +885 -1
- package/packages/client/dist/runner/headless/run-yoloop.js +2574 -2
- package/packages/client/dist/runner/headless/types.js +574 -2
- package/packages/client/dist/runner/local-console.js +396 -13
- package/packages/client/dist/runner/portal-sync-worker.js +1487 -1
- package/packages/client/dist/runner/project-identity.js +106 -1
- package/packages/client/dist/runner/scheduler.js +995 -7
- package/packages/client/dist/runner-cli.js +18 -2
- package/packages/client/dist/state/invocation-shape.js +13 -1
- package/packages/client/dist/state/project-state-snapshot.d.ts +8 -0
- package/packages/client/dist/state/project-state-snapshot.js +1731 -1
- package/packages/client/dist/state/state-json-repair.js +785 -3
- package/packages/client/dist/state/structural-dag-projection.js +118 -1
- package/packages/client/dist/telemetry/index.js +5 -1
- package/packages/client/dist/telemetry/offline-queue.js +131 -1
- package/packages/client/dist/tier/index.js +5 -1
- package/packages/client/dist/tier/tier-aware-client.js +405 -1
- package/packages/client/dist/types/index.js +38 -1
- package/packages/client/dist/version.js +27 -1
- package/packages/client/dist/yolo/change-evidence-collector.js +126 -1
- package/packages/client/dist/yolo/invoke-envelope-capability.js +62 -1
- package/packages/client/dist/yolo/spec-envelope-collector.js +184 -5
- package/packages/client/dist/yoloop/discovery-hook.js +300 -2
- package/packages/client/dist/yoloop/index.js +8 -1
- package/packages/client/dist/yoloop/invoke-hooks.js +669 -12
- package/packages/client/dist/yoloop/shared-discover-epics.js +1007 -1
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +65 -1
- package/packages/client/dist/yoloop/shared-yoloop-types.js +33 -1
- package/packages/client/dist/yoloop/target-normalization.js +180 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +8 -0
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1920 -1
- package/packages/shared/dist/yolo/spec-envelope.js +3 -6
- 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 +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1 +1,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};
|
|
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
|
+
}
|