@ornexus/neocortex 4.60.58 → 4.60.60
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/sbom.cdx.json +241 -82
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +6 -2
- package/packages/client/dist/cli.js +37 -4
- package/packages/client/dist/commands/architecture-answers.d.ts +26 -0
- package/packages/client/dist/commands/architecture-answers.js +7 -0
- package/packages/client/dist/commands/architecture-memory-terminal.d.ts +21 -0
- package/packages/client/dist/commands/architecture-memory-terminal.js +2 -0
- package/packages/client/dist/commands/architecture-root-binding.d.ts +2 -0
- package/packages/client/dist/commands/architecture-root-binding.js +1 -0
- package/packages/client/dist/commands/invoke.d.ts +43 -2
- package/packages/client/dist/commands/invoke.js +98 -70
- package/packages/client/dist/commands/legacy-story-branch.d.ts +10 -0
- package/packages/client/dist/commands/legacy-story-branch.js +1 -0
- package/packages/client/dist/commands/legacy-story-pr.d.ts +18 -0
- package/packages/client/dist/commands/legacy-story-pr.js +1 -0
- package/packages/client/dist/commands/planning-diagnosis.d.ts +4 -0
- package/packages/client/dist/commands/planning-diagnosis.js +1 -0
- package/packages/client/dist/commands/planning-generation.d.ts +2 -0
- package/packages/client/dist/commands/planning-generation.js +1 -0
- package/packages/client/dist/commands/planning-research.d.ts +3 -0
- package/packages/client/dist/commands/planning-research.js +1 -0
- package/packages/client/dist/commands/refresh-memory.d.ts +10 -0
- package/packages/client/dist/commands/refresh-memory.js +3 -1
- package/packages/client/dist/commands/story-checkpoint.d.ts +1 -1
- package/packages/client/dist/commands/story-checkpoint.js +1 -1
- package/packages/client/dist/config/profile-directory.d.ts +2 -0
- package/packages/client/dist/config/profile-directory.js +1 -0
- package/packages/client/dist/config/secure-config.js +5 -5
- package/packages/client/dist/continuity/runner-bootstrap-policy.d.ts +1 -1
- package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -1
- package/packages/client/dist/generated/command-registry.generated.d.ts +11 -11
- package/packages/client/dist/generated/command-registry.generated.js +1 -1
- package/packages/client/dist/generated/public-projections.generated.d.ts +20 -20
- package/packages/client/dist/generated/public-projections.generated.js +1 -1
- package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +9 -0
- package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -4
- package/packages/client/dist/git/git-lifecycle-step-ids.d.ts +5 -0
- package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -1
- package/packages/client/dist/git/git-lifecycle-store.d.ts +16 -0
- package/packages/client/dist/git/git-lifecycle-store.js +9 -4
- package/packages/client/dist/git/legacy-story-branch-checkpoint.d.ts +38 -0
- package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -0
- package/packages/client/dist/git/legacy-story-branch-setup.d.ts +56 -0
- package/packages/client/dist/git/legacy-story-branch-setup.js +4 -0
- package/packages/client/dist/git/legacy-story-pr.d.ts +514 -0
- package/packages/client/dist/git/legacy-story-pr.js +62 -0
- package/packages/client/dist/git/secure-filesystem-boundary.d.ts +2 -0
- package/packages/client/dist/git/secure-filesystem-boundary.js +34 -7
- package/packages/client/dist/machine/fingerprint.js +2 -2
- package/packages/client/dist/memory/project-memory-writer.js +17 -17
- package/packages/client/dist/memory/shared-project-memory-types.d.ts +23 -13
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -2
- package/packages/client/dist/migrations/state-json.d.ts +23 -1
- package/packages/client/dist/migrations/state-json.js +4 -4
- package/packages/client/dist/private-execution/delivery.d.ts +131 -3
- package/packages/client/dist/private-execution/delivery.js +2 -2
- package/packages/client/dist/private-execution/isolated-admission.d.ts +28 -1
- package/packages/client/dist/private-execution/isolated-admission.js +11 -2
- package/packages/client/dist/private-execution/isolated-session.js +2 -2
- package/packages/client/dist/private-execution/isolated-workspace.d.ts +8 -0
- package/packages/client/dist/private-execution/isolated-workspace.js +1 -1
- package/packages/client/dist/private-execution/pact-consumer-runtime.d.ts +19 -0
- package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -0
- package/packages/client/dist/private-execution/pact-test-source.d.ts +9 -0
- package/packages/client/dist/private-execution/pact-test-source.js +1 -0
- package/packages/client/dist/private-execution/pact-toolchain.d.ts +37 -0
- package/packages/client/dist/private-execution/pact-toolchain.js +3 -0
- package/packages/client/dist/private-execution/pact-write-set.d.ts +24 -0
- package/packages/client/dist/private-execution/pact-write-set.js +1 -0
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.d.ts +38 -0
- package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -0
- package/packages/client/dist/private-execution/planning-generation-checkpoint.d.ts +34 -0
- package/packages/client/dist/private-execution/planning-generation-checkpoint.js +4 -0
- package/packages/client/dist/private-execution/planning-research-checkpoint.d.ts +38 -0
- package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -0
- package/packages/client/dist/private-execution/ux-source-validation.d.ts +2 -0
- package/packages/client/dist/private-execution/ux-source-validation.js +1 -0
- package/packages/client/dist/private-execution/ux-write-set.d.ts +30 -0
- package/packages/client/dist/private-execution/ux-write-set.js +2 -0
- package/packages/client/dist/resolvers/remote-resolver.d.ts +5 -22
- package/packages/client/dist/resolvers/remote-resolver.js +1 -1
- package/packages/client/dist/runner/project-identity.js +1 -1
- package/packages/client/dist/runner/scheduler.d.ts +20 -0
- package/packages/client/dist/runner/scheduler.js +7 -7
- package/packages/client/dist/state/project-state-snapshot.js +1 -1
- package/packages/client/dist/yolo/change-evidence-collector.d.ts +8 -0
- package/packages/client/dist/yolo/change-evidence-collector.js +1 -1
- package/packages/client/dist/yolo/spec-envelope-collector.d.ts +2 -0
- package/packages/client/dist/yolo/spec-envelope-collector.js +3 -3
- package/packages/client/dist/yoloop/invoke-hooks.js +7 -7
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +15 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +5 -1
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1
- package/packages/shared/dist/generated/command-registry.generated.js +12 -10
- package/packages/shared/dist/generated/invocation-schema.generated.js +14 -6
- package/packages/shared/dist/generated/public-projections.generated.js +18 -18
- package/packages/shared/dist/invocation/index.d.ts +1 -1
- package/packages/shared/dist/merge-readiness/c12-remote-observation.js +80 -0
- package/packages/shared/dist/planning/diagnosis-state.js +143 -0
- package/packages/shared/dist/yoloop/branch-unit.js +23 -1
- 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
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* setSecureFilePermissions helper from secure-config.ts.
|
|
24
24
|
*/
|
|
25
25
|
import type { YoloopPersistedCounters, YoloopPersistedState, YoloopRecoveryAttemptRef, YoloopRunControlV1, YoloopRunTransitionReceiptV1, YoloopStateOperation, YoloopStateUpdate } from './shared-yoloop-types.js';
|
|
26
|
-
export declare const YOLOOP_PERSISTENCE_CAPABILITIES: readonly ["rollover-session-v1", "successor-transition-v1"];
|
|
26
|
+
export declare const YOLOOP_PERSISTENCE_CAPABILITIES: readonly ["rollover-session-v1", "successor-transition-v1", "continuation-replan-v1"];
|
|
27
27
|
type YoloopSessionRolloverOperation = Extract<YoloopStateOperation, {
|
|
28
28
|
readonly type: 'rollover_session';
|
|
29
29
|
}>;
|
|
@@ -82,6 +82,7 @@ export declare class YoloopClientStateStore {
|
|
|
82
82
|
private acknowledgeContinuationAuthority;
|
|
83
83
|
private repairContinuationAuthority;
|
|
84
84
|
private switchContinuationPolicy;
|
|
85
|
+
private replanContinuationSession;
|
|
85
86
|
/**
|
|
86
87
|
* Append a new recovery attempt to `state.recovery_history[]`. FIFO cap
|
|
87
88
|
* via `capRecoveryHistory`. Idempotent against retry storms: duplicate
|
|
@@ -168,6 +169,9 @@ export declare class YoloopClientStateStore {
|
|
|
168
169
|
/** Remove `yoloop-state.json.tmp.*` orphans from crashed writes. */
|
|
169
170
|
private cleanupOrphanTempFiles;
|
|
170
171
|
}
|
|
172
|
+
export declare function buildYoloopContinuationReplanPayloadHash(payload: Omit<Extract<YoloopStateOperation, {
|
|
173
|
+
readonly type: 'replan_continuation_session';
|
|
174
|
+
}>, 'type' | 'payloadHash' | 'receipt'>): string;
|
|
171
175
|
export declare function fingerprintYoloopSession(session: NonNullable<YoloopPersistedCounters['currentSession']>): string;
|
|
172
176
|
export declare function buildYoloopSessionRolloverPayloadHash(operation: Omit<YoloopSessionRolloverOperation, 'type' | 'payloadHash' | 'receipt'>): string;
|
|
173
177
|
export declare function buildYoloopSuccessorRunPayloadHash(operation: Omit<Extract<YoloopStateOperation, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{promises as u}from"node:fs";import{createHash as b}from"node:crypto";import{join as _,dirname as V}from"node:path";import{hostname as S}from"node:os";import{setSecureFilePermissions as $,setSecureDirPermissions as q}from"../config/secure-config.js";import{RECOVERY_HISTORY_CAP as C}from"./shared-yoloop-types.js";const B=".neocortex",g="yoloop-state.json",j="yoloop.lock",J="yoloop-state.mutation.lock",P="yoloop-session-rollover.json",A="yoloop-successor-transition.json",z=360*60*1e3,W=3e4,G=200,x=Object.freeze(["rollover-session-v1","successor-transition-v1"]),X={warn:()=>{},info:()=>{},error:()=>{}};function w(t){const e=t.slice(0,10);return{version:"1.1",lastResetAt:t,storiesRunToday:0,iterationsToday:0,iterationsTodayDate:e,tokensUsedToday:0,lastRunAt:null,coolDownUntil:null,totalRunsAllTime:0,totalStoriesAllTime:0,currentSession:null,continuationPolicy:"p205-finite-resume-v2",pendingContinuationAuthorities:[],continuationReceipts:[],continuationRecoveryReceipts:[]}}function we(t){return{lock:null,state:w(t),capabilities:x}}class fe{stateDir;statePath;lockPath;mutationLockPath;rolloverManifestPath;successorTransitionManifestPath;logger;constructor(e,s=X){this.stateDir=_(e,B),this.statePath=_(this.stateDir,g),this.lockPath=_(this.stateDir,j),this.mutationLockPath=_(this.stateDir,J),this.rolloverManifestPath=_(this.stateDir,P),this.successorTransitionManifestPath=_(this.stateDir,A),this.logger=s}async readAll(e){const s=e??new Date().toISOString();await this.cleanupOrphanTempFiles(),await this.recoverSessionRolloverIfPresent(s),await this.recoverSuccessorTransitionIfPresent(s);const[n,r]=await Promise.all([this.readLock(),this.readState(s)]);return{lock:await this.prepareLockForSnapshot(n,s),state:r,capabilities:x}}async applyUpdate(e,s){const n=s??new Date().toISOString();await this.ensureDir(),await this.recoverSessionRolloverIfPresent(n),await this.recoverSuccessorTransitionIfPresent(n);for(const r of e.operations)await this.applyOperation(r,n)}async requestRunTransition(e,s){const n=s??new Date().toISOString();return await this.ensureDir(),this.withMutationLock(()=>this.transitionRun(e,n))}async requestLifecycleAction(e,s={},n){const r=n??new Date().toISOString();return await this.ensureDir(),this.withMutationLock(async()=>{const i=(await this.readState(r)).runControl;if(!i)throw new Error("YOLOOP_RUN_TRANSITION_INVALID");const a=Date.parse(r).toString(36),c=s.operationId??`op-${e}-${a}`,l=s.idempotencyKey??`idem-${e}-${a}`,h=i.receipts.find(O=>O.operationId===c||O.idempotencyKey===l);if(h){if(h.operationId===c&&h.idempotencyKey===l)return h;throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}const y=ae(i.status,e);if(!y)throw new Error(E.has(i.status)?"YOLOOP_RUN_TERMINAL":"YOLOOP_RUN_TRANSITION_INVALID");return this.transitionRun(ce({current:i,action:e,target:y,operationId:c,idempotencyKey:l,occurredAt:r}),r)})}async applyOperation(e,s){switch(e.type){case"acquire_lock":await this.acquireLock(e.lock,s);return;case"release_lock":await this.releaseLock(e);return;case"write_state":await this.withMutationLock(async()=>{const n=await this.readState(s),{runControl:r,...o}=e.state;await this.writeState({...o,...n.runControl?{version:"1.2",runControl:n.runControl}:{}})});return;case"advance_epic":await this.advanceEpic(e.nextEpicIndex,e.nextEpicId,s);return;case"no_op":this.logger.info({event:"yoloop_noop"},"yoloop server sent no-op");return;case"reserve_continuation_authority":await this.reserveContinuationAuthority(e.authority,s);return;case"acknowledge_continuation_authority":await this.acknowledgeContinuationAuthority(e.idempotencyKey,e.receipt,s);return;case"repair_continuation_authority":await this.repairContinuationAuthority(e.beforeHash,e.authority,e.receipt,s);return;case"switch_continuation_policy":await this.switchContinuationPolicy(e.policy,e.session,s);return;case"transition_run":await this.requestRunTransition(e,s);return;case"start_successor_run":await this.withMutationLock(()=>this.startSuccessorRun(e,s));return;case"commit_successor_transition":await this.withMutationLock(()=>this.commitSuccessorTransition(e,s));return;case"rollover_session":await this.withMutationLock(()=>this.rolloverSession(e,s));return;case"recovery_attempted":await this.appendRecoveryAttempt(e.attempt,s);return;case"recovery_resolved":await this.resolveRecoveryAttempt(e.attempt_id,e.outcome,s);return;default:{const n=e;throw this.logger.error({event:"yoloop_unknown_op",op:n},"unknown yoloop operation rejected"),new Error("YOLOOP_STATE_OPERATION_UNSUPPORTED")}}}async reserveContinuationAuthority(e,s){const n=await this.readState(s),r=n.pendingContinuationAuthorities??[],o=typeof e.idempotencyKey=="string"?e.idempotencyKey:"";if(!o)throw new Error("YOLOOP_CONTINUATION_AUTHORITY_INVALID");const i=r.find(a=>a.idempotencyKey===o);if(i){if(d(i)===d(e))return;throw new Error("YOLOOP_CONTINUATION_AUTHORITY_CONFLICT")}if(r.length>=64)throw new Error("YOLOOP_PENDING_AUTHORITY_CAPACITY_EXCEEDED");await this.writeState({...n,pendingContinuationAuthorities:[...r,e]})}async acknowledgeContinuationAuthority(e,s,n){const r=await this.readState(n),o=r.pendingContinuationAuthorities??[],i=o.findIndex(h=>h.idempotencyKey===e);if(i<0)throw new Error("YOLOOP_CONTINUATION_RECEIPT_ORPHAN");const a=o[i];if(s.idempotencyKey!==e||s.sessionId!==a.sessionId||JSON.stringify(s.storyIds)!==JSON.stringify(a.storyIds))throw new Error("YOLOOP_CONTINUATION_RECEIPT_MISMATCH");const c=o.filter((h,y)=>y!==i),l=r.continuationReceipts??[];await this.writeState({...r,pendingContinuationAuthorities:c,continuationReceipts:[...l.slice(-63),s]})}async repairContinuationAuthority(e,s,n,r){const o=await this.readState(r),i=o.pendingContinuationAuthorities??[],a=o.continuationRecoveryReceipts??[],c=typeof n.idempotencyKey=="string"?n.idempotencyKey:"",l=a.find(O=>O.idempotencyKey===c);if(l){if(d(l)===d(n))return;throw new Error("YOLOOP_CONTINUATION_RECOVERY_RECEIPT_CONFLICT")}if(i.length!==1||p("p208-legacy-authority-before-v1",i[0])!==e)throw new Error("YOLOOP_CONTINUATION_RECOVERY_BEFORE_MISMATCH");const h=p("p208-legacy-authority-after-v1",s);if(n.beforeHash!==e||n.afterHash!==h)throw new Error("YOLOOP_CONTINUATION_RECOVERY_HASH_MISMATCH");const y={...n};if(delete y.idempotencyKey,!c||p("p208-legacy-authority-recovery-receipt-v1",y)!==c)throw new Error("YOLOOP_CONTINUATION_RECOVERY_RECEIPT_INVALID");await this.writeState({...o,pendingContinuationAuthorities:[s],continuationRecoveryReceipts:[...a.slice(-63),n]})}async switchContinuationPolicy(e,s,n){const r=await this.readState(n);if(r.currentSession||(r.pendingContinuationAuthorities?.length??0)>0)throw new Error("YOLOOP_POLICY_SWITCH_ACTIVE");try{await u.copyFile(this.statePath,`${this.statePath}.policy-backup`)}catch(o){if(o.code!=="ENOENT")throw o}await this.writeState({...r,continuationPolicy:e,continuationSession:s})}async appendRecoveryAttempt(e,s){const n=await this.readState(s),r=n.recovery_history??[];if(r.some(c=>c.attempted_at===e.attempted_at&&c.story_id===e.story_id)){this.logger.warn({event:"recovery_attempted_duplicate",story_id:e.story_id},"duplicate recovery_attempted op; skipping");return}const i=K([...r,e]),a={...n,recovery_history:i};await this.writeState(a),this.logger.info({event:"recovery_attempted_persisted",story_id:e.story_id,history_size:i.length},"recovery attempt persisted")}async resolveRecoveryAttempt(e,s,n){const r=await this.readState(n),o=r.recovery_history??[],i=o.findIndex(h=>h.recovery_step_id_tokenized===e);if(i===-1){this.logger.warn({event:"recovery_resolve_orphan",attempt_id:e},"recovery_resolved op had no matching attempt; skipping");return}const a=o[i];if(a.outcome===s&&a.finished_at){this.logger.info({event:"recovery_resolved_duplicate",attempt_id:e,outcome:s},"duplicate recovery_resolved op; skipping");return}const c=o.slice();c[i]={...a,outcome:s,finished_at:n};const l={...r,recovery_history:c};await this.writeState(l),this.logger.info({event:"recovery_resolved_persisted",attempt_id:e,outcome:s},"recovery attempt resolved")}async acquireLock(e,s){const n={...e,pid:process.pid,hostname:S(),ownerKind:"client",startedAt:e.startedAt||s,leaseId:e.leaseId??le(e.sessionId,s),leaseState:"active",leaseAcquiredAt:e.leaseAcquiredAt??s,leaseDurationMs:e.leaseDurationMs??z,leaseExpiresAt:e.leaseExpiresAt??e.ttlExpiresAt},r=JSON.stringify(n,null,2);try{const o=await u.open(this.lockPath,"wx");try{await o.writeFile(r,"utf8")}finally{await o.close()}this.trySetFilePerms(this.lockPath),this.logger.info({event:"yoloop_lock_acquired",pid:n.pid,sessionId:n.sessionId},"yoloop lock acquired");return}catch(o){const i=o;if(i.code!=="EEXIST")throw this.logger.error({event:"yoloop_lock_acquire_error",err:i.message},"failed to acquire yoloop lock"),o;const a=await this.readLock();if(a&&this.isSameClientSessionLock(a,n)){this.logger.info({event:"yoloop_lock_acquire_duplicate",sessionId:n.sessionId},"duplicate acquire_lock for current yoloop session; treating as no-op");return}if(!await this.maybeRecoverOrphanLock(s))throw this.logger.warn({event:"yoloop_lock_held"},"yoloop lock held by another live process"),o;const l=await u.open(this.lockPath,"wx");try{await l.writeFile(r,"utf8")}finally{await l.close()}this.trySetFilePerms(this.lockPath),this.logger.info({event:"yoloop_lock_acquired_after_recovery",pid:n.pid},"yoloop lock acquired after orphan recovery")}}async releaseLock(e){const s=await this.readState(new Date().toISOString()),n=await this.readLock();if(s.runControl){if(!e.expectedRunId||!e.expectedSessionId||!e.expectedLeaseId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if(e.expectedRunId!==s.runControl.runId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if(!n||e.expectedSessionId!==n.sessionId||e.expectedLeaseId!==n.leaseId)throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}try{await u.unlink(this.lockPath),this.logger.info({event:"yoloop_lock_released"},"yoloop lock released")}catch(r){const o=r;if(o.code==="ENOENT"){this.logger.info({event:"yoloop_lock_release_noop"},"yoloop lock release noop (file absent)");return}this.logger.warn({event:"yoloop_lock_release_error",err:o.message},"unexpected error releasing yoloop lock")}}async readLock(){try{const e=await u.readFile(this.lockPath,"utf8"),s=JSON.parse(e),n=s.currentEpicId??s.epicId??"";return{pid:s.pid??0,hostname:s.hostname??"",...s.ownerKind?{ownerKind:s.ownerKind}:{},...s.ownerPidState?{ownerPidState:s.ownerPidState}:{},...typeof s.leaseId=="string"?{leaseId:s.leaseId}:{},...s.leaseState?{leaseState:s.leaseState}:{},...typeof s.leaseAcquiredAt=="string"?{leaseAcquiredAt:s.leaseAcquiredAt}:{},...typeof s.leaseDurationMs=="number"?{leaseDurationMs:s.leaseDurationMs}:{},...typeof s.leaseExpiresAt=="string"?{leaseExpiresAt:s.leaseExpiresAt}:{},startedAt:s.startedAt??"",sessionId:s.sessionId??"",mode:s.mode??"epic-id",inputPath:s.inputPath??"",epicQueue:s.epicQueue??(n?[n]:[]),currentEpicIndex:s.currentEpicIndex??0,currentEpicId:n,ttlExpiresAt:s.ttlExpiresAt??"",epicId:s.epicId??n}}catch{return null}}async readState(e){try{const s=await u.readFile(this.statePath,"utf8"),n=JSON.parse(s);return he(n,e)}catch(s){const n=s;return n.code!=="ENOENT"&&this.logger.warn({event:"yoloop_state_read_error",err:n.message},"yoloop state file unreadable; using defaults"),w(e)}}async writeState(e){const s=`${this.statePath}.tmp.${process.pid}.${Date.now()}`,n=JSON.stringify(e,null,2);try{const r=await u.open(s,"w",384);try{await r.writeFile(n,"utf8"),await r.sync()}finally{await r.close()}this.trySetFilePerms(s),await u.rename(s,this.statePath),await this.syncStateDirectory()}catch(r){const o=r;this.logger.error({event:"yoloop_state_write_error",err:o.message},"failed to write yoloop state");try{await u.unlink(s)}catch{}throw r}}async rolloverSession(e,s){v(e);const n=await this.readRolloverManifest();if(n){if(oe(n.operation,e)){if(n.operation.payloadHash!==e.payloadHash)throw new Error("YOLOOP_SESSION_ROLLOVER_CONFLICT");if(n.phase==="applied")return;await this.completePreparedSessionRollover(n.operation,s);return}else if(n.phase==="prepared")throw new Error("YOLOOP_SESSION_ROLLOVER_CONFLICT")}const r=await this.readState(s),o=await this.readLock();H(e,r,o),await this.writeRolloverManifest({schemaVersion:"YoloopSessionRolloverManifest/1.0",phase:"prepared",operation:e}),await this.completePreparedSessionRollover(e,s)}async completePreparedSessionRollover(e,s){const n=await this.readState(s);let r=d(n)===d(e.next);if(!r){const i=await this.readLock();H(e,n,i),await this.writeState(e.next),r=!0}const o=await this.readLock();if(e.expectedSessionId===null||e.expectedLeaseId===null){if(e.expectedSessionId!==null||e.expectedLeaseId!==null||o)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT")}else if(o){if(o.sessionId!==e.expectedSessionId||o.leaseId!==e.expectedLeaseId)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT");await u.unlink(this.lockPath),await this.syncStateDirectory()}else if(!r)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT");await this.writeRolloverManifest({schemaVersion:"YoloopSessionRolloverManifest/1.0",phase:"applied",operation:e}),this.logger.info({event:"yoloop_session_rollover_applied",runId:e.runId,operationId:e.operationId,coolDownUntil:e.coolDownUntil},"yoloop per-run-cap session rollover applied")}async recoverSessionRolloverIfPresent(e){try{await u.access(this.rolloverManifestPath)}catch(s){if(s.code==="ENOENT")return;throw s}await this.ensureDir(),await this.withMutationLock(async()=>{const s=await this.readRolloverManifest();s?.phase==="prepared"&&await this.completePreparedSessionRollover(s.operation,e)})}async readRolloverManifest(){try{const e=JSON.parse(await u.readFile(this.rolloverManifestPath,"utf8"));if(e.schemaVersion!=="YoloopSessionRolloverManifest/1.0"||e.phase!=="prepared"&&e.phase!=="applied"||!e.operation||e.operation.type!=="rollover_session")throw new Error("YOLOOP_SESSION_ROLLOVER_MANIFEST_CORRUPT");return v(e.operation),e}catch(e){if(e.code==="ENOENT")return null;throw e.message==="YOLOOP_SESSION_ROLLOVER_MANIFEST_CORRUPT"?e:new Error("YOLOOP_SESSION_ROLLOVER_MANIFEST_CORRUPT")}}async writeRolloverManifest(e){const s=`${this.rolloverManifestPath}.tmp.${process.pid}.${Date.now()}`,n=await u.open(s,"w",384);try{await n.writeFile(JSON.stringify(e,null,2),"utf8"),await n.sync()}finally{await n.close()}this.trySetFilePerms(s),await u.rename(s,this.rolloverManifestPath),await this.syncStateDirectory()}async commitSuccessorTransition(e,s){f(e);const n=await this.readSuccessorTransitionManifest();if(n){if(se(n.operation,e)){if(n.operation.payloadHash!==e.payloadHash)throw new Error("YOLOOP_SUCCESSOR_TRANSITION_CONFLICT");if(n.phase==="applied")return;await this.completePreparedSuccessorTransition(n.operation,s);return}if(n.phase==="prepared")throw new Error("YOLOOP_SUCCESSOR_TRANSITION_CONFLICT")}const r=await this.readState(s);if(R(e,r)){await this.writeSuccessorTransitionManifest({schemaVersion:"YoloopSuccessorTransitionManifest/1.0",phase:"applied",operation:e});return}k(e,r),await this.writeSuccessorTransitionManifest({schemaVersion:"YoloopSuccessorTransitionManifest/1.0",phase:"prepared",operation:e}),await this.completePreparedSuccessorTransition(e,s)}async completePreparedSuccessorTransition(e,s){let n=await this.readState(s);if(!R(e,n)){d(n.runControl)===d(e.successor.next)||(await this.startSuccessorRun(e.successor,s),n=await this.readState(s));const o=re(e,n);await this.writeState(o),n=await this.readState(s)}if(!R(e,n))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_STATE_CONFLICT");await this.writeSuccessorTransitionManifest({schemaVersion:"YoloopSuccessorTransitionManifest/1.0",phase:"applied",operation:e}),this.logger.info({event:"yoloop_successor_transition_applied",runId:e.successor.runId,operationId:e.operationId},"yoloop successor transition applied")}async recoverSuccessorTransitionIfPresent(e){try{await u.access(this.successorTransitionManifestPath)}catch(s){if(s.code==="ENOENT")return;throw s}await this.ensureDir(),await this.withMutationLock(async()=>{const s=await this.readSuccessorTransitionManifest();s?.phase==="prepared"&&await this.completePreparedSuccessorTransition(s.operation,e)})}async readSuccessorTransitionManifest(){try{const e=JSON.parse(await u.readFile(this.successorTransitionManifestPath,"utf8"));if(e.schemaVersion!=="YoloopSuccessorTransitionManifest/1.0"||e.phase!=="prepared"&&e.phase!=="applied"||!e.operation||e.operation.type!=="commit_successor_transition")throw new Error("YOLOOP_SUCCESSOR_TRANSITION_MANIFEST_CORRUPT");return f(e.operation),e}catch(e){if(e.code==="ENOENT")return null;throw e.message==="YOLOOP_SUCCESSOR_TRANSITION_MANIFEST_CORRUPT"?e:new Error("YOLOOP_SUCCESSOR_TRANSITION_MANIFEST_CORRUPT")}}async writeSuccessorTransitionManifest(e){const s=`${this.successorTransitionManifestPath}.tmp.${process.pid}.${Date.now()}`,n=await u.open(s,"w",384);try{await n.writeFile(JSON.stringify(e,null,2),"utf8"),await n.sync()}finally{await n.close()}this.trySetFilePerms(s),await u.rename(s,this.successorTransitionManifestPath),await this.syncStateDirectory()}async advanceEpic(e,s,n){const r=await this.readState(n);if(!r.currentSession){this.logger.warn({event:"yoloop_advance_no_session",nextEpicId:s},"advance_epic with no active session; skipping");return}const o={...r,currentSession:{...r.currentSession,currentEpicIndex:e}};await this.writeState(o),this.logger.info({event:"yoloop_epic_advance",nextEpicId:s,nextIndex:e},"yoloop advanced to next epic")}async ensureDir(){try{await u.mkdir(this.stateDir,{recursive:!0}),this.trySetDirPerms(this.stateDir)}catch(e){const s=e;throw this.logger.error({event:"yoloop_mkdir_error",err:s.message,path:this.stateDir},"failed to ensure .neocortex/ directory"),e}}async syncStateDirectory(){try{const e=await u.open(this.stateDir,"r");try{await e.sync()}finally{await e.close()}}catch(e){const s=e.code;if(s!=="EINVAL"&&s!=="EISDIR"&&s!=="ENOTSUP"&&s!=="EPERM")throw e}}async withMutationLock(e){let s=null;for(let n=0;n<G;n+=1)try{s=await u.open(this.mutationLockPath,"wx",384),await s.writeFile(JSON.stringify({pid:process.pid,acquiredAt:new Date().toISOString()})),await s.sync();break}catch(r){if(r.code!=="EEXIST")throw r;try{const o=await u.stat(this.mutationLockPath);if(Date.now()-o.mtimeMs>W){await u.unlink(this.mutationLockPath);continue}}catch{continue}await new Promise(o=>setTimeout(o,5))}if(!s)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");try{return await e()}finally{await s.close().catch(()=>{}),await u.unlink(this.mutationLockPath).catch(()=>{})}}async transitionRun(e,s){T(e);const n=await this.readState(s),r=n.runControl,o=r?.receipts.find(l=>l.operationId===e.operationId||l.idempotencyKey===e.idempotencyKey);if(o){if(o.operationId===e.operationId&&o.idempotencyKey===e.idempotencyKey&&o.payloadHash===e.payloadHash)return o;throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}if(r&&r.runId!==e.runId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if((r?.revision??0)!==e.expectedRevision)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if((r?.latestSnapshotHash??null)!==e.expectedSnapshotHash)throw new Error("YOLOOP_RUN_SNAPSHOT_CONFLICT");L(r,e);const c=e.next.receipts.at(-1);if(!c)throw new Error("YOLOOP_RUN_OPERATION_CONFLICT");return await this.writeState({...n,version:"1.2",runControl:e.next}),this.logger.info({event:"yoloop_run_transition_committed",runId:e.runId,operationId:e.operationId,revision:c.revision,status:c.status},"yoloop run transition committed"),c}async startSuccessorRun(e,s){Y(e);const n=await this.readState(s),r=n.runControl,o={schemaVersion:"YoloopRunPredecessor/1.0",runId:e.previousRunId,revision:e.expectedRevision,snapshotHash:e.expectedSnapshotHash,successorOperationId:e.operationId,publicSafe:!0},i=r?.receipts.find(l=>l.operationId===e.operationId||l.idempotencyKey===e.idempotencyKey);if(i){if(i.operationId===e.operationId&&i.idempotencyKey===e.idempotencyKey&&e.next.receipts.at(-1)?.payloadHash===i.payloadHash&&d(r?.predecessor)===d(o))return i;throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}if(!r||r.runId!==e.previousRunId||!["exhausted","success"].includes(r.status)||r.revision!==e.expectedRevision||r.latestSnapshotHash!==e.expectedSnapshotHash||n.currentSession!==null)throw new Error("YOLOOP_RUN_SUCCESSOR_CONFLICT");const a=e.next.receipts.at(-1);if(!a||d(e.next.predecessor)!==d(o))throw new Error("YOLOOP_RUN_SUCCESSOR_CONFLICT");const c={type:"transition_run",runId:e.runId,operationId:e.operationId,idempotencyKey:e.idempotencyKey,expectedRevision:0,expectedSnapshotHash:null,payloadHash:a.payloadHash,next:e.next};return T(c),L(void 0,c),await this.writeState({...n,version:"1.2",runControl:e.next}),a}trySetFilePerms(e){try{$(e)}catch{}}trySetDirPerms(e){try{q(e)}catch{}}async prepareLockForSnapshot(e,s){if(!e)return null;if(e.ownerKind!=="client")return e;const n=F(e,s),r={...e,leaseState:n};if(n==="expired")return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_client_lock_recovered",reason:"lease_expired"},"expired client-owned yoloop lock lease recovered before snapshot echo"),null;if(!r.pid||r.pid<=0)return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_client_lock_recovered",reason:"invalid_pid"},"invalid PID in client-owned yoloop lock; recovered"),null;if(r.hostname&&r.hostname!==S())return{...r,ownerPidState:"unknown"};try{return process.kill(r.pid,0),{...r,ownerPidState:"live"}}catch{return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_client_lock_recovered",reason:"dead_pid"},"stale client-owned yoloop lock recovered before snapshot echo"),null}}async maybeRecoverOrphanLock(e){const s=await this.readLock();if(!s)return!0;if(F(s,e)==="expired")return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_orphan_lock_recovered",reason:"lease_expired"},"expired yoloop lock lease recovered"),!0;if(!s.pid||s.pid<=0)return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_orphan_lock_recovered",reason:"invalid_pid"},"invalid PID in lock file; recovered"),!0;if(s.hostname&&s.hostname!==S())return this.logger.warn({event:"yoloop_lock_remote_host",ownerKind:s.ownerKind??"server",ownerPidState:"unknown"},"lock held by a remote host; honoring"),!1;try{return process.kill(s.pid,0),!1}catch{return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_orphan_lock_recovered",reason:"dead_pid"},"orphan yoloop lock recovered"),!0}}async deleteLockBestEffort(){try{await u.unlink(this.lockPath)}catch{}}isSameClientSessionLock(e,s){return e.ownerKind==="client"&&e.sessionId===s.sessionId&&e.pid===process.pid&&e.hostname===S()}async cleanupOrphanTempFiles(){try{const e=V(this.statePath),s=await u.readdir(e),n=[`${g}.tmp.`,`${P}.tmp.`,`${A}.tmp.`],r=s.filter(o=>n.some(i=>o.startsWith(i)));for(const o of r)try{await u.unlink(_(e,o))}catch{}}catch{}}}function d(t){return t===null||typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(d).join(",")}]`:`{${Object.entries(t).filter(([,e])=>e!==void 0).sort(([e],[s])=>e.localeCompare(s)).map(([e,s])=>`${JSON.stringify(e)}:${d(s)}`).join(",")}}`}function p(t,e){return b("sha256").update(`${t}\0${d(e)}`).digest("hex")}function Z(t){return p("p9390812898200217364-yoloop-session-v1",t)}function Q(t){return p("p9390812898200217364-yoloop-rollover-v1",t)}function ee(t){return p("p9390812898200217364-yoloop-successor-run-v1",t)}function te(t){return p("p9046281030721413419-yoloop-successor-transition-v1",t)}function se(t,e){return t.operationId===e.operationId&&t.idempotencyKey===e.idempotencyKey}function f(t){const e=/^[A-Za-z0-9._:-]{1,128}$/;Y(t.successor);const s={operationId:t.operationId,idempotencyKey:t.idempotencyKey,successor:t.successor,state:t.state,...t.acknowledgement?{acknowledgement:t.acknowledgement}:{},...t.reservation?{reservation:t.reservation}:{}},n=t.acknowledgement?.idempotencyKey,r=t.acknowledgement?.receipt.idempotencyKey,o=t.reservation?.authority.idempotencyKey;if(t.successor.type!=="start_successor_run"||!e.test(t.operationId)||!e.test(t.idempotencyKey)||t.operationId!==t.successor.operationId||t.idempotencyKey!==t.successor.idempotencyKey||!/^[a-f0-9]{64}$/.test(t.payloadHash)||t.payloadHash!==te(s)||Buffer.byteLength(d(s),"utf8")>1048576||t.acknowledgement!==void 0&&(t.acknowledgement.type!=="acknowledge_continuation_authority"||typeof n!="string"||!e.test(n)||r!==n)||t.reservation!==void 0&&(t.reservation.type!=="reserve_continuation_authority"||typeof o!="string"||!e.test(o))||n!==void 0&&o===n)throw new Error("YOLOOP_SUCCESSOR_TRANSITION_CONFLICT")}function k(t,e){if(f(t),!(d(e.runControl)===d(t.successor.next))){const n=e.runControl;if(!n||n.runId!==t.successor.previousRunId||!["exhausted","success"].includes(n.status)||n.revision!==t.successor.expectedRevision||n.latestSnapshotHash!==t.successor.expectedSnapshotHash||e.currentSession!==null)throw new Error("YOLOOP_SUCCESSOR_TRANSITION_STATE_CONFLICT")}ne(t,e)}function ne(t,e){const s=e.pendingContinuationAuthorities??[],n=e.continuationReceipts??[],r=t.acknowledgement;if(r){const l=s.find(O=>O.idempotencyKey===r.idempotencyKey);if(!l||r.receipt.sessionId!==l.sessionId||d(r.receipt.storyIds)!==d(l.storyIds))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT");const h=s.filter(O=>O.idempotencyKey!==r.idempotencyKey),y=[...n.slice(-63),r.receipt];if(d(t.state.pendingContinuationAuthorities??[])!==d(h)||d(t.state.continuationReceipts??[])!==d(y))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT")}else if(d(t.state.pendingContinuationAuthorities??[])!==d(s)||d(t.state.continuationReceipts??[])!==d(n))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT");const o=t.reservation?.authority;if(!o)return;const i=String(o.idempotencyKey??""),a=t.state.pendingContinuationAuthorities??[],c=a.find(l=>l.idempotencyKey===i);if(c&&d(c)!==d(o))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT");if(!c&&a.length>=64)throw new Error("YOLOOP_PENDING_AUTHORITY_CAPACITY_EXCEEDED")}function re(t,e){k(t,e);const s=t.reservation?.authority,n=[...t.state.pendingContinuationAuthorities??[]];return s&&!n.some(r=>r.idempotencyKey===s.idempotencyKey)&&n.push(s),{...t.state,version:"1.2",runControl:t.successor.next,...s||t.state.pendingContinuationAuthorities!==void 0?{pendingContinuationAuthorities:n}:{}}}function R(t,e){const s=t.reservation?.authority,n=[...t.state.pendingContinuationAuthorities??[]];s&&!n.some(o=>o.idempotencyKey===s.idempotencyKey)&&n.push(s);const r={...t.state,version:"1.2",runControl:t.successor.next,...s||t.state.pendingContinuationAuthorities!==void 0?{pendingContinuationAuthorities:n}:{}};return d(e)===d(r)}function oe(t,e){return t.operationId===e.operationId&&t.idempotencyKey===e.idempotencyKey}function v(t){const e=/^[A-Za-z0-9._:-]{1,128}$/,s={runId:t.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.expectedRevision,expectedSnapshotHash:t.expectedSnapshotHash,expectedSessionId:t.expectedSessionId,expectedSessionFingerprint:t.expectedSessionFingerprint,expectedLeaseId:t.expectedLeaseId,coolDownUntil:t.coolDownUntil,reason:t.reason,exhaustedRun:t.exhaustedRun,next:t.next};if(!e.test(t.runId)||!e.test(t.operationId)||!e.test(t.idempotencyKey)||!/^[a-f0-9]{64}$/.test(t.expectedSessionFingerprint)||!/^[a-f0-9]{64}$/.test(t.payloadHash)||!Number.isSafeInteger(t.expectedRevision)||t.expectedRevision<0||!Number.isFinite(Date.parse(t.coolDownUntil))||!["YOLOOP_PER_RUN_CAP_REACHED","YOLOOP_SESSION_TIMEOUT"].includes(t.reason)||t.payloadHash!==Q(s)||t.receipt.schemaVersion!=="YoloopSessionRolloverReceipt/1.0"||t.receipt.runId!==t.runId||t.receipt.operationId!==t.operationId||t.receipt.idempotencyKey!==t.idempotencyKey||t.receipt.payloadHash!==t.payloadHash||t.receipt.coolDownUntil!==t.coolDownUntil||t.receipt.revision!==(t.exhaustedRun?.revision??0)||t.receipt.snapshotHash!==(t.exhaustedRun?.latestSnapshotHash??null)||t.receipt.status!=="exhausted"||t.receipt.publicSafe!==!0)throw new Error("YOLOOP_SESSION_ROLLOVER_CONFLICT");if(t.expectedSessionId===null!=(t.expectedLeaseId===null)||t.expectedSessionId!==null&&!e.test(t.expectedSessionId)||t.expectedLeaseId!==null&&!e.test(t.expectedLeaseId))throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT")}function Y(t){const e=/^[A-Za-z0-9._:-]{1,128}$/,s={runId:t.runId,previousRunId:t.previousRunId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.expectedRevision,expectedSnapshotHash:t.expectedSnapshotHash,next:t.next};if(!e.test(t.runId)||!e.test(t.previousRunId)||t.runId===t.previousRunId||!e.test(t.operationId)||!e.test(t.idempotencyKey)||!/^[a-f0-9]{64}$/.test(t.payloadHash)||!Number.isSafeInteger(t.expectedRevision)||t.expectedRevision<1||t.payloadHash!==ee(s))throw new Error("YOLOOP_RUN_SUCCESSOR_CONFLICT")}function H(t,e,s){v(t);const n=e.currentSession;if(!n||Z(n)!==t.expectedSessionFingerprint)throw new Error("YOLOOP_SESSION_ROLLOVER_SESSION_CONFLICT");const r=e.runControl;if((r?.revision??0)!==t.expectedRevision||(r?.latestSnapshotHash??null)!==t.expectedSnapshotHash||r&&r.runId!==t.runId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if(t.expectedSessionId===null||t.expectedLeaseId===null){if(s)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT")}else if(!s||s.sessionId!==t.expectedSessionId||s.leaseId!==t.expectedLeaseId)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT");const o=t.exhaustedRun;if(r)if(r.status==="exhausted"){if(d(o)!==d(r))throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT")}else{if(!o||o.status!=="exhausted")throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT");const a=o.receipts.at(-1);if(!a)throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT");const c={type:"transition_run",runId:r.runId,operationId:a.operationId,idempotencyKey:a.idempotencyKey,expectedRevision:r.revision,expectedSnapshotHash:r.latestSnapshotHash,payloadHash:a.payloadHash,next:o};T(c),L(r,c)}else{const a=o?.receipts[0],c=o?.receipts[1];if(o&&(o.runId!==t.runId||o.status!=="exhausted"||o.revision!==2||o.events.length!==2||o.progressSnapshots.length!==4||o.receipts.length!==2||a?.runId!==t.runId||a.revision!==1||a.status!=="created"||c?.runId!==t.runId||c.revision!==2||c.status!=="exhausted"||c.operationId!==`${t.operationId}:run`||c.idempotencyKey!==`${t.idempotencyKey}:run`||o.latestSnapshotHash!==c.snapshotHash||o.predecessor!==void 0))throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT")}const i={...e,version:"1.2",coolDownUntil:t.coolDownUntil,currentSession:null,...o?{runControl:o}:{}};if(d(i)!==d(t.next))throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT")}function U(t){const{receipts:e,...s}=t.next;return p("p220-yoloop-run-transition-payload-v1",{runId:t.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.expectedRevision,expectedSnapshotHash:t.expectedSnapshotHash,next:s})}const E=new Set(["success","partial","blocked","failure","exhausted","cancelled"]),ie={created:["running","cancelling","exhausted"],running:["pausing","cancelling","success","partial","blocked","failure","exhausted"],pausing:["paused","cancelling","failure","exhausted"],paused:["resuming","cancelling","exhausted"],resuming:["running","cancelling","failure","exhausted"],cancelling:["cancelled","failure","exhausted"]};function ae(t,e){return e==="pause"?t==="running"?"pausing":null:e==="resume"?t==="paused"?"resuming":null:E.has(t)||t==="cancelling"?null:"cancelling"}function ce(t){const e=t.current.revision+1,s=t.action==="cancel"?["YOLOOP_RUN_CANCEL_REQUESTED"]:[],n={schemaVersion:"YoloopOperationalBudgetSummary/1.0",limits:t.current.operationalBudget.limits,usage:t.current.usage,exhaustedDimension:null},r={schemaVersion:"ProgressSnapshot/1.0",runId:t.current.runId,sequence:t.current.progressSnapshots.length+1,revision:e,operationId:t.operationId,boundary:t.action,phase:"before",status:t.current.status,occurredAt:t.occurredAt,parentSnapshotHash:t.current.latestSnapshotHash,budget:n,reasonCodes:s,publicSafe:!0},o=p("p220-yoloop-progress-snapshot-id-v1",{runId:r.runId,operationId:r.operationId,sequence:r.sequence,phase:r.phase}),i={...r,snapshotId:o,snapshotHash:p("p220-yoloop-progress-snapshot-v1",{...r,snapshotId:o})},a={...r,sequence:i.sequence+1,phase:"after",status:t.target,parentSnapshotHash:i.snapshotHash},c=p("p220-yoloop-progress-snapshot-id-v1",{runId:a.runId,operationId:a.operationId,sequence:a.sequence,phase:a.phase}),l={...a,snapshotId:c,snapshotHash:p("p220-yoloop-progress-snapshot-v1",{...a,snapshotId:c})},h={schemaVersion:"YoloopRunEvent/1.0",runId:t.current.runId,operationId:t.operationId,kind:"intent",sequence:t.current.events.length+1,revision:e,occurredAt:t.occurredAt,fromStatus:t.current.status,toStatus:t.target,reasonCodes:s,budgetDimension:null,parentEventHash:t.current.events.at(-1)?.eventHash??null,publicSafe:!0},y=p("p220-yoloop-run-event-id-v1",{runId:h.runId,operationId:h.operationId,sequence:h.sequence}),O={...h,eventId:y,eventHash:p("p220-yoloop-run-event-v1",{...h,eventId:y})},I={...t.current,revision:e,status:t.target,events:[...t.current.events,O],progressSnapshots:[...t.current.progressSnapshots,i,l],latestSnapshotHash:l.snapshotHash,receipts:[...t.current.receipts]},N={runId:t.current.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.current.revision,expectedSnapshotHash:t.current.latestSnapshotHash,next:I},m=U(N),M={schemaVersion:"YoloopRunTransitionReceipt/1.0",runId:t.current.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,payloadHash:m,revision:e,snapshotHash:l.snapshotHash,status:t.target};return{type:"transition_run",...N,payloadHash:m,next:{...I,receipts:[...I.receipts,M]}}}function T(t){const e=/^[A-Za-z0-9._:-]{1,128}$/;if(!e.test(t.runId)||!e.test(t.operationId)||!e.test(t.idempotencyKey)||!/^[a-f0-9]{64}$/.test(t.payloadHash)||!Number.isSafeInteger(t.expectedRevision)||t.expectedRevision<0)throw new Error("YOLOOP_RUN_OPERATION_CONFLICT");if(t.payloadHash!==U(t))throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}function L(t,e){const s=e.next,n=t?.events??[],r=t?.progressSnapshots??[],o=t?.receipts??[];if(s.schemaVersion!=="YoloopRunControl/1.0"||s.runId!==e.runId||s.revision!==e.expectedRevision+1||s.events.length!==n.length+1||s.progressSnapshots.length!==r.length+2||s.receipts.length!==o.length+1||d(s.events.slice(0,n.length))!==d(n)||d(s.progressSnapshots.slice(0,r.length))!==d(r)||d(s.receipts.slice(0,o.length))!==d(o))throw new Error("YOLOOP_RUN_TRANSITION_INVALID");if(t){if(E.has(t.status))throw new Error("YOLOOP_RUN_TERMINAL");const h=s.progressSnapshots.at(-2)?.boundary,y=t.status===s.status&&(h==="budget"||h==="retry");if(!(ie[t.status]??[]).includes(s.status)&&!y)throw new Error("YOLOOP_RUN_TRANSITION_INVALID");if(t.startedAt!==s.startedAt||t.deadlineAt!==s.deadlineAt)throw new Error("YOLOOP_RUN_TRANSITION_INVALID");if(d(t.predecessor)!==d(s.predecessor))throw new Error("YOLOOP_RUN_TRANSITION_INVALID")}else if(s.status!=="created")throw new Error("YOLOOP_RUN_TRANSITION_INVALID");const i=s.progressSnapshots.at(-2),a=s.progressSnapshots.at(-1),c=s.events.at(-1),l=s.receipts.at(-1);if(!i||!a||!c||!l||i.phase!=="before"||a.phase!=="after"||i.operationId!==e.operationId||a.operationId!==e.operationId||c.operationId!==e.operationId||i.runId!==e.runId||a.runId!==e.runId||c.runId!==e.runId||i.revision!==s.revision||a.revision!==s.revision||c.revision!==s.revision||i.parentSnapshotHash!==(t?.latestSnapshotHash??null)||a.parentSnapshotHash!==i.snapshotHash||s.latestSnapshotHash!==a.snapshotHash||l.schemaVersion!=="YoloopRunTransitionReceipt/1.0"||l.runId!==e.runId||l.operationId!==e.operationId||l.idempotencyKey!==e.idempotencyKey||l.payloadHash!==e.payloadHash||l.revision!==s.revision||l.snapshotHash!==s.latestSnapshotHash||l.status!==s.status||Date.parse(s.deadlineAt)<=Date.parse(s.startedAt))throw new Error("YOLOOP_RUN_TRANSITION_INVALID");D(i),D(a),de(c,n.at(-1)?.eventHash??null)}function D(t){const{snapshotHash:e,...s}=t;if(e!==p("p220-yoloop-progress-snapshot-v1",s))throw new Error("YOLOOP_RUN_SNAPSHOT_CONFLICT")}function de(t,e){const{eventHash:s,...n}=t;if(t.parentEventHash!==e||s!==p("p220-yoloop-run-event-v1",n))throw new Error("YOLOOP_RUN_SNAPSHOT_CONFLICT")}function le(t,e){return`lease-${ue(`${t}:${e}`)}`}function ue(t){let e=2166136261;for(let s=0;s<t.length;s+=1)e^=t.charCodeAt(s),e=Math.imul(e,16777619)>>>0;return e.toString(36).padStart(7,"0")}function F(t,e){const s=t.leaseExpiresAt??t.ttlExpiresAt,n=Date.parse(s),r=Date.parse(e);return!Number.isFinite(n)||!Number.isFinite(r)?"unknown":n<=r?"expired":"active"}function he(t,e){const s=w(e);return{...t,version:t.version==="1.2"?"1.2":"1.1",lastResetAt:t.lastResetAt??s.lastResetAt,storiesRunToday:t.storiesRunToday??0,iterationsToday:t.iterationsToday??0,iterationsTodayDate:t.iterationsTodayDate??s.iterationsTodayDate,tokensUsedToday:t.tokensUsedToday??0,lastRunAt:t.lastRunAt??null,coolDownUntil:t.coolDownUntil??null,totalRunsAllTime:t.totalRunsAllTime??0,totalStoriesAllTime:t.totalStoriesAllTime??0,currentSession:t.currentSession??null,...t.runControl!==void 0?{runControl:t.runControl}:{},...t.continuationPolicy!==void 0?{continuationPolicy:t.continuationPolicy}:{},...t.continuationSession!==void 0?{continuationSession:t.continuationSession}:{},...t.pendingContinuationAuthorities!==void 0?{pendingContinuationAuthorities:t.pendingContinuationAuthorities}:{},...t.continuationReceipts!==void 0?{continuationReceipts:t.continuationReceipts}:{},...t.continuationRecoveryReceipts!==void 0?{continuationRecoveryReceipts:t.continuationRecoveryReceipts}:{},recovery_history:Array.isArray(t.recovery_history)?K(t.recovery_history):[]}}function K(t){return t.length<=C?t:t.slice(t.length-C)}export{x as YOLOOP_PERSISTENCE_CAPABILITIES,fe as YoloopClientStateStore,U as buildYoloopRunTransitionPayloadHash,Q as buildYoloopSessionRolloverPayloadHash,ee as buildYoloopSuccessorRunPayloadHash,te as buildYoloopSuccessorTransitionPayloadHash,K as capRecoveryHistory,w as createDefaultCounters,we as createDefaultState,Z as fingerprintYoloopSession};
|
|
1
|
+
import{promises as u}from"node:fs";import{createHash as V}from"node:crypto";import{join as S,dirname as $}from"node:path";import{hostname as w}from"node:os";import{setSecureFilePermissions as q,setSecureDirPermissions as B}from"../config/secure-config.js";import{RECOVERY_HISTORY_CAP as m}from"./shared-yoloop-types.js";const j=".neocortex",g="yoloop-state.json",J="yoloop.lock",z="yoloop-state.mutation.lock",A="yoloop-session-rollover.json",P="yoloop-successor-transition.json",W=360*60*1e3,G=3e4,X=200,x=Object.freeze(["rollover-session-v1","successor-transition-v1","continuation-replan-v1"]),Z={warn:()=>{},info:()=>{},error:()=>{}};function f(t){const e=t.slice(0,10);return{version:"1.1",lastResetAt:t,storiesRunToday:0,iterationsToday:0,iterationsTodayDate:e,tokensUsedToday:0,lastRunAt:null,coolDownUntil:null,totalRunsAllTime:0,totalStoriesAllTime:0,currentSession:null,continuationPolicy:"p205-finite-resume-v2",pendingContinuationAuthorities:[],continuationReceipts:[],continuationRecoveryReceipts:[]}}function Re(t){return{lock:null,state:f(t),capabilities:x}}class ve{stateDir;statePath;lockPath;mutationLockPath;rolloverManifestPath;successorTransitionManifestPath;logger;constructor(e,s=Z){this.stateDir=S(e,j),this.statePath=S(this.stateDir,g),this.lockPath=S(this.stateDir,J),this.mutationLockPath=S(this.stateDir,z),this.rolloverManifestPath=S(this.stateDir,A),this.successorTransitionManifestPath=S(this.stateDir,P),this.logger=s}async readAll(e){const s=e??new Date().toISOString();await this.cleanupOrphanTempFiles(),await this.recoverSessionRolloverIfPresent(s),await this.recoverSuccessorTransitionIfPresent(s);const[n,o]=await Promise.all([this.readLock(),this.readState(s)]);return{lock:await this.prepareLockForSnapshot(n,s),state:o,capabilities:x}}async applyUpdate(e,s){const n=s??new Date().toISOString();await this.ensureDir(),await this.recoverSessionRolloverIfPresent(n),await this.recoverSuccessorTransitionIfPresent(n);for(const o of e.operations)await this.applyOperation(o,n)}async requestRunTransition(e,s){const n=s??new Date().toISOString();return await this.ensureDir(),this.withMutationLock(()=>this.transitionRun(e,n))}async requestLifecycleAction(e,s={},n){const o=n??new Date().toISOString();return await this.ensureDir(),this.withMutationLock(async()=>{const c=(await this.readState(o)).runControl;if(!c)throw new Error("YOLOOP_RUN_TRANSITION_INVALID");const a=Date.parse(o).toString(36),i=s.operationId??`op-${e}-${a}`,l=s.idempotencyKey??`idem-${e}-${a}`,h=c.receipts.find(y=>y.operationId===i||y.idempotencyKey===l);if(h){if(h.operationId===i&&h.idempotencyKey===l)return h;throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}const O=de(c.status,e);if(!O)throw new Error(T.has(c.status)?"YOLOOP_RUN_TERMINAL":"YOLOOP_RUN_TRANSITION_INVALID");return this.transitionRun(le({current:c,action:e,target:O,operationId:i,idempotencyKey:l,occurredAt:o}),o)})}async applyOperation(e,s){switch(e.type){case"acquire_lock":await this.withMutationLock(()=>this.acquireLock(e.lock,s));return;case"release_lock":await this.withMutationLock(()=>this.releaseLock(e));return;case"write_state":await this.withMutationLock(async()=>{const n=await this.readState(s),{runControl:o,...r}=e.state;await this.writeState({...r,...n.runControl?{version:"1.2",runControl:n.runControl}:{}})});return;case"advance_epic":await this.advanceEpic(e.nextEpicIndex,e.nextEpicId,s);return;case"no_op":this.logger.info({event:"yoloop_noop"},"yoloop server sent no-op");return;case"reserve_continuation_authority":await this.reserveContinuationAuthority(e.authority,s);return;case"acknowledge_continuation_authority":await this.acknowledgeContinuationAuthority(e.idempotencyKey,e.receipt,s);return;case"repair_continuation_authority":await this.repairContinuationAuthority(e.beforeHash,e.authority,e.receipt,s);return;case"switch_continuation_policy":await this.switchContinuationPolicy(e.policy,e.session,s);return;case"transition_run":await this.requestRunTransition(e,s);return;case"start_successor_run":await this.withMutationLock(()=>this.startSuccessorRun(e,s));return;case"commit_successor_transition":await this.withMutationLock(()=>this.commitSuccessorTransition(e,s));return;case"rollover_session":await this.withMutationLock(()=>this.rolloverSession(e,s));return;case"replan_continuation_session":await this.withMutationLock(()=>this.replanContinuationSession(e,s));return;case"recovery_attempted":await this.appendRecoveryAttempt(e.attempt,s);return;case"recovery_resolved":await this.resolveRecoveryAttempt(e.attempt_id,e.outcome,s);return;default:{const n=e;throw this.logger.error({event:"yoloop_unknown_op",op:n},"unknown yoloop operation rejected"),new Error("YOLOOP_STATE_OPERATION_UNSUPPORTED")}}}async reserveContinuationAuthority(e,s){const n=await this.readState(s),o=n.pendingContinuationAuthorities??[],r=typeof e.idempotencyKey=="string"?e.idempotencyKey:"";if(!r)throw new Error("YOLOOP_CONTINUATION_AUTHORITY_INVALID");const c=o.find(a=>a.idempotencyKey===r);if(c){if(d(c)===d(e))return;throw new Error("YOLOOP_CONTINUATION_AUTHORITY_CONFLICT")}if(o.length>=64)throw new Error("YOLOOP_PENDING_AUTHORITY_CAPACITY_EXCEEDED");await this.writeState({...n,pendingContinuationAuthorities:[...o,e]})}async acknowledgeContinuationAuthority(e,s,n){const o=await this.readState(n),r=o.pendingContinuationAuthorities??[],c=r.findIndex(h=>h.idempotencyKey===e);if(c<0)throw new Error("YOLOOP_CONTINUATION_RECEIPT_ORPHAN");const a=r[c];if(s.idempotencyKey!==e||s.sessionId!==a.sessionId||JSON.stringify(s.storyIds)!==JSON.stringify(a.storyIds))throw new Error("YOLOOP_CONTINUATION_RECEIPT_MISMATCH");const i=r.filter((h,O)=>O!==c),l=o.continuationReceipts??[];await this.writeState({...o,pendingContinuationAuthorities:i,continuationReceipts:[...l.slice(-63),s]})}async repairContinuationAuthority(e,s,n,o){const r=await this.readState(o),c=r.pendingContinuationAuthorities??[],a=r.continuationRecoveryReceipts??[],i=typeof n.idempotencyKey=="string"?n.idempotencyKey:"",l=a.find(y=>y.idempotencyKey===i);if(l){if(d(l)===d(n))return;throw new Error("YOLOOP_CONTINUATION_RECOVERY_RECEIPT_CONFLICT")}if(c.length!==1||p("p208-legacy-authority-before-v1",c[0])!==e)throw new Error("YOLOOP_CONTINUATION_RECOVERY_BEFORE_MISMATCH");const h=p("p208-legacy-authority-after-v1",s);if(n.beforeHash!==e||n.afterHash!==h)throw new Error("YOLOOP_CONTINUATION_RECOVERY_HASH_MISMATCH");const O={...n};if(delete O.idempotencyKey,!i||p("p208-legacy-authority-recovery-receipt-v1",O)!==i)throw new Error("YOLOOP_CONTINUATION_RECOVERY_RECEIPT_INVALID");await this.writeState({...r,pendingContinuationAuthorities:[s],continuationRecoveryReceipts:[...a.slice(-63),n]})}async switchContinuationPolicy(e,s,n){const o=await this.readState(n);if(o.currentSession||(o.pendingContinuationAuthorities?.length??0)>0)throw new Error("YOLOOP_POLICY_SWITCH_ACTIVE");try{await u.copyFile(this.statePath,`${this.statePath}.policy-backup`)}catch(r){if(r.code!=="ENOENT")throw r}await this.writeState({...o,continuationPolicy:e,continuationSession:s})}async replanContinuationSession(e,s){const{type:n,payloadHash:o,receipt:r,...c}=e;if(Q(c)!==e.payloadHash||!k(e.nextSession))throw new Error("YOLOOP_CONTINUATION_REPLAN_PAYLOAD_INVALID");const a={schemaVersion:"YoloopContinuationReplanReceipt/1.0",operationId:e.operationId,idempotencyKey:e.idempotencyKey,runId:e.expectedRunId,previousSessionHash:e.expectedSessionHash,nextSessionHash:p("p222-yoloop-replan-session-v1",e.nextSession),retiredAuthorityHash:e.expectedPendingHash,retiredAuthorityCount:e.expectedPendingHash===p("p222-yoloop-replan-pending-v1",[])?0:1,payloadHash:e.payloadHash,outcome:"graph-replanned",publicSafe:!0};if(d(e.receipt)!==d(a))throw new Error("YOLOOP_CONTINUATION_REPLAN_RECEIPT_INVALID");const i=await this.readState(s);if(await this.readLock()||i.currentSession!==null)throw new Error("YOLOOP_CONTINUATION_REPLAN_ACTIVE");const l=i.runControl;if(!l||l.runId!==e.expectedRunId||!["success","exhausted"].includes(l.status)||l.revision!==e.expectedRevision||l.latestSnapshotHash!==e.expectedSnapshotHash||i.continuationPolicy!=="p205-finite-resume-v2")throw new Error("YOLOOP_CONTINUATION_REPLAN_RUN_CONFLICT");const h=i.continuationReplanReceipts??[],O=h.find(_=>_.idempotencyKey===e.idempotencyKey||_.operationId===e.operationId);if(O){if(d(O)===d(a)&&d(i.continuationSession)===d(e.nextSession)&&(i.pendingContinuationAuthorities?.length??0)===0)return;throw new Error("YOLOOP_CONTINUATION_REPLAN_REPLAY_CONFLICT")}const y=i.continuationSession,I=i.pendingContinuationAuthorities??[];if(!k(y)||p("p222-yoloop-replan-session-v1",y)!==e.expectedSessionHash||p("p222-yoloop-replan-pending-v1",I)!==e.expectedPendingHash||I.length>1||I.some(_=>_.delegated!==!1||_.sessionId!==y.sessionId||!_.certificate||_.certificate.sessionId!==y.sessionId))throw new Error("YOLOOP_CONTINUATION_REPLAN_AUTHORITY_CONFLICT");await this.writeState({...i,continuationSession:e.nextSession,pendingContinuationAuthorities:[],continuationReplanReceipts:[...h.slice(-63),a]})}async appendRecoveryAttempt(e,s){const n=await this.readState(s),o=n.recovery_history??[];if(o.some(i=>i.attempted_at===e.attempted_at&&i.story_id===e.story_id)){this.logger.warn({event:"recovery_attempted_duplicate",story_id:e.story_id},"duplicate recovery_attempted op; skipping");return}const c=M([...o,e]),a={...n,recovery_history:c};await this.writeState(a),this.logger.info({event:"recovery_attempted_persisted",story_id:e.story_id,history_size:c.length},"recovery attempt persisted")}async resolveRecoveryAttempt(e,s,n){const o=await this.readState(n),r=o.recovery_history??[],c=r.findIndex(h=>h.recovery_step_id_tokenized===e);if(c===-1){this.logger.warn({event:"recovery_resolve_orphan",attempt_id:e},"recovery_resolved op had no matching attempt; skipping");return}const a=r[c];if(a.outcome===s&&a.finished_at){this.logger.info({event:"recovery_resolved_duplicate",attempt_id:e,outcome:s},"duplicate recovery_resolved op; skipping");return}const i=r.slice();i[c]={...a,outcome:s,finished_at:n};const l={...o,recovery_history:i};await this.writeState(l),this.logger.info({event:"recovery_resolved_persisted",attempt_id:e,outcome:s},"recovery attempt resolved")}async acquireLock(e,s){const n={...e,pid:process.pid,hostname:w(),ownerKind:"client",startedAt:e.startedAt||s,leaseId:e.leaseId??he(e.sessionId,s),leaseState:"active",leaseAcquiredAt:e.leaseAcquiredAt??s,leaseDurationMs:e.leaseDurationMs??W,leaseExpiresAt:e.leaseExpiresAt??e.ttlExpiresAt},o=JSON.stringify(n,null,2);try{const r=await u.open(this.lockPath,"wx");try{await r.writeFile(o,"utf8")}finally{await r.close()}this.trySetFilePerms(this.lockPath),this.logger.info({event:"yoloop_lock_acquired",pid:n.pid,sessionId:n.sessionId},"yoloop lock acquired");return}catch(r){const c=r;if(c.code!=="EEXIST")throw this.logger.error({event:"yoloop_lock_acquire_error",err:c.message},"failed to acquire yoloop lock"),r;const a=await this.readLock();if(a&&this.isSameClientSessionLock(a,n)){this.logger.info({event:"yoloop_lock_acquire_duplicate",sessionId:n.sessionId},"duplicate acquire_lock for current yoloop session; treating as no-op");return}if(!await this.maybeRecoverOrphanLock(s))throw this.logger.warn({event:"yoloop_lock_held"},"yoloop lock held by another live process"),r;const l=await u.open(this.lockPath,"wx");try{await l.writeFile(o,"utf8")}finally{await l.close()}this.trySetFilePerms(this.lockPath),this.logger.info({event:"yoloop_lock_acquired_after_recovery",pid:n.pid},"yoloop lock acquired after orphan recovery")}}async releaseLock(e){const s=await this.readState(new Date().toISOString()),n=await this.readLock();if(s.runControl){if(!e.expectedRunId||!e.expectedSessionId||!e.expectedLeaseId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if(e.expectedRunId!==s.runControl.runId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if(!n||e.expectedSessionId!==n.sessionId||e.expectedLeaseId!==n.leaseId)throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}try{await u.unlink(this.lockPath),this.logger.info({event:"yoloop_lock_released"},"yoloop lock released")}catch(o){const r=o;if(r.code==="ENOENT"){this.logger.info({event:"yoloop_lock_release_noop"},"yoloop lock release noop (file absent)");return}this.logger.warn({event:"yoloop_lock_release_error",err:r.message},"unexpected error releasing yoloop lock")}}async readLock(){try{const e=await u.readFile(this.lockPath,"utf8"),s=JSON.parse(e),n=s.currentEpicId??s.epicId??"";return{pid:s.pid??0,hostname:s.hostname??"",...s.ownerKind?{ownerKind:s.ownerKind}:{},...s.ownerPidState?{ownerPidState:s.ownerPidState}:{},...typeof s.leaseId=="string"?{leaseId:s.leaseId}:{},...s.leaseState?{leaseState:s.leaseState}:{},...typeof s.leaseAcquiredAt=="string"?{leaseAcquiredAt:s.leaseAcquiredAt}:{},...typeof s.leaseDurationMs=="number"?{leaseDurationMs:s.leaseDurationMs}:{},...typeof s.leaseExpiresAt=="string"?{leaseExpiresAt:s.leaseExpiresAt}:{},startedAt:s.startedAt??"",sessionId:s.sessionId??"",mode:s.mode??"epic-id",inputPath:s.inputPath??"",epicQueue:s.epicQueue??(n?[n]:[]),currentEpicIndex:s.currentEpicIndex??0,currentEpicId:n,ttlExpiresAt:s.ttlExpiresAt??"",epicId:s.epicId??n}}catch{return null}}async readState(e){try{const s=await u.readFile(this.statePath,"utf8"),n=JSON.parse(s);return Oe(n,e)}catch(s){const n=s;return n.code!=="ENOENT"&&this.logger.warn({event:"yoloop_state_read_error",err:n.message},"yoloop state file unreadable; using defaults"),f(e)}}async writeState(e){const s=`${this.statePath}.tmp.${process.pid}.${Date.now()}`,n=JSON.stringify(e,null,2);try{const o=await u.open(s,"w",384);try{await o.writeFile(n,"utf8"),await o.sync()}finally{await o.close()}this.trySetFilePerms(s),await u.rename(s,this.statePath),await this.syncStateDirectory()}catch(o){const r=o;this.logger.error({event:"yoloop_state_write_error",err:r.message},"failed to write yoloop state");try{await u.unlink(s)}catch{}throw o}}async rolloverSession(e,s){E(e);const n=await this.readRolloverManifest();if(n){if(ae(n.operation,e)){if(n.operation.payloadHash!==e.payloadHash)throw new Error("YOLOOP_SESSION_ROLLOVER_CONFLICT");if(n.phase==="applied")return;await this.completePreparedSessionRollover(n.operation,s);return}else if(n.phase==="prepared")throw new Error("YOLOOP_SESSION_ROLLOVER_CONFLICT")}const o=await this.readState(s),r=await this.readLock();U(e,o,r),await this.writeRolloverManifest({schemaVersion:"YoloopSessionRolloverManifest/1.0",phase:"prepared",operation:e}),await this.completePreparedSessionRollover(e,s)}async completePreparedSessionRollover(e,s){const n=await this.readState(s);let o=d(n)===d(e.next);if(!o){const c=await this.readLock();U(e,n,c),await this.writeState(e.next),o=!0}const r=await this.readLock();if(e.expectedSessionId===null||e.expectedLeaseId===null){if(e.expectedSessionId!==null||e.expectedLeaseId!==null||r)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT")}else if(r){if(r.sessionId!==e.expectedSessionId||r.leaseId!==e.expectedLeaseId)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT");await u.unlink(this.lockPath),await this.syncStateDirectory()}else if(!o)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT");await this.writeRolloverManifest({schemaVersion:"YoloopSessionRolloverManifest/1.0",phase:"applied",operation:e}),this.logger.info({event:"yoloop_session_rollover_applied",runId:e.runId,operationId:e.operationId,coolDownUntil:e.coolDownUntil},"yoloop per-run-cap session rollover applied")}async recoverSessionRolloverIfPresent(e){try{await u.access(this.rolloverManifestPath)}catch(s){if(s.code==="ENOENT")return;throw s}await this.ensureDir(),await this.withMutationLock(async()=>{const s=await this.readRolloverManifest();s?.phase==="prepared"&&await this.completePreparedSessionRollover(s.operation,e)})}async readRolloverManifest(){try{const e=JSON.parse(await u.readFile(this.rolloverManifestPath,"utf8"));if(e.schemaVersion!=="YoloopSessionRolloverManifest/1.0"||e.phase!=="prepared"&&e.phase!=="applied"||!e.operation||e.operation.type!=="rollover_session")throw new Error("YOLOOP_SESSION_ROLLOVER_MANIFEST_CORRUPT");return E(e.operation),e}catch(e){if(e.code==="ENOENT")return null;throw e.message==="YOLOOP_SESSION_ROLLOVER_MANIFEST_CORRUPT"?e:new Error("YOLOOP_SESSION_ROLLOVER_MANIFEST_CORRUPT")}}async writeRolloverManifest(e){const s=`${this.rolloverManifestPath}.tmp.${process.pid}.${Date.now()}`,n=await u.open(s,"w",384);try{await n.writeFile(JSON.stringify(e,null,2),"utf8"),await n.sync()}finally{await n.close()}this.trySetFilePerms(s),await u.rename(s,this.rolloverManifestPath),await this.syncStateDirectory()}async commitSuccessorTransition(e,s){R(e);const n=await this.readSuccessorTransitionManifest();if(n){if(oe(n.operation,e)){if(n.operation.payloadHash!==e.payloadHash)throw new Error("YOLOOP_SUCCESSOR_TRANSITION_CONFLICT");if(n.phase==="applied")return;await this.completePreparedSuccessorTransition(n.operation,s);return}if(n.phase==="prepared")throw new Error("YOLOOP_SUCCESSOR_TRANSITION_CONFLICT")}const o=await this.readState(s);if(v(e,o)){await this.writeSuccessorTransitionManifest({schemaVersion:"YoloopSuccessorTransitionManifest/1.0",phase:"applied",operation:e});return}H(e,o),await this.writeSuccessorTransitionManifest({schemaVersion:"YoloopSuccessorTransitionManifest/1.0",phase:"prepared",operation:e}),await this.completePreparedSuccessorTransition(e,s)}async completePreparedSuccessorTransition(e,s){let n=await this.readState(s);if(!v(e,n)){d(n.runControl)===d(e.successor.next)||(await this.startSuccessorRun(e.successor,s),n=await this.readState(s));const r=ie(e,n);await this.writeState(r),n=await this.readState(s)}if(!v(e,n))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_STATE_CONFLICT");await this.writeSuccessorTransitionManifest({schemaVersion:"YoloopSuccessorTransitionManifest/1.0",phase:"applied",operation:e}),this.logger.info({event:"yoloop_successor_transition_applied",runId:e.successor.runId,operationId:e.operationId},"yoloop successor transition applied")}async recoverSuccessorTransitionIfPresent(e){try{await u.access(this.successorTransitionManifestPath)}catch(s){if(s.code==="ENOENT")return;throw s}await this.ensureDir(),await this.withMutationLock(async()=>{const s=await this.readSuccessorTransitionManifest();s?.phase==="prepared"&&await this.completePreparedSuccessorTransition(s.operation,e)})}async readSuccessorTransitionManifest(){try{const e=JSON.parse(await u.readFile(this.successorTransitionManifestPath,"utf8"));if(e.schemaVersion!=="YoloopSuccessorTransitionManifest/1.0"||e.phase!=="prepared"&&e.phase!=="applied"||!e.operation||e.operation.type!=="commit_successor_transition")throw new Error("YOLOOP_SUCCESSOR_TRANSITION_MANIFEST_CORRUPT");return R(e.operation),e}catch(e){if(e.code==="ENOENT")return null;throw e.message==="YOLOOP_SUCCESSOR_TRANSITION_MANIFEST_CORRUPT"?e:new Error("YOLOOP_SUCCESSOR_TRANSITION_MANIFEST_CORRUPT")}}async writeSuccessorTransitionManifest(e){const s=`${this.successorTransitionManifestPath}.tmp.${process.pid}.${Date.now()}`,n=await u.open(s,"w",384);try{await n.writeFile(JSON.stringify(e,null,2),"utf8"),await n.sync()}finally{await n.close()}this.trySetFilePerms(s),await u.rename(s,this.successorTransitionManifestPath),await this.syncStateDirectory()}async advanceEpic(e,s,n){const o=await this.readState(n);if(!o.currentSession){this.logger.warn({event:"yoloop_advance_no_session",nextEpicId:s},"advance_epic with no active session; skipping");return}const r={...o,currentSession:{...o.currentSession,currentEpicIndex:e}};await this.writeState(r),this.logger.info({event:"yoloop_epic_advance",nextEpicId:s,nextIndex:e},"yoloop advanced to next epic")}async ensureDir(){try{await u.mkdir(this.stateDir,{recursive:!0}),this.trySetDirPerms(this.stateDir)}catch(e){const s=e;throw this.logger.error({event:"yoloop_mkdir_error",err:s.message,path:this.stateDir},"failed to ensure .neocortex/ directory"),e}}async syncStateDirectory(){try{const e=await u.open(this.stateDir,"r");try{await e.sync()}finally{await e.close()}}catch(e){const s=e.code;if(s!=="EINVAL"&&s!=="EISDIR"&&s!=="ENOTSUP"&&s!=="EPERM")throw e}}async withMutationLock(e){let s=null;for(let n=0;n<X;n+=1)try{s=await u.open(this.mutationLockPath,"wx",384),await s.writeFile(JSON.stringify({pid:process.pid,acquiredAt:new Date().toISOString()})),await s.sync();break}catch(o){if(o.code!=="EEXIST")throw o;try{const r=await u.stat(this.mutationLockPath);if(Date.now()-r.mtimeMs>G){await u.unlink(this.mutationLockPath);continue}}catch{continue}await new Promise(r=>setTimeout(r,5))}if(!s)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");try{return await e()}finally{await s.close().catch(()=>{}),await u.unlink(this.mutationLockPath).catch(()=>{})}}async transitionRun(e,s){N(e);const n=await this.readState(s),o=n.runControl,r=o?.receipts.find(l=>l.operationId===e.operationId||l.idempotencyKey===e.idempotencyKey);if(r){if(r.operationId===e.operationId&&r.idempotencyKey===e.idempotencyKey&&r.payloadHash===e.payloadHash)return r;throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}if(o&&o.runId!==e.runId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if((o?.revision??0)!==e.expectedRevision)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if((o?.latestSnapshotHash??null)!==e.expectedSnapshotHash)throw new Error("YOLOOP_RUN_SNAPSHOT_CONFLICT");L(o,e);const i=e.next.receipts.at(-1);if(!i)throw new Error("YOLOOP_RUN_OPERATION_CONFLICT");return await this.writeState({...n,version:"1.2",runControl:e.next}),this.logger.info({event:"yoloop_run_transition_committed",runId:e.runId,operationId:e.operationId,revision:i.revision,status:i.status},"yoloop run transition committed"),i}async startSuccessorRun(e,s){Y(e);const n=await this.readState(s),o=n.runControl,r={schemaVersion:"YoloopRunPredecessor/1.0",runId:e.previousRunId,revision:e.expectedRevision,snapshotHash:e.expectedSnapshotHash,successorOperationId:e.operationId,publicSafe:!0},c=o?.receipts.find(l=>l.operationId===e.operationId||l.idempotencyKey===e.idempotencyKey);if(c){if(c.operationId===e.operationId&&c.idempotencyKey===e.idempotencyKey&&e.next.receipts.at(-1)?.payloadHash===c.payloadHash&&d(o?.predecessor)===d(r))return c;throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}if(!o||o.runId!==e.previousRunId||!["exhausted","success"].includes(o.status)||o.revision!==e.expectedRevision||o.latestSnapshotHash!==e.expectedSnapshotHash||n.currentSession!==null)throw new Error("YOLOOP_RUN_SUCCESSOR_CONFLICT");const a=e.next.receipts.at(-1);if(!a||d(e.next.predecessor)!==d(r))throw new Error("YOLOOP_RUN_SUCCESSOR_CONFLICT");const i={type:"transition_run",runId:e.runId,operationId:e.operationId,idempotencyKey:e.idempotencyKey,expectedRevision:0,expectedSnapshotHash:null,payloadHash:a.payloadHash,next:e.next};return N(i),L(void 0,i),await this.writeState({...n,version:"1.2",runControl:e.next}),a}trySetFilePerms(e){try{q(e)}catch{}}trySetDirPerms(e){try{B(e)}catch{}}async prepareLockForSnapshot(e,s){if(!e)return null;if(e.ownerKind!=="client")return e;const n=K(e,s),o={...e,leaseState:n};if(n==="expired")return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_client_lock_recovered",reason:"lease_expired"},"expired client-owned yoloop lock lease recovered before snapshot echo"),null;if(!o.pid||o.pid<=0)return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_client_lock_recovered",reason:"invalid_pid"},"invalid PID in client-owned yoloop lock; recovered"),null;if(o.hostname&&o.hostname!==w())return{...o,ownerPidState:"unknown"};try{return process.kill(o.pid,0),{...o,ownerPidState:"live"}}catch{return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_client_lock_recovered",reason:"dead_pid"},"stale client-owned yoloop lock recovered before snapshot echo"),null}}async maybeRecoverOrphanLock(e){const s=await this.readLock();if(!s)return!0;if(K(s,e)==="expired")return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_orphan_lock_recovered",reason:"lease_expired"},"expired yoloop lock lease recovered"),!0;if(!s.pid||s.pid<=0)return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_orphan_lock_recovered",reason:"invalid_pid"},"invalid PID in lock file; recovered"),!0;if(s.hostname&&s.hostname!==w())return this.logger.warn({event:"yoloop_lock_remote_host",ownerKind:s.ownerKind??"server",ownerPidState:"unknown"},"lock held by a remote host; honoring"),!1;try{return process.kill(s.pid,0),!1}catch{return await this.deleteLockBestEffort(),this.logger.warn({event:"yoloop_orphan_lock_recovered",reason:"dead_pid"},"orphan yoloop lock recovered"),!0}}async deleteLockBestEffort(){try{await u.unlink(this.lockPath)}catch{}}isSameClientSessionLock(e,s){return e.ownerKind==="client"&&e.sessionId===s.sessionId&&e.pid===process.pid&&e.hostname===w()}async cleanupOrphanTempFiles(){try{const e=$(this.statePath),s=await u.readdir(e),n=[`${g}.tmp.`,`${A}.tmp.`,`${P}.tmp.`],o=s.filter(r=>n.some(c=>r.startsWith(c)));for(const r of o)try{await u.unlink(S(e,r))}catch{}}catch{}}}function d(t){return t===null||typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(d).join(",")}]`:`{${Object.entries(t).filter(([,e])=>e!==void 0).sort(([e],[s])=>e.localeCompare(s)).map(([e,s])=>`${JSON.stringify(e)}:${d(s)}`).join(",")}}`}function p(t,e){return V("sha256").update(`${t}\0${d(e)}`).digest("hex")}function Q(t){return p("p222-yoloop-continuation-replan-v1",t)}function k(t){if(!t||typeof t!="object"||Array.isArray(t))return!1;const{sessionId:e,...s}=t;return typeof e=="string"&&/^[a-f0-9]{64}$/.test(e)&&e===p("p205-yoloop-session-v1",s)}function ee(t){return p("p9390812898200217364-yoloop-session-v1",t)}function te(t){return p("p9390812898200217364-yoloop-rollover-v1",t)}function se(t){return p("p9390812898200217364-yoloop-successor-run-v1",t)}function ne(t){return p("p9046281030721413419-yoloop-successor-transition-v1",t)}function oe(t,e){return t.operationId===e.operationId&&t.idempotencyKey===e.idempotencyKey}function R(t){const e=/^[A-Za-z0-9._:-]{1,128}$/;Y(t.successor);const s={operationId:t.operationId,idempotencyKey:t.idempotencyKey,successor:t.successor,state:t.state,...t.acknowledgement?{acknowledgement:t.acknowledgement}:{},...t.reservation?{reservation:t.reservation}:{}},n=t.acknowledgement?.idempotencyKey,o=t.acknowledgement?.receipt.idempotencyKey,r=t.reservation?.authority.idempotencyKey;if(t.successor.type!=="start_successor_run"||!e.test(t.operationId)||!e.test(t.idempotencyKey)||t.operationId!==t.successor.operationId||t.idempotencyKey!==t.successor.idempotencyKey||!/^[a-f0-9]{64}$/.test(t.payloadHash)||t.payloadHash!==ne(s)||Buffer.byteLength(d(s),"utf8")>1048576||t.acknowledgement!==void 0&&(t.acknowledgement.type!=="acknowledge_continuation_authority"||typeof n!="string"||!e.test(n)||o!==n)||t.reservation!==void 0&&(t.reservation.type!=="reserve_continuation_authority"||typeof r!="string"||!e.test(r))||n!==void 0&&r===n)throw new Error("YOLOOP_SUCCESSOR_TRANSITION_CONFLICT")}function H(t,e){if(R(t),!(d(e.runControl)===d(t.successor.next))){const n=e.runControl;if(!n||n.runId!==t.successor.previousRunId||!["exhausted","success"].includes(n.status)||n.revision!==t.successor.expectedRevision||n.latestSnapshotHash!==t.successor.expectedSnapshotHash||e.currentSession!==null)throw new Error("YOLOOP_SUCCESSOR_TRANSITION_STATE_CONFLICT")}re(t,e)}function re(t,e){const s=e.pendingContinuationAuthorities??[],n=e.continuationReceipts??[],o=t.acknowledgement;if(o){const l=s.find(y=>y.idempotencyKey===o.idempotencyKey);if(!l||o.receipt.sessionId!==l.sessionId||d(o.receipt.storyIds)!==d(l.storyIds))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT");const h=s.filter(y=>y.idempotencyKey!==o.idempotencyKey),O=[...n.slice(-63),o.receipt];if(d(t.state.pendingContinuationAuthorities??[])!==d(h)||d(t.state.continuationReceipts??[])!==d(O))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT")}else if(d(t.state.pendingContinuationAuthorities??[])!==d(s)||d(t.state.continuationReceipts??[])!==d(n))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT");const r=t.reservation?.authority;if(!r)return;const c=String(r.idempotencyKey??""),a=t.state.pendingContinuationAuthorities??[],i=a.find(l=>l.idempotencyKey===c);if(i&&d(i)!==d(r))throw new Error("YOLOOP_SUCCESSOR_TRANSITION_AUTHORITY_CONFLICT");if(!i&&a.length>=64)throw new Error("YOLOOP_PENDING_AUTHORITY_CAPACITY_EXCEEDED")}function ie(t,e){H(t,e);const s=t.reservation?.authority,n=[...t.state.pendingContinuationAuthorities??[]];return s&&!n.some(o=>o.idempotencyKey===s.idempotencyKey)&&n.push(s),{...t.state,version:"1.2",runControl:t.successor.next,...s||t.state.pendingContinuationAuthorities!==void 0?{pendingContinuationAuthorities:n}:{}}}function v(t,e){const s=t.reservation?.authority,n=[...t.state.pendingContinuationAuthorities??[]];s&&!n.some(r=>r.idempotencyKey===s.idempotencyKey)&&n.push(s);const o={...t.state,version:"1.2",runControl:t.successor.next,...s||t.state.pendingContinuationAuthorities!==void 0?{pendingContinuationAuthorities:n}:{}};return d(e)===d(o)}function ae(t,e){return t.operationId===e.operationId&&t.idempotencyKey===e.idempotencyKey}function E(t){const e=/^[A-Za-z0-9._:-]{1,128}$/,s={runId:t.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.expectedRevision,expectedSnapshotHash:t.expectedSnapshotHash,expectedSessionId:t.expectedSessionId,expectedSessionFingerprint:t.expectedSessionFingerprint,expectedLeaseId:t.expectedLeaseId,coolDownUntil:t.coolDownUntil,reason:t.reason,exhaustedRun:t.exhaustedRun,next:t.next};if(!e.test(t.runId)||!e.test(t.operationId)||!e.test(t.idempotencyKey)||!/^[a-f0-9]{64}$/.test(t.expectedSessionFingerprint)||!/^[a-f0-9]{64}$/.test(t.payloadHash)||!Number.isSafeInteger(t.expectedRevision)||t.expectedRevision<0||!Number.isFinite(Date.parse(t.coolDownUntil))||!["YOLOOP_PER_RUN_CAP_REACHED","YOLOOP_SESSION_TIMEOUT"].includes(t.reason)||t.payloadHash!==te(s)||t.receipt.schemaVersion!=="YoloopSessionRolloverReceipt/1.0"||t.receipt.runId!==t.runId||t.receipt.operationId!==t.operationId||t.receipt.idempotencyKey!==t.idempotencyKey||t.receipt.payloadHash!==t.payloadHash||t.receipt.coolDownUntil!==t.coolDownUntil||t.receipt.revision!==(t.exhaustedRun?.revision??0)||t.receipt.snapshotHash!==(t.exhaustedRun?.latestSnapshotHash??null)||t.receipt.status!=="exhausted"||t.receipt.publicSafe!==!0)throw new Error("YOLOOP_SESSION_ROLLOVER_CONFLICT");if(t.expectedSessionId===null!=(t.expectedLeaseId===null)||t.expectedSessionId!==null&&!e.test(t.expectedSessionId)||t.expectedLeaseId!==null&&!e.test(t.expectedLeaseId))throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT")}function Y(t){const e=/^[A-Za-z0-9._:-]{1,128}$/,s={runId:t.runId,previousRunId:t.previousRunId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.expectedRevision,expectedSnapshotHash:t.expectedSnapshotHash,next:t.next};if(!e.test(t.runId)||!e.test(t.previousRunId)||t.runId===t.previousRunId||!e.test(t.operationId)||!e.test(t.idempotencyKey)||!/^[a-f0-9]{64}$/.test(t.payloadHash)||!Number.isSafeInteger(t.expectedRevision)||t.expectedRevision<1||t.payloadHash!==se(s))throw new Error("YOLOOP_RUN_SUCCESSOR_CONFLICT")}function U(t,e,s){E(t);const n=e.currentSession;if(!n||ee(n)!==t.expectedSessionFingerprint)throw new Error("YOLOOP_SESSION_ROLLOVER_SESSION_CONFLICT");const o=e.runControl;if((o?.revision??0)!==t.expectedRevision||(o?.latestSnapshotHash??null)!==t.expectedSnapshotHash||o&&o.runId!==t.runId)throw new Error("YOLOOP_RUN_REVISION_CONFLICT");if(t.expectedSessionId===null||t.expectedLeaseId===null){if(s)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT")}else if(!s||s.sessionId!==t.expectedSessionId||s.leaseId!==t.expectedLeaseId)throw new Error("YOLOOP_SESSION_ROLLOVER_LOCK_CONFLICT");const r=t.exhaustedRun;if(o)if(o.status==="exhausted"){if(d(r)!==d(o))throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT")}else{if(!r||r.status!=="exhausted")throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT");const a=r.receipts.at(-1);if(!a)throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT");const i={type:"transition_run",runId:o.runId,operationId:a.operationId,idempotencyKey:a.idempotencyKey,expectedRevision:o.revision,expectedSnapshotHash:o.latestSnapshotHash,payloadHash:a.payloadHash,next:r};N(i),L(o,i)}else{const a=r?.receipts[0],i=r?.receipts[1];if(r&&(r.runId!==t.runId||r.status!=="exhausted"||r.revision!==2||r.events.length!==2||r.progressSnapshots.length!==4||r.receipts.length!==2||a?.runId!==t.runId||a.revision!==1||a.status!=="created"||i?.runId!==t.runId||i.revision!==2||i.status!=="exhausted"||i.operationId!==`${t.operationId}:run`||i.idempotencyKey!==`${t.idempotencyKey}:run`||r.latestSnapshotHash!==i.snapshotHash||r.predecessor!==void 0))throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT")}const c={...e,version:"1.2",coolDownUntil:t.coolDownUntil,currentSession:null,...r?{runControl:r}:{}};if(d(c)!==d(t.next))throw new Error("YOLOOP_SESSION_ROLLOVER_STATE_CONFLICT")}function D(t){const{receipts:e,...s}=t.next;return p("p220-yoloop-run-transition-payload-v1",{runId:t.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.expectedRevision,expectedSnapshotHash:t.expectedSnapshotHash,next:s})}const T=new Set(["success","partial","blocked","failure","exhausted","cancelled"]),ce={created:["running","cancelling","exhausted"],running:["pausing","cancelling","success","partial","blocked","failure","exhausted"],pausing:["paused","cancelling","failure","exhausted"],paused:["resuming","cancelling","exhausted"],resuming:["running","cancelling","failure","exhausted"],cancelling:["cancelled","failure","exhausted"]};function de(t,e){return e==="pause"?t==="running"?"pausing":null:e==="resume"?t==="paused"?"resuming":null:T.has(t)||t==="cancelling"?null:"cancelling"}function le(t){const e=t.current.revision+1,s=t.action==="cancel"?["YOLOOP_RUN_CANCEL_REQUESTED"]:[],n={schemaVersion:"YoloopOperationalBudgetSummary/1.0",limits:t.current.operationalBudget.limits,usage:t.current.usage,exhaustedDimension:null},o={schemaVersion:"ProgressSnapshot/1.0",runId:t.current.runId,sequence:t.current.progressSnapshots.length+1,revision:e,operationId:t.operationId,boundary:t.action,phase:"before",status:t.current.status,occurredAt:t.occurredAt,parentSnapshotHash:t.current.latestSnapshotHash,budget:n,reasonCodes:s,publicSafe:!0},r=p("p220-yoloop-progress-snapshot-id-v1",{runId:o.runId,operationId:o.operationId,sequence:o.sequence,phase:o.phase}),c={...o,snapshotId:r,snapshotHash:p("p220-yoloop-progress-snapshot-v1",{...o,snapshotId:r})},a={...o,sequence:c.sequence+1,phase:"after",status:t.target,parentSnapshotHash:c.snapshotHash},i=p("p220-yoloop-progress-snapshot-id-v1",{runId:a.runId,operationId:a.operationId,sequence:a.sequence,phase:a.phase}),l={...a,snapshotId:i,snapshotHash:p("p220-yoloop-progress-snapshot-v1",{...a,snapshotId:i})},h={schemaVersion:"YoloopRunEvent/1.0",runId:t.current.runId,operationId:t.operationId,kind:"intent",sequence:t.current.events.length+1,revision:e,occurredAt:t.occurredAt,fromStatus:t.current.status,toStatus:t.target,reasonCodes:s,budgetDimension:null,parentEventHash:t.current.events.at(-1)?.eventHash??null,publicSafe:!0},O=p("p220-yoloop-run-event-id-v1",{runId:h.runId,operationId:h.operationId,sequence:h.sequence}),y={...h,eventId:O,eventHash:p("p220-yoloop-run-event-v1",{...h,eventId:O})},I={...t.current,revision:e,status:t.target,events:[...t.current.events,y],progressSnapshots:[...t.current.progressSnapshots,c,l],latestSnapshotHash:l.snapshotHash,receipts:[...t.current.receipts]},_={runId:t.current.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,expectedRevision:t.current.revision,expectedSnapshotHash:t.current.latestSnapshotHash,next:I},C=D(_),b={schemaVersion:"YoloopRunTransitionReceipt/1.0",runId:t.current.runId,operationId:t.operationId,idempotencyKey:t.idempotencyKey,payloadHash:C,revision:e,snapshotHash:l.snapshotHash,status:t.target};return{type:"transition_run",..._,payloadHash:C,next:{...I,receipts:[...I.receipts,b]}}}function N(t){const e=/^[A-Za-z0-9._:-]{1,128}$/;if(!e.test(t.runId)||!e.test(t.operationId)||!e.test(t.idempotencyKey)||!/^[a-f0-9]{64}$/.test(t.payloadHash)||!Number.isSafeInteger(t.expectedRevision)||t.expectedRevision<0)throw new Error("YOLOOP_RUN_OPERATION_CONFLICT");if(t.payloadHash!==D(t))throw new Error("YOLOOP_RUN_OPERATION_CONFLICT")}function L(t,e){const s=e.next,n=t?.events??[],o=t?.progressSnapshots??[],r=t?.receipts??[];if(s.schemaVersion!=="YoloopRunControl/1.0"||s.runId!==e.runId||s.revision!==e.expectedRevision+1||s.events.length!==n.length+1||s.progressSnapshots.length!==o.length+2||s.receipts.length!==r.length+1||d(s.events.slice(0,n.length))!==d(n)||d(s.progressSnapshots.slice(0,o.length))!==d(o)||d(s.receipts.slice(0,r.length))!==d(r))throw new Error("YOLOOP_RUN_TRANSITION_INVALID");if(t){if(T.has(t.status))throw new Error("YOLOOP_RUN_TERMINAL");const h=s.progressSnapshots.at(-2)?.boundary,O=t.status===s.status&&(h==="budget"||h==="retry");if(!(ce[t.status]??[]).includes(s.status)&&!O)throw new Error("YOLOOP_RUN_TRANSITION_INVALID");if(t.startedAt!==s.startedAt||t.deadlineAt!==s.deadlineAt)throw new Error("YOLOOP_RUN_TRANSITION_INVALID");if(d(t.predecessor)!==d(s.predecessor))throw new Error("YOLOOP_RUN_TRANSITION_INVALID")}else if(s.status!=="created")throw new Error("YOLOOP_RUN_TRANSITION_INVALID");const c=s.progressSnapshots.at(-2),a=s.progressSnapshots.at(-1),i=s.events.at(-1),l=s.receipts.at(-1);if(!c||!a||!i||!l||c.phase!=="before"||a.phase!=="after"||c.operationId!==e.operationId||a.operationId!==e.operationId||i.operationId!==e.operationId||c.runId!==e.runId||a.runId!==e.runId||i.runId!==e.runId||c.revision!==s.revision||a.revision!==s.revision||i.revision!==s.revision||c.parentSnapshotHash!==(t?.latestSnapshotHash??null)||a.parentSnapshotHash!==c.snapshotHash||s.latestSnapshotHash!==a.snapshotHash||l.schemaVersion!=="YoloopRunTransitionReceipt/1.0"||l.runId!==e.runId||l.operationId!==e.operationId||l.idempotencyKey!==e.idempotencyKey||l.payloadHash!==e.payloadHash||l.revision!==s.revision||l.snapshotHash!==s.latestSnapshotHash||l.status!==s.status||Date.parse(s.deadlineAt)<=Date.parse(s.startedAt))throw new Error("YOLOOP_RUN_TRANSITION_INVALID");F(c),F(a),ue(i,n.at(-1)?.eventHash??null)}function F(t){const{snapshotHash:e,...s}=t;if(e!==p("p220-yoloop-progress-snapshot-v1",s))throw new Error("YOLOOP_RUN_SNAPSHOT_CONFLICT")}function ue(t,e){const{eventHash:s,...n}=t;if(t.parentEventHash!==e||s!==p("p220-yoloop-run-event-v1",n))throw new Error("YOLOOP_RUN_SNAPSHOT_CONFLICT")}function he(t,e){return`lease-${pe(`${t}:${e}`)}`}function pe(t){let e=2166136261;for(let s=0;s<t.length;s+=1)e^=t.charCodeAt(s),e=Math.imul(e,16777619)>>>0;return e.toString(36).padStart(7,"0")}function K(t,e){const s=t.leaseExpiresAt??t.ttlExpiresAt,n=Date.parse(s),o=Date.parse(e);return!Number.isFinite(n)||!Number.isFinite(o)?"unknown":n<=o?"expired":"active"}function Oe(t,e){const s=f(e);return{...t,version:t.version==="1.2"?"1.2":"1.1",lastResetAt:t.lastResetAt??s.lastResetAt,storiesRunToday:t.storiesRunToday??0,iterationsToday:t.iterationsToday??0,iterationsTodayDate:t.iterationsTodayDate??s.iterationsTodayDate,tokensUsedToday:t.tokensUsedToday??0,lastRunAt:t.lastRunAt??null,coolDownUntil:t.coolDownUntil??null,totalRunsAllTime:t.totalRunsAllTime??0,totalStoriesAllTime:t.totalStoriesAllTime??0,currentSession:t.currentSession??null,...t.runControl!==void 0?{runControl:t.runControl}:{},...t.continuationPolicy!==void 0?{continuationPolicy:t.continuationPolicy}:{},...t.continuationSession!==void 0?{continuationSession:t.continuationSession}:{},...t.pendingContinuationAuthorities!==void 0?{pendingContinuationAuthorities:t.pendingContinuationAuthorities}:{},...t.continuationReceipts!==void 0?{continuationReceipts:t.continuationReceipts}:{},...t.continuationRecoveryReceipts!==void 0?{continuationRecoveryReceipts:t.continuationRecoveryReceipts}:{},...t.continuationReplanReceipts!==void 0?{continuationReplanReceipts:t.continuationReplanReceipts}:{},recovery_history:Array.isArray(t.recovery_history)?M(t.recovery_history):[]}}function M(t){return t.length<=m?t:t.slice(t.length-m)}export{x as YOLOOP_PERSISTENCE_CAPABILITIES,ve as YoloopClientStateStore,Q as buildYoloopContinuationReplanPayloadHash,D as buildYoloopRunTransitionPayloadHash,te as buildYoloopSessionRolloverPayloadHash,se as buildYoloopSuccessorRunPayloadHash,ne as buildYoloopSuccessorTransitionPayloadHash,M as capRecoveryHistory,f as createDefaultCounters,Re as createDefaultState,ee as fingerprintYoloopSession};
|
|
@@ -2,41 +2,41 @@
|
|
|
2
2
|
export { PUBLIC_PROJECTION_BUNDLE, PUBLIC_PROJECTION_GENERATION_ID, PUBLIC_PROJECTION_BUNDLE_HASH } from './public-projections.generated.js';
|
|
3
3
|
export const COMMAND_REGISTRY_SCHEMA_VERSION = 1;
|
|
4
4
|
export const COMMAND_REGISTRY_VERSION = "1.0.0";
|
|
5
|
-
export const COMMAND_REGISTRY_CHECKSUM = "
|
|
5
|
+
export const COMMAND_REGISTRY_CHECKSUM = "a631fa17a24972952ef0270880105bfc4d1a6be17e8622c5916265d945fcb69d";
|
|
6
6
|
export const COMMAND_REGISTRY_PROJECTION_MANIFEST = {
|
|
7
7
|
"schemaVersion": 1,
|
|
8
8
|
"registryVersion": "1.0.0",
|
|
9
|
-
"registryChecksum": "
|
|
9
|
+
"registryChecksum": "a631fa17a24972952ef0270880105bfc4d1a6be17e8622c5916265d945fcb69d",
|
|
10
10
|
"projections": [
|
|
11
11
|
{
|
|
12
12
|
"kind": "admin",
|
|
13
13
|
"entryCount": 62,
|
|
14
|
-
"sha256": "
|
|
14
|
+
"sha256": "e155547000e8f0607360144555b10cb90f222f37b010120eb2e97ac802ce821f"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"kind": "client",
|
|
18
18
|
"entryCount": 62,
|
|
19
|
-
"sha256": "
|
|
19
|
+
"sha256": "e155547000e8f0607360144555b10cb90f222f37b010120eb2e97ac802ce821f"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"kind": "docs",
|
|
23
23
|
"entryCount": 62,
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "e155547000e8f0607360144555b10cb90f222f37b010120eb2e97ac802ce821f"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"kind": "metrics",
|
|
28
28
|
"entryCount": 62,
|
|
29
|
-
"sha256": "
|
|
29
|
+
"sha256": "e155547000e8f0607360144555b10cb90f222f37b010120eb2e97ac802ce821f"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"kind": "server",
|
|
33
33
|
"entryCount": 67,
|
|
34
|
-
"sha256": "
|
|
34
|
+
"sha256": "ffd28ece4ed51da16af392cbd97cfe5580433f925f926dabe1d409387126f1ab"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"kind": "shared",
|
|
38
38
|
"entryCount": 67,
|
|
39
|
-
"sha256": "
|
|
39
|
+
"sha256": "5c820b21ff7e1d72b867a7a6927d292b404479c1c311be26840d3672b0c056fb"
|
|
40
40
|
}
|
|
41
41
|
]
|
|
42
42
|
};
|
|
@@ -932,6 +932,7 @@ export const GENERATED_SHARED_COMMANDS = [
|
|
|
932
932
|
"pt-BR": "Commit e push"
|
|
933
933
|
},
|
|
934
934
|
"examples": [
|
|
935
|
+
"@neocortex *commit",
|
|
935
936
|
"@neocortex *commit @docs/stories/P126.01.story.md"
|
|
936
937
|
],
|
|
937
938
|
"id": "commit",
|
|
@@ -1444,7 +1445,8 @@ export const GENERATED_SHARED_COMMANDS = [
|
|
|
1444
1445
|
"pt-BR": "Gerar testes Pact (consumer + provider) de openapi.yaml"
|
|
1445
1446
|
},
|
|
1446
1447
|
"examples": [
|
|
1447
|
-
"@neocortex *generate-pacts"
|
|
1448
|
+
"@neocortex *generate-pacts",
|
|
1449
|
+
"@neocortex *generate-pacts P126"
|
|
1448
1450
|
],
|
|
1449
1451
|
"id": "generate-pacts",
|
|
1450
1452
|
"inputShape": "standalone",
|
|
@@ -3922,7 +3924,7 @@ export const GENERATED_SHARED_COMMANDS = [
|
|
|
3922
3924
|
"policy": {
|
|
3923
3925
|
"billingClass": "non-billable",
|
|
3924
3926
|
"idempotencyClass": "logical-supported",
|
|
3925
|
-
"minimumTier": "
|
|
3927
|
+
"minimumTier": "pro",
|
|
3926
3928
|
"mutationClass": "stateful",
|
|
3927
3929
|
"privilege": "authenticated",
|
|
3928
3930
|
"quotaClass": "none",
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
export const GENERATED_INVOCATION_SCHEMA_MANIFEST = {
|
|
3
3
|
"schemaVersion": "p205-invocation-schema-v1",
|
|
4
4
|
"registryVersion": "1.0.0",
|
|
5
|
-
"registryChecksum": "
|
|
6
|
-
"schemaChecksum": "
|
|
5
|
+
"registryChecksum": "a631fa17a24972952ef0270880105bfc4d1a6be17e8622c5916265d945fcb69d",
|
|
6
|
+
"schemaChecksum": "5c1a3ec92913a1e9c84b61de15ccfa72d223556db9d3d9e40504f28e86a09a0e",
|
|
7
7
|
"commandCount": 62,
|
|
8
8
|
"optionCount": 48,
|
|
9
|
-
"positionalCount":
|
|
9
|
+
"positionalCount": 44,
|
|
10
10
|
"operationCount": 35,
|
|
11
|
-
"projectionSha256": "
|
|
11
|
+
"projectionSha256": "15e972b62215710565cff40fbd552bcbb7ec6c48f224e57c6b963d0e5809d225",
|
|
12
12
|
"commands": [
|
|
13
13
|
{
|
|
14
14
|
"commandId": "arch-api-contracts",
|
|
@@ -391,7 +391,7 @@ export const GENERATED_INVOCATION_SCHEMA_MANIFEST = {
|
|
|
391
391
|
"id": "story",
|
|
392
392
|
"kind": "story-ref",
|
|
393
393
|
"order": 0,
|
|
394
|
-
"required":
|
|
394
|
+
"required": false
|
|
395
395
|
}
|
|
396
396
|
],
|
|
397
397
|
"terminator": true
|
|
@@ -585,7 +585,15 @@ export const GENERATED_INVOCATION_SCHEMA_MANIFEST = {
|
|
|
585
585
|
"operations": []
|
|
586
586
|
},
|
|
587
587
|
"options": [],
|
|
588
|
-
"positionals": [
|
|
588
|
+
"positionals": [
|
|
589
|
+
{
|
|
590
|
+
"cardinality": "one",
|
|
591
|
+
"id": "epic",
|
|
592
|
+
"kind": "epic-ref",
|
|
593
|
+
"order": 0,
|
|
594
|
+
"required": false
|
|
595
|
+
}
|
|
596
|
+
],
|
|
589
597
|
"terminator": true
|
|
590
598
|
},
|
|
591
599
|
{
|
|
@@ -1,56 +1,56 @@
|
|
|
1
1
|
/** Generated by scripts/generate-public-catalog-projections.mjs. Do not edit. */
|
|
2
2
|
export const PUBLIC_PROJECTION_BUNDLE = {
|
|
3
3
|
"schemaVersion": "PublicProjectionBundle/1.0",
|
|
4
|
-
"generationId": "sha256:
|
|
5
|
-
"sourceHash": "
|
|
6
|
-
"catalogHash": "
|
|
7
|
-
"publicProjectionHash": "
|
|
8
|
-
"stageProjectionHash": "
|
|
9
|
-
"compilationHash": "
|
|
4
|
+
"generationId": "sha256:80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929",
|
|
5
|
+
"sourceHash": "80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929",
|
|
6
|
+
"catalogHash": "82186cef7dbd3771284389571d313287322bfd0dd80b2142e3f7efec0a0c6bbc",
|
|
7
|
+
"publicProjectionHash": "45d4fe4407d45ba322d375a6f57e357afdb17fe115bcf016d49811ad1bb4415f",
|
|
8
|
+
"stageProjectionHash": "fe971b5270073ad859e4e0ba5ab7bec635d17925922d279ad1d087c606f90fbc",
|
|
9
|
+
"compilationHash": "1f1ec555fe6cb23e9a9d33fa678030c09889469efc4826f372cd32b4d1319dfe",
|
|
10
10
|
"registry": {
|
|
11
11
|
"version": "1.0.0",
|
|
12
|
-
"checksum": "
|
|
12
|
+
"checksum": "a631fa17a24972952ef0270880105bfc4d1a6be17e8622c5916265d945fcb69d"
|
|
13
13
|
},
|
|
14
14
|
"artifacts": [
|
|
15
15
|
{
|
|
16
16
|
"kind": "catalog",
|
|
17
17
|
"schemaVersion": "ExecutableGraphCatalogPublicProjection/1.0",
|
|
18
18
|
"relativePath": "catalog.json",
|
|
19
|
-
"sha256": "
|
|
19
|
+
"sha256": "e6a9ec0c1ecc84572c44fe752efbc46b330dc3569c0b5cb317b62c36737d0875",
|
|
20
20
|
"entryCount": 97,
|
|
21
|
-
"generationId": "sha256:
|
|
21
|
+
"generationId": "sha256:80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"kind": "commands",
|
|
25
25
|
"schemaVersion": "CommandRegistryPublicProjection/1.0",
|
|
26
26
|
"relativePath": "commands.json",
|
|
27
|
-
"sha256": "
|
|
27
|
+
"sha256": "c7041309517f9c76f4a7b325660a630325d036ad3d6d8a20d90d27889cff3018",
|
|
28
28
|
"entryCount": 62,
|
|
29
|
-
"generationId": "sha256:
|
|
29
|
+
"generationId": "sha256:80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"kind": "progress-view",
|
|
33
33
|
"schemaVersion": "ProgressViewContractProjection/1.0",
|
|
34
34
|
"relativePath": "progress-view.json",
|
|
35
|
-
"sha256": "
|
|
35
|
+
"sha256": "ebe398e5f1cdb408e9a4cde551ed1448df0a673fc0bba456b04d72ff96d4bbdb",
|
|
36
36
|
"entryCount": 1,
|
|
37
|
-
"generationId": "sha256:
|
|
37
|
+
"generationId": "sha256:80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"kind": "stages",
|
|
41
41
|
"schemaVersion": "ExecutableGraphStageProjection/1.0",
|
|
42
42
|
"relativePath": "stages.json",
|
|
43
|
-
"sha256": "
|
|
43
|
+
"sha256": "7688c18fb51c1ae074620c550a22277cd990ac11ed702ebc1d863df319030909",
|
|
44
44
|
"entryCount": 85,
|
|
45
|
-
"generationId": "sha256:
|
|
45
|
+
"generationId": "sha256:80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"kind": "surfaces",
|
|
49
49
|
"schemaVersion": "PublicProjectionSurfaces/1.0",
|
|
50
50
|
"relativePath": "surfaces.json",
|
|
51
|
-
"sha256": "
|
|
51
|
+
"sha256": "796bd7507fd99b99cc899cc7b693c27bf698e4dc98473ceca3bf8cd13ee322cf",
|
|
52
52
|
"entryCount": 6,
|
|
53
|
-
"generationId": "sha256:
|
|
53
|
+
"generationId": "sha256:80e56629e3fbf9cb6be8dd1ea937f231eb187c5a02442217e647910183d52929"
|
|
54
54
|
}
|
|
55
55
|
],
|
|
56
56
|
"compatibility": {
|
|
@@ -143,7 +143,7 @@ export const PUBLIC_PROJECTION_BUNDLE = {
|
|
|
143
143
|
}
|
|
144
144
|
],
|
|
145
145
|
"publicSafe": true,
|
|
146
|
-
"bundleHash": "
|
|
146
|
+
"bundleHash": "5371013ed9041484849f722a19f96b106874efc0112db9c459311ae497229f56"
|
|
147
147
|
};
|
|
148
148
|
export const PUBLIC_PROJECTION_GENERATION_ID = PUBLIC_PROJECTION_BUNDLE.generationId;
|
|
149
149
|
export const PUBLIC_PROJECTION_BUNDLE_HASH = PUBLIC_PROJECTION_BUNDLE.bundleHash;
|
|
@@ -122,7 +122,7 @@ export interface InvocationCommandSchemaV1 {
|
|
|
122
122
|
readonly positionals: readonly InvocationPositionalSchemaV1[];
|
|
123
123
|
readonly terminator: true;
|
|
124
124
|
}
|
|
125
|
-
export declare const INVOCATION_SCHEMA_CHECKSUM: "
|
|
125
|
+
export declare const INVOCATION_SCHEMA_CHECKSUM: "5c1a3ec92913a1e9c84b61de15ccfa72d223556db9d3d9e40504f28e86a09a0e";
|
|
126
126
|
export interface InvocationSchemaManifestV1 {
|
|
127
127
|
readonly schemaVersion: typeof INVOCATION_SCHEMA_VERSION;
|
|
128
128
|
readonly registryVersion: string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
function record(value) {
|
|
3
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
4
|
+
? value : null;
|
|
5
|
+
}
|
|
6
|
+
function checkRunReason(check) {
|
|
7
|
+
if (check.status === 'QUEUED' || check.status === 'PENDING')
|
|
8
|
+
return 'remote_ci_queued_observed';
|
|
9
|
+
if (check.status === 'IN_PROGRESS' || check.status === 'WAITING')
|
|
10
|
+
return 'remote_ci_in_progress_observed';
|
|
11
|
+
if (check.status !== 'COMPLETED')
|
|
12
|
+
return null;
|
|
13
|
+
if (check.conclusion === 'SUCCESS')
|
|
14
|
+
return 'success';
|
|
15
|
+
if (check.conclusion === 'CANCELLED')
|
|
16
|
+
return 'remote_ci_cancelled_observed';
|
|
17
|
+
if (check.conclusion === 'TIMED_OUT')
|
|
18
|
+
return 'remote_ci_timeout_observed';
|
|
19
|
+
if (check.conclusion === 'STALE')
|
|
20
|
+
return 'remote_ci_stale_observed';
|
|
21
|
+
if (['FAILURE', 'ACTION_REQUIRED', 'NEUTRAL', 'SKIPPED', 'STARTUP_FAILURE'].includes(String(check.conclusion))) {
|
|
22
|
+
return 'remote_ci_failed_observed';
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function statusContextReason(check) {
|
|
27
|
+
if (check.state === 'SUCCESS')
|
|
28
|
+
return 'success';
|
|
29
|
+
if (check.state === 'EXPECTED')
|
|
30
|
+
return 'remote_ci_missing_observed';
|
|
31
|
+
if (check.state === 'PENDING')
|
|
32
|
+
return 'remote_ci_queued_observed';
|
|
33
|
+
if (check.state === 'FAILURE' || check.state === 'ERROR')
|
|
34
|
+
return 'remote_ci_failed_observed';
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/** Summarize only known categorical fields; never return check names, URLs,
|
|
38
|
+
* descriptions, raw nodes or arbitrary provider strings. Missing/empty rollups
|
|
39
|
+
* are observations requiring debt, not green checks. */
|
|
40
|
+
export function summarizeC12RemoteRollup(value) {
|
|
41
|
+
const reasons = [];
|
|
42
|
+
const categorical = [];
|
|
43
|
+
if (value === undefined || value === null) {
|
|
44
|
+
reasons.push('remote_status_check_rollup_absent_observed');
|
|
45
|
+
categorical.push('rollup:absent');
|
|
46
|
+
}
|
|
47
|
+
else if (!Array.isArray(value) || value.length > 100) {
|
|
48
|
+
return { ok: false, reasonCode: 'legacy-c12-remote-observation-unclassified' };
|
|
49
|
+
}
|
|
50
|
+
else if (value.length === 0) {
|
|
51
|
+
reasons.push('remote_status_check_rollup_empty_observed');
|
|
52
|
+
categorical.push('rollup:empty');
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
for (const raw of value) {
|
|
56
|
+
const check = record(raw);
|
|
57
|
+
if (!check)
|
|
58
|
+
return { ok: false, reasonCode: 'legacy-c12-remote-observation-unclassified' };
|
|
59
|
+
const reason = check.__typename === 'CheckRun' ? checkRunReason(check)
|
|
60
|
+
: check.__typename === 'StatusContext' ? statusContextReason(check) : null;
|
|
61
|
+
if (!reason)
|
|
62
|
+
return { ok: false, reasonCode: 'legacy-c12-remote-observation-unclassified' };
|
|
63
|
+
reasons.push(reason);
|
|
64
|
+
categorical.push(`${check.__typename}:${reason}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const reasonCodes = [...new Set(reasons.filter((item) => item !== 'success'))]
|
|
68
|
+
.sort();
|
|
69
|
+
const reasonCounts = reasonCodes.map((reasonCode) => Object.freeze({
|
|
70
|
+
reasonCode, count: reasons.filter((item) => item === reasonCode).length,
|
|
71
|
+
}));
|
|
72
|
+
const digest = createHash('sha256').update(JSON.stringify([
|
|
73
|
+
'legacy-c12-remote-observation-v1', categorical.sort(),
|
|
74
|
+
])).digest('hex');
|
|
75
|
+
return { ok: true, value: Object.freeze({ schemaVersion: 1,
|
|
76
|
+
kind: 'legacy-c12-remote-observation', source: 'github-pr-status-rollup',
|
|
77
|
+
checkCount: Array.isArray(value) ? value.length : 0,
|
|
78
|
+
reasonCodes: Object.freeze(reasonCodes), reasonCounts: Object.freeze(reasonCounts),
|
|
79
|
+
digest, publicSafe: true }) };
|
|
80
|
+
}
|