@moxt-ai/mobius 0.0.29 → 0.0.30

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/dist/index.js CHANGED
@@ -24951,6 +24951,18 @@ import { createHash, randomUUID } from "node:crypto";
24951
24951
  import { stat as stat3 } from "node:fs/promises";
24952
24952
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
24953
24953
  var LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS = 90 * 24 * 60 * 60 * 1e3;
24954
+ var identifier2 = external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/);
24955
+ var savedApprovalSchema = external_exports.object({
24956
+ local_grant_id: identifier2,
24957
+ bot_id: identifier2,
24958
+ binding_id: identifier2,
24959
+ binding_revision: external_exports.number().int().positive(),
24960
+ agent_ref: identifier2,
24961
+ workspace_ref: external_exports.string().min(1).max(2048),
24962
+ revocation_epoch: external_exports.number().int().nonnegative(),
24963
+ agent_identity_digest: external_exports.string().regex(/^[a-f0-9]{64}$/),
24964
+ workspace_identity_digest: external_exports.string().regex(/^[a-f0-9]{64}$/)
24965
+ }).readonly();
24954
24966
  var LocalBotAuthorizationError = class extends Error {
24955
24967
  code;
24956
24968
  constructor(code, message, options = {}) {
@@ -25055,6 +25067,17 @@ function parseTargets(encoded) {
25055
25067
  }
25056
25068
  var LocalBotGrantStore = class {
25057
25069
  #database;
25070
+ static containsBot = (path2, botId) => {
25071
+ const database = new DatabaseSync3(path2, { readOnly: true });
25072
+ try {
25073
+ if (database.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'local_bot_grants'").get() === void 0) {
25074
+ return false;
25075
+ }
25076
+ return database.prepare("SELECT local_grant_id FROM local_bot_grants WHERE bot_id = ? LIMIT 1").get(botId) !== void 0;
25077
+ } finally {
25078
+ database.close();
25079
+ }
25080
+ };
25058
25081
  constructor(path2) {
25059
25082
  this.#database = new DatabaseSync3(path2);
25060
25083
  this.#database.exec(`
@@ -25088,6 +25111,78 @@ var LocalBotGrantStore = class {
25088
25111
  ) STRICT;
25089
25112
  `);
25090
25113
  }
25114
+ /** Only the local CLI may invoke this explicit approval transition. */
25115
+ reapprove = async (botId, catalog, directories, bindingAuthorityId, now, signal) => {
25116
+ signal.throwIfAborted();
25117
+ if (!identifier2.safeParse(botId).success) {
25118
+ throw new LocalBotAuthorizationError("invalid_request", "The Bot ID is invalid");
25119
+ }
25120
+ const row = this.#database.prepare("SELECT * FROM local_bot_grants WHERE bot_id = ? ORDER BY binding_revision DESC LIMIT 1").get(botId);
25121
+ if (row === void 0) {
25122
+ throw new LocalBotAuthorizationError("bot_not_found", "This machine has no saved approval for that Bot");
25123
+ }
25124
+ const parsed = savedApprovalSchema.safeParse(row);
25125
+ if (!parsed.success) {
25126
+ throw new LocalBotAuthorizationError("local_grant_invalid", "The saved Bot approval is invalid", {
25127
+ cause: parsed.error
25128
+ });
25129
+ }
25130
+ const saved = parsed.data;
25131
+ const agent = catalog.find(saved.agent_ref);
25132
+ if (!agent.capabilities.embeddedContextPrompt) {
25133
+ throw new LocalBotAuthorizationError("binding_incompatible", "The local Agent cannot apply Bot context");
25134
+ }
25135
+ const [adapterUnchanged, agentUnchanged] = await Promise.all([
25136
+ revalidateExecutable(agent.adapterIdentity),
25137
+ revalidateExecutable(agent.agentIdentity)
25138
+ ]);
25139
+ signal.throwIfAborted();
25140
+ if (!adapterUnchanged || !agentUnchanged) {
25141
+ throw new LocalBotAuthorizationError(
25142
+ "binding_incompatible",
25143
+ "The local Agent executable changed before approval"
25144
+ );
25145
+ }
25146
+ const workspacePath = await directories.resolve(bindingAuthorityId, saved.workspace_ref, signal);
25147
+ const workspaceIdentityDigest = await workspaceDigest(workspacePath);
25148
+ signal.throwIfAborted();
25149
+ this.#database.exec("BEGIN IMMEDIATE");
25150
+ try {
25151
+ const result = this.#database.prepare(`UPDATE local_bot_grants
25152
+ SET agent_identity_digest = ?, workspace_identity_digest = ?, workspace_path = ?,
25153
+ revoked_at = NULL, expires_at = ?, revocation_epoch = revocation_epoch + 1
25154
+ WHERE local_grant_id = ? AND revocation_epoch = ?
25155
+ AND agent_identity_digest = ? AND workspace_identity_digest = ?
25156
+ AND binding_revision = (SELECT MAX(binding_revision) FROM local_bot_grants WHERE bot_id = ?)`).run(
25157
+ agentDigest(agent),
25158
+ workspaceIdentityDigest,
25159
+ workspacePath,
25160
+ now + LOCAL_BOT_GRANT_LIFETIME_MILLISECONDS,
25161
+ saved.local_grant_id,
25162
+ saved.revocation_epoch,
25163
+ saved.agent_identity_digest,
25164
+ saved.workspace_identity_digest,
25165
+ botId
25166
+ );
25167
+ if (result.changes !== 1) {
25168
+ throw new LocalBotAuthorizationError(
25169
+ "binding_not_approved",
25170
+ "The Bot binding changed during approval; run the command again"
25171
+ );
25172
+ }
25173
+ this.#database.prepare(`INSERT INTO audit_events (recorded_at, event, command_id, session_id, details_json)
25174
+ VALUES (?, 'bot_binding_locally_reapproved', ?, '', ?)`).run(
25175
+ new Date(now).toISOString(),
25176
+ saved.binding_id,
25177
+ JSON.stringify({ botId, bindingId: saved.binding_id, agentRef: saved.agent_ref })
25178
+ );
25179
+ this.#database.exec("COMMIT");
25180
+ } catch (cause) {
25181
+ this.#database.exec("ROLLBACK");
25182
+ throw cause;
25183
+ }
25184
+ return { bindingId: saved.binding_id, workspacePath, agentRef: saved.agent_ref };
25185
+ };
25091
25186
  approve = async (request, catalog, directories, bindingAuthorityId, now, signal) => {
25092
25187
  signal.throwIfAborted();
25093
25188
  const existing = this.#database.prepare(
@@ -31712,7 +31807,7 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
31712
31807
  reportOperationFailure(socket, command.requestId, command.sessionId, error61);
31713
31808
  }
31714
31809
  }
31715
- async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, timer, signal) {
31810
+ async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, activateAttempt, timer, signal) {
31716
31811
  const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
31717
31812
  publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
31718
31813
  });
@@ -31741,6 +31836,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
31741
31836
  }),
31742
31837
  attemptId
31743
31838
  );
31839
+ activateAttempt();
31744
31840
  const attachments = await resolvePromptArtifacts(config2, command, signal);
31745
31841
  let stopReason;
31746
31842
  if (command.disposition === SessionPromptDisposition.Steer) {
@@ -31852,7 +31948,7 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
31852
31948
  agent.capabilities
31853
31949
  );
31854
31950
  }
31855
- async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, signal) {
31951
+ async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, activateAttempt, timer, signal) {
31856
31952
  const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
31857
31953
  publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
31858
31954
  });
@@ -31933,6 +32029,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
31933
32029
  }),
31934
32030
  attemptId
31935
32031
  );
32032
+ activateAttempt();
31936
32033
  const result = await sessions.runPrompt({
31937
32034
  additionalDirectories: resolved.additionalDirectories,
31938
32035
  agent,
@@ -32126,11 +32223,13 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32126
32223
  }
32127
32224
  const turn = new ActiveDaemonTurn(command, receipt.attemptId);
32128
32225
  activeTurns.set(command.commandId, turn);
32129
- if (trackActiveAttempt) {
32130
- activeAttemptIds.set(command.sessionId, receipt.attemptId);
32131
- }
32226
+ const activateAttempt = () => {
32227
+ if (trackActiveAttempt) {
32228
+ activeAttemptIds.set(command.sessionId, receipt.attemptId);
32229
+ }
32230
+ };
32132
32231
  try {
32133
- await execute(receipt.attemptId, turn.signal);
32232
+ await execute(receipt.attemptId, activateAttempt, turn.signal);
32134
32233
  } finally {
32135
32234
  activeTurns.delete(command.commandId);
32136
32235
  if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
@@ -32215,7 +32314,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32215
32314
  delivery,
32216
32315
  command,
32217
32316
  true,
32218
- (attemptId, executionSignal) => executeBotPrompt(
32317
+ (attemptId, activateAttempt, executionSignal) => executeBotPrompt(
32219
32318
  outbox,
32220
32319
  publisher,
32221
32320
  command,
@@ -32228,6 +32327,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32228
32327
  applicator,
32229
32328
  ipc,
32230
32329
  attemptId,
32330
+ activateAttempt,
32231
32331
  timer,
32232
32332
  executionSignal
32233
32333
  )
@@ -32240,7 +32340,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32240
32340
  delivery,
32241
32341
  command,
32242
32342
  command.disposition !== SessionPromptDisposition.Steer,
32243
- (attemptId, executionSignal) => executePrompt(
32343
+ (attemptId, activateAttempt, executionSignal) => executePrompt(
32244
32344
  outbox,
32245
32345
  publisher,
32246
32346
  command,
@@ -32249,6 +32349,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32249
32349
  audit,
32250
32350
  sessions,
32251
32351
  attemptId,
32352
+ activateAttempt,
32252
32353
  timer,
32253
32354
  executionSignal
32254
32355
  )
@@ -32750,7 +32851,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32750
32851
  }
32751
32852
  const turn2 = new ActiveDaemonTurn(command2, receipt2.attemptId);
32752
32853
  activeTurns.set(command2.commandId, turn2);
32753
- activeAttemptIds.set(command2.sessionId, receipt2.attemptId);
32754
32854
  void executeBotPrompt(
32755
32855
  outbox,
32756
32856
  publisher,
@@ -32764,6 +32864,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32764
32864
  applicator,
32765
32865
  ipc,
32766
32866
  receipt2.attemptId,
32867
+ () => activeAttemptIds.set(command2.sessionId, receipt2.attemptId),
32767
32868
  timer,
32768
32869
  turn2.signal
32769
32870
  ).catch((error61) => {
@@ -32790,9 +32891,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32790
32891
  }
32791
32892
  const turn = new ActiveDaemonTurn(command, receipt.attemptId);
32792
32893
  activeTurns.set(command.commandId, turn);
32793
- if (command.disposition !== SessionPromptDisposition.Steer) {
32794
- activeAttemptIds.set(command.sessionId, receipt.attemptId);
32795
- }
32796
32894
  void executePrompt(
32797
32895
  outbox,
32798
32896
  publisher,
@@ -32802,6 +32900,11 @@ async function runRelayConnection(config2, directories, audit, sessions, install
32802
32900
  audit,
32803
32901
  sessions,
32804
32902
  receipt.attemptId,
32903
+ () => {
32904
+ if (command.disposition !== SessionPromptDisposition.Steer) {
32905
+ activeAttemptIds.set(command.sessionId, receipt.attemptId);
32906
+ }
32907
+ },
32805
32908
  timer,
32806
32909
  turn.signal
32807
32910
  ).catch((error61) => {