@agentdocumentformat/cli 0.7.6 → 0.7.7

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.
@@ -18,8 +18,9 @@ import {
18
18
  checkFetchTarget,
19
19
  contextCategories,
20
20
  contextTotal,
21
- resolveCompactThreshold
22
- } from "./chunk-2WSQ7V6K.mjs";
21
+ resolveCompactThreshold,
22
+ resolveLoopThreshold
23
+ } from "./chunk-4N4ASPH6.mjs";
23
24
  import {
24
25
  startCallbackServer
25
26
  } from "./chunk-2NAP2ZF7.mjs";
@@ -434,8 +435,8 @@ var require_types = __commonJS({
434
435
  TokenType2[TokenType2.IS_POSTFIX = IS_POSTFIX] = "IS_POSTFIX";
435
436
  let IS_EXPRESSION_START = 512;
436
437
  TokenType2[TokenType2.IS_EXPRESSION_START = IS_EXPRESSION_START] = "IS_EXPRESSION_START";
437
- let num = 512;
438
- TokenType2[TokenType2.num = num] = "num";
438
+ let num2 = 512;
439
+ TokenType2[TokenType2.num = num2] = "num";
439
440
  let bigint = 1536;
440
441
  TokenType2[TokenType2.bigint = bigint] = "bigint";
441
442
  let decimal = 2560;
@@ -12388,14 +12389,14 @@ var require_sourcemap_codec_umd = __commonJS({
12388
12389
  let shouldNegate = value & 1;
12389
12390
  return value >>>= 1, shouldNegate && (value = -2147483648 | -value), relative4 + value;
12390
12391
  }
12391
- function encodeInteger(builder, num, relative4) {
12392
- let delta = num - relative4;
12392
+ function encodeInteger(builder, num2, relative4) {
12393
+ let delta = num2 - relative4;
12393
12394
  delta = delta < 0 ? -delta << 1 | 1 : delta << 1;
12394
12395
  do {
12395
12396
  let clamped = delta & 31;
12396
12397
  delta >>>= 5, delta > 0 && (clamped |= 32), builder.write(intToChar[clamped]);
12397
12398
  } while (delta > 0);
12398
- return num;
12399
+ return num2;
12399
12400
  }
12400
12401
  function hasMoreVlq(reader, max) {
12401
12402
  return reader.pos >= max ? !1 : reader.peek() !== comma;
@@ -23557,10 +23558,10 @@ var require_serializer = __commonJS({
23557
23558
  return "" + integer;
23558
23559
  }
23559
23560
  asNumber(i) {
23560
- let num = Number(i);
23561
- if (num !== num)
23561
+ let num2 = Number(i);
23562
+ if (num2 !== num2)
23562
23563
  throw new Error(`The value "${i}" cannot be converted to a number.`);
23563
- return num === 1 / 0 || num === -1 / 0 ? "null" : "" + num;
23564
+ return num2 === 1 / 0 || num2 === -1 / 0 ? "null" : "" + num2;
23564
23565
  }
23565
23566
  asBoolean(bool) {
23566
23567
  return bool && "true" || "false";
@@ -25317,21 +25318,21 @@ var require_secure_json_parse = __commonJS({
25317
25318
  return obj;
25318
25319
  return filter2(obj, { protoAction, constructorAction, safe: options && options.safe });
25319
25320
  }
25320
- function filter2(obj, { protoAction = "error", constructorAction = "error", safe } = {}) {
25321
+ function filter2(obj, { protoAction = "error", constructorAction = "error", safe: safe2 } = {}) {
25321
25322
  let next = [obj];
25322
25323
  for (; next.length; ) {
25323
25324
  let nodes = next;
25324
25325
  next = [];
25325
25326
  for (let node of nodes) {
25326
25327
  if (protoAction !== "ignore" && Object.prototype.hasOwnProperty.call(node, "__proto__")) {
25327
- if (safe === !0)
25328
+ if (safe2 === !0)
25328
25329
  return null;
25329
25330
  if (protoAction === "error")
25330
25331
  throw new SyntaxError("Object contains forbidden prototype property");
25331
25332
  delete node.__proto__;
25332
25333
  }
25333
25334
  if (constructorAction !== "ignore" && Object.prototype.hasOwnProperty.call(node, "constructor") && node.constructor !== null && typeof node.constructor == "object" && Object.prototype.hasOwnProperty.call(node.constructor, "prototype")) {
25334
- if (safe === !0)
25335
+ if (safe2 === !0)
25335
25336
  return null;
25336
25337
  if (constructorAction === "error")
25337
25338
  throw new SyntaxError("Object contains forbidden prototype property");
@@ -31799,8 +31800,8 @@ var require_parse = __commonJS({
31799
31800
  break;
31800
31801
  case "timestamp": {
31801
31802
  parseString(cxt);
31802
- let vts = (0, util_1.useFunc)(gen, timestamp_1.default), { allowDate, parseDate } = self2.opts, notValid = allowDate ? (0, codegen_1._)`!${vts}(${data}, true)` : (0, codegen_1._)`!${vts}(${data})`, fail2 = parseDate ? (0, codegen_1.or)(notValid, (0, codegen_1._)`(${data} = new Date(${data}), false)`, (0, codegen_1._)`isNaN(${data}.valueOf())`) : notValid;
31803
- gen.if(fail2, () => parsingError(cxt, (0, codegen_1.str)`invalid timestamp`));
31803
+ let vts = (0, util_1.useFunc)(gen, timestamp_1.default), { allowDate, parseDate } = self2.opts, notValid = allowDate ? (0, codegen_1._)`!${vts}(${data}, true)` : (0, codegen_1._)`!${vts}(${data})`, fail3 = parseDate ? (0, codegen_1.or)(notValid, (0, codegen_1._)`(${data} = new Date(${data}), false)`, (0, codegen_1._)`isNaN(${data}.valueOf())`) : notValid;
31804
+ gen.if(fail3, () => parsingError(cxt, (0, codegen_1.str)`invalid timestamp`));
31804
31805
  break;
31805
31806
  }
31806
31807
  case "float32":
@@ -31834,10 +31835,10 @@ var require_parse = __commonJS({
31834
31835
  let { gen } = cxt;
31835
31836
  skipWhitespace(cxt), gen.if((0, codegen_1._)`"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), () => parseWith(cxt, parseJson_1.parseJsonNumber, maxDigits));
31836
31837
  }
31837
- function parseBooleanToken(bool, fail2) {
31838
+ function parseBooleanToken(bool, fail3) {
31838
31839
  return (cxt) => {
31839
31840
  let { gen, data } = cxt;
31840
- tryParseToken(cxt, `${bool}`, () => fail2(cxt), () => gen.assign(data, bool));
31841
+ tryParseToken(cxt, `${bool}`, () => fail3(cxt), () => gen.assign(data, bool));
31841
31842
  };
31842
31843
  }
31843
31844
  function parseRef(cxt) {
@@ -31865,11 +31866,11 @@ var require_parse = __commonJS({
31865
31866
  function parseToken(cxt, tok) {
31866
31867
  tryParseToken(cxt, tok, jsonSyntaxError);
31867
31868
  }
31868
- function tryParseToken(cxt, tok, fail2, success) {
31869
+ function tryParseToken(cxt, tok, fail3, success) {
31869
31870
  let { gen } = cxt, n = tok.length;
31870
31871
  skipWhitespace(cxt), gen.if((0, codegen_1._)`${jsonSlice(n)} === ${tok}`, () => {
31871
31872
  gen.add(names_1.default.jsonPos, n), success?.(cxt);
31872
- }, () => fail2(cxt));
31873
+ }, () => fail3(cxt));
31873
31874
  }
31874
31875
  function skipWhitespace({ gen, char: c }) {
31875
31876
  gen.code((0, codegen_1._)`while((${c}=${names_1.default.json}[${names_1.default.jsonPos}],${c}===" "||${c}==="\\n"||${c}==="\\r"||${c}==="\\t"))${names_1.default.jsonPos}++;`);
@@ -32686,10 +32687,10 @@ var require_serializer2 = __commonJS({
32686
32687
  return "" + integer;
32687
32688
  }
32688
32689
  asNumber(i) {
32689
- let num = Number(i);
32690
- if (num !== num)
32690
+ let num2 = Number(i);
32691
+ if (num2 !== num2)
32691
32692
  throw new Error(`The value "${i}" cannot be converted to a number.`);
32692
- return num === 1 / 0 || num === -1 / 0 ? "null" : "" + num;
32693
+ return num2 === 1 / 0 || num2 === -1 / 0 ? "null" : "" + num2;
32693
32694
  }
32694
32695
  asBoolean(bool) {
32695
32696
  return bool && "true" || "false";
@@ -34861,8 +34862,8 @@ var require_re = __commonJS({
34861
34862
  value = value.split(`${token}*`).join(`${token}{0,${max}}`).split(`${token}+`).join(`${token}{1,${max}}`);
34862
34863
  return value;
34863
34864
  }, createToken = (name21, value, isGlobal) => {
34864
- let safe = makeSafeRegex(value), index = R++;
34865
- debug(name21, index, value), t[name21] = index, src[index] = value, safeSrc[index] = safe, re2[index] = new RegExp(value, isGlobal ? "g" : void 0), safeRe[index] = new RegExp(safe, isGlobal ? "g" : void 0);
34865
+ let safe2 = makeSafeRegex(value), index = R++;
34866
+ debug(name21, index, value), t[name21] = index, src[index] = value, safeSrc[index] = safe2, re2[index] = new RegExp(value, isGlobal ? "g" : void 0), safeRe[index] = new RegExp(safe2, isGlobal ? "g" : void 0);
34866
34867
  };
34867
34868
  createToken("NUMERICIDENTIFIER", "0|[1-9]\\d*");
34868
34869
  createToken("NUMERICIDENTIFIERLOOSE", "\\d+");
@@ -34967,9 +34968,9 @@ var require_semver = __commonJS({
34967
34968
  throw new TypeError("Invalid patch version");
34968
34969
  m[4] ? this.prerelease = m[4].split(".").map((id) => {
34969
34970
  if (/^[0-9]+$/.test(id)) {
34970
- let num = +id;
34971
- if (num >= 0 && num < MAX_SAFE_INTEGER)
34972
- return num;
34971
+ let num2 = +id;
34972
+ if (num2 >= 0 && num2 < MAX_SAFE_INTEGER)
34973
+ return num2;
34973
34974
  }
34974
34975
  return id;
34975
34976
  }) : this.prerelease = [], this.build = m[5] ? m[5].split(".") : [], this.format();
@@ -43451,8 +43452,8 @@ var require_parse4 = __commonJS({
43451
43452
  tokens
43452
43453
  };
43453
43454
  input = utils2.removePrefix(input, state), len = input.length;
43454
- let extglobs = [], braces = [], stack = [], prev = bos, value, eos = () => state.index === len - 1, peek = state.peek = (n = 1) => input[state.index + n], advance = state.advance = () => input[++state.index] || "", remaining = () => input.slice(state.index + 1), consume = (value2 = "", num = 0) => {
43455
- state.consumed += value2, state.index += num;
43455
+ let extglobs = [], braces = [], stack = [], prev = bos, value, eos = () => state.index === len - 1, peek = state.peek = (n = 1) => input[state.index + n], advance = state.advance = () => input[++state.index] || "", remaining = () => input.slice(state.index + 1), consume = (value2 = "", num2 = 0) => {
43456
+ state.consumed += value2, state.index += num2;
43456
43457
  }, append = (token) => {
43457
43458
  state.output += token.output != null ? token.output : token.value, consume(token.value);
43458
43459
  }, negate = () => {
@@ -45114,10 +45115,10 @@ function base58btcEncode(data) {
45114
45115
  let zeros = 0;
45115
45116
  for (let i = 0; i < data.length && data[i] === 0; i++)
45116
45117
  zeros++;
45117
- let num = BigInt("0x" + (data.length > 0 ? data.toString("hex") : "0")), chars = [];
45118
- for (; num > 0n; ) {
45119
- let remainder = Number(num % 58n);
45120
- num = num / 58n, chars.push(BASE58_ALPHABET[remainder]);
45118
+ let num2 = BigInt("0x" + (data.length > 0 ? data.toString("hex") : "0")), chars = [];
45119
+ for (; num2 > 0n; ) {
45120
+ let remainder = Number(num2 % 58n);
45121
+ num2 = num2 / 58n, chars.push(BASE58_ALPHABET[remainder]);
45121
45122
  }
45122
45123
  for (let i = 0; i < zeros; i++)
45123
45124
  chars.push("1");
@@ -45147,18 +45148,18 @@ function rawPublicKeyToSpki(rawKey) {
45147
45148
  return Buffer.concat([header, rawKey]);
45148
45149
  }
45149
45150
  function base58btcDecode(str2) {
45150
- let num = 0n;
45151
+ let num2 = 0n;
45151
45152
  for (let char of str2) {
45152
45153
  let idx = BASE58_ALPHABET.indexOf(char);
45153
45154
  if (idx === -1) throw new Error(`Invalid base58 character: ${char}`);
45154
- num = num * 58n + BigInt(idx);
45155
+ num2 = num2 * 58n + BigInt(idx);
45155
45156
  }
45156
45157
  let leadingZeros = 0;
45157
45158
  for (let char of str2) {
45158
45159
  if (char !== "1") break;
45159
45160
  leadingZeros++;
45160
45161
  }
45161
- let hex = num === 0n ? "" : num.toString(16), paddedHex = hex.length % 2 ? "0" + hex : hex, dataBytes = Buffer.from(paddedHex, "hex"), zeros = Buffer.alloc(leadingZeros);
45162
+ let hex = num2 === 0n ? "" : num2.toString(16), paddedHex = hex.length % 2 ? "0" + hex : hex, dataBytes = Buffer.from(paddedHex, "hex"), zeros = Buffer.alloc(leadingZeros);
45162
45163
  return Buffer.concat([zeros, dataBytes]);
45163
45164
  }
45164
45165
  function didToPublicKey(did) {
@@ -46680,8 +46681,15 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
46680
46681
  return _AdfDatabase.peekReadonly(filePath, (db) => {
46681
46682
  let row = db.prepare("SELECT config_json FROM adf_config WHERE id = 1").get();
46682
46683
  if (!row) return null;
46683
- let config = JSON.parse(row.config_json);
46684
+ let config = JSON.parse(row.config_json), did;
46685
+ try {
46686
+ let meta = db.prepare("SELECT value FROM adf_meta WHERE key = 'adf_did'").get();
46687
+ did = (meta ? Buffer.isBuffer(meta.value) ? meta.value.toString("utf-8") : String(meta.value) : "") || void 0;
46688
+ } catch {
46689
+ did = void 0;
46690
+ }
46684
46691
  return {
46692
+ did,
46685
46693
  id: config.id,
46686
46694
  name: config.name,
46687
46695
  receive: config.messaging?.receive ?? !1,
@@ -47940,6 +47948,7 @@ CREATE INDEX IF NOT EXISTS idx_adf_logs_origin ON adf_logs(origin);
47940
47948
  // src/main/adf/adf-workspace.ts
47941
47949
  import { brotliCompress, brotliCompressSync, brotliDecompressSync as brotliDecompressSync2, constants as zlibConstants } from "zlib";
47942
47950
  import { promisify } from "util";
47951
+ import { createPrivateKey as createPrivateKey3, createPublicKey as createPublicKey3 } from "crypto";
47943
47952
 
47944
47953
  // src/main/adf/skill-indexer.ts
47945
47954
  init_execution_context();
@@ -48425,13 +48434,25 @@ var LoopCompactionSupersededError = class extends Error {
48425
48434
  super(`compactLoop: source seq ${seq} no longer exists \u2014 another compaction or clear landed first`), this.name = "LoopCompactionSupersededError";
48426
48435
  }
48427
48436
  }, LOOP_REVISION_CHANGED = /* @__PURE__ */ Symbol("adf:loop-revision-changed");
48428
- var CONTEXT_BASELINE_META_KEY = "context_baseline_tokens", ENVELOPE_NAMES = ["identity", "credentials"], ENVELOPE_PURPOSE_PREFIX = "crypto:envelope:", MCP_OAUTH_PURPOSE_RE = /^mcp:[^:]+:oauth$/, MCP_CREDENTIAL_FILE_PURPOSE_RE = /^mcp:[^:]+:file:/;
48437
+ var CONTEXT_BASELINE_META_KEY = "context_baseline_tokens", ENVELOPE_NAMES = ["identity", "credentials"], ENVELOPE_PURPOSE_PREFIX = "crypto:envelope:";
48438
+ function numberedPath(path, n) {
48439
+ if (n <= 1) return path;
48440
+ let slash = path.lastIndexOf("/"), dot = path.lastIndexOf(".");
48441
+ return dot <= slash + 1 ? `${path}-${n}` : `${path.slice(0, dot)}-${n}${path.slice(dot)}`;
48442
+ }
48443
+ var MCP_OAUTH_PURPOSE_RE = /^mcp:[^:]+:oauth$/, MCP_CREDENTIAL_FILE_PURPOSE_RE = /^mcp:[^:]+:file:/;
48429
48444
  function isReservedMcpRuntimePurpose(purpose) {
48430
48445
  return MCP_OAUTH_PURPOSE_RE.test(purpose) || MCP_CREDENTIAL_FILE_PURPOSE_RE.test(purpose);
48431
48446
  }
48432
48447
  function mcpRuntimeIdentityAccess(purpose) {
48433
48448
  return isReservedMcpRuntimePurpose(purpose) ? { readUnlocked: !1, writeUnlocked: !1 } : { readUnlocked: !0, writeUnlocked: !0 };
48434
48449
  }
48450
+ function assertEd25519Pair(privateKey, publicKey) {
48451
+ let priv = createPrivateKey3({ key: privateKey, format: "der", type: "pkcs8" });
48452
+ if (priv.asymmetricKeyType !== "ed25519") throw new Error("Adopted identity key is not Ed25519");
48453
+ let derived = createPublicKey3(priv).export({ type: "spki", format: "der" });
48454
+ if (!Buffer.from(derived).equals(publicKey)) throw new Error("Adopted identity public key does not match its private key");
48455
+ }
48435
48456
  var AdfWorkspace = class _AdfWorkspace {
48436
48457
  db;
48437
48458
  filePath;
@@ -48975,9 +48996,15 @@ var AdfWorkspace = class _AdfWorkspace {
48975
48996
  /**
48976
48997
  * Generate Ed25519 key pair + DID for an ADF that doesn't have one.
48977
48998
  * If a password is active, the new keys are encrypted with the given derivedKey.
48978
- */
48979
- generateIdentityKeys(derivedKey) {
48980
- let keyPair = generateEd25519KeyPair(), rawPubKey = extractRawPublicKey(keyPair.publicKey), did = publicKeyToDid(rawPubKey), identityDek = this.envelopeDeks.get("identity");
48999
+ *
49000
+ * `adopt` stores a keypair minted earlier (an identity draft from the create
49001
+ * screen) instead of a fresh one. The rows, sealing, and adf_did are exactly
49002
+ * what a fresh mint writes. The pair is checked first: the public key must
49003
+ * be the Ed25519 public half of the private key.
49004
+ */
49005
+ generateIdentityKeys(derivedKey, adopt) {
49006
+ adopt && assertEd25519Pair(adopt.privateKey, adopt.publicKey);
49007
+ let keyPair = adopt ?? generateEd25519KeyPair(), rawPubKey = extractRawPublicKey(keyPair.publicKey), did = publicKeyToDid(rawPubKey), identityDek = this.envelopeDeks.get("identity");
48981
49008
  if (identityDek)
48982
49009
  this.db.setIdentityRaw("crypto:signing:private_key", sealWithDek(keyPair.privateKey, identityDek), envelopeAlgo("identity"), null, null), this.db.setIdentityRaw("crypto:signing:public_key", keyPair.publicKey, "plain", null, null);
48983
49010
  else if (derivedKey) {
@@ -49737,6 +49764,22 @@ var AdfWorkspace = class _AdfWorkspace {
49737
49764
  let existed = this.db.fileExists(relativePath), previous = this.onFileChangeCallback ? this.db.readFile(relativePath)?.content : void 0, protection = relativePath === "mind.md" || relativePath === "README.md" || relativePath === "document.md" ? "no_delete" : "none";
49738
49765
  this.db.writeFile(relativePath, content, mimeType, protection), this.emitUmbilical("file.written", { path: relativePath, bytes: content.length }), this.emitFileChange(relativePath, existed ? "modified" : "created", content, previous, this.fileChangeMeta(relativePath));
49739
49766
  }
49767
+ /**
49768
+ * Write a file without replacing a different one already at that path.
49769
+ * A taken name gets a number before its extension (`report.pdf` →
49770
+ * `report-2.pdf`). The same bytes already stored under the name or one of
49771
+ * its numbered copies are reused rather than written again, so a re-delivered
49772
+ * attachment or a re-uploaded file does not pile up copies.
49773
+ * Returns the path the content lives at.
49774
+ */
49775
+ writeFileBufferUnique(relativePath, content, mimeType) {
49776
+ for (let n = 1; ; n++) {
49777
+ let candidate = numberedPath(relativePath, n), existing = this.db.readFile(candidate);
49778
+ if (!existing)
49779
+ return this.writeFileBuffer(candidate, content, mimeType), candidate;
49780
+ if (existing.content.equals(content)) return candidate;
49781
+ }
49782
+ }
49740
49783
  /** File metadata for a change event — skipped entirely when no sink is registered. */
49741
49784
  fileChangeMeta(path) {
49742
49785
  return this.onFileChangeCallback ? this.getFileMeta(path) : null;
@@ -52526,6 +52569,26 @@ var ToolRegistry = class _ToolRegistry {
52526
52569
  let result = this.tools.delete(name21);
52527
52570
  return result && this.toolCache.clear(), result;
52528
52571
  }
52572
+ /** Host-built tools that exist while their declaration does (provideDeclared). */
52573
+ declaredFactories = /* @__PURE__ */ new Map();
52574
+ /**
52575
+ * Register `name` for as long as the agent config declares it (enabled or
52576
+ * not), now and again on every config change via syncDeclared. For host
52577
+ * tools whose registration follows declaration presence — sys_code,
52578
+ * sys_lambda, npm_install, npm_uninstall. Without the re-sync, a tool the
52579
+ * owner declared on a running agent stayed unregistered ("not available")
52580
+ * until a restart. Access is still gated by the declaration on every call
52581
+ * path; this only decides whether an instance exists to be gated.
52582
+ */
52583
+ provideDeclared(name21, make, declarations) {
52584
+ this.declaredFactories.set(name21, make), this.syncDeclared(declarations);
52585
+ }
52586
+ /** Re-apply provideDeclared against a new config's declarations. */
52587
+ syncDeclared(declarations) {
52588
+ let declared = new Set(declarations.map((d) => d.name));
52589
+ for (let [name21, make] of this.declaredFactories)
52590
+ declared.has(name21) ? this.tools.has(name21) || this.register(make()) : this.unregister(name21);
52591
+ }
52529
52592
  get(name21) {
52530
52593
  return this.tools.get(name21);
52531
52594
  }
@@ -53724,8 +53787,8 @@ var InputSchema = external_exports.object({
53724
53787
  let entry = workspace.getDatabase().readFile(path);
53725
53788
  if (!entry)
53726
53789
  return { content: `File not found: "${path}"`, isError: !0 };
53727
- let isText = isTextMime(entry.mime_type), fileContent;
53728
- if (isText) {
53790
+ let isText2 = isTextMime(entry.mime_type), fileContent;
53791
+ if (isText2) {
53729
53792
  let text2 = entry.content.toString("utf-8");
53730
53793
  if (start_line || end_line) {
53731
53794
  let lines = text2.split(`
@@ -57372,6 +57435,8 @@ var WARM_IDLE_TTL_MS = 5 * 6e4, COLD_IDLE_TTL_MS = 6e4, ADMISSION_MAX_WAIT_MS =
57372
57435
  stdlibModules = [];
57373
57436
  userPkgBasePath = null;
57374
57437
  userPkgModules = [];
57438
+ /** Per-agent package lists, keyed by owning agent (see setAgentPackageSource). */
57439
+ agentPackageSources = /* @__PURE__ */ new Map();
57375
57440
  /** Sandbox ids each agent has created, so destroyForAgent() reaps exactly
57376
57441
  * those. Prefix matching used to do this, but sandbox ids are absolute
57377
57442
  * Windows paths — an agent whose `.adf` declares `id: "C"` made
@@ -57392,13 +57457,48 @@ var WARM_IDLE_TTL_MS = 5 * 6e4, COLD_IDLE_TTL_MS = 6e4, ADMISSION_MAX_WAIT_MS =
57392
57457
  setStdlib(basePath, modules) {
57393
57458
  this.stdlibBasePath = basePath, this.stdlibModules = modules;
57394
57459
  }
57395
- /** Configure user-installed package path and visible module names for the sandbox. */
57460
+ /**
57461
+ * Configure the installed-package path and the RUNTIME packages (Settings >
57462
+ * Packages), which every agent's sandboxes can import. Pass a reader to
57463
+ * follow the setting live; it runs at each execution. An agent's own
57464
+ * packages come on top of these, from setAgentPackageSource.
57465
+ */
57396
57466
  setUserPackages(basePath, modules) {
57397
57467
  this.userPkgBasePath = basePath, this.userPkgModules = modules;
57398
57468
  }
57399
- /** Module names currently visible to sandboxed code (process-wide). */
57400
- getUserPackageModules() {
57401
- return [...this.userPkgModules];
57469
+ runtimePackageModules() {
57470
+ if (typeof this.userPkgModules != "function") return this.userPkgModules;
57471
+ try {
57472
+ return this.userPkgModules();
57473
+ } catch {
57474
+ return [];
57475
+ }
57476
+ }
57477
+ /**
57478
+ * The packages one agent installed (its code_execution.packages), importable
57479
+ * only by sandboxes that agent owns. `read` runs at each execution, so a
57480
+ * package the agent just installed is importable on its next run with no
57481
+ * refresh step. `keys` are every id the agent's sandboxes are owned under —
57482
+ * the `agent` execute option, which is the file path for most callers and
57483
+ * the config id for some (mesh routes, middleware).
57484
+ */
57485
+ setAgentPackageSource(keys, read) {
57486
+ for (let key of keys) this.agentPackageSources.set(key, read);
57487
+ }
57488
+ clearAgentPackageSource(keys, read) {
57489
+ for (let key of keys)
57490
+ read && this.agentPackageSources.get(key) !== read || this.agentPackageSources.delete(key);
57491
+ }
57492
+ /** Module names visible to `agent`'s sandboxes: runtime packages plus its own. */
57493
+ getUserPackageModules(agent) {
57494
+ let runtime2 = this.runtimePackageModules(), read = agent ? this.agentPackageSources.get(agent) : void 0;
57495
+ if (!read) return [...runtime2];
57496
+ let own = [];
57497
+ try {
57498
+ own = read();
57499
+ } catch {
57500
+ }
57501
+ return [.../* @__PURE__ */ new Set([...runtime2, ...own])];
57402
57502
  }
57403
57503
  /**
57404
57504
  * Set the global ceiling on concurrent cold-lambda workers (user setting —
@@ -57459,7 +57559,9 @@ var WARM_IDLE_TTL_MS = 5 * 6e4, COLD_IDLE_TTL_MS = 6e4, ADMISSION_MAX_WAIT_MS =
57459
57559
  stdlibBasePath: this.stdlibBasePath,
57460
57560
  stdlibModules: this.stdlibModules,
57461
57561
  userPkgBasePath: this.userPkgBasePath,
57462
- userPkgModules: this.userPkgModules
57562
+ // Per execution: a worker serves one owner, but the list must follow
57563
+ // that agent's installs and the runtime list as they change.
57564
+ userPkgModules: this.getUserPackageModules(options?.agent ?? agentId)
57463
57565
  });
57464
57566
  let execId = `exec_${++this.execCounter}`, isAuthorized = options?.handlerAuthorized ?? toolConfig?.isAuthorized ?? !1;
57465
57567
  entry.authLevels.add(isAuthorized), entry.inflight++;
@@ -58861,15 +58963,15 @@ function estimateObjectChars(obj) {
58861
58963
  if (typeof obj == "string") return obj.length + 2;
58862
58964
  if (typeof obj == "number" || typeof obj == "boolean") return 5;
58863
58965
  if (Array.isArray(obj)) {
58864
- let sum = 2;
58865
- for (let item of obj) sum += estimateObjectChars(item) + 1;
58866
- return sum;
58966
+ let sum2 = 2;
58967
+ for (let item of obj) sum2 += estimateObjectChars(item) + 1;
58968
+ return sum2;
58867
58969
  }
58868
58970
  if (typeof obj == "object") {
58869
- let sum = 2;
58971
+ let sum2 = 2;
58870
58972
  for (let key of Object.keys(obj))
58871
- sum += key.length + estimateObjectChars(obj[key]) + 2;
58872
- return sum;
58973
+ sum2 += key.length + estimateObjectChars(obj[key]) + 2;
58974
+ return sum2;
58873
58975
  }
58874
58976
  return 10;
58875
58977
  }
@@ -59119,9 +59221,9 @@ function escapeAttr(value) {
59119
59221
  function renderInjectedFile(path, content) {
59120
59222
  if (content === MISSING_FILE_SENTINEL)
59121
59223
  return `<${INJECTED_TAG} path="${escapeAttr(path)}" missing="true"/>`;
59122
- let safe = content.split(`</${INJECTED_TAG}`).join(`<\\/${INJECTED_TAG}`);
59224
+ let safe2 = content.split(`</${INJECTED_TAG}`).join(`<\\/${INJECTED_TAG}`);
59123
59225
  return `<${INJECTED_TAG} path="${escapeAttr(path)}">
59124
- ${safe}
59226
+ ${safe2}
59125
59227
  </${INJECTED_TAG}>`;
59126
59228
  }
59127
59229
  function collectInjectedFiles(sources, read, snapshot) {
@@ -59391,9 +59493,17 @@ var LLM_PRICING = {
59391
59493
  }
59392
59494
  };
59393
59495
  function estimateLlmCallCostUsd(metadata) {
59394
- let pricing = LLM_PRICING[metadata.model];
59496
+ return estimateTokenCostUsd(metadata.model, {
59497
+ input: metadata.input_tokens,
59498
+ output: metadata.output_tokens,
59499
+ cache_read: metadata.cache_read_tokens,
59500
+ cache_write: metadata.cache_write_tokens
59501
+ });
59502
+ }
59503
+ function estimateTokenCostUsd(model, tokens) {
59504
+ let pricing = LLM_PRICING[model];
59395
59505
  if (!pricing) return;
59396
- let cacheRead = metadata.cache_read_tokens ?? 0, cacheWrite = metadata.cache_write_tokens ?? 0, inputCost = Math.max(0, metadata.input_tokens - cacheRead - cacheWrite) * pricing.input_per_million / 1e6, cacheCost = cacheRead * (pricing.cache_read_per_million ?? pricing.input_per_million) / 1e6 + cacheWrite * (pricing.cache_write_per_million ?? pricing.input_per_million) / 1e6, outputCost = metadata.output_tokens * pricing.output_per_million / 1e6;
59506
+ let cacheRead = tokens.cache_read ?? 0, cacheWrite = tokens.cache_write ?? 0, inputCost = Math.max(0, (tokens.input ?? 0) - cacheRead - cacheWrite) * pricing.input_per_million / 1e6, cacheCost = cacheRead * (pricing.cache_read_per_million ?? pricing.input_per_million) / 1e6 + cacheWrite * (pricing.cache_write_per_million ?? pricing.input_per_million) / 1e6, outputCost = (tokens.output ?? 0) * pricing.output_per_million / 1e6;
59397
59507
  return Number((inputCost + cacheCost + outputCost).toFixed(8));
59398
59508
  }
59399
59509
 
@@ -62182,8 +62292,8 @@ Retry error: ${String(retryError)}
62182
62292
  let content = row.content;
62183
62293
  if (!content) return result;
62184
62294
  let lines = content.split(`
62185
- `), totalLines = lines.length, totalChars = content.length, approxTokens = Math.ceil(totalChars / 4), maxTokens = this.config.limits?.max_file_read_tokens ?? 3e4;
62186
- if (approxTokens > maxTokens) {
62295
+ `), totalLines = lines.length, totalChars = content.length, approxTokens2 = Math.ceil(totalChars / 4), maxTokens = this.config.limits?.max_file_read_tokens ?? 3e4;
62296
+ if (approxTokens2 > maxTokens) {
62187
62297
  let maxChars = maxTokens * 4, truncatedLines = content.slice(0, maxChars).split(`
62188
62298
  `);
62189
62299
  return truncatedLines.pop(), row.content = truncatedLines.join(`
@@ -62195,7 +62305,7 @@ Use start_line/end_line to read specific sections.`, { ...result, content: JSON.
62195
62305
  if (totalLines > 300) {
62196
62306
  let sizeStr = formatSize(Buffer.byteLength(content, "utf8")), preview = lines.slice(0, 50).join(`
62197
62307
  `);
62198
- return row.content = `[Large file: ${totalLines} lines, ${sizeStr}, ~${approxTokens.toLocaleString()} tokens]
62308
+ return row.content = `[Large file: ${totalLines} lines, ${sizeStr}, ~${approxTokens2.toLocaleString()} tokens]
62199
62309
  Use start_line/end_line to read sections (e.g. start_line=1, end_line=100).
62200
62310
 
62201
62311
  --- Preview (first 50 lines) ---
@@ -65339,12 +65449,134 @@ var execFileAsync = promisify2(execFile), IS_WIN = process.platform === "win32",
65339
65449
  var inputSchema = external_exports.object({
65340
65450
  name: external_exports.string().min(1).describe('npm package name (e.g. "lodash", "vega-lite", "@resvg/resvg-wasm")'),
65341
65451
  version: external_exports.string().optional().describe('Semver version or range (e.g. "^5.30.0", "4.17.21"). Defaults to "latest".')
65342
- });
65452
+ }), MAX_PACKAGES = 50, NpmInstallTool = class {
65453
+ constructor(packagesService, onPackageInstalled) {
65454
+ this.packagesService = packagesService;
65455
+ this.onPackageInstalled = onPackageInstalled;
65456
+ }
65457
+ packagesService;
65458
+ onPackageInstalled;
65459
+ name = "npm_install";
65460
+ description = 'Install an npm package for use in code execution (sys_code / sys_lambda). Pure JavaScript packages only \u2014 native addons are blocked. The package becomes available to import starting next turn. Example: npm_install({ name: "vega-lite", version: "^5.21.0" })';
65461
+ inputSchema = inputSchema;
65462
+ category = "system";
65463
+ async execute(input, workspace) {
65464
+ let { name: name21, version } = input, currentPackages = workspace.getAgentConfig().code_execution?.packages ?? [];
65465
+ if (currentPackages.length >= MAX_PACKAGES)
65466
+ return {
65467
+ content: JSON.stringify({
65468
+ success: !1,
65469
+ error: "package_limit",
65470
+ message: `Maximum ${MAX_PACKAGES} packages allowed. Remove unused packages with npm_uninstall first.`
65471
+ }),
65472
+ isError: !0
65473
+ };
65474
+ let existing = currentPackages.find((p) => p.name === name21);
65475
+ if (existing && version && existing.version === version)
65476
+ return {
65477
+ content: JSON.stringify({
65478
+ success: !0,
65479
+ name: name21,
65480
+ version: existing.version,
65481
+ already_installed: !0
65482
+ }),
65483
+ isError: !1
65484
+ };
65485
+ try {
65486
+ let agentName = workspace.getAgentConfig().name, result = await this.packagesService.install(name21, version, void 0, agentName), freshConfig = workspace.getAgentConfig(), packages = (freshConfig.code_execution?.packages ?? []).filter((p) => p.name !== name21);
65487
+ return packages.push({ name: name21, version: result.version }), workspace.setAgentConfig({
65488
+ ...freshConfig,
65489
+ code_execution: {
65490
+ ...freshConfig.code_execution,
65491
+ packages
65492
+ }
65493
+ }), this.onPackageInstalled?.(name21, result.version), {
65494
+ content: JSON.stringify({
65495
+ success: !0,
65496
+ name: name21,
65497
+ version: result.version,
65498
+ size_mb: result.size_mb,
65499
+ already_installed: result.already_installed
65500
+ }),
65501
+ isError: !1
65502
+ };
65503
+ } catch (error) {
65504
+ return error instanceof NativeAddonError ? {
65505
+ content: JSON.stringify({
65506
+ success: !1,
65507
+ error: "native_addon",
65508
+ message: "Package requires native code and cannot run in the sandbox."
65509
+ }),
65510
+ isError: !0
65511
+ } : error instanceof SizeLimitError ? {
65512
+ content: JSON.stringify({
65513
+ success: !1,
65514
+ error: "size_limit",
65515
+ message: error.message
65516
+ }),
65517
+ isError: !0
65518
+ } : {
65519
+ content: JSON.stringify({
65520
+ success: !1,
65521
+ error: "npm_error",
65522
+ message: String(error instanceof Error ? error.message : error)
65523
+ }),
65524
+ isError: !0
65525
+ };
65526
+ }
65527
+ }
65528
+ toProviderFormat() {
65529
+ return {
65530
+ name: this.name,
65531
+ description: this.description,
65532
+ input_schema: zodToJsonSchema(this.inputSchema)
65533
+ };
65534
+ }
65535
+ };
65343
65536
 
65344
65537
  // src/main/tools/built-in/npm-uninstall.tool.ts
65345
65538
  var inputSchema2 = external_exports.object({
65346
65539
  name: external_exports.string().min(1).describe('npm package name to remove (e.g. "lodash")')
65347
- });
65540
+ }), NpmUninstallTool = class {
65541
+ constructor(onPackageRemoved) {
65542
+ this.onPackageRemoved = onPackageRemoved;
65543
+ }
65544
+ onPackageRemoved;
65545
+ name = "npm_uninstall";
65546
+ description = "Remove an npm package from this agent's available packages. The package becomes unavailable to import starting next turn. Does not delete the package from disk (other agents may use it).";
65547
+ inputSchema = inputSchema2;
65548
+ category = "system";
65549
+ async execute(input, workspace) {
65550
+ let { name: name21 } = input, config = workspace.getAgentConfig(), packages = config.code_execution?.packages ?? [];
65551
+ if (packages.findIndex((p) => p.name === name21) === -1)
65552
+ return {
65553
+ content: JSON.stringify({
65554
+ success: !1,
65555
+ error: "not_found",
65556
+ message: `Package "${name21}" is not in this agent's installed packages.`
65557
+ }),
65558
+ isError: !0
65559
+ };
65560
+ let updated = packages.filter((p) => p.name !== name21);
65561
+ return workspace.setAgentConfig({
65562
+ ...config,
65563
+ code_execution: {
65564
+ ...config.code_execution,
65565
+ packages: updated
65566
+ }
65567
+ }), this.onPackageRemoved?.(name21), {
65568
+ content: JSON.stringify({ success: !0, name: name21 }),
65569
+ isError: !1
65570
+ };
65571
+ }
65572
+ toProviderFormat() {
65573
+ return {
65574
+ name: this.name,
65575
+ description: this.description,
65576
+ input_schema: zodToJsonSchema(this.inputSchema)
65577
+ };
65578
+ }
65579
+ };
65348
65580
 
65349
65581
  // src/main/tools/built-in/fs-transfer.tool.ts
65350
65582
  import {
@@ -66206,8 +66438,8 @@ function isHostPortFree(port2, host2) {
66206
66438
  });
66207
66439
  }
66208
66440
  function isolatedContainerName(agentName, agentId) {
66209
- let safe = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 30), shortId = agentId.slice(0, 8);
66210
- return `adf-${safe || shortId}-${shortId}`;
66441
+ let safe2 = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 30), shortId = agentId.slice(0, 8);
66442
+ return `adf-${safe2 || shortId}-${shortId}`;
66211
66443
  }
66212
66444
  function containerWorkspacePath(isolated, agentId) {
66213
66445
  return isolated ? "/workspace" : `/workspace/${agentId}`;
@@ -69821,6 +70053,8 @@ function assembleAgent(options) {
69821
70053
  })));
69822
70054
  }
69823
70055
  adfCallHandler?.attachSession(session);
70056
+ let packageKeys = [workspace.getFilePath(), config.id].filter((key) => !!key), readAgentPackages = () => (workspace.getAgentConfig().code_execution?.packages ?? []).map((pkg) => pkg.name);
70057
+ codeSandboxService2?.setAgentPackageSource(packageKeys, readAgentPackages);
69824
70058
  let preLlmHookRunner = codeSandboxService2 && adfCallHandler ? new PreLlmHookRunner(workspace, codeSandboxService2, adfCallHandler, config.id) : null, executor = new AgentExecutor(
69825
70059
  config,
69826
70060
  provider,
@@ -69842,7 +70076,7 @@ function assembleAgent(options) {
69842
70076
  toolPrompts: options.toolPrompts ?? {},
69843
70077
  compactionPrompt: options.compactionPrompt,
69844
70078
  sources,
69845
- sandboxModules: codeSandboxService2?.getUserPackageModules()
70079
+ sandboxModules: codeSandboxService2?.getUserPackageModules(packageKeys[0])
69846
70080
  }));
69847
70081
  };
69848
70082
  refreshEffectiveRuntime(), options.effectiveRuntime && executor.setProviderChangeListener(() => refreshEffectiveRuntime()), options.systemScopeHandler && executor.setSystemScopeHandler(options.systemScopeHandler);
@@ -70087,7 +70321,7 @@ ${json2.slice(0, room)}${note}`;
70087
70321
  };
70088
70322
  syncLoopToolRegistration(config);
70089
70323
  let sysUpdateTool = registry3.get("sys_update_config"), applyConfigChange = (updatedConfig, configOptions) => {
70090
- if (stripLoopNameMarker(updatedConfig), rawConfig = updatedConfig, snapshotConfig = updatedConfig, executor.updateConfig(updatedConfig), triggerEvaluator.updateConfig(updatedConfig), adfCallHandler?.updateConfig(updatedConfig), syncLoopToolRegistration(updatedConfig), loopPool.reconcile(updatedConfig), refreshEffectiveRuntime(), configOptions?.notifyHost !== !1)
70324
+ if (stripLoopNameMarker(updatedConfig), rawConfig = updatedConfig, snapshotConfig = updatedConfig, executor.updateConfig(updatedConfig), triggerEvaluator.updateConfig(updatedConfig), adfCallHandler?.updateConfig(updatedConfig), syncLoopToolRegistration(updatedConfig), registry3.syncDeclared(updatedConfig.tools ?? []), loopPool.reconcile(updatedConfig), refreshEffectiveRuntime(), configOptions?.notifyHost !== !1)
70091
70325
  for (let bindings of hostBindings()) bindings.onConfigChanged?.(updatedConfig);
70092
70326
  }, sysUpdateOnConfigChanged = (updatedConfig) => {
70093
70327
  applyConfigChange(updatedConfig);
@@ -70143,7 +70377,7 @@ ${json2.slice(0, room)}${note}`;
70143
70377
  adapterManager?.off("inbound", onAdapterInbound);
70144
70378
  } catch {
70145
70379
  }
70146
- executor.onToolCallIntercepted = void 0, executor.onTaskCreated = void 0, executor.onTaskCompleted = void 0, executor.onLlmCall = void 0, adfCallHandler && (adfCallHandler.onEvent = void 0, adfCallHandler.onTaskCompleted = void 0, adfCallHandler.onLambdaToolEndTurn = void 0, adfCallHandler.onHilApproved = void 0, adfCallHandler.requestProtectionApproval = void 0, adfCallHandler.onLlmCall = void 0), sysUpdateTool && sysUpdateTool.onConfigChanged === sysUpdateOnConfigChanged && (sysUpdateTool.onConfigChanged = void 0), createAdfTool && createAdfTool.onAutostartChild === createAdfOnAutostartChild && (createAdfTool.onAutostartChild = void 0), shellTool && (shellTool.onToolCallIntercepted === shellOnToolCallIntercepted && (shellTool.onToolCallIntercepted = void 0), shellTool.onApprovalRequired === shellOnApprovalRequired && (shellTool.onApprovalRequired = void 0), shellTool.onProtectionBlocked === shellOnProtectionBlocked && (shellTool.onProtectionBlocked = void 0)), activeHost = null, executor.removeAllListeners();
70380
+ codeSandboxService2?.clearAgentPackageSource(packageKeys, readAgentPackages), executor.onToolCallIntercepted = void 0, executor.onTaskCreated = void 0, executor.onTaskCompleted = void 0, executor.onLlmCall = void 0, adfCallHandler && (adfCallHandler.onEvent = void 0, adfCallHandler.onTaskCompleted = void 0, adfCallHandler.onLambdaToolEndTurn = void 0, adfCallHandler.onHilApproved = void 0, adfCallHandler.requestProtectionApproval = void 0, adfCallHandler.onLlmCall = void 0), sysUpdateTool && sysUpdateTool.onConfigChanged === sysUpdateOnConfigChanged && (sysUpdateTool.onConfigChanged = void 0), createAdfTool && createAdfTool.onAutostartChild === createAdfOnAutostartChild && (createAdfTool.onAutostartChild = void 0), shellTool && (shellTool.onToolCallIntercepted === shellOnToolCallIntercepted && (shellTool.onToolCallIntercepted = void 0), shellTool.onApprovalRequired === shellOnApprovalRequired && (shellTool.onApprovalRequired = void 0), shellTool.onProtectionBlocked === shellOnProtectionBlocked && (shellTool.onProtectionBlocked = void 0)), activeHost = null, executor.removeAllListeners();
70147
70381
  }
70148
70382
  }, teardownPromise = null, teardown = () => teardownPromise || (teardownPromise = (async () => {
70149
70383
  try {
@@ -70348,7 +70582,7 @@ var Pipeline = class {
70348
70582
  };
70349
70583
 
70350
70584
  // src/main/crypto/message-crypto.ts
70351
- import { hkdfSync as hkdfSync2, diffieHellman as diffieHellman2, createPrivateKey as createPrivateKey3, createPublicKey as createPublicKey3 } from "crypto";
70585
+ import { hkdfSync as hkdfSync2, diffieHellman as diffieHellman2, createPrivateKey as createPrivateKey4, createPublicKey as createPublicKey4 } from "crypto";
70352
70586
 
70353
70587
  // node_modules/@noble/hashes/utils.js
70354
70588
  function isBytes(a) {
@@ -70452,12 +70686,12 @@ function kdfInputToBytes(data, errorTitle = "") {
70452
70686
  return typeof data == "string" ? utf8ToBytes(data) : abytes(data, void 0, errorTitle);
70453
70687
  }
70454
70688
  function concatBytes(...arrays) {
70455
- let sum = 0;
70689
+ let sum2 = 0;
70456
70690
  for (let i = 0; i < arrays.length; i++) {
70457
70691
  let a = arrays[i];
70458
- abytes(a), sum += a.length;
70692
+ abytes(a), sum2 += a.length;
70459
70693
  }
70460
- let res = new Uint8Array(sum);
70694
+ let res = new Uint8Array(sum2);
70461
70695
  for (let i = 0, pad = 0; i < arrays.length; i++) {
70462
70696
  let a = arrays[i];
70463
70697
  res.set(a, pad), pad += a.length;
@@ -71060,7 +71294,7 @@ function tonelliShanks(P) {
71060
71294
  function FpSqrt(P) {
71061
71295
  return P % _4n === _3n ? sqrt3mod4 : P % _8n === _5n ? sqrt5mod8 : P % _16n === _9n ? sqrt9mod16(P) : tonelliShanks(P);
71062
71296
  }
71063
- var isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n2) === _1n2, FIELD_FIELDS = [
71297
+ var isNegativeLE = (num2, modulo) => (mod(num2, modulo) & _1n2) === _1n2, FIELD_FIELDS = [
71064
71298
  "create",
71065
71299
  "isValid",
71066
71300
  "is0",
@@ -71091,22 +71325,22 @@ function validateField(field) {
71091
71325
  throw new Error("invalid field: expected ORDER > 1, got " + field.ORDER);
71092
71326
  return field;
71093
71327
  }
71094
- function FpPow(Fp2, num, power) {
71328
+ function FpPow(Fp2, num2, power) {
71095
71329
  let F = Fp2;
71096
71330
  if (power < _0n2)
71097
71331
  throw new Error("invalid exponent, negatives unsupported");
71098
71332
  if (power === _0n2)
71099
71333
  return F.ONE;
71100
71334
  if (power === _1n2)
71101
- return num;
71102
- let p = F.ONE, d = num;
71335
+ return num2;
71336
+ let p = F.ONE, d = num2;
71103
71337
  for (; power > _0n2; )
71104
71338
  power & _1n2 && (p = F.mul(p, d)), d = F.sqr(d), power >>= _1n2;
71105
71339
  return p;
71106
71340
  }
71107
71341
  function FpInvertBatch(Fp2, nums, passZero = !1) {
71108
- let F = Fp2, inverted = new Array(nums.length).fill(passZero ? F.ZERO : void 0), multipliedAcc = nums.reduce((acc, num, i) => F.is0(num) ? acc : (inverted[i] = acc, F.mul(acc, num)), F.ONE), invertedAcc = F.inv(multipliedAcc);
71109
- return nums.reduceRight((acc, num, i) => F.is0(num) ? acc : (inverted[i] = F.mul(acc, inverted[i]), F.mul(acc, num)), invertedAcc), inverted;
71342
+ let F = Fp2, inverted = new Array(nums.length).fill(passZero ? F.ZERO : void 0), multipliedAcc = nums.reduce((acc, num2, i) => F.is0(num2) ? acc : (inverted[i] = acc, F.mul(acc, num2)), F.ONE), invertedAcc = F.inv(multipliedAcc);
71343
+ return nums.reduceRight((acc, num2, i) => F.is0(num2) ? acc : (inverted[i] = F.mul(acc, inverted[i]), F.mul(acc, num2)), invertedAcc), inverted;
71110
71344
  }
71111
71345
  function FpLegendre(Fp2, n) {
71112
71346
  let F = Fp2, p1mod2 = (F.ORDER - _1n2) / _2n, powered = F.pow(n, p1mod2), yes = F.eql(powered, F.ONE), zero = F.eql(powered, F.ZERO), no = F.eql(powered, F.neg(F.ONE));
@@ -71144,32 +71378,32 @@ var FIELD_SQRT = /* @__PURE__ */ new WeakMap(), _Field = class {
71144
71378
  throw new Error("invalid field: expected ORDER of <= 2048 bytes");
71145
71379
  this.ORDER = ORDER, this.BITS = nBitLength, this.BYTES = nByteLength, Object.freeze(this);
71146
71380
  }
71147
- create(num) {
71148
- return mod(num, this.ORDER);
71381
+ create(num2) {
71382
+ return mod(num2, this.ORDER);
71149
71383
  }
71150
- isValid(num) {
71151
- if (typeof num != "bigint")
71152
- throw new TypeError("invalid field element: expected bigint, got " + typeof num);
71153
- return _0n2 <= num && num < this.ORDER;
71384
+ isValid(num2) {
71385
+ if (typeof num2 != "bigint")
71386
+ throw new TypeError("invalid field element: expected bigint, got " + typeof num2);
71387
+ return _0n2 <= num2 && num2 < this.ORDER;
71154
71388
  }
71155
- is0(num) {
71156
- return num === _0n2;
71389
+ is0(num2) {
71390
+ return num2 === _0n2;
71157
71391
  }
71158
71392
  // is valid and invertible
71159
- isValidNot0(num) {
71160
- return !this.is0(num) && this.isValid(num);
71393
+ isValidNot0(num2) {
71394
+ return !this.is0(num2) && this.isValid(num2);
71161
71395
  }
71162
- isOdd(num) {
71163
- return (num & _1n2) === _1n2;
71396
+ isOdd(num2) {
71397
+ return (num2 & _1n2) === _1n2;
71164
71398
  }
71165
- neg(num) {
71166
- return mod(-num, this.ORDER);
71399
+ neg(num2) {
71400
+ return mod(-num2, this.ORDER);
71167
71401
  }
71168
71402
  eql(lhs, rhs) {
71169
71403
  return lhs === rhs;
71170
71404
  }
71171
- sqr(num) {
71172
- return mod(num * num, this.ORDER);
71405
+ sqr(num2) {
71406
+ return mod(num2 * num2, this.ORDER);
71173
71407
  }
71174
71408
  add(lhs, rhs) {
71175
71409
  return mod(lhs + rhs, this.ORDER);
@@ -71180,15 +71414,15 @@ var FIELD_SQRT = /* @__PURE__ */ new WeakMap(), _Field = class {
71180
71414
  mul(lhs, rhs) {
71181
71415
  return mod(lhs * rhs, this.ORDER);
71182
71416
  }
71183
- pow(num, power) {
71184
- return FpPow(this, num, power);
71417
+ pow(num2, power) {
71418
+ return FpPow(this, num2, power);
71185
71419
  }
71186
71420
  div(lhs, rhs) {
71187
71421
  return mod(lhs * invert(rhs, this.ORDER), this.ORDER);
71188
71422
  }
71189
71423
  // Same as above, but doesn't normalize
71190
- sqrN(num) {
71191
- return num * num;
71424
+ sqrN(num2) {
71425
+ return num2 * num2;
71192
71426
  }
71193
71427
  addN(lhs, rhs) {
71194
71428
  return lhs + rhs;
@@ -71199,15 +71433,15 @@ var FIELD_SQRT = /* @__PURE__ */ new WeakMap(), _Field = class {
71199
71433
  mulN(lhs, rhs) {
71200
71434
  return lhs * rhs;
71201
71435
  }
71202
- inv(num) {
71203
- return invert(num, this.ORDER);
71436
+ inv(num2) {
71437
+ return invert(num2, this.ORDER);
71204
71438
  }
71205
- sqrt(num) {
71439
+ sqrt(num2) {
71206
71440
  let sqrt = FIELD_SQRT.get(this);
71207
- return sqrt || FIELD_SQRT.set(this, sqrt = FpSqrt(this.ORDER)), sqrt(this, num);
71441
+ return sqrt || FIELD_SQRT.set(this, sqrt = FpSqrt(this.ORDER)), sqrt(this, num2);
71208
71442
  }
71209
- toBytes(num) {
71210
- return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES);
71443
+ toBytes(num2) {
71444
+ return this.isLE ? numberToBytesLE(num2, this.BYTES) : numberToBytesBE(num2, this.BYTES);
71211
71445
  }
71212
71446
  fromBytes(bytes, skipValidation = !1) {
71213
71447
  abytes2(bytes);
@@ -71999,8 +72233,8 @@ function encryptPayloadForDid(payload, recipientDid) {
71999
72233
  return null;
72000
72234
  }
72001
72235
  let ephemeral = generateX25519KeyPair(), ephemeralPubRaw = extractRawX25519PublicKey(ephemeral.publicKey), sharedSecret = diffieHellman2({
72002
- privateKey: createPrivateKey3({ key: ephemeral.privateKey, format: "der", type: "pkcs8" }),
72003
- publicKey: createPublicKey3({ key: rawX25519PublicToSpki(recipientXPub), format: "der", type: "spki" })
72236
+ privateKey: createPrivateKey4({ key: ephemeral.privateKey, format: "der", type: "pkcs8" }),
72237
+ publicKey: createPublicKey4({ key: rawX25519PublicToSpki(recipientXPub), format: "der", type: "spki" })
72004
72238
  }), key = deriveMessageKey(sharedSecret, ephemeralPubRaw), sealed = sealWithDek(Buffer.from(JSON.stringify(payload), "utf-8"), key), enc = { v: 1, alg: "x25519-hkdf-aes256gcm", epk: ephemeralPubRaw.toString("base64") };
72005
72239
  return {
72006
72240
  content: sealed.toString("base64"),
@@ -72016,8 +72250,8 @@ function decryptPayloadWithEd25519(payload, ed25519PrivatePkcs8) {
72016
72250
  if (!isEncryptedPayload(payload) || typeof payload.content != "string") return null;
72017
72251
  try {
72018
72252
  let enc = payload.meta.enc, ephemeralPubRaw = Buffer.from(enc.epk, "base64"), xPriv = ed25519PrivateToX25519Pkcs8(ed25519PrivatePkcs8), sharedSecret = diffieHellman2({
72019
- privateKey: createPrivateKey3({ key: xPriv, format: "der", type: "pkcs8" }),
72020
- publicKey: createPublicKey3({ key: rawX25519PublicToSpki(ephemeralPubRaw), format: "der", type: "spki" })
72253
+ privateKey: createPrivateKey4({ key: xPriv, format: "der", type: "pkcs8" }),
72254
+ publicKey: createPublicKey4({ key: rawX25519PublicToSpki(ephemeralPubRaw), format: "der", type: "spki" })
72021
72255
  }), key = deriveMessageKey(sharedSecret, ephemeralPubRaw), plain = openWithDek(Buffer.from(payload.content, "base64"), key);
72022
72256
  return plain ? JSON.parse(plain.toString("utf-8")) : null;
72023
72257
  } catch {
@@ -76448,9 +76682,7 @@ var ChannelAdapterManager = class extends EventEmitter7 {
76448
76682
  }
76449
76683
  return { ingested: state.ingested, deduped: state.deduped };
76450
76684
  },
76451
- writeAttachment: (path, data, mimeType) => {
76452
- workspace.writeFileBuffer(path, data, mimeType);
76453
- },
76685
+ writeAttachment: (path, data, mimeType) => workspace.writeFileBufferUnique(path, data, mimeType),
76454
76686
  getConfig: () => config,
76455
76687
  getCredential: (key) => workspace.getIdentityDecrypted(`adapter:${type}:${key}`, derivedKey) ?? null,
76456
76688
  log: (level, message) => {
@@ -76528,11 +76760,11 @@ var ChannelAdapterManager = class extends EventEmitter7 {
76528
76760
 
76529
76761
  // src/main/adapters/built-in-loaders.ts
76530
76762
  var BUILT_IN_ADAPTER_LOADERS = {
76531
- telegram: async () => (await import("./telegram-V4VH4PNE.mjs")).createAdapter,
76532
- email: async () => (await import("./email-V4Q4VJON.mjs")).createAdapter,
76533
- discord: async () => (await import("./discord-IWDQJA7A.mjs")).createAdapter,
76534
- slack: async () => (await import("./slack-3L5QTDZX.mjs")).createAdapter,
76535
- whatsapp: async () => (await import("./whatsapp-RDOC54X5.mjs")).createAdapter
76763
+ telegram: async () => (await import("./telegram-YI2OHNTD.mjs")).createAdapter,
76764
+ email: async () => (await import("./email-BRG7KUO2.mjs")).createAdapter,
76765
+ discord: async () => (await import("./discord-DSP4HUWI.mjs")).createAdapter,
76766
+ slack: async () => (await import("./slack-OCNNF4DX.mjs")).createAdapter,
76767
+ whatsapp: async () => (await import("./whatsapp-AVKT3CTJ.mjs")).createAdapter
76536
76768
  };
76537
76769
  async function loadBuiltInAdapter(adapterType) {
76538
76770
  let loader = BUILT_IN_ADAPTER_LOADERS[adapterType];
@@ -76931,6 +77163,7 @@ function buildOAuthProviderFactory(resolveStore, io = SILENT_OAUTH_IO, resolveOp
76931
77163
  var MCP_CONNECT_BUDGET_MS = 25e3, AgentRuntimeBuilder = class {
76932
77164
  settings;
76933
77165
  codeSandboxService;
77166
+ sandboxPackagesService;
76934
77167
  podmanService;
76935
77168
  wsConnectionManager;
76936
77169
  mcpPackageResolver;
@@ -76943,7 +77176,7 @@ var MCP_CONNECT_BUDGET_MS = 25e3, AgentRuntimeBuilder = class {
76943
77176
  mcpAuthPreflight;
76944
77177
  credentialEnvelopeLockedHint;
76945
77178
  constructor(opts = {}) {
76946
- this.settings = opts.settings, this.codeSandboxService = opts.codeSandboxService ?? null, this.podmanService = opts.podmanService ?? null, this.wsConnectionManager = opts.wsConnectionManager ?? null, this.mcpPackageResolver = opts.mcpPackageResolver ?? new PackageResolver("mcp-servers"), this.adapterPackageResolver = opts.adapterPackageResolver ?? new PackageResolver("channel-adapters"), this.uvxPackageResolver = opts.uvxPackageResolver ?? null, this.uvManager = opts.uvManager ?? null, this.basePrompt = opts.basePrompt ?? "", this.toolPrompts = opts.toolPrompts ?? {}, this.compactionPrompt = opts.compactionPrompt, this.mcpAuthPreflight = opts.mcpAuthPreflight ?? createHeadlessMcpAuthPreflight(), this.credentialEnvelopeLockedHint = opts.credentialEnvelopeLockedHint;
77179
+ this.settings = opts.settings, this.codeSandboxService = opts.codeSandboxService ?? null, this.sandboxPackagesService = opts.sandboxPackagesService ?? null, this.podmanService = opts.podmanService ?? null, this.wsConnectionManager = opts.wsConnectionManager ?? null, this.mcpPackageResolver = opts.mcpPackageResolver ?? new PackageResolver("mcp-servers"), this.adapterPackageResolver = opts.adapterPackageResolver ?? new PackageResolver("channel-adapters"), this.uvxPackageResolver = opts.uvxPackageResolver ?? null, this.uvManager = opts.uvManager ?? null, this.basePrompt = opts.basePrompt ?? "", this.toolPrompts = opts.toolPrompts ?? {}, this.compactionPrompt = opts.compactionPrompt, this.mcpAuthPreflight = opts.mcpAuthPreflight ?? createHeadlessMcpAuthPreflight(), this.credentialEnvelopeLockedHint = opts.credentialEnvelopeLockedHint;
76947
77180
  }
76948
77181
  async build(opts) {
76949
77182
  let { workspace, filePath, config, provider } = opts, agentId = filePath ?? config.id;
@@ -77118,17 +77351,21 @@ var MCP_CONNECT_BUDGET_MS = 25e3, AgentRuntimeBuilder = class {
77118
77351
  });
77119
77352
  }
77120
77353
  registerCodeTools(registry3, config, agentId, adfCallHandler) {
77121
- this.codeSandboxService && (config.tools.some((t) => t.name === "sys_code") && registry3.register(new SysCodeTool(
77122
- this.codeSandboxService,
77354
+ let sandbox = this.codeSandboxService;
77355
+ if (!sandbox) return;
77356
+ registry3.provideDeclared("sys_code", () => new SysCodeTool(
77357
+ sandbox,
77123
77358
  agentId,
77124
77359
  adfCallHandler ?? void 0,
77125
77360
  config.limits?.execution_timeout_ms
77126
- )), adfCallHandler && config.tools.some((t) => t.name === "sys_lambda") && registry3.register(new SysLambdaTool(
77127
- this.codeSandboxService,
77361
+ ), config.tools), adfCallHandler && registry3.provideDeclared("sys_lambda", () => new SysLambdaTool(
77362
+ sandbox,
77128
77363
  adfCallHandler,
77129
77364
  agentId,
77130
77365
  config.limits?.execution_timeout_ms
77131
- )));
77366
+ ), config.tools);
77367
+ let packages = this.sandboxPackagesService;
77368
+ packages && (registry3.provideDeclared("npm_install", () => new NpmInstallTool(packages), config.tools), registry3.provideDeclared("npm_uninstall", () => new NpmUninstallTool(), config.tools));
77132
77369
  }
77133
77370
  registerComputeTools(registry3, config, filePath) {
77134
77371
  let agentHostAllowed = !!config.compute?.host_access, computeSettings = this.settings?.get("compute"), runtimeHostAllowed = this.getComputeRoutingSettings().hostAccessEnabled, hostInfo = agentHostAllowed && runtimeHostAllowed ? describeHostEnv() : void 0, targetSelection = resolveAgentComputeTargetSelection(computeSettings, config.compute), caps = {
@@ -78747,6 +78984,26 @@ var RuntimeLoopError = class extends Error {
78747
78984
  listAgents() {
78748
78985
  return Array.from(this.agents.values()).map((managed) => this.toSummary(managed));
78749
78986
  }
78987
+ /**
78988
+ * Loaded file-backed agents with their live executor state, context gauge
78989
+ * and open workspace. Agent vitals reads a running agent out of this
78990
+ * workspace instead of reopening its file.
78991
+ */
78992
+ listLiveAgents() {
78993
+ let out = [];
78994
+ for (let managed of this.agents.values()) {
78995
+ if (!managed.filePath) continue;
78996
+ let executor = managed.agent.executor;
78997
+ out.push({
78998
+ agentId: managed.id,
78999
+ filePath: managed.filePath,
79000
+ executorState: executor.getState(),
79001
+ workspace: managed.agent.workspace,
79002
+ getContextGauge: () => executor.getContextGauge()
79003
+ });
79004
+ }
79005
+ return out;
79006
+ }
78750
79007
  on(event, listener) {
78751
79008
  return super.on(event, listener);
78752
79009
  }
@@ -100700,45 +100957,67 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
100700
100957
  }
100701
100958
  }
100702
100959
  },
100703
- "/agents/{id}/start": {
100704
- post: {
100960
+ "/agents/{id}/vitals": {
100961
+ get: {
100705
100962
  tags: [
100706
100963
  "Agents"
100707
100964
  ],
100708
- summary: "Start an agent, loading it from a tracked folder when needed",
100709
- description: "`id` may also name an agent that is not loaded: an .adf path, or an id / handle / name found in the tracked folders. The daemon loads it (review gate applies: 403) and starts it; `loaded` says whether it did. `startupTriggered` is true when the agent's start state fired its startup event. An identifier that matches several files answers 409 `ambiguous_agent` with the `candidates`: start one by its file path. No body.",
100710
- operationId: "startAgent",
100965
+ summary: "Agent vitals",
100966
+ description: "Studio's overview card for one agent: header facts (context size, next wake, 7-day cost, model, age) and four levelled stats: Reach, Access and Autonomy (levels from config points, with the factors behind them) and Experience (a level from maturity counts). `{id}` names a loaded agent (id, handle or name, as every /agents/{id} route) or, when no loaded agent matches, a tracked agent that is not loaded (agent id or handle from its file). A loaded agent is read from its open workspace (`live: true`); any other file is read with a readonly open. Results are cached: a live agent's for at most 2 s and until its database changes, any other file's until its mtime or size changes. `force=1` skips the cache. `agentsSpawned` counts tracked agents whose parent DID names this agent, from a fleet scan at most 30 s old (`force=1` rescans). `contextTokens` and `contextThreshold` are present only for a loaded agent whose executor has reported usage; `cost7dUsd` only when some adf_loop row of the last 7 local days has a price (the row's recorded `cost_usd`; rows from before 2026-08-27 without one are priced from Studio's pricing table).",
100967
+ operationId: "getAgentVitals",
100711
100968
  parameters: [
100712
100969
  {
100713
100970
  $ref: "#/components/parameters/AgentIdPath"
100971
+ },
100972
+ {
100973
+ name: "force",
100974
+ in: "query",
100975
+ required: !1,
100976
+ description: "Skip the cache: `1` or `true`. `0` and `false` are accepted and change nothing.",
100977
+ schema: {
100978
+ type: "string",
100979
+ enum: [
100980
+ "1",
100981
+ "0",
100982
+ "true",
100983
+ "false"
100984
+ ]
100985
+ }
100714
100986
  }
100715
100987
  ],
100716
100988
  responses: {
100717
100989
  "200": {
100718
- description: "Started",
100990
+ description: "Vitals",
100719
100991
  content: {
100720
100992
  "application/json": {
100721
100993
  schema: {
100722
- $ref: "#/components/schemas/StartAgentResponse"
100994
+ $ref: "#/components/schemas/AgentVitals"
100723
100995
  }
100724
100996
  }
100725
100997
  }
100726
100998
  },
100999
+ "400": {
101000
+ $ref: "#/components/responses/BadRequest"
101001
+ },
100727
101002
  "401": {
100728
101003
  $ref: "#/components/responses/Unauthorized"
100729
101004
  },
100730
101005
  "403": {
100731
- $ref: "#/components/responses/ReviewRequired"
101006
+ $ref: "#/components/responses/Forbidden"
100732
101007
  },
100733
101008
  "404": {
100734
101009
  $ref: "#/components/responses/NotFound"
100735
101010
  },
100736
101011
  "409": {
100737
- description: "The identifier matches several agent files (`ambiguous_agent`)",
101012
+ description: "The agent is loaded without an .adf file; vitals are read from the file",
100738
101013
  content: {
100739
101014
  "application/json": {
100740
101015
  schema: {
100741
- $ref: "#/components/schemas/AmbiguousAgentResponse"
101016
+ $ref: "#/components/schemas/ErrorResponse"
101017
+ },
101018
+ example: {
101019
+ error: "Agent has no .adf file; vitals are read from the file.",
101020
+ code: "conflict"
100742
101021
  }
100743
101022
  }
100744
101023
  }
@@ -100749,30 +101028,48 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
100749
101028
  }
100750
101029
  }
100751
101030
  },
100752
- "/agents/{id}/stop": {
100753
- post: {
101031
+ "/agents/{id}/activity": {
101032
+ get: {
100754
101033
  tags: [
100755
101034
  "Agents"
100756
101035
  ],
100757
- summary: "Stop and unload an agent",
100758
- description: "Graceful: a running turn gets a 5 s grace period, then the agent is unloaded (its file stays on disk). No body.",
100759
- operationId: "stopAgent",
101036
+ summary: "Agent activity",
101037
+ description: "The lower sections of Studio's agent overview: the next three timer wakes (raw lambda and input, or loop and prompt), 14 local days of messages and cost per day, and what the file holds (`mind/` files and skills with approximate tokens, `local_*` tables with rows). `{id}` resolves as in `GET /agents/{id}/vitals`. Everything is read from the .adf file. Messages per day: `adf_loop` rows of every role by `created_at`, plus compacted rows archived in `adf_audit` (metadata only, never the archived data): each archive's `entry_count` is spread evenly over an estimated span ending at its `created_at` and at most 24 h long, and days that include such counts have `estimated: true`. Cost per day: the sum of the day's priced loop rows; `costPartial` when some call had no recorded price or was compacted. The first read scans the window's loop rows once; later reads add only new rows. Skills follow the vitals rule: written more than 10 s after `adf_created_at`, `skills-registry.json` excluded. Cached like vitals (and per local day); `force=1` skips the cache. Pending approvals, asks and unread counts are not here: read `GET /agents/{id}/asks` and `GET /agents/{id}/inbox`.",
101038
+ operationId: "getAgentActivity",
100760
101039
  parameters: [
100761
101040
  {
100762
101041
  $ref: "#/components/parameters/AgentIdPath"
101042
+ },
101043
+ {
101044
+ name: "force",
101045
+ in: "query",
101046
+ required: !1,
101047
+ description: "Skip the cache: `1` or `true`. `0` and `false` are accepted and change nothing.",
101048
+ schema: {
101049
+ type: "string",
101050
+ enum: [
101051
+ "1",
101052
+ "0",
101053
+ "true",
101054
+ "false"
101055
+ ]
101056
+ }
100763
101057
  }
100764
101058
  ],
100765
101059
  responses: {
100766
101060
  "200": {
100767
- description: "Stopped and unloaded",
101061
+ description: "Activity",
100768
101062
  content: {
100769
101063
  "application/json": {
100770
101064
  schema: {
100771
- $ref: "#/components/schemas/SuccessResponse"
101065
+ $ref: "#/components/schemas/AgentActivity"
100772
101066
  }
100773
101067
  }
100774
101068
  }
100775
101069
  },
101070
+ "400": {
101071
+ $ref: "#/components/responses/BadRequest"
101072
+ },
100776
101073
  "401": {
100777
101074
  $ref: "#/components/responses/Unauthorized"
100778
101075
  },
@@ -100782,20 +101079,34 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
100782
101079
  "404": {
100783
101080
  $ref: "#/components/responses/NotFound"
100784
101081
  },
101082
+ "409": {
101083
+ description: "The agent is loaded without an .adf file; activity is read from the file",
101084
+ content: {
101085
+ "application/json": {
101086
+ schema: {
101087
+ $ref: "#/components/schemas/ErrorResponse"
101088
+ },
101089
+ example: {
101090
+ error: "Agent has no .adf file; activity is read from the file.",
101091
+ code: "conflict"
101092
+ }
101093
+ }
101094
+ }
101095
+ },
100785
101096
  "500": {
100786
101097
  $ref: "#/components/responses/ServerError"
100787
101098
  }
100788
101099
  }
100789
101100
  }
100790
101101
  },
100791
- "/agents/{id}/unload": {
101102
+ "/agents/{id}/start": {
100792
101103
  post: {
100793
101104
  tags: [
100794
101105
  "Agents"
100795
101106
  ],
100796
- summary: "Unload an agent (alias of stop)",
100797
- description: "No body.",
100798
- operationId: "unloadAgent",
101107
+ summary: "Start an agent, loading it from a tracked folder when needed",
101108
+ description: "`id` may also name an agent that is not loaded: an .adf path, or an id / handle / name found in the tracked folders. The daemon loads it (review gate applies: 403) and starts it; `loaded` says whether it did. `startupTriggered` is true when the agent's start state fired its startup event. An identifier that matches several files answers 409 `ambiguous_agent` with the `candidates`: start one by its file path. No body.",
101109
+ operationId: "startAgent",
100799
101110
  parameters: [
100800
101111
  {
100801
101112
  $ref: "#/components/parameters/AgentIdPath"
@@ -100803,11 +101114,11 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
100803
101114
  ],
100804
101115
  responses: {
100805
101116
  "200": {
100806
- description: "Unloaded",
101117
+ description: "Started",
100807
101118
  content: {
100808
101119
  "application/json": {
100809
101120
  schema: {
100810
- $ref: "#/components/schemas/SuccessResponse"
101121
+ $ref: "#/components/schemas/StartAgentResponse"
100811
101122
  }
100812
101123
  }
100813
101124
  }
@@ -100816,51 +101127,86 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
100816
101127
  $ref: "#/components/responses/Unauthorized"
100817
101128
  },
100818
101129
  "403": {
100819
- $ref: "#/components/responses/Forbidden"
101130
+ $ref: "#/components/responses/ReviewRequired"
100820
101131
  },
100821
101132
  "404": {
100822
101133
  $ref: "#/components/responses/NotFound"
100823
101134
  },
101135
+ "409": {
101136
+ description: "The identifier matches several agent files (`ambiguous_agent`)",
101137
+ content: {
101138
+ "application/json": {
101139
+ schema: {
101140
+ $ref: "#/components/schemas/AmbiguousAgentResponse"
101141
+ }
101142
+ }
101143
+ }
101144
+ },
100824
101145
  "500": {
100825
101146
  $ref: "#/components/responses/ServerError"
100826
101147
  }
100827
101148
  }
100828
101149
  }
100829
101150
  },
100830
- "/agents/{id}/abort": {
101151
+ "/agents/{id}/stop": {
100831
101152
  post: {
100832
101153
  tags: [
100833
101154
  "Agents"
100834
101155
  ],
100835
- summary: "Abort the current turn without unloading",
100836
- description: "A hard stop: the executor of main (or the named inner loop) is left `stopped` and runs no further turns, triggers or timers until the agent is reloaded. To end a turn and keep working, use /interrupt. Chats still queued behind the turn are discarded, never silently: a `chat.discarded` event names their `turnId`s and a System notice lands in the loop. Unknown loop 404; a loop with no running executor 409.",
100837
- operationId: "abortAgent",
101156
+ summary: "Stop and unload an agent",
101157
+ description: "Graceful: a running turn gets a 5 s grace period, then the agent is unloaded (its file stays on disk). No body.",
101158
+ operationId: "stopAgent",
100838
101159
  parameters: [
100839
101160
  {
100840
101161
  $ref: "#/components/parameters/AgentIdPath"
100841
- },
100842
- {
100843
- $ref: "#/components/parameters/LoopQuery"
100844
101162
  }
100845
101163
  ],
100846
- requestBody: {
100847
- required: !1,
100848
- description: "Optional; `?loop=` wins.",
100849
- content: {
100850
- "application/json": {
100851
- schema: {
100852
- $ref: "#/components/schemas/LoopBody"
101164
+ responses: {
101165
+ "200": {
101166
+ description: "Stopped and unloaded",
101167
+ content: {
101168
+ "application/json": {
101169
+ schema: {
101170
+ $ref: "#/components/schemas/SuccessResponse"
101171
+ }
100853
101172
  }
100854
101173
  }
101174
+ },
101175
+ "401": {
101176
+ $ref: "#/components/responses/Unauthorized"
101177
+ },
101178
+ "403": {
101179
+ $ref: "#/components/responses/Forbidden"
101180
+ },
101181
+ "404": {
101182
+ $ref: "#/components/responses/NotFound"
101183
+ },
101184
+ "500": {
101185
+ $ref: "#/components/responses/ServerError"
100855
101186
  }
100856
- },
101187
+ }
101188
+ }
101189
+ },
101190
+ "/agents/{id}/unload": {
101191
+ post: {
101192
+ tags: [
101193
+ "Agents"
101194
+ ],
101195
+ summary: "Unload an agent (alias of stop)",
101196
+ description: "No body.",
101197
+ operationId: "unloadAgent",
101198
+ parameters: [
101199
+ {
101200
+ $ref: "#/components/parameters/AgentIdPath"
101201
+ }
101202
+ ],
100857
101203
  responses: {
100858
101204
  "200": {
100859
- description: "Aborted",
101205
+ description: "Unloaded",
100860
101206
  content: {
100861
101207
  "application/json": {
100862
101208
  schema: {
100863
- $ref: "#/components/schemas/AbortResponse"
101209
+ $ref: "#/components/schemas/SuccessResponse"
100864
101210
  }
100865
101211
  }
100866
101212
  }
@@ -100874,23 +101220,76 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
100874
101220
  "404": {
100875
101221
  $ref: "#/components/responses/NotFound"
100876
101222
  },
100877
- "409": {
100878
- $ref: "#/components/responses/Conflict"
100879
- },
100880
101223
  "500": {
100881
101224
  $ref: "#/components/responses/ServerError"
100882
101225
  }
100883
101226
  }
100884
101227
  }
100885
101228
  },
100886
- "/agents/{id}/interrupt": {
101229
+ "/agents/{id}/abort": {
100887
101230
  post: {
100888
101231
  tags: [
100889
101232
  "Agents"
100890
101233
  ],
100891
- summary: "Interrupt the running turn and leave the loop idle",
100892
- description: "Ends main's (or the named loop's) running turn and sets that executor idle; it keeps accepting chats, triggers and timers. Unlike /abort it never stops the executor, and chats queued behind the turn are kept and run next. `interrupted` is false when nothing was running; 409 when the loop is stopped or errored.",
100893
- operationId: "interruptAgent",
101234
+ summary: "Abort the current turn without unloading",
101235
+ description: "A hard stop: the executor of main (or the named inner loop) is left `stopped` and runs no further turns, triggers or timers until the agent is reloaded. To end a turn and keep working, use /interrupt. Chats still queued behind the turn are discarded, never silently: a `chat.discarded` event names their `turnId`s and a System notice lands in the loop. Unknown loop 404; a loop with no running executor 409.",
101236
+ operationId: "abortAgent",
101237
+ parameters: [
101238
+ {
101239
+ $ref: "#/components/parameters/AgentIdPath"
101240
+ },
101241
+ {
101242
+ $ref: "#/components/parameters/LoopQuery"
101243
+ }
101244
+ ],
101245
+ requestBody: {
101246
+ required: !1,
101247
+ description: "Optional; `?loop=` wins.",
101248
+ content: {
101249
+ "application/json": {
101250
+ schema: {
101251
+ $ref: "#/components/schemas/LoopBody"
101252
+ }
101253
+ }
101254
+ }
101255
+ },
101256
+ responses: {
101257
+ "200": {
101258
+ description: "Aborted",
101259
+ content: {
101260
+ "application/json": {
101261
+ schema: {
101262
+ $ref: "#/components/schemas/AbortResponse"
101263
+ }
101264
+ }
101265
+ }
101266
+ },
101267
+ "401": {
101268
+ $ref: "#/components/responses/Unauthorized"
101269
+ },
101270
+ "403": {
101271
+ $ref: "#/components/responses/Forbidden"
101272
+ },
101273
+ "404": {
101274
+ $ref: "#/components/responses/NotFound"
101275
+ },
101276
+ "409": {
101277
+ $ref: "#/components/responses/Conflict"
101278
+ },
101279
+ "500": {
101280
+ $ref: "#/components/responses/ServerError"
101281
+ }
101282
+ }
101283
+ }
101284
+ },
101285
+ "/agents/{id}/interrupt": {
101286
+ post: {
101287
+ tags: [
101288
+ "Agents"
101289
+ ],
101290
+ summary: "Interrupt the running turn and leave the loop idle",
101291
+ description: "Ends main's (or the named loop's) running turn and sets that executor idle; it keeps accepting chats, triggers and timers. Unlike /abort it never stops the executor, and chats queued behind the turn are kept and run next. `interrupted` is false when nothing was running; 409 when the loop is stopped or errored.",
101292
+ operationId: "interruptAgent",
100894
101293
  parameters: [
100895
101294
  {
100896
101295
  $ref: "#/components/parameters/AgentIdPath"
@@ -117808,6 +118207,645 @@ data: {"cursor":42,"event":{"seq":7,"event_type":"agent.state.changed","turn_id"
117808
118207
  }
117809
118208
  ]
117810
118209
  }
118210
+ },
118211
+ AgentVitals: {
118212
+ type: "object",
118213
+ required: [
118214
+ "filePath",
118215
+ "computedAt",
118216
+ "live",
118217
+ "handle",
118218
+ "ageDays",
118219
+ "public",
118220
+ "ownerVerified",
118221
+ "stats",
118222
+ "maturity",
118223
+ "metrics"
118224
+ ],
118225
+ properties: {
118226
+ filePath: {
118227
+ type: "string"
118228
+ },
118229
+ computedAt: {
118230
+ type: "number",
118231
+ description: "When the stats and counts were computed (ms epoch)"
118232
+ },
118233
+ live: {
118234
+ type: "boolean",
118235
+ description: "True when read from the workspace of a loaded agent"
118236
+ },
118237
+ handle: {
118238
+ type: "string",
118239
+ example: "agent-1"
118240
+ },
118241
+ name: {
118242
+ type: "string"
118243
+ },
118244
+ did: {
118245
+ type: "string"
118246
+ },
118247
+ agentId: {
118248
+ type: "string"
118249
+ },
118250
+ model: {
118251
+ type: "string"
118252
+ },
118253
+ createdAt: {
118254
+ type: "string",
118255
+ description: "adf_created_at (ISO 8601)"
118256
+ },
118257
+ ageDays: {
118258
+ type: "number"
118259
+ },
118260
+ status: {
118261
+ type: "string",
118262
+ description: "The agent's own status line (adf_meta `status`); absent when unset"
118263
+ },
118264
+ description: {
118265
+ type: "string",
118266
+ description: "config.description, as on the ALF card; absent when blank"
118267
+ },
118268
+ public: {
118269
+ type: "boolean",
118270
+ description: "config.serving.public.enabled, the ALF card's `public`"
118271
+ },
118272
+ ownerVerified: {
118273
+ type: "boolean",
118274
+ description: "An owner attestation about the current DID is stored, unexpired and its signature verifies (whether or not the card publishes it)"
118275
+ },
118276
+ contextTokens: {
118277
+ type: "integer",
118278
+ description: "Last API-reported context size (loaded agents only)"
118279
+ },
118280
+ contextThreshold: {
118281
+ type: "integer",
118282
+ description: "Compact threshold (present with contextTokens)"
118283
+ },
118284
+ nextWakeAt: {
118285
+ type: "number",
118286
+ description: "Earliest next_wake_at among non-expired timers (ms epoch)"
118287
+ },
118288
+ cost7dUsd: {
118289
+ type: "number",
118290
+ description: "USD over the last 7 local days, summed from the agent's priced adf_loop rows (the activity read's daily cost)"
118291
+ },
118292
+ cost7dPartial: {
118293
+ type: "boolean",
118294
+ description: "True when some calls in the window had no recorded price or were compacted (cost7dUsd is a lower bound)"
118295
+ },
118296
+ metrics: {
118297
+ type: "array",
118298
+ description: 'adf_meta rows whose key starts with `metric:`, by key, at most 20. A value is a plain string, or a JSON object `{"value": number|string, "label"?, "unit"?, "min"?, "max"?, "target"?}`; parsed fields are returned beside `raw`. A value that is not such an object is returned as `value` = `raw`. Not part of any stat.',
118299
+ items: {
118300
+ type: "object",
118301
+ required: [
118302
+ "name",
118303
+ "label",
118304
+ "value",
118305
+ "raw"
118306
+ ],
118307
+ properties: {
118308
+ name: {
118309
+ type: "string",
118310
+ description: "Key without the `metric:` prefix",
118311
+ example: "weight"
118312
+ },
118313
+ label: {
118314
+ type: "string",
118315
+ description: "The object's `label` when given, else `name`",
118316
+ example: "Weight"
118317
+ },
118318
+ value: {
118319
+ oneOf: [
118320
+ {
118321
+ type: "number"
118322
+ },
118323
+ {
118324
+ type: "string"
118325
+ }
118326
+ ],
118327
+ description: "The object's `value`, or the raw string when the stored value is not a metric object",
118328
+ example: 82.4
118329
+ },
118330
+ unit: {
118331
+ type: "string",
118332
+ example: "kg"
118333
+ },
118334
+ min: {
118335
+ type: "number",
118336
+ description: "Bar start when `max` is set (default 0)"
118337
+ },
118338
+ max: {
118339
+ type: "number",
118340
+ example: 100
118341
+ },
118342
+ target: {
118343
+ type: "number",
118344
+ example: 78
118345
+ },
118346
+ raw: {
118347
+ type: "string",
118348
+ description: "The stored adf_meta value as is",
118349
+ example: '{"value":82.4,"label":"Weight","unit":"kg","target":78}'
118350
+ }
118351
+ }
118352
+ }
118353
+ },
118354
+ stats: {
118355
+ type: "object",
118356
+ required: [
118357
+ "reach",
118358
+ "access",
118359
+ "autonomy",
118360
+ "experience"
118361
+ ],
118362
+ properties: {
118363
+ reach: {
118364
+ $ref: "#/components/schemas/AgentPowerStat"
118365
+ },
118366
+ access: {
118367
+ $ref: "#/components/schemas/AgentPowerStat"
118368
+ },
118369
+ autonomy: {
118370
+ $ref: "#/components/schemas/AgentPowerStat"
118371
+ },
118372
+ experience: {
118373
+ $ref: "#/components/schemas/AgentExperienceStat"
118374
+ }
118375
+ }
118376
+ },
118377
+ maturity: {
118378
+ $ref: "#/components/schemas/AgentMaturity"
118379
+ }
118380
+ }
118381
+ },
118382
+ AgentPowerStat: {
118383
+ type: "object",
118384
+ description: "Reach, Access or Autonomy: a capped level on a 1-20 scale. fill = clamp(points / max, 0, 1); level = 1 + round(19 * fill), so an empty config is Lv 1 and a maxed-out one Lv 20. `max` is the sum of every capped positive factor of the stat (counts such as MCP servers and chat adapters only count their heaviest few). Gated points count toward the level. `points` is `open` + `gated`.",
118385
+ required: [
118386
+ "level",
118387
+ "points",
118388
+ "max",
118389
+ "fill",
118390
+ "open",
118391
+ "gated",
118392
+ "high",
118393
+ "rawPoints",
118394
+ "factors"
118395
+ ],
118396
+ properties: {
118397
+ level: {
118398
+ type: "integer",
118399
+ minimum: 1,
118400
+ maximum: 20
118401
+ },
118402
+ points: {
118403
+ type: "number",
118404
+ minimum: 0,
118405
+ description: "Points behind the level, after mitigations"
118406
+ },
118407
+ max: {
118408
+ type: "number",
118409
+ exclusiveMinimum: 0,
118410
+ description: "Points of a maxed-out config for this stat"
118411
+ },
118412
+ fill: {
118413
+ type: "number",
118414
+ minimum: 0,
118415
+ maximum: 1,
118416
+ description: "points / max, clamped: how much of the possible power the agent has"
118417
+ },
118418
+ open: {
118419
+ type: "number",
118420
+ description: "Points whose capability runs without approval (mitigations come off these first)"
118421
+ },
118422
+ gated: {
118423
+ type: "number",
118424
+ description: "Points whose capability needs approval"
118425
+ },
118426
+ high: {
118427
+ type: "boolean",
118428
+ description: "True when open / max >= 0.6 (open level 12 or more): much of this runs without anyone asking"
118429
+ },
118430
+ rawPoints: {
118431
+ type: "number",
118432
+ description: "Sum of factor points, mitigations included"
118433
+ },
118434
+ factors: {
118435
+ type: "array",
118436
+ items: {
118437
+ $ref: "#/components/schemas/AgentStatFactor"
118438
+ }
118439
+ }
118440
+ }
118441
+ },
118442
+ AgentStatFactor: {
118443
+ type: "object",
118444
+ description: "One line of a stat breakdown.",
118445
+ required: [
118446
+ "id",
118447
+ "label",
118448
+ "points",
118449
+ "gated",
118450
+ "configPath"
118451
+ ],
118452
+ properties: {
118453
+ id: {
118454
+ type: "string",
118455
+ description: "Stable id, e.g. `tool:compute_exec`, `mcp:github`, `visibility`",
118456
+ example: "tool:compute_exec"
118457
+ },
118458
+ label: {
118459
+ type: "string"
118460
+ },
118461
+ points: {
118462
+ type: "number",
118463
+ description: "Points this factor adds; negative for mitigations; 0 for an item past its kind's cap (7th MCP server, 4th chat adapter), listed but not counted"
118464
+ },
118465
+ gated: {
118466
+ type: "boolean",
118467
+ description: "True when the capability needs human approval"
118468
+ },
118469
+ configPath: {
118470
+ type: "string",
118471
+ description: "Config path (or table) that controls this factor"
118472
+ }
118473
+ }
118474
+ },
118475
+ AgentExperienceStat: {
118476
+ type: "object",
118477
+ description: "Experience. xpForLevel(L) = 5 * (L - 1)^2.25; level = largest L with xpForLevel(L) <= score.",
118478
+ required: [
118479
+ "level",
118480
+ "progress",
118481
+ "score",
118482
+ "levelStart",
118483
+ "nextLevelAt",
118484
+ "breakdown",
118485
+ "nextLevel"
118486
+ ],
118487
+ properties: {
118488
+ level: {
118489
+ type: "integer",
118490
+ minimum: 1
118491
+ },
118492
+ progress: {
118493
+ type: "number",
118494
+ minimum: 0,
118495
+ maximum: 1,
118496
+ description: "Progress within the current level"
118497
+ },
118498
+ score: {
118499
+ type: "number",
118500
+ description: "Total XP (sum of signal XP)"
118501
+ },
118502
+ levelStart: {
118503
+ type: "number",
118504
+ description: "XP at which the current level starts"
118505
+ },
118506
+ nextLevelAt: {
118507
+ type: "number",
118508
+ description: "XP at which the next level starts"
118509
+ },
118510
+ breakdown: {
118511
+ type: "array",
118512
+ items: {
118513
+ type: "object",
118514
+ required: [
118515
+ "id",
118516
+ "label",
118517
+ "value",
118518
+ "xp"
118519
+ ],
118520
+ properties: {
118521
+ id: {
118522
+ type: "string",
118523
+ enum: [
118524
+ "contextsWorked",
118525
+ "memoryTokens",
118526
+ "skills",
118527
+ "localTables",
118528
+ "localRows",
118529
+ "filesWritten",
118530
+ "agentsSpawned",
118531
+ "messages",
118532
+ "ageDays"
118533
+ ]
118534
+ },
118535
+ label: {
118536
+ type: "string"
118537
+ },
118538
+ value: {
118539
+ type: "number",
118540
+ description: "Raw count"
118541
+ },
118542
+ xp: {
118543
+ type: "number",
118544
+ description: "XP this signal adds"
118545
+ }
118546
+ }
118547
+ }
118548
+ },
118549
+ nextLevel: {
118550
+ type: "object",
118551
+ description: "What one more level takes (xp = nextLevelAt - score), in units of the cheapest signals",
118552
+ required: [
118553
+ "xp",
118554
+ "contexts",
118555
+ "memoryTokens",
118556
+ "skills",
118557
+ "hint"
118558
+ ],
118559
+ properties: {
118560
+ xp: {
118561
+ type: "number"
118562
+ },
118563
+ contexts: {
118564
+ type: "integer",
118565
+ description: "Contexts of work worth the remaining XP"
118566
+ },
118567
+ memoryTokens: {
118568
+ type: "integer",
118569
+ description: "Memory tokens worth the remaining XP"
118570
+ },
118571
+ skills: {
118572
+ type: "integer",
118573
+ description: "Skills worth the remaining XP"
118574
+ },
118575
+ hint: {
118576
+ type: "string"
118577
+ }
118578
+ }
118579
+ }
118580
+ }
118581
+ },
118582
+ AgentMaturity: {
118583
+ type: "object",
118584
+ description: "Raw maturity counts read from the agent's file.",
118585
+ required: [
118586
+ "contextsWorked",
118587
+ "filesWritten",
118588
+ "memoryTokens",
118589
+ "localTables",
118590
+ "localRows",
118591
+ "skills",
118592
+ "agentsSpawned",
118593
+ "messages",
118594
+ "ageDays"
118595
+ ],
118596
+ properties: {
118597
+ contextsWorked: {
118598
+ type: "number",
118599
+ minimum: 0,
118600
+ description: "Contexts of work over every loop (main, side, deleted): archived loop snapshots in adf_audit (one context at half the loop's compaction threshold or more, else its share) or live compaction summaries when loop audit is off, plus each live loop's current fill as a 0..1 share of its compaction threshold"
118601
+ },
118602
+ filesWritten: {
118603
+ type: "integer",
118604
+ description: "adf_files rows changed after the agent was created, `mind/` excluded"
118605
+ },
118606
+ memoryTokens: {
118607
+ type: "integer",
118608
+ description: "Approximate tokens in `mind/` files: total bytes, less the seeded `mind/log.md` header, / 4"
118609
+ },
118610
+ localTables: {
118611
+ type: "integer",
118612
+ description: "`local_*` tables"
118613
+ },
118614
+ localRows: {
118615
+ type: "integer",
118616
+ description: "Rows across `local_*` tables (the first 50 by name)"
118617
+ },
118618
+ skills: {
118619
+ type: "integer",
118620
+ description: "Distinct skills the agent installed or changed"
118621
+ },
118622
+ agentsSpawned: {
118623
+ type: [
118624
+ "integer",
118625
+ "null"
118626
+ ],
118627
+ description: "Tracked agents whose adf_parent_did names this agent; null when no fleet scan has run"
118628
+ },
118629
+ messages: {
118630
+ type: "integer",
118631
+ description: "Loop messages ever written, every loop current and past (adf_loop's shared AUTOINCREMENT high-water mark; compaction, clears and loop deletion do not lower it)"
118632
+ },
118633
+ ageDays: {
118634
+ type: "number",
118635
+ description: "Days since adf_created_at"
118636
+ }
118637
+ }
118638
+ },
118639
+ AgentActivity: {
118640
+ type: "object",
118641
+ required: [
118642
+ "filePath",
118643
+ "computedAt",
118644
+ "live",
118645
+ "upcoming",
118646
+ "daily",
118647
+ "contents"
118648
+ ],
118649
+ properties: {
118650
+ filePath: {
118651
+ type: "string"
118652
+ },
118653
+ computedAt: {
118654
+ type: "number",
118655
+ description: "When this read was computed (ms epoch)"
118656
+ },
118657
+ live: {
118658
+ type: "boolean",
118659
+ description: "True when read from the workspace of a loaded agent"
118660
+ },
118661
+ upcoming: {
118662
+ type: "array",
118663
+ maxItems: 3,
118664
+ description: "Next non-expired timers, soonest first",
118665
+ items: {
118666
+ $ref: "#/components/schemas/AgentUpcomingWake"
118667
+ }
118668
+ },
118669
+ daily: {
118670
+ type: "array",
118671
+ minItems: 14,
118672
+ maxItems: 14,
118673
+ description: "14 local days, oldest first; the last is today",
118674
+ items: {
118675
+ $ref: "#/components/schemas/AgentActivityDay"
118676
+ }
118677
+ },
118678
+ contents: {
118679
+ type: "object",
118680
+ description: "What the file holds, in three groups. Tokens are approximate (bytes / 4, rounded) and given for mind and skills only: table data is rarely read into context.",
118681
+ required: [
118682
+ "mind",
118683
+ "skills",
118684
+ "tables"
118685
+ ],
118686
+ properties: {
118687
+ mind: {
118688
+ type: "object",
118689
+ description: "Files under mind/",
118690
+ required: [
118691
+ "files",
118692
+ "tokens",
118693
+ "updatedThisWeek",
118694
+ "strata"
118695
+ ],
118696
+ properties: {
118697
+ files: {
118698
+ type: "integer"
118699
+ },
118700
+ tokens: {
118701
+ type: "integer",
118702
+ description: "SUM(size) / 4, rounded, the seeded mind/log.md header taken off that file"
118703
+ },
118704
+ updatedThisWeek: {
118705
+ type: "integer",
118706
+ description: "mind/ files whose updated_at is within the last 7 days"
118707
+ },
118708
+ strata: {
118709
+ type: "object",
118710
+ description: "Current mind/ tokens (size / 4, rounded, the seeded mind/log.md header taken off that file) split by each file's updated_at. A boundary belongs to the younger band.",
118711
+ required: [
118712
+ "older",
118713
+ "quarter",
118714
+ "month",
118715
+ "week"
118716
+ ],
118717
+ properties: {
118718
+ older: {
118719
+ type: "integer",
118720
+ description: "Last updated more than 90 days ago"
118721
+ },
118722
+ quarter: {
118723
+ type: "integer",
118724
+ description: "Last updated more than 30 and at most 90 days ago"
118725
+ },
118726
+ month: {
118727
+ type: "integer",
118728
+ description: "Last updated more than 7 and at most 30 days ago"
118729
+ },
118730
+ week: {
118731
+ type: "integer",
118732
+ description: "Last updated at most 7 days ago"
118733
+ }
118734
+ }
118735
+ }
118736
+ }
118737
+ },
118738
+ skills: {
118739
+ type: "object",
118740
+ description: "Skills the agent installed or changed (the Experience stat rule)",
118741
+ required: [
118742
+ "count",
118743
+ "tokens"
118744
+ ],
118745
+ properties: {
118746
+ count: {
118747
+ type: "integer"
118748
+ },
118749
+ tokens: {
118750
+ type: "integer",
118751
+ description: "Size of every file under those skills' directories / 4; registry entries without files add 0"
118752
+ }
118753
+ }
118754
+ },
118755
+ tables: {
118756
+ type: "object",
118757
+ description: "local_* tables (at most 50 read)",
118758
+ required: [
118759
+ "count",
118760
+ "rows"
118761
+ ],
118762
+ properties: {
118763
+ count: {
118764
+ type: "integer",
118765
+ maximum: 50
118766
+ },
118767
+ rows: {
118768
+ type: "integer",
118769
+ description: "Total rows"
118770
+ }
118771
+ }
118772
+ }
118773
+ }
118774
+ }
118775
+ }
118776
+ },
118777
+ AgentUpcomingWake: {
118778
+ type: "object",
118779
+ description: "One timer due soon, as raw pieces. `system` timers carry `lambda` and `input`; `agent` timers carry `loop` and `prompt`. Both texts are the timer payload as stored, cut to 1000 characters (ending in an ellipsis).",
118780
+ required: [
118781
+ "id",
118782
+ "at",
118783
+ "scope"
118784
+ ],
118785
+ properties: {
118786
+ id: {
118787
+ type: "integer"
118788
+ },
118789
+ at: {
118790
+ type: "number",
118791
+ description: "next_wake_at (ms epoch)"
118792
+ },
118793
+ scope: {
118794
+ type: "string",
118795
+ enum: [
118796
+ "system",
118797
+ "agent"
118798
+ ],
118799
+ description: "`system` runs a lambda; `agent` wakes a loop"
118800
+ },
118801
+ lambda: {
118802
+ type: "string",
118803
+ description: "system: lambda reference, e.g. `lib/sync.ts:run`"
118804
+ },
118805
+ input: {
118806
+ type: "string",
118807
+ maxLength: 1e3,
118808
+ description: "system: payload passed to the lambda"
118809
+ },
118810
+ loop: {
118811
+ type: "string",
118812
+ description: "agent: loop the wake dispatches to (`main` when the timer names none)"
118813
+ },
118814
+ prompt: {
118815
+ type: "string",
118816
+ maxLength: 1e3,
118817
+ description: "agent: payload the loop wakes with"
118818
+ }
118819
+ }
118820
+ },
118821
+ AgentActivityDay: {
118822
+ type: "object",
118823
+ required: [
118824
+ "date",
118825
+ "messages"
118826
+ ],
118827
+ properties: {
118828
+ date: {
118829
+ type: "string",
118830
+ description: "Local YYYY-MM-DD"
118831
+ },
118832
+ messages: {
118833
+ type: "integer",
118834
+ description: "adf_loop rows of every role created that day, plus the day's estimated share of compacted rows archived in adf_audit"
118835
+ },
118836
+ estimated: {
118837
+ type: "boolean",
118838
+ description: "Some of the day's messages are compacted history whose timing is estimated (spread evenly over a span ending at the archive's creation)"
118839
+ },
118840
+ costUsd: {
118841
+ type: "number",
118842
+ description: "USD of the day's priced adf_loop rows (cost_usd in the row's usage record); absent when none had a price"
118843
+ },
118844
+ costPartial: {
118845
+ type: "boolean",
118846
+ description: "Some calls that day had no recorded price, or were compacted (their cost is not recorded); costUsd is a lower bound"
118847
+ }
118848
+ }
117811
118849
  }
117812
118850
  }
117813
118851
  }
@@ -117837,7 +118875,7 @@ import { createHash as createHash6 } from "crypto";
117837
118875
  import { dirname as dirname16, join as join35, resolve as resolve12 } from "path";
117838
118876
 
117839
118877
  // src/main/crypto/mnemonic-identity.ts
117840
- import { createHmac, createPrivateKey as createPrivateKey4, createPublicKey as createPublicKey4 } from "crypto";
118878
+ import { createHmac, createPrivateKey as createPrivateKey5, createPublicKey as createPublicKey5 } from "crypto";
117841
118879
 
117842
118880
  // node_modules/@noble/hashes/hmac.js
117843
118881
  var _HMAC = class {
@@ -118073,16 +119111,16 @@ function convertRadix2(data, from, to, padding2) {
118073
119111
  return padding2 && pos > 0 && res.push(carry >>> 0), res;
118074
119112
  }
118075
119113
  // @__NO_SIDE_EFFECTS__
118076
- function radix(num) {
118077
- anumber3(num);
119114
+ function radix(num2) {
119115
+ anumber3(num2);
118078
119116
  let _256 = 2 ** 8;
118079
119117
  return {
118080
119118
  encode: (bytes) => {
118081
119119
  if (!isBytes3(bytes))
118082
119120
  throw new TypeError("radix.encode input should be Uint8Array");
118083
- return convertRadix(Array.from(bytes), _256, num);
119121
+ return convertRadix(Array.from(bytes), _256, num2);
118084
119122
  },
118085
- decode: (digits) => (anumArr("radix.decode", digits), Uint8Array.from(convertRadix(digits, num, _256)))
119123
+ decode: (digits) => (anumArr("radix.decode", digits), Uint8Array.from(convertRadix(digits, num2, _256)))
118086
119124
  };
118087
119125
  }
118088
119126
  // @__NO_SIDE_EFFECTS__
@@ -118109,8 +119147,8 @@ function checksum(len, fn) {
118109
119147
  encode(data) {
118110
119148
  if (!isBytes3(data))
118111
119149
  throw new TypeError("checksum.encode: input should be Uint8Array");
118112
- let sum = _fn(data).slice(0, len), res = new Uint8Array(data.length + len);
118113
- return res.set(data), res.set(sum, data.length), res;
119150
+ let sum2 = _fn(data).slice(0, len), res = new Uint8Array(data.length + len);
119151
+ return res.set(data), res.set(sum2, data.length), res;
118114
119152
  },
118115
119153
  decode(data) {
118116
119154
  if (!isBytes3(data))
@@ -120276,11 +121314,11 @@ function rawEd25519SeedToPkcs8(seed32) {
120276
121314
  return Buffer.concat([header, seed32]);
120277
121315
  }
120278
121316
  function deriveOwnerIdentity(mnemonic) {
120279
- let seed = mnemonicToSeed(mnemonic), { key } = slip10DeriveEd25519(seed, OWNER_KEY_PATH), privateKeyPkcs8 = rawEd25519SeedToPkcs8(key), keyObject = createPrivateKey4({ key: privateKeyPkcs8, format: "der", type: "pkcs8" }), publicKeySpki = createPublicKey4(keyObject).export({ format: "der", type: "spki" }), did = publicKeyToDid(extractRawPublicKey(Buffer.from(publicKeySpki)));
121317
+ let seed = mnemonicToSeed(mnemonic), { key } = slip10DeriveEd25519(seed, OWNER_KEY_PATH), privateKeyPkcs8 = rawEd25519SeedToPkcs8(key), keyObject = createPrivateKey5({ key: privateKeyPkcs8, format: "der", type: "pkcs8" }), publicKeySpki = createPublicKey5(keyObject).export({ format: "der", type: "spki" }), did = publicKeyToDid(extractRawPublicKey(Buffer.from(publicKeySpki)));
120280
121318
  return { privateKeyPkcs8, publicKeySpki: Buffer.from(publicKeySpki), did };
120281
121319
  }
120282
121320
  function deriveOwnerEncryptionKey(mnemonic) {
120283
- let seed = mnemonicToSeed(mnemonic), { key } = slip10DeriveEd25519(seed, OWNER_ENCRYPTION_KEY_PATH), privateKeyPkcs8 = rawX25519PrivateToPkcs8(key), keyObject = createPrivateKey4({ key: privateKeyPkcs8, format: "der", type: "pkcs8" }), publicKeySpki = createPublicKey4(keyObject).export({ format: "der", type: "spki" });
121321
+ let seed = mnemonicToSeed(mnemonic), { key } = slip10DeriveEd25519(seed, OWNER_ENCRYPTION_KEY_PATH), privateKeyPkcs8 = rawX25519PrivateToPkcs8(key), keyObject = createPrivateKey5({ key: privateKeyPkcs8, format: "der", type: "pkcs8" }), publicKeySpki = createPublicKey5(keyObject).export({ format: "der", type: "spki" });
120284
121322
  return { privateKeyPkcs8, publicKeyRaw: extractRawX25519PublicKey(Buffer.from(publicKeySpki)) };
120285
121323
  }
120286
121324
 
@@ -120291,13 +121329,13 @@ import { join as join34 } from "path";
120291
121329
  // src/main/daemon/daemon-enc-key.ts
120292
121330
  import { chmodSync as chmodSync2, existsSync as existsSync18, mkdirSync as mkdirSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync13 } from "fs";
120293
121331
  import { join as join33 } from "path";
120294
- import { createHash as createHash5, createPrivateKey as createPrivateKey5, createPublicKey as createPublicKey5 } from "crypto";
121332
+ import { createHash as createHash5, createPrivateKey as createPrivateKey6, createPublicKey as createPublicKey6 } from "crypto";
120295
121333
  var KEY_FILENAME = "runtime-enc-key";
120296
121334
  function daemonEncKeyLabel(publicKeyRaw) {
120297
121335
  return `daemon:${createHash5("sha256").update(publicKeyRaw).digest("hex").slice(0, 16)}`;
120298
121336
  }
120299
121337
  function publicFromPrivate(privateKeyPkcs8) {
120300
- let pub = createPublicKey5(createPrivateKey5({ key: privateKeyPkcs8, format: "der", type: "pkcs8" }));
121338
+ let pub = createPublicKey6(createPrivateKey6({ key: privateKeyPkcs8, format: "der", type: "pkcs8" }));
120301
121339
  return extractRawX25519PublicKey(pub.export({ format: "der", type: "spki" }));
120302
121340
  }
120303
121341
  function ensureDaemonEncKey(settingsDir) {
@@ -120723,6 +121761,11 @@ var OWNER_SEED_DERIVED_KEY = "ownerDidSeedDerived", OwnerIdentityService = class
120723
121761
  * a stripped-identity file is untrusted (anyone can strip and reshare), and
120724
121762
  * stamping our owner DID into it before the user accepts review would make
120725
121763
  * rejection meaningless. Minting happens on review-accept instead.
121764
+ *
121765
+ * adoptKeys: when keys are minted, store this pair instead of a fresh one
121766
+ * (an identity draft from the create screen). Everything else is the same
121767
+ * as a fresh mint: envelopes, sealing, stamps, attestations. Ignored when
121768
+ * the file already has signing keys, so it can never replace an identity.
120726
121769
  */
120727
121770
  ensureWorkspaceIdentity(workspace, opts = {}) {
120728
121771
  if (workspace.isPasswordProtected())
@@ -120739,7 +121782,7 @@ var OWNER_SEED_DERIVED_KEY = "ownerDidSeedDerived", OwnerIdentityService = class
120739
121782
  runtimePrivateKey: this.getRuntimeSigningKey()
120740
121783
  });
120741
121784
  }, signingRow = workspace.getIdentityRow("crypto:signing:private_key");
120742
- signingRow === null ? (workspace.generateIdentityKeys(null), keysGenerated = !0, stampAndAttest()) : signingRow.encryption_algo === "plain" && workspace.getDid() && !workspace.getMeta("adf_owner_did") && this.getOwnerSigningKey() && stampAndAttest();
121785
+ signingRow === null ? (workspace.generateIdentityKeys(null, opts.adoptKeys), keysGenerated = !0, stampAndAttest()) : signingRow.encryption_algo === "plain" && workspace.getDid() && !workspace.getMeta("adf_owner_did") && this.getOwnerSigningKey() && stampAndAttest();
120743
121786
  let sealed = workspace.sealPlainRowsIntoEnvelopes();
120744
121787
  return { keysGenerated, sealed };
120745
121788
  }
@@ -122174,8 +123217,1323 @@ function fail(reply, err) {
122174
123217
  return err instanceof RuntimeLoopError ? reply.code(err.statusCode).send({ ...err.details ?? {}, error: err.message, code: err.code ?? (err.statusCode === 404 ? "not_found" : err.statusCode === 409 ? "conflict" : err.statusCode === 502 ? "upstream_error" : "bad_request") }) : reply.code(500).send({ error: err instanceof Error ? err.message : String(err) });
122175
123218
  }
122176
123219
 
123220
+ // src/main/services/agent-vitals.ts
123221
+ import { promises as fsp4, statSync as statSync10 } from "fs";
123222
+
123223
+ // src/shared/types/agent-vitals.types.ts
123224
+ var MEMORY_STRATA_DAYS = { week: 7, month: 30, quarter: 90 };
123225
+
123226
+ // src/shared/utils/agent-stats.ts
123227
+ var EXPERIENCE_CURVE = { scale: 5, exponent: 2.25 }, POWER_LEVEL_MAX = 20, POWER_HIGH_OPEN_SHARE = 0.6;
123228
+ function powerLevel(points, max) {
123229
+ return 1 + Math.round((POWER_LEVEL_MAX - 1) * powerFill(points, max));
123230
+ }
123231
+ function powerFill(points, max) {
123232
+ return !(max > 0) || !Number.isFinite(points) ? 0 : Math.min(1, Math.max(0, points / max));
123233
+ }
123234
+ function xpForLevel(level, curve) {
123235
+ return level > 1 ? curve.scale * (level - 1) ** curve.exponent : 0;
123236
+ }
123237
+ function levelForXp(xp, curve) {
123238
+ if (!(xp > 0) || !Number.isFinite(xp)) return 1;
123239
+ let level = 1 + Math.floor((xp / curve.scale) ** (1 / curve.exponent));
123240
+ for (; xpForLevel(level + 1, curve) <= xp; ) level++;
123241
+ for (; level > 1 && xpForLevel(level, curve) > xp; ) level--;
123242
+ return level;
123243
+ }
123244
+ function levelPosition(xp, curve) {
123245
+ let x = Number.isFinite(xp) && xp > 0 ? xp : 0, level = levelForXp(x, curve), levelStart = xpForLevel(level, curve), nextLevelAt = xpForLevel(level + 1, curve), progress = Math.min(1, Math.max(0, (x - levelStart) / (nextLevelAt - levelStart)));
123246
+ return { level, progress, levelStart, nextLevelAt };
123247
+ }
123248
+ var ACCESS_TOOL_POINTS = {
123249
+ fs_read: { points: 0.25, label: "Reads its own files" },
123250
+ fs_write: { points: 0.25, label: "Writes its own files" },
123251
+ fs_delete: { points: 0.25, label: "Deletes its own files" },
123252
+ sys_code: { points: 0.5, label: "Runs code in the sandbox" },
123253
+ sys_lambda: { points: 0.25, label: "Runs stored lambdas" },
123254
+ sys_fetch: { points: 0.5, label: "Fetches URLs over HTTP" },
123255
+ db_execute: { points: 0.25, label: "Writes to its database tables" },
123256
+ adf_shell: { points: 0.5, label: "Runs shell commands in its workspace" },
123257
+ ws_connect: { points: 0.5, label: "Opens WebSocket connections" },
123258
+ stream_bind: { points: 0.5, label: "Binds network streams" },
123259
+ fs_transfer: { points: 0.5, label: "Transfers files to other agents" },
123260
+ mcp_install: { points: 1, label: "Installs MCP servers" },
123261
+ compute_exec: { points: 1, label: "Runs commands on compute targets" }
123262
+ }, ACCESS_POINTS = {
123263
+ mcpServer: 0.5,
123264
+ mcpCredentials: 0.25,
123265
+ /** Servers that count: the heaviest (server + credentials) six; open first on a tie. */
123266
+ mcpServerCap: 6,
123267
+ npmPackage: 0.25,
123268
+ npmPackageCap: 1,
123269
+ codeNetwork: 1,
123270
+ computeEnabled: 0.5,
123271
+ hostAccess: 2,
123272
+ computeHostTarget: 1.5,
123273
+ credential: 0.25,
123274
+ credentialCap: 1.5,
123275
+ privateKeyPlain: 1,
123276
+ privateKeySealed: 0.5
123277
+ }, VISIBILITY_POINTS = {
123278
+ off: 0,
123279
+ directory: 0.5,
123280
+ localhost: 1,
123281
+ lan: 2,
123282
+ public: 4
123283
+ }, REACH_POINTS = {
123284
+ sendProactive: 1,
123285
+ sendRespondOnly: 0.5,
123286
+ publicPage: 3,
123287
+ apiRoute: 0.5,
123288
+ apiRouteCap: 3,
123289
+ sharedFiles: 1,
123290
+ adapter: 2.5,
123291
+ adapterRestrictedDm: 1,
123292
+ /** Adapters that count: the heaviest three. */
123293
+ adapterCap: 3,
123294
+ wsConnection: 0.5,
123295
+ wsConnectionCap: 2,
123296
+ /** Mitigation: messages must be signed. */
123297
+ signedOnly: -0.5
123298
+ }, AUTONOMY_POINTS = {
123299
+ autonomous: 3,
123300
+ autostart: 2,
123301
+ timers: 1,
123302
+ timerFast: 2,
123303
+ // fastest interval <= 5 min
123304
+ timerHourly: 1,
123305
+ // fastest interval <= 1 h
123306
+ trigger: 0.25,
123307
+ triggerCap: 1.5,
123308
+ proactive: 0.5,
123309
+ createAgents: 3,
123310
+ updateConfig: 1,
123311
+ sideLoop: 0.5,
123312
+ sideLoopCap: 1.5,
123313
+ /** Mitigation per enabled restricted tool. */
123314
+ restrictedTool: -0.25,
123315
+ restrictedToolCap: -1
123316
+ }, sum = (ns) => ns.reduce((s, n) => s + n, 0), POWER_MAX = {
123317
+ access: sum(Object.values(ACCESS_TOOL_POINTS).map((t) => t.points)) + ACCESS_POINTS.computeHostTarget + ACCESS_POINTS.computeEnabled + ACCESS_POINTS.hostAccess + ACCESS_POINTS.codeNetwork + ACCESS_POINTS.npmPackageCap + ACCESS_POINTS.mcpServerCap * (ACCESS_POINTS.mcpServer + ACCESS_POINTS.mcpCredentials) + ACCESS_POINTS.credentialCap + Math.max(ACCESS_POINTS.privateKeyPlain, ACCESS_POINTS.privateKeySealed),
123318
+ reach: Math.max(...Object.values(VISIBILITY_POINTS)) + Math.max(REACH_POINTS.sendProactive, REACH_POINTS.sendRespondOnly) + REACH_POINTS.publicPage + REACH_POINTS.apiRouteCap + REACH_POINTS.sharedFiles + REACH_POINTS.adapterCap * Math.max(REACH_POINTS.adapter, REACH_POINTS.adapterRestrictedDm) + REACH_POINTS.wsConnectionCap,
123319
+ autonomy: AUTONOMY_POINTS.autonomous + AUTONOMY_POINTS.autostart + AUTONOMY_POINTS.timers + Math.max(AUTONOMY_POINTS.timerFast, AUTONOMY_POINTS.timerHourly) + AUTONOMY_POINTS.triggerCap + AUTONOMY_POINTS.proactive + AUTONOMY_POINTS.createAgents + AUTONOMY_POINTS.updateConfig + AUTONOMY_POINTS.sideLoopCap
123320
+ };
123321
+ function toPowerStat(factors, max) {
123322
+ let open3 = 0, gated = 0, mitigation = 0;
123323
+ for (let f of factors)
123324
+ f.points < 0 ? mitigation += f.points : f.gated ? gated += f.points : open3 += f.points;
123325
+ open3 += mitigation, open3 < 0 && (gated = Math.max(0, gated + open3), open3 = 0);
123326
+ let points = open3 + gated, rawPoints = factors.reduce((s, f) => s + f.points, 0);
123327
+ return {
123328
+ level: powerLevel(points, max),
123329
+ points,
123330
+ max,
123331
+ fill: powerFill(points, max),
123332
+ open: open3,
123333
+ gated,
123334
+ high: powerFill(open3, max) >= POWER_HIGH_OPEN_SHARE,
123335
+ rawPoints,
123336
+ factors
123337
+ };
123338
+ }
123339
+ function countedIndices(items, cap, weight, gated) {
123340
+ let order = items.map((t, i) => ({ i, w: weight(t), g: gated(t) })).sort((a, b) => b.w - a.w || Number(a.g) - Number(b.g) || a.i - b.i);
123341
+ return new Set(order.slice(0, cap).map((o) => o.i));
123342
+ }
123343
+ function toolMap(inputs) {
123344
+ let map = /* @__PURE__ */ new Map();
123345
+ for (let t of inputs.tools) t.enabled && map.set(t.name, { restricted: !!t.restricted });
123346
+ return map;
123347
+ }
123348
+ function plural(n, one, many = `${one}s`) {
123349
+ return `${n} ${n === 1 ? one : many}`;
123350
+ }
123351
+ function formatInterval(ms) {
123352
+ return ms < 6e4 ? `${Math.round(ms / 1e3)}s` : ms < 36e5 ? `${Math.round(ms / 6e4)} min` : `${Math.round(ms / 36e5)} h`;
123353
+ }
123354
+ function scoreAccess(inputs) {
123355
+ let tools = toolMap(inputs), factors = [];
123356
+ for (let [name21, def] of Object.entries(ACCESS_TOOL_POINTS)) {
123357
+ let t = tools.get(name21);
123358
+ t && factors.push({
123359
+ id: `tool:${name21}`,
123360
+ label: t.restricted ? `${def.label} (needs approval)` : def.label,
123361
+ points: def.points,
123362
+ gated: t.restricted,
123363
+ configPath: `tools.${name21}`
123364
+ });
123365
+ }
123366
+ let exec2 = tools.get("compute_exec");
123367
+ exec2 && inputs.compute.allowedTargets.includes("host") && factors.push({
123368
+ id: "compute:host_target",
123369
+ label: "Runs commands on the host machine",
123370
+ points: ACCESS_POINTS.computeHostTarget,
123371
+ gated: exec2.restricted,
123372
+ configPath: "compute.allowed_targets"
123373
+ }), inputs.compute.enabled && factors.push({
123374
+ id: "compute:enabled",
123375
+ label: "Has its own compute container",
123376
+ points: ACCESS_POINTS.computeEnabled,
123377
+ gated: !1,
123378
+ configPath: "compute.enabled"
123379
+ }), inputs.compute.hostAccess && factors.push({
123380
+ id: "compute:host_access",
123381
+ label: "May run MCP servers on the host machine",
123382
+ points: ACCESS_POINTS.hostAccess,
123383
+ gated: !1,
123384
+ configPath: "compute.host_access"
123385
+ }), inputs.codeNetwork && factors.push({
123386
+ id: "code:network",
123387
+ label: "Sandbox code has direct network access",
123388
+ points: ACCESS_POINTS.codeNetwork,
123389
+ gated: !1,
123390
+ configPath: "code_execution.network"
123391
+ }), inputs.npmPackageCount > 0 && factors.push({
123392
+ id: "code:packages",
123393
+ label: `${plural(inputs.npmPackageCount, "npm package")} installed`,
123394
+ points: Math.min(ACCESS_POINTS.npmPackageCap, inputs.npmPackageCount * ACCESS_POINTS.npmPackage),
123395
+ gated: !1,
123396
+ configPath: "code_execution.packages"
123397
+ });
123398
+ let countedMcp = countedIndices(
123399
+ inputs.mcpServers,
123400
+ ACCESS_POINTS.mcpServerCap,
123401
+ (s) => ACCESS_POINTS.mcpServer + (s.hasCredentials ? ACCESS_POINTS.mcpCredentials : 0),
123402
+ (s) => s.restricted
123403
+ );
123404
+ inputs.mcpServers.forEach((s, i) => {
123405
+ let counts = countedMcp.has(i);
123406
+ factors.push({
123407
+ id: `mcp:${s.name}`,
123408
+ label: s.restricted ? `MCP server ${s.name} (tools need approval)` : `MCP server ${s.name}`,
123409
+ points: counts ? ACCESS_POINTS.mcpServer : 0,
123410
+ gated: s.restricted,
123411
+ configPath: "mcp.servers"
123412
+ }), s.hasCredentials && factors.push({
123413
+ id: `mcp:${s.name}:credentials`,
123414
+ label: `MCP server ${s.name} holds credentials`,
123415
+ points: counts ? ACCESS_POINTS.mcpCredentials : 0,
123416
+ gated: s.restricted,
123417
+ configPath: "mcp.servers"
123418
+ });
123419
+ });
123420
+ let credBudget = ACCESS_POINTS.credentialCap, credPoints = (n) => {
123421
+ let p = Math.min(credBudget, n * ACCESS_POINTS.credential);
123422
+ return credBudget -= p, p;
123423
+ };
123424
+ return inputs.credentials.plain > 0 && factors.push({
123425
+ id: "identity:credentials_plain",
123426
+ label: `${plural(inputs.credentials.plain, "stored credential")}, unsealed`,
123427
+ points: credPoints(inputs.credentials.plain),
123428
+ gated: !1,
123429
+ configPath: "adf_identity"
123430
+ }), inputs.credentials.sealed > 0 && factors.push({
123431
+ id: "identity:credentials_sealed",
123432
+ label: `${plural(inputs.credentials.sealed, "stored credential")}, sealed`,
123433
+ points: credPoints(inputs.credentials.sealed),
123434
+ gated: !0,
123435
+ configPath: "adf_identity"
123436
+ }), inputs.privateKey === "plain" ? factors.push({
123437
+ id: "identity:private_key",
123438
+ label: "Signing key stored unsealed",
123439
+ points: ACCESS_POINTS.privateKeyPlain,
123440
+ gated: !1,
123441
+ configPath: "adf_identity.crypto:signing:private_key"
123442
+ }) : inputs.privateKey === "sealed" && factors.push({
123443
+ id: "identity:private_key",
123444
+ label: "Signing key (sealed)",
123445
+ points: ACCESS_POINTS.privateKeySealed,
123446
+ gated: !0,
123447
+ configPath: "adf_identity.crypto:signing:private_key"
123448
+ }), toPowerStat(factors, POWER_MAX.access);
123449
+ }
123450
+ function scoreReach(inputs) {
123451
+ let tools = toolMap(inputs), factors = [], m = inputs.messaging, inbound = m.receive ? VISIBILITY_POINTS[m.visibility] ?? 0 : 0;
123452
+ inbound > 0 && (factors.push({
123453
+ id: "messaging:visibility",
123454
+ label: `Receives messages from ${m.visibility === "directory" ? "agents in its directory" : `the ${m.visibility} tier`}`,
123455
+ points: inbound,
123456
+ gated: !1,
123457
+ configPath: "messaging.visibility"
123458
+ }), m.allowListCount > 0 && factors.push({
123459
+ id: "messaging:allow_list",
123460
+ label: `Inbox limited to ${plural(m.allowListCount, "allowed sender")}`,
123461
+ points: -inbound / 2,
123462
+ gated: !1,
123463
+ configPath: "messaging.allow_list"
123464
+ }), (!inputs.security.allowUnsigned || inputs.security.requireSignature) && factors.push({
123465
+ id: "security:signed_only",
123466
+ label: "Only accepts signed messages",
123467
+ points: REACH_POINTS.signedOnly,
123468
+ gated: !1,
123469
+ configPath: inputs.security.requireSignature ? "security.require_signature" : "security.allow_unsigned"
123470
+ }));
123471
+ let send = tools.get("msg_send");
123472
+ if (send && m.mode !== "listen_only") {
123473
+ let proactive = m.mode === "proactive";
123474
+ factors.push({
123475
+ id: "messaging:send",
123476
+ label: proactive ? "Sends messages on its own" : "Replies to messages it receives",
123477
+ points: proactive ? REACH_POINTS.sendProactive : REACH_POINTS.sendRespondOnly,
123478
+ gated: send.restricted,
123479
+ configPath: "messaging.mode"
123480
+ });
123481
+ }
123482
+ inputs.serving.publicEnabled && factors.push({
123483
+ id: "serving:public",
123484
+ label: "Serves a public web page",
123485
+ points: REACH_POINTS.publicPage,
123486
+ gated: !1,
123487
+ configPath: "serving.public.enabled"
123488
+ }), inputs.serving.apiRouteCount > 0 && factors.push({
123489
+ id: "serving:api",
123490
+ label: `${plural(inputs.serving.apiRouteCount, "HTTP API route")}`,
123491
+ points: Math.min(REACH_POINTS.apiRouteCap, inputs.serving.apiRouteCount * REACH_POINTS.apiRoute),
123492
+ gated: !1,
123493
+ configPath: "serving.api"
123494
+ }), inputs.serving.sharedEnabled && factors.push({
123495
+ id: "serving:shared",
123496
+ label: "Shares files with peers",
123497
+ points: REACH_POINTS.sharedFiles,
123498
+ gated: !1,
123499
+ configPath: "serving.shared.enabled"
123500
+ });
123501
+ let adapterPoints = (a) => a.restrictedDm ? REACH_POINTS.adapterRestrictedDm : REACH_POINTS.adapter, countedAdapters = countedIndices(inputs.adapters, REACH_POINTS.adapterCap, adapterPoints, () => !1);
123502
+ return inputs.adapters.forEach((a, i) => {
123503
+ factors.push({
123504
+ id: `adapter:${a.type}`,
123505
+ label: a.restrictedDm ? `${a.type} adapter (DMs restricted)` : `${a.type} adapter`,
123506
+ points: countedAdapters.has(i) ? adapterPoints(a) : 0,
123507
+ gated: !1,
123508
+ configPath: `adapters.${a.type}`
123509
+ });
123510
+ }), inputs.wsConnectionCount > 0 && factors.push({
123511
+ id: "ws_connections",
123512
+ label: `${plural(inputs.wsConnectionCount, "standing WebSocket connection")}`,
123513
+ points: Math.min(REACH_POINTS.wsConnectionCap, inputs.wsConnectionCount * REACH_POINTS.wsConnection),
123514
+ gated: !1,
123515
+ configPath: "ws_connections"
123516
+ }), toPowerStat(factors, POWER_MAX.reach);
123517
+ }
123518
+ function scoreAutonomy(inputs) {
123519
+ let tools = toolMap(inputs), factors = [];
123520
+ if (inputs.autonomous && factors.push({ id: "autonomous", label: "Acts without waiting for a human", points: AUTONOMY_POINTS.autonomous, gated: !1, configPath: "autonomous" }), inputs.autostart && factors.push({ id: "autostart", label: "Starts when the runtime starts", points: AUTONOMY_POINTS.autostart, gated: !1, configPath: "autostart" }), inputs.timers.active > 0) {
123521
+ factors.push({ id: "timers", label: `${plural(inputs.timers.active, "active timer")}`, points: AUTONOMY_POINTS.timers, gated: !1, configPath: "adf_timers" });
123522
+ let fastest = inputs.timers.fastestIntervalMs;
123523
+ fastest !== void 0 && fastest <= 36e5 && factors.push({
123524
+ id: "timers:fastest",
123525
+ label: `Wakes every ${formatInterval(fastest)}`,
123526
+ points: fastest <= 3e5 ? AUTONOMY_POINTS.timerFast : AUTONOMY_POINTS.timerHourly,
123527
+ gated: !1,
123528
+ configPath: "adf_timers"
123529
+ });
123530
+ }
123531
+ let triggerTargets = inputs.triggers.reduce((s, t) => s + t.targets, 0);
123532
+ triggerTargets > 0 && factors.push({
123533
+ id: "triggers",
123534
+ label: `Wakes on ${plural(inputs.triggers.length, "event type")} (${inputs.triggers.map((t) => t.type).join(", ")})`,
123535
+ points: Math.min(AUTONOMY_POINTS.triggerCap, triggerTargets * AUTONOMY_POINTS.trigger),
123536
+ gated: !1,
123537
+ configPath: "triggers"
123538
+ });
123539
+ let send = tools.get("msg_send");
123540
+ send && inputs.messaging.mode === "proactive" && factors.push({ id: "messaging:proactive", label: "Starts conversations", points: AUTONOMY_POINTS.proactive, gated: send.restricted, configPath: "messaging.mode" });
123541
+ let spawn5 = tools.get("sys_create_adf");
123542
+ spawn5 && factors.push({ id: "tool:sys_create_adf", label: spawn5.restricted ? "Creates new agents (needs approval)" : "Creates new agents", points: AUTONOMY_POINTS.createAgents, gated: spawn5.restricted, configPath: "tools.sys_create_adf" });
123543
+ let update = tools.get("sys_update_config");
123544
+ update && factors.push({ id: "tool:sys_update_config", label: update.restricted ? "Changes its own config (needs approval)" : "Changes its own config", points: AUTONOMY_POINTS.updateConfig, gated: update.restricted, configPath: "tools.sys_update_config" }), inputs.sideLoopCount > 0 && factors.push({
123545
+ id: "loops",
123546
+ label: `${plural(inputs.sideLoopCount, "inner loop")}`,
123547
+ points: Math.min(AUTONOMY_POINTS.sideLoopCap, inputs.sideLoopCount * AUTONOMY_POINTS.sideLoop),
123548
+ gated: !1,
123549
+ configPath: "loops"
123550
+ });
123551
+ let restricted = [...tools.values()].filter((t) => t.restricted).length;
123552
+ return restricted > 0 && factors.push({
123553
+ id: "tools:restricted",
123554
+ label: `${plural(restricted, "tool")} need${restricted === 1 ? "s" : ""} approval`,
123555
+ points: Math.max(AUTONOMY_POINTS.restrictedToolCap, restricted * AUTONOMY_POINTS.restrictedTool),
123556
+ gated: !1,
123557
+ configPath: "tools[].restricted"
123558
+ }), toPowerStat(factors, POWER_MAX.autonomy);
123559
+ }
123560
+ var EXPERIENCE_WEIGHTS = {
123561
+ /** Per context worked: one compaction, or a loop's current fill as a share of its compaction threshold. */
123562
+ contextsWorked: 10,
123563
+ /**
123564
+ * Multiplied by sqrt(approximate tokens in `mind/` files), so 10x memory is
123565
+ * about 3.2x the XP and a bulk dump cannot buy levels (1M tokens alone is
123566
+ * Lv 17). 2.6 keeps the first-session anchor (300 tokens) at Lv 3 with
123567
+ * margin; skills, rows and children carry the heavy anchor instead.
123568
+ */
123569
+ memoryTokensSqrt: 2.6,
123570
+ /** Per skill installed or changed. */
123571
+ skills: 80,
123572
+ /** Per `local_*` table. */
123573
+ localTables: 10,
123574
+ /** Multiplied by sqrt(total local rows). */
123575
+ localRowsSqrt: 7,
123576
+ /** Per file written or changed after creation. */
123577
+ filesWritten: 2,
123578
+ /** Per child agent. */
123579
+ agentsSpawned: 75,
123580
+ /** Per loop message ever written (all loops, current and past). Small: volume alone must not level an agent. */
123581
+ messages: 0.01,
123582
+ /** Per day of age, scaled by activity = min(1, contextsWorked / ageActivityContexts) so an idle file does not level up by waiting. */
123583
+ ageDays: 0.5,
123584
+ ageActivityContexts: 10
123585
+ }, EXPERIENCE_LABELS = {
123586
+ contextsWorked: "Contexts of work",
123587
+ filesWritten: "Files written",
123588
+ memoryTokens: "Memory tokens",
123589
+ skills: "Skills",
123590
+ localTables: "Database tables",
123591
+ localRows: "Database rows",
123592
+ agentsSpawned: "Agents created",
123593
+ messages: "Messages",
123594
+ ageDays: "Days active"
123595
+ };
123596
+ function experienceSignals(inputs) {
123597
+ let w = EXPERIENCE_WEIGHTS, n = (v) => typeof v == "number" && Number.isFinite(v) && v > 0 ? v : 0, activity = Math.min(1, n(inputs.contextsWorked) / w.ageActivityContexts), signals = [
123598
+ { id: "contextsWorked", label: EXPERIENCE_LABELS.contextsWorked, value: n(inputs.contextsWorked), xp: n(inputs.contextsWorked) * w.contextsWorked },
123599
+ { id: "memoryTokens", label: EXPERIENCE_LABELS.memoryTokens, value: n(inputs.memoryTokens), xp: Math.sqrt(n(inputs.memoryTokens)) * w.memoryTokensSqrt },
123600
+ { id: "skills", label: EXPERIENCE_LABELS.skills, value: n(inputs.skills), xp: n(inputs.skills) * w.skills },
123601
+ { id: "localTables", label: EXPERIENCE_LABELS.localTables, value: n(inputs.localTables), xp: n(inputs.localTables) * w.localTables },
123602
+ { id: "localRows", label: EXPERIENCE_LABELS.localRows, value: n(inputs.localRows), xp: Math.sqrt(n(inputs.localRows)) * w.localRowsSqrt },
123603
+ { id: "filesWritten", label: EXPERIENCE_LABELS.filesWritten, value: n(inputs.filesWritten), xp: n(inputs.filesWritten) * w.filesWritten }
123604
+ ];
123605
+ return inputs.agentsSpawned !== null && signals.push({ id: "agentsSpawned", label: EXPERIENCE_LABELS.agentsSpawned, value: n(inputs.agentsSpawned), xp: n(inputs.agentsSpawned) * w.agentsSpawned }), signals.push(
123606
+ { id: "messages", label: EXPERIENCE_LABELS.messages, value: n(inputs.messages), xp: n(inputs.messages) * w.messages },
123607
+ { id: "ageDays", label: EXPERIENCE_LABELS.ageDays, value: Math.floor(n(inputs.ageDays)), xp: n(inputs.ageDays) * w.ageDays * activity }
123608
+ ), signals;
123609
+ }
123610
+ function ceilCount(n) {
123611
+ if (n < 1e3) return String(n);
123612
+ let k = Math.ceil(n / 100) / 10;
123613
+ return `${k % 1 === 0 ? k.toFixed(0) : k.toFixed(1)}k`;
123614
+ }
123615
+ function scoreExperience(inputs) {
123616
+ let breakdown = experienceSignals(inputs), score = breakdown.reduce((s, b) => s + b.xp, 0), pos = levelPosition(score, EXPERIENCE_CURVE), xp = Math.max(0, pos.nextLevelAt - score), w = EXPERIENCE_WEIGHTS, contexts = Math.ceil(xp / w.contextsWorked), memoryNow = typeof inputs.memoryTokens == "number" && Number.isFinite(inputs.memoryTokens) && inputs.memoryTokens > 0 ? inputs.memoryTokens : 0, memoryXpNow = Math.sqrt(memoryNow) * w.memoryTokensSqrt, memoryTokens = Math.max(0, Math.ceil(((memoryXpNow + xp) / w.memoryTokensSqrt) ** 2 - memoryNow)), skills = Math.ceil(xp / w.skills);
123617
+ return {
123618
+ level: pos.level,
123619
+ progress: pos.progress,
123620
+ score,
123621
+ levelStart: pos.levelStart,
123622
+ nextLevelAt: pos.nextLevelAt,
123623
+ breakdown,
123624
+ nextLevel: {
123625
+ xp,
123626
+ contexts,
123627
+ memoryTokens,
123628
+ skills,
123629
+ hint: `Lv ${pos.level + 1} needs ${Math.ceil(xp)} more XP: about ${plural(contexts, "context")} of work, ${ceilCount(memoryTokens)} memory tokens or ${plural(skills, "skill")}`
123630
+ }
123631
+ };
123632
+ }
123633
+ function scheduleIntervalMs(schedule) {
123634
+ if (schedule) {
123635
+ if (schedule.mode === "interval") return schedule.every_ms > 0 ? schedule.every_ms : void 0;
123636
+ if (schedule.mode === "cron") {
123637
+ let minute = schedule.cron.trim().split(/\s+/)[0] ?? "";
123638
+ if (minute === "*") return 6e4;
123639
+ let step = /^\*\/(\d+)$/.exec(minute);
123640
+ return step ? Number(step[1]) * 6e4 : void 0;
123641
+ }
123642
+ }
123643
+ }
123644
+ function powerInputsFromConfig(config, tables) {
123645
+ let triggers = [];
123646
+ for (let [type, t] of Object.entries(config.triggers ?? {})) {
123647
+ if (type === "on_chat" || !t?.enabled) continue;
123648
+ let targets = Array.isArray(t.targets) ? t.targets.length : 0;
123649
+ targets > 0 && triggers.push({ type, targets });
123650
+ }
123651
+ let mcpServers = (config.mcp?.servers ?? []).map((s) => ({
123652
+ name: s.name,
123653
+ restricted: !!s.restricted,
123654
+ hasCredentials: !!(s.oauth || s.bearer_token_env_var || s.env_keys && s.env_keys.length > 0 || s.env_schema && s.env_schema.length > 0 || s.header_env && s.header_env.length > 0 || s.credential_files && s.credential_files.length > 0)
123655
+ })), adapters = Object.entries(config.adapters ?? {}).filter(([, a]) => a?.enabled).map(([type, a]) => ({ type, restrictedDm: a.policy?.dm === "allowlist" || a.policy?.dm === "none" })), allowedTargets = [...config.compute?.allowed_targets ?? []];
123656
+ return config.compute?.target && !allowedTargets.includes(config.compute.target) && allowedTargets.push(config.compute.target), {
123657
+ tools: (config.tools ?? []).map((t) => ({ name: t.name, enabled: !!t.enabled, restricted: !!t.restricted })),
123658
+ autonomous: !!config.autonomous,
123659
+ autostart: !!config.autostart,
123660
+ messaging: {
123661
+ receive: config.messaging?.receive ?? !1,
123662
+ mode: config.messaging?.mode ?? "proactive",
123663
+ visibility: config.messaging?.visibility ?? "localhost",
123664
+ allowListCount: config.messaging?.allow_list?.length ?? 0
123665
+ },
123666
+ security: {
123667
+ allowUnsigned: config.security?.allow_unsigned ?? !0,
123668
+ requireSignature: !!config.security?.require_signature
123669
+ },
123670
+ serving: {
123671
+ publicEnabled: !!config.serving?.public?.enabled,
123672
+ apiRouteCount: config.serving?.api?.length ?? 0,
123673
+ sharedEnabled: !!config.serving?.shared?.enabled
123674
+ },
123675
+ adapters,
123676
+ wsConnectionCount: (config.ws_connections ?? []).filter((c) => c?.enabled).length,
123677
+ mcpServers,
123678
+ npmPackageCount: config.code_execution?.packages?.length ?? 0,
123679
+ codeNetwork: !!config.code_execution?.network,
123680
+ compute: {
123681
+ enabled: !!config.compute?.enabled,
123682
+ hostAccess: !!config.compute?.host_access,
123683
+ allowedTargets
123684
+ },
123685
+ triggers,
123686
+ sideLoopCount: config.loops?.length ?? 0,
123687
+ credentials: tables.credentials,
123688
+ privateKey: tables.privateKey,
123689
+ timers: tables.timers
123690
+ };
123691
+ }
123692
+ function scoreAgent(power, experience) {
123693
+ return {
123694
+ reach: scoreReach(power),
123695
+ access: scoreAccess(power),
123696
+ autonomy: scoreAutonomy(power),
123697
+ experience: scoreExperience(experience)
123698
+ };
123699
+ }
123700
+
123701
+ // src/shared/utils/agent-activity.ts
123702
+ var ACTIVITY_DAYS = 14;
123703
+ function localDayKeys(now2, days = ACTIVITY_DAYS) {
123704
+ let d = new Date(now2), out = [];
123705
+ for (let i = days - 1; i >= 0; i--)
123706
+ out.push(localDateKey(new Date(d.getFullYear(), d.getMonth(), d.getDate() - i)));
123707
+ return out;
123708
+ }
123709
+ function windowStartMs(now2, days = ACTIVITY_DAYS) {
123710
+ let d = new Date(now2);
123711
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate() - (days - 1)).getTime();
123712
+ }
123713
+
123714
+ // src/main/services/agent-daily.ts
123715
+ function rowsOf(q, sql, params) {
123716
+ try {
123717
+ return q(sql, params);
123718
+ } catch {
123719
+ return null;
123720
+ }
123721
+ }
123722
+ var num = (v) => typeof v == "number" && Number.isFinite(v) ? v : null, DAY_MS = 1440 * 60 * 1e3, LOOP_COST_RECORDED_SINCE_MS = Date.UTC(2026, 7, 27);
123723
+ function rowCost(model, tokens, createdAt) {
123724
+ if (tokens == null || tokens === "") return { kind: "none" };
123725
+ let usage = tokens;
123726
+ if (typeof tokens == "string")
123727
+ try {
123728
+ usage = JSON.parse(tokens);
123729
+ } catch {
123730
+ return { kind: "unpriced" };
123731
+ }
123732
+ if (!usage || typeof usage != "object" || Array.isArray(usage)) return { kind: "unpriced" };
123733
+ let u = usage, recorded = num(u.cost_usd);
123734
+ if (recorded !== null) return { kind: "priced", usd: recorded };
123735
+ if (createdAt >= LOOP_COST_RECORDED_SINCE_MS || typeof model != "string" || !model) return { kind: "unpriced" };
123736
+ let usd = estimateTokenCostUsd(model, {
123737
+ input: num(u.input) ?? void 0,
123738
+ output: num(u.output) ?? void 0,
123739
+ cache_read: num(u.cache_read) ?? void 0,
123740
+ cache_write: num(u.cache_write) ?? void 0
123741
+ });
123742
+ return usd === void 0 ? { kind: "unpriced" } : { kind: "priced", usd };
123743
+ }
123744
+ function maxLoopSeq(q) {
123745
+ return num(rowsOf(q, "SELECT MAX(seq) AS n FROM adf_loop")?.[0]?.n) ?? 0;
123746
+ }
123747
+ function firstSeqSince(q, since, maxSeq = maxLoopSeq(q)) {
123748
+ let probe = (seq) => {
123749
+ let r = rowsOf(q, "SELECT seq, created_at AS t FROM adf_loop WHERE seq >= ? ORDER BY seq LIMIT 1", [seq])?.[0];
123750
+ return r ? { seq: r.seq, t: num(r.t) ?? 0 } : null;
123751
+ }, lo = 0, hi = maxSeq + 1;
123752
+ for (; lo < hi; ) {
123753
+ let mid = Math.floor((lo + hi) / 2), row = probe(mid);
123754
+ !row || row.seq >= hi || row.t >= since ? hi = mid : lo = row.seq + 1;
123755
+ }
123756
+ return lo;
123757
+ }
123758
+ function startLoopDayScan(q, since) {
123759
+ let startSeq = firstSeqSince(q, since);
123760
+ return { since, startSeq, lastSeq: startSeq - 1, rows: /* @__PURE__ */ new Map() };
123761
+ }
123762
+ function addRows(scan, rows) {
123763
+ for (let r of rows) {
123764
+ let t = num(r.t) ?? 0, cost = rowCost(r.model, r.tokens, t);
123765
+ scan.rows.set(r.seq, cost.kind === "priced" ? { t, usd: cost.usd } : cost.kind === "unpriced" ? { t, unpriced: !0 } : { t });
123766
+ }
123767
+ }
123768
+ function scanLoopRows(q, scan, upTo) {
123769
+ if (upTo <= scan.lastSeq) return scan;
123770
+ let rows = rowsOf(q, "SELECT seq, created_at AS t, model, tokens FROM adf_loop WHERE seq > ? AND seq <= ?", [scan.lastSeq, upTo]) ?? rowsOf(q, "SELECT seq, created_at AS t, NULL AS model, NULL AS tokens FROM adf_loop WHERE seq > ? AND seq <= ?", [scan.lastSeq, upTo]) ?? [];
123771
+ return addRows(scan, rows), scan.lastSeq = upTo, scan;
123772
+ }
123773
+ function seqRangeRows(q, select, from, to) {
123774
+ return rowsOf(q, `SELECT ${select} FROM adf_loop INDEXED BY idx_adf_loop_loop_seq WHERE seq BETWEEN ? AND ?`, [from, to]) ?? rowsOf(q, `SELECT ${select} FROM adf_loop WHERE seq BETWEEN ? AND ?`, [from, to]);
123775
+ }
123776
+ function refreshLoopDayScan(q, since, prior) {
123777
+ let maxSeq = maxLoopSeq(q);
123778
+ if (!prior || prior.since !== since || prior.lastSeq > maxSeq) return scanLoopRows(q, startLoopDayScan(q, since), maxSeq);
123779
+ let scan = prior;
123780
+ if (scan.lastSeq >= scan.startSeq) {
123781
+ let count = num(seqRangeRows(q, "COUNT(*) AS n", scan.startSeq, scan.lastSeq)?.[0]?.n);
123782
+ if (count !== null && count !== scan.rows.size) {
123783
+ let alive = new Set((seqRangeRows(q, "seq", scan.startSeq, scan.lastSeq) ?? []).map((r) => r.seq));
123784
+ for (let seq of scan.rows.keys()) alive.has(seq) || scan.rows.delete(seq);
123785
+ let missing = [...alive].filter((seq) => !scan.rows.has(seq));
123786
+ missing.length > 0 && addRows(scan, rowsOf(q, "SELECT seq, created_at AS t, model, tokens FROM adf_loop WHERE seq IN (SELECT value FROM json_each(?))", [JSON.stringify(missing)]) ?? []);
123787
+ }
123788
+ }
123789
+ return scanLoopRows(q, scan, maxSeq);
123790
+ }
123791
+ var LOOP_AUDIT_WHERE = "(source = 'loop' OR source LIKE 'loop:%')", ARCHIVE_SPREAD_MAX_MS = DAY_MS, loopOfSource = (source) => source === "loop" ? "main" : source.slice(5) || "main";
123792
+ function firstAuditIdSince(q, at) {
123793
+ let bounds = rowsOf(q, "SELECT MIN(id) AS lo, MAX(id) AS hi FROM adf_audit")?.[0], minId = num(bounds?.lo), maxId = num(bounds?.hi);
123794
+ if (minId === null || maxId === null) return null;
123795
+ let lo = minId, hi = maxId + 1;
123796
+ for (; lo < hi; ) {
123797
+ let mid = Math.floor((lo + hi) / 2), row = rowsOf(q, "SELECT id, created_at AS t FROM adf_audit WHERE id >= ? ORDER BY id LIMIT 1", [mid])?.[0];
123798
+ !row || row.id >= hi || (num(row.t) ?? 0) >= at ? hi = mid : lo = row.id + 1;
123799
+ }
123800
+ return lo;
123801
+ }
123802
+ function readArchiveSpans(q, since) {
123803
+ let firstId = firstAuditIdSince(q, since - ARCHIVE_SPREAD_MAX_MS);
123804
+ if (firstId === null) return [];
123805
+ let cols = (seqCols) => `SELECT id, source, ${seqCols}, entry_count, created_at FROM adf_audit WHERE id >= ? AND ${LOOP_AUDIT_WHERE} ORDER BY id`, archives = rowsOf(q, cols("start_seq, end_seq"), [firstId]) ?? rowsOf(q, cols("NULL AS start_seq, NULL AS end_seq"), [firstId]) ?? [], liveAt = (sql, seq) => num(rowsOf(q, sql, [seq])?.[0]?.t), liveInRange = (loop, from, to) => num(rowsOf(q, "SELECT COUNT(*) AS n FROM adf_loop WHERE loop = ? AND seq BETWEEN ? AND ?", [loop, from, to])?.[0]?.n) ?? (loop === "main" ? num(rowsOf(q, "SELECT COUNT(*) AS n FROM adf_loop WHERE seq BETWEEN ? AND ?", [from, to])?.[0]?.n) ?? 0 : 0), spans = [], prevCreated = /* @__PURE__ */ new Map();
123806
+ return archives.forEach((a, i) => {
123807
+ let loop = loopOfSource(a.source), created = num(a.created_at) ?? 0, prev = prevCreated.get(loop);
123808
+ if (prevCreated.set(loop, created), created < since) return;
123809
+ let startSeq = num(a.start_seq), endSeq = num(a.end_seq), hasRange = startSeq !== null && endSeq !== null;
123810
+ if (hasRange && archives.slice(i + 1).some((b) => loopOfSource(b.source) === loop && num(b.start_seq) !== null && num(b.start_seq) <= startSeq && (num(b.end_seq) ?? -1 / 0) >= endSeq)) return;
123811
+ let count = num(a.entry_count) ?? 0;
123812
+ if (hasRange && (count -= liveInRange(loop, startSeq, endSeq)), count <= 0) return;
123813
+ let to = created, after = hasRange ? liveAt("SELECT created_at AS t FROM adf_loop WHERE seq > ? ORDER BY seq LIMIT 1", endSeq) : null;
123814
+ after !== null && after < to && (to = after);
123815
+ let from = to - ARCHIVE_SPREAD_MAX_MS;
123816
+ prev !== void 0 && (from = Math.max(from, prev));
123817
+ let before = hasRange ? liveAt("SELECT created_at AS t FROM adf_loop WHERE seq < ? ORDER BY seq DESC LIMIT 1", startSeq) : null;
123818
+ before !== null && (from = Math.max(from, before)), spans.push({ loop, from: Math.min(from, to), to, count });
123819
+ }), spans;
123820
+ }
123821
+ function spreadSpans(spans, dayStarts) {
123822
+ let out = new Array(dayStarts.length - 1).fill(0);
123823
+ for (let s of spans) {
123824
+ let width = s.to - s.from;
123825
+ for (let i = 0; i < out.length; i++) {
123826
+ let a = dayStarts[i], b = dayStarts[i + 1];
123827
+ if (width <= 0) {
123828
+ s.to >= a && s.to < b && (out[i] += s.count);
123829
+ continue;
123830
+ }
123831
+ let overlap = Math.min(b, s.to) - Math.max(a, s.from);
123832
+ overlap > 0 && (out[i] += s.count * overlap / width);
123833
+ }
123834
+ }
123835
+ return out;
123836
+ }
123837
+ function windowDayStarts(now2, days) {
123838
+ let d = new Date(now2), out = [];
123839
+ for (let i = days - 1; i >= -1; i--) out.push(new Date(d.getFullYear(), d.getMonth(), d.getDate() - i).getTime());
123840
+ return out;
123841
+ }
123842
+ function buildDailyActivity(scan, spans, now2, days) {
123843
+ let keys = localDayKeys(now2, days), starts = windowDayStarts(now2, keys.length), live = keys.map(() => ({ messages: 0, usd: 0, priced: !1, unpriced: !1 }));
123844
+ for (let r of scan.rows.values()) {
123845
+ if (r.t < starts[0] || r.t >= starts[starts.length - 1]) continue;
123846
+ let i = 0;
123847
+ for (; r.t >= starts[i + 1]; ) i++;
123848
+ let day = live[i];
123849
+ day.messages++, r.usd !== void 0 && (day.usd += r.usd, day.priced = !0), r.unpriced && (day.unpriced = !0);
123850
+ }
123851
+ let archived = spreadSpans(spans, starts);
123852
+ return keys.map((date, i) => {
123853
+ let extra = Math.round(archived[i]), d = live[i];
123854
+ return {
123855
+ date,
123856
+ messages: d.messages + extra,
123857
+ ...extra > 0 ? { estimated: !0 } : {},
123858
+ ...d.priced ? { costUsd: Number(d.usd.toFixed(8)) } : {},
123859
+ ...d.unpriced || extra > 0 ? { costPartial: !0 } : {}
123860
+ };
123861
+ });
123862
+ }
123863
+ function readDailyActivity(q, now2, prior) {
123864
+ let since = windowStartMs(now2), scan = refreshLoopDayScan(q, since, prior);
123865
+ return { daily: buildDailyActivity(scan, readArchiveSpans(q, since), now2), scan };
123866
+ }
123867
+ function recentCost(daily, days = 7) {
123868
+ let recent = daily.slice(-days);
123869
+ if (recent.some((d) => d.costUsd !== void 0))
123870
+ return {
123871
+ usd: Number(recent.reduce((s, d) => s + (d.costUsd ?? 0), 0).toFixed(8)),
123872
+ partial: recent.some((d) => d.costPartial === !0)
123873
+ };
123874
+ }
123875
+
123876
+ // src/shared/utils/agent-metrics.ts
123877
+ var isNum = (v) => typeof v == "number" && Number.isFinite(v), isText = (v) => typeof v == "string" && v.trim() !== "";
123878
+ function parseMetric(name21, raw) {
123879
+ let text2 = raw == null ? "" : String(raw), plain = { name: name21, label: name21, value: text2, raw: text2 };
123880
+ if (!text2.trimStart().startsWith("{")) return plain;
123881
+ let o;
123882
+ try {
123883
+ o = JSON.parse(text2);
123884
+ } catch {
123885
+ return plain;
123886
+ }
123887
+ if (!o || typeof o != "object" || Array.isArray(o)) return plain;
123888
+ let r = o;
123889
+ return !isNum(r.value) && typeof r.value != "string" ? plain : {
123890
+ name: name21,
123891
+ label: isText(r.label) ? r.label : name21,
123892
+ value: r.value,
123893
+ ...isText(r.unit) ? { unit: r.unit } : {},
123894
+ ...isNum(r.min) ? { min: r.min } : {},
123895
+ ...isNum(r.max) ? { max: r.max } : {},
123896
+ ...isNum(r.target) ? { target: r.target } : {},
123897
+ raw: text2
123898
+ };
123899
+ }
123900
+
123901
+ // src/main/services/agent-vitals.ts
123902
+ var MIND_LOG_SEED_BYTES = Buffer.byteLength(DEFAULT_MIND_LOG_CONTENT);
123903
+ function memoryBytes(path, size) {
123904
+ let bytes = typeof size == "number" && Number.isFinite(size) ? size : 0;
123905
+ return path === "mind/log.md" ? Math.max(0, bytes - MIND_LOG_SEED_BYTES) : bytes;
123906
+ }
123907
+ function overlayLiveStates(meshAgents, live) {
123908
+ let byPath = /* @__PURE__ */ new Map();
123909
+ for (let l of live)
123910
+ byPath.set(l.filePath, { state: l.state, activeLoops: l.activeLoops ?? byPath.get(l.filePath)?.activeLoops });
123911
+ return meshAgents.map((a) => {
123912
+ let l = byPath.get(a.filePath);
123913
+ return l ? l.activeLoops === void 0 ? { ...a, state: l.state } : { ...a, state: l.state, activeLoops: l.activeLoops } : a;
123914
+ });
123915
+ }
123916
+ function parseDidHistory(raw) {
123917
+ if (!raw) return [];
123918
+ try {
123919
+ let parsed = JSON.parse(raw);
123920
+ return Array.isArray(parsed) ? parsed.filter((d) => typeof d == "string" && d) : [];
123921
+ } catch {
123922
+ return [];
123923
+ }
123924
+ }
123925
+ function liveFleetMeta(workspace) {
123926
+ try {
123927
+ let config = workspace.getAgentConfig();
123928
+ return {
123929
+ handle: workspace.getMeta("adf_handle") || config?.handle || null,
123930
+ name: workspace.getMeta("adf_name") || config?.name || null,
123931
+ icon: config?.icon ?? null,
123932
+ model: config?.model?.model_id || null,
123933
+ status: workspace.getMeta("status") ?? null,
123934
+ did: workspace.getMeta("adf_did") || null,
123935
+ didHistory: parseDidHistory(workspace.getMeta("adf_did_history")),
123936
+ agentId: config?.id ?? null,
123937
+ parentDid: workspace.getMeta("adf_parent_did") || null,
123938
+ createdAt: workspace.getMeta("adf_created_at") || config?.metadata?.created_at || null
123939
+ };
123940
+ } catch {
123941
+ return null;
123942
+ }
123943
+ }
123944
+ function safe(q, sql, map, fallback, params) {
123945
+ try {
123946
+ return map(q(sql, params));
123947
+ } catch {
123948
+ return fallback;
123949
+ }
123950
+ }
123951
+ function safeOr(q, sql, map, orElse, params) {
123952
+ try {
123953
+ return map(q(sql, params));
123954
+ } catch {
123955
+ return orElse();
123956
+ }
123957
+ }
123958
+ var firstNumber2 = (rows, col = "n") => {
123959
+ let v = rows[0]?.[col];
123960
+ return typeof v == "number" && Number.isFinite(v) ? v : 0;
123961
+ }, DERIVED_FILES = /* @__PURE__ */ new Set(["skills-registry.json"]), SKILL_FILE = /^skills\/([^/]+)\//, CREATION_GRACE_MS = 1e4;
123962
+ function agentWriteCutoff(createdAt) {
123963
+ let createdMs = createdAt ? Date.parse(createdAt) : NaN;
123964
+ return Number.isFinite(createdMs) ? new Date(createdMs + CREATION_GRACE_MS).toISOString() : "";
123965
+ }
123966
+ function classifyFiles(files, cutoff, registrySkills = []) {
123967
+ let mine = [], agentSkills = /* @__PURE__ */ new Set(), starterSkills = /* @__PURE__ */ new Set();
123968
+ for (let f of files) {
123969
+ if (DERIVED_FILES.has(f.path)) continue;
123970
+ let own = !cutoff || f.updated_at > cutoff, skill = SKILL_FILE.exec(f.path)?.[1];
123971
+ own ? (mine.push(f), skill && agentSkills.add(skill)) : skill && starterSkills.add(skill);
123972
+ }
123973
+ for (let name21 of registrySkills) starterSkills.has(name21) || agentSkills.add(name21);
123974
+ return { mine, agentSkills };
123975
+ }
123976
+ function readRegistrySkills(q) {
123977
+ return safe(
123978
+ q,
123979
+ "SELECT content FROM adf_files WHERE path = 'skills-registry.json'",
123980
+ (rows) => {
123981
+ let c = rows[0]?.content;
123982
+ if (!c) return [];
123983
+ let skills = JSON.parse(Buffer.isBuffer(c) ? c.toString("utf-8") : String(c)).skills;
123984
+ return Array.isArray(skills) ? skills.map((s) => s?.name).filter((n) => typeof n == "string") : skills && typeof skills == "object" ? Object.keys(skills) : [];
123985
+ },
123986
+ []
123987
+ );
123988
+ }
123989
+ var MAX_TABLES = 50;
123990
+ function listLocalTables(q) {
123991
+ return safe(
123992
+ q,
123993
+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'local\\_%' ESCAPE '\\' ORDER BY name",
123994
+ (rows) => rows.map((r) => r.name).filter((n) => /^local_[A-Za-z0-9_]+$/.test(n)),
123995
+ []
123996
+ );
123997
+ }
123998
+ var LOOP_AUDIT_WHERE2 = "(source = 'loop' OR source LIKE 'loop:%')", FULL_SNAPSHOT_SHARE = 0.5, BASELINE_KEY = "context_baseline_tokens", loopOfSource2 = (source) => source === "loop" ? "main" : source.slice(5) || "main", clamp01 = (x) => Number.isFinite(x) ? Math.min(1, Math.max(0, x)) : 0;
123999
+ function readSummaryScan(q, prior, upTo = 1 / 0) {
124000
+ let maxSeq = Math.min(upTo, safe(q, "SELECT MAX(seq) AS n FROM adf_loop", (r) => firstNumber2(r), 0)), base = prior && prior.lastSeq <= maxSeq ? prior : null, sql = (loopCol) => `SELECT seq, ${loopCol} AS loop FROM adf_loop WHERE seq > ? AND seq <= ? AND role = 'user' AND content_json LIKE '%[Loop Compacted%'`, params = [base ? base.lastSeq : 0, maxSeq], fresh = safeOr(q, sql("loop"), (r) => r, () => safe(q, sql("'main'"), (r) => r, [], params), params), kept = [];
124001
+ if (base && base.hits.length > 0) {
124002
+ let alive = new Set(safe(
124003
+ q,
124004
+ "SELECT seq FROM adf_loop WHERE seq IN (SELECT value FROM json_each(?))",
124005
+ (r) => r.map((x) => x.seq),
124006
+ [],
124007
+ [JSON.stringify(base.hits.map((h) => h.seq))]
124008
+ ));
124009
+ kept = base.hits.filter((h) => alive.has(h.seq));
124010
+ }
124011
+ return { lastSeq: maxSeq, hits: [...kept, ...fresh.map((h) => ({ seq: h.seq, loop: h.loop || "main" }))] };
124012
+ }
124013
+ function readContextsWorked(q, config, summaryScan = readSummaryScan(q)) {
124014
+ let threshold = (loop) => resolveLoopThreshold(config, loop), loops = /* @__PURE__ */ new Set(["main", ...(config.loops ?? []).map((l) => l?.name).filter((n) => typeof n == "string" && !!n)]), live = safeOr(
124015
+ q,
124016
+ "SELECT loop, COUNT(*) AS n FROM adf_loop GROUP BY loop",
124017
+ (r) => r,
124018
+ () => safe(q, "SELECT 'main' AS loop, COUNT(*) AS n FROM adf_loop", (r) => r, [])
124019
+ ), liveRows = /* @__PURE__ */ new Map();
124020
+ for (let r of live) {
124021
+ let name21 = r.loop || "main";
124022
+ liveRows.set(name21, (liveRows.get(name21) ?? 0) + r.n), loops.add(name21);
124023
+ }
124024
+ let archived = /* @__PURE__ */ new Map();
124025
+ for (let r of safe(q, `SELECT source, size_bytes AS bytes FROM adf_audit WHERE ${LOOP_AUDIT_WHERE2}`, (rows) => rows, [])) {
124026
+ let name21 = loopOfSource2(r.source);
124027
+ loops.add(name21);
124028
+ let bytes = typeof r.bytes == "number" ? r.bytes : 0;
124029
+ archived.set(name21, (archived.get(name21) ?? 0) + clamp01(bytes / 4 / (threshold(name21) * FULL_SNAPSHOT_SHARE)));
124030
+ }
124031
+ let summaries = /* @__PURE__ */ new Map();
124032
+ for (let h of summaryScan.hits) summaries.set(h.loop, (summaries.get(h.loop) ?? 0) + 1);
124033
+ let baselines = /* @__PURE__ */ new Map();
124034
+ for (let r of safe(q, `SELECT key, value FROM adf_meta WHERE key = '${BASELINE_KEY}' OR key LIKE '${BASELINE_KEY}:%'`, (rows) => rows, []))
124035
+ try {
124036
+ let tokens = JSON.parse(r.value).tokens;
124037
+ typeof tokens == "number" && Number.isFinite(tokens) && baselines.set(r.key === BASELINE_KEY ? "main" : r.key.slice(BASELINE_KEY.length + 1), tokens);
124038
+ } catch {
124039
+ }
124040
+ let total = 0;
124041
+ for (let loop of loops) {
124042
+ if (total += Math.max(archived.get(loop) ?? 0, summaries.get(loop) ?? 0), !liveRows.get(loop)) continue;
124043
+ let tokens = baselines.get(loop);
124044
+ if (tokens === void 0) {
124045
+ let bytesSql = (where) => `SELECT SUM(octet_length(content_json)) AS n FROM adf_loop ${where}`;
124046
+ tokens = (loop === "main" ? safeOr(q, bytesSql("WHERE loop = 'main'"), (r) => firstNumber2(r), () => safe(q, bytesSql(""), (r) => firstNumber2(r), 0)) : safe(q, bytesSql("WHERE loop = ?"), (r) => firstNumber2(r), 0, [loop])) / 4;
124047
+ }
124048
+ total += clamp01(tokens / threshold(loop));
124049
+ }
124050
+ return Math.round(total * 100) / 100;
124051
+ }
124052
+ function readOwnerVerified(q, did) {
124053
+ if (!did) return !1;
124054
+ let rows = safe(q, "SELECT raw_json FROM adf_attestations", (r) => r, []);
124055
+ for (let r of rows) {
124056
+ let att = null;
124057
+ try {
124058
+ att = JSON.parse(r.raw_json);
124059
+ } catch {
124060
+ att = null;
124061
+ }
124062
+ if (att && att.role === "owner" && verifyAttestation(att, { expectedSubject: did })) return !0;
124063
+ }
124064
+ return !1;
124065
+ }
124066
+ function readVitalsCheapPart(q) {
124067
+ let config = safe(
124068
+ q,
124069
+ "SELECT config_json FROM adf_config WHERE id = 1",
124070
+ (rows) => {
124071
+ let raw = rows[0]?.config_json;
124072
+ return raw ? JSON.parse(raw) : {};
124073
+ },
124074
+ {}
124075
+ ), metaMap = safe(
124076
+ q,
124077
+ "SELECT key, value FROM adf_meta WHERE key IN ('adf_handle','adf_name','status','adf_did','adf_did_history','adf_parent_did','adf_created_at')",
124078
+ (rows) => new Map(rows.map((r) => [r.key, r.value])),
124079
+ /* @__PURE__ */ new Map()
124080
+ ), getMeta = (k) => metaMap.get(k) ?? null, meta = {
124081
+ handle: getMeta("adf_handle") || config.handle || null,
124082
+ name: getMeta("adf_name") || config.name || null,
124083
+ icon: config.icon ?? null,
124084
+ model: config.model?.model_id || null,
124085
+ status: getMeta("status"),
124086
+ did: getMeta("adf_did") || null,
124087
+ didHistory: parseDidHistory(getMeta("adf_did_history")),
124088
+ agentId: config.id ?? null,
124089
+ parentDid: getMeta("adf_parent_did") || null,
124090
+ createdAt: getMeta("adf_created_at") || config.metadata?.created_at || null
124091
+ }, identity = safe(
124092
+ q,
124093
+ "SELECT purpose, encryption_algo FROM adf_identity",
124094
+ (rows) => rows,
124095
+ []
124096
+ ), credentials = { plain: 0, sealed: 0 }, privateKey = "none";
124097
+ for (let r of identity) {
124098
+ let sealed = !!r.encryption_algo && r.encryption_algo !== "plain";
124099
+ if (r.purpose === "crypto:signing:private_key") privateKey = sealed ? "sealed" : "plain";
124100
+ else {
124101
+ if (r.purpose.startsWith("crypto:")) continue;
124102
+ sealed ? credentials.sealed++ : credentials.plain++;
124103
+ }
124104
+ }
124105
+ let timerRows = safeOr(
124106
+ q,
124107
+ "SELECT schedule_json, next_wake_at FROM adf_timers WHERE expired = 0",
124108
+ (rows) => rows,
124109
+ () => safe(q, "SELECT schedule_json, next_wake_at FROM adf_timers", (rows) => rows, [])
124110
+ ), fastestIntervalMs, nextWakeAt;
124111
+ for (let t of timerRows) {
124112
+ typeof t.next_wake_at == "number" && (nextWakeAt === void 0 || t.next_wake_at < nextWakeAt) && (nextWakeAt = t.next_wake_at);
124113
+ let schedule = null;
124114
+ try {
124115
+ schedule = JSON.parse(t.schedule_json);
124116
+ } catch {
124117
+ schedule = null;
124118
+ }
124119
+ let every = scheduleIntervalMs(schedule);
124120
+ every !== void 0 && (fastestIntervalMs === void 0 || every < fastestIntervalMs) && (fastestIntervalMs = every);
124121
+ }
124122
+ let messages = Math.max(
124123
+ safe(q, "SELECT seq AS n FROM sqlite_sequence WHERE name = 'adf_loop'", (r) => firstNumber2(r), 0),
124124
+ safe(q, "SELECT MAX(seq) AS n FROM adf_loop", (r) => firstNumber2(r), 0)
124125
+ ), metrics = safe(
124126
+ q,
124127
+ "SELECT key, value FROM adf_meta WHERE key LIKE 'metric:%' ORDER BY key LIMIT 20",
124128
+ (rows) => rows.map((r) => parseMetric(r.key.slice(7), r.value)),
124129
+ []
124130
+ );
124131
+ return {
124132
+ config,
124133
+ meta,
124134
+ power: powerInputsFromConfig(config, {
124135
+ credentials,
124136
+ privateKey,
124137
+ timers: { active: timerRows.length, fastestIntervalMs }
124138
+ }),
124139
+ messages,
124140
+ metrics,
124141
+ nextWakeAt,
124142
+ card: {
124143
+ ...config.description?.trim() ? { description: config.description.trim() } : {},
124144
+ public: config.serving?.public?.enabled ?? !1,
124145
+ ownerVerified: readOwnerVerified(q, meta.did)
124146
+ }
124147
+ };
124148
+ }
124149
+ function readVitalsHeavyPart(q, cheap, prior) {
124150
+ let cutoff = agentWriteCutoff(cheap.meta.createdAt), { mine, agentSkills } = classifyFiles(
124151
+ safe(q, "SELECT path, updated_at FROM adf_files", (rows) => rows, []),
124152
+ cutoff,
124153
+ readRegistrySkills(q)
124154
+ ), filesWritten = mine.filter((f) => !f.path.startsWith("mind/")).length, mindFiles = safe(q, "SELECT path, size FROM adf_files WHERE path LIKE 'mind/%'", (rows) => rows, []), memoryTokens = Math.round(mindFiles.reduce((n, f) => n + memoryBytes(f.path, f.size), 0) / 4), tables = listLocalTables(q), localRows = 0;
124155
+ for (let t of tables.slice(0, MAX_TABLES)) localRows += safe(q, `SELECT COUNT(*) AS n FROM "${t}"`, (r) => firstNumber2(r), 0);
124156
+ let summaryScan = readSummaryScan(q, prior);
124157
+ return {
124158
+ maturity: {
124159
+ contextsWorked: readContextsWorked(q, cheap.config, summaryScan),
124160
+ filesWritten,
124161
+ memoryTokens,
124162
+ localTables: tables.length,
124163
+ localRows,
124164
+ skills: agentSkills.size
124165
+ },
124166
+ summaryScan
124167
+ };
124168
+ }
124169
+ var WEEK_MS = 10080 * 60 * 1e3, approxTokens = (bytes) => Math.round(bytes / 4), DAY_MS2 = 1440 * 60 * 1e3;
124170
+ function readMemoryStrata(q, now2 = Date.now()) {
124171
+ let since = (days) => new Date(now2 - days * DAY_MS2).toISOString(), week = since(MEMORY_STRATA_DAYS.week), month = since(MEMORY_STRATA_DAYS.month), quarter = since(MEMORY_STRATA_DAYS.quarter), bytes = { older: 0, quarter: 0, month: 0, week: 0 }, rows = safe(q, "SELECT path, size, updated_at FROM adf_files WHERE path LIKE 'mind/%'", (r) => r, []);
124172
+ for (let r of rows) {
124173
+ let at = String(r.updated_at ?? ""), band = at >= week ? "week" : at >= month ? "month" : at >= quarter ? "quarter" : "older";
124174
+ bytes[band] += memoryBytes(r.path, r.size);
124175
+ }
124176
+ return { older: approxTokens(bytes.older), quarter: approxTokens(bytes.quarter), month: approxTokens(bytes.month), week: approxTokens(bytes.week) };
124177
+ }
124178
+ function readContents(q, files, agentSkills, now2 = Date.now()) {
124179
+ let weekAgo = now2 - WEEK_MS, mindFiles = 0, mindBytes = 0, mindRecent = 0, skillBytes = 0;
124180
+ for (let f of files) {
124181
+ let size = typeof f.size == "number" && Number.isFinite(f.size) ? f.size : 0;
124182
+ if (f.path.startsWith("mind/")) {
124183
+ mindFiles++, mindBytes += memoryBytes(f.path, size), Date.parse(f.updated_at) >= weekAgo && mindRecent++;
124184
+ continue;
124185
+ }
124186
+ let skill = SKILL_FILE.exec(f.path)?.[1];
124187
+ skill && agentSkills.has(skill) && (skillBytes += size);
124188
+ }
124189
+ let tables = listLocalTables(q).slice(0, MAX_TABLES), rows = 0;
124190
+ for (let t of tables) rows += safe(q, `SELECT COUNT(*) AS n FROM "${t}"`, (r) => firstNumber2(r), 0);
124191
+ return {
124192
+ mind: { files: mindFiles, tokens: approxTokens(mindBytes), updatedThisWeek: mindRecent, strata: readMemoryStrata(q, now2) },
124193
+ skills: { count: agentSkills.size, tokens: approxTokens(skillBytes) },
124194
+ tables: { count: tables.length, rows }
124195
+ };
124196
+ }
124197
+ function upcomingText(v) {
124198
+ if (!(typeof v != "string" || !v.trim()))
124199
+ return v.length > 1e3 ? `${v.slice(0, 999)}\u2026` : v;
124200
+ }
124201
+ function readUpcoming(q) {
124202
+ let readTimers = (cols, where) => safe(q, `SELECT ${cols} FROM adf_timers ${where} ORDER BY next_wake_at LIMIT 3`, (r) => r, null);
124203
+ return (readTimers("id, next_wake_at, scope, payload, lambda, loop", "WHERE expired = 0") ?? readTimers("id, next_wake_at, scope, payload, lambda, NULL AS loop", "WHERE expired = 0") ?? readTimers("id, next_wake_at, scope, payload, lambda, NULL AS loop", "") ?? []).map((t) => {
124204
+ let scope = "system";
124205
+ try {
124206
+ let parsed = JSON.parse(t.scope ?? "[]");
124207
+ Array.isArray(parsed) && parsed[0] === "agent" && (scope = "agent");
124208
+ } catch {
124209
+ }
124210
+ let text2 = upcomingText(t.payload);
124211
+ return scope === "agent" ? { id: t.id, at: t.next_wake_at, scope, loop: t.loop || "main", ...text2 ? { prompt: text2 } : {} } : { id: t.id, at: t.next_wake_at, scope, ...t.lambda ? { lambda: t.lambda } : {}, ...text2 ? { input: text2 } : {} };
124212
+ });
124213
+ }
124214
+ function readActivityHeavy(q, now2, prior) {
124215
+ let createdAt = safe(
124216
+ q,
124217
+ "SELECT COALESCE((SELECT value FROM adf_meta WHERE key = 'adf_created_at'), (SELECT json_extract(config_json, '$.metadata.created_at') FROM adf_config WHERE id = 1)) AS v",
124218
+ (r) => r[0]?.v ?? null,
124219
+ null
124220
+ ), cutoff = agentWriteCutoff(createdAt), files = safe(q, "SELECT path, updated_at, size FROM adf_files", (rows) => rows, []), { agentSkills } = classifyFiles(files, cutoff, readRegistrySkills(q)), { daily, scan } = readDailyActivity(q, now2, prior);
124221
+ return { daily, contents: readContents(q, files, agentSkills, now2), dayScan: scan };
124222
+ }
124223
+ var LIVE_MIN_INTERVAL_MS = 2e3, HEAVY_MIN_AGE_MS = 3e4, SUMMARY_SCAN_CHUNK = 2e3, DAY_SCAN_CHUNK = 2e3, FORCE_MIN_INTERVAL_MS = 2e3, AgentVitalsService = class {
124224
+ constructor(deps) {
124225
+ this.deps = deps;
124226
+ }
124227
+ deps;
124228
+ // Ghost metadata cache, keyed by file path. Two jobs:
124229
+ // 1. Perf — the 5s fleet poll would otherwise open every offline agent's
124230
+ // SQLite each cycle; unchanged mtime serves from memory.
124231
+ // 2. Stability — a peek can fail transiently (SQLITE_BUSY while an agent
124232
+ // is mass-starting and writing its own file). Serving the last good
124233
+ // meta instead of dropping the entry stops agents blinking off the map.
124234
+ fleetMetaCache = /* @__PURE__ */ new Map();
124235
+ // First-observed time of each agent's current status line (for status age)
124236
+ statusSinceMap = /* @__PURE__ */ new Map();
124237
+ /** Last fleet result, for the spawned-children count. */
124238
+ lastFleet = null;
124239
+ lastFleetAt = 0;
124240
+ slowCache = /* @__PURE__ */ new Map();
124241
+ activityCache = /* @__PURE__ */ new Map();
124242
+ /**
124243
+ * Per-row adf_loop scan of the activity window per file, shared by the
124244
+ * vitals (7-day cost) and activity (per-day chart) reads; makes the next
124245
+ * one incremental.
124246
+ */
124247
+ dayScans = /* @__PURE__ */ new Map();
124248
+ /** In-flight cold day scans, so a vitals and an activity read share one. */
124249
+ dayScanWarming = /* @__PURE__ */ new Map();
124250
+ /** Last compaction-summary scan per file; makes the next one incremental. */
124251
+ summaryScans = /* @__PURE__ */ new Map();
124252
+ workspaceIds = /* @__PURE__ */ new WeakMap();
124253
+ nextWorkspaceId = 1;
124254
+ now() {
124255
+ return this.deps.now ? this.deps.now() : Date.now();
124256
+ }
124257
+ peekFleetMetaCached(filePath) {
124258
+ let mtimeMs;
124259
+ try {
124260
+ mtimeMs = statSync10(filePath).mtimeMs;
124261
+ } catch {
124262
+ return this.fleetMetaCache.delete(filePath), null;
124263
+ }
124264
+ let cached = this.fleetMetaCache.get(filePath);
124265
+ if (cached && cached.mtimeMs === mtimeMs) return cached.meta;
124266
+ let meta = AdfDatabase.peekFleetMeta(filePath);
124267
+ return meta ? (this.fleetMetaCache.set(filePath, { mtimeMs, meta }), meta) : cached?.meta ?? null;
124268
+ }
124269
+ // Fleet map: live mesh agents plus on-disk .adf files in tracked
124270
+ // directories that have no running executor ("ghost" nodes). Works even
124271
+ // with the mesh disabled — every on-disk agent is then a ghost.
124272
+ async getFleetStatus() {
124273
+ let deps = this.deps, running = deps.isMeshRunning(), agents = deps.getLiveMeshAgents().map((a) => {
124274
+ let ctx = deps.getContextGauge(a.filePath);
124275
+ return {
124276
+ ...a,
124277
+ online: !0,
124278
+ contextTokens: ctx && ctx.tokens > 0 ? ctx.tokens : void 0,
124279
+ contextThreshold: ctx && ctx.tokens > 0 ? ctx.threshold : void 0,
124280
+ // Standing boundary links — open WS pipes render as dashed channel
124281
+ // edges to the perimeter, distinct from request traffic
124282
+ wsConnections: deps.getWsConnectionCount(a.filePath)
124283
+ };
124284
+ }), trackedDirs2 = deps.getTrackedDirectories(), maxDepth = deps.getMaxScanDepth(), seen = new Set(agents.map((a) => canonicalizePath(a.filePath))), findGhostTrackedDirRoot = (filePath) => {
124285
+ let canonFile = canonicalizePath(filePath), longestMatch, longestLen = -1;
124286
+ for (let dir of trackedDirs2) {
124287
+ let canonDir = canonicalizePath(dir);
124288
+ containsPath(canonDir, canonFile) && canonDir.length > longestLen && (longestMatch = dir, longestLen = canonDir.length);
124289
+ }
124290
+ return longestMatch;
124291
+ }, liveExecStates = /* @__PURE__ */ new Map();
124292
+ for (let s of deps.getLiveExecStates()) liveExecStates.set(canonicalizePath(s.filePath), s.state);
124293
+ let liveWorkspaces = /* @__PURE__ */ new Map();
124294
+ for (let w of deps.getOpenWorkspaces()) liveWorkspaces.set(canonicalizePath(w.filePath), w.workspace);
124295
+ for (let dir of trackedDirs2) {
124296
+ let filePaths = await deps.listAdfFiles(dir, maxDepth);
124297
+ for (let filePath of filePaths) {
124298
+ let canon2 = canonicalizePath(filePath);
124299
+ if (seen.has(canon2)) continue;
124300
+ seen.add(canon2);
124301
+ let openWorkspace = liveWorkspaces.get(canon2), meta = (openWorkspace ? liveFleetMeta(openWorkspace) : null) ?? this.peekFleetMetaCached(filePath);
124302
+ if (!meta) continue;
124303
+ let live = liveExecStates.get(canon2), isLive = live !== void 0 && live !== "off", ctx = isLive ? deps.getContextGauge(filePath) : void 0;
124304
+ agents.push({
124305
+ filePath,
124306
+ handle: meta.handle || deriveHandle(filePath),
124307
+ did: meta.did ?? void 0,
124308
+ agentId: meta.agentId ?? void 0,
124309
+ parentDid: meta.parentDid ?? void 0,
124310
+ didHistory: meta.didHistory.length > 0 ? meta.didHistory : void 0,
124311
+ icon: meta.icon ?? void 0,
124312
+ state: isLive ? live : "off",
124313
+ status: meta.status ?? void 0,
124314
+ model: meta.model ?? void 0,
124315
+ trackedDirRoot: findGhostTrackedDirRoot(filePath),
124316
+ createdAt: meta.createdAt ?? void 0,
124317
+ participating: !1,
124318
+ online: isLive,
124319
+ contextTokens: ctx && ctx.tokens > 0 ? ctx.tokens : void 0,
124320
+ contextThreshold: ctx && ctx.tokens > 0 ? ctx.threshold : void 0
124321
+ });
124322
+ }
124323
+ }
124324
+ let now2 = this.now();
124325
+ for (let a of agents) {
124326
+ if (!a.status) {
124327
+ this.statusSinceMap.delete(a.filePath);
124328
+ continue;
124329
+ }
124330
+ let prev = this.statusSinceMap.get(a.filePath);
124331
+ (!prev || prev.value !== a.status) && this.statusSinceMap.set(a.filePath, { value: a.status, since: now2 }), a.statusSince = this.statusSinceMap.get(a.filePath).since;
124332
+ }
124333
+ return this.lastFleet = agents, this.lastFleetAt = now2, { running, agents };
124334
+ }
124335
+ /**
124336
+ * Redo the fleet scan behind agentsSpawned when it is missing or older than
124337
+ * `maxAgeMs` (always when `force`). Studio's fleet map polls it anyway; a
124338
+ * vitals read with the map closed, or from the daemon, refreshes it here so
124339
+ * the Experience level does not depend on which screen was opened first.
124340
+ */
124341
+ async ensureFleetScan(maxAgeMs, force = !1) {
124342
+ !force && this.lastFleet && this.now() - this.lastFleetAt <= maxAgeMs || await this.getFleetStatus();
124343
+ }
124344
+ findOpenWorkspace(filePath) {
124345
+ let canon2 = canonicalizePath(filePath), found;
124346
+ for (let w of this.deps.getOpenWorkspaces())
124347
+ canonicalizePath(w.filePath) === canon2 && (found = w.workspace);
124348
+ return found;
124349
+ }
124350
+ workspaceId(ws) {
124351
+ let id = this.workspaceIds.get(ws);
124352
+ return id === void 0 && (id = this.nextWorkspaceId++, this.workspaceIds.set(ws, id)), id;
124353
+ }
124354
+ /** Drop the cached slow part (e.g. after a config change made elsewhere). */
124355
+ invalidate(filePath) {
124356
+ filePath ? (this.slowCache.delete(filePath), this.activityCache.delete(filePath), this.dayScans.delete(filePath), this.summaryScans.delete(filePath)) : (this.slowCache.clear(), this.activityCache.clear(), this.dayScans.clear(), this.summaryScans.clear());
124357
+ }
124358
+ /**
124359
+ * One cached read in two parts against one connection. Live agents: keyed by
124360
+ * the connection's total_changes() (any write — a finished turn, a config
124361
+ * change — bumps it), re-read at most every LIVE_MIN_INTERVAL_MS. Files
124362
+ * nobody has open: keyed by the file's and its WAL's mtime/size, read with
124363
+ * one readonly peek. A new key re-reads `cheap`; `heavy` (the scans) is
124364
+ * reused until it is HEAVY_MIN_AGE_MS old. `force` re-reads `cheap` always
124365
+ * and `heavy` unless it is younger than FORCE_MIN_INTERVAL_MS. `salt` (the
124366
+ * activity read's local day) drops both when it changes.
124367
+ */
124368
+ async splitRead(cache2, filePath, force, readCheap, readHeavy, salt = "") {
124369
+ await new Promise((resolve15) => setImmediate(resolve15));
124370
+ let ws = this.findOpenWorkspace(filePath), cached = cache2.get(filePath), fresh = cached && cached.salt === salt ? cached : void 0, now2 = this.now(), key;
124371
+ if (ws) {
124372
+ if (!force && fresh?.key.startsWith("live:") && now2 - fresh.at < LIVE_MIN_INTERVAL_MS) return { entry: fresh, live: !0 };
124373
+ let changes = safe(ws.querySQL.bind(ws), "SELECT total_changes() AS n", (r) => firstNumber2(r), -1);
124374
+ key = `live:${this.workspaceId(ws)}:${changes}`;
124375
+ } else {
124376
+ let [main, wal] = await Promise.all([
124377
+ fsp4.stat(filePath),
124378
+ fsp4.stat(`${filePath}-wal`).catch(() => null)
124379
+ ]);
124380
+ key = `file:${main.mtimeMs}:${main.size}:${wal?.mtimeMs ?? 0}:${wal?.size ?? 0}`;
124381
+ }
124382
+ if (!force && fresh && fresh.key === key)
124383
+ return ws && (fresh.at = now2), { entry: fresh, live: !!ws };
124384
+ let heavyAge = fresh ? now2 - fresh.heavyAt : 1 / 0, reuseHeavy = fresh !== void 0 && heavyAge < (force ? FORCE_MIN_INTERVAL_MS : HEAVY_MIN_AGE_MS), run2 = (q) => {
124385
+ let cheap = readCheap(q);
124386
+ return { cheap, heavy: reuseHeavy ? fresh.heavy : readHeavy(q, cheap) };
124387
+ };
124388
+ try {
124389
+ let { cheap, heavy } = ws ? run2((sql, params) => ws.querySQL(sql, params)) : AdfDatabase.peek(filePath, (db) => run2((sql, params) => db.prepare(sql).all(...params ?? []))), at = this.now(), entry = { key, salt, at, cheap, heavy, heavyAt: reuseHeavy ? fresh.heavyAt : at };
124390
+ return cache2.set(filePath, entry), { entry, live: !!ws };
124391
+ } catch (err) {
124392
+ if (cached) return { entry: cached, live: !!ws };
124393
+ throw err;
124394
+ }
124395
+ }
124396
+ /**
124397
+ * A live agent's first compaction-summary scan, in SUMMARY_SCAN_CHUNK seq
124398
+ * ranges with the event loop free between them: the LIKE reads every user
124399
+ * row's content, which on a long unbroken loop is tens of milliseconds.
124400
+ * Later scans only read rows above the high-water mark.
124401
+ */
124402
+ async warmSummaryScan(filePath, ws) {
124403
+ let q = (sql, params) => ws.querySQL(sql, params), maxSeq = safe(q, "SELECT MAX(seq) AS n FROM adf_loop", (r) => firstNumber2(r), 0), scan = { lastSeq: 0, hits: [] };
124404
+ for (; scan.lastSeq < maxSeq; )
124405
+ if (scan = readSummaryScan(q, scan, scan.lastSeq + SUMMARY_SCAN_CHUNK), await new Promise((resolve15) => setImmediate(resolve15)), this.findOpenWorkspace(filePath) !== ws) return;
124406
+ this.summaryScans.has(filePath) || this.summaryScans.set(filePath, scan);
124407
+ }
124408
+ /**
124409
+ * The first day scan of a file for the current window, in DAY_SCAN_CHUNK
124410
+ * seq ranges with the event loop free between them: reading created_at
124411
+ * walks each row's content overflow, about 3 ms per 1 000 rows. A live
124412
+ * agent is read through its workspace; a closed file through one readonly
124413
+ * peek per chunk. Later reads are incremental (refreshLoopDayScan).
124414
+ * Concurrent callers share one warm-up.
124415
+ */
124416
+ warmDayScan(filePath) {
124417
+ let since = windowStartMs(this.now());
124418
+ if (this.dayScans.get(filePath)?.since === since) return Promise.resolve();
124419
+ let warming = this.dayScanWarming.get(filePath);
124420
+ return warming || (warming = this.runDayScanWarmUp(filePath, since).catch(() => {
124421
+ }).finally(() => this.dayScanWarming.delete(filePath)), this.dayScanWarming.set(filePath, warming)), warming;
124422
+ }
124423
+ async runDayScanWarmUp(filePath, since) {
124424
+ let ws = this.findOpenWorkspace(filePath), run2 = (read) => ws ? read((sql, params) => ws.querySQL(sql, params)) : AdfDatabase.peek(filePath, (db) => read((sql, params) => db.prepare(sql).all(...params ?? []))), scan = run2((q) => startLoopDayScan(q, since)), maxSeq = run2(maxLoopSeq);
124425
+ for (; scan.lastSeq < maxSeq; ) {
124426
+ if (await new Promise((resolve15) => setImmediate(resolve15)), this.findOpenWorkspace(filePath) !== ws) return;
124427
+ let upTo = Math.min(maxSeq, scan.lastSeq + DAY_SCAN_CHUNK);
124428
+ scan = run2((q) => scanLoopRows(q, scan, upTo));
124429
+ }
124430
+ this.dayScans.get(filePath)?.since !== since && this.dayScans.set(filePath, scan);
124431
+ }
124432
+ /** Children in the last fleet scan whose parent reference names this agent. */
124433
+ countSpawned(meta) {
124434
+ if (!this.lastFleet) return null;
124435
+ let ids = /* @__PURE__ */ new Set([...meta.did ? [meta.did] : [], ...meta.didHistory, ...meta.agentId ? [meta.agentId] : []]);
124436
+ return ids.size === 0 ? 0 : this.lastFleet.filter((a) => a.parentDid && ids.has(a.parentDid)).length;
124437
+ }
124438
+ async getAgentVitals(filePath, opts) {
124439
+ let ws = this.findOpenWorkspace(filePath);
124440
+ ws && !this.summaryScans.has(filePath) && await this.warmSummaryScan(filePath, ws), await this.warmDayScan(filePath);
124441
+ let { entry, live } = await this.splitRead(
124442
+ this.slowCache,
124443
+ filePath,
124444
+ !!opts?.force,
124445
+ readVitalsCheapPart,
124446
+ (q, cheap2) => {
124447
+ let heavy2 = readVitalsHeavyPart(q, cheap2, this.summaryScans.get(filePath));
124448
+ this.summaryScans.set(filePath, heavy2.summaryScan);
124449
+ let { daily, scan } = readDailyActivity(q, this.now(), this.dayScans.get(filePath));
124450
+ this.dayScans.set(filePath, scan);
124451
+ let cost7d = recentCost(daily);
124452
+ return cost7d ? { ...heavy2, cost7d } : heavy2;
124453
+ }
124454
+ ), { cheap, heavy } = entry, now2 = this.now(), createdMs = cheap.meta.createdAt ? Date.parse(cheap.meta.createdAt) : NaN, ageDays = Number.isFinite(createdMs) ? Math.max(0, (now2 - createdMs) / 864e5) : 0, maturity = {
124455
+ ...heavy.maturity,
124456
+ messages: cheap.messages,
124457
+ agentsSpawned: this.countSpawned(cheap.meta),
124458
+ ageDays
124459
+ }, ctx = live ? this.deps.getContextGauge(filePath) : void 0, cost = heavy.cost7d;
124460
+ return {
124461
+ filePath,
124462
+ computedAt: entry.at,
124463
+ live,
124464
+ handle: cheap.meta.handle || deriveHandle(filePath),
124465
+ name: cheap.meta.name ?? void 0,
124466
+ did: cheap.meta.did ?? void 0,
124467
+ agentId: cheap.meta.agentId ?? void 0,
124468
+ model: cheap.meta.model ?? void 0,
124469
+ createdAt: cheap.meta.createdAt ?? void 0,
124470
+ ageDays,
124471
+ ...cheap.meta.status?.trim() ? { status: cheap.meta.status.trim() } : {},
124472
+ ...cheap.card.description ? { description: cheap.card.description } : {},
124473
+ public: cheap.card.public,
124474
+ ownerVerified: cheap.card.ownerVerified,
124475
+ contextTokens: ctx && ctx.tokens > 0 ? ctx.tokens : void 0,
124476
+ contextThreshold: ctx && ctx.tokens > 0 ? ctx.threshold : void 0,
124477
+ nextWakeAt: cheap.nextWakeAt,
124478
+ cost7dUsd: cost ? cost.usd : void 0,
124479
+ cost7dPartial: cost ? cost.partial : void 0,
124480
+ metrics: cheap.metrics,
124481
+ stats: scoreAgent(cheap.power, maturity),
124482
+ maturity
124483
+ };
124484
+ }
124485
+ /**
124486
+ * The overview's lower sections: next wakes, 14 days of messages and cost
124487
+ * per day (agent-daily.ts, from the file alone), and what the file holds
124488
+ * (mind and skills with approximate tokens, local tables with rows). Cached
124489
+ * like the vitals: wakes are the cheap part, days and contents the heavy one.
124490
+ */
124491
+ async getAgentActivity(filePath, opts) {
124492
+ let now2 = this.now();
124493
+ await this.warmDayScan(filePath);
124494
+ let { entry, live } = await this.splitRead(
124495
+ this.activityCache,
124496
+ filePath,
124497
+ !!opts?.force,
124498
+ (q) => ({ upcoming: readUpcoming(q) }),
124499
+ (q) => {
124500
+ let { dayScan, ...read } = readActivityHeavy(q, this.now(), this.dayScans.get(filePath));
124501
+ return this.dayScans.set(filePath, dayScan), read;
124502
+ },
124503
+ localDateKey(new Date(now2))
124504
+ );
124505
+ return { filePath, computedAt: entry.at, live, upcoming: entry.cheap.upcoming, ...entry.heavy };
124506
+ }
124507
+ };
124508
+
124509
+ // src/main/runtime/display-state.ts
124510
+ function toDisplayState(executorState) {
124511
+ switch (executorState) {
124512
+ case "thinking":
124513
+ case "tool_use":
124514
+ return "active";
124515
+ case "idle":
124516
+ return "idle";
124517
+ case "awaiting_approval":
124518
+ case "awaiting_ask":
124519
+ case "suspended":
124520
+ return "suspended";
124521
+ case "error":
124522
+ return "error";
124523
+ case "stopped":
124524
+ return "off";
124525
+ // ADF display states (pass-through from sys_set_state target)
124526
+ case "active":
124527
+ case "hibernate":
124528
+ case "off":
124529
+ return executorState;
124530
+ default:
124531
+ return "off";
124532
+ }
124533
+ }
124534
+
122177
124535
  // src/main/daemon/tracked-dirs.ts
122178
- import { existsSync as existsSync21, statSync as statSync10 } from "node:fs";
124536
+ import { existsSync as existsSync21, statSync as statSync11 } from "node:fs";
122179
124537
  import { basename as basename12, isAbsolute as isAbsolute6 } from "node:path";
122180
124538
  var TrackedDirError = class extends Error {
122181
124539
  constructor(message, statusCode, details) {
@@ -122204,7 +124562,7 @@ function sameDir(a, b) {
122204
124562
  }
122205
124563
  function isDirectory(p) {
122206
124564
  try {
122207
- return existsSync21(p) && statSync10(p).isDirectory();
124565
+ return existsSync21(p) && statSync11(p).isDirectory();
122208
124566
  } catch {
122209
124567
  return !1;
122210
124568
  }
@@ -122226,7 +124584,7 @@ function loadedUnder(runtime2, dir) {
122226
124584
  var PEEK_CACHE_MAX = 2e3, peekCache = /* @__PURE__ */ new Map();
122227
124585
  function statStamp(path) {
122228
124586
  try {
122229
- let st = statSync10(path);
124587
+ let st = statSync11(path);
122230
124588
  return `${st.mtimeMs}:${st.size}`;
122231
124589
  } catch {
122232
124590
  return "-";
@@ -122380,6 +124738,76 @@ async function untrackDir(runtime2, settings2, input, opts = {}, onChanged) {
122380
124738
  return { removed: match, directories, unloaded };
122381
124739
  }
122382
124740
 
124741
+ // src/main/daemon/vitals-routes.ts
124742
+ var FLEET_SCAN_MAX_AGE_MS = 3e4;
124743
+ function canon(filePath) {
124744
+ try {
124745
+ return canonicalizePath(filePath);
124746
+ } catch {
124747
+ return filePath;
124748
+ }
124749
+ }
124750
+ function createDaemonVitalsDeps(src) {
124751
+ let live = () => src.runtime.listLiveAgents(), liveStates = () => live().map((a) => ({ filePath: a.filePath, state: toDisplayState(a.executorState) }));
124752
+ return {
124753
+ isMeshRunning: () => !!src.mesh?.isEnabled(),
124754
+ getLiveMeshAgents: () => src.mesh?.isEnabled() ? overlayLiveStates(src.mesh.getAgentStatuses(), liveStates()) : [],
124755
+ getTrackedDirectories: () => src.settings ? storedDirs(src.settings) : [],
124756
+ getMaxScanDepth: () => src.settings ? maxDepthOf(src.settings) : 5,
124757
+ listAdfFiles: async (dir, maxDepth) => src.runtime.scanAdfFiles([dir], maxDepth),
124758
+ getContextGauge: (filePath) => {
124759
+ let key = canon(filePath);
124760
+ return live().find((a) => canon(a.filePath) === key)?.getContextGauge();
124761
+ },
124762
+ getWsConnectionCount: (filePath) => src.ws && src.ws.getConnections(filePath).length || void 0,
124763
+ getLiveExecStates: liveStates,
124764
+ getOpenWorkspaces: () => live().map((a) => ({ filePath: a.filePath, workspace: a.workspace }))
124765
+ };
124766
+ }
124767
+ function resolveVitalsFile(id, runtime2, vitals, settings2) {
124768
+ let loaded = runtime2.getAgent(id);
124769
+ if (loaded) return { filePath: loaded.filePath };
124770
+ if (!settings2) return;
124771
+ let files = runtime2.scanAdfFiles(storedDirs(settings2), maxDepthOf(settings2)), byHandle;
124772
+ for (let filePath of files) {
124773
+ let meta = vitals.peekFleetMetaCached(filePath);
124774
+ if (meta) {
124775
+ if (meta.agentId === id) return { filePath };
124776
+ !byHandle && meta.handle === id && (byHandle = filePath);
124777
+ }
124778
+ }
124779
+ return byHandle ? { filePath: byHandle } : void 0;
124780
+ }
124781
+ function registerVitalsRoutes(server, deps) {
124782
+ let target = (request, reply, what) => {
124783
+ let force = request.query.force;
124784
+ if (force !== void 0 && !["1", "0", "true", "false"].includes(force))
124785
+ return reply.code(400).send({ error: "force must be 1, 0, true or false", code: "bad_request" }), null;
124786
+ let found = resolveVitalsFile(request.params.id, deps.runtime, deps.vitals, deps.settings);
124787
+ return found ? found.filePath ? { filePath: found.filePath, forced: force === "1" || force === "true" } : (reply.code(409).send({ error: `Agent has no .adf file; ${what} read from the file.`, code: "conflict" }), null) : (reply.code(404).send({ error: `Unknown agent "${request.params.id}"`, code: "not_found" }), null);
124788
+ };
124789
+ server.get("/agents/:id/vitals", async (request, reply) => {
124790
+ let t = target(request, reply, "vitals are");
124791
+ if (!t) return reply;
124792
+ try {
124793
+ return await deps.vitals.ensureFleetScan(FLEET_SCAN_MAX_AGE_MS, t.forced), await deps.vitals.getAgentVitals(t.filePath, { force: t.forced });
124794
+ } catch (err) {
124795
+ return fail2(reply, err);
124796
+ }
124797
+ }), server.get("/agents/:id/activity", async (request, reply) => {
124798
+ let t = target(request, reply, "activity is");
124799
+ if (!t) return reply;
124800
+ try {
124801
+ return await deps.vitals.getAgentActivity(t.filePath, { force: t.forced });
124802
+ } catch (err) {
124803
+ return fail2(reply, err);
124804
+ }
124805
+ });
124806
+ }
124807
+ function fail2(reply, err) {
124808
+ return reply.code(500).send({ error: err instanceof Error ? err.message : String(err) });
124809
+ }
124810
+
122383
124811
  // src/main/daemon/http-api.ts
122384
124812
  var AdapterAttachRequestSchema = external_exports.object({
122385
124813
  adapterType: external_exports.string(),
@@ -122469,7 +124897,16 @@ function createDaemonHttpApi(runtime2, opts = {}) {
122469
124897
  behindProxy: opts.security?.behindProxy ?? behindProxyFromEnv(),
122470
124898
  localProof: opts.security?.localProof ?? null
122471
124899
  };
122472
- server.post("/daemon/shutdown", async (request, reply) => requireLocalCaller(request, reply, localAccess, "The daemon can be stopped") ? opts.requestShutdown ? (setTimeout(() => opts.requestShutdown?.(), 50), reply.code(202).send({ accepted: !0, pid: process.pid })) : methodNotAllowed(reply, "Shutdown is not available on this daemon.") : reply), registerIdentityRoutes(server, { identity: opts.identity, agentFactory: opts.agentFactory, localAccess }), registerTemplateRoutes(server, { agentFactory: opts.agentFactory }), registerProviderRoutes(server, { settingsStore: opts.settingsStore, providerKeys: opts.providerKeys }), registerContextRoutes(server, runtime2), server.get("/events", async (request, reply) => {
124900
+ server.post("/daemon/shutdown", async (request, reply) => requireLocalCaller(request, reply, localAccess, "The daemon can be stopped") ? opts.requestShutdown ? (setTimeout(() => opts.requestShutdown?.(), 50), reply.code(202).send({ accepted: !0, pid: process.pid })) : methodNotAllowed(reply, "Shutdown is not available on this daemon.") : reply), registerIdentityRoutes(server, { identity: opts.identity, agentFactory: opts.agentFactory, localAccess }), registerTemplateRoutes(server, { agentFactory: opts.agentFactory }), registerProviderRoutes(server, { settingsStore: opts.settingsStore, providerKeys: opts.providerKeys }), registerContextRoutes(server, runtime2), registerVitalsRoutes(server, {
124901
+ runtime: runtime2,
124902
+ settings: opts.settingsStore,
124903
+ vitals: new AgentVitalsService(createDaemonVitalsDeps({
124904
+ runtime: runtime2,
124905
+ settings: opts.settingsStore,
124906
+ mesh: opts.mesh,
124907
+ ws: opts.wsService
124908
+ }))
124909
+ }), server.get("/events", async (request, reply) => {
122473
124910
  if (!opts.eventBus) return unavailable2(reply, "Event bus is not configured.");
122474
124911
  let bus = opts.eventBus, lastEventId = single2(request.headers["last-event-id"])?.trim(), sinceText = request.query.since ?? (lastEventId || void 0), since = parseOptionalInteger(sinceText);
122475
124912
  if (sinceText !== void 0 && since === void 0)
@@ -124013,6 +126450,7 @@ var DaemonHost = class {
124013
126450
  eventBus;
124014
126451
  wsService;
124015
126452
  networkService;
126453
+ mesh;
124016
126454
  mcpPackageService;
124017
126455
  mcpPythonPackageService;
124018
126456
  adapterPackageService;
@@ -124029,7 +126467,7 @@ var DaemonHost = class {
124029
126467
  signalHandlersInstalled = !1;
124030
126468
  stopping = null;
124031
126469
  constructor(opts) {
124032
- this.runtime = opts.runtime, this.host = opts.host ?? "127.0.0.1", this.port = opts.port ?? 7385, this.token = opts.token ?? process.env.ADF_DAEMON_TOKEN ?? null, this.allowedHosts = opts.allowedHosts ?? parseAllowedHostsEnv(process.env.ADF_DAEMON_ALLOWED_HOSTS), this.behindProxy = opts.behindProxy, this.localProof = opts.localProof ?? null, this.pidFile = opts.pidFile, this.logger = opts.logger ?? !1, this.shutdownAgentTimeoutMs = opts.shutdownAgentTimeoutMs ?? envShutdownTimeoutMs() ?? DEFAULT_AGENT_UNLOAD_TIMEOUT_MS, this.computeService = opts.computeService, this.settingsStore = opts.settingsStore, this.eventBus = opts.eventBus, this.wsService = opts.wsService, this.networkService = opts.networkService, this.mcpPackageService = opts.mcpPackageService, this.mcpPythonPackageService = opts.mcpPythonPackageService, this.adapterPackageService = opts.adapterPackageService, this.sandboxPackageService = opts.sandboxPackageService, this.identity = opts.identity, this.providerKeys = opts.providerKeys, this.agentFactory = opts.agentFactory, this.requestShutdown = opts.requestShutdown, this.onTrackedDirectoriesChanged = opts.onTrackedDirectoriesChanged, this.onShutdownStart = opts.onShutdownStart ?? [], this.onShutdown = opts.onShutdown ?? [], this.shouldInstallSignalHandlers = opts.installSignalHandlers ?? !0;
126470
+ this.runtime = opts.runtime, this.host = opts.host ?? "127.0.0.1", this.port = opts.port ?? 7385, this.token = opts.token ?? process.env.ADF_DAEMON_TOKEN ?? null, this.allowedHosts = opts.allowedHosts ?? parseAllowedHostsEnv(process.env.ADF_DAEMON_ALLOWED_HOSTS), this.behindProxy = opts.behindProxy, this.localProof = opts.localProof ?? null, this.pidFile = opts.pidFile, this.logger = opts.logger ?? !1, this.shutdownAgentTimeoutMs = opts.shutdownAgentTimeoutMs ?? envShutdownTimeoutMs() ?? DEFAULT_AGENT_UNLOAD_TIMEOUT_MS, this.computeService = opts.computeService, this.settingsStore = opts.settingsStore, this.eventBus = opts.eventBus, this.wsService = opts.wsService, this.networkService = opts.networkService, this.mesh = opts.mesh, this.mcpPackageService = opts.mcpPackageService, this.mcpPythonPackageService = opts.mcpPythonPackageService, this.adapterPackageService = opts.adapterPackageService, this.sandboxPackageService = opts.sandboxPackageService, this.identity = opts.identity, this.providerKeys = opts.providerKeys, this.agentFactory = opts.agentFactory, this.requestShutdown = opts.requestShutdown, this.onTrackedDirectoriesChanged = opts.onTrackedDirectoriesChanged, this.onShutdownStart = opts.onShutdownStart ?? [], this.onShutdown = opts.onShutdown ?? [], this.shouldInstallSignalHandlers = opts.installSignalHandlers ?? !0;
124033
126471
  }
124034
126472
  async start() {
124035
126473
  if (this.server) return { host: this.host, port: this.port };
@@ -124045,6 +126483,7 @@ var DaemonHost = class {
124045
126483
  eventBus: this.eventBus,
124046
126484
  wsService: this.wsService,
124047
126485
  networkService: this.networkService,
126486
+ mesh: this.mesh,
124048
126487
  mcpPackageService: this.mcpPackageService,
124049
126488
  mcpPythonPackageService: this.mcpPythonPackageService,
124050
126489
  adapterPackageService: this.adapterPackageService,
@@ -124251,7 +126690,7 @@ var DaemonEventBus = class {
124251
126690
 
124252
126691
  // src/main/daemon/file-settings-store.ts
124253
126692
  import { dirname as dirname17, join as join37 } from "node:path";
124254
- import { existsSync as existsSync23, mkdirSync as mkdirSync17, statSync as statSync11 } from "node:fs";
126693
+ import { existsSync as existsSync23, mkdirSync as mkdirSync17, statSync as statSync12 } from "node:fs";
124255
126694
 
124256
126695
  // src/shared/constants/compute-defaults.ts
124257
126696
  var DEFAULT_COMPUTE_SETTINGS = {
@@ -124324,11 +126763,17 @@ function createSettingsDefaults() {
124324
126763
  uiFontCustom: "",
124325
126764
  // Electron zoom factor for the whole window (1 = 100%).
124326
126765
  uiScale: 1,
126766
+ // Small agent avatars: 'orbital' (generated from the DID) or 'emoji'
126767
+ // (config.icon). The home and Overview orbitals ignore it.
126768
+ agentAvatars: "orbital",
124327
126769
  globalSystemPrompt: DEFAULT_BASE_PROMPT,
124328
126770
  // Dynamic instruction templates share the toolPrompts record (dyn_ keys)
124329
126771
  // so they ride the existing settings→executor plumbing.
124330
126772
  toolPrompts: { ...DEFAULT_TOOL_PROMPTS, ...DEFAULT_DYNAMIC_PROMPTS },
124331
126773
  compactionPrompt: DEFAULT_COMPACTION_PROMPT,
126774
+ // On = a release that changes any shipped prompt overwrites all three
126775
+ // prompt keys above (settings-migrations applyPromptUpdates).
126776
+ autoApplyPromptUpdates: !1,
124332
126777
  trackedDirectories: [],
124333
126778
  // Empty = built-in default (Documents/adf-agents), resolved on demand.
124334
126779
  agentsFolder: "",
@@ -124336,6 +126781,9 @@ function createSettingsDefaults() {
124336
126781
  // focused. On by default: an agent blocked on a human the user cannot see
124337
126782
  // is exactly the failure this exists to prevent.
124338
126783
  nativeNotificationsEnabled: !0,
126784
+ // macOS menu bar icon: running agents, requests waiting on the user, and
126785
+ // today's tokens, reachable with the window closed.
126786
+ menuBarEnabled: !0,
124339
126787
  // Owner opt-outs for traffic Studio starts on its own: the background
124340
126788
  // update check (GitHub Releases), the MCP / agent registry fetches (GitHub
124341
126789
  // raw), and provider connection checks. Off = no request; see docs/NETWORK.md.
@@ -124374,9 +126822,29 @@ Your private working memory (\`mind.md\`), snapshotted at the start of each sess
124374
126822
  function applySettingsMigrations(data) {
124375
126823
  let changedKeys = /* @__PURE__ */ new Set();
124376
126824
  migrateBuiltInAdapters(data) && changedKeys.add("adapters"), migrateComputeDefaults(data) && changedKeys.add("compute"), migrateToolPrompts(data) && changedKeys.add("toolPrompts"), migrateSkillsPromptPlaceholder(data) && changedKeys.add("toolPrompts"), migrateGlobalSystemPromptSoul(data) && changedKeys.add("globalSystemPrompt"), migrateGlobalSystemPromptMind(data) && changedKeys.add("globalSystemPrompt");
126825
+ for (let key of applyPromptUpdates(data)) changedKeys.add(key);
124377
126826
  for (let key of migrateUiFontBrandDefault(data)) changedKeys.add(key);
124378
126827
  return { changed: changedKeys.size > 0, changedKeys: [...changedKeys] };
124379
126828
  }
126829
+ var PROMPT_AUTO_UPDATE_KEY = "autoApplyPromptUpdates", PROMPT_DEFAULTS_APPLIED_KEY = "promptDefaultsApplied";
126830
+ function currentPromptDefaults() {
126831
+ return {
126832
+ globalSystemPrompt: DEFAULT_BASE_PROMPT,
126833
+ compactionPrompt: DEFAULT_COMPACTION_PROMPT,
126834
+ toolPrompts: { ...DEFAULT_TOOL_PROMPTS, ...DEFAULT_DYNAMIC_PROMPTS }
126835
+ };
126836
+ }
126837
+ function promptDefaultsFingerprint() {
126838
+ let text2 = JSON.stringify(currentPromptDefaults()), hash = 2166136261;
126839
+ for (let i = 0; i < text2.length; i++)
126840
+ hash ^= text2.charCodeAt(i), hash = Math.imul(hash, 16777619);
126841
+ return (hash >>> 0).toString(16).padStart(8, "0") + ":" + text2.length;
126842
+ }
126843
+ function applyPromptUpdates(data) {
126844
+ if (data[PROMPT_AUTO_UPDATE_KEY] !== !0) return [];
126845
+ let fingerprint = promptDefaultsFingerprint();
126846
+ return data[PROMPT_DEFAULTS_APPLIED_KEY] === fingerprint ? [] : (Object.assign(data, currentPromptDefaults()), data[PROMPT_DEFAULTS_APPLIED_KEY] = fingerprint, console.log("[Settings] Applied updated default prompts"), ["globalSystemPrompt", "compactionPrompt", "toolPrompts", PROMPT_DEFAULTS_APPLIED_KEY]);
126847
+ }
124380
126848
  var UI_FONT_BRAND_MIGRATION_KEY = "uiFontBrandDefaultApplied";
124381
126849
  function migrateUiFontBrandDefault(data) {
124382
126850
  return data[UI_FONT_BRAND_MIGRATION_KEY] === !0 ? [] : (data[UI_FONT_BRAND_MIGRATION_KEY] = !0, data.uiFont === void 0 || data.uiFont === "system" ? (data.uiFont = "inter-tight", [UI_FONT_BRAND_MIGRATION_KEY, "uiFont"]) : [UI_FONT_BRAND_MIGRATION_KEY]);
@@ -124452,7 +126920,7 @@ function recordQuarantine(originalPath, quarantinedTo) {
124452
126920
  }
124453
126921
  function fileFingerprint(path) {
124454
126922
  try {
124455
- let s = statSync11(path);
126923
+ let s = statSync12(path);
124456
126924
  return { mtimeMs: s.mtimeMs, size: s.size };
124457
126925
  } catch {
124458
126926
  return { mtimeMs: 0, size: 0 };
@@ -124696,7 +127164,9 @@ setMandatoryReasoningPersister((modelId) => {
124696
127164
  });
124697
127165
  var basePrompt = settings.get("globalSystemPrompt") ?? "", toolPrompts = settings.get("toolPrompts") ?? {}, compactionPrompt = settings.get("compactionPrompt") ?? void 0, trackedDirs = settings.get("trackedDirectories") ?? [], codeSandboxService = new CodeSandboxService();
124698
127166
  codeSandboxService.setMaxWorkers(settings.get("sandboxMaxWorkers"));
124699
- var sandboxPackagesService = new SandboxPackagesService(), sandboxStdlibService = new SandboxStdlibService(), podmanService = new PodmanService();
127167
+ var sandboxPackagesService = new SandboxPackagesService();
127168
+ codeSandboxService.setUserPackages(sandboxPackagesService.getBasePath(), () => (settings.get("sandboxPackages") ?? []).map((p) => p.name));
127169
+ var sandboxStdlibService = new SandboxStdlibService(), podmanService = new PodmanService();
124700
127170
  setManagedBrowserEnsurer((containerName) => podmanService.ensureManagedBrowserUp(containerName));
124701
127171
  podmanService.setSettingsAccessor(() => readComputeSettings(settings.get("compute")));
124702
127172
  var uvManager = new UvManager(), mcpPackageResolver = new PackageResolver("mcp-servers"), adapterPackageResolver = new PackageResolver("channel-adapters"), uvxPackageResolver = new UvxPackageResolver(uvManager);
@@ -124712,6 +127182,7 @@ settings.get("meshEnabled") !== !1 && meshManager.enableMesh();
124712
127182
  var agentRuntimeBuilder = new AgentRuntimeBuilder({
124713
127183
  settings,
124714
127184
  codeSandboxService,
127185
+ sandboxPackagesService,
124715
127186
  podmanService,
124716
127187
  credentialEnvelopeLockedHint: daemonEncKey ? `Make the owner identity available (\`adf identity restore\` / \`adf identity unlock\`, or /identity in the TUI), or add this daemon's runtime key (${daemonEncKey.pubKeyPath}) to Studio's trusted daemon keys (trustedDaemonEncKeys) and open the agent in Studio once.` : void 0,
124717
127188
  wsConnectionManager,
@@ -124772,6 +127243,7 @@ var loadedAgentEvents = /* @__PURE__ */ new Map(), agentFactory = new DaemonAgen
124772
127243
  providerKeys: ownerIdentity.providerKeys,
124773
127244
  eventBus,
124774
127245
  wsService: wsConnectionManager,
127246
+ mesh: meshManager,
124775
127247
  networkService: {
124776
127248
  getStatus: () => ({
124777
127249
  meshEnabled: meshManager.isEnabled(),