@ouro.bot/cli 0.1.0-alpha.815 → 0.1.0-alpha.816

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.
@@ -52,9 +52,11 @@ const sanctuary_acceptance_marker_1 = require("../heart/daemon/sanctuary-accepta
52
52
  const telegram_1 = require("./telegram");
53
53
  const context_1 = require("../mind/context");
54
54
  const session_transaction_1 = require("../mind/session-transaction");
55
+ const relationship_authorization_1 = require("../repertoire/relationship-authorization");
55
56
  const tools_1 = require("../repertoire/tools");
56
57
  const mcp_manager_1 = require("../repertoire/mcp-manager");
57
58
  const runtime_1 = require("../nerves/runtime");
59
+ const shared_turn_1 = require("./shared-turn");
58
60
  const telegram_client_1 = require("./telegram-client");
59
61
  function telegramApprovalCommitBarrierHooks(effectBarrier) {
60
62
  return {
@@ -190,11 +192,11 @@ function createTelegramApprovalRuntime(options) {
190
192
  const resolveTool = options.dependencies?.resolveTool ?? tools_1.resolveToolDefinition;
191
193
  const agentRoot = options.dependencies?.agentRoot ?? (0, identity_1.getAgentRoot)(options.agentName);
192
194
  const owner = Object.freeze({ agentName: options.agentName, agentRoot });
193
- const currentOptions = async (record) => {
195
+ const currentOptions = async (record, phase) => {
194
196
  try {
195
197
  if (!options.resolveLiveToolContext)
196
198
  throw new Error("current owner authority producer is unavailable");
197
- const context = await options.resolveLiveToolContext(record);
199
+ let context = await options.resolveLiveToolContext(record);
198
200
  const relationship = context.relationshipAuthorization;
199
201
  if (context.agentName !== owner.agentName || context.agentRoot !== owner.agentRoot
200
202
  || relationship?.profileId !== "sanctuary-owner" || relationship.actor?.trustLevel !== "family"
@@ -203,6 +205,26 @@ function createTelegramApprovalRuntime(options) {
203
205
  || context.currentSession.key !== record.sessionKey || context.currentSession.sessionPath !== record.sessionPath) {
204
206
  throw new Error("current approval owner coordinates are not exact");
205
207
  }
208
+ if (record.toolName === "unraid_restart_container") {
209
+ const restartOwner = await resolveRestartOwnerContext(record, context);
210
+ if (restartOwner.allowed) {
211
+ context = restartOwner.context;
212
+ }
213
+ else {
214
+ const { reason } = restartOwner;
215
+ (0, runtime_1.emitNervesEvent)({
216
+ level: "warn", component: "senses", event: "senses.telegram_approval_owner_denied",
217
+ message: "restart approval owner revalidation failed",
218
+ meta: { approvalId: record.approvalId, reason, phase },
219
+ });
220
+ if (phase === "decision")
221
+ return null;
222
+ context = {
223
+ ...context,
224
+ relationshipAuthorization: { authorizedContextScopes: [], advertisedToolNames: [], authorizeTool: () => ({ allowed: false, reason }) },
225
+ };
226
+ }
227
+ }
206
228
  const runtime = await (options.dependencies?.getProviderRuntime ?? core_1.getProviderRuntime)("human", owner);
207
229
  const mcpManager = await (options.dependencies?.getSharedMcpManager ?? mcp_manager_1.getSharedMcpManager)(owner) ?? undefined;
208
230
  const selectCurrentTools = () => (0, tools_1.selectToolsForChannel)((0, friends_1.getChannelCapabilities)("telegram"), context.context?.friend.toolPreferences, context.context, runtime.capabilities, mcpManager, runtime.model, context);
@@ -220,9 +242,10 @@ function createTelegramApprovalRuntime(options) {
220
242
  return null;
221
243
  }
222
244
  };
223
- const invocationContext = (current, definition) => ({
245
+ const invocationContext = (current, definition, restartApproval) => ({
224
246
  ...current.toolContext,
225
247
  toolSelection: Object.freeze({ ordinary: Object.freeze([definition]), engine: Object.freeze([]) }),
248
+ ...(restartApproval ? { restartApproval } : {}),
226
249
  });
227
250
  const stateRoot = path.join(agentRoot, "state", "approvals");
228
251
  const store = (0, approval_store_1.openApprovalStore)({ databasePath: path.join(stateRoot, "approvals.sqlite"), now: () => new Date(now()) });
@@ -230,6 +253,44 @@ function createTelegramApprovalRuntime(options) {
230
253
  const tokens = new approval_files_1.FileApprovalTokenStore(path.join(stateRoot, "tokens.json"));
231
254
  const pendingStore = new telegram_client_1.FileTelegramPendingApprovalStore(path.join(stateRoot, "telegram-pending.json"));
232
255
  let transport;
256
+ const ownerSessionMatches = (binding, sessionPath) => {
257
+ if (binding.sessionKey !== `telegram:${options.subject}`
258
+ || sessionPath !== (0, shared_turn_1.getSenseSessionPath)(options.agentName, binding.friendId, "telegram", binding.sessionKey, agentRoot))
259
+ return false;
260
+ const envelope = (0, session_events_1.loadSessionEnvelopeFile)(sessionPath);
261
+ const latestUser = envelope && (0, session_events_1.selectEffectiveSessionEvents)(envelope.events).findLast((event) => event.role === "user");
262
+ return latestUser?.id === binding.sessionEventId && latestUser.relations.references.includes(binding.requestId);
263
+ };
264
+ const resolveRestartOwnerContext = async (record, currentContext) => {
265
+ const resolve = options.resolveOwnerRelationship;
266
+ const binding = record.ownerBinding && Object.freeze({ ...record.ownerBinding });
267
+ if (!binding || !resolve || (options.toolContext.agentRoot ?? agentRoot) !== agentRoot || record.sessionKey !== binding.sessionKey) {
268
+ return { allowed: false, reason: "restart approval owner binding is unavailable" };
269
+ }
270
+ try {
271
+ if (!ownerSessionMatches(binding, record.sessionPath))
272
+ return { allowed: false, reason: "restart approval owner session changed" };
273
+ const relationship = await resolve(binding);
274
+ const context = {
275
+ ...currentContext,
276
+ relationshipAuthorization: {
277
+ ...relationship,
278
+ requestId: binding.requestId,
279
+ authorizeTool: async (name, args) => {
280
+ const current = await resolve(binding);
281
+ if (!ownerSessionMatches(binding, record.sessionPath))
282
+ return { allowed: false, reason: "restart approval owner session changed" };
283
+ return current.authorizeTool(name, args);
284
+ },
285
+ },
286
+ };
287
+ const authorization = await (0, relationship_authorization_1.authorizeRestartApprovalRequester)(context, record.arguments, binding);
288
+ return authorization.allowed ? { allowed: true, context } : authorization;
289
+ }
290
+ catch {
291
+ return { allowed: false, reason: "restart approval owner authorization is unavailable" };
292
+ }
293
+ };
233
294
  const commitAcceptanceEvidence = options.dependencies?.commitAcceptanceEvidence ?? (async (event, meta) => {
234
295
  if (event === "telegram.callback_settled") {
235
296
  await (0, runtime_1.emitNervesEventDurable)({
@@ -256,6 +317,25 @@ function createTelegramApprovalRuntime(options) {
256
317
  if (request.toolCall.type !== "function")
257
318
  throw new Error("approval requires a function tool call");
258
319
  effectBarrier();
320
+ let ownerBinding;
321
+ if (request.toolCall.function.name === "unraid_restart_container") {
322
+ const live = request.liveToolContext;
323
+ const authorization = await (0, relationship_authorization_1.authorizeRoutineActionRequester)(live, request.arguments);
324
+ if (!authorization.allowed || authorization.requester.kind !== "owner"
325
+ || live?.agentRoot !== agentRoot || live.currentSession?.sessionPath !== context.sessionPath) {
326
+ throw new Error("restart approval requires a current owner request");
327
+ }
328
+ const requester = authorization.requester;
329
+ ownerBinding = {
330
+ friendId: requester.friendId, requestId: requester.requestId, sessionEventId: requester.sessionEventId,
331
+ sessionKey: requester.origin.key, profileVersion: authorization.profileVersion,
332
+ };
333
+ if (!ownerSessionMatches(ownerBinding, context.sessionPath))
334
+ throw new Error("restart approval owner session changed");
335
+ const policy = await (0, tools_1.approvalPolicyForInvocation)("unraid_restart_container", request.arguments, live);
336
+ if (policy.kind !== "required")
337
+ throw new Error("restart approval no longer has owner fallback");
338
+ }
259
339
  const scenarioHandleDigest = acceptanceMarker()?.scenarioHandleDigest;
260
340
  const committed = (0, tool_approval_1.commitApprovalProposal)({
261
341
  approvalStore: store,
@@ -279,6 +359,7 @@ function createTelegramApprovalRuntime(options) {
279
359
  transportChatId: options.subject,
280
360
  expiresAt: new Date(now() + 300_000).toISOString(),
281
361
  frozenAssistantMessage: request.frozenAssistantMessage,
362
+ ...(ownerBinding ? { ownerBinding } : {}),
282
363
  ...(scenarioHandleDigest ? { scenarioHandleDigest } : {}),
283
364
  },
284
365
  preCallMessages: request.preCallMessages,
@@ -369,7 +450,7 @@ function createTelegramApprovalRuntime(options) {
369
450
  completeContinuation: () => { effectBarrier(); store.completeContinuation({ approvalId: record.approvalId, ownerId: continuationOwnerId, epoch: continuationEpoch }); },
370
451
  runAgent: provider,
371
452
  revalidate: async () => {
372
- const current = await currentOptions(record);
453
+ const current = await currentOptions(record, "continuation");
373
454
  continuationAuthorized = current !== null;
374
455
  return current ? { ...current, ...approvalContinuationRunAgentOptions(current.toolContext, continuationCoordinator) } : null;
375
456
  },
@@ -457,6 +538,7 @@ function createTelegramApprovalRuntime(options) {
457
538
  }
458
539
  else if (existing.state === "proposed") {
459
540
  const ownerId = `telegram-decision-${(0, node_crypto_1.randomUUID)()}`;
541
+ let restartApproval;
460
542
  let definition;
461
543
  record = await (0, session_transaction_1.withSessionTurnLease)(existing.sessionPath, async (lease) => (0, tool_approval_1.executeApprovalDecision)({
462
544
  approvalStore: store,
@@ -470,31 +552,67 @@ function createTelegramApprovalRuntime(options) {
470
552
  sessionKey: existing.sessionKey,
471
553
  },
472
554
  ownerId,
473
- currentSessionRevision: (0, session_transaction_1.readSessionTransaction)(existing.sessionPath, lease).revision,
555
+ currentSessionRevision: () => (0, session_transaction_1.readSessionTransaction)(existing.sessionPath, lease).revision,
474
556
  resolveTool: async (name) => {
475
- const current = await currentOptions(existing);
557
+ const current = await currentOptions(existing, "decision");
476
558
  definition = current ? resolveTool(name, current.toolContext.toolSelection) : undefined;
477
559
  return definition;
478
560
  },
479
561
  resolveApprovalPolicy: async (name, args) => {
480
- const current = await currentOptions(existing);
562
+ const current = await currentOptions(existing, "decision");
481
563
  return current ? (0, tools_1.approvalPolicyForInvocation)(name, args, current.toolContext) : { kind: "not_required" };
482
564
  },
483
- liveGuard: async () => ({ ok: true }),
565
+ liveGuard: async (context) => {
566
+ if (existing.toolName !== "unraid_restart_container")
567
+ return { ok: true };
568
+ const current = await currentOptions(existing, "decision");
569
+ if (!current || !existing.ownerBinding)
570
+ return { ok: false, reason: "restart approval owner binding is unavailable" };
571
+ const restartToolContext = current.toolContext;
572
+ const authorization = await (0, relationship_authorization_1.authorizeRestartApprovalRequester)(restartToolContext, context.arguments, existing.ownerBinding);
573
+ if (!authorization.allowed)
574
+ return { ok: false, reason: authorization.reason };
575
+ try {
576
+ const listed = await restartToolContext.sanctuary?.listContainers();
577
+ if (!listed || typeof listed !== "object" || !("ok" in listed) || listed.ok !== true
578
+ || !("data" in listed) || !listed.data || typeof listed.data !== "object"
579
+ || !("truncated" in listed.data) || listed.data.truncated !== false
580
+ || !("containers" in listed.data) || !Array.isArray(listed.data.containers)) {
581
+ return { ok: false, reason: "restart approval inventory is invalid" };
582
+ }
583
+ const matches = listed.data.containers.filter((target) => target?.name === context.arguments.container);
584
+ const target = matches[0];
585
+ if (matches.length !== 1 || typeof target?.id !== "string" || !target.id.trim() || target.id !== target.id.trim() || target.degraded !== false) {
586
+ return { ok: false, reason: "restart approval target is not exact" };
587
+ }
588
+ restartApproval = Object.freeze({
589
+ approvalId: existing.approvalId, agentRoot, sessionPath: existing.sessionPath,
590
+ ownerBinding: Object.freeze({ ...existing.ownerBinding }),
591
+ argumentDigest: existing.argumentDigest,
592
+ target: Object.freeze({ id: target.id, name: String(context.arguments.container) }),
593
+ });
594
+ return { ok: true };
595
+ }
596
+ catch {
597
+ return { ok: false, reason: "restart approval target is unavailable" };
598
+ }
599
+ },
484
600
  liveRisk: async () => ({ ok: true }),
485
601
  preflight: async (context) => {
486
- const current = await currentOptions(existing);
602
+ const current = await currentOptions(existing, "decision");
487
603
  if (!current)
488
604
  return { ok: false, reason: "current tool authority is unavailable" };
489
- const result = await (0, tools_1.preflightToolCall)(context.record.toolName, context.arguments, invocationContext(current, context.definition));
605
+ const result = await (0, tools_1.preflightToolCall)(context.record.toolName, context.arguments, invocationContext(current, context.definition, restartApproval));
490
606
  return result.kind === "ready" ? { ok: true } : { ok: false, reason: result.text };
491
607
  },
492
608
  hooks: telegramApprovalDecisionBarrierHooks(effectBarrier),
493
609
  execute: async (name, args) => {
494
- const current = await currentOptions(existing);
610
+ if (name === "unraid_restart_container" && !restartApproval)
611
+ return { kind: "rejected_before_handler", text: "restart approval was not revalidated" };
612
+ const current = await currentOptions(existing, "decision");
495
613
  if (!current || !definition)
496
614
  return { kind: "rejected_before_handler", text: "current approved tool authority is unavailable" };
497
- const approvedToolContext = invocationContext(current, definition);
615
+ const approvedToolContext = invocationContext(current, definition, restartApproval);
498
616
  const execute = () => executeApprovedTelegramTool(name, args, (toolName, toolArgs) => (0, tools_1.executeTool)(toolName, toolArgs, approvedToolContext, options.dependencies?.executeTool), decisionScenarioDigest, existing.approvalId, effectBarrier);
499
617
  return decisionScenarioDigest
500
618
  ? (0, sanctuary_acceptance_marker_1.runWithSanctuaryAcceptanceApproval)({ approvalId: existing.approvalId, argumentDigest: existing.argumentDigest }, execute)
@@ -913,6 +913,12 @@ function createTelegramSenseApp(options) {
913
913
  subject,
914
914
  identityKey,
915
915
  toolContext: toolContext ?? {},
916
+ ...(options.resolveRelationshipAuthorization ? {
917
+ resolveOwnerRelationship: (binding) => options.resolveRelationshipAuthorization({
918
+ friendId: binding.friendId, requestId: binding.requestId, sessionEventId: binding.sessionEventId, sessionKey: binding.sessionKey,
919
+ botId: botId, userId: authorizedUserId, chatId: authorizedChatId,
920
+ }),
921
+ } : {}),
916
922
  resolveLiveToolContext: async (record) => {
917
923
  const sessionPath = (0, shared_turn_1.getSenseSessionPath)(options.agentName, configuredOwnerFriendId, "telegram", configuredOwnerSessionKey, agentRoot);
918
924
  if (record.transport !== "telegram" || record.requesterId !== subject
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.815",
3
+ "version": "0.1.0-alpha.816",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.815",
9
+ "version": "0.1.0-alpha.816",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.815",
3
+ "version": "0.1.0-alpha.816",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },