motebit 1.4.4 → 1.5.0

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.
Files changed (2) hide show
  1. package/dist/index.js +2165 -353
  2. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -980,7 +980,7 @@ var init_routing = __esm({
980
980
  function isTokenAudience(value) {
981
981
  return typeof value === "string" && ALL_TOKEN_AUDIENCES.includes(value);
982
982
  }
983
- var SYNC_AUDIENCE, DEVICE_AUTH_AUDIENCE, PAIR_AUDIENCE, ROTATE_KEY_AUDIENCE, PUSH_REGISTER_AUDIENCE, TASK_SUBMIT_AUDIENCE, ADMIN_QUERY_AUDIENCE, PROPOSAL_AUDIENCE, ACCOUNT_BALANCE_AUDIENCE, ACCOUNT_DEPOSIT_AUDIENCE, ACCOUNT_WITHDRAW_AUDIENCE, ACCOUNT_WITHDRAWALS_AUDIENCE, ACCOUNT_CHECKOUT_AUDIENCE, BROWSER_SANDBOX_GRANT_AUDIENCE, BROWSER_SANDBOX_AUDIENCE, ALL_TOKEN_AUDIENCES;
983
+ var SYNC_AUDIENCE, DEVICE_AUTH_AUDIENCE, PAIR_AUDIENCE, ROTATE_KEY_AUDIENCE, PUSH_REGISTER_AUDIENCE, TASK_SUBMIT_AUDIENCE, ADMIN_QUERY_AUDIENCE, PROPOSAL_AUDIENCE, ACCOUNT_BALANCE_AUDIENCE, ACCOUNT_DEPOSIT_AUDIENCE, ACCOUNT_WITHDRAW_AUDIENCE, ACCOUNT_WITHDRAWALS_AUDIENCE, ACCOUNT_CHECKOUT_AUDIENCE, BROWSER_SANDBOX_GRANT_AUDIENCE, BROWSER_SANDBOX_AUDIENCE, RUNTIME_ATTACH_AUDIENCE, ALL_TOKEN_AUDIENCES;
984
984
  var init_audience = __esm({
985
985
  "../../packages/protocol/dist/audience.js"() {
986
986
  "use strict";
@@ -1000,6 +1000,7 @@ var init_audience = __esm({
1000
1000
  ACCOUNT_CHECKOUT_AUDIENCE = "account:checkout";
1001
1001
  BROWSER_SANDBOX_GRANT_AUDIENCE = "browser-sandbox-grant";
1002
1002
  BROWSER_SANDBOX_AUDIENCE = "browser-sandbox";
1003
+ RUNTIME_ATTACH_AUDIENCE = "runtime:attach";
1003
1004
  ALL_TOKEN_AUDIENCES = Object.freeze([
1004
1005
  "sync",
1005
1006
  "device:auth",
@@ -1016,7 +1017,8 @@ var init_audience = __esm({
1016
1017
  "account:withdrawals",
1017
1018
  "account:checkout",
1018
1019
  "browser-sandbox-grant",
1019
- "browser-sandbox"
1020
+ "browser-sandbox",
1021
+ "runtime:attach"
1020
1022
  ]);
1021
1023
  }
1022
1024
  });
@@ -4064,6 +4066,7 @@ __export(dist_exports, {
4064
4066
  RESEARCH_TASK_SHAPE: () => RESEARCH_TASK_SHAPE,
4065
4067
  RISK_LABELS: () => RISK_LABELS,
4066
4068
  ROTATE_KEY_AUDIENCE: () => ROTATE_KEY_AUDIENCE,
4069
+ RUNTIME_ATTACH_AUDIENCE: () => RUNTIME_ATTACH_AUDIENCE,
4067
4070
  RUNTIME_RETENTION_REGISTRY: () => RUNTIME_RETENTION_REGISTRY,
4068
4071
  RegulatoryRiskSemiring: () => RegulatoryRiskSemiring,
4069
4072
  RelationType: () => RelationType,
@@ -8738,6 +8741,8 @@ __export(dist_exports2, {
8738
8741
  SKILL_SIGNATURE_SUITE: () => SKILL_SIGNATURE_SUITE,
8739
8742
  TOOL_INVOCATION_RECEIPT_SUITE: () => TOOL_INVOCATION_RECEIPT_SUITE,
8740
8743
  WITNESS_OMISSION_DISPUTE_WINDOW_MS: () => WITNESS_OMISSION_DISPUTE_WINDOW_MS,
8744
+ agentCommandAudience: () => agentCommandAudience,
8745
+ agentCommandPayload: () => agentCommandPayload,
8741
8746
  base58btcDecode: () => base58btcDecode,
8742
8747
  base58btcEncode: () => base58btcEncode,
8743
8748
  bytesToHex: () => bytesToHex4,
@@ -8788,6 +8793,7 @@ __export(dist_exports2, {
8788
8793
  resolveTreeHashVersion: () => resolveTreeHashVersion,
8789
8794
  sha256: () => sha2562,
8790
8795
  signAdjudicatorVote: () => signAdjudicatorVote,
8796
+ signAgentCommandEnvelope: () => signAgentCommandEnvelope,
8791
8797
  signApprovalDecision: () => signApprovalDecision,
8792
8798
  signBalanceWaiver: () => signBalanceWaiver,
8793
8799
  signBySuite: () => signBySuite,
@@ -8830,6 +8836,7 @@ __export(dist_exports2, {
8830
8836
  toBase64Url: () => toBase64Url,
8831
8837
  verify: () => verify,
8832
8838
  verifyAdjudicatorVote: () => verifyAdjudicatorVote,
8839
+ verifyAgentCommandEnvelope: () => verifyAgentCommandEnvelope,
8833
8840
  verifyAgentSettlementAnchor: () => verifyAgentSettlementAnchor,
8834
8841
  verifyApprovalDecision: () => verifyApprovalDecision,
8835
8842
  verifyBalanceWaiver: () => verifyBalanceWaiver,
@@ -11747,6 +11754,9 @@ function createSWU(Point2, opts) {
11747
11754
  let map;
11748
11755
  return (scalars) => (map || (map = mapToCurveSimpleSWU(Point2.Fp, opts)))(scalars[0]);
11749
11756
  }
11757
+ function assertNeverSuite(suite) {
11758
+ throw new Error(`unsupported cryptosuite: ${String(suite)}`);
11759
+ }
11750
11760
  async function verifyBySuite(suite, canonicalBytes, signatureBytes, publicKeyBytes) {
11751
11761
  switch (suite) {
11752
11762
  case "motebit-jcs-ed25519-b64-v1":
@@ -11759,6 +11769,8 @@ async function verifyBySuite(suite, canonicalBytes, signatureBytes, publicKeyByt
11759
11769
  } catch {
11760
11770
  return false;
11761
11771
  }
11772
+ default:
11773
+ return false;
11762
11774
  }
11763
11775
  }
11764
11776
  async function signBySuite(suite, canonicalBytes, privateKeyBytes) {
@@ -11769,6 +11781,8 @@ async function signBySuite(suite, canonicalBytes, privateKeyBytes) {
11769
11781
  case "motebit-concat-ed25519-hex-v1":
11770
11782
  case "eddsa-jcs-2022":
11771
11783
  return signAsync(canonicalBytes, privateKeyBytes);
11784
+ default:
11785
+ return assertNeverSuite(suite);
11772
11786
  }
11773
11787
  }
11774
11788
  async function ed25519Sign(message2, privateKey) {
@@ -11793,6 +11807,8 @@ async function getPublicKeyBySuite(privateKey, suite) {
11793
11807
  case "motebit-concat-ed25519-hex-v1":
11794
11808
  case "eddsa-jcs-2022":
11795
11809
  return getPublicKeyAsync(privateKey);
11810
+ default:
11811
+ return assertNeverSuite(suite);
11796
11812
  }
11797
11813
  }
11798
11814
  function verifyP256EcdsaSha256(publicKeyCompressedHex, messageBytes, signatureDerBytes) {
@@ -12463,9 +12479,11 @@ async function signRequestEnvelope(payload, fields, identityPrivateKey) {
12463
12479
  async function verifyRequestEnvelope(envelope, registeredPublicKey, options) {
12464
12480
  if (envelope.suite !== SIGNED_REQUEST_ENVELOPE_SUITE) return false;
12465
12481
  if (options?.expectedAud !== void 0 && envelope.aud !== options.expectedAud) return false;
12466
- if (options?.now !== void 0) {
12467
- const windowMs = options.windowMs ?? 3e5;
12468
- if (Math.abs(options.now - envelope.ts) > windowMs) return false;
12482
+ const checkFreshness = options?.checkFreshness ?? true;
12483
+ if (checkFreshness) {
12484
+ const now = options?.now ?? Date.now();
12485
+ const windowMs = options?.windowMs ?? 3e5;
12486
+ if (Math.abs(now - envelope.ts) > windowMs) return false;
12469
12487
  }
12470
12488
  if (options?.payload !== void 0) {
12471
12489
  const recomputed = await canonicalSha256(options.payload);
@@ -13555,11 +13573,11 @@ async function signVerifiableCredential(unsignedVC, privateKey, publicKey) {
13555
13573
  );
13556
13574
  return { ...unsignedVC, proof };
13557
13575
  }
13558
- async function verifyVerifiableCredential(vc) {
13559
- if (vc.validUntil) {
13560
- const expiresAt = new Date(vc.validUntil).getTime();
13561
- if (Date.now() > expiresAt) return false;
13562
- }
13576
+ async function verifyVerifiableCredential(vc, options) {
13577
+ const now = options?.now ?? Date.now();
13578
+ if (vc.validUntil && now > new Date(vc.validUntil).getTime()) return false;
13579
+ if (vc.validFrom && now < new Date(vc.validFrom).getTime()) return false;
13580
+ if (vc.credentialStatus && options?.isRevoked?.(vc.credentialStatus.id) === true) return false;
13563
13581
  return verifyDataIntegritySigning(vc, vc.proof);
13564
13582
  }
13565
13583
  async function signVerifiablePresentation(unsignedVP, privateKey, publicKey) {
@@ -14552,6 +14570,49 @@ async function verifyAlternativePeeringArtifact(artifact, cert, ctx, errors) {
14552
14570
  if (!ok) errors.push("peering artifact signature does not verify against cert issuer pubkey");
14553
14571
  return ok;
14554
14572
  }
14573
+ function agentCommandAudience(targetMotebitId) {
14574
+ return `agent-command/${targetMotebitId}`;
14575
+ }
14576
+ function agentCommandPayload(command2, args) {
14577
+ return { command: command2, args: args ?? null };
14578
+ }
14579
+ async function signAgentCommandEnvelope(opts) {
14580
+ return signRequestEnvelope(
14581
+ agentCommandPayload(opts.command, opts.args),
14582
+ {
14583
+ motebit_id: opts.motebitId,
14584
+ ts: (opts.now ?? Date.now)(),
14585
+ aud: agentCommandAudience(opts.motebitId),
14586
+ ...opts.nonce !== void 0 ? { nonce: opts.nonce } : {}
14587
+ },
14588
+ opts.identityPrivateKey
14589
+ );
14590
+ }
14591
+ async function verifyAgentCommandEnvelope(opts) {
14592
+ const candidate = opts.envelope;
14593
+ if (candidate == null || typeof candidate !== "object" || typeof candidate.signature !== "string" || typeof candidate.motebit_id !== "string") {
14594
+ return {
14595
+ ok: false,
14596
+ reason: "command_request rejected: missing signed-request-envelope@1.0 (unsigned remote commands are not accepted)"
14597
+ };
14598
+ }
14599
+ if (candidate.motebit_id !== opts.motebitId) {
14600
+ return {
14601
+ ok: false,
14602
+ reason: "command_request rejected: envelope identity is not this agent's identity"
14603
+ };
14604
+ }
14605
+ const key = typeof opts.identityPublicKey === "string" ? hexToBytes5(opts.identityPublicKey) : opts.identityPublicKey;
14606
+ const valid = await verifyRequestEnvelope(candidate, key, {
14607
+ payload: agentCommandPayload(opts.command, opts.args),
14608
+ expectedAud: agentCommandAudience(opts.motebitId),
14609
+ ...opts.now !== void 0 ? { now: opts.now } : {}
14610
+ });
14611
+ return valid ? { ok: true } : {
14612
+ ok: false,
14613
+ reason: "command_request rejected: envelope verification failed (signature, audience, freshness, or payload digest)"
14614
+ };
14615
+ }
14555
14616
  function parseYamlValue(raw) {
14556
14617
  const trimmed = raw.trim();
14557
14618
  if (trimmed === "null") return null;
@@ -15436,15 +15497,24 @@ async function verifySkillEnvelopeArtifact(envelope) {
15436
15497
  }
15437
15498
  async function verifyCredential(vc, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECONDS, hardwareAttestationVerifiers) {
15438
15499
  const errors = [];
15500
+ const skewMs = clockSkewSeconds * 1e3;
15439
15501
  let expired = false;
15440
15502
  if (vc.validUntil) {
15441
15503
  const expiresAt = new Date(vc.validUntil).getTime();
15442
- const skewMs = clockSkewSeconds * 1e3;
15443
15504
  if (Date.now() > expiresAt + skewMs) {
15444
15505
  expired = true;
15445
15506
  errors.push({ message: "Credential has expired", path: "validUntil" });
15446
15507
  }
15447
15508
  }
15509
+ let notYetValid = false;
15510
+ if (vc.validFrom) {
15511
+ const activeAt = new Date(vc.validFrom).getTime();
15512
+ if (Date.now() + skewMs < activeAt) {
15513
+ notYetValid = true;
15514
+ errors.push({ message: "Credential is not yet valid", path: "validFrom" });
15515
+ }
15516
+ }
15517
+ const revocationUnchecked = vc.credentialStatus !== void 0;
15448
15518
  const proofValid = await verifyDataIntegrity(vc, vc.proof);
15449
15519
  if (!proofValid) {
15450
15520
  errors.push({ message: "Credential proof verification failed", path: "proof" });
@@ -15468,11 +15538,13 @@ async function verifyCredential(vc, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECOND
15468
15538
  }
15469
15539
  return {
15470
15540
  type: "credential",
15471
- valid: proofValid && !expired,
15541
+ valid: proofValid && !expired && !notYetValid,
15472
15542
  credential: vc,
15473
15543
  issuer: issuerDid,
15474
15544
  subject: subjectId,
15475
15545
  expired,
15546
+ ...notYetValid ? { not_yet_valid: true } : {},
15547
+ ...revocationUnchecked ? { revocation_unchecked: true } : {},
15476
15548
  ...hardwareAttestation && { hardware_attestation: hardwareAttestation },
15477
15549
  ...errors.length > 0 ? { errors } : {}
15478
15550
  };
@@ -18975,7 +19047,7 @@ var init_config2 = __esm({
18975
19047
  "src/config.ts"() {
18976
19048
  "use strict";
18977
19049
  init_esm_shims();
18978
- VERSION = true ? "1.4.4" : "0.0.0-dev";
19050
+ VERSION = true ? "1.5.0" : "0.0.0-dev";
18979
19051
  CONFIG_DIR = process.env["MOTEBIT_CONFIG_DIR"] ?? path2.join(os.homedir(), ".motebit");
18980
19052
  CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
18981
19053
  RELAY_DIR = path2.join(CONFIG_DIR, "relay");
@@ -27002,7 +27074,7 @@ function createRelayReceiptExchange(config) {
27002
27074
  if (closed)
27003
27075
  break;
27004
27076
  if (!response.ok) {
27005
- await sleep2(pollRetryDelayMs, pollAbortController.signal);
27077
+ await sleep3(pollRetryDelayMs, pollAbortController.signal);
27006
27078
  continue;
27007
27079
  }
27008
27080
  const text = await response.text();
@@ -27051,13 +27123,13 @@ function createRelayReceiptExchange(config) {
27051
27123
  break;
27052
27124
  const msg = err2 instanceof Error ? err2.message : String(err2);
27053
27125
  if (!msg.includes("abort")) {
27054
- await sleep2(pollRetryDelayMs, pollAbortController.signal);
27126
+ await sleep3(pollRetryDelayMs, pollAbortController.signal);
27055
27127
  }
27056
27128
  }
27057
27129
  }
27058
27130
  pollAbortController = null;
27059
27131
  }
27060
- async function sleep2(ms, signal) {
27132
+ async function sleep3(ms, signal) {
27061
27133
  return new Promise((resolve14) => {
27062
27134
  const timer = setTimeout(resolve14, ms);
27063
27135
  signal?.addEventListener("abort", () => {
@@ -46380,6 +46452,7 @@ __export(dist_exports6, {
46380
46452
  SANDBOX_TOKEN_REFRESH_MARGIN_MS: () => SANDBOX_TOKEN_REFRESH_MARGIN_MS,
46381
46453
  SimpleToolRegistry: () => SimpleToolRegistry,
46382
46454
  SovereignTierRequiredError: () => SovereignTierRequiredError,
46455
+ agentCommandAudience: () => agentCommandAudience,
46383
46456
  bindSlabControllerToRenderer: () => bindSlabControllerToRenderer,
46384
46457
  buildPrecisionContext: () => buildPrecisionContext,
46385
46458
  buildUserInputAuditDetail: () => buildUserInputAuditDetail,
@@ -46419,6 +46492,7 @@ __export(dist_exports6, {
46419
46492
  submitP2pDelegation: () => submitP2pDelegation,
46420
46493
  summarizeGradientHistory: () => summarizeGradientHistory,
46421
46494
  urlAuditDetail: () => urlAuditDetail,
46495
+ verifyAgentCommandEnvelope: () => verifyAgentCommandEnvelope,
46422
46496
  verifyGrantForTurn: () => verifyGrantForTurn
46423
46497
  });
46424
46498
  var init_dist22 = __esm({
@@ -46460,6 +46534,7 @@ var init_dist22 = __esm({
46460
46534
  init_relay_delegation();
46461
46535
  init_invoke_capability();
46462
46536
  init_grant_verifier();
46537
+ init_dist5();
46463
46538
  }
46464
46539
  });
46465
46540
 
@@ -67341,7 +67416,7 @@ var init_execution_receipt = __esm({
67341
67416
  DeviceIdSchema = z2.string().min(1).describe("Device identity \u2014 UUIDv7 string for the signing device. Brand-checked at the type layer; the wire shape is a non-empty string.");
67342
67417
  IntentOriginSchema = z2.enum(["user-tap", "ai-loop", "scheduled", "agent-to-agent"]).describe("How this task was authorized for invocation. `user-tap` = explicit affordance (surface determinism); `ai-loop` = model-mediated delegation; `scheduled` = cron; `agent-to-agent` = another motebit initiated it. Signature-bound \u2014 tampering breaks verification.");
67343
67418
  SuiteSchema = z2.literal("motebit-jcs-ed25519-b64-v1").describe("Cryptosuite identifier. Always `motebit-jcs-ed25519-b64-v1` for ExecutionReceipt today: JCS canonicalization (RFC 8785), Ed25519 signature, base64url-encoded signature, hex-encoded public key. See @motebit/protocol SUITE_REGISTRY.");
67344
- StatusSchema = z2.enum(["completed", "failed", "denied"]).describe("Terminal state of the task. `completed` = result produced and signed. `failed` = execution errored. `denied` = policy or budget gate rejected the invocation.");
67419
+ StatusSchema = z2.enum(["completed", "failed", "denied"]).describe("Terminal state of the task. `completed` = result produced and signed. `failed` = the execution interior did not yield its outcome (crashes, timeouts, and the worker's own principled refusals all included). `denied` = the governance boundary refused: a policy or budget gate blocked the task's actions and no permitted work completed. The discriminator is who refused \u2014 the boundary (`denied`) or the interior (`failed`). See execution-ledger-v1 \xA711.1 Status semantics.");
67345
67420
  ExecutionReceiptSchema = z2.lazy(() => z2.object({
67346
67421
  task_id: z2.string().min(1).describe("Task identifier \u2014 UUID assigned at submission time; stable across retries."),
67347
67422
  motebit_id: MotebitIdSchema,
@@ -70572,10 +70647,177 @@ var init_goal_tools = __esm({
70572
70647
  });
70573
70648
 
70574
70649
  // ../../packages/tools/dist/builtins/computer.js
70650
+ var POINT_SCHEMA, TARGET_HINT_SCHEMA, MODIFIERS_SCHEMA, computerDefinition;
70575
70651
  var init_computer = __esm({
70576
70652
  "../../packages/tools/dist/builtins/computer.js"() {
70577
70653
  "use strict";
70578
70654
  init_esm_shims();
70655
+ POINT_SCHEMA = {
70656
+ type: "object",
70657
+ properties: {
70658
+ x: { type: "integer", description: "Logical pixel X." },
70659
+ y: { type: "integer", description: "Logical pixel Y." }
70660
+ },
70661
+ required: ["x", "y"],
70662
+ additionalProperties: false
70663
+ };
70664
+ TARGET_HINT_SCHEMA = {
70665
+ type: "object",
70666
+ properties: {
70667
+ role: { type: "string", description: "Accessibility role. Examples: 'button', 'link'." },
70668
+ label: { type: "string", description: "Accessible label or visible text." },
70669
+ source: {
70670
+ type: "string",
70671
+ description: "Source: 'accessibility', 'dom', 'vision', 'user_annotation'."
70672
+ }
70673
+ },
70674
+ required: ["source"],
70675
+ additionalProperties: true
70676
+ };
70677
+ MODIFIERS_SCHEMA = {
70678
+ type: "array",
70679
+ items: { type: "string", enum: ["cmd", "ctrl", "alt", "shift"] },
70680
+ description: "Modifier keys held during the action."
70681
+ };
70682
+ computerDefinition = {
70683
+ name: "computer",
70684
+ mode: "pixels",
70685
+ description: "Observe or act on the user's computer \u2014 screenshot, click, type, scroll, navigate. Available on desktop (drives the user's OS via xcap+enigo) and on web (drives a cloud-browser sandbox via Playwright). On the cloud-browser surface, use `navigate` to reach a URL \u2014 there's no address bar for `key`/`type` to drive. Every observation and action emits a signed receipt and flows through the governance gate. See spec/computer-use-v1.md.",
70686
+ inputSchema: {
70687
+ type: "object",
70688
+ properties: {
70689
+ session_id: {
70690
+ type: "string",
70691
+ description: "Open computer-use session identifier. Optional \u2014 if omitted, the runtime's default session for this motebit is used."
70692
+ },
70693
+ action: {
70694
+ description: "Action to perform. Must be a discriminated variant with a `kind` field.",
70695
+ oneOf: [
70696
+ {
70697
+ type: "object",
70698
+ properties: {
70699
+ kind: { type: "string", enum: ["screenshot"] }
70700
+ },
70701
+ required: ["kind"],
70702
+ additionalProperties: false,
70703
+ description: "Capture the primary display's current frame."
70704
+ },
70705
+ {
70706
+ type: "object",
70707
+ properties: {
70708
+ kind: { type: "string", enum: ["cursor_position"] }
70709
+ },
70710
+ required: ["kind"],
70711
+ additionalProperties: false,
70712
+ description: "Read the current cursor coordinates."
70713
+ },
70714
+ {
70715
+ type: "object",
70716
+ properties: {
70717
+ kind: { type: "string", enum: ["click"] },
70718
+ target: POINT_SCHEMA,
70719
+ button: { type: "string", enum: ["left", "right", "middle"] },
70720
+ modifiers: MODIFIERS_SCHEMA,
70721
+ target_hint: TARGET_HINT_SCHEMA
70722
+ },
70723
+ required: ["kind", "target"],
70724
+ additionalProperties: false,
70725
+ description: "Single mouse click at `target`."
70726
+ },
70727
+ {
70728
+ type: "object",
70729
+ properties: {
70730
+ kind: { type: "string", enum: ["double_click"] },
70731
+ target: POINT_SCHEMA,
70732
+ button: { type: "string", enum: ["left", "right", "middle"] },
70733
+ modifiers: MODIFIERS_SCHEMA,
70734
+ target_hint: TARGET_HINT_SCHEMA
70735
+ },
70736
+ required: ["kind", "target"],
70737
+ additionalProperties: false,
70738
+ description: "Double click at `target`."
70739
+ },
70740
+ {
70741
+ type: "object",
70742
+ properties: {
70743
+ kind: { type: "string", enum: ["mouse_move"] },
70744
+ target: POINT_SCHEMA,
70745
+ target_hint: TARGET_HINT_SCHEMA
70746
+ },
70747
+ required: ["kind", "target"],
70748
+ additionalProperties: false,
70749
+ description: "Move cursor to `target` without clicking."
70750
+ },
70751
+ {
70752
+ type: "object",
70753
+ properties: {
70754
+ kind: { type: "string", enum: ["drag"] },
70755
+ from: POINT_SCHEMA,
70756
+ to: POINT_SCHEMA,
70757
+ button: { type: "string", enum: ["left", "right", "middle"] },
70758
+ modifiers: MODIFIERS_SCHEMA,
70759
+ duration_ms: { type: "integer", minimum: 0 },
70760
+ target_hint: TARGET_HINT_SCHEMA
70761
+ },
70762
+ required: ["kind", "from", "to"],
70763
+ additionalProperties: false,
70764
+ description: "Press at `from`, move to `to`, release."
70765
+ },
70766
+ {
70767
+ type: "object",
70768
+ properties: {
70769
+ kind: { type: "string", enum: ["type"] },
70770
+ text: { type: "string", description: "Text to type." },
70771
+ per_char_delay_ms: { type: "integer", minimum: 0 }
70772
+ },
70773
+ required: ["kind", "text"],
70774
+ additionalProperties: false,
70775
+ description: "Keyboard text input."
70776
+ },
70777
+ {
70778
+ type: "object",
70779
+ properties: {
70780
+ kind: { type: "string", enum: ["key"] },
70781
+ key: {
70782
+ type: "string",
70783
+ description: "Key combination. Example: 'cmd+c', 'escape'."
70784
+ }
70785
+ },
70786
+ required: ["kind", "key"],
70787
+ additionalProperties: false,
70788
+ description: "Keyboard combination."
70789
+ },
70790
+ {
70791
+ type: "object",
70792
+ properties: {
70793
+ kind: { type: "string", enum: ["scroll"] },
70794
+ target: POINT_SCHEMA,
70795
+ dx: { type: "integer", description: "Scroll wheel delta X." },
70796
+ dy: { type: "integer", description: "Scroll wheel delta Y." }
70797
+ },
70798
+ required: ["kind", "target", "dx", "dy"],
70799
+ additionalProperties: false,
70800
+ description: "Scroll at `target` by `(dx, dy)` wheel deltas."
70801
+ },
70802
+ {
70803
+ type: "object",
70804
+ properties: {
70805
+ kind: { type: "string", enum: ["navigate"] },
70806
+ url: {
70807
+ type: "string",
70808
+ description: "Target URL. Hostnames without a scheme (e.g. 'example.com') are normalized to 'https://example.com'."
70809
+ }
70810
+ },
70811
+ required: ["kind", "url"],
70812
+ additionalProperties: false,
70813
+ description: "Navigate the active browser context to a URL. Cloud-browser-only \u2014 use this when you need to reach a specific page on the cloud-browser surface (no address bar exists in the headless viewport for `key`/`type` to drive). Not available on the desktop surface (the user controls which app is focused there)."
70814
+ }
70815
+ ]
70816
+ }
70817
+ },
70818
+ required: ["action"]
70819
+ }
70820
+ };
70579
70821
  }
70580
70822
  });
70581
70823
 
@@ -71103,8 +71345,8 @@ function buildCliSkillSelectorHook(getHardwareAttestationScore) {
71103
71345
  }
71104
71346
  };
71105
71347
  }
71106
- function mapNodePlatformToSkillPlatform(nodePlatform) {
71107
- switch (nodePlatform) {
71348
+ function mapNodePlatformToSkillPlatform(nodePlatform2) {
71349
+ switch (nodePlatform2) {
71108
71350
  case "darwin":
71109
71351
  return "macos";
71110
71352
  case "linux":
@@ -71895,6 +72137,134 @@ var init_idempotency = __esm({
71895
72137
  }
71896
72138
  });
71897
72139
 
72140
+ // ../../services/relay/dist/loop-supervisor.js
72141
+ function superviseInterval(supervisor, name, intervalMs, tick, opts = {}) {
72142
+ supervisor?.register(name, intervalMs);
72143
+ return setInterval(() => {
72144
+ if (opts.isFrozen?.()) {
72145
+ supervisor?.markSkip(name);
72146
+ return;
72147
+ }
72148
+ if (supervisor === void 0) {
72149
+ void Promise.resolve().then(tick).catch(() => {
72150
+ });
72151
+ return;
72152
+ }
72153
+ supervisor.markStart(name);
72154
+ Promise.resolve().then(tick).then(() => supervisor.markOk(name)).catch((err2) => supervisor.markError(name, err2));
72155
+ }, intervalMs);
72156
+ }
72157
+ var LoopSupervisor;
72158
+ var init_loop_supervisor = __esm({
72159
+ "../../services/relay/dist/loop-supervisor.js"() {
72160
+ "use strict";
72161
+ init_esm_shims();
72162
+ LoopSupervisor = class {
72163
+ loops = /* @__PURE__ */ new Map();
72164
+ now;
72165
+ staleFactor;
72166
+ constructor(opts = {}) {
72167
+ this.now = opts.now ?? (() => Date.now());
72168
+ this.staleFactor = opts.staleFactor ?? 3;
72169
+ }
72170
+ /** Register a loop. Idempotent — re-registering preserves existing counters. */
72171
+ register(name, intervalMs) {
72172
+ if (this.loops.has(name))
72173
+ return;
72174
+ this.loops.set(name, {
72175
+ name,
72176
+ intervalMs,
72177
+ registeredAt: this.now(),
72178
+ lastStartAt: null,
72179
+ lastOkAt: null,
72180
+ lastErrorAt: null,
72181
+ lastError: null,
72182
+ lastSkipAt: null,
72183
+ tickCount: 0,
72184
+ okCount: 0,
72185
+ errorCount: 0,
72186
+ skipCount: 0,
72187
+ running: false
72188
+ });
72189
+ }
72190
+ markStart(name) {
72191
+ const r2 = this.loops.get(name);
72192
+ if (!r2)
72193
+ return;
72194
+ r2.lastStartAt = this.now();
72195
+ r2.tickCount += 1;
72196
+ r2.running = true;
72197
+ }
72198
+ markOk(name) {
72199
+ const r2 = this.loops.get(name);
72200
+ if (!r2)
72201
+ return;
72202
+ r2.lastOkAt = this.now();
72203
+ r2.okCount += 1;
72204
+ r2.running = false;
72205
+ }
72206
+ markError(name, err2) {
72207
+ const r2 = this.loops.get(name);
72208
+ if (!r2)
72209
+ return;
72210
+ r2.lastErrorAt = this.now();
72211
+ r2.lastError = err2 instanceof Error ? err2.message : String(err2);
72212
+ r2.errorCount += 1;
72213
+ r2.running = false;
72214
+ }
72215
+ markSkip(name) {
72216
+ const r2 = this.loops.get(name);
72217
+ if (!r2)
72218
+ return;
72219
+ r2.lastSkipAt = this.now();
72220
+ r2.skipCount += 1;
72221
+ }
72222
+ /** Compute the derived status for a record at time `now`. */
72223
+ statusOf(r2, now) {
72224
+ const freshness = r2.intervalMs * this.staleFactor;
72225
+ if (r2.running && r2.lastStartAt != null && now - r2.lastStartAt > freshness)
72226
+ return "hung";
72227
+ const lastHealthy = Math.max(r2.lastOkAt ?? 0, r2.lastSkipAt ?? 0);
72228
+ if (lastHealthy > 0) {
72229
+ if (now - lastHealthy <= freshness)
72230
+ return "ok";
72231
+ if (r2.lastErrorAt != null && r2.lastErrorAt >= lastHealthy)
72232
+ return "erroring";
72233
+ return "stale";
72234
+ }
72235
+ if (r2.lastErrorAt != null)
72236
+ return "erroring";
72237
+ if (now - r2.registeredAt <= freshness)
72238
+ return "idle";
72239
+ return "stale";
72240
+ }
72241
+ snapshot() {
72242
+ const now = this.now();
72243
+ return [...this.loops.values()].map((r2) => ({
72244
+ name: r2.name,
72245
+ interval_ms: r2.intervalMs,
72246
+ registered_at: r2.registeredAt,
72247
+ last_start_at: r2.lastStartAt,
72248
+ last_ok_at: r2.lastOkAt,
72249
+ last_error_at: r2.lastErrorAt,
72250
+ last_error: r2.lastError,
72251
+ last_skip_at: r2.lastSkipAt,
72252
+ tick_count: r2.tickCount,
72253
+ ok_count: r2.okCount,
72254
+ error_count: r2.errorCount,
72255
+ skip_count: r2.skipCount,
72256
+ running: r2.running,
72257
+ status: this.statusOf(r2, now)
72258
+ })).sort((a3, b3) => a3.name.localeCompare(b3.name));
72259
+ }
72260
+ /** True if any registered loop is not `ok`/`idle` — the alerting signal. */
72261
+ anyUnhealthy() {
72262
+ return this.snapshot().some((l6) => l6.status === "stale" || l6.status === "erroring" || l6.status === "hung");
72263
+ }
72264
+ };
72265
+ }
72266
+ });
72267
+
71898
72268
  // ../../services/relay/dist/horizon.js
71899
72269
  function truncateExecutionLedgersBeforeHorizon(db, horizonTs) {
71900
72270
  const result = db.prepare("DELETE FROM relay_execution_ledgers WHERE created_at < ?").run(horizonTs);
@@ -72134,16 +72504,17 @@ async function advanceRevocationHorizon(db, ctx) {
72134
72504
  const horizonTs = Date.now() - REVOCATION_TTL_MS;
72135
72505
  return advanceRelayHorizon(db, "relay_revocation_events", horizonTs, ctx);
72136
72506
  }
72137
- function startRevocationHorizonLoop(db, ctx, intervalMs = DEFAULT_REVOCATION_HORIZON_INTERVAL_MS, isFrozen) {
72138
- return setInterval(() => {
72139
- if (isFrozen?.())
72140
- return;
72141
- void advanceRevocationHorizon(db, ctx).catch((err2) => {
72507
+ function startRevocationHorizonLoop(db, ctx, intervalMs = DEFAULT_REVOCATION_HORIZON_INTERVAL_MS, isFrozen, supervisor) {
72508
+ return superviseInterval(supervisor, "revocation-horizon", intervalMs, async () => {
72509
+ try {
72510
+ await advanceRevocationHorizon(db, ctx);
72511
+ } catch (err2) {
72142
72512
  logger9.error("horizon.revocation.loop_tick_failed", {
72143
72513
  error: err2 instanceof Error ? err2.message : String(err2)
72144
72514
  });
72145
- });
72146
- }, intervalMs);
72515
+ throw err2;
72516
+ }
72517
+ }, { ...isFrozen ? { isFrozen } : {} });
72147
72518
  }
72148
72519
  var logger9, HORIZON_CERT_SUITE, DEFAULT_WITNESS_SOLICITATION_TIMEOUT_MS, MAX_HORIZON_ADVANCE_ATTEMPTS, HORIZON_RETRY_BACKOFF_MS, PEER_FRESHNESS_FLOOR_MS, STORE_TRUNCATE_REGISTRY, advanceLocks, DEFAULT_REVOCATION_HORIZON_INTERVAL_MS;
72149
72520
  var init_horizon = __esm({
@@ -72156,6 +72527,7 @@ var init_horizon = __esm({
72156
72527
  init_dist30();
72157
72528
  init_federation();
72158
72529
  init_logger();
72530
+ init_loop_supervisor();
72159
72531
  logger9 = createLogger({ service: "relay", module: "horizon" });
72160
72532
  HORIZON_CERT_SUITE = "motebit-jcs-ed25519-b64-v1";
72161
72533
  DEFAULT_WITNESS_SOLICITATION_TIMEOUT_MS = 1e4;
@@ -72174,134 +72546,6 @@ var init_horizon = __esm({
72174
72546
  }
72175
72547
  });
72176
72548
 
72177
- // ../../services/relay/dist/loop-supervisor.js
72178
- function superviseInterval(supervisor, name, intervalMs, tick, opts = {}) {
72179
- supervisor?.register(name, intervalMs);
72180
- return setInterval(() => {
72181
- if (opts.isFrozen?.()) {
72182
- supervisor?.markSkip(name);
72183
- return;
72184
- }
72185
- if (supervisor === void 0) {
72186
- void Promise.resolve().then(tick).catch(() => {
72187
- });
72188
- return;
72189
- }
72190
- supervisor.markStart(name);
72191
- Promise.resolve().then(tick).then(() => supervisor.markOk(name)).catch((err2) => supervisor.markError(name, err2));
72192
- }, intervalMs);
72193
- }
72194
- var LoopSupervisor;
72195
- var init_loop_supervisor = __esm({
72196
- "../../services/relay/dist/loop-supervisor.js"() {
72197
- "use strict";
72198
- init_esm_shims();
72199
- LoopSupervisor = class {
72200
- loops = /* @__PURE__ */ new Map();
72201
- now;
72202
- staleFactor;
72203
- constructor(opts = {}) {
72204
- this.now = opts.now ?? (() => Date.now());
72205
- this.staleFactor = opts.staleFactor ?? 3;
72206
- }
72207
- /** Register a loop. Idempotent — re-registering preserves existing counters. */
72208
- register(name, intervalMs) {
72209
- if (this.loops.has(name))
72210
- return;
72211
- this.loops.set(name, {
72212
- name,
72213
- intervalMs,
72214
- registeredAt: this.now(),
72215
- lastStartAt: null,
72216
- lastOkAt: null,
72217
- lastErrorAt: null,
72218
- lastError: null,
72219
- lastSkipAt: null,
72220
- tickCount: 0,
72221
- okCount: 0,
72222
- errorCount: 0,
72223
- skipCount: 0,
72224
- running: false
72225
- });
72226
- }
72227
- markStart(name) {
72228
- const r2 = this.loops.get(name);
72229
- if (!r2)
72230
- return;
72231
- r2.lastStartAt = this.now();
72232
- r2.tickCount += 1;
72233
- r2.running = true;
72234
- }
72235
- markOk(name) {
72236
- const r2 = this.loops.get(name);
72237
- if (!r2)
72238
- return;
72239
- r2.lastOkAt = this.now();
72240
- r2.okCount += 1;
72241
- r2.running = false;
72242
- }
72243
- markError(name, err2) {
72244
- const r2 = this.loops.get(name);
72245
- if (!r2)
72246
- return;
72247
- r2.lastErrorAt = this.now();
72248
- r2.lastError = err2 instanceof Error ? err2.message : String(err2);
72249
- r2.errorCount += 1;
72250
- r2.running = false;
72251
- }
72252
- markSkip(name) {
72253
- const r2 = this.loops.get(name);
72254
- if (!r2)
72255
- return;
72256
- r2.lastSkipAt = this.now();
72257
- r2.skipCount += 1;
72258
- }
72259
- /** Compute the derived status for a record at time `now`. */
72260
- statusOf(r2, now) {
72261
- const freshness = r2.intervalMs * this.staleFactor;
72262
- if (r2.running && r2.lastStartAt != null && now - r2.lastStartAt > freshness)
72263
- return "hung";
72264
- const lastHealthy = Math.max(r2.lastOkAt ?? 0, r2.lastSkipAt ?? 0);
72265
- if (lastHealthy > 0) {
72266
- if (now - lastHealthy <= freshness)
72267
- return "ok";
72268
- if (r2.lastErrorAt != null && r2.lastErrorAt >= lastHealthy)
72269
- return "erroring";
72270
- return "stale";
72271
- }
72272
- if (r2.lastErrorAt != null)
72273
- return "erroring";
72274
- if (now - r2.registeredAt <= freshness)
72275
- return "idle";
72276
- return "stale";
72277
- }
72278
- snapshot() {
72279
- const now = this.now();
72280
- return [...this.loops.values()].map((r2) => ({
72281
- name: r2.name,
72282
- interval_ms: r2.intervalMs,
72283
- registered_at: r2.registeredAt,
72284
- last_start_at: r2.lastStartAt,
72285
- last_ok_at: r2.lastOkAt,
72286
- last_error_at: r2.lastErrorAt,
72287
- last_error: r2.lastError,
72288
- last_skip_at: r2.lastSkipAt,
72289
- tick_count: r2.tickCount,
72290
- ok_count: r2.okCount,
72291
- error_count: r2.errorCount,
72292
- skip_count: r2.skipCount,
72293
- running: r2.running,
72294
- status: this.statusOf(r2, now)
72295
- })).sort((a3, b3) => a3.name.localeCompare(b3.name));
72296
- }
72297
- /** True if any registered loop is not `ok`/`idle` — the alerting signal. */
72298
- anyUnhealthy() {
72299
- return this.snapshot().some((l6) => l6.status === "stale" || l6.status === "erroring" || l6.status === "hung");
72300
- }
72301
- };
72302
- }
72303
- });
72304
-
72305
72549
  // ../../services/relay/dist/anchoring.js
72306
72550
  function createAnchoringTables(db) {
72307
72551
  db.exec(`
@@ -72420,43 +72664,40 @@ async function submitAnchorOnChain(db, batchId, submitter) {
72420
72664
  return false;
72421
72665
  }
72422
72666
  }
72423
- function startBatchAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
72667
+ function startBatchAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
72424
72668
  const maxSize = config.batchMaxSize ?? DEFAULT_BATCH_MAX_SIZE;
72425
72669
  const intervalMs = config.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS;
72426
72670
  const checkIntervalMs = Math.min(6e4, intervalMs);
72427
72671
  const submitter = config.submitter;
72428
- return setInterval(() => {
72429
- if (isFrozen?.())
72430
- return;
72431
- void (async () => {
72432
- try {
72433
- const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
72434
- if (countRow.cnt === 0)
72435
- return;
72436
- let batch = null;
72437
- if (countRow.cnt >= maxSize) {
72672
+ return superviseInterval(supervisor, "federation-anchor", checkIntervalMs, async () => {
72673
+ try {
72674
+ const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
72675
+ if (countRow.cnt === 0)
72676
+ return;
72677
+ let batch = null;
72678
+ if (countRow.cnt >= maxSize) {
72679
+ batch = await cutBatch(db, relayIdentity, maxSize);
72680
+ } else {
72681
+ const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
72682
+ if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
72438
72683
  batch = await cutBatch(db, relayIdentity, maxSize);
72439
- } else {
72440
- const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_federation_settlements WHERE anchor_batch_id IS NULL AND record_json IS NOT NULL").get();
72441
- if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
72442
- batch = await cutBatch(db, relayIdentity, maxSize);
72443
- }
72444
- }
72445
- if (batch && submitter) {
72446
- await submitAnchorOnChain(db, batch.batch_id, submitter);
72447
72684
  }
72448
- if (submitter) {
72449
- const failedBatches = db.prepare("SELECT batch_id FROM relay_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
72450
- for (const fb of failedBatches) {
72451
- await submitAnchorOnChain(db, fb.batch_id, submitter);
72452
- }
72685
+ }
72686
+ if (batch && submitter) {
72687
+ await submitAnchorOnChain(db, batch.batch_id, submitter);
72688
+ }
72689
+ if (submitter) {
72690
+ const failedBatches = db.prepare("SELECT batch_id FROM relay_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
72691
+ for (const fb of failedBatches) {
72692
+ await submitAnchorOnChain(db, fb.batch_id, submitter);
72453
72693
  }
72454
- } catch (err2) {
72455
- const message2 = err2 instanceof Error ? err2.message : String(err2);
72456
- logger10.error("anchoring.loop_error", { error: message2 });
72457
72694
  }
72458
- })();
72459
- }, checkIntervalMs);
72695
+ } catch (err2) {
72696
+ const message2 = err2 instanceof Error ? err2.message : String(err2);
72697
+ logger10.error("anchoring.loop_error", { error: message2 });
72698
+ throw err2;
72699
+ }
72700
+ }, { ...isFrozen ? { isFrozen } : {} });
72460
72701
  }
72461
72702
  async function getSettlementProof(db, settlementId) {
72462
72703
  const settlement = db.prepare(`SELECT settlement_id, settled_at, record_json, anchor_batch_id
@@ -72644,43 +72885,40 @@ function chainAnchorFromRow(row) {
72644
72885
  anchored_at: row.anchored_at
72645
72886
  };
72646
72887
  }
72647
- function startAgentSettlementAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
72888
+ function startAgentSettlementAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
72648
72889
  const maxSize = config.batchMaxSize ?? DEFAULT_BATCH_MAX_SIZE;
72649
72890
  const intervalMs = config.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS;
72650
72891
  const checkIntervalMs = Math.min(6e4, intervalMs);
72651
72892
  const submitter = config.submitter;
72652
- return setInterval(() => {
72653
- if (isFrozen?.())
72654
- return;
72655
- void (async () => {
72656
- try {
72657
- const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
72658
- if (countRow.cnt === 0)
72659
- return;
72660
- let batch = null;
72661
- if (countRow.cnt >= maxSize) {
72893
+ return superviseInterval(supervisor, "agent-settlement-anchor", checkIntervalMs, async () => {
72894
+ try {
72895
+ const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
72896
+ if (countRow.cnt === 0)
72897
+ return;
72898
+ let batch = null;
72899
+ if (countRow.cnt >= maxSize) {
72900
+ batch = await cutAgentSettlementBatch(db, relayIdentity, maxSize);
72901
+ } else {
72902
+ const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
72903
+ if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
72662
72904
  batch = await cutAgentSettlementBatch(db, relayIdentity, maxSize);
72663
- } else {
72664
- const oldest = db.prepare("SELECT MIN(settled_at) as oldest FROM relay_settlements WHERE anchor_batch_id IS NULL AND signature IS NOT NULL").get();
72665
- if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
72666
- batch = await cutAgentSettlementBatch(db, relayIdentity, maxSize);
72667
- }
72668
72905
  }
72669
- if (batch && submitter) {
72670
- await submitAgentAnchorOnChain(db, batch.batch_id, submitter);
72671
- }
72672
- if (submitter) {
72673
- const failedBatches = db.prepare("SELECT batch_id FROM relay_agent_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
72674
- for (const fb of failedBatches) {
72675
- await submitAgentAnchorOnChain(db, fb.batch_id, submitter);
72676
- }
72906
+ }
72907
+ if (batch && submitter) {
72908
+ await submitAgentAnchorOnChain(db, batch.batch_id, submitter);
72909
+ }
72910
+ if (submitter) {
72911
+ const failedBatches = db.prepare("SELECT batch_id FROM relay_agent_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
72912
+ for (const fb of failedBatches) {
72913
+ await submitAgentAnchorOnChain(db, fb.batch_id, submitter);
72677
72914
  }
72678
- } catch (err2) {
72679
- const message2 = err2 instanceof Error ? err2.message : String(err2);
72680
- logger10.error("anchoring.agent_loop_error", { error: message2 });
72681
72915
  }
72682
- })();
72683
- }, checkIntervalMs);
72916
+ } catch (err2) {
72917
+ const message2 = err2 instanceof Error ? err2.message : String(err2);
72918
+ logger10.error("anchoring.agent_loop_error", { error: message2 });
72919
+ throw err2;
72920
+ }
72921
+ }, { ...isFrozen ? { isFrozen } : {} });
72684
72922
  }
72685
72923
  async function getAgentSettlementProof(db, settlementId) {
72686
72924
  const settlement = db.prepare(`SELECT settlement_id, settled_at, record_json, anchor_batch_id
@@ -72738,6 +72976,7 @@ var init_anchoring = __esm({
72738
72976
  init_esm_shims();
72739
72977
  init_dist6();
72740
72978
  init_logger();
72979
+ init_loop_supervisor();
72741
72980
  AGENT_SETTLEMENT_ANCHOR_SUITE2 = "motebit-jcs-ed25519-hex-v1";
72742
72981
  FEDERATION_SETTLEMENT_ANCHOR_SUITE2 = "motebit-jcs-ed25519-hex-v1";
72743
72982
  AGENT_SETTLEMENT_TREE_HASH_VERSION = "merkle-sha256-rfc6962-v2";
@@ -72868,43 +73107,40 @@ async function submitCredentialAnchorOnChain(db, batchId, submitter) {
72868
73107
  return false;
72869
73108
  }
72870
73109
  }
72871
- function startCredentialAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
73110
+ function startCredentialAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
72872
73111
  const maxSize = config.batchMaxSize ?? DEFAULT_BATCH_MAX_SIZE2;
72873
73112
  const intervalMs = config.batchIntervalMs ?? DEFAULT_BATCH_INTERVAL_MS2;
72874
73113
  const submitter = config.submitter;
72875
73114
  const checkIntervalMs = Math.min(6e4, intervalMs);
72876
- return setInterval(() => {
72877
- if (isFrozen?.())
72878
- return;
72879
- void (async () => {
72880
- try {
72881
- const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
72882
- if (countRow.cnt === 0)
72883
- return;
72884
- let batch = null;
72885
- if (countRow.cnt >= maxSize) {
73115
+ return superviseInterval(supervisor, "credential-anchor", checkIntervalMs, async () => {
73116
+ try {
73117
+ const countRow = db.prepare("SELECT COUNT(*) as cnt FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
73118
+ if (countRow.cnt === 0)
73119
+ return;
73120
+ let batch = null;
73121
+ if (countRow.cnt >= maxSize) {
73122
+ batch = await cutCredentialBatch(db, relayIdentity, maxSize);
73123
+ } else {
73124
+ const oldest = db.prepare("SELECT MIN(issued_at) as oldest FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
73125
+ if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
72886
73126
  batch = await cutCredentialBatch(db, relayIdentity, maxSize);
72887
- } else {
72888
- const oldest = db.prepare("SELECT MIN(issued_at) as oldest FROM relay_credentials WHERE anchor_batch_id IS NULL").get();
72889
- if (oldest.oldest != null && Date.now() - oldest.oldest >= intervalMs) {
72890
- batch = await cutCredentialBatch(db, relayIdentity, maxSize);
72891
- }
72892
- }
72893
- if (batch && submitter) {
72894
- await submitCredentialAnchorOnChain(db, batch.batch_id, submitter);
72895
73127
  }
72896
- if (submitter) {
72897
- const failedBatches = db.prepare("SELECT batch_id FROM relay_credential_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
72898
- for (const fb of failedBatches) {
72899
- await submitCredentialAnchorOnChain(db, fb.batch_id, submitter);
72900
- }
73128
+ }
73129
+ if (batch && submitter) {
73130
+ await submitCredentialAnchorOnChain(db, batch.batch_id, submitter);
73131
+ }
73132
+ if (submitter) {
73133
+ const failedBatches = db.prepare("SELECT batch_id FROM relay_credential_anchor_batches WHERE status = 'signed' AND tx_hash IS NULL").all();
73134
+ for (const fb of failedBatches) {
73135
+ await submitCredentialAnchorOnChain(db, fb.batch_id, submitter);
72901
73136
  }
72902
- } catch (err2) {
72903
- const message2 = err2 instanceof Error ? err2.message : String(err2);
72904
- logger11.error("credential_anchoring.loop_error", { error: message2 });
72905
73137
  }
72906
- })();
72907
- }, checkIntervalMs);
73138
+ } catch (err2) {
73139
+ const message2 = err2 instanceof Error ? err2.message : String(err2);
73140
+ logger11.error("credential_anchoring.loop_error", { error: message2 });
73141
+ throw err2;
73142
+ }
73143
+ }, { ...isFrozen ? { isFrozen } : {} });
72908
73144
  }
72909
73145
  async function getCredentialAnchorProof(db, credentialId) {
72910
73146
  const credential = db.prepare(`SELECT credential_id, credential_json, issued_at, anchor_batch_id
@@ -73041,6 +73277,7 @@ var init_credential_anchoring = __esm({
73041
73277
  init_esm_shims();
73042
73278
  init_dist6();
73043
73279
  init_logger();
73280
+ init_loop_supervisor();
73044
73281
  logger11 = createLogger({ service: "relay", module: "credential-anchoring" });
73045
73282
  CREDENTIAL_TREE_HASH_VERSION = "merkle-sha256-rfc6962-v2";
73046
73283
  DEFAULT_BATCH_MAX_SIZE2 = 50;
@@ -74043,18 +74280,19 @@ function createTaskRouter(deps) {
74043
74280
  return [];
74044
74281
  }
74045
74282
  try {
74283
+ const discoverBody = await signDiscoverBody({
74284
+ query: { capability: requiredCaps[0], limit: 20 },
74285
+ hop_count: 0,
74286
+ max_hops: 1,
74287
+ // Only one hop for task routing candidates
74288
+ visited,
74289
+ query_id: queryId,
74290
+ origin_relay: relayIdentity.relayMotebitId
74291
+ }, relayIdentity);
74046
74292
  const resp = await fetch(`${peer.endpoint_url}/federation/v1/discover`, {
74047
74293
  method: "POST",
74048
74294
  headers: { "Content-Type": "application/json", "X-Correlation-ID": queryId },
74049
- body: JSON.stringify({
74050
- query: { capability: requiredCaps[0], limit: 20 },
74051
- hop_count: 0,
74052
- max_hops: 1,
74053
- // Only one hop for task routing candidates
74054
- visited,
74055
- query_id: queryId,
74056
- origin_relay: relayIdentity.relayMotebitId
74057
- }),
74295
+ body: JSON.stringify(discoverBody),
74058
74296
  signal: AbortSignal.timeout(5e3)
74059
74297
  });
74060
74298
  if (!resp.ok)
@@ -74456,6 +74694,7 @@ var init_task_routing = __esm({
74456
74694
  init_dist34();
74457
74695
  init_dist5();
74458
74696
  init_dist6();
74697
+ init_federation();
74459
74698
  init_dist35();
74460
74699
  init_logger();
74461
74700
  logger12 = createLogger({ service: "relay", module: "task-routing" });
@@ -76162,6 +76401,7 @@ __export(federation_exports, {
76162
76401
  registerFederationRoutes: () => registerFederationRoutes,
76163
76402
  sendHeartbeats: () => sendHeartbeats,
76164
76403
  setRevocationAnchorSubmitter: () => setRevocationAnchorSubmitter,
76404
+ signDiscoverBody: () => signDiscoverBody,
76165
76405
  startHeartbeatLoop: () => startHeartbeatLoop,
76166
76406
  startSettlementRetryLoop: () => startSettlementRetryLoop
76167
76407
  });
@@ -76447,15 +76687,15 @@ async function initRelayIdentity(db, passphrase, logger39) {
76447
76687
  did
76448
76688
  };
76449
76689
  }
76450
- function createFederationQueryCache() {
76690
+ function createFederationQueryCache(supervisor) {
76451
76691
  const cache2 = /* @__PURE__ */ new Map();
76452
- const pruneInterval = setInterval(() => {
76692
+ const pruneInterval = superviseInterval(supervisor, "federation-query-cache-prune", FEDERATION_QUERY_TTL_MS, () => {
76453
76693
  const cutoff = Date.now() - FEDERATION_QUERY_TTL_MS;
76454
76694
  for (const [id, ts] of cache2) {
76455
76695
  if (ts < cutoff)
76456
76696
  cache2.delete(id);
76457
76697
  }
76458
- }, FEDERATION_QUERY_TTL_MS);
76698
+ });
76459
76699
  return { cache: cache2, pruneInterval };
76460
76700
  }
76461
76701
  async function sendHeartbeats(db, relayIdentity) {
@@ -76573,21 +76813,18 @@ async function processSettlementRetries(db, relayIdentity, onRetryExhausted, ret
76573
76813
  function startSettlementRetryLoop(db, relayIdentity, intervalMs = 3e4, onRetryExhausted, isFrozen, retryPolicy = DEFAULT_RETRY_POLICY, supervisor) {
76574
76814
  return superviseInterval(supervisor, "settlement-retry", intervalMs, () => processSettlementRetries(db, relayIdentity, onRetryExhausted, retryPolicy), { isFrozen });
76575
76815
  }
76576
- function startHeartbeatLoop(db, relayIdentity, intervalMs = 6e4, isFrozen) {
76577
- return setInterval(() => {
76578
- if (isFrozen?.())
76579
- return;
76580
- void sendHeartbeats(db, relayIdentity);
76581
- }, intervalMs);
76816
+ function startHeartbeatLoop(db, relayIdentity, intervalMs = 6e4, isFrozen, supervisor) {
76817
+ return superviseInterval(supervisor, "federation-heartbeat", intervalMs, () => sendHeartbeats(db, relayIdentity), { ...isFrozen ? { isFrozen } : {} });
76582
76818
  }
76583
76819
  async function verifyPeerSignature(db, peerId, signatureHex, payloadBytes, allowedStates = ["active"], timestamp) {
76584
- if (timestamp != null) {
76585
- const drift = Math.abs(Date.now() - timestamp);
76586
- if (drift > FEDERATION_TIMESTAMP_DRIFT_MS) {
76587
- throw new HTTPException6(400, {
76588
- message: "Request timestamp outside acceptable drift (\xB15min)"
76589
- });
76590
- }
76820
+ if (timestamp == null || !Number.isFinite(timestamp)) {
76821
+ throw new HTTPException6(400, { message: "Federation request timestamp required" });
76822
+ }
76823
+ const drift = Math.abs(Date.now() - timestamp);
76824
+ if (drift > FEDERATION_TIMESTAMP_DRIFT_MS) {
76825
+ throw new HTTPException6(400, {
76826
+ message: "Request timestamp outside acceptable drift (\xB15min)"
76827
+ });
76591
76828
  }
76592
76829
  const stateList = allowedStates.map(() => "?").join(", ");
76593
76830
  const peer = db.prepare(`SELECT public_key FROM relay_peers WHERE peer_relay_id = ? AND state IN (${stateList})`).get(peerId, ...allowedStates);
@@ -76600,6 +76837,31 @@ async function verifyPeerSignature(db, peerId, signatureHex, payloadBytes, allow
76600
76837
  }
76601
76838
  return peer.public_key;
76602
76839
  }
76840
+ async function signDiscoverBody(body, relayIdentity) {
76841
+ const signed = {
76842
+ ...body,
76843
+ sender_relay: relayIdentity.relayMotebitId,
76844
+ timestamp: Date.now()
76845
+ };
76846
+ const sig = await ed25519Sign(new TextEncoder().encode(canonicalJson(signed)), relayIdentity.privateKey);
76847
+ return { ...signed, signature: bytesToHex4(sig) };
76848
+ }
76849
+ async function verifyDiscoverSender(db, body, requireSignature) {
76850
+ if (body.signature == null) {
76851
+ if (requireSignature) {
76852
+ throw new HTTPException6(403, { message: "Unsigned discovery request rejected" });
76853
+ }
76854
+ return null;
76855
+ }
76856
+ if (typeof body.sender_relay !== "string") {
76857
+ throw new HTTPException6(400, { message: "Signed discovery request missing sender_relay" });
76858
+ }
76859
+ if (typeof body.timestamp !== "number") {
76860
+ throw new HTTPException6(400, { message: "Signed discovery request missing timestamp" });
76861
+ }
76862
+ const { signature, ...payload } = body;
76863
+ return verifyPeerSignature(db, body.sender_relay, signature, new TextEncoder().encode(canonicalJson(payload)), ["active"], body.timestamp);
76864
+ }
76603
76865
  function registerFederationRoutes(deps) {
76604
76866
  const { db, app, relayIdentity, federationConfig, federationQueryCache } = deps;
76605
76867
  const peerLimiter = new FixedWindowLimiter(30, 6e4);
@@ -76614,6 +76876,7 @@ function registerFederationRoutes(deps) {
76614
76876
  }
76615
76877
  const federationEnabled = federationConfig?.enabled !== void 0 ? federationConfig.enabled : federationConfig?.endpointUrl != null;
76616
76878
  const maxPeers = federationConfig?.maxPeers ?? 50;
76879
+ const requireDiscoverSignature = federationConfig?.requireDiscoverSignature ?? false;
76617
76880
  function checkFederationEnabled() {
76618
76881
  if (!federationEnabled) {
76619
76882
  throw new HTTPException6(403, { message: "Federation is disabled on this relay" });
@@ -77029,7 +77292,14 @@ function registerFederationRoutes(deps) {
77029
77292
  if (!federationEnabled) {
77030
77293
  return c3.json({ agents: [] });
77031
77294
  }
77032
- checkPeerLimit(body.origin_relay);
77295
+ const verifiedSender = await verifyDiscoverSender(db, body, requireDiscoverSignature);
77296
+ if (verifiedSender == null) {
77297
+ logger17.warn("federation.discover.unsigned", {
77298
+ origin_relay: body.origin_relay,
77299
+ query_id: body.query_id
77300
+ });
77301
+ }
77302
+ checkPeerLimit(verifiedSender ?? body.origin_relay);
77033
77303
  if (federationQueryCache.has(body.query_id))
77034
77304
  return c3.json({ agents: [] });
77035
77305
  federationQueryCache.set(body.query_id, Date.now());
@@ -77052,20 +77322,21 @@ function registerFederationRoutes(deps) {
77052
77322
  const peers = db.prepare("SELECT peer_relay_id, endpoint_url FROM relay_peers WHERE state = 'active'").all();
77053
77323
  const forwardPromises = peers.filter((p5) => !visitedSet.has(p5.peer_relay_id)).map(async (peer) => {
77054
77324
  try {
77325
+ const forwardBody = await signDiscoverBody({
77326
+ query: body.query,
77327
+ hop_count: body.hop_count + 1,
77328
+ max_hops: body.max_hops,
77329
+ visited,
77330
+ query_id: body.query_id,
77331
+ origin_relay: body.origin_relay
77332
+ }, relayIdentity);
77055
77333
  const resp = await fetch(`${peer.endpoint_url}/federation/v1/discover`, {
77056
77334
  method: "POST",
77057
77335
  headers: {
77058
77336
  "Content-Type": "application/json",
77059
77337
  "X-Correlation-ID": body.query_id
77060
77338
  },
77061
- body: JSON.stringify({
77062
- query: body.query,
77063
- hop_count: body.hop_count + 1,
77064
- max_hops: body.max_hops,
77065
- visited,
77066
- query_id: body.query_id,
77067
- origin_relay: body.origin_relay
77068
- }),
77339
+ body: JSON.stringify(forwardBody),
77069
77340
  signal: AbortSignal.timeout(5e3)
77070
77341
  });
77071
77342
  if (!resp.ok)
@@ -77201,7 +77472,7 @@ var init_federation = __esm({
77201
77472
  init_retry_policy();
77202
77473
  init_dist30();
77203
77474
  FEDERATION_SUITE = "motebit-concat-ed25519-hex-v1";
77204
- RELAY_SPEC_VERSION = "motebit/relay-federation@1.2";
77475
+ RELAY_SPEC_VERSION = "motebit/relay-federation@1.3";
77205
77476
  logger17 = createLogger({ service: "relay", module: "federation" });
77206
77477
  REVOCATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
77207
77478
  PBKDF2_ITERATIONS = 6e5;
@@ -77561,10 +77832,12 @@ function startDepositDetector(config) {
77561
77832
  chain: config.chain,
77562
77833
  error: err2 instanceof Error ? err2.message : String(err2)
77563
77834
  });
77835
+ throw err2;
77564
77836
  }
77565
77837
  };
77566
- void tick();
77567
- return setInterval(() => void tick(), intervalMs);
77838
+ void tick().catch(() => {
77839
+ });
77840
+ return superviseInterval(config.supervisor, "deposit-detector", intervalMs, tick);
77568
77841
  }
77569
77842
  var logger18, TRANSFER_TOPIC, USDC_CONTRACTS, DEFAULT_RPC_URLS, CONFIRMATIONS_BY_CHAIN, USDC_ONCHAIN_TO_MICRO, SqliteDepositDetectorStore;
77570
77843
  var init_deposit_detector = __esm({
@@ -77575,6 +77848,7 @@ var init_deposit_detector = __esm({
77575
77848
  init_dist38();
77576
77849
  init_account_store_sqlite();
77577
77850
  init_logger();
77851
+ init_loop_supervisor();
77578
77852
  logger18 = createLogger({ service: "deposit-detector" });
77579
77853
  TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
77580
77854
  USDC_CONTRACTS = {
@@ -80814,18 +81088,19 @@ async function runIdentityLogAnchorTick(db, relayIdentity, config = {}) {
80814
81088
  await submitIdentityLogAnchorOnChain(db, anchor.anchor_id, submitter);
80815
81089
  }
80816
81090
  }
80817
- function startIdentityLogAnchorLoop(db, relayIdentity, config = {}, isFrozen) {
81091
+ function startIdentityLogAnchorLoop(db, relayIdentity, config = {}, isFrozen, supervisor) {
80818
81092
  const intervalMs = config.intervalMs ?? DEFAULT_IDENTITY_ANCHOR_INTERVAL_MS;
80819
81093
  const checkIntervalMs = Math.min(IDENTITY_ANCHOR_CHECK_INTERVAL_MS, intervalMs);
80820
- return setInterval(() => {
80821
- if (isFrozen?.())
80822
- return;
80823
- void runIdentityLogAnchorTick(db, relayIdentity, config).catch((err2) => {
81094
+ return superviseInterval(supervisor, "identity-log-anchor", checkIntervalMs, async () => {
81095
+ try {
81096
+ await runIdentityLogAnchorTick(db, relayIdentity, config);
81097
+ } catch (err2) {
80824
81098
  logger33.error("identity_log.anchor_loop_error", {
80825
81099
  error: err2 instanceof Error ? err2.message : String(err2)
80826
81100
  });
80827
- });
80828
- }, checkIntervalMs);
81101
+ throw err2;
81102
+ }
81103
+ }, { ...isFrozen ? { isFrozen } : {} });
80829
81104
  }
80830
81105
  var logger33, IDENTITY_LOG_TREE_HASH_VERSION, DEFAULT_IDENTITY_ANCHOR_INTERVAL_MS, IDENTITY_ANCHOR_CHECK_INTERVAL_MS;
80831
81106
  var init_identity_log_anchoring = __esm({
@@ -80834,6 +81109,7 @@ var init_identity_log_anchoring = __esm({
80834
81109
  init_esm_shims();
80835
81110
  init_dist6();
80836
81111
  init_logger();
81112
+ init_loop_supervisor();
80837
81113
  init_identity_log();
80838
81114
  init_identity_transparency();
80839
81115
  logger33 = createLogger({ service: "relay", module: "identity-log-anchoring" });
@@ -81815,13 +82091,16 @@ async function runDeferredOrchestrationCycle(deps) {
81815
82091
  }
81816
82092
  function startDeferredOrchestrationWorker(deps) {
81817
82093
  const intervalMs = deps.intervalMs ?? ORCHESTRATION_WORKER_INTERVAL_MS;
81818
- const handle = setInterval(() => {
81819
- void runDeferredOrchestrationCycle(deps).catch((err2) => {
82094
+ const handle = superviseInterval(deps.supervisor, "dispute-orchestration", intervalMs, async () => {
82095
+ try {
82096
+ await runDeferredOrchestrationCycle(deps);
82097
+ } catch (err2) {
81820
82098
  logger36.warn("dispute.orchestration.cycle_failed", {
81821
82099
  error: err2 instanceof Error ? err2.message : String(err2)
81822
82100
  });
81823
- });
81824
- }, intervalMs);
82101
+ throw err2;
82102
+ }
82103
+ });
81825
82104
  if (typeof handle.unref === "function")
81826
82105
  handle.unref();
81827
82106
  return handle;
@@ -82359,6 +82638,7 @@ var init_disputes = __esm({
82359
82638
  init_dist30();
82360
82639
  init_accounts();
82361
82640
  init_logger();
82641
+ init_loop_supervisor();
82362
82642
  init_dispute_orchestration();
82363
82643
  logger36 = createLogger({ service: "relay", module: "disputes" });
82364
82644
  EVIDENCE_WINDOW_MS = 48 * 60 * 60 * 1e3;
@@ -84351,6 +84631,1441 @@ async function consumeStream(stream, runtime, opts) {
84351
84631
  }
84352
84632
  }
84353
84633
 
84634
+ // src/runtime-host.ts
84635
+ init_esm_shims();
84636
+
84637
+ // ../../packages/runtime-host/dist/index.js
84638
+ init_esm_shims();
84639
+
84640
+ // ../../packages/runtime-host/dist/protocol.js
84641
+ init_esm_shims();
84642
+ var RUNTIME_HOST_PROTOCOL_VERSION = 1;
84643
+ var MAX_FRAME_BYTES = 4 * 1024 * 1024;
84644
+ function encodeFrame(message2) {
84645
+ return `${JSON.stringify(message2)}
84646
+ `;
84647
+ }
84648
+ var JsonLineDecoder = class {
84649
+ buffer = "";
84650
+ // Streaming decoder: a multibyte UTF-8 character split across two
84651
+ // socket chunks must not corrupt — `stream: true` holds the partial
84652
+ // sequence until its continuation bytes arrive.
84653
+ utf8 = new TextDecoder();
84654
+ push(data) {
84655
+ this.buffer += typeof data === "string" ? data : this.utf8.decode(data, { stream: true });
84656
+ if (this.buffer.length > MAX_FRAME_BYTES) {
84657
+ throw new Error(`frame exceeds ${MAX_FRAME_BYTES} bytes`);
84658
+ }
84659
+ const frames = [];
84660
+ let newline = this.buffer.indexOf("\n");
84661
+ while (newline !== -1) {
84662
+ const line = this.buffer.slice(0, newline);
84663
+ this.buffer = this.buffer.slice(newline + 1);
84664
+ if (line.trim().length > 0) {
84665
+ let parsed;
84666
+ try {
84667
+ parsed = JSON.parse(line);
84668
+ } catch (err2) {
84669
+ throw new Error(`malformed frame: ${err2 instanceof Error ? err2.message : String(err2)}`, {
84670
+ cause: err2
84671
+ });
84672
+ }
84673
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
84674
+ throw new Error("malformed frame: not a JSON object");
84675
+ }
84676
+ frames.push(parsed);
84677
+ }
84678
+ newline = this.buffer.indexOf("\n");
84679
+ }
84680
+ return frames;
84681
+ }
84682
+ };
84683
+
84684
+ // ../../packages/runtime-host/dist/paths-shared.js
84685
+ init_esm_shims();
84686
+ function isWindowsPipePath(path19) {
84687
+ return path19.startsWith("\\\\.\\pipe\\");
84688
+ }
84689
+
84690
+ // ../../packages/runtime-host/dist/lockfile.js
84691
+ init_esm_shims();
84692
+ async function readLockfile(platform, path19) {
84693
+ const raw = await platform.readFile(path19);
84694
+ if (raw === null)
84695
+ return null;
84696
+ try {
84697
+ const parsed = JSON.parse(raw);
84698
+ if (parsed === null || typeof parsed !== "object")
84699
+ return null;
84700
+ const record = parsed;
84701
+ if (typeof record.pid !== "number" || typeof record.bound_at !== "number" || typeof record.protocol_version !== "number") {
84702
+ return null;
84703
+ }
84704
+ return {
84705
+ pid: record.pid,
84706
+ bound_at: record.bound_at,
84707
+ protocol_version: record.protocol_version
84708
+ };
84709
+ } catch {
84710
+ return null;
84711
+ }
84712
+ }
84713
+ async function writeLockfile(platform, path19, record) {
84714
+ await platform.writeFile(path19, `${JSON.stringify(record)}
84715
+ `);
84716
+ }
84717
+ async function removeLockfile(platform, path19, pid) {
84718
+ const current = await readLockfile(platform, path19);
84719
+ if (current !== null && current.pid !== pid)
84720
+ return;
84721
+ try {
84722
+ await platform.removeFile(path19);
84723
+ } catch {
84724
+ }
84725
+ }
84726
+
84727
+ // ../../packages/runtime-host/dist/server.js
84728
+ init_esm_shims();
84729
+ init_dist5();
84730
+ init_dist();
84731
+ var CoordinatorAlreadyBoundError = class extends Error {
84732
+ constructor(socketPath) {
84733
+ super(`another process already binds ${socketPath}`);
84734
+ this.name = "CoordinatorAlreadyBoundError";
84735
+ }
84736
+ };
84737
+ function parseTokenClaims(token) {
84738
+ const dot = token.indexOf(".");
84739
+ if (dot <= 0)
84740
+ return null;
84741
+ try {
84742
+ const payload = JSON.parse(new TextDecoder().decode(fromBase64Url(token.slice(0, dot))));
84743
+ if (payload === null || typeof payload !== "object")
84744
+ return null;
84745
+ const record = payload;
84746
+ if (typeof record.mid !== "string" || typeof record.did !== "string" || typeof record.aud !== "string") {
84747
+ return null;
84748
+ }
84749
+ return { mid: record.mid, did: record.did, aud: record.aud };
84750
+ } catch {
84751
+ return null;
84752
+ }
84753
+ }
84754
+ var RuntimeHostServer = class _RuntimeHostServer {
84755
+ listener;
84756
+ opts;
84757
+ pid;
84758
+ connections = /* @__PURE__ */ new Set();
84759
+ /** capability → the connection contributing it (last registrar wins). */
84760
+ bridged = /* @__PURE__ */ new Map();
84761
+ bridgeInflight = /* @__PURE__ */ new Map();
84762
+ capabilityListeners = /* @__PURE__ */ new Set();
84763
+ bridgeCounter = 0;
84764
+ closed = false;
84765
+ constructor(listener, opts, pid) {
84766
+ this.listener = listener;
84767
+ this.opts = opts;
84768
+ this.pid = pid;
84769
+ }
84770
+ /**
84771
+ * Bind the canonical endpoint and become the coordinator. Throws
84772
+ * `CoordinatorAlreadyBoundError` when another listener holds it —
84773
+ * the caller attaches instead. The caller is responsible for
84774
+ * clearing a *verified-stale* socket file first (the election does
84775
+ * this); bind never unlinks.
84776
+ */
84777
+ static async bind(opts) {
84778
+ const pid = opts.pid ?? opts.platform.pid;
84779
+ const now = opts.now ?? (() => Date.now());
84780
+ const bound = await opts.platform.bind(opts.socketPath);
84781
+ if (bound === "in_use")
84782
+ throw new CoordinatorAlreadyBoundError(opts.socketPath);
84783
+ const host = new _RuntimeHostServer(bound, opts, pid);
84784
+ bound.onConnection((conn) => host.onConnection(conn));
84785
+ await writeLockfile(opts.platform, opts.lockfilePath, {
84786
+ pid,
84787
+ bound_at: now(),
84788
+ protocol_version: RUNTIME_HOST_PROTOCOL_VERSION
84789
+ });
84790
+ return host;
84791
+ }
84792
+ get attachedCount() {
84793
+ let count2 = 0;
84794
+ for (const conn of this.connections)
84795
+ if (conn.authenticated)
84796
+ count2 += 1;
84797
+ return count2;
84798
+ }
84799
+ /** The organs currently contributed by attached frontends. */
84800
+ get bridgedCapabilities() {
84801
+ return [...this.bridged.keys()];
84802
+ }
84803
+ /**
84804
+ * Subscribe to changes in the bridged-organ set — fired after a
84805
+ * frontend registers capabilities and after a contributing frontend
84806
+ * disconnects. The listener receives the CURRENT full set (idempotent
84807
+ * sync semantics, never deltas: last-registrar-wins and multi-frontend
84808
+ * churn collapse to "reconcile against this"). Returns unsubscribe.
84809
+ */
84810
+ onBridgedCapabilitiesChanged(listener) {
84811
+ this.capabilityListeners.add(listener);
84812
+ return () => this.capabilityListeners.delete(listener);
84813
+ }
84814
+ /**
84815
+ * Listener faults must not tear down the coordinator or the frame
84816
+ * that triggered the change — log and keep serving.
84817
+ */
84818
+ notifyCapabilitiesChanged() {
84819
+ const capabilities = this.bridgedCapabilities;
84820
+ for (const listener of this.capabilityListeners) {
84821
+ try {
84822
+ listener(capabilities);
84823
+ } catch (err2) {
84824
+ this.opts.logger?.warn("runtime-host: bridged-capabilities listener failed", {
84825
+ error: err2 instanceof Error ? err2.message : String(err2)
84826
+ });
84827
+ }
84828
+ }
84829
+ }
84830
+ /** Push an event to every attached frontend subscribed to `channel`. */
84831
+ publishEvent(channel, payload) {
84832
+ for (const conn of this.connections) {
84833
+ if (conn.authenticated && conn.channels.has(channel)) {
84834
+ this.send(conn, { t: "event", channel, payload });
84835
+ }
84836
+ }
84837
+ }
84838
+ /**
84839
+ * Invoke an organ a frontend contributed — the bridging half of the
84840
+ * election doctrine. Streams the frontend's chunks; throws honestly
84841
+ * when no attached frontend contributes the capability or the
84842
+ * contributor disconnects mid-stream (fail-loud, never silent retry
84843
+ * across the authority boundary).
84844
+ */
84845
+ async *invokeBridged(capability, prompt2, options) {
84846
+ const conn = this.bridged.get(capability);
84847
+ if (conn === void 0 || conn.conn.destroyed) {
84848
+ throw new Error(`no attached frontend contributes capability "${capability}"`);
84849
+ }
84850
+ this.bridgeCounter += 1;
84851
+ const id = `bridge-${this.bridgeCounter}`;
84852
+ const state = { conn, queue: [], done: false, error: null, wake: null };
84853
+ this.bridgeInflight.set(id, state);
84854
+ conn.bridgeIds.add(id);
84855
+ this.send(conn, { t: "bridge_invoke", id, capability, prompt: prompt2, options });
84856
+ try {
84857
+ for (; ; ) {
84858
+ while (state.queue.length > 0)
84859
+ yield state.queue.shift();
84860
+ if (state.error !== null)
84861
+ throw state.error;
84862
+ if (state.done)
84863
+ return;
84864
+ await new Promise((resolve14) => {
84865
+ state.wake = resolve14;
84866
+ });
84867
+ state.wake = null;
84868
+ }
84869
+ } finally {
84870
+ this.bridgeInflight.delete(id);
84871
+ conn.bridgeIds.delete(id);
84872
+ }
84873
+ }
84874
+ /** Stop coordinating: drop connections, release the socket + lock. */
84875
+ async close() {
84876
+ if (this.closed)
84877
+ return;
84878
+ this.closed = true;
84879
+ for (const conn of this.connections)
84880
+ this.destroyConnection(conn);
84881
+ await this.listener.close();
84882
+ await this.opts.platform.removeSocketFile(this.opts.socketPath);
84883
+ await removeLockfile(this.opts.platform, this.opts.lockfilePath, this.pid);
84884
+ }
84885
+ onConnection(frameConn) {
84886
+ const conn = {
84887
+ conn: frameConn,
84888
+ decoder: new JsonLineDecoder(),
84889
+ authenticated: false,
84890
+ channels: /* @__PURE__ */ new Set(),
84891
+ capabilities: /* @__PURE__ */ new Set(),
84892
+ inflight: /* @__PURE__ */ new Map(),
84893
+ bridgeIds: /* @__PURE__ */ new Set(),
84894
+ handshakeTimer: null
84895
+ };
84896
+ this.connections.add(conn);
84897
+ conn.handshakeTimer = setTimeout(() => {
84898
+ if (!conn.authenticated)
84899
+ this.destroyConnection(conn);
84900
+ }, this.opts.handshakeTimeoutMs ?? 5e3);
84901
+ frameConn.onData((data) => {
84902
+ let frames;
84903
+ try {
84904
+ frames = conn.decoder.push(data);
84905
+ } catch (err2) {
84906
+ this.opts.logger?.warn("runtime-host: destroying connection on malformed frame", {
84907
+ error: err2 instanceof Error ? err2.message : String(err2)
84908
+ });
84909
+ this.destroyConnection(conn);
84910
+ return;
84911
+ }
84912
+ for (const frame of frames) {
84913
+ void this.onMessage(conn, frame);
84914
+ }
84915
+ });
84916
+ frameConn.onClose(() => this.destroyConnection(conn));
84917
+ }
84918
+ async onMessage(conn, message2) {
84919
+ if (!conn.authenticated) {
84920
+ if (message2.t !== "hello") {
84921
+ this.destroyConnection(conn);
84922
+ return;
84923
+ }
84924
+ await this.onHello(conn, message2);
84925
+ return;
84926
+ }
84927
+ switch (message2.t) {
84928
+ case "hello":
84929
+ this.destroyConnection(conn);
84930
+ return;
84931
+ case "subscribe":
84932
+ if (typeof message2.channel === "string")
84933
+ conn.channels.add(message2.channel);
84934
+ return;
84935
+ case "unsubscribe":
84936
+ if (typeof message2.channel === "string")
84937
+ conn.channels.delete(message2.channel);
84938
+ return;
84939
+ case "register_capabilities": {
84940
+ if (!Array.isArray(message2.capabilities) || message2.capabilities.some((c3) => typeof c3 !== "string")) {
84941
+ this.destroyConnection(conn);
84942
+ return;
84943
+ }
84944
+ for (const capability of conn.capabilities) {
84945
+ if (this.bridged.get(capability) === conn)
84946
+ this.bridged.delete(capability);
84947
+ }
84948
+ conn.capabilities = new Set(message2.capabilities);
84949
+ for (const capability of conn.capabilities) {
84950
+ this.bridged.set(capability, conn);
84951
+ }
84952
+ this.notifyCapabilitiesChanged();
84953
+ return;
84954
+ }
84955
+ case "bridge_chunk":
84956
+ case "bridge_end":
84957
+ case "bridge_error": {
84958
+ const state = this.bridgeInflight.get(message2.id);
84959
+ if (state === void 0 || state.conn !== conn)
84960
+ return;
84961
+ if (message2.t === "bridge_chunk")
84962
+ state.queue.push(message2.chunk);
84963
+ else if (message2.t === "bridge_end")
84964
+ state.done = true;
84965
+ else
84966
+ state.error = new Error(message2.message);
84967
+ state.wake?.();
84968
+ return;
84969
+ }
84970
+ case "invoke":
84971
+ if (typeof message2.id !== "string" || typeof message2.capability !== "string" || typeof message2.prompt !== "string") {
84972
+ this.destroyConnection(conn);
84973
+ return;
84974
+ }
84975
+ await this.runStream(conn, message2.id, (ctx) => this.opts.onInvoke(message2.capability, message2.prompt, message2.options, ctx));
84976
+ return;
84977
+ case "chat": {
84978
+ if (typeof message2.id !== "string" || typeof message2.text !== "string") {
84979
+ this.destroyConnection(conn);
84980
+ return;
84981
+ }
84982
+ const onChat = this.opts.onChat;
84983
+ if (onChat === void 0) {
84984
+ this.send(conn, {
84985
+ t: "invoke_error",
84986
+ id: message2.id,
84987
+ message: "this coordinator does not proxy chat"
84988
+ });
84989
+ return;
84990
+ }
84991
+ await this.runStream(conn, message2.id, (ctx) => onChat(message2.text, message2.options, ctx));
84992
+ return;
84993
+ }
84994
+ case "resolve_approval": {
84995
+ if (typeof message2.id !== "string" || typeof message2.approved !== "boolean" || typeof message2.approver_id !== "string") {
84996
+ this.destroyConnection(conn);
84997
+ return;
84998
+ }
84999
+ const onResolveApproval = this.opts.onResolveApproval;
85000
+ if (onResolveApproval === void 0) {
85001
+ this.send(conn, {
85002
+ t: "invoke_error",
85003
+ id: message2.id,
85004
+ message: "this coordinator does not proxy approval resolution"
85005
+ });
85006
+ return;
85007
+ }
85008
+ await this.runStream(conn, message2.id, (ctx) => onResolveApproval(message2.approved, message2.approver_id, ctx));
85009
+ return;
85010
+ }
85011
+ default:
85012
+ this.destroyConnection(conn);
85013
+ }
85014
+ }
85015
+ refuse(conn, reason, detail) {
85016
+ this.send(conn, { t: "refuse", reason, detail });
85017
+ conn.conn.end();
85018
+ }
85019
+ async onHello(conn, hello) {
85020
+ if (typeof hello.protocol_version !== "number" || typeof hello.token !== "string") {
85021
+ this.refuse(conn, "malformed_hello", "hello requires protocol_version and token");
85022
+ return;
85023
+ }
85024
+ if (hello.protocol_version !== RUNTIME_HOST_PROTOCOL_VERSION) {
85025
+ this.refuse(conn, "version_skew", `coordinator speaks runtime-host protocol v${RUNTIME_HOST_PROTOCOL_VERSION}, attacher sent v${hello.protocol_version}`);
85026
+ return;
85027
+ }
85028
+ const claims = parseTokenClaims(hello.token);
85029
+ if (claims === null) {
85030
+ this.refuse(conn, "auth_failed", "attach token is malformed");
85031
+ return;
85032
+ }
85033
+ if (claims.aud !== RUNTIME_ATTACH_AUDIENCE) {
85034
+ this.refuse(conn, "auth_failed", `attach token audience is "${claims.aud}", expected "${RUNTIME_ATTACH_AUDIENCE}"`);
85035
+ return;
85036
+ }
85037
+ if (claims.mid !== this.opts.motebitId) {
85038
+ this.refuse(conn, "auth_failed", "attach token is for a different motebit identity");
85039
+ return;
85040
+ }
85041
+ let publicKey;
85042
+ try {
85043
+ publicKey = await this.opts.resolveDevicePublicKey(claims.did);
85044
+ } catch (err2) {
85045
+ this.opts.logger?.warn("runtime-host: device key resolution failed", {
85046
+ error: err2 instanceof Error ? err2.message : String(err2)
85047
+ });
85048
+ publicKey = null;
85049
+ }
85050
+ if (publicKey === null) {
85051
+ this.refuse(conn, "auth_failed", `unknown device "${claims.did}"`);
85052
+ return;
85053
+ }
85054
+ const payload = await verifySignedToken(hello.token, publicKey);
85055
+ if (payload === null) {
85056
+ this.refuse(conn, "auth_failed", "attach token signature invalid or token expired");
85057
+ return;
85058
+ }
85059
+ conn.authenticated = true;
85060
+ if (conn.handshakeTimer !== null) {
85061
+ clearTimeout(conn.handshakeTimer);
85062
+ conn.handshakeTimer = null;
85063
+ }
85064
+ this.send(conn, {
85065
+ t: "hello_ack",
85066
+ protocol_version: RUNTIME_HOST_PROTOCOL_VERSION,
85067
+ coordinator_pid: this.pid
85068
+ });
85069
+ }
85070
+ /** Drive one handler stream onto the wire: chunk* then end, or invoke_error. */
85071
+ async runStream(conn, id, start) {
85072
+ const controller = new AbortController();
85073
+ conn.inflight.set(id, controller);
85074
+ try {
85075
+ for await (const chunk of start({ signal: controller.signal })) {
85076
+ if (controller.signal.aborted || conn.conn.destroyed)
85077
+ return;
85078
+ this.send(conn, { t: "chunk", id, chunk });
85079
+ }
85080
+ if (!conn.conn.destroyed)
85081
+ this.send(conn, { t: "end", id });
85082
+ } catch (err2) {
85083
+ if (!conn.conn.destroyed) {
85084
+ this.send(conn, {
85085
+ t: "invoke_error",
85086
+ id,
85087
+ message: err2 instanceof Error ? err2.message : String(err2)
85088
+ });
85089
+ }
85090
+ } finally {
85091
+ conn.inflight.delete(id);
85092
+ }
85093
+ }
85094
+ send(conn, message2) {
85095
+ if (conn.conn.destroyed)
85096
+ return;
85097
+ conn.conn.send(encodeFrame(message2));
85098
+ }
85099
+ destroyConnection(conn) {
85100
+ if (!this.connections.has(conn))
85101
+ return;
85102
+ this.connections.delete(conn);
85103
+ if (conn.handshakeTimer !== null)
85104
+ clearTimeout(conn.handshakeTimer);
85105
+ for (const controller of conn.inflight.values())
85106
+ controller.abort();
85107
+ conn.inflight.clear();
85108
+ for (const id of conn.bridgeIds) {
85109
+ const state = this.bridgeInflight.get(id);
85110
+ if (state !== void 0 && state.error === null && !state.done) {
85111
+ state.error = new Error("bridged frontend disconnected mid-invocation");
85112
+ state.wake?.();
85113
+ }
85114
+ }
85115
+ conn.bridgeIds.clear();
85116
+ let removedOrgans = 0;
85117
+ for (const capability of conn.capabilities) {
85118
+ if (this.bridged.get(capability) === conn) {
85119
+ this.bridged.delete(capability);
85120
+ removedOrgans += 1;
85121
+ }
85122
+ }
85123
+ conn.conn.destroy();
85124
+ if (removedOrgans > 0)
85125
+ this.notifyCapabilitiesChanged();
85126
+ }
85127
+ };
85128
+
85129
+ // ../../packages/runtime-host/dist/client.js
85130
+ init_esm_shims();
85131
+ init_dist5();
85132
+ init_dist();
85133
+ var AttachRefusedError = class extends Error {
85134
+ reason;
85135
+ detail;
85136
+ constructor(reason, detail) {
85137
+ super(`attach refused (${reason}): ${detail}`);
85138
+ this.reason = reason;
85139
+ this.detail = detail;
85140
+ this.name = "AttachRefusedError";
85141
+ }
85142
+ };
85143
+ var CoordinatorUnreachableError = class extends Error {
85144
+ constructor(socketPath, cause) {
85145
+ super(`no coordinator reachable at ${socketPath}`, { cause });
85146
+ this.name = "CoordinatorUnreachableError";
85147
+ }
85148
+ };
85149
+ async function mintAttachToken(identity, privateKey, opts = {}) {
85150
+ const now = opts.now ?? (() => Date.now());
85151
+ const issuedAt = now();
85152
+ return createSignedToken({
85153
+ mid: identity.motebitId,
85154
+ did: identity.deviceId,
85155
+ iat: issuedAt,
85156
+ exp: issuedAt + (opts.ttlMs ?? 3e4),
85157
+ // Web Crypto — available in node ≥ 20 AND the desktop webview;
85158
+ // this module must never import node:crypto.
85159
+ jti: globalThis.crypto.randomUUID(),
85160
+ aud: RUNTIME_ATTACH_AUDIENCE
85161
+ }, privateKey);
85162
+ }
85163
+ var RuntimeHostClient = class _RuntimeHostClient {
85164
+ conn;
85165
+ coordinatorPid;
85166
+ inflight = /* @__PURE__ */ new Map();
85167
+ eventHandlers = /* @__PURE__ */ new Map();
85168
+ closeHandlers = /* @__PURE__ */ new Set();
85169
+ capabilityHandlers = /* @__PURE__ */ new Map();
85170
+ bridgeAborts = /* @__PURE__ */ new Map();
85171
+ requestCounter = 0;
85172
+ closed = false;
85173
+ constructor(conn, coordinatorPid) {
85174
+ this.conn = conn;
85175
+ this.coordinatorPid = coordinatorPid;
85176
+ }
85177
+ /**
85178
+ * Connect + handshake. Resolves to an attached client, or throws
85179
+ * `CoordinatorUnreachableError` (nothing listening — caller should
85180
+ * run the election) / `AttachRefusedError` (a live coordinator said
85181
+ * no — caller must NOT try to bind over it).
85182
+ */
85183
+ static async attach(opts) {
85184
+ const conn = await opts.platform.connect(opts.socketPath, opts.connectTimeoutMs ?? 2e3);
85185
+ if (conn === null) {
85186
+ throw new CoordinatorUnreachableError(opts.socketPath, new Error("connect failed"));
85187
+ }
85188
+ conn.send(encodeFrame({
85189
+ t: "hello",
85190
+ protocol_version: opts.protocolVersion ?? RUNTIME_HOST_PROTOCOL_VERSION,
85191
+ token: opts.token
85192
+ }));
85193
+ const decoder2 = new JsonLineDecoder();
85194
+ const { first, rest } = await new Promise((resolve14, reject) => {
85195
+ let settled = false;
85196
+ const timer = setTimeout(() => {
85197
+ settle();
85198
+ conn.destroy();
85199
+ reject(new CoordinatorUnreachableError(opts.socketPath, new Error("handshake timeout")));
85200
+ }, opts.handshakeTimeoutMs ?? 3e3);
85201
+ const settle = () => {
85202
+ settled = true;
85203
+ clearTimeout(timer);
85204
+ };
85205
+ conn.onData((data) => {
85206
+ if (settled)
85207
+ return;
85208
+ let frames;
85209
+ try {
85210
+ frames = decoder2.push(data);
85211
+ } catch (err2) {
85212
+ settle();
85213
+ conn.destroy();
85214
+ reject(new Error(`malformed coordinator frame: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 }));
85215
+ return;
85216
+ }
85217
+ if (frames.length > 0) {
85218
+ settle();
85219
+ resolve14({
85220
+ first: frames[0],
85221
+ rest: frames.slice(1)
85222
+ });
85223
+ }
85224
+ });
85225
+ conn.onClose(() => {
85226
+ if (settled)
85227
+ return;
85228
+ settle();
85229
+ conn.destroy();
85230
+ reject(new CoordinatorUnreachableError(opts.socketPath, new Error("closed during handshake")));
85231
+ });
85232
+ });
85233
+ if (first.t === "refuse") {
85234
+ conn.destroy();
85235
+ throw new AttachRefusedError(first.reason, first.detail);
85236
+ }
85237
+ if (first.t !== "hello_ack") {
85238
+ conn.destroy();
85239
+ throw new Error(`unexpected first coordinator frame: ${first.t}`);
85240
+ }
85241
+ const client = new _RuntimeHostClient(conn, first.coordinator_pid);
85242
+ client.wire(decoder2);
85243
+ for (const frame of rest)
85244
+ client.onMessage(frame);
85245
+ if (opts.capabilities !== void 0)
85246
+ client.setBridgedCapabilities(opts.capabilities);
85247
+ return client;
85248
+ }
85249
+ /**
85250
+ * Proxy a typed capability invocation to the coordinator. Yields the
85251
+ * coordinator's streamed chunks; throws on `invoke_error` or if the
85252
+ * coordinator goes away mid-stream (fail-loud, per doctrine).
85253
+ */
85254
+ async *invoke(capability, prompt2, options) {
85255
+ yield* this.stream((id) => ({ t: "invoke", id, capability, prompt: prompt2, options }));
85256
+ }
85257
+ /**
85258
+ * Proxy one conversational turn (`sendMessageStreaming`) to the
85259
+ * coordinator. A turn that pauses on `approval_request` ends its
85260
+ * stream; continue it with `resolveApproval`.
85261
+ */
85262
+ async *chat(text, options) {
85263
+ yield* this.stream((id) => ({ t: "chat", id, text, options }));
85264
+ }
85265
+ /**
85266
+ * Resolve a pending approval on the coordinator
85267
+ * (`resolveApprovalVote`); yields the continuation turn's chunks.
85268
+ */
85269
+ async *resolveApproval(approved, approverId) {
85270
+ yield* this.stream((id) => ({
85271
+ t: "resolve_approval",
85272
+ id,
85273
+ approved,
85274
+ approver_id: approverId
85275
+ }));
85276
+ }
85277
+ /**
85278
+ * Contribute this frontend's organs. Replaces the previous set, both
85279
+ * locally and on the coordinator (idempotent).
85280
+ */
85281
+ setBridgedCapabilities(handlers) {
85282
+ if (this.closed)
85283
+ throw new Error("runtime-host client is closed");
85284
+ this.capabilityHandlers = new Map(Object.entries(handlers));
85285
+ this.conn.send(encodeFrame({
85286
+ t: "register_capabilities",
85287
+ capabilities: [...this.capabilityHandlers.keys()]
85288
+ }));
85289
+ }
85290
+ /** Subscribe to a coordinator event channel. Returns the unsubscriber. */
85291
+ subscribe(channel, handler) {
85292
+ let handlers = this.eventHandlers.get(channel);
85293
+ if (handlers === void 0) {
85294
+ handlers = /* @__PURE__ */ new Set();
85295
+ this.eventHandlers.set(channel, handlers);
85296
+ this.conn.send(encodeFrame({ t: "subscribe", channel }));
85297
+ }
85298
+ handlers.add(handler);
85299
+ return () => {
85300
+ const current = this.eventHandlers.get(channel);
85301
+ if (current === void 0)
85302
+ return;
85303
+ current.delete(handler);
85304
+ if (current.size === 0) {
85305
+ this.eventHandlers.delete(channel);
85306
+ if (!this.closed)
85307
+ this.conn.send(encodeFrame({ t: "unsubscribe", channel }));
85308
+ }
85309
+ };
85310
+ }
85311
+ /** Fires once when the coordinator connection ends — the re-elect signal. */
85312
+ onClose(handler) {
85313
+ this.closeHandlers.add(handler);
85314
+ return () => this.closeHandlers.delete(handler);
85315
+ }
85316
+ close() {
85317
+ this.teardown(null);
85318
+ }
85319
+ async *stream(buildFrame) {
85320
+ if (this.closed)
85321
+ throw new Error("runtime-host client is closed");
85322
+ this.requestCounter += 1;
85323
+ const id = `req-${this.requestCounter}-${globalThis.crypto.randomUUID().slice(0, 8)}`;
85324
+ const state = { queue: [], done: false, error: null, wake: null };
85325
+ this.inflight.set(id, state);
85326
+ this.conn.send(encodeFrame(buildFrame(id)));
85327
+ try {
85328
+ for (; ; ) {
85329
+ while (state.queue.length > 0)
85330
+ yield state.queue.shift();
85331
+ if (state.error !== null)
85332
+ throw state.error;
85333
+ if (state.done)
85334
+ return;
85335
+ await new Promise((resolve14) => {
85336
+ state.wake = resolve14;
85337
+ });
85338
+ state.wake = null;
85339
+ }
85340
+ } finally {
85341
+ this.inflight.delete(id);
85342
+ }
85343
+ }
85344
+ wire(decoder2) {
85345
+ this.conn.onData((data) => {
85346
+ let frames;
85347
+ try {
85348
+ frames = decoder2.push(data);
85349
+ } catch (err2) {
85350
+ this.teardown(new Error(`malformed coordinator frame: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 }));
85351
+ return;
85352
+ }
85353
+ for (const frame of frames)
85354
+ this.onMessage(frame);
85355
+ });
85356
+ this.conn.onClose(() => this.teardown(new Error("coordinator connection closed")));
85357
+ }
85358
+ onMessage(message2) {
85359
+ switch (message2.t) {
85360
+ case "chunk": {
85361
+ const state = this.inflight.get(message2.id);
85362
+ if (state !== void 0) {
85363
+ state.queue.push(message2.chunk);
85364
+ state.wake?.();
85365
+ }
85366
+ return;
85367
+ }
85368
+ case "end": {
85369
+ const state = this.inflight.get(message2.id);
85370
+ if (state !== void 0) {
85371
+ state.done = true;
85372
+ state.wake?.();
85373
+ }
85374
+ return;
85375
+ }
85376
+ case "invoke_error": {
85377
+ const state = this.inflight.get(message2.id);
85378
+ if (state !== void 0) {
85379
+ state.error = new Error(message2.message);
85380
+ state.wake?.();
85381
+ }
85382
+ return;
85383
+ }
85384
+ case "event": {
85385
+ const handlers = this.eventHandlers.get(message2.channel);
85386
+ if (handlers !== void 0) {
85387
+ for (const handler of handlers)
85388
+ handler(message2.payload);
85389
+ }
85390
+ return;
85391
+ }
85392
+ case "bridge_invoke": {
85393
+ if (typeof message2.id === "string" && typeof message2.capability === "string" && typeof message2.prompt === "string") {
85394
+ void this.runBridgedInvocation(message2.id, message2.capability, message2.prompt, message2.options);
85395
+ }
85396
+ return;
85397
+ }
85398
+ default:
85399
+ return;
85400
+ }
85401
+ }
85402
+ /** Answer a coordinator-initiated bridged invocation. */
85403
+ async runBridgedInvocation(id, capability, prompt2, options) {
85404
+ const handler = this.capabilityHandlers.get(capability);
85405
+ if (handler === void 0) {
85406
+ this.conn.send(encodeFrame({
85407
+ t: "bridge_error",
85408
+ id,
85409
+ message: `capability "${capability}" is not contributed by this frontend`
85410
+ }));
85411
+ return;
85412
+ }
85413
+ const controller = new AbortController();
85414
+ this.bridgeAborts.set(id, controller);
85415
+ try {
85416
+ for await (const chunk of handler(prompt2, options, { signal: controller.signal })) {
85417
+ if (controller.signal.aborted || this.closed)
85418
+ return;
85419
+ this.conn.send(encodeFrame({ t: "bridge_chunk", id, chunk }));
85420
+ }
85421
+ if (!this.closed)
85422
+ this.conn.send(encodeFrame({ t: "bridge_end", id }));
85423
+ } catch (err2) {
85424
+ if (!this.closed) {
85425
+ this.conn.send(encodeFrame({
85426
+ t: "bridge_error",
85427
+ id,
85428
+ message: err2 instanceof Error ? err2.message : String(err2)
85429
+ }));
85430
+ }
85431
+ } finally {
85432
+ this.bridgeAborts.delete(id);
85433
+ }
85434
+ }
85435
+ teardown(cause) {
85436
+ if (this.closed)
85437
+ return;
85438
+ this.closed = true;
85439
+ for (const state of this.inflight.values()) {
85440
+ if (!state.done && state.error === null) {
85441
+ state.error = cause ?? new Error("runtime-host client closed");
85442
+ }
85443
+ state.wake?.();
85444
+ }
85445
+ for (const controller of this.bridgeAborts.values())
85446
+ controller.abort();
85447
+ this.bridgeAborts.clear();
85448
+ this.conn.destroy();
85449
+ for (const handler of this.closeHandlers)
85450
+ handler();
85451
+ this.closeHandlers.clear();
85452
+ }
85453
+ };
85454
+
85455
+ // ../../packages/runtime-host/dist/safe-options.js
85456
+ init_esm_shims();
85457
+ function pickSafeChatOptions(options) {
85458
+ if (options === void 0)
85459
+ return void 0;
85460
+ const picked = {};
85461
+ if (typeof options["delegationScope"] === "string") {
85462
+ picked.delegationScope = options["delegationScope"];
85463
+ }
85464
+ if (typeof options["suppressHistory"] === "boolean") {
85465
+ picked.suppressHistory = options["suppressHistory"];
85466
+ }
85467
+ return picked;
85468
+ }
85469
+ function pickSafeInvokeOptions(options) {
85470
+ const picked = {};
85471
+ if (options === void 0)
85472
+ return picked;
85473
+ if (typeof options["targetWorkerId"] === "string") {
85474
+ picked.targetWorkerId = options["targetWorkerId"];
85475
+ }
85476
+ if (typeof options["acknowledgeNoHistoryRisk"] === "boolean") {
85477
+ picked.acknowledgeNoHistoryRisk = options["acknowledgeNoHistoryRisk"];
85478
+ }
85479
+ return picked;
85480
+ }
85481
+
85482
+ // ../../packages/runtime-host/dist/bridged-tools.js
85483
+ init_esm_shims();
85484
+ var AI_LOOP_EXCLUDED_ORGANS = /* @__PURE__ */ new Map([
85485
+ [
85486
+ "se_attestation",
85487
+ "hardware-attestation minting is a user-initiated identity affordance (docs/doctrine/hardware-attestation.md), never a model-chosen act"
85488
+ ]
85489
+ ]);
85490
+ function isToolResultShaped(value) {
85491
+ return typeof value === "object" && value !== null && typeof value.ok === "boolean";
85492
+ }
85493
+ function bridgedToolRegistry(server, definitions) {
85494
+ for (const capability of Object.keys(definitions)) {
85495
+ const exclusion = AI_LOOP_EXCLUDED_ORGANS.get(capability);
85496
+ if (exclusion !== void 0) {
85497
+ throw new Error(`bridged organ "${capability}" must not surface to the AI loop as a tool: ${exclusion}`);
85498
+ }
85499
+ }
85500
+ const exposed = () => server.bridgedCapabilities.filter((capability) => definitions[capability] !== void 0).map((capability) => ({ capability, definition: definitions[capability] }));
85501
+ return {
85502
+ list() {
85503
+ return exposed().map((e2) => e2.definition);
85504
+ },
85505
+ async execute(name, args) {
85506
+ const entry = exposed().find((e2) => e2.definition.name === name);
85507
+ if (entry === void 0) {
85508
+ return { ok: false, error: `no attached frontend currently contributes tool "${name}"` };
85509
+ }
85510
+ const chunks = [];
85511
+ try {
85512
+ for await (const chunk of server.invokeBridged(entry.capability, "", args)) {
85513
+ chunks.push(chunk);
85514
+ }
85515
+ } catch (err2) {
85516
+ return { ok: false, error: err2 instanceof Error ? err2.message : String(err2) };
85517
+ }
85518
+ const data = chunks.length === 1 ? chunks[0] : chunks;
85519
+ if (isToolResultShaped(data))
85520
+ return data;
85521
+ return { ok: true, data };
85522
+ },
85523
+ register() {
85524
+ throw new Error("bridged tool registry is a read-only view over attached frontends' organs");
85525
+ }
85526
+ };
85527
+ }
85528
+ var BRIDGED_ORGAN_TOOL_SOURCE = "runtime-host:bridged-organs";
85529
+ function wireBridgedOrganTools(server, host, definitions) {
85530
+ const registry = bridgedToolRegistry(server, definitions);
85531
+ const sync = () => {
85532
+ host.unregisterExternalTools(BRIDGED_ORGAN_TOOL_SOURCE);
85533
+ host.registerExternalTools(BRIDGED_ORGAN_TOOL_SOURCE, registry);
85534
+ };
85535
+ const unsubscribe = server.onBridgedCapabilitiesChanged(sync);
85536
+ sync();
85537
+ return unsubscribe;
85538
+ }
85539
+
85540
+ // ../../packages/runtime-host/dist/election.js
85541
+ init_esm_shims();
85542
+ var sleep = (ms) => new Promise((resolve14) => setTimeout(resolve14, ms));
85543
+ async function probeSocketLive(platform, socketPath, timeoutMs) {
85544
+ const conn = await platform.connect(socketPath, timeoutMs);
85545
+ if (conn === null)
85546
+ return false;
85547
+ conn.destroy();
85548
+ return true;
85549
+ }
85550
+ async function acquireTakeoverMutex(platform, mutexDir, pid, probePid) {
85551
+ for (let i3 = 0; i3 < 2; i3 += 1) {
85552
+ if (await platform.mkdirExclusive(mutexDir) === "created") {
85553
+ await platform.writeFile(`${mutexDir}/pid`, String(pid));
85554
+ return true;
85555
+ }
85556
+ const holderRaw = await platform.readFile(`${mutexDir}/pid`);
85557
+ if (holderRaw === null) {
85558
+ return false;
85559
+ }
85560
+ const holderPid = Number(holderRaw.trim());
85561
+ if (Number.isInteger(holderPid) && holderPid > 0 && await probePid(holderPid))
85562
+ return false;
85563
+ await platform.removeDir(mutexDir);
85564
+ }
85565
+ return false;
85566
+ }
85567
+ async function releaseTakeoverMutex(platform, mutexDir) {
85568
+ await platform.removeDir(mutexDir);
85569
+ }
85570
+ async function electRuntimeHost(opts) {
85571
+ const platform = opts.platform;
85572
+ const maxAttempts = opts.maxAttempts ?? 4;
85573
+ const retryDelayMs = opts.retryDelayMs ?? 150;
85574
+ const probePid = opts.probePid ?? ((pid) => platform.isPidAlive(pid));
85575
+ let lastError = null;
85576
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
85577
+ if (await probeSocketLive(platform, opts.socketPath, retryDelayMs * 2)) {
85578
+ let token;
85579
+ try {
85580
+ token = await opts.mintToken();
85581
+ } catch (err2) {
85582
+ throw new Error(`a coordinator is live at ${opts.socketPath} but minting the attach token failed: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 });
85583
+ }
85584
+ try {
85585
+ const client = await RuntimeHostClient.attach({
85586
+ platform,
85587
+ socketPath: opts.socketPath,
85588
+ token,
85589
+ handshakeTimeoutMs: opts.handshakeTimeoutMs
85590
+ });
85591
+ return { role: "frontend", client };
85592
+ } catch (err2) {
85593
+ if (err2 instanceof AttachRefusedError)
85594
+ throw err2;
85595
+ if (!(err2 instanceof CoordinatorUnreachableError)) {
85596
+ throw new Error(`runtime-host attach failed: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 });
85597
+ }
85598
+ lastError = err2;
85599
+ }
85600
+ }
85601
+ const lock = await readLockfile(platform, opts.lockfilePath);
85602
+ if (lock !== null && await probePid(lock.pid) && attempt < maxAttempts) {
85603
+ await sleep(retryDelayMs);
85604
+ continue;
85605
+ }
85606
+ const mutexDir = `${opts.lockfilePath}.takeover`;
85607
+ const pid = opts.pid ?? platform.pid;
85608
+ if (!await acquireTakeoverMutex(platform, mutexDir, pid, probePid)) {
85609
+ await sleep(retryDelayMs);
85610
+ continue;
85611
+ }
85612
+ try {
85613
+ if (await probeSocketLive(platform, opts.socketPath, retryDelayMs * 2)) {
85614
+ continue;
85615
+ }
85616
+ if (!isWindowsPipePath(opts.socketPath)) {
85617
+ await platform.removeSocketFile(opts.socketPath);
85618
+ }
85619
+ const server = await RuntimeHostServer.bind(opts);
85620
+ return { role: "coordinator", server };
85621
+ } catch (err2) {
85622
+ if (err2 instanceof CoordinatorAlreadyBoundError) {
85623
+ lastError = err2;
85624
+ await sleep(retryDelayMs);
85625
+ continue;
85626
+ }
85627
+ throw err2;
85628
+ } finally {
85629
+ await releaseTakeoverMutex(platform, mutexDir);
85630
+ }
85631
+ }
85632
+ throw new Error(`runtime-host election did not converge after ${maxAttempts} attempts: ${lastError instanceof Error ? lastError.message : String(lastError)}`, { cause: lastError });
85633
+ }
85634
+
85635
+ // ../../packages/runtime-host/dist/node.js
85636
+ init_esm_shims();
85637
+
85638
+ // ../../packages/runtime-host/dist/node-platform.js
85639
+ init_esm_shims();
85640
+ import { chmodSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
85641
+ import { connect, createServer } from "net";
85642
+ import { dirname as dirname4 } from "path";
85643
+ function isWindowsPipe(path19) {
85644
+ return path19.startsWith("\\\\.\\pipe\\");
85645
+ }
85646
+ function wrapSocket(socket) {
85647
+ return {
85648
+ send: (data) => {
85649
+ if (!socket.destroyed)
85650
+ socket.write(data);
85651
+ },
85652
+ onData: (cb) => {
85653
+ socket.on("data", cb);
85654
+ },
85655
+ onClose: (cb) => {
85656
+ socket.on("close", cb);
85657
+ socket.on("error", () => socket.destroy());
85658
+ },
85659
+ end: () => socket.end(),
85660
+ destroy: () => socket.destroy(),
85661
+ get destroyed() {
85662
+ return socket.destroyed;
85663
+ }
85664
+ };
85665
+ }
85666
+ var NodeFrameListener = class {
85667
+ server;
85668
+ constructor(server) {
85669
+ this.server = server;
85670
+ }
85671
+ onConnection(cb) {
85672
+ this.server.on("connection", (socket) => cb(wrapSocket(socket)));
85673
+ }
85674
+ close() {
85675
+ return new Promise((resolve14) => {
85676
+ this.server.close(() => resolve14());
85677
+ });
85678
+ }
85679
+ };
85680
+ function nodePlatform(overrides = {}) {
85681
+ return {
85682
+ pid: overrides.pid ?? process.pid,
85683
+ connect(socketPath, timeoutMs) {
85684
+ return new Promise((resolve14) => {
85685
+ const socket = connect(socketPath);
85686
+ const timer = setTimeout(() => {
85687
+ socket.destroy();
85688
+ resolve14(null);
85689
+ }, timeoutMs);
85690
+ socket.once("connect", () => {
85691
+ clearTimeout(timer);
85692
+ resolve14(wrapSocket(socket));
85693
+ });
85694
+ socket.once("error", () => {
85695
+ clearTimeout(timer);
85696
+ resolve14(null);
85697
+ });
85698
+ });
85699
+ },
85700
+ bind(socketPath) {
85701
+ return new Promise((resolve14, reject) => {
85702
+ const server = createServer();
85703
+ const onError = (err2) => {
85704
+ server.removeListener("listening", onListening);
85705
+ if (err2.code === "EADDRINUSE")
85706
+ resolve14("in_use");
85707
+ else
85708
+ reject(new Error(`runtime-host bind failed: ${err2.message}`, { cause: err2 }));
85709
+ };
85710
+ const onListening = () => {
85711
+ server.removeListener("error", onError);
85712
+ if (!isWindowsPipe(socketPath)) {
85713
+ try {
85714
+ chmodSync(socketPath, 384);
85715
+ } catch (err2) {
85716
+ server.close();
85717
+ reject(new Error(`runtime-host bind failed: could not restrict socket permissions: ${err2 instanceof Error ? err2.message : String(err2)}`, { cause: err2 }));
85718
+ return;
85719
+ }
85720
+ }
85721
+ resolve14(new NodeFrameListener(server));
85722
+ };
85723
+ server.once("error", onError);
85724
+ server.once("listening", onListening);
85725
+ server.listen(socketPath);
85726
+ });
85727
+ },
85728
+ // eslint-disable-next-line @typescript-eslint/require-await
85729
+ async removeSocketFile(socketPath) {
85730
+ if (isWindowsPipe(socketPath))
85731
+ return;
85732
+ try {
85733
+ rmSync2(socketPath, { force: true });
85734
+ } catch {
85735
+ }
85736
+ },
85737
+ // eslint-disable-next-line @typescript-eslint/require-await
85738
+ async readFile(path19) {
85739
+ try {
85740
+ return readFileSync4(path19, "utf8");
85741
+ } catch {
85742
+ return null;
85743
+ }
85744
+ },
85745
+ // eslint-disable-next-line @typescript-eslint/require-await
85746
+ async writeFile(path19, content) {
85747
+ mkdirSync4(dirname4(path19), { recursive: true, mode: 448 });
85748
+ writeFileSync4(path19, content, { mode: 384 });
85749
+ },
85750
+ // eslint-disable-next-line @typescript-eslint/require-await
85751
+ async removeFile(path19) {
85752
+ rmSync2(path19, { force: true });
85753
+ },
85754
+ // eslint-disable-next-line @typescript-eslint/require-await
85755
+ async mkdirExclusive(path19) {
85756
+ mkdirSync4(dirname4(path19), { recursive: true, mode: 448 });
85757
+ try {
85758
+ mkdirSync4(path19);
85759
+ return "created";
85760
+ } catch {
85761
+ return "exists";
85762
+ }
85763
+ },
85764
+ // eslint-disable-next-line @typescript-eslint/require-await
85765
+ async removeDir(path19) {
85766
+ try {
85767
+ rmSync2(path19, { recursive: true, force: true });
85768
+ } catch {
85769
+ }
85770
+ },
85771
+ // eslint-disable-next-line @typescript-eslint/require-await
85772
+ async isPidAlive(pid) {
85773
+ if (!Number.isInteger(pid) || pid <= 0)
85774
+ return false;
85775
+ try {
85776
+ process.kill(pid, 0);
85777
+ return true;
85778
+ } catch (err2) {
85779
+ return err2.code === "EPERM";
85780
+ }
85781
+ }
85782
+ };
85783
+ }
85784
+
85785
+ // ../../packages/runtime-host/dist/paths.js
85786
+ init_esm_shims();
85787
+ import { createHash } from "crypto";
85788
+ import { homedir as homedir3 } from "os";
85789
+ import { join as join7 } from "path";
85790
+ function defaultRuntimeHostPaths(home = homedir3(), platform = process.platform) {
85791
+ const dir = join7(home, ".motebit");
85792
+ if (platform === "win32") {
85793
+ const tag2 = createHash("sha256").update(dir).digest("hex").slice(0, 16);
85794
+ return {
85795
+ socketPath: `\\\\.\\pipe\\motebit-runtime-${tag2}`,
85796
+ lockfilePath: join7(dir, "runtime.lock")
85797
+ };
85798
+ }
85799
+ return {
85800
+ socketPath: join7(dir, "runtime.sock"),
85801
+ lockfilePath: join7(dir, "runtime.lock")
85802
+ };
85803
+ }
85804
+
85805
+ // ../../packages/tools/dist/web-safe.js
85806
+ init_esm_shims();
85807
+ init_web_search();
85808
+ init_read_url();
85809
+ init_recall_memories();
85810
+ init_current_time();
85811
+ init_list_events();
85812
+ init_rewrite_memory();
85813
+ init_search_conversations();
85814
+ init_self_reflect();
85815
+ init_web_search();
85816
+ init_read_url();
85817
+ init_recall_memories();
85818
+ init_current_time();
85819
+ init_recall_self();
85820
+ init_list_events();
85821
+ init_self_reflect();
85822
+ init_search_provider();
85823
+ init_brave_search();
85824
+ init_duckduckgo();
85825
+ init_proxy_search();
85826
+ init_tavily_search();
85827
+ init_biased_search();
85828
+ init_goal_tools();
85829
+ init_computer();
85830
+ init_request_control();
85831
+ init_read_page();
85832
+ init_registry4();
85833
+
85834
+ // src/runtime-host.ts
85835
+ init_identity();
85836
+ function requireRuntime(ref) {
85837
+ if (ref.current === null) {
85838
+ throw new Error("coordinator runtime is still starting \u2014 retry shortly");
85839
+ }
85840
+ return ref.current;
85841
+ }
85842
+ async function electCliRuntimeHost(deps) {
85843
+ const paths = deps.paths ?? defaultRuntimeHostPaths();
85844
+ const deviceId = deps.fullConfig.device_id;
85845
+ const devicePublicKey = deps.fullConfig.device_public_key;
85846
+ return electRuntimeHost({
85847
+ platform: nodePlatform(),
85848
+ socketPath: paths.socketPath,
85849
+ lockfilePath: paths.lockfilePath,
85850
+ motebitId: deps.motebitId,
85851
+ resolveDevicePublicKey: (did) => deviceId != null && did === deviceId && devicePublicKey != null && devicePublicKey !== "" ? fromHex3(devicePublicKey) : null,
85852
+ onInvoke: (capability, prompt2, options, ctx) => requireRuntime(deps.runtimeRef).invokeCapability(capability, prompt2, {
85853
+ ...pickSafeInvokeOptions(options),
85854
+ signal: ctx.signal
85855
+ }),
85856
+ // sendMessageStreaming has no abort seam; a frontend disconnect
85857
+ // lets the turn run to completion on the coordinator (its memory
85858
+ // and receipts stay consistent), which is the correct authority
85859
+ // semantics — the renderer left, the act didn't.
85860
+ onChat: (text, options) => requireRuntime(deps.runtimeRef).sendMessageStreaming(
85861
+ text,
85862
+ void 0,
85863
+ pickSafeChatOptions(options)
85864
+ ),
85865
+ onResolveApproval: (approved, approverId) => requireRuntime(deps.runtimeRef).resolveApprovalVote(approved, approverId),
85866
+ mintToken: async () => {
85867
+ if (deviceId == null || deviceId === "") {
85868
+ throw new Error(
85869
+ "no device_id in ~/.motebit/config.json \u2014 run `motebit` once to mint an identity"
85870
+ );
85871
+ }
85872
+ return mintAttachToken({ motebitId: deps.motebitId, deviceId }, await deps.loadPrivateKey());
85873
+ }
85874
+ });
85875
+ }
85876
+ function wireBridgedOrgans(server, runtime) {
85877
+ wireBridgedOrganTools(server, runtime, {
85878
+ computer_use: { ...computerDefinition, embodimentMode: "desktop_drive" }
85879
+ });
85880
+ }
85881
+ async function electCoordinatorRole(fullConfig, motebitId, runtimeRef) {
85882
+ let election;
85883
+ try {
85884
+ election = await electCliRuntimeHost({
85885
+ fullConfig,
85886
+ motebitId,
85887
+ loadPrivateKey: async () => (await loadActiveSigningKey(fullConfig, {
85888
+ promptLabel: "Passphrase (to verify the running coordinator): "
85889
+ })).privateKey,
85890
+ runtimeRef
85891
+ });
85892
+ } catch (err2) {
85893
+ console.error(
85894
+ `Runtime-host election failed: ${err2 instanceof Error ? err2.message : String(err2)}`
85895
+ );
85896
+ console.error(
85897
+ "A coordinator may already be running with an incompatible build or a locked signing key. Stop it and retry."
85898
+ );
85899
+ process.exit(1);
85900
+ }
85901
+ if (election.role === "frontend") {
85902
+ const pid = election.client.coordinatorPid;
85903
+ election.client.close();
85904
+ console.error(`Another motebit process is already coordinating this machine (pid ${pid}).`);
85905
+ console.error(
85906
+ "One runtime per machine: stop that process first, or run `motebit` (no arguments) to attach a rendering REPL to it."
85907
+ );
85908
+ process.exit(1);
85909
+ }
85910
+ return election.server;
85911
+ }
85912
+
85913
+ // src/attached-repl.ts
85914
+ init_esm_shims();
85915
+ init_colors();
85916
+ async function renderStream(stream) {
85917
+ let pendingApproval = null;
85918
+ try {
85919
+ for await (const raw of stream) {
85920
+ const chunk = raw;
85921
+ switch (chunk.type) {
85922
+ case "text":
85923
+ if (typeof chunk.text === "string") writeOutput(chunk.text);
85924
+ break;
85925
+ case "tool_status":
85926
+ if (chunk.status === "calling") {
85927
+ writeOutput(`
85928
+ ${meta(`[${chunk.name ?? "tool"}]`)} `);
85929
+ }
85930
+ break;
85931
+ case "approval_request":
85932
+ pendingApproval = {
85933
+ name: chunk.name ?? "tool",
85934
+ args: chunk.args ?? {}
85935
+ };
85936
+ break;
85937
+ case "delegation_start":
85938
+ writeOutput(`
85939
+ ${dim(`[delegating \xB7 ${chunk.tool ?? ""}]`)} `);
85940
+ break;
85941
+ case "delegation_complete":
85942
+ if (chunk.receipt?.task_id != null) {
85943
+ writeOutput(
85944
+ `
85945
+ ${dim(`[receipt ${chunk.receipt.task_id.slice(0, 8)} \xB7 ${chunk.receipt.status ?? ""}]`)}
85946
+ `
85947
+ );
85948
+ }
85949
+ break;
85950
+ case "injection_warning":
85951
+ writeOutput(`
85952
+ ${warn("\u26A0")} suspicious content in ${chunk.tool_name ?? "tool"} output
85953
+ `);
85954
+ break;
85955
+ case "invoke_error":
85956
+ writeOutput(
85957
+ `
85958
+ ${warn(`[invoke failed${chunk.code != null ? ` \xB7 ${chunk.code}` : ""}]`)} ${chunk.message ?? ""}
85959
+ `
85960
+ );
85961
+ break;
85962
+ case "task_step_narration":
85963
+ if (typeof chunk.text === "string") writeOutput(dim(chunk.text));
85964
+ break;
85965
+ default:
85966
+ break;
85967
+ }
85968
+ }
85969
+ } catch (err2) {
85970
+ writeOutput(
85971
+ `
85972
+ ${warn("[coordinator error]")} ${err2 instanceof Error ? err2.message : String(err2)}
85973
+ `
85974
+ );
85975
+ }
85976
+ return { pendingApproval };
85977
+ }
85978
+ async function renderTurn(client, motebitId, stream) {
85979
+ let current = stream;
85980
+ for (; ; ) {
85981
+ const { pendingApproval } = await renderStream(current);
85982
+ if (pendingApproval === null) return;
85983
+ const argsPreview = JSON.stringify(pendingApproval.args).slice(0, 120);
85984
+ const answer = await askQuestion(
85985
+ `
85986
+ ${warn("?")} ${pendingApproval.name}(${argsPreview})
85987
+ Allow? (y/n) `
85988
+ );
85989
+ const approved = answer.trim().toLowerCase() === "y";
85990
+ current = client.resolveApproval(approved, motebitId);
85991
+ }
85992
+ }
85993
+ var ATTACHED_HELP = `
85994
+ Attached mode \u2014 the coordinator process owns the runtime; this terminal renders.
85995
+ <text> chat with your motebit (proxied to the coordinator)
85996
+ /invoke <cap> <text> invoke a capability deterministically
85997
+ /help this help
85998
+ /exit leave (the coordinator keeps running)
85999
+ Other slash commands need the runtime in-process: exit, stop the coordinator, and re-run \`motebit\`.
86000
+ `;
86001
+ async function runAttachedRepl(client, motebitId) {
86002
+ writeOutput(
86003
+ `
86004
+ ${dim("Attached to the machine's coordinator runtime")} ${dim(`(pid ${client.coordinatorPid})`)}
86005
+ ${dim("This terminal renders; the coordinator acts. /help for what's available.")}
86006
+
86007
+ `
86008
+ );
86009
+ let closed = false;
86010
+ client.onClose(() => {
86011
+ closed = true;
86012
+ writeOutput(
86013
+ `
86014
+ ${warn("Coordinator exited.")} Run \`motebit\` again to take over as coordinator.
86015
+ `
86016
+ );
86017
+ destroyTerminal();
86018
+ process.exit(0);
86019
+ });
86020
+ for (; ; ) {
86021
+ if (closed) return;
86022
+ let line;
86023
+ try {
86024
+ line = await readInput(prompt("you>") + " ");
86025
+ } catch {
86026
+ break;
86027
+ }
86028
+ const trimmed = line.trim();
86029
+ if (trimmed === "") continue;
86030
+ if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "exit" || trimmed === "quit") {
86031
+ writeOutput("Goodbye! (coordinator keeps running)\n");
86032
+ break;
86033
+ }
86034
+ if (trimmed === "/help") {
86035
+ writeOutput(ATTACHED_HELP);
86036
+ continue;
86037
+ }
86038
+ if (trimmed.startsWith("/invoke ")) {
86039
+ const rest = trimmed.slice("/invoke ".length).trim();
86040
+ const space = rest.indexOf(" ");
86041
+ const capability = space === -1 ? rest : rest.slice(0, space);
86042
+ const prompt2 = space === -1 ? "" : rest.slice(space + 1);
86043
+ if (capability === "") {
86044
+ writeOutput(`${warn("usage:")} /invoke <capability> <prompt>
86045
+ `);
86046
+ continue;
86047
+ }
86048
+ writeOutput(prompt("mote>") + " ");
86049
+ await renderTurn(client, motebitId, client.invoke(capability, prompt2));
86050
+ writeOutput("\n");
86051
+ continue;
86052
+ }
86053
+ if (trimmed.startsWith("/")) {
86054
+ writeOutput(
86055
+ `${dim(`${trimmed.split(" ")[0]} needs the runtime in-process \u2014 not available in attached mode.`)}
86056
+ ${dim("Stop the coordinator and re-run `motebit` to use it.")}
86057
+ `
86058
+ );
86059
+ continue;
86060
+ }
86061
+ writeOutput(prompt("mote>") + " ");
86062
+ await renderTurn(client, motebitId, client.chat(trimmed));
86063
+ writeOutput("\n");
86064
+ }
86065
+ client.close();
86066
+ destroyTerminal();
86067
+ }
86068
+
84354
86069
  // src/index.ts
84355
86070
  init_colors();
84356
86071
 
@@ -84545,7 +86260,7 @@ init_dist6();
84545
86260
 
84546
86261
  // ../../packages/mcp-server/dist/bootstrap-service.js
84547
86262
  init_esm_shims();
84548
- import { writeFileSync as writeFileSync5 } from "fs";
86263
+ import { writeFileSync as writeFileSync6 } from "fs";
84549
86264
  import { resolve as resolve6 } from "path";
84550
86265
 
84551
86266
  // ../../packages/core-identity/dist/node.js
@@ -84553,12 +86268,12 @@ init_esm_shims();
84553
86268
 
84554
86269
  // ../../packages/core-identity/dist/file-stores.js
84555
86270
  init_esm_shims();
84556
- import { existsSync, readFileSync as readFileSync4, writeFileSync as writeFileSync4, renameSync as renameSync2, chmodSync } from "fs";
86271
+ import { existsSync, readFileSync as readFileSync5, writeFileSync as writeFileSync5, renameSync as renameSync2, chmodSync as chmodSync2 } from "fs";
84557
86272
 
84558
86273
  // ../../packages/core-identity/dist/bootstrap-service.js
84559
86274
  init_esm_shims();
84560
- import { mkdirSync as mkdirSync4, existsSync as existsSync2 } from "fs";
84561
- import { join as join7 } from "path";
86275
+ import { mkdirSync as mkdirSync5, existsSync as existsSync2 } from "fs";
86276
+ import { join as join8 } from "path";
84562
86277
  init_dist9();
84563
86278
  init_dist8();
84564
86279
 
@@ -85338,7 +87053,7 @@ var McpServerAdapter = class _McpServerAdapter {
85338
87053
  init_config2();
85339
87054
  init_dist31();
85340
87055
  init_node_fs();
85341
- import { join as join8 } from "path";
87056
+ import { join as join9 } from "path";
85342
87057
 
85343
87058
  // src/utils.ts
85344
87059
  init_esm_shims();
@@ -87352,7 +89067,7 @@ Curiosity targets (${targets.length}):
87352
89067
  }
87353
89068
  case "skills": {
87354
89069
  const registry = new SkillRegistry(
87355
- new NodeFsSkillStorageAdapter({ root: join8(CONFIG_DIR, "skills") })
89070
+ new NodeFsSkillStorageAdapter({ root: join9(CONFIG_DIR, "skills") })
87356
89071
  );
87357
89072
  const records = await registry.list();
87358
89073
  if (records.length === 0) {
@@ -87383,7 +89098,7 @@ Curiosity targets (${targets.length}):
87383
89098
  break;
87384
89099
  }
87385
89100
  const registry = new SkillRegistry(
87386
- new NodeFsSkillStorageAdapter({ root: join8(CONFIG_DIR, "skills") })
89101
+ new NodeFsSkillStorageAdapter({ root: join9(CONFIG_DIR, "skills") })
87387
89102
  );
87388
89103
  const record = await registry.get(name);
87389
89104
  if (!record) {
@@ -87899,12 +89614,13 @@ init_config2();
87899
89614
  init_runtime_factory();
87900
89615
  async function handleDelegatePlan(config, motebitId, prompt2) {
87901
89616
  const relayUrl = getRelayUrl(config);
89617
+ const runtimeRef = { current: null };
89618
+ const hostServer = await electCoordinatorRole(loadFullConfig(), motebitId, runtimeRef);
87902
89619
  const authHeaders = await getRelayAuthHeaders(config, { aud: "task:submit", json: true });
87903
89620
  const { createProvider: createProvider2, buildToolRegistry: buildToolRegistry2, buildStorageAdapters: buildStorageAdapters2, deriveGovernanceForRuntime: deriveGovernanceForRuntime2 } = await Promise.resolve().then(() => (init_runtime_factory(), runtime_factory_exports));
87904
89621
  const { MotebitRuntime: MotebitRuntime2, NullRenderer: NullRenderer2, PLANNING_TASK_ROUTER: PLANNING_TASK_ROUTER2 } = await Promise.resolve().then(() => (init_dist22(), dist_exports6));
87905
89622
  const dbPath = getDbPath(config.dbPath);
87906
89623
  const moteDb = await openMotebitDatabase(dbPath);
87907
- const runtimeRef = { current: null };
87908
89624
  const provider = createProvider2(config);
87909
89625
  const registry = buildToolRegistry2(config, runtimeRef, motebitId);
87910
89626
  const storage = buildStorageAdapters2(moteDb);
@@ -88123,6 +89839,8 @@ Plan failed: ${chunk.reason}`);
88123
89839
  } finally {
88124
89840
  runtime.stop();
88125
89841
  moteDb.close();
89842
+ await hostServer.close().catch(() => {
89843
+ });
88126
89844
  }
88127
89845
  }
88128
89846
  async function handleDelegate(config) {
@@ -89459,7 +91177,7 @@ async function handleLogs(config) {
89459
91177
  if (config.tail !== true) return;
89460
91178
  let lastShown = initial.length > 0 ? initial[0].ran_at : 0;
89461
91179
  while (true) {
89462
- await sleep(POLL_INTERVAL_MS2);
91180
+ await sleep2(POLL_INTERVAL_MS2);
89463
91181
  const fresh = moteDb.goalOutcomeStore.listForGoal(goal.goal_id, 50);
89464
91182
  const newer = [...fresh].filter((o5) => o5.ran_at > lastShown).reverse();
89465
91183
  for (const o5 of newer) {
@@ -89484,7 +91202,7 @@ function printOutcome(ranAt, status, summary, errMsg) {
89484
91202
  const body = summary ?? errMsg ?? "";
89485
91203
  console.log(`${prefix} ${dim(when.padStart(10))} ${body}`);
89486
91204
  }
89487
- function sleep(ms) {
91205
+ function sleep2(ms) {
89488
91206
  return new Promise((resolve14) => setTimeout(resolve14, ms));
89489
91207
  }
89490
91208
 
@@ -93621,6 +95339,7 @@ async function reconcileTreasury(config) {
93621
95339
 
93622
95340
  // ../../services/relay/dist/treasury-reconciliation.js
93623
95341
  init_logger();
95342
+ init_loop_supervisor();
93624
95343
  var logger19 = createLogger({ service: "treasury-reconciliation" });
93625
95344
  var DEFAULT_INTERVAL_MS = 15 * 6e4;
93626
95345
  var DEFAULT_CONFIRMATION_LAG_BUFFER_MS = 5 * 6e4;
@@ -93714,8 +95433,6 @@ function startTreasuryReconciliationLoop(config) {
93714
95433
  confirmationLagBufferMs
93715
95434
  });
93716
95435
  const tick = async () => {
93717
- if (config.isFrozen?.())
93718
- return;
93719
95436
  try {
93720
95437
  await reconcileTreasury({
93721
95438
  rpc: config.rpc,
@@ -93733,17 +95450,19 @@ function startTreasuryReconciliationLoop(config) {
93733
95450
  chain: config.chain,
93734
95451
  error: err2 instanceof Error ? err2.message : String(err2)
93735
95452
  });
95453
+ throw err2;
93736
95454
  }
93737
95455
  };
93738
- return setInterval(() => {
93739
- void tick();
93740
- }, intervalMs);
95456
+ return superviseInterval(config.supervisor, "treasury-reconciliation", intervalMs, tick, {
95457
+ ...config.isFrozen ? { isFrozen: config.isFrozen } : {}
95458
+ });
93741
95459
  }
93742
95460
 
93743
95461
  // ../../services/relay/dist/solana-treasury-reconciliation.js
93744
95462
  init_esm_shims();
93745
95463
  init_dist39();
93746
95464
  init_logger();
95465
+ init_loop_supervisor();
93747
95466
  import { randomUUID as randomUUID2 } from "crypto";
93748
95467
  var logger20 = createLogger({ service: "solana-treasury-reconciliation" });
93749
95468
  var DEFAULT_INTERVAL_MS2 = 15 * 6e4;
@@ -93803,8 +95522,6 @@ function startSolanaTreasuryReconciliationLoop(config) {
93803
95522
  confirmationLagBufferMs
93804
95523
  });
93805
95524
  const tick = async () => {
93806
- if (config.isFrozen?.())
93807
- return;
93808
95525
  try {
93809
95526
  await reconciler.reconcile({
93810
95527
  store,
@@ -93818,11 +95535,12 @@ function startSolanaTreasuryReconciliationLoop(config) {
93818
95535
  chain,
93819
95536
  error: err2 instanceof Error ? err2.message : String(err2)
93820
95537
  });
95538
+ throw err2;
93821
95539
  }
93822
95540
  };
93823
- return setInterval(() => {
93824
- void tick();
93825
- }, intervalMs);
95541
+ return superviseInterval(config.supervisor, "solana-treasury-reconciliation", intervalMs, tick, {
95542
+ ...config.isFrozen ? { isFrozen: config.isFrozen } : {}
95543
+ });
93826
95544
  }
93827
95545
 
93828
95546
  // ../../services/relay/dist/index.js
@@ -99716,6 +101434,7 @@ var TaskQueue = class {
99716
101434
 
99717
101435
  // ../../services/relay/dist/command-route.js
99718
101436
  init_esm_shims();
101437
+ init_dist5();
99719
101438
  import { HTTPException as HTTPException20 } from "hono/http-exception";
99720
101439
  var RELAY_SIDE_COMMANDS = /* @__PURE__ */ new Set(["balance", "deposits", "discover", "proposals"]);
99721
101440
  var RUNTIME_SIDE_COMMANDS = /* @__PURE__ */ new Set([
@@ -99750,6 +101469,22 @@ function registerCommandRoutes(deps) {
99750
101469
  }
99751
101470
  const command2 = body.command;
99752
101471
  const args = typeof body.args === "string" ? body.args : void 0;
101472
+ const registered = db.prepare("SELECT public_key FROM agent_registry WHERE motebit_id = ?").get(motebitId);
101473
+ if (!registered || registered.public_key === "") {
101474
+ throw new HTTPException20(401, {
101475
+ message: "Unknown agent identity \u2014 no registered public key to verify the command against"
101476
+ });
101477
+ }
101478
+ const verdict = await verifyAgentCommandEnvelope({
101479
+ envelope: body.envelope,
101480
+ command: command2,
101481
+ args,
101482
+ motebitId,
101483
+ identityPublicKey: registered.public_key
101484
+ });
101485
+ if (!verdict.ok) {
101486
+ throw new HTTPException20(401, { message: verdict.reason });
101487
+ }
99753
101488
  if (command2 in INFO_COMMANDS) {
99754
101489
  return c3.json({ summary: INFO_COMMANDS[command2] });
99755
101490
  }
@@ -99768,7 +101503,7 @@ function registerCommandRoutes(deps) {
99768
101503
  return c3.json({ summary: "Agent not connected." }, 404);
99769
101504
  }
99770
101505
  try {
99771
- const result = await forwardCommandToAgent(peers, command2, args);
101506
+ const result = await forwardCommandToAgent(peers, command2, args, body.envelope);
99772
101507
  return c3.json(result);
99773
101508
  } catch (err2) {
99774
101509
  const msg = err2 instanceof Error ? err2.message : String(err2);
@@ -99795,13 +101530,14 @@ function handleCommandResponse(commandId, result) {
99795
101530
  pendingCommands.delete(commandId);
99796
101531
  pending.resolve(result);
99797
101532
  }
99798
- async function forwardCommandToAgent(peers, command2, args) {
101533
+ async function forwardCommandToAgent(peers, command2, args, envelope) {
99799
101534
  const commandId = crypto.randomUUID();
99800
101535
  const payload = JSON.stringify({
99801
101536
  type: "command_request",
99802
101537
  id: commandId,
99803
101538
  command: command2,
99804
- args
101539
+ args,
101540
+ envelope
99805
101541
  });
99806
101542
  return new Promise((resolve14, reject) => {
99807
101543
  const timer = setTimeout(() => {
@@ -100380,7 +102116,7 @@ async function createSyncRelay(config) {
100380
102116
  const taskQueue = new TaskQueue(moteDb.db);
100381
102117
  const relayIdentity = await initRelayIdentity(moteDb.db, config.relayKeyPassphrase, createLogger({ service: "relay" }));
100382
102118
  const taskRouter = createTaskRouter({ db: moteDb.db, relayIdentity, federationConfig });
100383
- const { cache: federationQueryCache, pruneInterval: federationQueryPruneInterval } = createFederationQueryCache();
102119
+ const { cache: federationQueryCache, pruneInterval: federationQueryPruneInterval } = createFederationQueryCache(loopSupervisor);
100384
102120
  const app = new Hono();
100385
102121
  const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
100386
102122
  const logger39 = createLogger({ service: "relay" });
@@ -100424,7 +102160,7 @@ async function createSyncRelay(config) {
100424
102160
  if (STALE_ALLOCATION_HORIZON_MS < 3 * TASK_TTL_MS) {
100425
102161
  throw new Error(`STALE_ALLOCATION_HORIZON_MS (${STALE_ALLOCATION_HORIZON_MS}ms) must be >= 3x TASK_TTL_MS (${TASK_TTL_MS}ms) \u2014 sweeping a live task's allocation opens a refund/settlement double-credit race`);
100426
102162
  }
100427
- const taskCleanupInterval = setInterval(() => {
102163
+ const taskCleanupInterval = superviseInterval(loopSupervisor, "task-cleanup", 6e4, () => {
100428
102164
  const now = Date.now();
100429
102165
  taskQueue.cleanup(now);
100430
102166
  const evicted = taskQueue.evict(MAX_TASK_QUEUE_SIZE2);
@@ -100471,7 +102207,7 @@ async function createSyncRelay(config) {
100471
102207
  }
100472
102208
  } catch {
100473
102209
  }
100474
- }, 6e4);
102210
+ });
100475
102211
  registerWebSocketRoutes({
100476
102212
  app,
100477
102213
  upgradeWebSocket,
@@ -100572,7 +102308,8 @@ async function createSyncRelay(config) {
100572
102308
  });
100573
102309
  const orchestrationWorkerInterval = startDeferredOrchestrationWorker2({
100574
102310
  db: moteDb.db,
100575
- relayIdentity
102311
+ relayIdentity,
102312
+ supervisor: loopSupervisor
100576
102313
  });
100577
102314
  logger39.info("dispute_orchestration_worker.started");
100578
102315
  const { registerTransparencyRoutes: registerTransparencyRoutes2 } = await Promise.resolve().then(() => (init_transparency2(), transparency_exports));
@@ -100779,8 +102516,8 @@ async function createSyncRelay(config) {
100779
102516
  isAgentRevoked
100780
102517
  });
100781
102518
  registerCommandRoutes({ app, db: moteDb.db, connections, logger: logger39 });
100782
- const heartbeatInterval = startHeartbeatLoop(moteDb.db, relayIdentity, 6e4, () => getEmergencyFreeze());
100783
- const revocationHorizonInterval = startRevocationHorizonLoop(moteDb.db, { relayIdentity, witnessSolicitationTimeoutMs: federationConfig?.witnessSolicitationTimeoutMs }, federationConfig?.revocationHorizonIntervalMs, () => getEmergencyFreeze());
102519
+ const heartbeatInterval = startHeartbeatLoop(moteDb.db, relayIdentity, 6e4, () => getEmergencyFreeze(), loopSupervisor);
102520
+ const revocationHorizonInterval = startRevocationHorizonLoop(moteDb.db, { relayIdentity, witnessSolicitationTimeoutMs: federationConfig?.witnessSolicitationTimeoutMs }, federationConfig?.revocationHorizonIntervalMs, () => getEmergencyFreeze(), loopSupervisor);
100784
102521
  const settlementRetryInterval = startSettlementRetryLoop(
100785
102522
  moteDb.db,
100786
102523
  relayIdentity,
@@ -100874,16 +102611,17 @@ async function createSyncRelay(config) {
100874
102611
  }
100875
102612
  })();
100876
102613
  }
100877
- const batchAnchorInterval = startBatchAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze());
100878
- const agentAnchorInterval = startAgentSettlementAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze());
102614
+ const batchAnchorInterval = startBatchAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze(), loopSupervisor);
102615
+ const agentAnchorInterval = startAgentSettlementAnchorLoop(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze(), loopSupervisor);
100879
102616
  credentialAnchorConfig.submitter = anchorSubmitter;
100880
- const credentialAnchorInterval = startCredentialAnchorLoop(moteDb.db, relayIdentity, credentialAnchorConfig, () => getEmergencyFreeze());
102617
+ const credentialAnchorInterval = startCredentialAnchorLoop(moteDb.db, relayIdentity, credentialAnchorConfig, () => getEmergencyFreeze(), loopSupervisor);
100881
102618
  const { startIdentityLogAnchorLoop: startIdentityLogAnchorLoop2 } = await Promise.resolve().then(() => (init_identity_log_anchoring(), identity_log_anchoring_exports));
100882
- const identityLogAnchorInterval = startIdentityLogAnchorLoop2(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze());
102619
+ const identityLogAnchorInterval = startIdentityLogAnchorLoop2(moteDb.db, relayIdentity, { submitter: anchorSubmitter }, () => getEmergencyFreeze(), loopSupervisor);
100883
102620
  const depositDetectorChain = x402Config.network;
100884
102621
  const depositDetectorInterval = startDepositDetector({
100885
102622
  db: moteDb.db,
100886
- chain: depositDetectorChain
102623
+ chain: depositDetectorChain,
102624
+ supervisor: loopSupervisor
100887
102625
  });
100888
102626
  let treasuryReconciliationInterval;
100889
102627
  {
@@ -100901,7 +102639,8 @@ async function createSyncRelay(config) {
100901
102639
  treasuryAddress: x402Config.payToAddress,
100902
102640
  usdcContractAddress,
100903
102641
  intervalMs,
100904
- isFrozen: () => getEmergencyFreeze()
102642
+ isFrozen: () => getEmergencyFreeze(),
102643
+ supervisor: loopSupervisor
100905
102644
  });
100906
102645
  } else {
100907
102646
  logger39.info("treasury-reconciliation.disabled", {
@@ -100933,7 +102672,8 @@ async function createSyncRelay(config) {
100933
102672
  // negative-drift on every cycle.
100934
102673
  ...solanaUsdcMint ? { usdcMint: solanaUsdcMint } : {},
100935
102674
  intervalMs: solanaReconciliationIntervalMs,
100936
- isFrozen: () => getEmergencyFreeze()
102675
+ isFrozen: () => getEmergencyFreeze(),
102676
+ supervisor: loopSupervisor
100937
102677
  });
100938
102678
  } else {
100939
102679
  logger39.info("solana-treasury-reconciliation.disabled", {
@@ -101855,13 +103595,13 @@ init_colors();
101855
103595
  import {
101856
103596
  appendFileSync,
101857
103597
  existsSync as existsSync8,
101858
- mkdirSync as mkdirSync9,
101859
- readFileSync as readFileSync9,
103598
+ mkdirSync as mkdirSync10,
103599
+ readFileSync as readFileSync10,
101860
103600
  readdirSync as readdirSync3,
101861
103601
  statSync as statSync4,
101862
- writeFileSync as writeFileSync13
103602
+ writeFileSync as writeFileSync14
101863
103603
  } from "fs";
101864
- import { isAbsolute as isAbsolute3, join as join16, resolve as resolve10 } from "path";
103604
+ import { isAbsolute as isAbsolute3, join as join17, resolve as resolve10 } from "path";
101865
103605
  import * as readline5 from "readline";
101866
103606
  var DEFAULT_RELAY_URL = "https://relay.motebit.com";
101867
103607
  var REGISTRY_ADDRESS_RE = /^did:key:z[1-9A-HJ-NP-Za-km-z]+\/[a-z0-9-]+@[^/]+$/;
@@ -101881,15 +103621,15 @@ function resolveRelayUrl() {
101881
103621
  var SKILLS_DIR_NAME = "skills";
101882
103622
  var AUDIT_LOG_NAME = "audit.log";
101883
103623
  function getSkillsRoot() {
101884
- return join16(CONFIG_DIR, SKILLS_DIR_NAME);
103624
+ return join17(CONFIG_DIR, SKILLS_DIR_NAME);
101885
103625
  }
101886
103626
  function getAuditLogPath() {
101887
- return join16(getSkillsRoot(), AUDIT_LOG_NAME);
103627
+ return join17(getSkillsRoot(), AUDIT_LOG_NAME);
101888
103628
  }
101889
103629
  function makeAuditSink() {
101890
103630
  return (event) => {
101891
103631
  const root = getSkillsRoot();
101892
- if (!existsSync8(root)) mkdirSync9(root, { recursive: true });
103632
+ if (!existsSync8(root)) mkdirSync10(root, { recursive: true });
101893
103633
  appendFileSync(getAuditLogPath(), JSON.stringify(event) + "\n", "utf-8");
101894
103634
  };
101895
103635
  }
@@ -102227,7 +103967,7 @@ function parseAuditFilters(config) {
102227
103967
  function readAuditEvents() {
102228
103968
  const path19 = getAuditLogPath();
102229
103969
  if (!existsSync8(path19)) return [];
102230
- const text = readFileSync9(path19, "utf-8");
103970
+ const text = readFileSync10(path19, "utf-8");
102231
103971
  const events = [];
102232
103972
  for (const line of text.split("\n")) {
102233
103973
  const trimmed = line.trim();
@@ -102386,14 +104126,14 @@ async function handleSkillsRunScript(config) {
102386
104126
  }
102387
104127
  moteDb.close();
102388
104128
  if (!approved) process.exit(1);
102389
- const { mkdtempSync, writeFileSync: writeBytes, chmodSync: chmodSync2, rmSync: rmSync2 } = await import("fs");
104129
+ const { mkdtempSync, writeFileSync: writeBytes, chmodSync: chmodSync3, rmSync: rmSync3 } = await import("fs");
102390
104130
  const { tmpdir: tmpdir2 } = await import("os");
102391
104131
  const { spawnSync } = await import("child_process");
102392
- const tempDir = mkdtempSync(join16(tmpdir2(), "motebit-skill-script-"));
102393
- const tempPath = join16(tempDir, scriptName);
104132
+ const tempDir = mkdtempSync(join17(tmpdir2(), "motebit-skill-script-"));
104133
+ const tempPath = join17(tempDir, scriptName);
102394
104134
  try {
102395
104135
  writeBytes(tempPath, Buffer.from(scriptBytes));
102396
- chmodSync2(tempPath, 448);
104136
+ chmodSync3(tempPath, 448);
102397
104137
  const interpreter = detectInterpreter(scriptBytes, scriptName);
102398
104138
  const spawnArgs = interpreter.useShebang ? scriptArgs : [tempPath, ...scriptArgs];
102399
104139
  const command2 = interpreter.useShebang ? tempPath : interpreter.command;
@@ -102408,7 +104148,7 @@ async function handleSkillsRunScript(config) {
102408
104148
  process.exit(result.status ?? 0);
102409
104149
  } finally {
102410
104150
  try {
102411
- rmSync2(tempDir, { recursive: true, force: true });
104151
+ rmSync3(tempDir, { recursive: true, force: true });
102412
104152
  } catch {
102413
104153
  }
102414
104154
  }
@@ -102460,17 +104200,17 @@ function collectAuxFiles2(skillDir) {
102460
104200
  function walk2(current, base) {
102461
104201
  const entries = readdirSync3(current, { withFileTypes: true });
102462
104202
  for (const entry of entries) {
102463
- const full = join16(current, entry.name);
104203
+ const full = join17(current, entry.name);
102464
104204
  if (entry.isDirectory()) {
102465
104205
  walk2(full, base);
102466
104206
  } else if (entry.isFile()) {
102467
104207
  const rel = full.slice(base.length + 1).split(/[\\/]+/).join("/");
102468
- out[rel] = readFileSync9(full);
104208
+ out[rel] = readFileSync10(full);
102469
104209
  }
102470
104210
  }
102471
104211
  }
102472
104212
  for (const subdir of AUX_DIRS2) {
102473
- const subdirPath = join16(skillDir, subdir);
104213
+ const subdirPath = join17(skillDir, subdir);
102474
104214
  if (existsSync8(subdirPath)) walk2(subdirPath, skillDir);
102475
104215
  }
102476
104216
  return out;
@@ -102518,12 +104258,12 @@ async function loadIdentityKey() {
102518
104258
  };
102519
104259
  }
102520
104260
  async function signSkillDirectory(skillDir, privateKey, publicKey) {
102521
- const skillMdPath = join16(skillDir, SKILL_MD_NAME);
104261
+ const skillMdPath = join17(skillDir, SKILL_MD_NAME);
102522
104262
  if (!existsSync8(skillMdPath)) {
102523
104263
  console.error(error(` No SKILL.md at ${skillMdPath}`));
102524
104264
  process.exit(1);
102525
104265
  }
102526
- const text = readFileSync9(skillMdPath, "utf-8");
104266
+ const text = readFileSync10(skillMdPath, "utf-8");
102527
104267
  const parsed = parseSkillFile(text);
102528
104268
  const body = parsed.body;
102529
104269
  const motebitNoSig = { ...parsed.manifest.motebit };
@@ -102560,9 +104300,9 @@ async function signSkillDirectory(skillDir, privateKey, publicKey) {
102560
104300
  publicKey
102561
104301
  );
102562
104302
  const skillMdContent = serializeSkillFile(signedManifest, body);
102563
- writeFileSync13(skillMdPath, skillMdContent);
102564
- writeFileSync13(
102565
- join16(skillDir, SKILL_ENVELOPE_JSON_NAME),
104303
+ writeFileSync14(skillMdPath, skillMdContent);
104304
+ writeFileSync14(
104305
+ join17(skillDir, SKILL_ENVELOPE_JSON_NAME),
102566
104306
  JSON.stringify(signedEnvelope, null, 2) + "\n"
102567
104307
  );
102568
104308
  return { envelope: signedEnvelope, manifest: signedManifest, body };
@@ -102982,7 +104722,7 @@ init_esm_shims();
102982
104722
  init_dist6();
102983
104723
  init_dist30();
102984
104724
  init_colors();
102985
- import { readFileSync as readFileSync11 } from "fs";
104725
+ import { readFileSync as readFileSync12 } from "fs";
102986
104726
  import { resolve as resolve12 } from "path";
102987
104727
  var KIND_KEYWORDS = /* @__PURE__ */ new Set(["receipt", "token", "listing"]);
102988
104728
  function isVerifyKind(s3) {
@@ -102993,7 +104733,7 @@ async function verifyWire(kind, filePath, now = Date.now()) {
102993
104733
  const absPath = resolve12(filePath);
102994
104734
  let raw;
102995
104735
  try {
102996
- const text = readFileSync11(absPath, "utf-8");
104736
+ const text = readFileSync12(absPath, "utf-8");
102997
104737
  raw = JSON.parse(text);
102998
104738
  checks.push({ name: "json", ok: true, detail: `parsed ${text.length} bytes` });
102999
104739
  } catch (err2) {
@@ -103198,7 +104938,7 @@ init_esm_shims();
103198
104938
  init_dist2();
103199
104939
  init_dist32();
103200
104940
  init_dist3();
103201
- import { createHash as createHash2 } from "crypto";
104941
+ import { createHash as createHash3 } from "crypto";
103202
104942
  var MAX_TOOL_CALLS_PER_RUN = 50;
103203
104943
  var GoalScheduler = class _GoalScheduler {
103204
104944
  constructor(runtime, goalStore, approvalStore, goalOutcomeStore, motebitId, denyAbove, defaultTtlMs = 36e5, goalWallClockMs = 10 * 60 * 1e3) {
@@ -104000,7 +105740,7 @@ var GoalScheduler = class _GoalScheduler {
104000
105740
  }
104001
105741
  };
104002
105742
  function hashArgs(argsJson) {
104003
- return createHash2("sha256").update(argsJson).digest("hex");
105743
+ return createHash3("sha256").update(argsJson).digest("hex");
104004
105744
  }
104005
105745
  function formatTimeAgo2(ms) {
104006
105746
  if (ms < 6e4) return "just now";
@@ -104106,6 +105846,7 @@ async function handleRun(config) {
104106
105846
  const motebitId = identity.motebit_id;
104107
105847
  const runtimeRef = { current: null };
104108
105848
  const toolRegistry = buildToolRegistry(config, runtimeRef, motebitId);
105849
+ const runtimeHostServer = await electCoordinatorRole(fullConfig, motebitId, runtimeRef);
104109
105850
  const mcpServers = (fullConfig.mcp_servers ?? []).map((s3) => ({
104110
105851
  ...s3,
104111
105852
  trusted: (fullConfig.mcp_trusted_servers ?? []).includes(s3.name)
@@ -104137,6 +105878,7 @@ async function handleRun(config) {
104137
105878
  }
104138
105879
  );
104139
105880
  runtimeRef.current = runtime;
105881
+ wireBridgedOrgans(runtimeHostServer, runtime);
104140
105882
  await runtime.init();
104141
105883
  runtime.setLocalCapabilities([
104142
105884
  DeviceCapability.StdioMcp,
@@ -104328,6 +106070,23 @@ Collaborative step ${stepId}... (proposal ${proposalId}...) ${stepStatus} by ${c
104328
106070
  const cmdMsg = msg;
104329
106071
  void (async () => {
104330
106072
  try {
106073
+ const verdict = await verifyAgentCommandEnvelope({
106074
+ envelope: cmdMsg.envelope,
106075
+ command: cmdMsg.command,
106076
+ args: cmdMsg.args,
106077
+ motebitId,
106078
+ identityPublicKey: identity.identity.public_key
106079
+ });
106080
+ if (!verdict.ok) {
106081
+ wsAdapter.sendRaw(
106082
+ JSON.stringify({
106083
+ type: "command_response",
106084
+ id: cmdMsg.id,
106085
+ result: { summary: verdict.reason }
106086
+ })
106087
+ );
106088
+ return;
106089
+ }
104331
106090
  const result = await executeCommand(runtime, cmdMsg.command, cmdMsg.args);
104332
106091
  wsAdapter.sendRaw(
104333
106092
  JSON.stringify({ type: "command_response", id: cmdMsg.id, result })
@@ -104521,6 +106280,8 @@ Agent task received: ${task.task_id.slice(0, 8)}... prompt: "${task.prompt.slice
104521
106280
  }
104522
106281
  scheduler.stop();
104523
106282
  wsAdapter?.disconnect();
106283
+ void runtimeHostServer.close().catch(() => {
106284
+ });
104524
106285
  runtime.stop();
104525
106286
  moteDb.close();
104526
106287
  if (privKeyBytes) secureErase(privKeyBytes);
@@ -104613,6 +106374,7 @@ async function handleServe(config) {
104613
106374
  }
104614
106375
  const runtimeRef = { current: null };
104615
106376
  const toolRegistry = buildToolRegistry(config, runtimeRef, motebitId);
106377
+ const runtimeHostServer = await electCoordinatorRole(loadFullConfig(), motebitId, runtimeRef);
104616
106378
  if (config.tools) {
104617
106379
  const { pathToFileURL } = await import("url");
104618
106380
  const resolved = path18.resolve(config.tools);
@@ -104663,6 +106425,7 @@ async function handleServe(config) {
104663
106425
  }
104664
106426
  );
104665
106427
  runtimeRef.current = runtime;
106428
+ wireBridgedOrgans(runtimeHostServer, runtime);
104666
106429
  await runtime.init();
104667
106430
  const deps = {
104668
106431
  motebitId,
@@ -104922,6 +106685,26 @@ async function handleServe(config) {
104922
106685
  const cmdMsg = msg;
104923
106686
  void (async () => {
104924
106687
  try {
106688
+ const verdict = publicKeyHex == null || publicKeyHex === "" ? {
106689
+ ok: false,
106690
+ reason: "command_request rejected: no registered identity public key to verify against"
106691
+ } : await verifyAgentCommandEnvelope({
106692
+ envelope: cmdMsg.envelope,
106693
+ command: cmdMsg.command,
106694
+ args: cmdMsg.args,
106695
+ motebitId,
106696
+ identityPublicKey: publicKeyHex
106697
+ });
106698
+ if (!verdict.ok) {
106699
+ serveWsAdapter.sendRaw(
106700
+ JSON.stringify({
106701
+ type: "command_response",
106702
+ id: cmdMsg.id,
106703
+ result: { summary: verdict.reason }
106704
+ })
106705
+ );
106706
+ return;
106707
+ }
104925
106708
  const result = await executeCommand(runtimeRef.current, cmdMsg.command, cmdMsg.args);
104926
106709
  serveWsAdapter.sendRaw(
104927
106710
  JSON.stringify({ type: "command_response", id: cmdMsg.id, result })
@@ -105103,6 +106886,8 @@ async function handleServe(config) {
105103
106886
  }
105104
106887
  serveWsAdapter?.disconnect();
105105
106888
  await mcpServer.stop();
106889
+ await runtimeHostServer.close().catch(() => {
106890
+ });
105106
106891
  runtime.stop();
105107
106892
  moteDb.close();
105108
106893
  } catch (err2) {
@@ -105120,7 +106905,7 @@ init_esm_shims();
105120
106905
  import { spawn } from "child_process";
105121
106906
  import { mkdtemp, writeFile as writeFile3, unlink } from "fs/promises";
105122
106907
  import { tmpdir } from "os";
105123
- import { join as join18 } from "path";
106908
+ import { join as join19 } from "path";
105124
106909
 
105125
106910
  // ../../packages/voice/dist/index.js
105126
106911
  init_esm_shims();
@@ -105396,8 +107181,8 @@ var SystemTTSProvider = class {
105396
107181
  }
105397
107182
  };
105398
107183
  async function writeTempMp3(buf) {
105399
- const dir = await mkdtemp(join18(tmpdir(), "motebit-tts-"));
105400
- const path19 = join18(dir, "out.mp3");
107184
+ const dir = await mkdtemp(join19(tmpdir(), "motebit-tts-"));
107185
+ const path19 = join19(dir, "out.mp3");
105401
107186
  await writeFile3(path19, buf);
105402
107187
  return path19;
105403
107188
  }
@@ -105881,6 +107666,31 @@ async function main() {
105881
107666
  current: null
105882
107667
  };
105883
107668
  const toolRegistry = buildToolRegistry(config, runtimeRef, motebitId);
107669
+ let election;
107670
+ try {
107671
+ election = await electCliRuntimeHost({
107672
+ fullConfig: reloadedConfig,
107673
+ motebitId,
107674
+ loadPrivateKey: () => privateKeyBytes !== void 0 ? Promise.resolve(privateKeyBytes) : Promise.reject(
107675
+ new Error("signing key unavailable \u2014 cannot attach to the running coordinator")
107676
+ ),
107677
+ runtimeRef
107678
+ });
107679
+ } catch (err2) {
107680
+ console.error(
107681
+ `Runtime-host election failed: ${err2 instanceof Error ? err2.message : String(err2)}`
107682
+ );
107683
+ console.error(
107684
+ "Another motebit process may be coordinating with an incompatible build. Stop it and retry."
107685
+ );
107686
+ destroyTerminal();
107687
+ process.exit(1);
107688
+ }
107689
+ if (election.role === "frontend") {
107690
+ await runAttachedRepl(election.client, motebitId);
107691
+ return;
107692
+ }
107693
+ const runtimeHostServer = election.server;
105884
107694
  const trustedServers = fullConfig.mcp_trusted_servers ?? [];
105885
107695
  const mcpServers = (fullConfig.mcp_servers ?? []).map((s3) => ({
105886
107696
  ...s3,
@@ -106022,6 +107832,8 @@ async function main() {
106022
107832
  }
106023
107833
  const shutdown = async () => {
106024
107834
  destroyTerminal();
107835
+ await runtimeHostServer.close().catch(() => {
107836
+ });
106025
107837
  runtime.stop();
106026
107838
  await Promise.allSettled(mcpAdapters.map((a3) => a3.disconnect()));
106027
107839
  try {