@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
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { lstat, open, readdir } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { AGENT_SKILLS_BOUNDS, parseAgentSkills } from "./grokWorkerSkills.js";
|
|
5
|
+
const invalid = (detail) => new TypeError(`invalid agent skills directory: ${detail}`);
|
|
6
|
+
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
7
|
+
/**
|
|
8
|
+
* Reads a skills tree — `<root>/<name>/SKILL.md` plus any supporting files —
|
|
9
|
+
* into the engine-neutral {@link AgentSkill} input, or `undefined` when `root`
|
|
10
|
+
* does not exist.
|
|
11
|
+
*
|
|
12
|
+
* It is the same reader for both ends of the Grok skills path: provisioning
|
|
13
|
+
* reads the compiled workspace's `.agents/skills` (the tree Codex reads in
|
|
14
|
+
* place), and the broker re-reads the rendered `$GROK_HOME/skills` before every
|
|
15
|
+
* turn under a root-owned, read-only policy. Symlinks, special files, hard
|
|
16
|
+
* links (read-only policy), non-UTF-8 content and anything past the
|
|
17
|
+
* {@link AGENT_SKILLS_BOUNDS} are refused rather than skipped, because a
|
|
18
|
+
* skipped entry would make the attested digest describe less than Grok loads.
|
|
19
|
+
*/
|
|
20
|
+
export async function readAgentSkillsDirectory(root, policy = {}) {
|
|
21
|
+
let top;
|
|
22
|
+
try {
|
|
23
|
+
top = await lstat(root);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error.code === "ENOENT")
|
|
27
|
+
return undefined;
|
|
28
|
+
throw invalid("unreadable root");
|
|
29
|
+
}
|
|
30
|
+
assertEntry(top, "directory", policy, ".");
|
|
31
|
+
const names = await boundedEntries(root, AGENT_SKILLS_BOUNDS.skills);
|
|
32
|
+
const skills = [];
|
|
33
|
+
let budget = AGENT_SKILLS_BOUNDS.totalBytes;
|
|
34
|
+
for (const name of names) {
|
|
35
|
+
const directory = path.join(root, name);
|
|
36
|
+
assertEntry(await lstat(directory), "directory", policy, name);
|
|
37
|
+
const files = [];
|
|
38
|
+
const walk = async (relative, depth) => {
|
|
39
|
+
for (const child of await boundedEntries(path.join(directory, relative), AGENT_SKILLS_BOUNDS.filesPerSkill)) {
|
|
40
|
+
const childRelative = relative === "" ? child : `${relative}/${child}`;
|
|
41
|
+
const absolute = path.join(directory, childRelative);
|
|
42
|
+
const where = `${name}/${childRelative}`;
|
|
43
|
+
const entry = await lstat(absolute);
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
if (depth + 1 >= AGENT_SKILLS_BOUNDS.depth)
|
|
46
|
+
throw invalid(`too deep at ${JSON.stringify(where)}`);
|
|
47
|
+
assertEntry(entry, "directory", policy, where);
|
|
48
|
+
await walk(childRelative, depth + 1);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
assertEntry(entry, "file", policy, where);
|
|
52
|
+
if (files.length >= AGENT_SKILLS_BOUNDS.filesPerSkill)
|
|
53
|
+
throw invalid(`too many files in ${JSON.stringify(name)}`);
|
|
54
|
+
const content = await readPinnedFile(absolute, where, entry, Math.min(AGENT_SKILLS_BOUNDS.fileBytes, budget));
|
|
55
|
+
budget -= Buffer.byteLength(content, "utf8");
|
|
56
|
+
files.push({ path: childRelative, content });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
await walk("", 0);
|
|
60
|
+
skills.push({ name, files });
|
|
61
|
+
}
|
|
62
|
+
return parseAgentSkills(skills);
|
|
63
|
+
}
|
|
64
|
+
/** `where` is the entry's path relative to the skills root, named in every refusal. */
|
|
65
|
+
function assertEntry(entry, kind, policy, where) {
|
|
66
|
+
if (entry.isSymbolicLink() || (kind === "directory" ? !entry.isDirectory() : !entry.isFile()))
|
|
67
|
+
throw invalid(`expected a plain ${kind} at ${JSON.stringify(where)}`);
|
|
68
|
+
if (policy.ownerUid !== undefined && entry.uid !== policy.ownerUid)
|
|
69
|
+
throw invalid(`unexpected owner of ${JSON.stringify(where)}`);
|
|
70
|
+
if (policy.readOnly === true && ((Number(entry.mode) & 0o7222) !== 0 || (kind === "file" && entry.nlink !== 1)))
|
|
71
|
+
throw invalid(`writable or linked entry ${JSON.stringify(where)}`);
|
|
72
|
+
}
|
|
73
|
+
async function boundedEntries(directory, limit) {
|
|
74
|
+
const entries = await readdir(directory);
|
|
75
|
+
if (entries.length > limit)
|
|
76
|
+
throw invalid(`too many entries in ${JSON.stringify(path.basename(directory))}`);
|
|
77
|
+
return entries.sort();
|
|
78
|
+
}
|
|
79
|
+
/** Opens without following links and requires the inode `lstat` saw, so a swap between the two cannot change the bytes read. */
|
|
80
|
+
async function readPinnedFile(file, where, seen, limit) {
|
|
81
|
+
if (seen.size > limit)
|
|
82
|
+
throw invalid(`file too large ${JSON.stringify(where)}`);
|
|
83
|
+
const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
84
|
+
try {
|
|
85
|
+
const opened = await handle.stat();
|
|
86
|
+
if (!opened.isFile() || opened.dev !== seen.dev || opened.ino !== seen.ino || opened.uid !== seen.uid || opened.mode !== seen.mode || opened.nlink !== seen.nlink || opened.size > limit)
|
|
87
|
+
throw invalid(`file changed while read ${JSON.stringify(where)}`);
|
|
88
|
+
const bytes = await handle.readFile();
|
|
89
|
+
if (bytes.length > limit)
|
|
90
|
+
throw invalid(`file too large ${JSON.stringify(where)}`);
|
|
91
|
+
try {
|
|
92
|
+
return decoder.decode(bytes);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw invalid(`file is not UTF-8 ${JSON.stringify(where)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
await handle.close().catch(() => undefined);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "
|
|
1
|
+
import { spawnRealTime as spawn } from "./taskClockProcess.js";
|
|
2
2
|
import { cliChildEnvironment } from "../pi/cliEnvironment.js";
|
|
3
3
|
import { startAgySubscriptionRealm } from "./agySubscriptionRealm.js";
|
|
4
4
|
import { prepareEngineExecutable, verifyAgySubscriptionEnrollment } from "./engineReadiness.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
|
-
import { spawn } from "
|
|
2
|
+
import { spawnRealTime as spawn } from "./taskClockProcess.js";
|
|
3
3
|
import { chmod, lstat, mkdir, mkdtemp, open, realpath, rm } from "node:fs/promises";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { StoredWakeAcceptanceRecord } from "./wakeAcceptanceRecord.js";
|
|
2
3
|
import type { AttentionConfig } from "../contracts/attentionContract.js";
|
|
3
4
|
export type { AttentionConfig } from "../contracts/attentionContract.js";
|
|
4
5
|
export type AttentionMessage = Readonly<{
|
|
@@ -10,12 +11,16 @@ export type AttentionMessage = Readonly<{
|
|
|
10
11
|
}>;
|
|
11
12
|
export type AttentionTurn = Readonly<{
|
|
12
13
|
executionId: string;
|
|
14
|
+
/** Projected once by the dispatcher; already in the agent's clock domain. */
|
|
13
15
|
messages: readonly AttentionMessage[];
|
|
14
16
|
budget(): Promise<unknown>;
|
|
15
17
|
disposition(deliveryId: string, disposition: "complete" | "defer"): Promise<void>;
|
|
16
18
|
}>;
|
|
17
19
|
/** Owned by one control host; never shared across hosts or agent identities. */
|
|
18
20
|
export type AttentionRegistry = Map<string, AttentionTurn>;
|
|
21
|
+
/** Project real delivery instants, preserving queue age and relative offsets.
|
|
22
|
+
* Only the private durable provenance exempts a native task-calendar occurrence. */
|
|
23
|
+
export declare function taskClockAttentionMessages(records: readonly Pick<StoredWakeAcceptanceRecord, "acceptance_id" | "delivery_id" | "event" | "native_schedule">[]): readonly AttentionMessage[];
|
|
19
24
|
export declare function attentionTools(agentId: string, registry: AttentionRegistry): ToolDefinition[];
|
|
20
25
|
export declare function parseAttention(value: unknown): AttentionConfig;
|
|
21
26
|
//# sourceMappingURL=attention.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attention.d.ts","sourceRoot":"","sources":["../../src/runtime/attention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACzI,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnF,CAAC,CAAC;AACH,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"attention.d.ts","sourceRoot":"","sources":["../../src/runtime/attention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACzI,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnF,CAAC,CAAC;AACH,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D;oFACoF;AACpF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,IAAI,CAAC,0BAA0B,EAAE,eAAe,GAAG,aAAa,GAAG,OAAO,GAAG,iBAAiB,CAAC,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAK3L;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,cAAc,EAAE,CAyB7F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAS9D"}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { readTaskClock, taskClockTimestamp } from "./taskClock.js";
|
|
2
|
+
/** Project real delivery instants, preserving queue age and relative offsets.
|
|
3
|
+
* Only the private durable provenance exempts a native task-calendar occurrence. */
|
|
4
|
+
export function taskClockAttentionMessages(records) {
|
|
5
|
+
const clock = readTaskClock();
|
|
6
|
+
return records.map((record) => ({ acceptance_id: record.acceptance_id, delivery_id: record.delivery_id, ...record.event,
|
|
7
|
+
occurred_at: record.native_schedule === true ? record.event.occurred_at : taskClockTimestamp(record.event.occurred_at, clock)
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
1
10
|
export function attentionTools(agentId, registry) {
|
|
2
11
|
const current = () => { const turn = registry.get(agentId); if (!turn)
|
|
3
12
|
throw new Error("No active inbox turn"); return turn; };
|
|
@@ -6,7 +15,11 @@ export function attentionTools(agentId, registry) {
|
|
|
6
15
|
parameters: { type: "object", additionalProperties: false, properties: {} },
|
|
7
16
|
async execute() {
|
|
8
17
|
const turn = current();
|
|
9
|
-
const
|
|
18
|
+
const budget = await turn.budget();
|
|
19
|
+
const clock = readTaskClock();
|
|
20
|
+
const visibleBudget = clock !== undefined && budget !== null && typeof budget === "object" && "epoch" in budget && typeof budget.epoch === "string"
|
|
21
|
+
? { ...budget, epoch: budget.epoch.replace(/^(organization-[a-f0-9]{64}-)\d{4}-\d{2}-\d{2}$/u, `$1${new Date(clock.now()).toISOString().slice(0, 10)}`) } : budget;
|
|
22
|
+
const details = { version: "noopolis.daimon.inbox.v1", execution_id: turn.executionId, messages: turn.messages, budget: visibleBudget };
|
|
10
23
|
return { content: [{ type: "text", text: JSON.stringify(details) }], details };
|
|
11
24
|
}
|
|
12
25
|
}, {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AttentionRegistry } from "./attention.js";
|
|
2
2
|
import type { OrganizationRuntimeAgentConfig, OrganizationRuntimeHost } from "./organizationRuntime.js";
|
|
3
3
|
import { WakeAcceptanceStore } from "./wakeAcceptanceStore.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type StoredWakeAcceptanceRecord } from "./wakeAcceptanceRecord.js";
|
|
5
5
|
import { WakeFuse } from "./wakeFuse.js";
|
|
6
|
+
import { type WakeMomentOptions } from "./wakeMomentRecorder.js";
|
|
6
7
|
type Options = Readonly<{
|
|
7
8
|
store: WakeAcceptanceStore;
|
|
8
9
|
host: OrganizationRuntimeHost;
|
|
@@ -11,6 +12,7 @@ type Options = Readonly<{
|
|
|
11
12
|
registry: AttentionRegistry;
|
|
12
13
|
token: string | undefined;
|
|
13
14
|
onIdle(agentId: string): void;
|
|
15
|
+
recordingOptionsForTest?: WakeMomentOptions;
|
|
14
16
|
}>;
|
|
15
17
|
/** Durable deliveries wait here; only a selected execution reserves a budget slot. */
|
|
16
18
|
export declare class AttentionDispatcher {
|
|
@@ -23,6 +25,10 @@ export declare class AttentionDispatcher {
|
|
|
23
25
|
private readonly active;
|
|
24
26
|
private readonly errors;
|
|
25
27
|
private stopping;
|
|
28
|
+
/** Reversible drain: no new execution starts; durable deliveries stay queued. */
|
|
29
|
+
private paused;
|
|
30
|
+
/** Agents whose inbox loop gave up its turn to a drain; resume owes each one a pass. */
|
|
31
|
+
private readonly pausedOut;
|
|
26
32
|
private fatal;
|
|
27
33
|
constructor(options: Options);
|
|
28
34
|
notify(agentId: string, fresh?: boolean): void;
|
|
@@ -37,6 +43,12 @@ export declare class AttentionDispatcher {
|
|
|
37
43
|
failure(agentId: string): string | undefined;
|
|
38
44
|
stop(): Promise<void>;
|
|
39
45
|
halt(): void;
|
|
46
|
+
/** Stops admitting executions; a running turn finishes and its records settle as usual. */
|
|
47
|
+
pause(): void;
|
|
48
|
+
/** Admits again and revisits every agent whose inbox moved while paused. */
|
|
49
|
+
resume(queued?: Iterable<string>): void;
|
|
50
|
+
/** True once no inbox loop is alive: nothing runs, and nothing can start until resume. */
|
|
51
|
+
quiescent(): boolean;
|
|
40
52
|
private drain;
|
|
41
53
|
private execute;
|
|
42
54
|
private failClosed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attentionDispatcher.d.ts","sourceRoot":"","sources":["../../src/runtime/attentionDispatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"attentionDispatcher.d.ts","sourceRoot":"","sources":["../../src/runtime/attentionDispatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,KAAK,EAAE,8BAA8B,EAAE,uBAAuB,EAAiC,MAAM,0BAA0B,CAAC;AAEvI,OAAO,EAAE,mBAAmB,EAAwD,MAAM,0BAA0B,CAAC;AACrH,OAAO,EAA0B,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEpG,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGnF,KAAK,OAAO,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,uBAAuB,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,8BAA8B,EAAE,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,uBAAuB,CAAC,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAAC;AAE9R,sFAAsF;AACtF,qBAAa,mBAAmB;IAclB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAbpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuE;IAC9F,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAS;IACzB,iFAAiF;IACjF,OAAO,CAAC,MAAM,CAAS;IACvB,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,KAAK,CAAmC;gBACnB,OAAO,EAAE,OAAO;IAE7C,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,IAAI;IAkB5C,WAAW,IAAI,oBAAoB,GAAG,SAAS;IAC/C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAC9B,gBAAgB;;sBA9B0C,MAAM;sBAAgB,MAAM,EAAE;;;IA+BxF,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IACtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,IAAI,IAAI,IAAI;IACZ,2FAA2F;IAC3F,KAAK,IAAI,IAAI;IACb,4EAA4E;IAC5E,MAAM,CAAC,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,IAAI;IAO3C,0FAA0F;IAC1F,SAAS,IAAI,OAAO;YAEN,KAAK;YA0CL,OAAO;IAmFrB,OAAO,CAAC,UAAU;YASJ,SAAS;CAOxB;AAkBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,0BAA0B,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,eAAe,EAAE,OAAO,GAAG,0BAA0B,EAAE,CAmBzK;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,MAAM,EAAE,8BAA8B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,SAAQ,GAAG,MAAM,CAsB5I"}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { taskClockAttentionMessages } from "./attention.js";
|
|
2
3
|
import { engineFailureDetail } from "./organizationRuntimeHost.js";
|
|
3
4
|
import { WakeExecutionClaimLostError } from "./wakeAcceptanceStore.js";
|
|
5
|
+
import { sanitizeExecutionError } from "./wakeAcceptanceRecord.js";
|
|
6
|
+
import { STALE_QUEUED_DELIVERY_MS } from "./wakeAcceptanceRetention.js";
|
|
4
7
|
import { ORGANIZATION_RUNTIME_MAX_STRING_CODEPOINTS, ORGANIZATION_RUNTIME_MAX_WAKE_TEXT_BYTES } from "../contracts/organizationRuntimeContract.js";
|
|
5
8
|
import { grokDaimonToolName } from "../contracts/grokWorkerContract.js";
|
|
9
|
+
import { verifyHostRecordingStore, wakeFromDurableInbox } from "./organizationRuntimeAdmission.js";
|
|
10
|
+
import { recordWakeMoment } from "./wakeMomentRecorder.js";
|
|
6
11
|
/** Durable deliveries wait here; only a selected execution reserves a budget slot. */
|
|
7
12
|
export class AttentionDispatcher {
|
|
8
13
|
options;
|
|
@@ -14,6 +19,10 @@ export class AttentionDispatcher {
|
|
|
14
19
|
active = new Map();
|
|
15
20
|
errors = new Map();
|
|
16
21
|
stopping = false;
|
|
22
|
+
/** Reversible drain: no new execution starts; durable deliveries stay queued. */
|
|
23
|
+
paused = false;
|
|
24
|
+
/** Agents whose inbox loop gave up its turn to a drain; resume owes each one a pass. */
|
|
25
|
+
pausedOut = new Set();
|
|
17
26
|
fatal;
|
|
18
27
|
constructor(options) {
|
|
19
28
|
this.options = options;
|
|
@@ -21,11 +30,22 @@ export class AttentionDispatcher {
|
|
|
21
30
|
notify(agentId, fresh = false) {
|
|
22
31
|
if (fresh)
|
|
23
32
|
this.generations.set(agentId, (this.generations.get(agentId) ?? 0) + 1);
|
|
24
|
-
if (this.stopping
|
|
33
|
+
if (this.stopping)
|
|
34
|
+
return;
|
|
35
|
+
// Every pass a drain withholds is owed back on resume.
|
|
36
|
+
if (this.paused) {
|
|
37
|
+
this.pausedOut.add(agentId);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (this.work.has(agentId))
|
|
25
41
|
return;
|
|
26
|
-
|
|
42
|
+
// An owed pass is not new input: it starts from the generation already seen, so
|
|
43
|
+
// deferred deliveries stay deferred unless something really arrived meanwhile.
|
|
44
|
+
const baseline = this.pausedOut.delete(agentId) ? this.observed.get(agentId) ?? 0 : 0;
|
|
45
|
+
const task = this.drain(agentId, baseline).catch((error) => { this.observed.set(agentId, this.generations.get(agentId) ?? 0); this.errors.set(agentId, engineFailureDetail(error) ?? "inbox_storage_unavailable"); }).finally(() => {
|
|
27
46
|
this.work.delete(agentId);
|
|
28
|
-
|
|
47
|
+
// A resume that landed while this loop was still unwinding its paused exit found it busy; run the owed pass now.
|
|
48
|
+
if (!this.stopping && ((this.generations.get(agentId) ?? 0) > (this.observed.get(agentId) ?? 0) || this.pausedOut.has(agentId)))
|
|
29
49
|
this.notify(agentId);
|
|
30
50
|
this.options.onIdle(agentId);
|
|
31
51
|
});
|
|
@@ -44,12 +64,36 @@ export class AttentionDispatcher {
|
|
|
44
64
|
}
|
|
45
65
|
halt() { this.stopping = true; for (const finish of this.waiters)
|
|
46
66
|
finish(); }
|
|
47
|
-
|
|
67
|
+
/** Stops admitting executions; a running turn finishes and its records settle as usual. */
|
|
68
|
+
pause() { this.paused = true; for (const finish of this.waiters)
|
|
69
|
+
finish(); }
|
|
70
|
+
/** Admits again and revisits every agent whose inbox moved while paused. */
|
|
71
|
+
resume(queued = []) {
|
|
72
|
+
this.paused = false;
|
|
73
|
+
// Agents with queued work get an owed pass too: a delivery parked before the
|
|
74
|
+
// drain (a budget pause, say) has no generation of its own to wake it.
|
|
75
|
+
for (const agentId of queued)
|
|
76
|
+
this.pausedOut.add(agentId);
|
|
77
|
+
for (const agent of this.options.agents)
|
|
78
|
+
if ((this.generations.get(agent.id) ?? 0) > (this.observed.get(agent.id) ?? 0) || this.pausedOut.has(agent.id))
|
|
79
|
+
this.notify(agent.id);
|
|
80
|
+
}
|
|
81
|
+
/** True once no inbox loop is alive: nothing runs, and nothing can start until resume. */
|
|
82
|
+
quiescent() { return this.work.size === 0; }
|
|
83
|
+
async drain(agentId, baseline = 0) {
|
|
48
84
|
const agent = this.options.agents.find((value) => value.id === agentId);
|
|
49
|
-
let observedGeneration =
|
|
85
|
+
let observedGeneration = baseline;
|
|
50
86
|
while (!this.stopping) {
|
|
87
|
+
if (this.paused) {
|
|
88
|
+
this.pausedOut.add(agentId);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
51
91
|
const generation = this.generations.get(agentId) ?? 0;
|
|
52
|
-
const
|
|
92
|
+
const stale = [];
|
|
93
|
+
const records = (await this.options.store.recoverable(new Set(this.options.agents.map((value) => value.id)), (record) => { if (record.agent_id === agentId)
|
|
94
|
+
stale.push(record); })).filter((record) => record.agent_id === agentId);
|
|
95
|
+
for (const record of stale)
|
|
96
|
+
await expireStaleDelivery(this.options.store, record);
|
|
53
97
|
const selected = selectBatch(records, agent, generation > observedGeneration);
|
|
54
98
|
observedGeneration = generation;
|
|
55
99
|
this.observed.set(agentId, generation);
|
|
@@ -59,6 +103,10 @@ export class AttentionDispatcher {
|
|
|
59
103
|
const budget = await this.options.fuse.snapshot(agentId, agent.attention);
|
|
60
104
|
if (budget.state !== "available")
|
|
61
105
|
return;
|
|
106
|
+
if (this.paused) {
|
|
107
|
+
this.pausedOut.add(agentId);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
62
110
|
const executionId = selected[0].execution_id ?? randomUUID();
|
|
63
111
|
const acquired = await this.options.store.acquireClaim(selected[0].acceptance_id, this.owner, selected.map((record) => record.acceptance_id), executionId);
|
|
64
112
|
if (acquired.state === "held") {
|
|
@@ -70,8 +118,22 @@ export class AttentionDispatcher {
|
|
|
70
118
|
const claimed = selected.map((record) => ({ record, claim: acquired.claim, done: false }));
|
|
71
119
|
for (const item of claimed)
|
|
72
120
|
item.record = await this.options.store.transitionClaimed(item.record.acceptance_id, item.claim, "running", undefined, undefined, agent.attention === undefined ? undefined : { execution_id: executionId, deferred: false });
|
|
73
|
-
|
|
74
|
-
|
|
121
|
+
// A member ended between selection and claim (another host's expiry or stop
|
|
122
|
+
// reached it first): never wake cognition on a batch that includes it.
|
|
123
|
+
if (claimed.some((item) => item.record.state !== "running")) {
|
|
124
|
+
for (const item of claimed.filter((value) => value.record.state === "running"))
|
|
125
|
+
await this.options.store.transitionClaimed(item.record.acceptance_id, item.claim, "accepted");
|
|
126
|
+
await this.options.store.releaseClaim(claimed[0].claim);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
// A drain that arrived after the claim releases it before any budget is spent.
|
|
130
|
+
// Admission is the drain's linearization point: a turn the fuse admitted before
|
|
131
|
+
// the loop saw the drain is a running turn, and keeps the host "draining".
|
|
132
|
+
const pausedBeforeAdmit = this.paused;
|
|
133
|
+
const verdict = pausedBeforeAdmit ? undefined : await this.options.fuse.admit(agentId, randomUUID(), agent.attention);
|
|
134
|
+
if (verdict?.state !== "admitted" || this.stopping) {
|
|
135
|
+
if (pausedBeforeAdmit)
|
|
136
|
+
this.pausedOut.add(agentId);
|
|
75
137
|
for (const item of claimed)
|
|
76
138
|
await this.options.store.transitionClaimed(item.record.acceptance_id, item.claim, "accepted");
|
|
77
139
|
await this.options.store.releaseClaim(claimed[0].claim);
|
|
@@ -101,31 +163,34 @@ export class AttentionDispatcher {
|
|
|
101
163
|
}).catch((error) => this.failClosed(agent.id, error));
|
|
102
164
|
}, store.claimHeartbeatIntervalMs());
|
|
103
165
|
this.active.set(agent.id, { execution_id: agent.attention === undefined ? claimed[0].record.delivery_id : executionId, delivery_ids: claimed.map((item) => item.record.delivery_id) });
|
|
104
|
-
const messages = claimed.map(({ record }) => ({ acceptance_id: record.acceptance_id, delivery_id: record.delivery_id, ...record.event }));
|
|
105
|
-
if (agent.attention !== undefined)
|
|
106
|
-
registry.set(agent.id, {
|
|
107
|
-
executionId, messages, budget: () => fuse.snapshot(agent.id, agent.attention),
|
|
108
|
-
disposition: (deliveryId, disposition) => serialize(async () => {
|
|
109
|
-
const item = claimed.find((value) => value.record.delivery_id === deliveryId);
|
|
110
|
-
if (!item)
|
|
111
|
-
throw new Error("Delivery is outside this agent's selected inbox turn");
|
|
112
|
-
if (item.done && item.record.state === "completed") {
|
|
113
|
-
if (disposition !== "complete")
|
|
114
|
-
throw new Error("A completed delivery cannot be deferred");
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (item.done && disposition === "defer")
|
|
118
|
-
return;
|
|
119
|
-
item.record = await store.transitionClaimed(item.record.acceptance_id, item.claim, disposition === "complete" ? "completed" : "accepted", undefined, disposition === "complete" ? "" : undefined, disposition === "defer" ? { deferred: true, clear_execution: true, execution_error: null } : { execution_id: executionId, deferred: false, execution_error: null });
|
|
120
|
-
item.done = true;
|
|
121
|
-
})
|
|
122
|
-
});
|
|
123
166
|
let result;
|
|
124
167
|
try {
|
|
168
|
+
const messages = taskClockAttentionMessages(claimed.map(({ record }) => record));
|
|
169
|
+
if (agent.attention !== undefined)
|
|
170
|
+
registry.set(agent.id, {
|
|
171
|
+
executionId, messages, budget: () => fuse.snapshot(agent.id, agent.attention),
|
|
172
|
+
disposition: (deliveryId, disposition) => serialize(async () => {
|
|
173
|
+
const item = claimed.find((value) => value.record.delivery_id === deliveryId);
|
|
174
|
+
if (!item)
|
|
175
|
+
throw new Error("Delivery is outside this agent's selected inbox turn");
|
|
176
|
+
if (item.done && item.record.state === "completed") {
|
|
177
|
+
if (disposition !== "complete")
|
|
178
|
+
throw new Error("A completed delivery cannot be deferred");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (item.done && disposition === "defer")
|
|
182
|
+
return;
|
|
183
|
+
item.record = await store.transitionClaimed(item.record.acceptance_id, item.claim, disposition === "complete" ? "completed" : "accepted", undefined, disposition === "complete" ? "" : undefined, disposition === "defer" ? { deferred: true, clear_execution: true, execution_error: null } : { execution_id: executionId, deferred: false, execution_error: null });
|
|
184
|
+
item.done = true;
|
|
185
|
+
})
|
|
186
|
+
});
|
|
125
187
|
const first = claimed[0].record;
|
|
126
|
-
|
|
188
|
+
if (agent.recording) {
|
|
189
|
+
await recordWakeMoment(agent, agent.attention === undefined ? first.delivery_id : executionId, claimed.map(({ record }) => record), { ...this.options.recordingOptionsForTest, verifyStore: () => verifyHostRecordingStore(host, agent.id) });
|
|
190
|
+
}
|
|
191
|
+
result = await wakeFromDurableInbox(host, { token, agentId: agent.id, event: {
|
|
127
192
|
version: "noopolis.daimon.wake.v1", id: agent.attention === undefined ? first.delivery_id : executionId, kind: first.event.kind,
|
|
128
|
-
occurredAt:
|
|
193
|
+
occurredAt: messages[0].occurred_at,
|
|
129
194
|
text: agent.attention === undefined ? first.event.text : inboxPrompt(messages, agent.engine.kind, agent.attention.maxBatchBytes)
|
|
130
195
|
} });
|
|
131
196
|
}
|
|
@@ -152,9 +217,21 @@ export class AttentionDispatcher {
|
|
|
152
217
|
await store.transitionClaimed(item.record.acceptance_id, item.claim, "accepted", result.code);
|
|
153
218
|
}
|
|
154
219
|
else if (agent.attention !== undefined) {
|
|
155
|
-
// Successful reading is not completion
|
|
156
|
-
//
|
|
157
|
-
|
|
220
|
+
// Successful reading is not completion: an undisposed delivery of a
|
|
221
|
+
// completed execution waits, deferred, for new input.
|
|
222
|
+
//
|
|
223
|
+
// A failed execution is retried ONCE, at once, under a fresh execution
|
|
224
|
+
// id. Keeping its id made every retry the same broker turn — a sealed
|
|
225
|
+
// failure replays forever and a changed prompt is a turn conflict — and
|
|
226
|
+
// deferring it left work a turn died on (a token ceiling after a
|
|
227
|
+
// validated revision) waiting for mail that might never come. The
|
|
228
|
+
// record's `execution_error` is the bound: it is set by the failure and
|
|
229
|
+
// cleared only by a disposition or a completed execution, so a delivery
|
|
230
|
+
// that fails again is deferred like any other.
|
|
231
|
+
const retry = result.status === "failed" && item.record.execution_error === undefined;
|
|
232
|
+
await store.transitionClaimed(item.record.acceptance_id, item.claim, "accepted", undefined, undefined, { deferred: !retry, clear_execution: result.status !== "rejected", execution_error: executionError });
|
|
233
|
+
if (retry)
|
|
234
|
+
console.error(`daimon: wake requeued once agent=${agent.id} delivery=${item.record.delivery_id} after ${sanitizeExecutionError(executionError ?? "engine_failed")}`);
|
|
158
235
|
}
|
|
159
236
|
else if (result.status === "completed")
|
|
160
237
|
await store.transitionClaimed(item.record.acceptance_id, item.claim, "completed", undefined, result.text);
|
|
@@ -179,7 +256,7 @@ export class AttentionDispatcher {
|
|
|
179
256
|
void this.options.host.stop().catch(() => undefined);
|
|
180
257
|
}
|
|
181
258
|
async waitUntil(timestamp) {
|
|
182
|
-
if (this.stopping)
|
|
259
|
+
if (this.stopping || this.paused)
|
|
183
260
|
return;
|
|
184
261
|
await new Promise((resolve) => {
|
|
185
262
|
const finish = () => { clearTimeout(timer); this.waiters.delete(finish); resolve(); };
|
|
@@ -188,6 +265,23 @@ export class AttentionDispatcher {
|
|
|
188
265
|
});
|
|
189
266
|
}
|
|
190
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* A delivery past `STALE_QUEUED_DELIVERY_MS` is stopped `queued_wake_stopped`
|
|
270
|
+
* where the dispatcher meets it, and the stop is logged: an expiry nobody can
|
|
271
|
+
* see reads, in every artifact, like work that was never sent. Stopping goes
|
|
272
|
+
* through the store's accepted-only seam, so a delivery claimed in the meantime
|
|
273
|
+
* is left alone; a failure to stop is logged and retried on the next drain.
|
|
274
|
+
*/
|
|
275
|
+
async function expireStaleDelivery(store, record) {
|
|
276
|
+
try {
|
|
277
|
+
const stopped = await store.transitionAcceptedToStopped(record.acceptance_id, "queued_wake_stopped");
|
|
278
|
+
if (stopped.state === "stopped")
|
|
279
|
+
console.error(`daimon: wake expired agent=${record.agent_id} delivery=${record.delivery_id} accepted_at=${record.accepted_at} unhandled past ${STALE_QUEUED_DELIVERY_MS / 3_600_000}h; stopped queued_wake_stopped${record.execution_error === undefined ? "" : ` (last failure: ${record.execution_error})`}`);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
console.error(`daimon: wake expiry failed agent=${record.agent_id} delivery=${record.delivery_id}: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
191
285
|
export function selectBatch(records, agent, revisitDeferred) {
|
|
192
286
|
const eligible = records.filter((record) => !record.deferred || revisitDeferred);
|
|
193
287
|
const first = eligible[0];
|
package/dist/runtime/cli.js
CHANGED
|
@@ -139,6 +139,11 @@ async function handleRequest(request, response, host, control, expectedToken) {
|
|
|
139
139
|
const availability = await control.availability(expectedToken);
|
|
140
140
|
return availability === undefined ? respond(response, 404, { error: "not_found" }) : respond(response, 200, availability);
|
|
141
141
|
}
|
|
142
|
+
if (control !== undefined && request.method === "POST" && (url.pathname === "/v2/drain" || url.pathname === "/v2/resume")) {
|
|
143
|
+
assertQuery(url, []);
|
|
144
|
+
const availability = url.pathname === "/v2/drain" ? await control.drain(expectedToken) : await control.resume(expectedToken);
|
|
145
|
+
return availability === undefined ? respond(response, 404, { error: "not_found" }) : respond(response, 200, availability);
|
|
146
|
+
}
|
|
142
147
|
if (control !== undefined && request.method === "GET" && url.pathname === "/v2/activity") {
|
|
143
148
|
assertQuery(url, []);
|
|
144
149
|
const activity = await control.activityV2(expectedToken);
|