@noopolis/daimon 0.2.0 → 0.2.1
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/README.md +6 -0
- package/dist/contracts/attentionContract.d.ts +25 -0
- package/dist/contracts/attentionContract.d.ts.map +1 -1
- package/dist/contracts/attentionContract.js +8 -1
- package/dist/contracts/grokBrokerTaskClock.d.ts +18 -0
- package/dist/contracts/grokBrokerTaskClock.d.ts.map +1 -0
- package/dist/contracts/grokBrokerTaskClock.js +16 -0
- package/dist/contracts/organizationRuntimeContract.d.ts +76 -0
- package/dist/contracts/organizationRuntimeContract.d.ts.map +1 -1
- package/dist/contracts/organizationRuntimeContract.js +2 -0
- package/dist/contracts/runtimeContractManifest.d.ts +222 -15
- package/dist/contracts/runtimeContractManifest.d.ts.map +1 -1
- package/dist/contracts/runtimeContractManifest.js +35 -10
- package/dist/contracts/shellEnvironmentPolicy.d.ts +3 -0
- package/dist/contracts/shellEnvironmentPolicy.d.ts.map +1 -0
- package/dist/contracts/shellEnvironmentPolicy.js +5 -0
- package/dist/contracts/wakeMomentContract.d.ts +61 -0
- package/dist/contracts/wakeMomentContract.d.ts.map +1 -0
- package/dist/contracts/wakeMomentContract.js +27 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/examples/jungianPlayAgent.d.ts +1 -0
- package/dist/examples/jungianPlayAgent.d.ts.map +1 -1
- package/dist/examples/jungianPlayAgent.js +6 -2
- package/dist/observability/causalEvents.d.ts +3 -3
- package/dist/observability/causalEvents.d.ts.map +1 -1
- package/dist/observability/causalEvents.js +4 -4
- package/dist/pi/cliEngineSpawn.d.ts +2 -2
- package/dist/pi/cliEngineSpawn.d.ts.map +1 -1
- package/dist/pi/cliEngineSpawn.js +7 -2
- package/dist/pi/cliEnvironment.d.ts.map +1 -1
- package/dist/pi/cliEnvironment.js +2 -0
- package/dist/pi/cliMcpRegistration.d.ts +1 -1
- package/dist/pi/cliMcpRegistration.d.ts.map +1 -1
- package/dist/pi/cliMcpRegistration.js +1 -1
- package/dist/pi/cliSession.d.ts +2 -0
- package/dist/pi/cliSession.d.ts.map +1 -1
- package/dist/pi/cliSession.js +3 -80
- package/dist/pi/cliSessionMcp.d.ts +9 -0
- package/dist/pi/cliSessionMcp.d.ts.map +1 -0
- package/dist/pi/cliSessionMcp.js +80 -0
- package/dist/pi/engineTaskClock.d.ts +9 -0
- package/dist/pi/engineTaskClock.d.ts.map +1 -0
- package/dist/pi/engineTaskClock.js +28 -0
- package/dist/pi/grokHomeMcpRegistration.d.ts.map +1 -1
- package/dist/pi/grokHomeMcpRegistration.js +2 -1
- package/dist/pi/grokSandbox.js +1 -1
- package/dist/pi/memoryClock.d.ts +25 -0
- package/dist/pi/memoryClock.d.ts.map +1 -0
- package/dist/pi/memoryClock.js +18 -0
- package/dist/pi/piAgentHandle.d.ts +3 -1
- package/dist/pi/piAgentHandle.d.ts.map +1 -1
- package/dist/pi/piAgentHandle.js +5 -2
- package/dist/pi/piHarness.d.ts +8 -2
- package/dist/pi/piHarness.d.ts.map +1 -1
- package/dist/pi/piHarness.js +28 -12
- package/dist/pi/taskClockProcess.d.ts +6 -0
- package/dist/pi/taskClockProcess.d.ts.map +1 -0
- package/dist/pi/taskClockProcess.js +8 -0
- package/dist/pi/turnCausal.d.ts +4 -2
- package/dist/pi/turnCausal.d.ts.map +1 -1
- package/dist/pi/turnCausal.js +4 -4
- package/dist/pi/wakeAcceptance.d.ts +1 -1
- package/dist/pi/wakeAcceptance.d.ts.map +1 -1
- package/dist/pi/wakeAcceptance.js +2 -2
- package/dist/runtime/agySubscriptionBootstrap.js +1 -1
- package/dist/runtime/agySubscriptionRealm.js +1 -1
- package/dist/runtime/attention.d.ts +5 -0
- package/dist/runtime/attention.d.ts.map +1 -1
- package/dist/runtime/attention.js +14 -1
- package/dist/runtime/attentionDispatcher.d.ts +14 -2
- package/dist/runtime/attentionDispatcher.d.ts.map +1 -1
- package/dist/runtime/attentionDispatcher.js +127 -33
- package/dist/runtime/cli.js +5 -0
- package/dist/runtime/contract-manifest.json +1 -1
- package/dist/runtime/contract-manifest.sha256 +1 -1
- package/dist/runtime/engineBrokerCapabilities.d.ts +10 -0
- package/dist/runtime/engineBrokerCapabilities.d.ts.map +1 -1
- package/dist/runtime/engineBrokerCapabilities.js +10 -0
- package/dist/runtime/engineBrokerControlClient.d.ts +12 -1
- package/dist/runtime/engineBrokerControlClient.d.ts.map +1 -1
- package/dist/runtime/engineBrokerControlClient.js +139 -43
- package/dist/runtime/engineBrokerCredentialJournal.d.ts +2 -0
- package/dist/runtime/engineBrokerCredentialJournal.d.ts.map +1 -1
- package/dist/runtime/engineBrokerCredentialJournal.js +9 -0
- package/dist/runtime/engineBrokerInferenceProtocol.d.ts +3 -2
- package/dist/runtime/engineBrokerInferenceProtocol.d.ts.map +1 -1
- package/dist/runtime/engineBrokerInferenceProtocol.js +4 -4
- package/dist/runtime/engineBrokerMcpCallLog.d.ts +11 -0
- package/dist/runtime/engineBrokerMcpCallLog.d.ts.map +1 -1
- package/dist/runtime/engineBrokerMcpCallLog.js +13 -1
- package/dist/runtime/engineBrokerMcpFacade.d.ts +5 -2
- package/dist/runtime/engineBrokerMcpFacade.d.ts.map +1 -1
- package/dist/runtime/engineBrokerMcpFacade.js +5 -2
- package/dist/runtime/engineBrokerNativeClient.js +2 -2
- package/dist/runtime/engineBrokerProtocol.d.ts +42 -10
- package/dist/runtime/engineBrokerProtocol.d.ts.map +1 -1
- package/dist/runtime/engineBrokerProtocol.js +97 -14
- package/dist/runtime/engineBrokerSealLedger.d.ts.map +1 -1
- package/dist/runtime/engineBrokerSealLedger.js +9 -0
- package/dist/runtime/engineBrokerService.d.ts +8 -2
- package/dist/runtime/engineBrokerService.d.ts.map +1 -1
- package/dist/runtime/engineBrokerService.js +95 -37
- package/dist/runtime/engineBrokerServiceCli.js +1 -1
- package/dist/runtime/engineBrokerServiceConfig.d.ts +5 -0
- package/dist/runtime/engineBrokerServiceConfig.d.ts.map +1 -1
- package/dist/runtime/engineBrokerServiceConfig.js +47 -9
- package/dist/runtime/engineBrokerTurnRegistry.d.ts +1 -1
- package/dist/runtime/engineBrokerTurnRegistry.d.ts.map +1 -1
- package/dist/runtime/engineBrokerTurnRegistry.js +7 -6
- package/dist/runtime/engineDispatcher.d.ts +2 -0
- package/dist/runtime/engineDispatcher.d.ts.map +1 -1
- package/dist/runtime/engineDispatcher.js +14 -2
- package/dist/runtime/engineReadiness.js +1 -1
- package/dist/runtime/grokBrokerCredentialAuthority.d.ts +1 -0
- package/dist/runtime/grokBrokerCredentialAuthority.d.ts.map +1 -1
- package/dist/runtime/grokBrokerCredentialAuthority.js +48 -22
- package/dist/runtime/grokBrokerCredentialReader.d.ts +1 -0
- package/dist/runtime/grokBrokerCredentialReader.d.ts.map +1 -1
- package/dist/runtime/grokBrokerCredentialReader.js +2 -1
- package/dist/runtime/grokBrokerProjection.d.ts +9 -1
- package/dist/runtime/grokBrokerProjection.d.ts.map +1 -1
- package/dist/runtime/grokBrokerProjection.js +11 -4
- package/dist/runtime/grokBrokerProviderClock.d.ts +26 -0
- package/dist/runtime/grokBrokerProviderClock.d.ts.map +1 -0
- package/dist/runtime/grokBrokerProviderClock.js +133 -0
- package/dist/runtime/grokBrokerProxy.d.ts +17 -3
- package/dist/runtime/grokBrokerProxy.d.ts.map +1 -1
- package/dist/runtime/grokBrokerProxy.js +73 -9
- package/dist/runtime/grokBrokerProxyJson.d.ts +10 -0
- package/dist/runtime/grokBrokerProxyJson.d.ts.map +1 -0
- package/dist/runtime/grokBrokerProxyJson.js +31 -0
- package/dist/runtime/grokBrokerProxyRequest.d.ts.map +1 -1
- package/dist/runtime/grokBrokerProxyRequest.js +17 -6
- package/dist/runtime/grokBrokerRealmLease.js +1 -1
- package/dist/runtime/grokBrokerRefresh.js +1 -1
- package/dist/runtime/grokBrokerRunBudget.d.ts +20 -0
- package/dist/runtime/grokBrokerRunBudget.d.ts.map +1 -0
- package/dist/runtime/grokBrokerRunBudget.js +85 -0
- package/dist/runtime/grokBrokerSlot.d.ts +6 -0
- package/dist/runtime/grokBrokerSlot.d.ts.map +1 -0
- package/dist/runtime/grokBrokerSlot.js +21 -0
- package/dist/runtime/grokBrokerTaskClock.d.ts +32 -0
- package/dist/runtime/grokBrokerTaskClock.d.ts.map +1 -0
- package/dist/runtime/grokBrokerTaskClock.js +120 -0
- package/dist/runtime/grokBrokerTurnMeter.d.ts +55 -3
- package/dist/runtime/grokBrokerTurnMeter.d.ts.map +1 -1
- package/dist/runtime/grokBrokerTurnMeter.js +90 -4
- package/dist/runtime/grokBrokerWorkerConfig.d.ts +5 -2
- package/dist/runtime/grokBrokerWorkerConfig.d.ts.map +1 -1
- package/dist/runtime/grokBrokerWorkerConfig.js +22 -3
- package/dist/runtime/grokEngineBroker.d.ts +43 -3
- package/dist/runtime/grokEngineBroker.d.ts.map +1 -1
- package/dist/runtime/grokEngineBroker.js +159 -71
- package/dist/runtime/grokEngineBrokerTurn.d.ts +32 -8
- package/dist/runtime/grokEngineBrokerTurn.d.ts.map +1 -1
- package/dist/runtime/grokEngineBrokerTurn.js +204 -21
- package/dist/runtime/grokEngineBrokerTurnEnd.d.ts +88 -0
- package/dist/runtime/grokEngineBrokerTurnEnd.d.ts.map +1 -0
- package/dist/runtime/grokEngineBrokerTurnEnd.js +148 -0
- package/dist/runtime/grokInferenceProxy.d.ts.map +1 -1
- package/dist/runtime/grokInferenceProxy.js +3 -0
- package/dist/runtime/grokSubscriptionRealm.js +1 -1
- package/dist/runtime/grokWorkerAttestation.d.ts +9 -4
- package/dist/runtime/grokWorkerAttestation.d.ts.map +1 -1
- package/dist/runtime/grokWorkerAttestation.js +2 -2
- package/dist/runtime/grokWorkerHomeAttestation.d.ts +4 -2
- package/dist/runtime/grokWorkerHomeAttestation.d.ts.map +1 -1
- package/dist/runtime/grokWorkerHomeAttestation.js +10 -4
- package/dist/runtime/index.d.ts +7 -2
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +5 -1
- package/dist/runtime/native/daimon-engine-broker +0 -0
- package/dist/runtime/organizationRuntime.d.ts +3 -0
- package/dist/runtime/organizationRuntime.d.ts.map +1 -1
- package/dist/runtime/organizationRuntimeAdmission.d.ts +6 -0
- package/dist/runtime/organizationRuntimeAdmission.d.ts.map +1 -0
- package/dist/runtime/organizationRuntimeAdmission.js +22 -0
- package/dist/runtime/organizationRuntimeControl.d.ts +9 -0
- package/dist/runtime/organizationRuntimeControl.d.ts.map +1 -1
- package/dist/runtime/organizationRuntimeControl.js +72 -24
- package/dist/runtime/organizationRuntimeHost.d.ts.map +1 -1
- package/dist/runtime/organizationRuntimeHost.js +13 -4
- package/dist/runtime/organizationRuntimeParsing.d.ts.map +1 -1
- package/dist/runtime/organizationRuntimeParsing.js +4 -1
- package/dist/runtime/organizationRuntimeReadiness.d.ts.map +1 -1
- package/dist/runtime/organizationRuntimeReadiness.js +8 -0
- package/dist/runtime/organizationRuntimeRecording.d.ts +7 -0
- package/dist/runtime/organizationRuntimeRecording.d.ts.map +1 -0
- package/dist/runtime/organizationRuntimeRecording.js +58 -0
- package/dist/runtime/physicalReadiness.d.ts +12 -0
- package/dist/runtime/physicalReadiness.d.ts.map +1 -1
- package/dist/runtime/physicalReadiness.js +43 -9
- package/dist/runtime/productionAgentTools.d.ts.map +1 -1
- package/dist/runtime/productionAgentTools.js +13 -5
- package/dist/runtime/relocateRuntimeConfig.d.ts +14 -0
- package/dist/runtime/relocateRuntimeConfig.d.ts.map +1 -0
- package/dist/runtime/relocateRuntimeConfig.js +38 -0
- package/dist/runtime/schedule.d.ts.map +1 -1
- package/dist/runtime/schedule.js +12 -7
- package/dist/runtime/storeCoordination.d.ts +10 -0
- package/dist/runtime/storeCoordination.d.ts.map +1 -1
- package/dist/runtime/storeCoordination.js +23 -0
- package/dist/runtime/taskClock.d.ts +42 -0
- package/dist/runtime/taskClock.d.ts.map +1 -0
- package/dist/runtime/taskClock.js +102 -0
- package/dist/runtime/taskClockProcess.d.ts +17 -0
- package/dist/runtime/taskClockProcess.d.ts.map +1 -0
- package/dist/runtime/taskClockProcess.js +58 -0
- package/dist/runtime/turnRequestLedger.d.ts +0 -5
- package/dist/runtime/turnRequestLedger.d.ts.map +1 -1
- package/dist/runtime/turnRequestLedger.js +23 -21
- package/dist/runtime/wakeAcceptanceClaimDiagnostics.d.ts +34 -0
- package/dist/runtime/wakeAcceptanceClaimDiagnostics.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceClaimDiagnostics.js +79 -0
- package/dist/runtime/wakeAcceptanceIndex.d.ts +55 -0
- package/dist/runtime/wakeAcceptanceIndex.d.ts.map +1 -0
- package/dist/runtime/wakeAcceptanceIndex.js +158 -0
- package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts +9 -1
- package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts.map +1 -1
- package/dist/runtime/wakeAcceptanceProcessIdentity.js +5 -1
- package/dist/runtime/wakeAcceptanceRecord.d.ts +2 -0
- package/dist/runtime/wakeAcceptanceRecord.d.ts.map +1 -1
- package/dist/runtime/wakeAcceptanceRecord.js +4 -2
- package/dist/runtime/wakeAcceptanceRetention.d.ts +25 -1
- package/dist/runtime/wakeAcceptanceRetention.d.ts.map +1 -1
- package/dist/runtime/wakeAcceptanceRetention.js +28 -2
- package/dist/runtime/wakeAcceptanceStore.d.ts +8 -8
- package/dist/runtime/wakeAcceptanceStore.d.ts.map +1 -1
- package/dist/runtime/wakeAcceptanceStore.js +66 -63
- package/dist/runtime/wakeFuse.d.ts +58 -3
- package/dist/runtime/wakeFuse.d.ts.map +1 -1
- package/dist/runtime/wakeFuse.js +106 -7
- package/dist/runtime/wakeMomentFs.d.ts +27 -0
- package/dist/runtime/wakeMomentFs.d.ts.map +1 -0
- package/dist/runtime/wakeMomentFs.js +223 -0
- package/dist/runtime/wakeMomentHistory.d.ts +26 -0
- package/dist/runtime/wakeMomentHistory.d.ts.map +1 -0
- package/dist/runtime/wakeMomentHistory.js +109 -0
- package/dist/runtime/wakeMomentIo.d.ts +22 -0
- package/dist/runtime/wakeMomentIo.d.ts.map +1 -0
- package/dist/runtime/wakeMomentIo.js +114 -0
- package/dist/runtime/wakeMomentRecorder.d.ts +33 -0
- package/dist/runtime/wakeMomentRecorder.d.ts.map +1 -0
- package/dist/runtime/wakeMomentRecorder.js +81 -0
- package/dist/runtime/wakeMomentRetention.d.ts +7 -0
- package/dist/runtime/wakeMomentRetention.d.ts.map +1 -0
- package/dist/runtime/wakeMomentRetention.js +143 -0
- package/dist/runtime/wakeMomentRows.d.ts +8 -0
- package/dist/runtime/wakeMomentRows.d.ts.map +1 -0
- package/dist/runtime/wakeMomentRows.js +66 -0
- package/dist/runtime/wakeMomentSnapshot.d.ts +56 -0
- package/dist/runtime/wakeMomentSnapshot.d.ts.map +1 -0
- package/dist/runtime/wakeMomentSnapshot.js +248 -0
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wakeAcceptanceProcessIdentity.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceProcessIdentity.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC3L,qBAAa,8BAA+B,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"wakeAcceptanceProcessIdentity.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceProcessIdentity.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC3L,KAAK,iBAAiB,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CAAC,CAAC;AAC/J,qBAAa,8BAA+B,SAAQ,KAAK;IAE3C,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB;IAD/C,QAAQ,CAAC,IAAI,EAAG,iCAAiC,CAAU;gBACtC,MAAM,CAAC,EAAE,iBAAiB,YAAA;CAChD;AACD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,GAAG,mBAAmB,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,mBAAmB,GAAG,mBAAmB,CAAC,GAAG,OAAO,CAAqH;AAC1S,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC,CAKvG;AACD,wBAAsB,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3E"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
export class WakeTransitionLockBlockedError extends Error {
|
|
3
|
+
detail;
|
|
3
4
|
code = "offline_reconciliation_required";
|
|
4
|
-
constructor() {
|
|
5
|
+
constructor(detail) {
|
|
6
|
+
super(`wake transition lock requires offline reconciliation${detail === undefined ? "" : `: step=${detail.step} assertion=transition_lock_namespace ${JSON.stringify(detail)}`}`);
|
|
7
|
+
this.detail = detail;
|
|
8
|
+
}
|
|
5
9
|
}
|
|
6
10
|
export function sameNamespace(left, right) { return left.pid_namespace_dev === right.pid_namespace_dev && left.pid_namespace_ino === right.pid_namespace_ino; }
|
|
7
11
|
export async function currentProcessIdentity() {
|
|
@@ -6,6 +6,8 @@ export type StoredWakeAcceptanceRecord = Readonly<{
|
|
|
6
6
|
request_digest: string;
|
|
7
7
|
event: OrganizationRuntimeWakeAcceptanceRequest["event"];
|
|
8
8
|
state: WakeReceiptState;
|
|
9
|
+
/** Set only through native scheduler acceptance; absent means a real delivery instant. */
|
|
10
|
+
native_schedule?: true;
|
|
9
11
|
accepted_at: string;
|
|
10
12
|
updated_at: string;
|
|
11
13
|
claim_generation?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wakeAcceptanceRecord.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceRecord.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,iCAAiC,EACtC,KAAK,wCAAwC,EAC7C,KAAK,oCAAoC,EACzC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAEtB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IACrF,KAAK,EAAE,wCAAwC,CAAC,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC;IAClF,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CAChL,CAAC,CAAC;AAEH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,0BAA0B,GAAG,iCAAiC,CAEtG;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,0BAA0B,GAAG,oCAAoC,CAErG;AACD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,
|
|
1
|
+
{"version":3,"file":"wakeAcceptanceRecord.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceRecord.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,iCAAiC,EACtC,KAAK,wCAAwC,EAC7C,KAAK,oCAAoC,EACzC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAEtB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IACrF,KAAK,EAAE,wCAAwC,CAAC,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC;IAClF,0FAA0F;IAC1F,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CAChL,CAAC,CAAC;AAEH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,0BAA0B,GAAG,iCAAiC,CAEtG;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,0BAA0B,GAAG,oCAAoC,CAErG;AACD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,CA2BpF;AAMD,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D"}
|
|
@@ -10,10 +10,12 @@ export function parseStoredWakeAcceptance(value) {
|
|
|
10
10
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
11
11
|
throw new Error("wake acceptance record is invalid");
|
|
12
12
|
const record = value;
|
|
13
|
-
const keys = ["acceptance_id", "agent_id", "delivery_id", "request_digest", "event", "state", "accepted_at", "updated_at", "claim_generation", "execution_id", "deferred", "execution_error", "code", "text"];
|
|
13
|
+
const keys = ["acceptance_id", "agent_id", "delivery_id", "request_digest", "event", "state", "accepted_at", "updated_at", "claim_generation", "execution_id", "deferred", "execution_error", "code", "text", "native_schedule"];
|
|
14
14
|
if (Object.keys(record).some((key) => !keys.includes(key)))
|
|
15
15
|
throw new Error("wake acceptance record is invalid");
|
|
16
16
|
const parsed = parseWakeAcceptanceRequest({ token: undefined, agent_id: string(record.agent_id), delivery_id: string(record.delivery_id), event: record.event });
|
|
17
|
+
if (record.native_schedule !== undefined && (record.native_schedule !== true || parsed.event.kind !== "schedule"))
|
|
18
|
+
throw new Error("wake acceptance schedule provenance is invalid");
|
|
17
19
|
const state = string(record.state);
|
|
18
20
|
if (!["accepted", "running", "completed", "failed", "stopped"].includes(state))
|
|
19
21
|
throw new Error("wake acceptance record is invalid");
|
|
@@ -42,7 +44,7 @@ export function parseStoredWakeAcceptance(value) {
|
|
|
42
44
|
throw new Error("wake acceptance record is invalid");
|
|
43
45
|
if (string(record.request_digest) !== wakeAcceptanceDigest(parsed) || !uuid(string(record.acceptance_id)))
|
|
44
46
|
throw new Error("wake acceptance record is invalid");
|
|
45
|
-
return { acceptance_id: string(record.acceptance_id), agent_id: parsed.agent_id, delivery_id: parsed.delivery_id, request_digest: string(record.request_digest), event: parsed.event, state, accepted_at: timestamp(record.accepted_at), updated_at: timestamp(record.updated_at), ...(claimGeneration === undefined ? {} : { claim_generation: claimGeneration }), ...(executionId === undefined ? {} : { execution_id: executionId }), ...(record.deferred === undefined ? {} : { deferred: record.deferred }), ...(code === undefined ? {} : { code }), ...(completionText === undefined ? {} : { text: completionText }), ...(executionError === undefined ? {} : { execution_error: executionError }) };
|
|
47
|
+
return { acceptance_id: string(record.acceptance_id), agent_id: parsed.agent_id, delivery_id: parsed.delivery_id, request_digest: string(record.request_digest), event: parsed.event, state, accepted_at: timestamp(record.accepted_at), updated_at: timestamp(record.updated_at), ...(record.native_schedule === true ? { native_schedule: true } : {}), ...(claimGeneration === undefined ? {} : { claim_generation: claimGeneration }), ...(executionId === undefined ? {} : { execution_id: executionId }), ...(record.deferred === undefined ? {} : { deferred: record.deferred }), ...(code === undefined ? {} : { code }), ...(completionText === undefined ? {} : { text: completionText }), ...(executionError === undefined ? {} : { execution_error: executionError }) };
|
|
46
48
|
}
|
|
47
49
|
function string(value) { if (typeof value !== "string")
|
|
48
50
|
throw new Error("wake acceptance record is invalid"); return value; }
|
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
export declare const TERMINAL_RECEIPT_IDEMPOTENCY_HORIZON = 2048;
|
|
2
2
|
export declare const WAKE_ACCEPTANCE_COMPACTION_THRESHOLD = 2112;
|
|
3
3
|
export declare const MAX_WAKE_ACCEPTANCE_RECORDS = 2176;
|
|
4
|
+
/**
|
|
5
|
+
* How long a delivery may sit in `accepted` before the store stops it.
|
|
6
|
+
*
|
|
7
|
+
* Inbox agents return every delivery they did not dispose of to `accepted`
|
|
8
|
+
* (deferred), and a failed execution keeps its batch for retry. Nothing ever
|
|
9
|
+
* ended either, so by 2026-10-05 213 deliveries up to two weeks old sat in the
|
|
10
|
+
* queue, were re-offered on every wake, and kept the store at its bound. A
|
|
11
|
+
* day is longer than any delivery stays actionable in a daily organization: at
|
|
12
|
+
* two days, deliveries from 2026-10-05 were still being retried — and failing —
|
|
13
|
+
* through 10-07's edition. The dispatcher stops an expired delivery
|
|
14
|
+
* `queued_wake_stopped` the first time it meets one and says so on stderr
|
|
15
|
+
* (`attentionDispatcher.ts`), rather than leaving it hidden until compaction.
|
|
16
|
+
*/
|
|
17
|
+
export declare const STALE_QUEUED_DELIVERY_MS: number;
|
|
4
18
|
export type RetentionCandidate = Readonly<{
|
|
5
19
|
file: string;
|
|
6
20
|
state: "accepted" | "running" | "completed" | "failed" | "stopped";
|
|
7
21
|
updatedAt: string;
|
|
22
|
+
acceptedAt?: string;
|
|
8
23
|
acceptanceId: string;
|
|
9
24
|
}>;
|
|
10
|
-
/**
|
|
25
|
+
/** Queued, unclaimed deliveries accepted before the staleness horizon. Running or claimed work never qualifies. */
|
|
26
|
+
export declare function staleQueuedFilesToStop(records: readonly (RetentionCandidate & Readonly<{
|
|
27
|
+
claimed: boolean;
|
|
28
|
+
}>)[], nowMs: number): readonly string[];
|
|
29
|
+
/**
|
|
30
|
+
* Active work is never eligible. The newest terminal receipts survive up to the
|
|
31
|
+
* idempotency horizon, but never so many that active work plus kept receipts
|
|
32
|
+
* still sits at the compaction threshold: a horizon that ignores active work
|
|
33
|
+
* lets parked deliveries hold the store at its bound with nothing to compact.
|
|
34
|
+
*/
|
|
11
35
|
export declare function terminalFilesToCompact(records: readonly RetentionCandidate[]): readonly string[];
|
|
12
36
|
//# sourceMappingURL=wakeAcceptanceRetention.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wakeAcceptanceRetention.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceRetention.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAC1D,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAC1D,eAAO,MAAM,2BAA2B,OAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"wakeAcceptanceRetention.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceRetention.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAC1D,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAC1D,eAAO,MAAM,2BAA2B,OAAQ,CAAC;AACjD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AAEzD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAAC;AAEH,mHAAmH;AACnH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,kBAAkB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAIlJ;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,MAAM,EAAE,CAOhG"}
|
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
export const TERMINAL_RECEIPT_IDEMPOTENCY_HORIZON = 2_048;
|
|
2
2
|
export const WAKE_ACCEPTANCE_COMPACTION_THRESHOLD = 2_112;
|
|
3
3
|
export const MAX_WAKE_ACCEPTANCE_RECORDS = 2_176;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* How long a delivery may sit in `accepted` before the store stops it.
|
|
6
|
+
*
|
|
7
|
+
* Inbox agents return every delivery they did not dispose of to `accepted`
|
|
8
|
+
* (deferred), and a failed execution keeps its batch for retry. Nothing ever
|
|
9
|
+
* ended either, so by 2026-10-05 213 deliveries up to two weeks old sat in the
|
|
10
|
+
* queue, were re-offered on every wake, and kept the store at its bound. A
|
|
11
|
+
* day is longer than any delivery stays actionable in a daily organization: at
|
|
12
|
+
* two days, deliveries from 2026-10-05 were still being retried — and failing —
|
|
13
|
+
* through 10-07's edition. The dispatcher stops an expired delivery
|
|
14
|
+
* `queued_wake_stopped` the first time it meets one and says so on stderr
|
|
15
|
+
* (`attentionDispatcher.ts`), rather than leaving it hidden until compaction.
|
|
16
|
+
*/
|
|
17
|
+
export const STALE_QUEUED_DELIVERY_MS = 24 * 60 * 60_000;
|
|
18
|
+
/** Queued, unclaimed deliveries accepted before the staleness horizon. Running or claimed work never qualifies. */
|
|
19
|
+
export function staleQueuedFilesToStop(records, nowMs) {
|
|
20
|
+
return records
|
|
21
|
+
.filter((record) => record.state === "accepted" && !record.claimed && record.acceptedAt !== undefined && Date.parse(record.acceptedAt) <= nowMs - STALE_QUEUED_DELIVERY_MS)
|
|
22
|
+
.map((record) => record.file);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Active work is never eligible. The newest terminal receipts survive up to the
|
|
26
|
+
* idempotency horizon, but never so many that active work plus kept receipts
|
|
27
|
+
* still sits at the compaction threshold: a horizon that ignores active work
|
|
28
|
+
* lets parked deliveries hold the store at its bound with nothing to compact.
|
|
29
|
+
*/
|
|
5
30
|
export function terminalFilesToCompact(records) {
|
|
6
31
|
if (records.length < WAKE_ACCEPTANCE_COMPACTION_THRESHOLD)
|
|
7
32
|
return [];
|
|
8
33
|
const terminal = records
|
|
9
34
|
.filter((record) => record.state === "completed" || record.state === "failed" || record.state === "stopped")
|
|
10
35
|
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt) || right.acceptanceId.localeCompare(left.acceptanceId));
|
|
11
|
-
|
|
36
|
+
const keep = Math.max(0, Math.min(TERMINAL_RECEIPT_IDEMPOTENCY_HORIZON, WAKE_ACCEPTANCE_COMPACTION_THRESHOLD - 1 - (records.length - terminal.length)));
|
|
37
|
+
return terminal.slice(keep).map((record) => record.file);
|
|
12
38
|
}
|
|
@@ -43,10 +43,12 @@ export declare class WakeAcceptanceStore {
|
|
|
43
43
|
private readonly ownerLiveness;
|
|
44
44
|
private readonly afterFinalLockAssertion?;
|
|
45
45
|
private mutations;
|
|
46
|
-
private readonly
|
|
46
|
+
private readonly index;
|
|
47
|
+
private onRecordRead?;
|
|
48
|
+
private claimTransitionLockTiming?;
|
|
47
49
|
private constructor();
|
|
48
50
|
static open(root: string, options?: WakeAcceptanceStoreTestOptions): Promise<WakeAcceptanceStore>;
|
|
49
|
-
accept(request: OrganizationRuntimeWakeAcceptanceRequest): Promise<{
|
|
51
|
+
accept(request: OrganizationRuntimeWakeAcceptanceRequest, nativeSchedule?: boolean): Promise<{
|
|
50
52
|
readonly record: Stored;
|
|
51
53
|
readonly created: boolean;
|
|
52
54
|
}>;
|
|
@@ -56,10 +58,10 @@ export declare class WakeAcceptanceStore {
|
|
|
56
58
|
/** Internal availability view; diagnostics never enter the public receipt wire. */
|
|
57
59
|
activityWithExecutionErrors(): Promise<readonly ActivityRow[]>;
|
|
58
60
|
private activityRows;
|
|
59
|
-
recoverable(agentIds: ReadonlySet<string
|
|
61
|
+
recoverable(agentIds: ReadonlySet<string>, onStale?: (record: Stored) => void): Promise<readonly Stored[]>;
|
|
60
62
|
transition(acceptanceId: string, state: WakeReceiptState, code?: WakeReceiptCode): Promise<Stored>;
|
|
61
63
|
/** Fuse race seam: never stops a record that was claimed after enumeration. */
|
|
62
|
-
transitionAcceptedToStopped(acceptanceId: string): Promise<Stored>;
|
|
64
|
+
transitionAcceptedToStopped(acceptanceId: string, code?: "host_stopping" | "queued_wake_stopped"): Promise<Stored>;
|
|
63
65
|
acquireClaim(acceptanceId: string, ownerId: string, acceptanceIds?: readonly string[], executionId?: string): Promise<WakeExecutionClaimResult>;
|
|
64
66
|
claimHeartbeatIntervalMs(): number;
|
|
65
67
|
renewClaim(acceptanceId: string, claim: WakeExecutionClaim): Promise<WakeExecutionClaim>;
|
|
@@ -99,14 +101,12 @@ export declare class WakeAcceptanceStore {
|
|
|
99
101
|
private verify;
|
|
100
102
|
}
|
|
101
103
|
export declare class WakeInboxFullError extends Error {
|
|
102
|
-
constructor();
|
|
104
|
+
constructor(message?: string);
|
|
103
105
|
}
|
|
104
106
|
export declare class WakeAcceptanceConflictError extends Error {
|
|
105
107
|
constructor();
|
|
106
108
|
}
|
|
107
|
-
export
|
|
108
|
-
constructor();
|
|
109
|
-
}
|
|
109
|
+
export { WakeExecutionClaimLostError, WakeTransitionLockContentionError } from "./wakeAcceptanceClaimDiagnostics.js";
|
|
110
110
|
/** A container boundary requires deployment-authorized offline reconciliation. */
|
|
111
111
|
export { WakeTransitionLockBlockedError } from "./wakeAcceptanceProcessIdentity.js";
|
|
112
112
|
export { publicAcceptance } from "./wakeAcceptanceRecord.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wakeAcceptanceStore.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wakeAcceptanceStore.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeAcceptanceStore.ts"],"names":[],"mappings":"AACA,OAAO,EAAyF,KAAK,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAKhK,OAAO,EAIL,KAAK,wCAAwC,EAC7C,KAAK,oCAAoC,EACzC,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAEtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAqF,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAK/J,KAAK,MAAM,GAAG,0BAA0B,CAAC;AACzC,KAAK,WAAW,GAAG,oCAAoC,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC3I,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC1L,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAEpL,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC,CAAA;CAAE,CAAC,CAAC;AAIvS,qFAAqF;AACrF,qBAAa,mBAAmB;IAKV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAU,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAoC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAqB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAAyB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAU,OAAO,CAAC,QAAQ,CAAC,GAAG;IAAgB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAmD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA8C,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IAJ5d,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,YAAY,CAAC,CAAyB;IAC9C,OAAO,CAAC,yBAAyB,CAAC,CAAmD;IACrF,OAAO;WAGM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,8BAAmC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAyB3G,MAAM,CAAC,OAAO,EAAE,wCAAwC,EAAE,cAAc,UAAQ,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;YAGpI,SAAS;IA6BjB,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oCAAoC,GAAG,SAAS,CAAC;IAIvF,QAAQ,IAAI,OAAO,CAAC,SAAS,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,CAAC;IAC1E,mFAAmF;IAC7E,2BAA2B,IAAI,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC;YACtD,YAAY;IAcpB,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC;IAchH,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAGlG,+EAA+E;IAC/E,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,GAAE,eAAe,GAAG,qBAAuC,GAAG,OAAO,CAAC,MAAM,CAAC;IAUnI,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IA8B/I,wBAAwB,IAAI,MAAM;IAClC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAaxF,+FAA+F;IAC/F,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,eAAe,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAkCnR,YAAY,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAS/B,aAAa;IAQrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAC5B,OAAO,CAAC,SAAS;YAKH,kBAAkB;YAKlB,mBAAmB;IAKjC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;YACD,qBAAqB;YAerB,oBAAoB;YAIpB,qBAAqB;IAKnC,OAAO,CAAC,OAAO;YAGD,KAAK;YAOL,UAAU;YACV,YAAY;YACZ,IAAI;YAQJ,iBAAiB;YACjB,SAAS;YAcT,gBAAgB;YAChB,QAAQ;YAWR,oBAAoB;YAMpB,sBAAsB;YAatB,QAAQ;YAIR,OAAO;YACP,YAAY;YAIZ,MAAM;CAMrB;AACD,qBAAa,kBAAmB,SAAQ,KAAK;gBAAe,OAAO,SAAuE;CAAuB;AACjK,qBAAa,2BAA4B,SAAQ,KAAK;;CAAoF;AAC1I,OAAO,EAAE,2BAA2B,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AACrH,kFAAkF;AAClF,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WakeExecutionClaimLostError, readExpectedClaim, waitForClaimTransitionLock } from "./wakeAcceptanceClaimDiagnostics.js";
|
|
1
2
|
import { sameNamespace, currentProcessIdentity, processIsAlive, WakeTransitionLockBlockedError } from "./wakeAcceptanceProcessIdentity.js";
|
|
2
3
|
import { constants } from "node:fs";
|
|
3
4
|
import { link, lstat, open, readFile, readdir, realpath, rename, unlink } from "node:fs/promises";
|
|
@@ -6,8 +7,9 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
6
7
|
import { MAX_WAKE_ACCEPTANCE_RECORD_BYTES, sanitizeWakeCompletionText, wakeAcceptanceDigest } from "./wakeAcceptanceTypes.js";
|
|
7
8
|
import { sanitizeExecutionError, parseStoredWakeAcceptance, publicStatus } from "./wakeAcceptanceRecord.js";
|
|
8
9
|
import { assertOfflineReconciliationLeaseAvailable } from "./wakeAcceptanceReconciliation.js";
|
|
9
|
-
import { acquireHostRegistration, releaseHostRegistration } from "./storeCoordination.js";
|
|
10
|
-
import {
|
|
10
|
+
import { acquireHostRegistration, pruneDeadHostRegistrations, releaseHostRegistration } from "./storeCoordination.js";
|
|
11
|
+
import { WakeAcceptanceIndex } from "./wakeAcceptanceIndex.js";
|
|
12
|
+
import { MAX_WAKE_ACCEPTANCE_RECORDS, WAKE_ACCEPTANCE_COMPACTION_THRESHOLD, staleQueuedFilesToStop, terminalFilesToCompact } from "./wakeAcceptanceRetention.js";
|
|
11
13
|
const DEFAULT_CLAIM_TTL_MS = 240_000;
|
|
12
14
|
/** Durable, private idempotency authority; callers must pre-create its 0700 root. */
|
|
13
15
|
export class WakeAcceptanceStore {
|
|
@@ -21,7 +23,9 @@ export class WakeAcceptanceStore {
|
|
|
21
23
|
ownerLiveness;
|
|
22
24
|
afterFinalLockAssertion;
|
|
23
25
|
mutations = Promise.resolve();
|
|
24
|
-
|
|
26
|
+
index;
|
|
27
|
+
onRecordRead;
|
|
28
|
+
claimTransitionLockTiming;
|
|
25
29
|
constructor(root, directory, identity, registration, claimTtlMs, now, owner, ownerLiveness, afterFinalLockAssertion) {
|
|
26
30
|
this.root = root;
|
|
27
31
|
this.directory = directory;
|
|
@@ -32,6 +36,8 @@ export class WakeAcceptanceStore {
|
|
|
32
36
|
this.owner = owner;
|
|
33
37
|
this.ownerLiveness = ownerLiveness;
|
|
34
38
|
this.afterFinalLockAssertion = afterFinalLockAssertion;
|
|
39
|
+
this.index = new WakeAcceptanceIndex(root, { directoryStat: async () => await this.directory.stat({ bigint: true }), files: async () => await this.files(), readId: async (file) => (await this.read(file)).acceptance_id, fileStat: async (file) => await lstat(file, { bigint: true }).catch((error) => { if (error.code === "ENOENT")
|
|
40
|
+
return undefined; throw error; }) }, () => BigInt(this.now()) * 1000000n);
|
|
35
41
|
}
|
|
36
42
|
static async open(root, options = {}) {
|
|
37
43
|
const claimTtlMs = options.claimTtlMs ?? DEFAULT_CLAIM_TTL_MS;
|
|
@@ -53,23 +59,27 @@ export class WakeAcceptanceStore {
|
|
|
53
59
|
if (!same(identity(before), identity(after)))
|
|
54
60
|
throw new Error("wake acceptance store changed during validation");
|
|
55
61
|
const registration = await acquireHostRegistration(real, directory, owner);
|
|
56
|
-
|
|
62
|
+
await pruneDeadHostRegistrations(real, directory, registration, options.ownerLiveness ?? processIsAlive);
|
|
63
|
+
const store = new WakeAcceptanceStore(real, directory, identity(before), registration, claimTtlMs, options.nowForTest ?? Date.now, owner, options.ownerLiveness ?? processIsAlive, options.afterFinalLockAssertion);
|
|
64
|
+
store.claimTransitionLockTiming = options.claimTransitionLockTimingForTest;
|
|
65
|
+
store.onRecordRead = options.onRecordReadForTest;
|
|
66
|
+
return store;
|
|
57
67
|
}
|
|
58
68
|
catch (error) {
|
|
59
69
|
await directory.close().catch(() => undefined);
|
|
60
70
|
throw error;
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
|
-
accept(request) {
|
|
64
|
-
return this.serialize(async () => await this.acceptNow(request));
|
|
73
|
+
accept(request, nativeSchedule = false) {
|
|
74
|
+
return this.serialize(async () => await this.acceptNow(request, nativeSchedule));
|
|
65
75
|
}
|
|
66
|
-
async acceptNow(request) {
|
|
76
|
+
async acceptNow(request, nativeSchedule) {
|
|
67
77
|
await this.verify();
|
|
68
78
|
const request_digest = wakeAcceptanceDigest(request);
|
|
69
79
|
const target = this.fileFor(request.agent_id, request.delivery_id);
|
|
70
80
|
const existing = await this.readOptional(target);
|
|
71
81
|
if (existing !== undefined) {
|
|
72
|
-
if (existing.request_digest !== request_digest)
|
|
82
|
+
if (existing.request_digest !== request_digest || existing.native_schedule !== (nativeSchedule ? true : undefined))
|
|
73
83
|
throw new WakeAcceptanceConflictError();
|
|
74
84
|
return { record: existing, created: false };
|
|
75
85
|
}
|
|
@@ -77,7 +87,7 @@ export class WakeAcceptanceStore {
|
|
|
77
87
|
if ((await this.files()).length >= MAX_WAKE_ACCEPTANCE_RECORDS)
|
|
78
88
|
throw new WakeInboxFullError();
|
|
79
89
|
const now = new Date().toISOString();
|
|
80
|
-
const record = { acceptance_id: randomUUID(), agent_id: request.agent_id, delivery_id: request.delivery_id, request_digest, event: request.event, state: "accepted", accepted_at: now, updated_at: now };
|
|
90
|
+
const record = { acceptance_id: randomUUID(), agent_id: request.agent_id, delivery_id: request.delivery_id, request_digest, event: request.event, state: "accepted", accepted_at: now, updated_at: now, ...(nativeSchedule ? { native_schedule: true } : {}) };
|
|
81
91
|
const temporary = path.join(this.root, `.pending-${randomUUID()}`);
|
|
82
92
|
try {
|
|
83
93
|
await this.writeNew(temporary, record);
|
|
@@ -88,12 +98,12 @@ export class WakeAcceptanceStore {
|
|
|
88
98
|
if (error.code !== "EEXIST")
|
|
89
99
|
throw error;
|
|
90
100
|
const winner = await this.read(target);
|
|
91
|
-
if (winner.request_digest !== request_digest)
|
|
101
|
+
if (winner.request_digest !== request_digest || winner.native_schedule !== (nativeSchedule ? true : undefined))
|
|
92
102
|
throw new WakeAcceptanceConflictError();
|
|
93
103
|
return { record: winner, created: false };
|
|
94
104
|
}
|
|
95
105
|
await this.directory.sync();
|
|
96
|
-
this.
|
|
106
|
+
this.index.set(record.acceptance_id, target);
|
|
97
107
|
return { record, created: true };
|
|
98
108
|
}
|
|
99
109
|
finally {
|
|
@@ -123,7 +133,7 @@ export class WakeAcceptanceStore {
|
|
|
123
133
|
...(includeExecutionErrors && record.execution_error !== undefined ? { execution_error: record.execution_error } : {}) };
|
|
124
134
|
});
|
|
125
135
|
}
|
|
126
|
-
async recoverable(agentIds) {
|
|
136
|
+
async recoverable(agentIds, onStale) {
|
|
127
137
|
const result = [];
|
|
128
138
|
for (const file of await this.files()) {
|
|
129
139
|
const record = await this.read(path.join(this.root, file));
|
|
@@ -131,7 +141,12 @@ export class WakeAcceptanceStore {
|
|
|
131
141
|
throw new Error("wake acceptance store contains an unknown agent authority");
|
|
132
142
|
if (record.state === "accepted" || record.state === "running") {
|
|
133
143
|
const claim = await this.readClaimOptional(this.claimFor(record));
|
|
134
|
-
|
|
144
|
+
// A long-parked delivery is no longer offered: it would head every batch ahead of today's mail. Compaction stops it.
|
|
145
|
+
if (staleQueuedFilesToStop([{ file, state: record.state, updatedAt: record.updated_at, acceptedAt: record.accepted_at, acceptanceId: record.acceptance_id, claimed: claim !== undefined && !expired(claim, this.now()) }], this.now()).length) {
|
|
146
|
+
onStale?.(record);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
result.push(!record.deferred && record.execution_id === undefined && record.execution_error === undefined && claim?.acceptance_ids?.includes(record.acceptance_id) && claim.execution_id !== undefined ? { ...record, execution_id: claim.execution_id } : record);
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
152
|
return result.sort((left, right) => left.accepted_at.localeCompare(right.accepted_at) || left.acceptance_id.localeCompare(right.acceptance_id));
|
|
@@ -140,13 +155,14 @@ export class WakeAcceptanceStore {
|
|
|
140
155
|
return this.serialize(async () => await this.transitionNow(acceptanceId, state, code));
|
|
141
156
|
}
|
|
142
157
|
/** Fuse race seam: never stops a record that was claimed after enumeration. */
|
|
143
|
-
transitionAcceptedToStopped(acceptanceId) {
|
|
158
|
+
transitionAcceptedToStopped(acceptanceId, code = "host_stopping") {
|
|
144
159
|
return this.serialize(async () => {
|
|
145
160
|
const target = await this.pathForAcceptanceId(acceptanceId);
|
|
146
161
|
const prior = await this.read(target);
|
|
147
|
-
|
|
162
|
+
const claim = prior.state === "accepted" ? await this.readClaimOptional(this.claimFor(prior)) : undefined;
|
|
163
|
+
if (prior.state !== "accepted" || (claim !== undefined && !expired(claim, this.now())))
|
|
148
164
|
return prior;
|
|
149
|
-
const record = { ...prior, state: "stopped", code
|
|
165
|
+
const record = { ...prior, state: "stopped", code, updated_at: new Date().toISOString() };
|
|
150
166
|
await this.replace(target, record);
|
|
151
167
|
return record;
|
|
152
168
|
});
|
|
@@ -216,13 +232,9 @@ export class WakeAcceptanceStore {
|
|
|
216
232
|
const record = await this.findByAcceptanceId(acceptanceId);
|
|
217
233
|
if (record === undefined)
|
|
218
234
|
throw new Error("wake acceptance receipt is unavailable");
|
|
219
|
-
const lock = await this.acquireTransitionLock(record, claim.owner_id);
|
|
220
|
-
if (lock === undefined)
|
|
221
|
-
throw new WakeExecutionClaimLostError();
|
|
235
|
+
const lock = await waitForClaimTransitionLock(() => this.acquireTransitionLock(record, claim.owner_id), () => this.readLockOptional(this.lockFor(record)), acceptanceId, claim, "renew", this.claimTransitionLockTiming);
|
|
222
236
|
try {
|
|
223
|
-
const current = await this.readClaim(this.claimFor(record));
|
|
224
|
-
if (!(current.acceptance_id === acceptanceId || current.acceptance_ids?.includes(acceptanceId)) || current.acceptance_id !== claim.acceptance_id || current.owner_id !== claim.owner_id || current.generation !== claim.generation || current.expires_at !== claim.expires_at || expired(current, this.now()))
|
|
225
|
-
throw new WakeExecutionClaimLostError();
|
|
237
|
+
const current = await readExpectedClaim(() => this.readClaim(this.claimFor(record)), acceptanceId, claim, "renew", this.now, lock);
|
|
226
238
|
const renewed = { ...current, expires_at: new Date(this.now() + this.claimTtlMs).toISOString() };
|
|
227
239
|
await this.replaceValue(this.claimFor(record), renewed);
|
|
228
240
|
return renewed;
|
|
@@ -240,24 +252,22 @@ export class WakeAcceptanceStore {
|
|
|
240
252
|
const initial = await this.findByAcceptanceId(acceptanceId);
|
|
241
253
|
if (initial === undefined)
|
|
242
254
|
throw new Error("wake acceptance receipt is unavailable");
|
|
243
|
-
const lock = await this.acquireTransitionLock(initial, claim.owner_id);
|
|
244
|
-
if (lock === undefined)
|
|
245
|
-
throw new WakeExecutionClaimLostError();
|
|
255
|
+
const lock = await waitForClaimTransitionLock(() => this.acquireTransitionLock(initial, claim.owner_id), () => this.readLockOptional(this.lockFor(initial)), acceptanceId, claim, `transition:${state}`, this.claimTransitionLockTiming);
|
|
246
256
|
try {
|
|
247
|
-
let record = await this.transitionNowClaimed(acceptanceId, claim);
|
|
257
|
+
let record = await this.transitionNowClaimed(acceptanceId, claim, `transition:${state}:initial`, lock);
|
|
248
258
|
if (isTerminal(record.state))
|
|
249
259
|
return record;
|
|
250
260
|
if (state !== "running" && record.claim_generation !== claim.generation)
|
|
251
|
-
throw new WakeExecutionClaimLostError();
|
|
252
|
-
await this.assertTransitionLock(record, lock);
|
|
253
|
-
record = await this.transitionNowClaimed(acceptanceId, claim);
|
|
261
|
+
throw new WakeExecutionClaimLostError({ step: `transition:${state}`, assertion: "record_claim_generation", acceptance_id: acceptanceId, expected: claim.generation, current: record.claim_generation ?? null, lock });
|
|
262
|
+
await this.assertTransitionLock(record, lock, `transition:${state}`);
|
|
263
|
+
record = await this.transitionNowClaimed(acceptanceId, claim, `transition:${state}:final`, lock);
|
|
254
264
|
if (isTerminal(record.state))
|
|
255
265
|
return record;
|
|
256
266
|
if (state !== "running" && record.claim_generation !== claim.generation)
|
|
257
|
-
throw new WakeExecutionClaimLostError();
|
|
258
|
-
await this.assertTransitionLock(record, lock);
|
|
267
|
+
throw new WakeExecutionClaimLostError({ step: `transition:${state}`, assertion: "record_claim_generation", acceptance_id: acceptanceId, expected: claim.generation, current: record.claim_generation ?? null, lock });
|
|
268
|
+
await this.assertTransitionLock(record, lock, `transition:${state}`);
|
|
259
269
|
await this.afterFinalLockAssertion?.();
|
|
260
|
-
await this.assertTransitionLock(record, lock);
|
|
270
|
+
await this.assertTransitionLock(record, lock, `transition:${state}:before_write`);
|
|
261
271
|
const target = this.fileFor(record.agent_id, record.delivery_id);
|
|
262
272
|
// The code explains the transition that produced the CURRENT state, so a
|
|
263
273
|
// transition that names none clears it. While codes existed only on terminal
|
|
@@ -285,14 +295,12 @@ export class WakeAcceptanceStore {
|
|
|
285
295
|
return this.serialize(async () => {
|
|
286
296
|
const record = await this.findByAcceptanceId(claim.acceptance_id);
|
|
287
297
|
if (!record)
|
|
288
|
-
throw new WakeExecutionClaimLostError();
|
|
289
|
-
const lock = await this.acquireTransitionLock(record, claim.owner_id);
|
|
290
|
-
if (!lock)
|
|
291
|
-
throw new WakeExecutionClaimLostError();
|
|
298
|
+
throw new WakeExecutionClaimLostError({ step: "release", assertion: "record_missing", acceptance_id: claim.acceptance_id, expected: claim, current: null });
|
|
299
|
+
const lock = await waitForClaimTransitionLock(() => this.acquireTransitionLock(record, claim.owner_id), () => this.readLockOptional(this.lockFor(record)), claim.acceptance_id, claim, "release", this.claimTransitionLockTiming);
|
|
292
300
|
try {
|
|
293
301
|
const current = await this.readClaimOptional(this.claimFor(record));
|
|
294
302
|
if (current?.generation !== claim.generation || current.owner_id !== claim.owner_id)
|
|
295
|
-
throw new WakeExecutionClaimLostError();
|
|
303
|
+
throw new WakeExecutionClaimLostError({ step: "release", assertion: "claim_identity", acceptance_id: claim.acceptance_id, expected: claim, current: current ?? null, lock });
|
|
296
304
|
await unlink(this.claimFor(record));
|
|
297
305
|
await this.directory.sync();
|
|
298
306
|
}
|
|
@@ -329,16 +337,8 @@ export class WakeAcceptanceStore {
|
|
|
329
337
|
async findByAcceptanceId(acceptanceId) {
|
|
330
338
|
if (!uuid(acceptanceId))
|
|
331
339
|
return undefined;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return await this.readOptional(known);
|
|
335
|
-
for (const file of await this.files()) {
|
|
336
|
-
const record = await this.read(path.join(this.root, file));
|
|
337
|
-
this.acceptanceFiles.set(record.acceptance_id, path.join(this.root, file));
|
|
338
|
-
if (record.acceptance_id === acceptanceId)
|
|
339
|
-
return record;
|
|
340
|
-
}
|
|
341
|
-
return undefined;
|
|
340
|
+
await this.verify();
|
|
341
|
+
return await this.index.find(acceptanceId, async (file) => await this.readOptional(file));
|
|
342
342
|
}
|
|
343
343
|
async pathForAcceptanceId(acceptanceId) {
|
|
344
344
|
const record = await this.findByAcceptanceId(acceptanceId);
|
|
@@ -352,7 +352,7 @@ export class WakeAcceptanceStore {
|
|
|
352
352
|
const target = this.lockFor(record);
|
|
353
353
|
const current = await this.readLockOptional(target);
|
|
354
354
|
if (current !== undefined && !sameNamespace(current, this.owner))
|
|
355
|
-
throw new WakeTransitionLockBlockedError();
|
|
355
|
+
throw new WakeTransitionLockBlockedError({ step: "acquire_transition_lock", acceptance_id: record.acceptance_id, expected: this.owner, current });
|
|
356
356
|
if (current !== undefined && await this.ownerLiveness(current))
|
|
357
357
|
return undefined;
|
|
358
358
|
if (current !== undefined) {
|
|
@@ -378,10 +378,10 @@ export class WakeAcceptanceStore {
|
|
|
378
378
|
await unlink(temporary).catch(() => undefined);
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
|
-
async assertTransitionLock(record, lock) {
|
|
382
|
-
const current = await this.readLock(this.lockFor(record)).catch(() => { throw new WakeExecutionClaimLostError(); });
|
|
381
|
+
async assertTransitionLock(record, lock, step) {
|
|
382
|
+
const current = await this.readLock(this.lockFor(record)).catch((cause) => { throw new WakeExecutionClaimLostError({ step, assertion: "transition_lock_read", acceptance_id: record.acceptance_id, expected: lock, current: null }, cause); });
|
|
383
383
|
if (current.owner_id !== lock.owner_id || current.generation !== lock.generation || current.pid !== lock.pid || current.process_start !== lock.process_start || current.boot_id !== lock.boot_id || !sameNamespace(current, lock))
|
|
384
|
-
throw new WakeExecutionClaimLostError();
|
|
384
|
+
throw new WakeExecutionClaimLostError({ step, assertion: "transition_lock_identity", acceptance_id: record.acceptance_id, expected: lock, current });
|
|
385
385
|
}
|
|
386
386
|
async releaseTransitionLock(record, lock) {
|
|
387
387
|
const target = this.lockFor(record);
|
|
@@ -399,7 +399,7 @@ export class WakeAcceptanceStore {
|
|
|
399
399
|
const entries = await readdir(this.root);
|
|
400
400
|
const files = entries.filter((entry) => /^[a-f0-9]{64}\.json$/.test(entry));
|
|
401
401
|
if (files.length > MAX_WAKE_ACCEPTANCE_RECORDS || entries.length > MAX_WAKE_ACCEPTANCE_RECORDS + 128)
|
|
402
|
-
throw new
|
|
402
|
+
throw new WakeInboxFullError("wake acceptance store exceeds its bounded record limit");
|
|
403
403
|
return files;
|
|
404
404
|
}
|
|
405
405
|
async claimFiles() { return (await readdir(this.root)).filter((entry) => /^[a-f0-9]{64}\.agent-claim$/.test(entry)); }
|
|
@@ -415,6 +415,7 @@ export class WakeAcceptanceStore {
|
|
|
415
415
|
const entry = await lstat(file);
|
|
416
416
|
if (!entry.isFile() || entry.isSymbolicLink() || entry.uid !== process.getuid?.() || (entry.mode & 0o777) !== 0o600 || entry.size > MAX_WAKE_ACCEPTANCE_RECORD_BYTES)
|
|
417
417
|
throw new Error("wake acceptance record is unsafe");
|
|
418
|
+
this.onRecordRead?.(file);
|
|
418
419
|
const bytes = await readFile(file);
|
|
419
420
|
if (bytes.length > MAX_WAKE_ACCEPTANCE_RECORD_BYTES)
|
|
420
421
|
throw new Error("wake acceptance record exceeds its bound");
|
|
@@ -471,25 +472,29 @@ export class WakeAcceptanceStore {
|
|
|
471
472
|
throw new Error("wake transition lock is invalid");
|
|
472
473
|
return result;
|
|
473
474
|
}
|
|
474
|
-
async transitionNowClaimed(acceptanceId, claim) {
|
|
475
|
+
async transitionNowClaimed(acceptanceId, claim, step, lock) {
|
|
475
476
|
const record = await this.findByAcceptanceId(acceptanceId);
|
|
476
477
|
if (record === undefined)
|
|
477
478
|
throw new Error("wake acceptance receipt is unavailable");
|
|
478
|
-
|
|
479
|
-
if (!(current.acceptance_id === acceptanceId || current.acceptance_ids?.includes(acceptanceId)) || current.acceptance_id !== claim.acceptance_id || current.owner_id !== claim.owner_id || current.generation !== claim.generation || current.expires_at !== claim.expires_at || expired(current, this.now()))
|
|
480
|
-
throw new WakeExecutionClaimLostError();
|
|
479
|
+
await readExpectedClaim(() => this.readClaim(this.claimFor(record)), acceptanceId, claim, step, this.now, lock);
|
|
481
480
|
return record;
|
|
482
481
|
}
|
|
483
482
|
async compactTerminalRecords() {
|
|
484
483
|
const files = await this.files();
|
|
485
|
-
if (files.length <
|
|
484
|
+
if (files.length < WAKE_ACCEPTANCE_COMPACTION_THRESHOLD)
|
|
486
485
|
return;
|
|
487
486
|
const records = await Promise.all(files.map(async (file) => ({ file, record: await this.read(path.join(this.root, file)) })));
|
|
487
|
+
const candidates = await Promise.all(records.map(async ({ file, record }) => ({ file, state: record.state, updatedAt: record.updated_at, acceptedAt: record.accepted_at, acceptanceId: record.acceptance_id, claimed: await this.readClaimOptional(this.claimFor(record)).then((claim) => claim !== undefined && !expired(claim, this.now())) })));
|
|
488
|
+
for (const file of staleQueuedFilesToStop(candidates, this.now())) {
|
|
489
|
+
const entry = records.find((candidate) => candidate.file === file);
|
|
490
|
+
entry.record = { ...entry.record, state: "stopped", code: "queued_wake_stopped", updated_at: new Date(this.now()).toISOString() };
|
|
491
|
+
await this.replace(path.join(this.root, file), entry.record);
|
|
492
|
+
}
|
|
488
493
|
for (const file of terminalFilesToCompact(records.map(({ file, record }) => ({ file, state: record.state, updatedAt: record.updated_at, acceptanceId: record.acceptance_id })))) {
|
|
489
494
|
const record = records.find((candidate) => candidate.file === file)?.record;
|
|
490
|
-
if (record !== undefined)
|
|
491
|
-
this.acceptanceFiles.delete(record.acceptance_id);
|
|
492
495
|
await unlink(path.join(this.root, file));
|
|
496
|
+
if (record !== undefined)
|
|
497
|
+
this.index.forget(path.join(this.root, file));
|
|
493
498
|
}
|
|
494
499
|
await this.directory.sync();
|
|
495
500
|
}
|
|
@@ -524,14 +529,12 @@ export class WakeAcceptanceStore {
|
|
|
524
529
|
}
|
|
525
530
|
}
|
|
526
531
|
export class WakeInboxFullError extends Error {
|
|
527
|
-
constructor(
|
|
532
|
+
constructor(message = "wake acceptance store has no capacity without deleting active work") { super(message); }
|
|
528
533
|
}
|
|
529
534
|
export class WakeAcceptanceConflictError extends Error {
|
|
530
535
|
constructor() { super("delivery id is already bound to a different request"); }
|
|
531
536
|
}
|
|
532
|
-
export
|
|
533
|
-
constructor() { super("wake execution claim was lost"); }
|
|
534
|
-
}
|
|
537
|
+
export { WakeExecutionClaimLostError, WakeTransitionLockContentionError } from "./wakeAcceptanceClaimDiagnostics.js";
|
|
535
538
|
/** A container boundary requires deployment-authorized offline reconciliation. */
|
|
536
539
|
export { WakeTransitionLockBlockedError } from "./wakeAcceptanceProcessIdentity.js";
|
|
537
540
|
export { publicAcceptance } from "./wakeAcceptanceRecord.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const WAKE_FUSE_VERSION: "noopolis.daimon.wake-fuse.v1";
|
|
2
2
|
export declare const WAKE_FUSE_DIRECTORY_ENV: "DAIMON_WAKE_FUSE_DIRECTORY";
|
|
3
|
+
export declare const WAKE_FUSE_EPOCH_ZONE_ENV: "DAIMON_WAKE_FUSE_EPOCH_ZONE";
|
|
3
4
|
export declare const DEFAULT_WAKE_FUSE_MAX_WAKES = 100;
|
|
4
5
|
export declare const DEFAULT_WAKE_FUSE_MAX_TOKENS = 5000000;
|
|
5
6
|
export type WakeFuseTripReason = "wake_ceiling" | "token_ceiling" | "operator_stop" | "ledger_unavailable";
|
|
@@ -30,6 +31,26 @@ export type WakeFuseVerdict = Readonly<{
|
|
|
30
31
|
state: "paused";
|
|
31
32
|
reason: "agent_execution_ceiling" | "agent_token_ceiling";
|
|
32
33
|
}>;
|
|
34
|
+
/**
|
|
35
|
+
* The counting window, when the operator has not named one.
|
|
36
|
+
*
|
|
37
|
+
* WHY THIS IS DERIVED AND NOT CONFIGURED
|
|
38
|
+
* --------------------------------------
|
|
39
|
+
* `DAIMON_WAKE_FUSE_EPOCH` is read from the container's env file, and an env
|
|
40
|
+
* file is applied at container CREATION only. So an operator who wants a daily
|
|
41
|
+
* budget has to rewrite that file and RECREATE THE CONTAINER every day, and
|
|
42
|
+
* the only thing that ever did that was a daily rebuild. The budget was
|
|
43
|
+
* therefore coupled to a deploy: stop deploying daily and the wake ceiling
|
|
44
|
+
* never resets, and the organization stops admitting wakes for good.
|
|
45
|
+
*
|
|
46
|
+
* A date-derived epoch removes the coupling instead of moving it. There is no
|
|
47
|
+
* file to rewrite, no deploy, no restart, and no clock for anybody to forget.
|
|
48
|
+
*
|
|
49
|
+
* An explicitly configured epoch still wins, unchanged: an operator pinning one
|
|
50
|
+
* window is doing it deliberately, and this must not roll it out from under
|
|
51
|
+
* them.
|
|
52
|
+
*/
|
|
53
|
+
export declare function derivedEpoch(organizationKey: string, at: Date, zone: string): string;
|
|
33
54
|
export type WakeFuseOptions = Readonly<{
|
|
34
55
|
organizationKey: string;
|
|
35
56
|
environment?: NodeJS.ProcessEnv;
|
|
@@ -45,23 +66,57 @@ export type WakeFuseOptions = Readonly<{
|
|
|
45
66
|
export declare class WakeFuse {
|
|
46
67
|
private readonly armed;
|
|
47
68
|
private readonly directory;
|
|
48
|
-
private
|
|
49
|
-
private
|
|
69
|
+
private epoch;
|
|
70
|
+
private epochStartedAt;
|
|
50
71
|
private readonly maxWakes;
|
|
51
72
|
private readonly maxTokens;
|
|
52
|
-
private
|
|
73
|
+
private admissions;
|
|
53
74
|
private readonly usageLedgerPath;
|
|
54
75
|
private readonly now;
|
|
76
|
+
private readonly nextEpoch;
|
|
55
77
|
private serial;
|
|
56
78
|
private reason;
|
|
57
79
|
private tripMarkerMissing;
|
|
58
80
|
private constructor();
|
|
59
81
|
static open(options: WakeFuseOptions): Promise<WakeFuse>;
|
|
60
82
|
admit(agentId: string, deliveryId: string, limits?: WakeBudgetLimits): Promise<WakeFuseVerdict>;
|
|
83
|
+
/**
|
|
84
|
+
* The budget as the caller should act on it NOW — which means after the day
|
|
85
|
+
* has been allowed to roll.
|
|
86
|
+
*
|
|
87
|
+
* This is the gate the scheduler actually consults: `AttentionDispatcher.drain()`
|
|
88
|
+
* returns at `snapshot().state !== "available"` before it ever reaches
|
|
89
|
+
* `admit()`. Rolling only inside `admitNow` therefore never fired for an
|
|
90
|
+
* organization that had hit its ceiling — it stayed paused across midnight
|
|
91
|
+
* and only a process restart could renew the budget. The roll is serialized
|
|
92
|
+
* with admissions so a snapshot and an admission never see two windows.
|
|
93
|
+
*/
|
|
61
94
|
snapshot(agentId: string, limits?: WakeBudgetLimits): Promise<WakeBudgetSnapshot>;
|
|
95
|
+
private snapshotNow;
|
|
62
96
|
pollOperatorStop(): Promise<WakeFuseTripReason | undefined>;
|
|
63
97
|
tripped(): WakeFuseTripReason | undefined;
|
|
64
98
|
close(): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Moves the counting window when the derived day has changed.
|
|
101
|
+
*
|
|
102
|
+
* Called at admission, which is the only moment the answer can matter: the
|
|
103
|
+
* organization runtime is a long-lived process, so an epoch computed once at
|
|
104
|
+
* open() would be the same epoch a week later.
|
|
105
|
+
*
|
|
106
|
+
* WHAT ROLLS AND WHAT DOES NOT
|
|
107
|
+
* ----------------------------
|
|
108
|
+
* A new window clears a CEILING trip, exactly as selecting a new epoch by
|
|
109
|
+
* hand always did: `readTripMarker` only honours a marker belonging to the
|
|
110
|
+
* current epoch. It does NOT clear an operator stop. `fuse.stop` is checked
|
|
111
|
+
* by existence and is deliberately not epoch-scoped, so a parked newsroom
|
|
112
|
+
* stays parked across midnight. That asymmetry is the whole point: a budget
|
|
113
|
+
* is a thing that renews, an operator stop is a decision that does not.
|
|
114
|
+
*
|
|
115
|
+
* Admissions are rebuilt from the ledger rather than emptied, so a clock that
|
|
116
|
+
* steps BACKWARD into a window that already spent wakes does not hand the
|
|
117
|
+
* organization a fresh budget.
|
|
118
|
+
*/
|
|
119
|
+
private rollEpochIfDue;
|
|
65
120
|
private admitNow;
|
|
66
121
|
private trip;
|
|
67
122
|
}
|