@ouro.bot/cli 0.1.0-alpha.733 → 0.1.0-alpha.735
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 +34 -0
- package/deploy/unraid/Dockerfile +27 -0
- package/deploy/unraid/README.txt +1605 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +705 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1777 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +502 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
- package/deploy/unraid/sanctuary.xml +25 -0
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +168 -9
- package/dist/heart/auth/auth-flow.js +17 -1
- package/dist/heart/autonomy-budget.js +44 -1
- package/dist/heart/awaiting/await-scheduler.js +6 -2
- package/dist/heart/config.js +8 -0
- package/dist/heart/core.js +117 -132
- package/dist/heart/daemon/cli-exec.js +73 -1
- package/dist/heart/daemon/cli-help.js +5 -0
- package/dist/heart/daemon/cli-parse.js +6 -4
- package/dist/heart/daemon/container-credential-bootstrap.js +208 -0
- package/dist/heart/daemon/container-healthcheck.js +94 -0
- package/dist/heart/daemon/container-runtime.js +84 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
- package/dist/heart/daemon/container-spec-auditor.js +156 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
- package/dist/heart/daemon/daemon-entry.js +116 -28
- package/dist/heart/daemon/daemon-health.js +10 -0
- package/dist/heart/daemon/daemon.js +92 -5
- package/dist/heart/daemon/runtime-logging.js +6 -0
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
- package/dist/heart/daemon/sense-manager.js +25 -6
- package/dist/heart/daemon/supercronic-supervisor.js +267 -0
- package/dist/heart/habits/habit-scheduler.js +7 -3
- package/dist/heart/identity.js +13 -3
- package/dist/heart/provider-credentials.js +9 -1
- package/dist/heart/provider-models.js +8 -0
- package/dist/heart/provider-ping.js +5 -2
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/runtime-credentials.js +217 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/tool-approval.js +1 -0
- package/dist/heart/turn-context.js +8 -0
- package/dist/mind/prompt.js +9 -0
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/index.js +75 -9
- package/dist/nerves/runtime.js +12 -0
- package/dist/repertoire/tool-arguments.js +39 -0
- package/dist/repertoire/tools-unraid.js +353 -0
- package/dist/repertoire/tools.js +19 -1
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +130 -0
- package/dist/senses/private-runtime-worker.js +3 -2
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
- package/dist/senses/sanctuary-health-runner.js +146 -0
- package/dist/senses/sanctuary-health.js +447 -0
- package/dist/senses/sanctuary-interactive-control.js +306 -0
- package/dist/senses/sanctuary-runtime.js +206 -0
- package/dist/senses/shared-turn.js +11 -3
- package/dist/senses/telegram-approval-runtime.js +526 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-client.js +1471 -0
- package/dist/senses/telegram-entry.js +80 -0
- package/dist/senses/telegram.js +1232 -0
- package/npm-shrinkwrap.json +6558 -0
- package/package.json +4 -2
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.telegramApprovalCommitBarrierHooks = telegramApprovalCommitBarrierHooks;
|
|
37
|
+
exports.telegramApprovalDecisionBarrierHooks = telegramApprovalDecisionBarrierHooks;
|
|
38
|
+
exports.approvalContinuationRunAgentOptions = approvalContinuationRunAgentOptions;
|
|
39
|
+
exports.executeApprovedTelegramTool = executeApprovedTelegramTool;
|
|
40
|
+
exports.createTelegramApprovalRuntime = createTelegramApprovalRuntime;
|
|
41
|
+
const path = __importStar(require("node:path"));
|
|
42
|
+
const node_crypto_1 = require("node:crypto");
|
|
43
|
+
const approval_files_1 = require("../heart/approval-files");
|
|
44
|
+
const approval_store_1 = require("../heart/approval-store");
|
|
45
|
+
const tool_approval_1 = require("../heart/tool-approval");
|
|
46
|
+
const core_1 = require("../heart/core");
|
|
47
|
+
const identity_1 = require("../heart/identity");
|
|
48
|
+
const sanctuary_acceptance_marker_1 = require("../heart/daemon/sanctuary-acceptance-marker");
|
|
49
|
+
const telegram_1 = require("./telegram");
|
|
50
|
+
const context_1 = require("../mind/context");
|
|
51
|
+
const session_transaction_1 = require("../mind/session-transaction");
|
|
52
|
+
const tools_1 = require("../repertoire/tools");
|
|
53
|
+
const runtime_1 = require("../nerves/runtime");
|
|
54
|
+
const telegram_client_1 = require("./telegram-client");
|
|
55
|
+
function telegramApprovalCommitBarrierHooks(effectBarrier) {
|
|
56
|
+
return {
|
|
57
|
+
afterJournalPrepare: effectBarrier,
|
|
58
|
+
afterTokenPersist: effectBarrier,
|
|
59
|
+
afterCheckpointWrite: effectBarrier,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function telegramApprovalDecisionBarrierHooks(effectBarrier) {
|
|
63
|
+
return {
|
|
64
|
+
afterClaim: effectBarrier,
|
|
65
|
+
afterAttempt: effectBarrier,
|
|
66
|
+
afterHandler: effectBarrier,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function approvalContinuationRunAgentOptions(toolContext, approvalCoordinator) {
|
|
70
|
+
return { toolContext: toolContext, approvalCoordinator };
|
|
71
|
+
}
|
|
72
|
+
async function executeApprovedTelegramTool(name, args, execute, scenarioHandleDigest, approvalId, effectBarrier = () => undefined) {
|
|
73
|
+
if (name === "unraid_restart_container")
|
|
74
|
+
(0, runtime_1.emitNervesEvent)({
|
|
75
|
+
component: "senses",
|
|
76
|
+
event: "senses.telegram_approved_restart_start",
|
|
77
|
+
message: "approved Sanctuary restart execution started",
|
|
78
|
+
meta: { ...(scenarioHandleDigest ? { scenarioHandleDigest } : {}), ...(approvalId ? { approvalId } : {}) },
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
effectBarrier();
|
|
82
|
+
const result = await execute(name, args);
|
|
83
|
+
if (name !== "unraid_restart_container")
|
|
84
|
+
return result;
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(result);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new tool_approval_1.ApprovalExecutionFailedError("approved restart returned an invalid result");
|
|
91
|
+
}
|
|
92
|
+
if (!parsed || typeof parsed !== "object" || !("ok" in parsed)) {
|
|
93
|
+
throw new tool_approval_1.ApprovalExecutionFailedError("approved restart returned an invalid result");
|
|
94
|
+
}
|
|
95
|
+
if (parsed.ok !== true) {
|
|
96
|
+
const error = parsed.error;
|
|
97
|
+
const message = error && typeof error === "object" && typeof error.message === "string"
|
|
98
|
+
? error.message.slice(0, 240)
|
|
99
|
+
: "approved restart failed";
|
|
100
|
+
throw new tool_approval_1.ApprovalExecutionFailedError(message);
|
|
101
|
+
}
|
|
102
|
+
const data = parsed.data;
|
|
103
|
+
const container = data && typeof data === "object" ? data.container : null;
|
|
104
|
+
const validSuccess = data !== null
|
|
105
|
+
&& typeof data === "object"
|
|
106
|
+
&& container !== null
|
|
107
|
+
&& typeof container === "object"
|
|
108
|
+
&& typeof container.id === "string"
|
|
109
|
+
&& container.id.length > 0
|
|
110
|
+
&& typeof container.name === "string"
|
|
111
|
+
&& container.name.length > 0
|
|
112
|
+
&& typeof data.beforeState === "string"
|
|
113
|
+
&& typeof data.afterState === "string"
|
|
114
|
+
&& data.observedRestart === true
|
|
115
|
+
&& data.degraded === false;
|
|
116
|
+
if (!validSuccess) {
|
|
117
|
+
throw new tool_approval_1.ApprovalExecutionFailedError("approved restart returned an invalid result");
|
|
118
|
+
}
|
|
119
|
+
(0, runtime_1.emitNervesEvent)({
|
|
120
|
+
component: "senses",
|
|
121
|
+
event: "senses.telegram_approved_restart_end",
|
|
122
|
+
message: "approved Sanctuary restart execution completed",
|
|
123
|
+
meta: { ...(scenarioHandleDigest ? { scenarioHandleDigest } : {}), ...(approvalId ? { approvalId } : {}), observedRestart: true },
|
|
124
|
+
});
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (name === "unraid_restart_container")
|
|
129
|
+
(0, runtime_1.emitNervesEvent)({
|
|
130
|
+
level: "error",
|
|
131
|
+
component: "senses",
|
|
132
|
+
event: "senses.telegram_approved_restart_error",
|
|
133
|
+
message: "approved Sanctuary restart execution failed",
|
|
134
|
+
meta: { ...(scenarioHandleDigest ? { scenarioHandleDigest } : {}), ...(approvalId ? { approvalId } : {}), reason: error instanceof Error ? error.message.slice(0, 240) : "unknown" },
|
|
135
|
+
});
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function opaqueTelegramMessageBinding(subject, messageId) {
|
|
140
|
+
return `tgm_${(0, node_crypto_1.createHmac)("sha256", subject).update(`message:${messageId}`, "utf8").digest("base64url")}`;
|
|
141
|
+
}
|
|
142
|
+
function createTelegramApprovalRuntime(options) {
|
|
143
|
+
const effectBarrier = options.effectBarrier ?? (() => undefined);
|
|
144
|
+
(0, runtime_1.emitNervesEvent)({
|
|
145
|
+
component: "senses",
|
|
146
|
+
event: "senses.telegram_approval_runtime_create",
|
|
147
|
+
message: "creating durable Telegram approval runtime",
|
|
148
|
+
meta: { agentName: options.agentName },
|
|
149
|
+
});
|
|
150
|
+
const now = options.dependencies?.now ?? Date.now;
|
|
151
|
+
const acceptanceMarker = options.dependencies?.acceptanceMarker ?? (() => (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(options.agentName));
|
|
152
|
+
const provider = options.dependencies?.runProvider ?? core_1.runAgent;
|
|
153
|
+
const resolveTool = options.dependencies?.resolveTool ?? tools_1.resolveToolDefinition;
|
|
154
|
+
const executeTool = options.dependencies?.executeTool ?? tools_1.execTool;
|
|
155
|
+
const stateRoot = path.join(options.dependencies?.agentRoot ?? (0, identity_1.getAgentRoot)(options.agentName), "state", "approvals");
|
|
156
|
+
const store = (0, approval_store_1.openApprovalStore)({ databasePath: path.join(stateRoot, "approvals.sqlite"), now: () => new Date(now()) });
|
|
157
|
+
const checkpoints = new approval_files_1.FileApprovalCheckpointStore(path.join(stateRoot, "checkpoints.json"));
|
|
158
|
+
const tokens = new approval_files_1.FileApprovalTokenStore(path.join(stateRoot, "tokens.json"));
|
|
159
|
+
const pendingStore = new telegram_client_1.FileTelegramPendingApprovalStore(path.join(stateRoot, "telegram-pending.json"));
|
|
160
|
+
let transport;
|
|
161
|
+
const commitAcceptanceEvidence = options.dependencies?.commitAcceptanceEvidence ?? (async (event, meta) => {
|
|
162
|
+
if (event === "telegram.callback_settled") {
|
|
163
|
+
await (0, runtime_1.emitNervesEventDurable)({
|
|
164
|
+
component: "senses",
|
|
165
|
+
event: "telegram.callback_settled",
|
|
166
|
+
message: "Telegram approval acceptance evidence durably recorded",
|
|
167
|
+
meta,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else if (event === "telegram.callback_recovery_settled") {
|
|
171
|
+
await (0, runtime_1.emitNervesEventDurable)({
|
|
172
|
+
component: "senses",
|
|
173
|
+
event: "telegram.callback_recovery_settled",
|
|
174
|
+
message: "Telegram approval acceptance evidence durably recorded",
|
|
175
|
+
meta,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
throw new Error("Telegram durable acceptance settlement event is unsupported");
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
const coordinator = (context) => ({
|
|
183
|
+
propose: async (request) => {
|
|
184
|
+
if (request.toolCall.type !== "function")
|
|
185
|
+
throw new Error("approval requires a function tool call");
|
|
186
|
+
effectBarrier();
|
|
187
|
+
const scenarioHandleDigest = acceptanceMarker()?.scenarioHandleDigest;
|
|
188
|
+
const committed = (0, tool_approval_1.commitApprovalProposal)({
|
|
189
|
+
approvalStore: store,
|
|
190
|
+
checkpointStore: checkpoints,
|
|
191
|
+
tokenStore: tokens,
|
|
192
|
+
proposal: {
|
|
193
|
+
toolCallId: request.toolCall.id,
|
|
194
|
+
toolName: request.toolCall.function.name,
|
|
195
|
+
arguments: request.arguments,
|
|
196
|
+
schemaDigest: request.schemaDigest,
|
|
197
|
+
toolDigest: request.toolDigest,
|
|
198
|
+
policyDigest: request.policyDigest,
|
|
199
|
+
policyId: request.policyId,
|
|
200
|
+
sessionKey: `telegram:${options.subject}`,
|
|
201
|
+
sessionPath: context.sessionPath,
|
|
202
|
+
baseSessionRevision: context.baseSessionRevision,
|
|
203
|
+
checkpointDigest: "0".repeat(64),
|
|
204
|
+
requesterId: options.subject,
|
|
205
|
+
transport: "telegram",
|
|
206
|
+
transportUserId: options.subject,
|
|
207
|
+
transportChatId: options.subject,
|
|
208
|
+
expiresAt: new Date(now() + 300_000).toISOString(),
|
|
209
|
+
frozenAssistantMessage: request.frozenAssistantMessage,
|
|
210
|
+
...(scenarioHandleDigest ? { scenarioHandleDigest } : {}),
|
|
211
|
+
},
|
|
212
|
+
preCallMessages: request.preCallMessages,
|
|
213
|
+
hooks: telegramApprovalCommitBarrierHooks(effectBarrier),
|
|
214
|
+
});
|
|
215
|
+
const prompt = `Approve ${request.toolCall.function.name} with exact arguments ${JSON.stringify(request.arguments)}?`;
|
|
216
|
+
effectBarrier();
|
|
217
|
+
const actionDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ toolName: committed.record.toolName, argumentDigest: committed.record.argumentDigest })).digest("hex");
|
|
218
|
+
const targetDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: committed.record.arguments.container })).digest("hex");
|
|
219
|
+
const sent = await transport.sendApproval({
|
|
220
|
+
approvalId: committed.record.approvalId,
|
|
221
|
+
decisionToken: committed.decisionToken,
|
|
222
|
+
prompt,
|
|
223
|
+
...(scenarioHandleDigest ? { acceptanceBinding: {
|
|
224
|
+
scenarioHandleDigest,
|
|
225
|
+
actionDigest,
|
|
226
|
+
targetDigest,
|
|
227
|
+
checkpointDigest: committed.record.checkpointDigest,
|
|
228
|
+
suspendedSessionRevisionDigest: (0, node_crypto_1.createHash)("sha256").update(committed.record.suspendedSessionRevision, "utf8").digest("hex"),
|
|
229
|
+
} } : {}),
|
|
230
|
+
});
|
|
231
|
+
effectBarrier();
|
|
232
|
+
store.bindPrompt({
|
|
233
|
+
approvalId: committed.record.approvalId,
|
|
234
|
+
transport: "telegram",
|
|
235
|
+
transportChatId: options.subject,
|
|
236
|
+
transportMessageId: opaqueTelegramMessageBinding(options.subject, sent.messageId),
|
|
237
|
+
expiresAt: new Date(sent.expiresAt).toISOString(),
|
|
238
|
+
});
|
|
239
|
+
(0, runtime_1.emitNervesEvent)({
|
|
240
|
+
component: "senses",
|
|
241
|
+
event: "senses.telegram_approval_proposed",
|
|
242
|
+
message: "Telegram approval proposal was durably bound",
|
|
243
|
+
meta: { ...(scenarioHandleDigest ? { scenarioHandleDigest } : {}), toolName: request.toolCall.function.name },
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
approvalId: committed.record.approvalId,
|
|
247
|
+
checkpointDigest: committed.record.checkpointDigest,
|
|
248
|
+
suspendedSessionRevision: committed.record.suspendedSessionRevision,
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
const terminalOutcome = (record) => {
|
|
253
|
+
const accepted = record.state === "succeeded";
|
|
254
|
+
return {
|
|
255
|
+
accepted,
|
|
256
|
+
terminalText: accepted
|
|
257
|
+
? "✅ Approved — action completed"
|
|
258
|
+
: record.state === "denied"
|
|
259
|
+
? "❌ Denied — no action taken"
|
|
260
|
+
: record.state === "attempted_indeterminate"
|
|
261
|
+
? "⚠️ Action outcome is indeterminate after restart — it was not retried"
|
|
262
|
+
: "⚠️ Approval did not complete",
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
const continueTerminalRecord = (record, acceptanceBinding) => {
|
|
266
|
+
return (0, session_transaction_1.withSessionTurnLease)(record.sessionPath, async (lease) => {
|
|
267
|
+
const checkpoint = checkpoints.read(record.approvalId);
|
|
268
|
+
if (!checkpoint)
|
|
269
|
+
return { accepted: false, terminalText: "⚠️ Approval checkpoint is unavailable" };
|
|
270
|
+
const continuationOwnerId = `telegram-continuation-${(0, node_crypto_1.randomUUID)()}`;
|
|
271
|
+
let continuationEpoch = 0;
|
|
272
|
+
const continuationCoordinator = {
|
|
273
|
+
propose: (request) => coordinator({
|
|
274
|
+
sessionPath: record.sessionPath,
|
|
275
|
+
baseSessionRevision: (0, session_transaction_1.readSessionTransaction)(record.sessionPath, lease).revision,
|
|
276
|
+
}).propose(request),
|
|
277
|
+
};
|
|
278
|
+
effectBarrier();
|
|
279
|
+
await (0, core_1.resumeApprovalContinuation)({
|
|
280
|
+
record,
|
|
281
|
+
checkpoint,
|
|
282
|
+
currentSessionRevision: (0, session_transaction_1.readSessionTransaction)(record.sessionPath, lease).revision,
|
|
283
|
+
sessionMessages: checkpoint.preCallMessages,
|
|
284
|
+
callbacks: {},
|
|
285
|
+
channel: "telegram",
|
|
286
|
+
claimContinuation: () => {
|
|
287
|
+
effectBarrier();
|
|
288
|
+
const claim = store.claimContinuation({ approvalId: record.approvalId, ownerId: continuationOwnerId });
|
|
289
|
+
continuationEpoch = claim.record.continuationEpoch;
|
|
290
|
+
return claim;
|
|
291
|
+
},
|
|
292
|
+
markContinuationMaterialized: () => { effectBarrier(); store.markContinuationMaterialized({ approvalId: record.approvalId, ownerId: continuationOwnerId, epoch: continuationEpoch }); },
|
|
293
|
+
markContinuationAttempted: () => { effectBarrier(); store.markContinuationAttempted({ approvalId: record.approvalId, ownerId: continuationOwnerId, epoch: continuationEpoch }); },
|
|
294
|
+
completeContinuation: () => { effectBarrier(); store.completeContinuation({ approvalId: record.approvalId, ownerId: continuationOwnerId, epoch: continuationEpoch }); },
|
|
295
|
+
runAgent: provider,
|
|
296
|
+
runAgentOptions: approvalContinuationRunAgentOptions(options.toolContext, continuationCoordinator),
|
|
297
|
+
persist: (messages, result) => { effectBarrier(); (0, context_1.saveSession)(record.sessionPath, messages, result?.usage, undefined, lease); },
|
|
298
|
+
deliver: async (text) => {
|
|
299
|
+
effectBarrier();
|
|
300
|
+
const messageIds = await (0, telegram_client_1.sendTelegramText)(options.api, options.authorizedChatId, text);
|
|
301
|
+
effectBarrier();
|
|
302
|
+
if (acceptanceBinding) {
|
|
303
|
+
const unsigned = {
|
|
304
|
+
approvalId: record.approvalId,
|
|
305
|
+
...acceptanceBinding,
|
|
306
|
+
deliveredAt: now(),
|
|
307
|
+
resultDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ state: record.state, result: record.result })).digest("hex"),
|
|
308
|
+
deliveryDigest: (0, node_crypto_1.createHash)("sha256").update(text, "utf8").digest("hex"),
|
|
309
|
+
deliveryMessageIdDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(messageIds ?? [])).digest("hex"),
|
|
310
|
+
};
|
|
311
|
+
(0, runtime_1.emitNervesEvent)({
|
|
312
|
+
component: "senses",
|
|
313
|
+
event: "senses.telegram_approval_continuation_delivered",
|
|
314
|
+
message: "Telegram approval continuation result was delivered",
|
|
315
|
+
meta: { ...unsigned, evidenceMac: (0, telegram_1.sanctuaryTelegramApprovalEvidenceMac)(options.identityKey, "senses.telegram_approval_continuation_delivered", unsigned) },
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
return terminalOutcome(record);
|
|
321
|
+
});
|
|
322
|
+
};
|
|
323
|
+
transport = (0, telegram_client_1.createTelegramApprovalTransport)({
|
|
324
|
+
api: options.api,
|
|
325
|
+
expectedUserId: options.authorizedUserId,
|
|
326
|
+
expectedChatId: options.authorizedChatId,
|
|
327
|
+
pendingStore,
|
|
328
|
+
createOpaqueHandle: () => (0, node_crypto_1.randomBytes)(12).toString("base64url"),
|
|
329
|
+
acceptanceEventMeta: () => {
|
|
330
|
+
const scenarioHandleDigest = acceptanceMarker()?.scenarioHandleDigest;
|
|
331
|
+
const meta = scenarioHandleDigest ? { scenarioHandleDigest } : {};
|
|
332
|
+
return meta;
|
|
333
|
+
},
|
|
334
|
+
effectBarrier,
|
|
335
|
+
signAcceptanceEvidence: (event, meta) => (0, telegram_1.sanctuaryTelegramApprovalEvidenceMac)(options.identityKey, event, meta),
|
|
336
|
+
commitAcceptanceEvidence,
|
|
337
|
+
onSettlementComplete: async (approvalId) => { tokens.remove(approvalId); },
|
|
338
|
+
acceptanceMessageIdDigest: (messageId) => (0, node_crypto_1.createHash)("sha256").update(opaqueTelegramMessageBinding(options.subject, messageId), "utf8").digest("hex"),
|
|
339
|
+
now,
|
|
340
|
+
resolveDecisionToken: async (approvalId) => tokens.get(approvalId) ?? "",
|
|
341
|
+
onExpire: async (approvalId) => {
|
|
342
|
+
effectBarrier();
|
|
343
|
+
store.expire({ approvalId });
|
|
344
|
+
effectBarrier();
|
|
345
|
+
tokens.remove(approvalId);
|
|
346
|
+
},
|
|
347
|
+
onDecision: async (decision) => {
|
|
348
|
+
effectBarrier();
|
|
349
|
+
const decisionScenarioDigest = acceptanceMarker()?.scenarioHandleDigest;
|
|
350
|
+
const existing = store.read(decision.approvalId);
|
|
351
|
+
if (!existing)
|
|
352
|
+
return { accepted: false, terminalText: "⚠️ Approval is no longer valid" };
|
|
353
|
+
let record;
|
|
354
|
+
if (existing.state === "claimed") {
|
|
355
|
+
effectBarrier();
|
|
356
|
+
record = (0, tool_approval_1.recoverClaimedApproval)({ approvalStore: store, approvalId: existing.approvalId, reason: "decision interrupted before action attempt; action was not executed" });
|
|
357
|
+
}
|
|
358
|
+
else if (existing.state === "attempted") {
|
|
359
|
+
effectBarrier();
|
|
360
|
+
record = (0, tool_approval_1.recoverAttemptedApproval)({ approvalStore: store, approvalId: existing.approvalId });
|
|
361
|
+
}
|
|
362
|
+
else if (existing.state === "proposed") {
|
|
363
|
+
const ownerId = `telegram-decision-${(0, node_crypto_1.randomUUID)()}`;
|
|
364
|
+
record = await (0, session_transaction_1.withSessionTurnLease)(existing.sessionPath, async (lease) => (0, tool_approval_1.executeApprovalDecision)({
|
|
365
|
+
approvalStore: store,
|
|
366
|
+
checkpointStore: checkpoints,
|
|
367
|
+
decision: {
|
|
368
|
+
...decision,
|
|
369
|
+
requesterId: options.subject,
|
|
370
|
+
transportUserId: options.subject,
|
|
371
|
+
transportChatId: options.subject,
|
|
372
|
+
transportMessageId: opaqueTelegramMessageBinding(options.subject, decision.transportMessageId),
|
|
373
|
+
sessionKey: existing.sessionKey,
|
|
374
|
+
},
|
|
375
|
+
ownerId,
|
|
376
|
+
currentSessionRevision: (0, session_transaction_1.readSessionTransaction)(existing.sessionPath, lease).revision,
|
|
377
|
+
resolveTool,
|
|
378
|
+
liveGuard: async () => ({ ok: true }),
|
|
379
|
+
liveRisk: async () => ({ ok: true }),
|
|
380
|
+
hooks: telegramApprovalDecisionBarrierHooks(effectBarrier),
|
|
381
|
+
execute: (name, args) => {
|
|
382
|
+
const execute = () => executeApprovedTelegramTool(name, args, (toolName, toolArgs) => executeTool(toolName, toolArgs, options.toolContext), decisionScenarioDigest, existing.approvalId, effectBarrier);
|
|
383
|
+
return decisionScenarioDigest
|
|
384
|
+
? (0, sanctuary_acceptance_marker_1.runWithSanctuaryAcceptanceApproval)({ approvalId: existing.approvalId, argumentDigest: existing.argumentDigest }, execute)
|
|
385
|
+
: execute();
|
|
386
|
+
},
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
389
|
+
else if (["succeeded", "failed", "attempted_indeterminate", "denied", "expired", "drifted", "session_head_changed", "abandoned_before_attempt"].includes(existing.state)) {
|
|
390
|
+
record = existing;
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
return { accepted: false, terminalText: "⚠️ Approval is not recoverable" };
|
|
394
|
+
}
|
|
395
|
+
(0, runtime_1.emitNervesEvent)({
|
|
396
|
+
component: "senses",
|
|
397
|
+
event: "senses.telegram_approval_terminal",
|
|
398
|
+
message: "Telegram approval reached a terminal decision state",
|
|
399
|
+
meta: { state: record.state, ...(decisionScenarioDigest ? { scenarioHandleDigest: decisionScenarioDigest } : {}) },
|
|
400
|
+
});
|
|
401
|
+
return continueTerminalRecord(record, decision.acceptanceBinding);
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
const recover = async () => {
|
|
405
|
+
let failureCount = 0;
|
|
406
|
+
let fencedFailure;
|
|
407
|
+
for (const pending of transport.listPendingDeliveries()) {
|
|
408
|
+
let persistedState;
|
|
409
|
+
try {
|
|
410
|
+
persistedState = (0, telegram_client_1.classifyTelegramPersistedApprovalState)(pending);
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
failureCount += 1;
|
|
414
|
+
fencedFailure ??= error;
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const mustFailClosed = persistedState === "decision_attempt" || persistedState === "action_terminal";
|
|
418
|
+
try {
|
|
419
|
+
const existing = store.read(pending.approvalId);
|
|
420
|
+
if (!existing) {
|
|
421
|
+
if (mustFailClosed)
|
|
422
|
+
throw new Error("Telegram fenced approval journal is unavailable");
|
|
423
|
+
const orphanRecovery = await transport.terminalizeOrphaned(pending.approvalId, "⚠️ Approval record is unavailable — no action was taken");
|
|
424
|
+
(0, runtime_1.emitNervesEvent)({
|
|
425
|
+
component: "senses",
|
|
426
|
+
event: "senses.telegram_approval_orphan_recovered",
|
|
427
|
+
message: "orphaned Telegram approval transport state was removed",
|
|
428
|
+
meta: {
|
|
429
|
+
agentName: options.agentName,
|
|
430
|
+
recovery: "missing_journal",
|
|
431
|
+
terminalEditSucceeded: orphanRecovery.terminalEditSucceeded,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (mustFailClosed) {
|
|
437
|
+
await transport.recoverDecisionAttempt(pending.approvalId);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (persistedState === "expiry_observed") {
|
|
441
|
+
await transport.reconcileExpired();
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (persistedState === "delivery_interruption") {
|
|
445
|
+
await transport.terminalizeRecovered(pending.approvalId, pending.terminal.terminalText);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const deliveryState = pending.deliveryState ?? "bound";
|
|
449
|
+
if (existing.state === "awaiting_prompt_binding" && deliveryState !== "bound") {
|
|
450
|
+
effectBarrier();
|
|
451
|
+
const record = store.abandonPromptBinding({
|
|
452
|
+
approvalId: existing.approvalId,
|
|
453
|
+
reason: deliveryState === "pending"
|
|
454
|
+
? "approval prompt was interrupted before delivery; action was not executed"
|
|
455
|
+
: "approval prompt delivery was indeterminate; action was not executed",
|
|
456
|
+
});
|
|
457
|
+
const outcome = await continueTerminalRecord(record);
|
|
458
|
+
await transport.terminalizeRecovered(record.approvalId, outcome.terminalText);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (existing.state === "awaiting_prompt_binding" && deliveryState === "bound" && pending.messageId) {
|
|
462
|
+
effectBarrier();
|
|
463
|
+
store.bindPrompt({
|
|
464
|
+
approvalId: existing.approvalId,
|
|
465
|
+
transport: "telegram",
|
|
466
|
+
transportChatId: options.subject,
|
|
467
|
+
transportMessageId: opaqueTelegramMessageBinding(options.subject, pending.messageId),
|
|
468
|
+
});
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (existing.state === "proposed" || existing.state === "preparing" || existing.state === "awaiting_prompt_binding")
|
|
472
|
+
continue;
|
|
473
|
+
if (existing.state === "expired" && !pending.terminal) {
|
|
474
|
+
await transport.reconcileExpired();
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
let record = existing;
|
|
478
|
+
if (record.state === "claimed") {
|
|
479
|
+
effectBarrier();
|
|
480
|
+
record = (0, tool_approval_1.recoverClaimedApproval)({ approvalStore: store, approvalId: record.approvalId, reason: "decision interrupted before action attempt; action was not executed" });
|
|
481
|
+
}
|
|
482
|
+
else if (record.state === "attempted") {
|
|
483
|
+
effectBarrier();
|
|
484
|
+
record = (0, tool_approval_1.recoverAttemptedApproval)({ approvalStore: store, approvalId: record.approvalId });
|
|
485
|
+
}
|
|
486
|
+
const outcome = await continueTerminalRecord(record);
|
|
487
|
+
await transport.terminalizeRecovered(record.approvalId, outcome.terminalText);
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
failureCount += 1;
|
|
491
|
+
if (mustFailClosed)
|
|
492
|
+
fencedFailure ??= error;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (failureCount > 0) {
|
|
496
|
+
(0, runtime_1.emitNervesEvent)({
|
|
497
|
+
level: "error",
|
|
498
|
+
component: "senses",
|
|
499
|
+
event: "senses.telegram_approval_recovery_error",
|
|
500
|
+
message: "Telegram approval startup recovery completed with isolated failures",
|
|
501
|
+
meta: { failureCount },
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
if (fencedFailure !== undefined)
|
|
505
|
+
throw fencedFailure;
|
|
506
|
+
};
|
|
507
|
+
const legacySubjects = () => (store.listTelegramIdentitySubjects?.() ?? [])
|
|
508
|
+
.filter((candidate) => candidate !== options.subject);
|
|
509
|
+
const migrateIdentity = (subjects) => {
|
|
510
|
+
for (const legacySubject of subjects) {
|
|
511
|
+
effectBarrier();
|
|
512
|
+
store.migrateTelegramIdentity?.({
|
|
513
|
+
legacyUserId: legacySubject,
|
|
514
|
+
legacyChatId: legacySubject,
|
|
515
|
+
subject: options.subject,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
effectBarrier();
|
|
519
|
+
store.migrateTelegramIdentity?.({
|
|
520
|
+
legacyUserId: options.authorizedUserId,
|
|
521
|
+
legacyChatId: options.authorizedChatId,
|
|
522
|
+
subject: options.subject,
|
|
523
|
+
});
|
|
524
|
+
};
|
|
525
|
+
return { transport, coordinator, legacySubjects, migrateIdentity, recover, close: () => store.close() };
|
|
526
|
+
}
|