@ouro.bot/cli 0.1.0-alpha.742 → 0.1.0-alpha.744
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/changelog.json +32 -0
- package/deploy/unraid/Dockerfile +7 -2
- package/deploy/unraid/README.txt +704 -64
- package/deploy/unraid/migrate-sanctuary-bundle.mjs +21 -0
- package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
- package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
- package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
- package/deploy/unraid/ouro-events/install-usenet-guard.sh +434 -0
- package/deploy/unraid/ouro-events/usenet-health.sh +178 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +4 -3
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +5 -1
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +2 -2
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +1 -1
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -1
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -1
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +4 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +6 -2
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +4 -2
- package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +19 -17
- package/deploy/unraid/sanctuary.xml +13 -10
- package/dist/arc/cares.js +171 -37
- package/dist/arc/obligations.js +38 -0
- package/dist/heart/awaiting/await-alert.js +4 -2
- package/dist/heart/awaiting/await-expiry.js +39 -4
- package/dist/heart/awaiting/await-parser.js +8 -0
- package/dist/heart/core.js +37 -7
- package/dist/heart/cross-chat-delivery.js +1 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +11 -7
- package/dist/heart/daemon/container-spec-auditor.js +38 -11
- package/dist/heart/daemon/daemon-entry.js +19 -5
- package/dist/heart/daemon/daemon.js +154 -22
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +126 -42
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +26 -29
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +42 -51
- package/dist/heart/daemon/sanctuary-bundle-migration.js +398 -0
- package/dist/heart/external-events/router.js +1025 -43
- package/dist/heart/session-events.js +23 -5
- package/dist/heart/steward-policy.js +302 -0
- package/dist/heart/tool-approval.js +3 -1
- package/dist/heart/turn-context.js +1 -0
- package/dist/mind/context.js +8 -3
- package/dist/mind/pending.js +25 -0
- package/dist/mind/prompt.js +49 -1
- package/dist/repertoire/guardrails.js +2 -1
- package/dist/repertoire/relationship-authorization.js +194 -0
- package/dist/repertoire/tools-awaiting.js +229 -26
- package/dist/repertoire/tools-base.js +15 -0
- package/dist/repertoire/tools-continuity.js +259 -4
- package/dist/repertoire/tools-notes.js +29 -3
- package/dist/repertoire/tools-session.js +219 -4
- package/dist/repertoire/tools-steward-policy.js +88 -0
- package/dist/repertoire/tools-telegram-contacts.js +37 -0
- package/dist/repertoire/tools-unraid.js +113 -6
- package/dist/repertoire/tools.js +104 -9
- package/dist/repertoire/unraid-restart.js +86 -4
- package/dist/senses/private-runtime-worker.js +36 -3
- package/dist/senses/private-runtime.js +310 -63
- package/dist/senses/sanctuary-health-acceptance-probe.js +8 -6
- package/dist/senses/sanctuary-health-runner.js +47 -103
- package/dist/senses/sanctuary-health.js +53 -105
- package/dist/senses/sanctuary-runtime.js +36 -2
- package/dist/senses/sanctuary-sab.js +80 -0
- package/dist/senses/shared-turn.js +64 -9
- package/dist/senses/telegram-admission.js +684 -0
- package/dist/senses/telegram-approval-runtime.js +46 -6
- package/dist/senses/telegram-client.js +212 -66
- package/dist/senses/telegram-effect-adapter.js +787 -0
- package/dist/senses/telegram.js +728 -48
- package/npm-shrinkwrap.json +6 -6
- package/package.json +2 -2
|
@@ -56,6 +56,9 @@ const context_1 = require("../mind/context");
|
|
|
56
56
|
const prompt_1 = require("../mind/prompt");
|
|
57
57
|
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
58
58
|
const tools_1 = require("../repertoire/tools");
|
|
59
|
+
const tools_awaiting_1 = require("../repertoire/tools-awaiting");
|
|
60
|
+
const relationship_authorization_1 = require("../repertoire/relationship-authorization");
|
|
61
|
+
const run_ledger_1 = require("../heart/run-ledger");
|
|
59
62
|
const bundle_manifest_1 = require("../mind/bundle-manifest");
|
|
60
63
|
const pending_1 = require("../mind/pending");
|
|
61
64
|
const obligations_1 = require("../arc/obligations");
|
|
@@ -83,6 +86,9 @@ const habit_policy_1 = require("../rsvp/habit-policy");
|
|
|
83
86
|
const daemon_health_1 = require("../heart/daemon/daemon-health");
|
|
84
87
|
const flight_recorder_1 = require("../arc/flight-recorder");
|
|
85
88
|
const desk_section_1 = require("../mind/desk-section");
|
|
89
|
+
const sanctuary_runtime_1 = require("./sanctuary-runtime");
|
|
90
|
+
const shared_turn_1 = require("./shared-turn");
|
|
91
|
+
const relationship_authorization_2 = require("../repertoire/relationship-authorization");
|
|
86
92
|
const private_runtime_1 = require("../heart/private-runtime");
|
|
87
93
|
exports.PRIVATE_TURN_DECISION_MAX_AGE_MS = 15 * 60_000;
|
|
88
94
|
const DUPLICATE_PRIVATE_TURN_STATUS = "DUPLICATE_PRIVATE_TURN";
|
|
@@ -93,6 +99,74 @@ const DEFAULT_PRIVATE_RUNTIME_INSTINCTS = [
|
|
|
93
99
|
enabled: true,
|
|
94
100
|
},
|
|
95
101
|
];
|
|
102
|
+
class StaleRelationshipAwaitError extends Error {
|
|
103
|
+
reason;
|
|
104
|
+
constructor(reason) {
|
|
105
|
+
super(`Relationship await authority ${reason}`);
|
|
106
|
+
this.reason = reason;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function exactFriendFileIsMissing(agentRoot, friendId) {
|
|
110
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(friendId))
|
|
111
|
+
throw new Error("Relationship await authority friend id is invalid");
|
|
112
|
+
try {
|
|
113
|
+
fs.lstatSync(path.join(agentRoot, "friends", `${friendId}.json`));
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error.code === "ENOENT")
|
|
118
|
+
return true;
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function relationshipAwaitCoordinates(awaitFile) {
|
|
123
|
+
const values = [awaitFile.filed_for_friend_id, awaitFile.filed_from, awaitFile.filed_from_key];
|
|
124
|
+
if (!awaitFile.request_id) {
|
|
125
|
+
if (awaitFile.filed_from === "external-event" && awaitFile.filed_for_friend_id && awaitFile.filed_from_key) {
|
|
126
|
+
if (Buffer.byteLength(awaitFile.filed_for_friend_id) > 256 || Buffer.byteLength(awaitFile.filed_from_key) > 1_024)
|
|
127
|
+
throw new Error("Relationship await provenance is invalid");
|
|
128
|
+
return { friendId: awaitFile.filed_for_friend_id, channel: awaitFile.filed_from, key: awaitFile.filed_from_key, requestId: null };
|
|
129
|
+
}
|
|
130
|
+
const isSystemAwait = awaitFile.filed_for_friend_id === null
|
|
131
|
+
&& awaitFile.filed_from_key === null
|
|
132
|
+
&& (awaitFile.filed_from === null || awaitFile.filed_from === "unknown" || awaitFile.filed_from === "cli");
|
|
133
|
+
if (isSystemAwait)
|
|
134
|
+
return null;
|
|
135
|
+
throw new Error("Relationship await provenance is incomplete or legacy");
|
|
136
|
+
}
|
|
137
|
+
if (values.some((value) => value === null))
|
|
138
|
+
throw new Error("Relationship await provenance is incomplete or legacy");
|
|
139
|
+
const [friendId, channel, key] = values;
|
|
140
|
+
if (channel !== "telegram" || Buffer.byteLength(friendId) > 256 || Buffer.byteLength(channel) > 64 || Buffer.byteLength(key) > 1_024 || Buffer.byteLength(awaitFile.request_id) > 256) {
|
|
141
|
+
throw new Error("Relationship await provenance is invalid");
|
|
142
|
+
}
|
|
143
|
+
return { friendId, channel, key, requestId: awaitFile.request_id };
|
|
144
|
+
}
|
|
145
|
+
async function resolveRelationshipAwaitAuthority(agentRoot, store, registry, coordinates) {
|
|
146
|
+
const friend = await store.get(coordinates.friendId);
|
|
147
|
+
if (!friend) {
|
|
148
|
+
if (exactFriendFileIsMissing(agentRoot, coordinates.friendId))
|
|
149
|
+
throw new StaleRelationshipAwaitError("friend is missing");
|
|
150
|
+
throw new Error("Relationship await authority friend exists but could not be read");
|
|
151
|
+
}
|
|
152
|
+
if (friend.id !== coordinates.friendId)
|
|
153
|
+
throw new Error("Relationship await authority friend record is ambiguous");
|
|
154
|
+
if (friend.admissionState !== "active" || !friend.capabilityProfileId)
|
|
155
|
+
throw new StaleRelationshipAwaitError("admission or profile is not active");
|
|
156
|
+
if (!registry.profiles[friend.capabilityProfileId])
|
|
157
|
+
throw new StaleRelationshipAwaitError("admission or profile is not active");
|
|
158
|
+
if (coordinates.channel === "external-event" && (friend.capabilityProfileId !== "sanctuary-owner" || !registry.profiles["sanctuary-event"])) {
|
|
159
|
+
throw new StaleRelationshipAwaitError("admission or profile is not active");
|
|
160
|
+
}
|
|
161
|
+
const authorization = coordinates.channel === "external-event"
|
|
162
|
+
? (0, relationship_authorization_2.createRelationshipAuthorizationEvaluator)({ friend, registry, profileId: "sanctuary-event", requestPhase: "follow_up" })
|
|
163
|
+
: (0, relationship_authorization_2.createRelationshipAuthorizationEvaluator)({ friend, registry, requestId: coordinates.requestId, requestPhase: "follow_up" });
|
|
164
|
+
if (authorization.subject.friendId !== coordinates.friendId)
|
|
165
|
+
throw new Error("Relationship await authority friend is ambiguous");
|
|
166
|
+
if (!authorization.authorizeTool("resolve_await").allowed)
|
|
167
|
+
throw new StaleRelationshipAwaitError("admission or profile is not active");
|
|
168
|
+
return { authorization, friend };
|
|
169
|
+
}
|
|
96
170
|
function readAspirations(agentRoot) {
|
|
97
171
|
try {
|
|
98
172
|
return fs.readFileSync(path.join(agentRoot, "psyche", "ASPIRATIONS.md"), "utf8").trim();
|
|
@@ -728,6 +802,16 @@ function createSelfFriend(agentName) {
|
|
|
728
802
|
schemaVersion: 1,
|
|
729
803
|
};
|
|
730
804
|
}
|
|
805
|
+
function buildOwnerPresentationPreferences(friend) {
|
|
806
|
+
const lines = [
|
|
807
|
+
`## ${friend.name}'s presentation preferences`,
|
|
808
|
+
"Use this relationship context to decide whether and how to contact the owner. It is presentation-only and grants no mutation authority.",
|
|
809
|
+
];
|
|
810
|
+
lines.push(...(0, relationship_authorization_1.renderRelationshipPreferences)(friend));
|
|
811
|
+
if (lines.length === 2)
|
|
812
|
+
lines.push("- No presentation preferences have been learned yet; be calm, concise, and useful.");
|
|
813
|
+
return lines.join("\n");
|
|
814
|
+
}
|
|
731
815
|
// No-op friend store for the private runtime. It doesn't track token usage per-friend.
|
|
732
816
|
function createNoOpFriendStore() {
|
|
733
817
|
return {
|
|
@@ -877,6 +961,7 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
877
961
|
let userContent;
|
|
878
962
|
let habitTools;
|
|
879
963
|
let habitParsedSuccessfully = false;
|
|
964
|
+
let parsedAwait = null;
|
|
880
965
|
const isPayloadSpecificWake = !!options?.taskId
|
|
881
966
|
|| (reason === "habit" && !!options?.habitName)
|
|
882
967
|
|| (reason === "await" && !!options?.awaitName);
|
|
@@ -1010,6 +1095,7 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1010
1095
|
try {
|
|
1011
1096
|
const awaitContent = fs.readFileSync(awaitFilePath, "utf-8");
|
|
1012
1097
|
const parsed = (0, await_runtime_state_1.applyAwaitRuntimeState)(agentRoot, (0, await_parser_1.parseAwaitFile)(awaitContent, awaitFilePath));
|
|
1098
|
+
parsedAwait = parsed;
|
|
1013
1099
|
awaitFound = true;
|
|
1014
1100
|
awaitBody = parsed.body || undefined;
|
|
1015
1101
|
condition = parsed.condition;
|
|
@@ -1043,12 +1129,121 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1043
1129
|
if (shouldUseHeldReturnWake) {
|
|
1044
1130
|
userContent = buildHeldReturnWakeMessage();
|
|
1045
1131
|
}
|
|
1046
|
-
const userMessage = { role: "user", content: userContent };
|
|
1047
1132
|
// ── Session loader: wraps existing session logic ──────────────────
|
|
1048
1133
|
const innerCapabilities = (0, friends_1.getChannelCapabilities)("inner");
|
|
1049
1134
|
const selfFriend = createSelfFriend(agentName);
|
|
1050
1135
|
const selfContext = { friend: selfFriend, channel: innerCapabilities };
|
|
1051
1136
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
1137
|
+
const relationshipAwaitCoordinatesValue = parsedAwait ? relationshipAwaitCoordinates(parsedAwait) : null;
|
|
1138
|
+
const relationshipAwait = relationshipAwaitCoordinatesValue
|
|
1139
|
+
? await (async () => {
|
|
1140
|
+
const agentRoot = (0, identity_1.getAgentRoot)(agentName);
|
|
1141
|
+
const assertLiveBinding = () => {
|
|
1142
|
+
const binding = relationshipAwaitCoordinatesValue.channel === "external-event"
|
|
1143
|
+
? parsedAwait.wake_at && (0, tools_awaiting_1.hasActiveExternalEventAwait)(agentName, { recordPath: relationshipAwaitCoordinatesValue.key, awaitName: parsedAwait.name, wakeAt: parsedAwait.wake_at })
|
|
1144
|
+
? { active: true }
|
|
1145
|
+
: { active: false, reason: "external event disposition is no longer active" }
|
|
1146
|
+
: (0, tools_awaiting_1.inspectRelationshipFollowUp)(agentRoot, {
|
|
1147
|
+
friendId: relationshipAwaitCoordinatesValue.friendId,
|
|
1148
|
+
channel: relationshipAwaitCoordinatesValue.channel,
|
|
1149
|
+
key: relationshipAwaitCoordinatesValue.key,
|
|
1150
|
+
requestId: relationshipAwaitCoordinatesValue.requestId,
|
|
1151
|
+
awaitName: parsedAwait.name,
|
|
1152
|
+
now: now().getTime(),
|
|
1153
|
+
});
|
|
1154
|
+
if (!binding.active) {
|
|
1155
|
+
(0, tools_awaiting_1.cancelStaleAwait)(agentRoot, agentName, parsedAwait.name, binding.reason);
|
|
1156
|
+
throw new Error(`Relationship await authority binding is missing, stale, or ambiguous: ${binding.reason}`);
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
assertLiveBinding();
|
|
1160
|
+
const store = new friends_1.FileFriendStore(path.join(agentRoot, "friends"));
|
|
1161
|
+
const resolve = () => resolveRelationshipAwaitAuthority(agentRoot, store, (0, relationship_authorization_2.loadRelationshipCapabilityRegistry)(agentRoot), relationshipAwaitCoordinatesValue);
|
|
1162
|
+
const resolveOrCancel = async () => {
|
|
1163
|
+
try {
|
|
1164
|
+
return await resolve();
|
|
1165
|
+
}
|
|
1166
|
+
catch (error) {
|
|
1167
|
+
if (error instanceof StaleRelationshipAwaitError)
|
|
1168
|
+
(0, tools_awaiting_1.cancelStaleAwait)(agentRoot, agentName, parsedAwait.name, error.reason);
|
|
1169
|
+
throw error;
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
const initial = await resolveOrCancel();
|
|
1173
|
+
return {
|
|
1174
|
+
store,
|
|
1175
|
+
context: { friend: initial.friend, channel: innerCapabilities },
|
|
1176
|
+
currentSession: {
|
|
1177
|
+
friendId: relationshipAwaitCoordinatesValue.friendId,
|
|
1178
|
+
channel: relationshipAwaitCoordinatesValue.channel,
|
|
1179
|
+
key: relationshipAwaitCoordinatesValue.key,
|
|
1180
|
+
sessionPath: relationshipAwaitCoordinatesValue.channel === "telegram"
|
|
1181
|
+
? (0, shared_turn_1.getSenseSessionPath)(agentName, relationshipAwaitCoordinatesValue.friendId, "telegram", relationshipAwaitCoordinatesValue.key, agentRoot)
|
|
1182
|
+
: sessionFilePath,
|
|
1183
|
+
},
|
|
1184
|
+
relationshipAuthorization: {
|
|
1185
|
+
...(relationshipAwaitCoordinatesValue.requestId ? { requestId: relationshipAwaitCoordinatesValue.requestId } : {}),
|
|
1186
|
+
profileId: initial.authorization.profileId,
|
|
1187
|
+
authorizedContextScopes: initial.authorization.authorizedContextScopes,
|
|
1188
|
+
advertisedToolNames: relationshipAwaitCoordinatesValue.channel === "external-event" ? ["resolve_await"] : initial.authorization.advertisedToolNames,
|
|
1189
|
+
authorizeTool: async (name, args) => {
|
|
1190
|
+
assertLiveBinding();
|
|
1191
|
+
const decision = (await resolveOrCancel()).authorization.authorizeTool(name, args);
|
|
1192
|
+
return relationshipAwaitCoordinatesValue.channel !== "external-event" || name === "resolve_await"
|
|
1193
|
+
? decision
|
|
1194
|
+
: { ...decision, allowed: false, reason: "external event await wake is resolve-only" };
|
|
1195
|
+
},
|
|
1196
|
+
},
|
|
1197
|
+
};
|
|
1198
|
+
})()
|
|
1199
|
+
: undefined;
|
|
1200
|
+
const externalEventRelationship = options?.externalEvent
|
|
1201
|
+
? await (async () => {
|
|
1202
|
+
const agentRoot = (0, identity_1.getAgentRoot)(agentName);
|
|
1203
|
+
const store = new friends_1.FileFriendStore(path.join(agentRoot, "friends"));
|
|
1204
|
+
const registry = (0, relationship_authorization_2.loadRelationshipCapabilityRegistry)(agentRoot);
|
|
1205
|
+
const resolve = () => (0, relationship_authorization_2.resolveProfileScopedRelationshipAuthorization)({
|
|
1206
|
+
store,
|
|
1207
|
+
registry,
|
|
1208
|
+
relationshipProfileId: "sanctuary-owner",
|
|
1209
|
+
profileId: "sanctuary-event",
|
|
1210
|
+
});
|
|
1211
|
+
const initial = await resolve();
|
|
1212
|
+
const initialDisposition = initial.authorizeTool("external_event_disposition");
|
|
1213
|
+
if (!initialDisposition.allowed)
|
|
1214
|
+
throw new Error(`external event relationship authority denied: ${initialDisposition.reason}`);
|
|
1215
|
+
const owners = (await store.listAll()).filter((friend) => friend.capabilityProfileId === "sanctuary-owner");
|
|
1216
|
+
const [owner] = owners;
|
|
1217
|
+
if (owners.length !== 1 || !owner)
|
|
1218
|
+
throw new Error("external event owner presentation context must resolve to exactly one Friend");
|
|
1219
|
+
return {
|
|
1220
|
+
store,
|
|
1221
|
+
context: { friend: owner, channel: innerCapabilities },
|
|
1222
|
+
ownerPresentationPreferences: buildOwnerPresentationPreferences(owner),
|
|
1223
|
+
relationshipAuthorization: {
|
|
1224
|
+
profileId: initial.profileId,
|
|
1225
|
+
authorizedContextScopes: initial.authorizedContextScopes,
|
|
1226
|
+
advertisedToolNames: initial.advertisedToolNames,
|
|
1227
|
+
authorizeTool: async (name) => (await resolve()).authorizeTool(name),
|
|
1228
|
+
},
|
|
1229
|
+
externalEventAuthority: {
|
|
1230
|
+
authorizeDisposition: () => {
|
|
1231
|
+
const decision = initial.authorizeTool("external_event_disposition");
|
|
1232
|
+
return decision.allowed ? { allowed: true, reason: "relationship-authorized" } : { allowed: false, reason: decision.reason };
|
|
1233
|
+
},
|
|
1234
|
+
},
|
|
1235
|
+
externalEventEffects: {
|
|
1236
|
+
deliverOwnerDecision: async (input) => {
|
|
1237
|
+
const { sendTelegramExternalEventDecision } = await Promise.resolve().then(() => __importStar(require("./telegram")));
|
|
1238
|
+
await sendTelegramExternalEventDecision(agentName, input);
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
};
|
|
1242
|
+
})()
|
|
1243
|
+
: undefined;
|
|
1244
|
+
if (externalEventRelationship)
|
|
1245
|
+
userContent = `${userContent}\n\n${externalEventRelationship.ownerPresentationPreferences}`;
|
|
1246
|
+
const userMessage = { role: "user", content: userContent };
|
|
1052
1247
|
// ── Habit tool enforcement ───────────────────────────────────────
|
|
1053
1248
|
let habitToolsResolved;
|
|
1054
1249
|
if (habitTools !== undefined) {
|
|
@@ -1076,6 +1271,12 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1076
1271
|
if (options?.noSend === true) {
|
|
1077
1272
|
habitToolsResolved = [];
|
|
1078
1273
|
}
|
|
1274
|
+
const externalEventToolsResolved = options?.externalEvent
|
|
1275
|
+
? (0, tools_1.getSanctuaryRelationshipTools)(externalEventRelationship.relationshipAuthorization.advertisedToolNames)
|
|
1276
|
+
: undefined;
|
|
1277
|
+
const relationshipAwaitToolsResolved = relationshipAwait
|
|
1278
|
+
? (0, tools_1.getSanctuaryRelationshipTools)(relationshipAwait.relationshipAuthorization.advertisedToolNames)
|
|
1279
|
+
: undefined;
|
|
1079
1280
|
const effectiveHabitSession = options?.noSend === true
|
|
1080
1281
|
? reduceHabitSessionToNoSend(options.habitSession)
|
|
1081
1282
|
: options?.habitSession;
|
|
@@ -1105,71 +1306,117 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1105
1306
|
const traceId = (0, nerves_1.createTraceId)();
|
|
1106
1307
|
// Attention queue: built when pending messages are drained, shared with tool context
|
|
1107
1308
|
let attentionQueue = [];
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1309
|
+
const externalRunStartedAt = now().toISOString();
|
|
1310
|
+
const externalRunBase = options?.externalEvent ? {
|
|
1311
|
+
agent: agentName,
|
|
1312
|
+
triggerType: "inbound",
|
|
1313
|
+
sourceKind: "private-runtime",
|
|
1314
|
+
senseOrHabit: "external-event",
|
|
1315
|
+
target: { source: options.externalEvent.source, eventId: options.externalEvent.eventId, generation: options.externalEvent.generation, observationRevision: options.externalEvent.observationRevision },
|
|
1316
|
+
idempotencyScope: { recordPath: options.externalEvent.recordPath, generation: options.externalEvent.generation, observationRevision: options.externalEvent.observationRevision },
|
|
1317
|
+
startedAt: externalRunStartedAt,
|
|
1318
|
+
} : null;
|
|
1319
|
+
if (externalRunBase)
|
|
1320
|
+
(0, run_ledger_1.appendRunLedgerRecordNonFatal)((0, identity_1.getAgentRoot)(agentName), (0, run_ledger_1.createRunLedgerRecord)({ ...externalRunBase, lifecycle: "started" }));
|
|
1321
|
+
let result;
|
|
1322
|
+
try {
|
|
1323
|
+
result = await (0, pipeline_1.handleInboundTurn)({
|
|
1324
|
+
channel: "inner",
|
|
1325
|
+
sessionKey: "dialog",
|
|
1326
|
+
capabilities: innerCapabilities,
|
|
1327
|
+
messages: [userMessage],
|
|
1328
|
+
continuityIngressTexts: [],
|
|
1329
|
+
callbacks,
|
|
1330
|
+
sessionTurnLease,
|
|
1331
|
+
friendResolver: { resolve: () => Promise.resolve(selfContext) },
|
|
1332
|
+
sessionLoader,
|
|
1333
|
+
pendingDir,
|
|
1334
|
+
friendStore: createNoOpFriendStore(),
|
|
1335
|
+
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
1336
|
+
drainPending: pending_1.drainPending,
|
|
1337
|
+
runAgent: core_1.runAgent,
|
|
1338
|
+
postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
|
|
1339
|
+
const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
|
|
1340
|
+
return (0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
|
|
1341
|
+
},
|
|
1342
|
+
accumulateFriendTokens: friends_1.accumulateFriendTokens,
|
|
1343
|
+
signal: options?.signal,
|
|
1344
|
+
/* v8 ignore start -- attention queue: callback invoked by pipeline during pending drain; tested via attention-queue unit tests @preserve */
|
|
1345
|
+
onPendingDrained: (drained) => {
|
|
1346
|
+
const outstandingObligations = (0, obligations_1.listActiveReturnObligations)(agentName);
|
|
1347
|
+
const builtAttentionQueue = (0, attention_queue_1.buildAttentionQueue)({
|
|
1348
|
+
drainedPending: drained,
|
|
1349
|
+
outstandingObligations,
|
|
1350
|
+
friendNameResolver: (friendId) => {
|
|
1351
|
+
try {
|
|
1352
|
+
const raw = fs.readFileSync(path.join((0, identity_1.getAgentRoot)(agentName), "friends", friendId + ".json"), "utf-8");
|
|
1353
|
+
const parsed = JSON.parse(raw);
|
|
1354
|
+
return typeof parsed.name === "string" ? parsed.name : null;
|
|
1355
|
+
}
|
|
1356
|
+
catch {
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
packetResolver: (packetId) => {
|
|
1361
|
+
try {
|
|
1362
|
+
return (0, packets_1.readPonderPacket)((0, identity_1.getAgentRoot)(agentName), packetId);
|
|
1363
|
+
}
|
|
1364
|
+
catch {
|
|
1365
|
+
return null;
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
});
|
|
1369
|
+
attentionQueue.splice(0, attentionQueue.length, ...builtAttentionQueue);
|
|
1370
|
+
const attentionFrame = (0, attention_queue_1.buildAttentionQueueStatusFrame)(attentionQueue);
|
|
1371
|
+
return attentionFrame ? [attentionFrame] : [];
|
|
1372
|
+
},
|
|
1373
|
+
/* v8 ignore stop */
|
|
1374
|
+
runAgentOptions: {
|
|
1375
|
+
traceId,
|
|
1376
|
+
toolChoiceRequired: true,
|
|
1377
|
+
mcpManager,
|
|
1378
|
+
...(options?.externalEvent
|
|
1379
|
+
? { tools: externalEventToolsResolved }
|
|
1380
|
+
: relationshipAwaitToolsResolved !== undefined ? { tools: relationshipAwaitToolsResolved }
|
|
1381
|
+
: habitToolsResolved !== undefined ? { tools: habitToolsResolved } : {}),
|
|
1382
|
+
toolContext: {
|
|
1383
|
+
signin: async () => undefined,
|
|
1384
|
+
delegatedOrigins: attentionQueue,
|
|
1385
|
+
...(relationshipAwait ? {
|
|
1386
|
+
friendStore: relationshipAwait.store,
|
|
1387
|
+
context: relationshipAwait.context,
|
|
1388
|
+
currentSession: relationshipAwait.currentSession,
|
|
1389
|
+
relationshipAuthorization: relationshipAwait.relationshipAuthorization,
|
|
1390
|
+
} : {}),
|
|
1391
|
+
...(options?.externalEvent ? {
|
|
1392
|
+
currentExternalEvent: Object.freeze({ ...options.externalEvent }),
|
|
1393
|
+
friendStore: externalEventRelationship.store,
|
|
1394
|
+
context: externalEventRelationship.context,
|
|
1395
|
+
relationshipAuthorization: externalEventRelationship.relationshipAuthorization,
|
|
1396
|
+
externalEventAuthority: externalEventRelationship.externalEventAuthority,
|
|
1397
|
+
externalEventEffects: externalEventRelationship.externalEventEffects,
|
|
1398
|
+
...(["sanctuary-health", "sanctuary-usenet"].includes(options.externalEvent.source) ? (0, sanctuary_runtime_1.createSanctuaryToolContext)(agentName) : {}),
|
|
1399
|
+
} : {}),
|
|
1400
|
+
...(options?.noSend ? { noSend: true } : {}),
|
|
1401
|
+
...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
|
|
1152
1402
|
},
|
|
1153
|
-
});
|
|
1154
|
-
attentionQueue.splice(0, attentionQueue.length, ...builtAttentionQueue);
|
|
1155
|
-
const attentionFrame = (0, attention_queue_1.buildAttentionQueueStatusFrame)(attentionQueue);
|
|
1156
|
-
return attentionFrame ? [attentionFrame] : [];
|
|
1157
|
-
},
|
|
1158
|
-
/* v8 ignore stop */
|
|
1159
|
-
runAgentOptions: {
|
|
1160
|
-
traceId,
|
|
1161
|
-
toolChoiceRequired: true,
|
|
1162
|
-
mcpManager,
|
|
1163
|
-
...(habitToolsResolved !== undefined && { tools: habitToolsResolved }),
|
|
1164
|
-
toolContext: {
|
|
1165
|
-
signin: async () => undefined,
|
|
1166
|
-
delegatedOrigins: attentionQueue,
|
|
1167
|
-
...(options?.noSend ? { noSend: true } : {}),
|
|
1168
1403
|
...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
|
|
1169
1404
|
},
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1405
|
+
});
|
|
1406
|
+
if (externalRunBase) {
|
|
1407
|
+
const lifecycle = result.turnOutcome === "settled" || result.turnOutcome === "observed" || result.turnOutcome === "rested"
|
|
1408
|
+
? "completed"
|
|
1409
|
+
: result.turnOutcome === "errored" ? "error"
|
|
1410
|
+
: result.turnOutcome === "blocked" || result.turnOutcome === "suspended" ? "blocked"
|
|
1411
|
+
: "skipped";
|
|
1412
|
+
(0, run_ledger_1.appendRunLedgerRecordNonFatal)((0, identity_1.getAgentRoot)(agentName), (0, run_ledger_1.createRunLedgerRecord)({ ...externalRunBase, lifecycle, endedAt: now().toISOString(), usage: (0, run_ledger_1.usageMetadataFromUsageData)(result.usage, result.usage ? "provider" : "reported-unavailable"), ...(result.turnOutcome === "errored" ? { errorName: "AgentTurnError" } : {}) }));
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
catch (error) {
|
|
1416
|
+
if (externalRunBase)
|
|
1417
|
+
(0, run_ledger_1.appendRunLedgerRecordNonFatal)((0, identity_1.getAgentRoot)(agentName), (0, run_ledger_1.createRunLedgerRecord)({ ...externalRunBase, lifecycle: "error", endedAt: now().toISOString(), errorName: error instanceof Error ? error.name : "UnknownError" }));
|
|
1418
|
+
throw error;
|
|
1419
|
+
}
|
|
1173
1420
|
// Post-turn routeDelegatedCompletion removed: delivery is now inline via surface tool.
|
|
1174
1421
|
// settle in the private runtime produces no CompletionMetadata, so routeDelegatedCompletion
|
|
1175
1422
|
// would be a no-op. The routing infrastructure is reused by the surface handler.
|
|
@@ -65,7 +65,7 @@ const TARGET_ENDPOINT = "https://books.mendelow.cloud/";
|
|
|
65
65
|
const CRON_MARKER = "# ouro:habit:sanctuary:sanctuary:sanctuary-health";
|
|
66
66
|
const CRON_COMMAND = "*/15 * * * * /usr/local/bin/node /opt/ouro/dist/heart/daemon/ouro-entry.js poke sanctuary --habit sanctuary-health --trigger cron";
|
|
67
67
|
const PROCESS_ENTRY = "/opt/ouro/dist/senses/sanctuary-health-acceptance-probe-entry.js";
|
|
68
|
-
const LABELS = ["unit-16f-cron-fingerprint", "unit-16g-health-transition", "unit-16h-
|
|
68
|
+
const LABELS = ["unit-16f-cron-fingerprint", "unit-16g-health-transition", "unit-16h-acceptance-delivery-probe"];
|
|
69
69
|
function digestBytes(value) {
|
|
70
70
|
return (0, node_crypto_1.createHash)("sha256").update(value).digest("hex");
|
|
71
71
|
}
|
|
@@ -505,7 +505,7 @@ async function runSanctuaryHealthAcceptanceProbe(input, dependencies = createSan
|
|
|
505
505
|
const cronBefore = fs.readFileSync(cronPath, "utf8");
|
|
506
506
|
const cronFingerprintBefore = digestBytes(cronBefore);
|
|
507
507
|
const cronRegisteredBefore = canonicalCron(cronBefore);
|
|
508
|
-
const effectiveNow = input.label === "unit-16h-
|
|
508
|
+
const effectiveNow = input.label === "unit-16h-acceptance-delivery-probe" ? exactLocalDailyBoundary(dependencies.now()) : dependencies.now();
|
|
509
509
|
let fixture;
|
|
510
510
|
let expectedIncidentDigest = "";
|
|
511
511
|
let realCheckEquivalent = false;
|
|
@@ -539,7 +539,7 @@ async function runSanctuaryHealthAcceptanceProbe(input, dependencies = createSan
|
|
|
539
539
|
else
|
|
540
540
|
await (0, sanctuary_health_1.withSanctuaryHealthStateLease)(statePath, async (lease) => {
|
|
541
541
|
expectedIncidentDigest = await normalizeWorkingState({ workspace, statePath, deps: dependencies, effectiveNow });
|
|
542
|
-
if (input.label === "unit-16h-
|
|
542
|
+
if (input.label === "unit-16h-acceptance-delivery-probe") {
|
|
543
543
|
const state = readHealthState(statePath);
|
|
544
544
|
state.incidents[`endpoint:${TARGET_ENDPOINT}`] = { id: `endpoint:${TARGET_ENDPOINT}`, summary: `${TARGET_ENDPOINT} returned 503` };
|
|
545
545
|
state.lastDigestDay = localDay(new Date(effectiveNow.getTime() - 36 * 60 * 60 * 1_000));
|
|
@@ -570,8 +570,8 @@ async function runSanctuaryHealthAcceptanceProbe(input, dependencies = createSan
|
|
|
570
570
|
}
|
|
571
571
|
else {
|
|
572
572
|
fixture = await createSanctuaryHealthAcceptanceLoopbackFixture([503, 503], dependencies.ambientFetch, dependencies.createLoopbackServer);
|
|
573
|
-
await runPhase("
|
|
574
|
-
await runPhase("
|
|
573
|
+
await runPhase("delivery-probe-first", "acceptance", 503, fixture.fetch, effectiveNow);
|
|
574
|
+
await runPhase("delivery-probe-repeat", "acceptance", 503, fixture.fetch, effectiveNow);
|
|
575
575
|
}
|
|
576
576
|
await closeServer(fixture.server);
|
|
577
577
|
socketAbsent = fixture.server.address() === null;
|
|
@@ -602,7 +602,7 @@ async function runSanctuaryHealthAcceptanceProbe(input, dependencies = createSan
|
|
|
602
602
|
cronDegradedBefore: !cronRegisteredBefore,
|
|
603
603
|
cronDegradedAfter: !cronRegisteredAfter,
|
|
604
604
|
fixtureSequenceDigest: digestJson(observedFixtureSequence),
|
|
605
|
-
clockMode: input.label === "unit-16h-
|
|
605
|
+
clockMode: input.label === "unit-16h-acceptance-delivery-probe" ? "local-daily-boundary" : "ambient",
|
|
606
606
|
effectiveNow: effectiveNow.toISOString(),
|
|
607
607
|
phases,
|
|
608
608
|
providerInvocationCount,
|
|
@@ -615,6 +615,8 @@ async function runSanctuaryHealthAcceptanceProbe(input, dependencies = createSan
|
|
|
615
615
|
productionRestored: beforeStateDigest === restoredStateDigest && cronFingerprintBefore === cronFingerprintAfter
|
|
616
616
|
&& cronRegisteredBefore && cronRegisteredAfter && socketAbsent && realCheckEquivalent,
|
|
617
617
|
schedulerReceipt,
|
|
618
|
+
acceptanceOnly: true,
|
|
619
|
+
productionScheduleChanged: false,
|
|
618
620
|
};
|
|
619
621
|
if (input.label === "unit-16f-cron-fingerprint")
|
|
620
622
|
receipt.productionRestored = cronFingerprintBefore === cronFingerprintAfter
|