@noopolis/daimon 0.2.0 → 0.2.2
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/agentSkillsDirectory.d.ts +25 -0
- package/dist/runtime/agentSkillsDirectory.d.ts.map +1 -0
- package/dist/runtime/agentSkillsDirectory.js +101 -0
- 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 +15 -2
- package/dist/runtime/engineBrokerServiceConfig.d.ts.map +1 -1
- package/dist/runtime/engineBrokerServiceConfig.js +53 -11
- 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 +26 -2
- package/dist/runtime/grokBrokerProjection.d.ts.map +1 -1
- package/dist/runtime/grokBrokerProjection.js +18 -7
- 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 +12 -5
- package/dist/runtime/grokWorkerAttestation.d.ts.map +1 -1
- package/dist/runtime/grokWorkerAttestation.js +5 -3
- package/dist/runtime/grokWorkerHomeAttestation.d.ts +51 -5
- package/dist/runtime/grokWorkerHomeAttestation.d.ts.map +1 -1
- package/dist/runtime/grokWorkerHomeAttestation.js +103 -8
- package/dist/runtime/grokWorkerSkills.d.ts +86 -0
- package/dist/runtime/grokWorkerSkills.d.ts.map +1 -0
- package/dist/runtime/grokWorkerSkills.js +135 -0
- package/dist/runtime/index.d.ts +10 -2
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +8 -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,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "
|
|
1
|
+
import { spawn, readyTaskClockEnvironment } from "./taskClockProcess.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { constants } from "node:fs";
|
|
4
4
|
import { lstat, open, readdir, rename, unlink } from "node:fs/promises";
|
|
@@ -12,6 +12,7 @@ import { McpToolCallError, MCP_TOOL_RESULT_MAX_BYTES, renderMcpToolResult, repla
|
|
|
12
12
|
import { ensureRuntimeHomeDirectory } from "./runtimeHomeLayout.js";
|
|
13
13
|
import { capToolResult, resolveExemptToolNames, resolveToolResultMaxBytes, TOOL_OUTPUT_DIRECTORY_NAME } from "./toolResultSpill.js";
|
|
14
14
|
import { cliChildEnvironment } from "../pi/cliEnvironment.js";
|
|
15
|
+
import { taskClockChildEnvironment, taskClockProcessEnvironment, taskClockTimestamp } from "./taskClock.js";
|
|
15
16
|
const MAX_RESULT = 65_536;
|
|
16
17
|
const TIMEOUT = 10_000;
|
|
17
18
|
/**
|
|
@@ -25,6 +26,10 @@ const TIMEOUT = 10_000;
|
|
|
25
26
|
*/
|
|
26
27
|
const DAIMON_ACTION_ID_PREFIX = "daimon-";
|
|
27
28
|
export async function createProductionAgentTools(agent, wakeContext = {}) {
|
|
29
|
+
taskClockChildEnvironment();
|
|
30
|
+
for (const server of agent.mcp ?? [])
|
|
31
|
+
taskClockChildEnvironment(server.env);
|
|
32
|
+
readyTaskClockEnvironment(cliChildEnvironment([], agent.runtimeHomePath));
|
|
28
33
|
await ensureRuntimeHomeDirectory(agent.runtimeHomePath, "tool-state");
|
|
29
34
|
// Resolved once, at agent start: a malformed bound is a configuration error
|
|
30
35
|
// that should refuse the agent, not a surprise thrown from the middle of a
|
|
@@ -202,7 +207,8 @@ function moltnetTool(agent, wakeContext) {
|
|
|
202
207
|
const prior = await priorReceipt(agent, deliveryId);
|
|
203
208
|
if (prior !== undefined) {
|
|
204
209
|
wakeContext.spokeFor = wakeContext.current;
|
|
205
|
-
|
|
210
|
+
const visible = typeof prior.at === "string" ? { ...prior, at: taskClockTimestamp(prior.at) } : prior;
|
|
211
|
+
return { content: [{ type: "text", text: JSON.stringify(visible) }], details: visible };
|
|
206
212
|
}
|
|
207
213
|
const response = await machine(agent.moltnet.cliPath, agent.moltnet.configPath, input.network, { version: "moltnet.machine.v1", correlation_id: deliveryId, operation: "send_nudge", send_nudge: { delivery_id: deliveryId, target: { kind, id: target }, body: input.text } });
|
|
208
214
|
const result = moltnetOperationResult(response, "send_nudge", "send");
|
|
@@ -219,10 +225,11 @@ function moltnetTool(agent, wakeContext) {
|
|
|
219
225
|
};
|
|
220
226
|
}
|
|
221
227
|
async function connect(agent, server) {
|
|
222
|
-
|
|
228
|
+
taskClockChildEnvironment(server.env);
|
|
229
|
+
const client = new Client({ name: "daimon-production", version: "0.2.2" });
|
|
223
230
|
const headers = server.authSecretEnv === undefined ? {} : { authorization: `Bearer ${requiredSecret(server.authSecretEnv)}` };
|
|
224
231
|
const transport = server.transport === "stdio"
|
|
225
|
-
? new StdioClientTransport({ command: server.command, args: [...server.args], env: stringEnvironment({ ...server.env, ...cliChildEnvironment([], agent.runtimeHomePath, { executablePath: server.command }) }) })
|
|
232
|
+
? new StdioClientTransport({ command: server.command, args: [...server.args], env: stringEnvironment(readyTaskClockEnvironment({ ...server.env, ...cliChildEnvironment([], agent.runtimeHomePath, { executablePath: server.command }) })) })
|
|
226
233
|
: server.transport === "sse" ? new SSEClientTransport(new URL(server.url), { requestInit: { headers } })
|
|
227
234
|
: new StreamableHTTPClientTransport(new URL(server.url), { requestInit: { headers } });
|
|
228
235
|
await client.connect(transport);
|
|
@@ -286,8 +293,9 @@ finally {
|
|
|
286
293
|
* no longer cancel it.
|
|
287
294
|
*/
|
|
288
295
|
async function machine(cli, config, network, request) {
|
|
296
|
+
const clockEnvironment = taskClockChildEnvironment();
|
|
289
297
|
return await new Promise((resolve, reject) => {
|
|
290
|
-
const child = spawn(cli, ["machine", "--config", config, "--network", network], { stdio: ["pipe", "pipe", "pipe"] });
|
|
298
|
+
const child = spawn(cli, ["machine", "--config", config, "--network", network], { stdio: ["pipe", "pipe", "pipe"], env: taskClockProcessEnvironment(process.env, clockEnvironment) });
|
|
291
299
|
let output = "", error = "", settled = false;
|
|
292
300
|
const settle = (action) => { if (settled)
|
|
293
301
|
return; settled = true; clearTimeout(timer); action(); };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type OrganizationRuntimeConfig } from "./organizationRuntime.js";
|
|
2
|
+
export type RuntimeConfigLocation = Readonly<{
|
|
3
|
+
workspace: string;
|
|
4
|
+
home: string;
|
|
5
|
+
memory: string;
|
|
6
|
+
/** Both paths are explicit: runtime config declares no control-token file. */
|
|
7
|
+
controlToken: Readonly<{
|
|
8
|
+
from: string;
|
|
9
|
+
to: string;
|
|
10
|
+
}>;
|
|
11
|
+
}>;
|
|
12
|
+
/** Rebase one memory-backed agent's path prefixes, preserving instructions verbatim. */
|
|
13
|
+
export declare function relocateRuntimeConfig(config: OrganizationRuntimeConfig, location: RuntimeConfigLocation): OrganizationRuntimeConfig;
|
|
14
|
+
//# sourceMappingURL=relocateRuntimeConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relocateRuntimeConfig.d.ts","sourceRoot":"","sources":["../../src/runtime/relocateRuntimeConfig.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1G,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,YAAY,EAAE,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,yBAAyB,EAAE,QAAQ,EAAE,qBAAqB,GAAG,yBAAyB,CA8BnI"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import { parseOrganizationRuntimeConfig } from "./organizationRuntime.js";
|
|
3
|
+
/** Rebase one memory-backed agent's path prefixes, preserving instructions verbatim. */
|
|
4
|
+
export function relocateRuntimeConfig(config, location) {
|
|
5
|
+
const parsed = parseOrganizationRuntimeConfig(config);
|
|
6
|
+
const agent = parsed.agents[0];
|
|
7
|
+
if (parsed.agents.length !== 1 || !agent?.memory)
|
|
8
|
+
throw new TypeError("Relocation requires one memory-backed agent");
|
|
9
|
+
const mappings = [
|
|
10
|
+
[agent.workspacePath, location.workspace],
|
|
11
|
+
[agent.runtimeHomePath, location.home],
|
|
12
|
+
[agent.memory.runtimeHomePath, location.memory],
|
|
13
|
+
[location.controlToken.from, location.controlToken.to]
|
|
14
|
+
];
|
|
15
|
+
for (const pair of mappings)
|
|
16
|
+
for (const value of pair) {
|
|
17
|
+
if (typeof value !== "string" || !posix.isAbsolute(value) || posix.normalize(value) !== value || value === "/" || value.endsWith("/") || value.includes("\\") || value.includes("\0")) {
|
|
18
|
+
throw new TypeError("Relocation paths must be canonical absolute POSIX paths other than root");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Memory may live below home; the most specific source must own that subtree.
|
|
22
|
+
const ordered = [...mappings].sort(([a], [b]) => b.length - a.length);
|
|
23
|
+
if (new Set(ordered.map(([source]) => source)).size !== ordered.length)
|
|
24
|
+
throw new TypeError("Relocation source paths must be distinct");
|
|
25
|
+
const relocate = (value) => {
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
const mapping = ordered.find(([source]) => value === source || value.startsWith(`${source}/`));
|
|
28
|
+
return mapping ? mapping[1] + value.slice(mapping[0].length) : value;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return value.map(relocate);
|
|
32
|
+
if (value !== null && typeof value === "object") {
|
|
33
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, key === "instructions" ? item : relocate(item)]));
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
37
|
+
return parseOrganizationRuntimeConfig(relocate(parsed));
|
|
38
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/runtime/schedule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/runtime/schedule.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAK5G,eAAO,MAAM,kBAAkB,aAAgB,CAAC;AAKhD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACxH,KAAK,cAAc,GAAG,OAAO,CAAC,2BAA2B,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAGjF,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAE3C,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAAC;AACrI,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,SAAS,8BAA8B,EAAE,CAAC;IAClD,8EAA8E;IAC9E,MAAM,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACjE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;IAC5D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACpC,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,KAAK,IAAI,CAAC;CAC9F,CAAC,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CA8F/F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,mBAAmB,CAIxG;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAY1H;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAA8E;AAEnI,iHAAiH;AACjH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAKjG"}
|
package/dist/runtime/schedule.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertTaskInstant, validTaskInstant } from "./taskClock.js";
|
|
1
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
3
|
import { constants } from "node:fs";
|
|
3
4
|
import { lstat, open, readFile, rename, unlink } from "node:fs/promises";
|
|
@@ -11,7 +12,7 @@ const MAX_SEARCH_CHECKS = 1_000_000;
|
|
|
11
12
|
const CRON_FIELD_BOUNDS = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 7]];
|
|
12
13
|
/** Schedules create durable pending occurrences; engine execution remains elsewhere. */
|
|
13
14
|
export function createScheduleController(options) {
|
|
14
|
-
const now = options.now ?? Date.now;
|
|
15
|
+
const now = () => assertTaskInstant((options.now ?? Date.now)());
|
|
15
16
|
const random = options.random ?? Math.random;
|
|
16
17
|
const setTimer = options.setTimer ?? ((callback, delay) => setTimeout(callback, delay));
|
|
17
18
|
const clearTimer = options.clearTimer ?? clearTimeout;
|
|
@@ -131,6 +132,7 @@ export function createScheduleController(options) {
|
|
|
131
132
|
};
|
|
132
133
|
}
|
|
133
134
|
export function occurrenceFor(agentId, schedule, at) {
|
|
135
|
+
assertTaskInstant(at);
|
|
134
136
|
const local = schedule.kind === "cron" ? occurrenceForOffset(at, schedule.timezone) : new Date(at).toISOString();
|
|
135
137
|
return { agentId, occurredAt: new Date(at).toISOString(), prompt: schedule.prompt, deliveryId: `schedule:${identity(agentId, schedule)}:${local}` };
|
|
136
138
|
}
|
|
@@ -142,16 +144,19 @@ export function isCanonicalScheduleDeliveryId(value) {
|
|
|
142
144
|
|| /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}@(?:GMT(?:[+-]\d{2}:\d{2})?|UTC)$/u.test(occurrence);
|
|
143
145
|
}
|
|
144
146
|
export function nextOccurrence(agentId, schedule, anchor, from) {
|
|
147
|
+
assertTaskInstant(from);
|
|
148
|
+
if (anchor !== undefined)
|
|
149
|
+
assertTaskInstant(anchor);
|
|
145
150
|
if (schedule.kind === "every") {
|
|
146
151
|
const base = anchor ?? from;
|
|
147
|
-
return base > from ? base : base + (Math.floor((from - base) / schedule.interval_ms) + 1) * schedule.interval_ms;
|
|
152
|
+
return assertTaskInstant(base > from ? base : base + (Math.floor((from - base) / schedule.interval_ms) + 1) * schedule.interval_ms);
|
|
148
153
|
}
|
|
149
154
|
const fields = parseCron(schedule.cron);
|
|
150
155
|
if (!calendarPossible(fields))
|
|
151
156
|
throw new Error(`schedule ${agentId} is impossible`);
|
|
152
157
|
const found = searchCron(fields, schedule.timezone, from, from + SEARCH_HORIZON_MS, 1);
|
|
153
158
|
if (found !== undefined)
|
|
154
|
-
return found;
|
|
159
|
+
return assertTaskInstant(found);
|
|
155
160
|
throw new Error(`schedule ${agentId} has no occurrence within the deterministic search bound`);
|
|
156
161
|
}
|
|
157
162
|
export function cronIsPossible(cron) { try {
|
|
@@ -170,7 +175,7 @@ export function jitterOffsetMs(schedule, random) {
|
|
|
170
175
|
}
|
|
171
176
|
function withFireTarget(entry, schedule, random) {
|
|
172
177
|
const offset = jitterOffsetMs(schedule, random);
|
|
173
|
-
return offset === 0 ? entry : { ...entry, fire_at_ms: entry.next_due_ms + offset };
|
|
178
|
+
return offset === 0 ? entry : { ...entry, fire_at_ms: assertTaskInstant(entry.next_due_ms + offset) };
|
|
174
179
|
}
|
|
175
180
|
function withoutPending(entry) {
|
|
176
181
|
return { next_due_ms: entry.next_due_ms, ...(entry.fire_at_ms === undefined ? {} : { fire_at_ms: entry.fire_at_ms }) };
|
|
@@ -306,13 +311,13 @@ async function restore(file, allowed) {
|
|
|
306
311
|
const keys = Object.keys(entry).sort().join();
|
|
307
312
|
if (!["fire_at_ms,next_due_ms", "fire_at_ms,latest_pending,next_due_ms", "latest_pending,next_due_ms", "next_due_ms"].includes(keys))
|
|
308
313
|
throw new Error("schedule state is invalid");
|
|
309
|
-
if (!
|
|
314
|
+
if (!validTaskInstant(entry.next_due_ms))
|
|
310
315
|
throw new Error("schedule state is invalid");
|
|
311
316
|
const nextDue = entry.next_due_ms;
|
|
312
317
|
const fireAt = entry.fire_at_ms;
|
|
313
318
|
if (fireAt !== undefined) {
|
|
314
319
|
const maximumFireAt = nextDue + jitterOffsetMs(allowed.get(key).schedule, () => 0.999_999_999);
|
|
315
|
-
if (!
|
|
320
|
+
if (!validTaskInstant(fireAt) || fireAt < nextDue || fireAt > maximumFireAt)
|
|
316
321
|
throw new Error("schedule state is invalid");
|
|
317
322
|
}
|
|
318
323
|
const pending = entry.latest_pending === undefined ? undefined : parseOccurrence(entry.latest_pending);
|
|
@@ -338,7 +343,7 @@ function parseOccurrence(value) {
|
|
|
338
343
|
if (Object.keys(item).sort().join() !== "agentId,deliveryId,occurredAt,prompt" || Object.values(item).some((entry) => typeof entry !== "string" || Buffer.byteLength(entry, "utf8") > 16_384))
|
|
339
344
|
throw new Error("schedule state is invalid");
|
|
340
345
|
const occurredAt = item.occurredAt;
|
|
341
|
-
if (
|
|
346
|
+
if (!validTaskInstant(Date.parse(occurredAt)) || new Date(occurredAt).toISOString() !== occurredAt || !/^schedule:[a-f0-9]{64}:/u.test(item.deliveryId))
|
|
342
347
|
throw new Error("schedule state is invalid");
|
|
343
348
|
return item;
|
|
344
349
|
}
|
|
@@ -15,6 +15,16 @@ export type StoreHostRegistrationIdentity = StoreHostRegistration & Readonly<{
|
|
|
15
15
|
}>;
|
|
16
16
|
export declare function acquireHostRegistration(root: string, directory: Awaited<ReturnType<typeof open>>, identity: StoreProcessIdentity): Promise<StoreHostRegistration>;
|
|
17
17
|
export declare function releaseHostRegistration(root: string, directory: Awaited<ReturnType<typeof open>>, registration: StoreHostRegistration): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Remove `.host-online-*` markers left by hosts that are provably dead in this
|
|
20
|
+
* PID namespace. A host only ever removes its own marker on a clean close, so
|
|
21
|
+
* every crash, kill or container recreate left one behind (148 by 2026-10-05),
|
|
22
|
+
* and the markers count against the store's directory bound. The opening host,
|
|
23
|
+
* live owners, other namespaces and anything unreadable are left untouched.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pruneDeadHostRegistrations(root: string, directory: Awaited<ReturnType<typeof open>>, self: StoreHostRegistration, liveness: (owner: StoreHostRegistration & Readonly<{
|
|
26
|
+
generation: string;
|
|
27
|
+
}>) => Promise<boolean>): Promise<number>;
|
|
18
28
|
/** Admin callers hold their durable offline lease before calling this. */
|
|
19
29
|
export declare function listHostRegistrations(root: string): Promise<readonly StoreHostRegistrationIdentity[]>;
|
|
20
30
|
/** Returns undefined when any registration is live, unknown, or changed. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storeCoordination.d.ts","sourceRoot":"","sources":["../../src/runtime/storeCoordination.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,IAAI,EAA6B,MAAM,kBAAkB,CAAC;AAG1E,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,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;AAC3J,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC1F,MAAM,MAAM,6BAA6B,GAAG,qBAAqB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3G,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWvK;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3J;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,6BAA6B,EAAE,CAAC,CAA0O;AACtV,4EAA4E;AAC5E,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAgB/R;AAkBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAA6E"}
|
|
1
|
+
{"version":3,"file":"storeCoordination.d.ts","sourceRoot":"","sources":["../../src/runtime/storeCoordination.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,IAAI,EAA6B,MAAM,kBAAkB,CAAC;AAG1E,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,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;AAC3J,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC1F,MAAM,MAAM,6BAA6B,GAAG,qBAAqB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3G,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWvK;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3J;AAED;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,GAAG,QAAQ,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAWzP;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,6BAA6B,EAAE,CAAC,CAA0O;AACtV,4EAA4E;AAC5E,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAgB/R;AAkBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAA6E"}
|
|
@@ -22,6 +22,29 @@ export async function releaseHostRegistration(root, directory, registration) {
|
|
|
22
22
|
await unlink(target);
|
|
23
23
|
await directory.sync();
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Remove `.host-online-*` markers left by hosts that are provably dead in this
|
|
27
|
+
* PID namespace. A host only ever removes its own marker on a clean close, so
|
|
28
|
+
* every crash, kill or container recreate left one behind (148 by 2026-10-05),
|
|
29
|
+
* and the markers count against the store's directory bound. The opening host,
|
|
30
|
+
* live owners, other namespaces and anything unreadable are left untouched.
|
|
31
|
+
*/
|
|
32
|
+
export async function pruneDeadHostRegistrations(root, directory, self, liveness) {
|
|
33
|
+
let pruned = 0;
|
|
34
|
+
for (const entry of await readdir(root)) {
|
|
35
|
+
if (!/^\.host-online-[0-9a-f-]{36}\.json$/iu.test(entry) || entry === `.host-online-${self.owner_id}.json`)
|
|
36
|
+
continue;
|
|
37
|
+
const owner = await readRegistration(path.join(root, entry)).catch(() => undefined);
|
|
38
|
+
// This very process is alive by definition, whichever store in it registered the marker.
|
|
39
|
+
if (owner === undefined || !sameNamespace(owner, self) || (owner.pid === self.pid && owner.process_start === self.process_start && owner.boot_id === self.boot_id) || await liveness({ ...owner, generation: owner.owner_id }).catch(() => true))
|
|
40
|
+
continue;
|
|
41
|
+
await unlink(path.join(root, entry)).catch(() => undefined);
|
|
42
|
+
pruned += 1;
|
|
43
|
+
}
|
|
44
|
+
if (pruned > 0)
|
|
45
|
+
await directory.sync();
|
|
46
|
+
return pruned;
|
|
47
|
+
}
|
|
25
48
|
/** Admin callers hold their durable offline lease before calling this. */
|
|
26
49
|
export async function listHostRegistrations(root) { const result = []; for (const entry of await readdir(root)) {
|
|
27
50
|
if (/^\.host-online-[0-9a-f-]{36}\.json$/iu.test(entry))
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare const TASK_CLOCK_ENV = "NOOPOLIS_TASK_CLOCK";
|
|
2
|
+
export declare const TASK_CLOCK_VERSION = "noopolis.task-clock.v1";
|
|
3
|
+
export declare const TASK_CLOCK_MIN_MS = 0;
|
|
4
|
+
export declare const TASK_CLOCK_MAX_MS = 253402300799999;
|
|
5
|
+
export declare function validTaskInstant(value: unknown): value is number;
|
|
6
|
+
export declare function assertTaskInstant(value: number): number;
|
|
7
|
+
export type TaskClock = Readonly<{
|
|
8
|
+
raw: string;
|
|
9
|
+
origin: string;
|
|
10
|
+
anchorEpochMs: number;
|
|
11
|
+
/** Convert a real epoch timestamp without resetting the shared anchor. */
|
|
12
|
+
at(realEpochMs: number): number;
|
|
13
|
+
now(): number;
|
|
14
|
+
}>;
|
|
15
|
+
/** Environment-only contract: no clock callbacks or compiler config in the public API. */
|
|
16
|
+
export declare function readTaskClock(environment?: NodeJS.ProcessEnv): TaskClock | undefined;
|
|
17
|
+
export declare function parseTaskClock(raw: string | undefined): TaskClock | undefined;
|
|
18
|
+
/** The caller installs libfaketime; Daimon derives one process-independent offset. */
|
|
19
|
+
export declare function taskClockChildEnvironment(declared?: NodeJS.ProcessEnv, clock?: Readonly<{
|
|
20
|
+
raw: string;
|
|
21
|
+
origin: string;
|
|
22
|
+
anchorEpochMs: number;
|
|
23
|
+
/** Convert a real epoch timestamp without resetting the shared anchor. */
|
|
24
|
+
at(realEpochMs: number): number;
|
|
25
|
+
now(): number;
|
|
26
|
+
}> | undefined, environment?: NodeJS.ProcessEnv): Record<string, string>;
|
|
27
|
+
/** Inherited environments must not reintroduce another offset owner or loader controls. */
|
|
28
|
+
export declare function taskClockProcessEnvironment(inherited: NodeJS.ProcessEnv, derived?: Record<string, string>): NodeJS.ProcessEnv;
|
|
29
|
+
/** Provider/auth/control processes must never inherit the agent's process clock. */
|
|
30
|
+
export declare function realTimeProcessEnvironment(inherited: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
31
|
+
/** Reject mixed loader lists: unrelated libraries must never ride this exception. */
|
|
32
|
+
export declare function taskClockPreload(value: string | undefined): string | undefined;
|
|
33
|
+
/** Project real delivery/receipt timestamps, never historical dates inside tool payloads. */
|
|
34
|
+
export declare function taskClockTimestamp(realTimestamp: string, clock?: Readonly<{
|
|
35
|
+
raw: string;
|
|
36
|
+
origin: string;
|
|
37
|
+
anchorEpochMs: number;
|
|
38
|
+
/** Convert a real epoch timestamp without resetting the shared anchor. */
|
|
39
|
+
at(realEpochMs: number): number;
|
|
40
|
+
now(): number;
|
|
41
|
+
}> | undefined): string;
|
|
42
|
+
//# sourceMappingURL=taskClock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskClock.d.ts","sourceRoot":"","sources":["../../src/runtime/taskClock.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,wBAAwB,CAAC;AACpD,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,iBAAiB,kBAAsB,CAAC;AAErD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,GAAG,IAAI,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,GAAG,SAAS,CAEjG;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAc7E;AAED,sFAAsF;AACtF,wBAAgB,yBAAyB,CACvC,QAAQ,GAAE,MAAM,CAAC,UAAe,EAAE,KAAK;SA/BlC,MAAM;YACH,MAAM;mBACC,MAAM;IACrB,0EAA0E;oBAC1D,MAAM,GAAG,MAAM;WACxB,MAAM;cA0B4C,EAAE,WAAW,GAAE,MAAM,CAAC,UAAwB,GACtG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBxB;AAED,2FAA2F;AAC3F,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,yBAA8B,GAClE,MAAM,CAAC,UAAU,CAGnB;AAMD,oFAAoF;AACpF,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAG1F;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG9E;AAED,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK;SAhFxD,MAAM;YACH,MAAM;mBACC,MAAM;IACrB,0EAA0E;oBAC1D,MAAM,GAAG,MAAM;WACxB,MAAM;cA2EkE,GAAG,MAAM,CAEzF"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export const TASK_CLOCK_ENV = "NOOPOLIS_TASK_CLOCK";
|
|
2
|
+
export const TASK_CLOCK_VERSION = "noopolis.task-clock.v1";
|
|
3
|
+
export const TASK_CLOCK_MIN_MS = 0;
|
|
4
|
+
export const TASK_CLOCK_MAX_MS = 253_402_300_799_999;
|
|
5
|
+
export function validTaskInstant(value) {
|
|
6
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= TASK_CLOCK_MIN_MS && value <= TASK_CLOCK_MAX_MS;
|
|
7
|
+
}
|
|
8
|
+
export function assertTaskInstant(value) {
|
|
9
|
+
if (!validTaskInstant(value))
|
|
10
|
+
throw invalid("task instant must be within 1970-01-01T00:00:00.000Z .. 9999-12-31T23:59:59.999Z");
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
/** Environment-only contract: no clock callbacks or compiler config in the public API. */
|
|
14
|
+
export function readTaskClock(environment = process.env) {
|
|
15
|
+
return parseTaskClock(environment[TASK_CLOCK_ENV]);
|
|
16
|
+
}
|
|
17
|
+
export function parseTaskClock(raw) {
|
|
18
|
+
if (raw === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
let value;
|
|
21
|
+
try {
|
|
22
|
+
value = JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw invalid("must be valid JSON");
|
|
26
|
+
}
|
|
27
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
28
|
+
throw invalid("must be an object");
|
|
29
|
+
const fields = value;
|
|
30
|
+
if (Object.keys(fields).sort().join() !== "anchorEpochMs,origin,version")
|
|
31
|
+
throw invalid("requires exactly version, origin, anchorEpochMs (no unknown fields)");
|
|
32
|
+
if (fields.version !== TASK_CLOCK_VERSION)
|
|
33
|
+
throw invalid(`version must be ${TASK_CLOCK_VERSION}`);
|
|
34
|
+
if (typeof fields.origin !== "string" || !validInstant(fields.origin))
|
|
35
|
+
throw invalid("origin must be a valid ISO-8601 instant with Z or an explicit offset");
|
|
36
|
+
if (typeof fields.anchorEpochMs !== "number" || !Number.isSafeInteger(fields.anchorEpochMs))
|
|
37
|
+
throw invalid("anchorEpochMs must be a safe integer");
|
|
38
|
+
const origin = fields.origin, anchorEpochMs = fields.anchorEpochMs, originMs = Date.parse(origin);
|
|
39
|
+
assertTaskInstant(originMs);
|
|
40
|
+
const at = (realEpochMs) => assertTaskInstant(originMs + (realEpochMs - anchorEpochMs));
|
|
41
|
+
return Object.freeze({ raw, origin, anchorEpochMs, at, now: () => at(Date.now()) });
|
|
42
|
+
}
|
|
43
|
+
/** The caller installs libfaketime; Daimon derives one process-independent offset. */
|
|
44
|
+
export function taskClockChildEnvironment(declared = {}, clock = readTaskClock(), environment = process.env) {
|
|
45
|
+
if (clock === undefined)
|
|
46
|
+
return {};
|
|
47
|
+
for (const name of Object.keys(declared)) {
|
|
48
|
+
if (declared[name] !== undefined && clockVariable(name))
|
|
49
|
+
throw invalid(`declared child environment conflicts with ${name}`);
|
|
50
|
+
}
|
|
51
|
+
// Integer arithmetic keeps every millisecond even at the safe-integer anchor limits.
|
|
52
|
+
const offset = BigInt(Date.parse(clock.origin)) - BigInt(clock.anchorEpochMs);
|
|
53
|
+
const magnitude = offset < 0n ? -offset : offset;
|
|
54
|
+
const fraction = magnitude % 1000n;
|
|
55
|
+
const seconds = `${offset < 0n ? "-" : "+"}${magnitude / 1000n}${fraction === 0n ? "" : `.${String(fraction).padStart(3, "0")}`}`;
|
|
56
|
+
const preload = taskClockPreload(environment.LD_PRELOAD);
|
|
57
|
+
if (preload === undefined)
|
|
58
|
+
throw invalid("process clock startup probe requires caller-provided LD_PRELOAD naming libfaketime*.so*; refusing clocked execution");
|
|
59
|
+
return {
|
|
60
|
+
// The child process clock owns the offset; Mneme and nested consumers use Date.now().
|
|
61
|
+
// libfaketime's relative format defaults to seconds; an `s` suffix is not supported.
|
|
62
|
+
FAKETIME: seconds,
|
|
63
|
+
FAKETIME_DONT_FAKE_MONOTONIC: "1",
|
|
64
|
+
// libfaketime parses decimal fractions using the process locale.
|
|
65
|
+
LC_ALL: "C",
|
|
66
|
+
LD_PRELOAD: preload
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Inherited environments must not reintroduce another offset owner or loader controls. */
|
|
70
|
+
export function taskClockProcessEnvironment(inherited, derived = taskClockChildEnvironment()) {
|
|
71
|
+
if (Object.keys(derived).length === 0)
|
|
72
|
+
return inherited;
|
|
73
|
+
return { ...Object.fromEntries(Object.entries(inherited).filter(([name]) => !clockVariable(name))), ...derived };
|
|
74
|
+
}
|
|
75
|
+
function clockVariable(name) {
|
|
76
|
+
return /^(?:LD_PRELOAD$|DYLD_|FAKETIME|NOOPOLIS_TASK_CLOCK$|MNEME_CLOCK_)/u.test(name);
|
|
77
|
+
}
|
|
78
|
+
/** Provider/auth/control processes must never inherit the agent's process clock. */
|
|
79
|
+
export function realTimeProcessEnvironment(inherited) {
|
|
80
|
+
if (readTaskClock() === undefined)
|
|
81
|
+
return inherited;
|
|
82
|
+
return Object.fromEntries(Object.entries(inherited).filter(([name]) => !clockVariable(name)));
|
|
83
|
+
}
|
|
84
|
+
/** Reject mixed loader lists: unrelated libraries must never ride this exception. */
|
|
85
|
+
export function taskClockPreload(value) {
|
|
86
|
+
const libraries = value?.split(/[\s:]+/u).filter(Boolean);
|
|
87
|
+
return libraries?.length && libraries.every((library) => /^libfaketime[^/]*\.so[^/]*$/u.test(library.split("/").at(-1))) ? value : undefined;
|
|
88
|
+
}
|
|
89
|
+
/** Project real delivery/receipt timestamps, never historical dates inside tool payloads. */
|
|
90
|
+
export function taskClockTimestamp(realTimestamp, clock = readTaskClock()) {
|
|
91
|
+
return clock === undefined ? realTimestamp : new Date(clock.at(Date.parse(realTimestamp))).toISOString();
|
|
92
|
+
}
|
|
93
|
+
function invalid(reason) { return new Error(`${TASK_CLOCK_ENV}: ${reason}`); }
|
|
94
|
+
function validInstant(value) {
|
|
95
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):?[0-5]\d)$/u.exec(value);
|
|
96
|
+
if (match === null || !Number.isFinite(Date.parse(value)))
|
|
97
|
+
return false;
|
|
98
|
+
const year = Number(match[1]), month = Number(match[2]), day = Number(match[3]);
|
|
99
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
100
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
101
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= days[month - 1];
|
|
102
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import childProcess from "node:child_process";
|
|
2
|
+
import { type TaskClock } from "./taskClock.js";
|
|
3
|
+
export type { ChildProcess } from "node:child_process";
|
|
4
|
+
/** @internal Synchronous injection seam; never runtime configuration. */
|
|
5
|
+
export type TaskClockProcessProbe = (command: string, args: readonly string[], environment: NodeJS.ProcessEnv) => string;
|
|
6
|
+
/** @internal Isolate simulated probes between tests. */
|
|
7
|
+
export declare function resetTaskClockProcessForTest(): void;
|
|
8
|
+
/** Shared child-launch boundary, including SDK transports and Pi's spawn hook.
|
|
9
|
+
* Keep the synchronous spawn API: only the first launch blocks for the bounded probes.
|
|
10
|
+
* Every launch derives a fresh environment; only successful interposition is memoized.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readyTaskClockEnvironment(environment: NodeJS.ProcessEnv, clock?: TaskClock | undefined, probe?: TaskClockProcessProbe): NodeJS.ProcessEnv;
|
|
13
|
+
/** Only agent-facing tool/server children cross the shifted readiness gate. */
|
|
14
|
+
export declare const spawn: typeof childProcess.spawn;
|
|
15
|
+
/** Engine CLIs and operational helpers stay real; no libfaketime probe runs here. */
|
|
16
|
+
export declare const spawnRealTime: typeof childProcess.spawn;
|
|
17
|
+
//# sourceMappingURL=taskClockProcess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskClockProcess.d.ts","sourceRoot":"","sources":["../../src/runtime/taskClockProcess.ts"],"names":[],"mappings":"AAAA,OAAO,YAAmC,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAqG,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEnJ,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,yEAAyE;AACzE,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC;AAGzH,wDAAwD;AACxD,wBAAgB,4BAA4B,IAAI,IAAI,CAAsB;AAS1E;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,GAAE,SAAS,GAAG,SAA2B,EAAE,KAAK,GAAE,qBAAiC,GACvH,MAAM,CAAC,UAAU,CAyBnB;AAED,+EAA+E;AAC/E,eAAO,MAAM,KAAK,EAAE,OAAO,YAAY,CAAC,KAIT,CAAC;AAEhC,qFAAqF;AACrF,eAAO,MAAM,aAAa,EAAE,OAAO,YAAY,CAAC,KAIjB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import childProcess from "node:child_process";
|
|
2
|
+
import { readTaskClock, realTimeProcessEnvironment, taskClockChildEnvironment, taskClockProcessEnvironment } from "./taskClock.js";
|
|
3
|
+
const verified = new Set();
|
|
4
|
+
/** @internal Isolate simulated probes between tests. */
|
|
5
|
+
export function resetTaskClockProcessForTest() { verified.clear(); }
|
|
6
|
+
const dateProbe = (command, args, env) => {
|
|
7
|
+
const result = childProcess.spawnSync(command, [...args], { env, encoding: "utf8", timeout: 5000, killSignal: "SIGKILL", maxBuffer: 1024 });
|
|
8
|
+
// Loaders can ignore a missing library and still exit zero, including at zero offset.
|
|
9
|
+
if (result.error || result.status !== 0 || result.stderr?.trim())
|
|
10
|
+
throw result.error ?? new Error("date/libfaketime emitted a startup diagnostic");
|
|
11
|
+
return result.stdout;
|
|
12
|
+
};
|
|
13
|
+
/** Shared child-launch boundary, including SDK transports and Pi's spawn hook.
|
|
14
|
+
* Keep the synchronous spawn API: only the first launch blocks for the bounded probes.
|
|
15
|
+
* Every launch derives a fresh environment; only successful interposition is memoized.
|
|
16
|
+
*/
|
|
17
|
+
export function readyTaskClockEnvironment(environment, clock = readTaskClock(), probe = dateProbe) {
|
|
18
|
+
if (clock === undefined)
|
|
19
|
+
return environment;
|
|
20
|
+
const refuse = (reason) => new Error(`NOOPOLIS_TASK_CLOCK: process clock startup probe ${reason}; refusing clocked execution`);
|
|
21
|
+
if (process.platform !== "linux")
|
|
22
|
+
throw refuse("requires Linux libfaketime interposition");
|
|
23
|
+
const derived = taskClockChildEnvironment({}, clock);
|
|
24
|
+
const env = taskClockProcessEnvironment(environment, derived);
|
|
25
|
+
clock.now(); // A cached process clock never permits reads outside the supported calendar.
|
|
26
|
+
const key = JSON.stringify([process.pid, process.platform, derived.LD_PRELOAD, derived.FAKETIME]);
|
|
27
|
+
if (verified.has(key))
|
|
28
|
+
return env;
|
|
29
|
+
for (const sentinel of [true, false]) {
|
|
30
|
+
let output;
|
|
31
|
+
try {
|
|
32
|
+
// Absolute path: engine-specific PATHs need not contain system utilities.
|
|
33
|
+
output = probe("/bin/date", ["-u", "+%s"], sentinel ? { ...env, FAKETIME: "-31536000" } : env);
|
|
34
|
+
}
|
|
35
|
+
catch (cause) {
|
|
36
|
+
throw new Error(refuse("could not run date -u +%s (date/libfaketime missing or unusable)").message, { cause });
|
|
37
|
+
}
|
|
38
|
+
const seconds = Number(output.trim());
|
|
39
|
+
const expected = sentinel ? Date.now() - 31_536_000_000 : clock.now();
|
|
40
|
+
if (!/^-?\d+$/u.test(output.trim()) || !Number.isSafeInteger(seconds) || Math.abs(seconds * 1000 - expected) > 5000) {
|
|
41
|
+
throw refuse(`did not observe ${sentinel ? "sentinel offset" : "task time"} within ±5 seconds (check libfaketime and its offset)`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
verified.add(key);
|
|
45
|
+
return env;
|
|
46
|
+
}
|
|
47
|
+
/** Only agent-facing tool/server children cross the shifted readiness gate. */
|
|
48
|
+
export const spawn = ((command, args, options) => {
|
|
49
|
+
const argv = Array.isArray(args) ? args : [];
|
|
50
|
+
const settings = (Array.isArray(args) ? options : args ?? options);
|
|
51
|
+
return childProcess.spawn(command, argv, { ...settings, env: readyTaskClockEnvironment(settings?.env ?? process.env) });
|
|
52
|
+
});
|
|
53
|
+
/** Engine CLIs and operational helpers stay real; no libfaketime probe runs here. */
|
|
54
|
+
export const spawnRealTime = ((command, args, options) => {
|
|
55
|
+
const argv = Array.isArray(args) ? args : [];
|
|
56
|
+
const settings = (Array.isArray(args) ? options : args ?? options);
|
|
57
|
+
return childProcess.spawn(command, argv, { ...settings, env: realTimeProcessEnvironment(settings?.env ?? process.env) });
|
|
58
|
+
});
|
|
@@ -123,11 +123,6 @@ export type GrokTurnRequestEntry = Readonly<{
|
|
|
123
123
|
* field is not.
|
|
124
124
|
*/
|
|
125
125
|
export declare const renderGrokTurnRequestLines: (entry: GrokTurnRequestEntry) => string;
|
|
126
|
-
/**
|
|
127
|
-
* Append already-rendered, newline-terminated ledger lines in one write, with the
|
|
128
|
-
* same rotation and file mode as both ledgers. Advisory: never rejects. The
|
|
129
|
-
* broker uses it to append the exact bytes it sealed into a turn record.
|
|
130
|
-
*/
|
|
131
126
|
export declare const recordLedgerLines: (file: string, lines: string) => Promise<boolean>;
|
|
132
127
|
/** Advisory and never rejects, like {@link recordTurnRequests}; an empty turn writes nothing. */
|
|
133
128
|
export declare const recordGrokTurnRequests: (file: string, entry: GrokTurnRequestEntry) => Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turnRequestLedger.d.ts","sourceRoot":"","sources":["../../src/runtime/turnRequestLedger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"turnRequestLedger.d.ts","sourceRoot":"","sources":["../../src/runtime/turnRequestLedger.ts"],"names":[],"mappings":"AAIA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,2BAA2B,EAAG,kCAA2C,CAAC;AAEvF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,EAAG,kCAA2C,CAAC;AAExF,eAAO,MAAM,4BAA4B,GAAI,cAAa,MAAM,CAAC,UAAwB,KAAG,MAG3F,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAIH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,gBAAgB,KAAG,MAqBhE,CAAC;AASF,0FAA0F;AAC1F;;;;GAIG;AACH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AACvQ;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/M;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,oBAAoB,KAAG,MAuBxE,CAAC;AASF,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,OAAO,CAWpF,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,sBAAsB,GAAU,MAAM,MAAM,EAAE,OAAO,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAGvG,CAAC;AAoBF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,OAAO,gBAAgB,KAAG,OAAO,CAAC,OAAO,CAQ/F,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAAU,OAAO,QAAQ,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,KAAG,OAAO,CAAC,OAAO,CAiBnB,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { constants } from "node:fs";
|
|
2
3
|
import { open, rename, stat } from "node:fs/promises";
|
|
3
4
|
import { readCodexRolloutRequests } from "../pi/codexRolloutUsage.js";
|
|
@@ -138,16 +139,29 @@ export const renderGrokTurnRequestLines = (entry) => {
|
|
|
138
139
|
* same rotation and file mode as both ledgers. Advisory: never rejects. The
|
|
139
140
|
* broker uses it to append the exact bytes it sealed into a turn record.
|
|
140
141
|
*/
|
|
142
|
+
const ledgerWrites = new Map();
|
|
141
143
|
export const recordLedgerLines = async (file, lines) => {
|
|
142
144
|
if (lines.length === 0)
|
|
143
145
|
return false;
|
|
146
|
+
const key = path.resolve(file);
|
|
147
|
+
const previous = ledgerWrites.get(key) ?? Promise.resolve(true);
|
|
148
|
+
const pending = previous.then(async () => {
|
|
149
|
+
try {
|
|
150
|
+
await rotate(key);
|
|
151
|
+
await appendLines(key, lines);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
ledgerWrites.set(key, pending);
|
|
144
159
|
try {
|
|
145
|
-
await
|
|
146
|
-
await appendLines(file, lines);
|
|
147
|
-
return true;
|
|
160
|
+
return await pending;
|
|
148
161
|
}
|
|
149
|
-
|
|
150
|
-
|
|
162
|
+
finally {
|
|
163
|
+
if (ledgerWrites.get(key) === pending)
|
|
164
|
+
ledgerWrites.delete(key);
|
|
151
165
|
}
|
|
152
166
|
};
|
|
153
167
|
/** Advisory and never rejects, like {@link recordTurnRequests}; an empty turn writes nothing. */
|
|
@@ -155,9 +169,7 @@ export const recordGrokTurnRequests = async (file, entry) => {
|
|
|
155
169
|
if (entry.requests.length === 0)
|
|
156
170
|
return false;
|
|
157
171
|
try {
|
|
158
|
-
await
|
|
159
|
-
await appendLines(file, renderGrokTurnRequestLines(entry));
|
|
160
|
-
return true;
|
|
172
|
+
return await recordLedgerLines(file, renderGrokTurnRequestLines(entry));
|
|
161
173
|
}
|
|
162
174
|
catch {
|
|
163
175
|
return false;
|
|
@@ -174,16 +186,8 @@ const rotate = async (file) => {
|
|
|
174
186
|
if (size >= TURN_USAGE_ROTATE_BYTES)
|
|
175
187
|
await rename(file, `${file}.1`);
|
|
176
188
|
};
|
|
177
|
-
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* Turns for different agents run concurrently inside the single broker process.
|
|
181
|
-
* `O_APPEND` makes a single sub-page write atomic against them, so a wake's rows
|
|
182
|
-
* are contiguous and never interleaved with another wake's — which matters more
|
|
183
|
-
* here than in the per-wake ledger, because a row only means something beside
|
|
184
|
-
* the other rows of its own thread. A wake with enough requests to exceed a page
|
|
185
|
-
* is still append-ordered per line; interleaving would cost grouping, not
|
|
186
|
-
* correctness, since every row carries its own thread id.
|
|
189
|
+
/** Rotation and all writes are serialized per canonical path in this broker process.
|
|
190
|
+
* The realm lease excludes another broker. Independent paths remain concurrent.
|
|
187
191
|
*/
|
|
188
192
|
const appendLines = async (file, lines) => {
|
|
189
193
|
const bytes = Buffer.from(lines, "utf8");
|
|
@@ -209,9 +213,7 @@ export const recordTurnRequests = async (file, entry) => {
|
|
|
209
213
|
if (entry.requests.length === 0)
|
|
210
214
|
return false;
|
|
211
215
|
try {
|
|
212
|
-
await
|
|
213
|
-
await appendLines(file, renderTurnRequestLines(entry));
|
|
214
|
-
return true;
|
|
216
|
+
return await recordLedgerLines(file, renderTurnRequestLines(entry));
|
|
215
217
|
}
|
|
216
218
|
catch {
|
|
217
219
|
/* advisory: instrumentation never fails a wake that published */
|