@corenel/cli 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +794 -248
  2. package/package.json +7 -7
package/dist/main.js CHANGED
@@ -454,7 +454,7 @@ var init_fileService = __esm({
454
454
  // ../tools-node/auth.ts
455
455
  import { promises as fs3 } from "node:fs";
456
456
  import { homedir } from "node:os";
457
- import { join as join3 } from "node:path";
457
+ import { join as join3, resolve as resolve2 } from "node:path";
458
458
  import { stateDirName, DEFAULT_API_BASE } from "@corenel/protocol";
459
459
  function corenelDir(base = homedir()) {
460
460
  return join3(base, stateDirName());
@@ -500,6 +500,83 @@ async function writeAuthToken(token, stateDir) {
500
500
  } catch {
501
501
  }
502
502
  }
503
+ function authObjectOf(raw) {
504
+ let parsed;
505
+ try {
506
+ parsed = JSON.parse(raw);
507
+ } catch {
508
+ return null;
509
+ }
510
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
511
+ return parsed;
512
+ }
513
+ function withoutSignIn(doc) {
514
+ const rest = { ...doc };
515
+ for (const k of SIGN_IN_KEYS) delete rest[k];
516
+ return rest;
517
+ }
518
+ function codeOf2(err) {
519
+ const code3 = typeof err === "object" && err !== null ? err.code : void 0;
520
+ return typeof code3 === "string" && code3 ? code3 : "unknown error";
521
+ }
522
+ function authDocLocks() {
523
+ const host = globalThis;
524
+ let m = host[AUTH_DOC_LOCKS];
525
+ if (!m) {
526
+ m = /* @__PURE__ */ new Map();
527
+ host[AUTH_DOC_LOCKS] = m;
528
+ }
529
+ return m;
530
+ }
531
+ async function withAuthDocLock(path, fn) {
532
+ const abs = resolve2(path);
533
+ const key = process.platform === "win32" ? abs.toLowerCase() : abs;
534
+ const locks = authDocLocks();
535
+ const prev = locks.get(key) ?? Promise.resolve();
536
+ let release = () => {
537
+ };
538
+ const mine = new Promise((r) => {
539
+ release = r;
540
+ });
541
+ const tail = prev.then(() => mine);
542
+ locks.set(key, tail);
543
+ await prev;
544
+ try {
545
+ return await fn();
546
+ } finally {
547
+ release();
548
+ if (locks.get(key) === tail) locks.delete(key);
549
+ }
550
+ }
551
+ async function readAuthDocForWrite(path, writer) {
552
+ let bytes;
553
+ try {
554
+ bytes = await fs3.readFile(path);
555
+ } catch (err) {
556
+ if (isMissing(err)) return { kind: "missing" };
557
+ const code3 = codeOf2(err);
558
+ const what = writer === "sign-in" ? "sign-in aborted so the MCP and agent credentials in it are not lost" : "not saved so the credentials in it are not lost";
559
+ throw Object.assign(new Error(`Could not read ${path} (${code3}); ${what}. Close whatever holds it and retry.`), { code: code3 });
560
+ }
561
+ const doc = authObjectOf(bytes.toString("utf8"));
562
+ return doc ? { kind: "doc", doc } : { kind: "corrupt", bytes };
563
+ }
564
+ async function backupCorruptAuthDoc(path, bytes, notify = toStderr) {
565
+ const backup = `${path}.bad-${Date.now()}`;
566
+ await fs3.writeFile(backup, bytes, { mode: 384, flag: "wx" });
567
+ try {
568
+ await fs3.chmod(backup, 384);
569
+ } catch {
570
+ }
571
+ notify(`${path} was not valid JSON; a copy was kept at ${backup} and it was replaced.`);
572
+ return backup;
573
+ }
574
+ async function readForeignAuthSections(path, notify) {
575
+ const read = await readAuthDocForWrite(path, "sign-in");
576
+ if (read.kind === "doc") return withoutSignIn(read.doc);
577
+ if (read.kind === "corrupt") await backupCorruptAuthDoc(path, read.bytes, notify);
578
+ return {};
579
+ }
503
580
  async function writeLoginCredential(token, opts = {}) {
504
581
  const cred = {
505
582
  accessToken: token.trim(),
@@ -510,9 +587,17 @@ async function writeLoginCredential(token, opts = {}) {
510
587
  const credPath = credentialFilePath(opts.stateDir);
511
588
  const tokPath = tokenFilePath(opts.stateDir);
512
589
  await fs3.mkdir(dir, { recursive: true, mode: 448 });
590
+ await withAuthDocLock(credPath, () => publishSignIn(credPath, tokPath, cred, opts.notify ?? toStderr));
591
+ try {
592
+ await fs3.chmod(dir, 448);
593
+ } catch {
594
+ }
595
+ }
596
+ async function publishSignIn(credPath, tokPath, cred, notify) {
597
+ const record = { ...await readForeignAuthSections(credPath, notify), ...cred };
513
598
  const credTmp = `${credPath}.tmp-${process.pid.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
514
599
  const tokTmp = `${tokPath}.tmp-${process.pid.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
515
- await fs3.writeFile(credTmp, `${JSON.stringify(cred, null, 2)}
600
+ await fs3.writeFile(credTmp, `${JSON.stringify(record, null, 2)}
516
601
  `, { mode: 384 });
517
602
  await fs3.writeFile(tokTmp, `${cred.accessToken}
518
603
  `, { mode: 384 });
@@ -538,10 +623,6 @@ async function writeLoginCredential(token, opts = {}) {
538
623
  }
539
624
  throw e;
540
625
  }
541
- try {
542
- await fs3.chmod(dir, 448);
543
- } catch {
544
- }
545
626
  }
546
627
  function isMissing(err) {
547
628
  return typeof err === "object" && err !== null && err.code === "ENOENT";
@@ -591,12 +672,48 @@ async function readCredentialFileRead(stateDir) {
591
672
  return { value: null, unreadable: false };
592
673
  }
593
674
  }
594
- async function clearAllCredentials(stateDir) {
595
- await clearAuthToken(stateDir);
675
+ async function clearAllCredentials(stateDir, opts = {}) {
676
+ const failures = [];
677
+ const tokPath = tokenFilePath(stateDir);
596
678
  try {
597
- await fs3.rm(credentialFilePath(stateDir), { force: true });
598
- } catch {
679
+ await fs3.rm(tokPath, { force: true });
680
+ } catch (err) {
681
+ failures.push(`${tokPath} (${codeOf2(err)})`);
682
+ }
683
+ const credPath = credentialFilePath(stateDir);
684
+ try {
685
+ await withAuthDocLock(credPath, () => clearSignInRecord(credPath, opts.notify ?? toStderr));
686
+ } catch (err) {
687
+ failures.push(`${credPath} (${codeOf2(err)})`);
688
+ }
689
+ if (failures.length) {
690
+ throw new Error(`Could not remove the sign-in from ${failures.join(" and ")}; it is still on disk.`);
691
+ }
692
+ }
693
+ async function clearSignInRecord(path, notify) {
694
+ let raw;
695
+ try {
696
+ raw = await fs3.readFile(path, "utf8");
697
+ } catch (err) {
698
+ if (isMissing(err)) return;
699
+ await fs3.rm(path, { force: true });
700
+ notify("auth.json could not be read and was removed; MCP and agent credentials in it are gone");
701
+ return;
702
+ }
703
+ const doc = authObjectOf(raw);
704
+ const rest = doc ? withoutSignIn(doc) : {};
705
+ if (Object.keys(rest).length > 0) {
706
+ try {
707
+ await writeFilePrivateAtomic(path, `${JSON.stringify(rest, null, 2)}
708
+ `);
709
+ return;
710
+ } catch {
711
+ await fs3.rm(path, { force: true });
712
+ notify("auth.json could not be rewritten and was removed; MCP and agent credentials in it are gone");
713
+ return;
714
+ }
599
715
  }
716
+ await fs3.rm(path, { force: true });
600
717
  }
601
718
  async function resolveNodeCredential(opts = {}) {
602
719
  const env = opts.env ?? process.env;
@@ -644,6 +761,7 @@ async function resolveApiBase(opts = {}) {
644
761
  if (explicit) return { base: explicit, source: "explicit" };
645
762
  const fromEnv = (opts.env ?? process.env)[API_BASE_ENV]?.trim();
646
763
  if (fromEnv) return { base: fromEnv, source: "env" };
764
+ if (opts.ignoreCredential) return { base: DEFAULT_API_BASE, source: "default" };
647
765
  const cred = await resolveNodeCredential({
648
766
  env: {},
649
767
  ...opts.stateDir ? { stateDir: opts.stateDir } : {}
@@ -669,12 +787,17 @@ function maskAccount(account) {
669
787
  function nodeAuthToken(stateDir) {
670
788
  return { getToken: async () => (await resolveNodeCredential(stateDir ? { stateDir } : {})).token };
671
789
  }
672
- var TOKEN_ENV, API_BASE_ENV;
790
+ var TOKEN_ENV, API_BASE_ENV, SIGN_IN_KEYS, toStderr, AUTH_DOC_LOCKS;
673
791
  var init_auth = __esm({
674
792
  "../tools-node/auth.ts"() {
675
793
  "use strict";
676
794
  TOKEN_ENV = "CORENEL_TOKEN";
677
795
  API_BASE_ENV = "CORENEL_API_BASE";
796
+ SIGN_IN_KEYS = ["accessToken", "refreshToken", "expiresAt", "account", "apiBase"];
797
+ toStderr = (line) => {
798
+ console.error(line);
799
+ };
800
+ AUTH_DOC_LOCKS = Symbol.for("corenel.tools-node.authDocLocks");
678
801
  }
679
802
  });
680
803
 
@@ -12547,7 +12670,7 @@ var require_runtime = __commonJS({
12547
12670
  }
12548
12671
  return p && p.lookup(name);
12549
12672
  };
12550
- _proto.resolve = function resolve4(name, forWrite) {
12673
+ _proto.resolve = function resolve5(name, forWrite) {
12551
12674
  var p = forWrite && this.isolateWrites ? void 0 : this.parent;
12552
12675
  var val = this.variables[name];
12553
12676
  if (val !== void 0) {
@@ -14277,7 +14400,7 @@ var require_loader = __commonJS({
14277
14400
  return _EmitterObj.apply(this, arguments) || this;
14278
14401
  }
14279
14402
  var _proto = Loader2.prototype;
14280
- _proto.resolve = function resolve4(from, to) {
14403
+ _proto.resolve = function resolve5(from, to) {
14281
14404
  return path.resolve(path.dirname(from), to);
14282
14405
  };
14283
14406
  _proto.isRelative = function isRelative(filename) {
@@ -17813,10 +17936,10 @@ ${content}` : parentData.content;
17813
17936
  });
17814
17937
  this.registerFiltersOnEnv(env);
17815
17938
  const renderTimeout = 5e3;
17816
- const renderPromise = new Promise((resolve4, reject) => {
17939
+ const renderPromise = new Promise((resolve5, reject) => {
17817
17940
  try {
17818
17941
  const result = env.renderString(content, context.parameters);
17819
- resolve4(result);
17942
+ resolve5(result);
17820
17943
  } catch (error) {
17821
17944
  reject(error);
17822
17945
  }
@@ -18839,7 +18962,7 @@ var init_openBrowser = __esm({
18839
18962
  // ../tools-node/approvalsStore.ts
18840
18963
  import { promises as fs5, realpath } from "node:fs";
18841
18964
  import { createHash, randomBytes as randomBytes2 } from "node:crypto";
18842
- import { basename as basename3, dirname as dirname4, join as join5, relative as relative2, resolve as resolve2 } from "node:path";
18965
+ import { basename as basename3, dirname as dirname4, join as join5, relative as relative2, resolve as resolve3 } from "node:path";
18843
18966
  function approvalsHomeDir(base) {
18844
18967
  return join5(corenelDir(base), "approvals");
18845
18968
  }
@@ -18907,7 +19030,7 @@ async function realOrAncestor(abs) {
18907
19030
  }
18908
19031
  }
18909
19032
  async function workspaceApprovalsId(dir) {
18910
- const real = await realOrAncestor(resolve2(dir));
19033
+ const real = await realOrAncestor(resolve3(dir));
18911
19034
  const norm = process.platform === "win32" ? real.toLowerCase() : real;
18912
19035
  return createHash("sha256").update(norm).digest("hex").slice(0, 32);
18913
19036
  }
@@ -18926,7 +19049,7 @@ function sameIdentity(a, b) {
18926
19049
  return a !== null && b !== null && a.dev === b.dev && a.ino === b.ino;
18927
19050
  }
18928
19051
  async function existingAncestor(abs) {
18929
- let cur = resolve2(abs);
19052
+ let cur = resolve3(abs);
18930
19053
  const rest = [];
18931
19054
  for (; ; ) {
18932
19055
  if (await identity(cur) !== null) return { dir: cur, rest };
@@ -18937,12 +19060,12 @@ async function existingAncestor(abs) {
18937
19060
  }
18938
19061
  }
18939
19062
  async function insideAnyRoot(roots, dir) {
18940
- const target = resolve2(dir);
19063
+ const target = resolve3(dir);
18941
19064
  const realTarget = await realOrAncestor(target);
18942
19065
  const targetAnc = await existingAncestor(realTarget);
18943
19066
  const targetAncId = await identity(targetAnc.dir);
18944
19067
  for (const r of roots) {
18945
- const root = resolve2(r);
19068
+ const root = resolve3(r);
18946
19069
  if (contains(root, target)) return r;
18947
19070
  const realRoot = await realOrAncestor(root);
18948
19071
  if (contains(realRoot, realTarget)) return r;
@@ -18975,19 +19098,130 @@ var init_approvalsStore = __esm({
18975
19098
  }
18976
19099
  });
18977
19100
 
19101
+ // ../tools-node/deviceLogin.ts
19102
+ function deadlineSignal(ms, outer) {
19103
+ const ac = new AbortController();
19104
+ const onOuter = () => ac.abort();
19105
+ const timer = setTimeout(() => ac.abort(), ms);
19106
+ if (outer) {
19107
+ if (outer.aborted) ac.abort();
19108
+ else outer.addEventListener("abort", onOuter, { once: true });
19109
+ }
19110
+ return {
19111
+ signal: ac.signal,
19112
+ dispose: () => {
19113
+ clearTimeout(timer);
19114
+ outer?.removeEventListener("abort", onOuter);
19115
+ }
19116
+ };
19117
+ }
19118
+ async function whoIsThis(base, token, f, outer, deadlineMs = ACCOUNT_LOOKUP_MS) {
19119
+ const { signal, dispose } = deadlineSignal(deadlineMs, outer);
19120
+ try {
19121
+ const res = await f(`${base}/auth/me`, {
19122
+ headers: { Authorization: `Bearer ${token}` },
19123
+ signal
19124
+ });
19125
+ if (!res.ok) return void 0;
19126
+ const body = JSON.parse(await res.text());
19127
+ const email = body?.user?.email;
19128
+ if (typeof email !== "string") return void 0;
19129
+ const trimmed = email.trim();
19130
+ return trimmed ? trimmed : void 0;
19131
+ } catch {
19132
+ return void 0;
19133
+ } finally {
19134
+ dispose();
19135
+ }
19136
+ }
19137
+ function defaultOpen(uri, code3) {
19138
+ process.stdout.write(`
19139
+ To authorize, open: ${uri}
19140
+ and enter the code: ${code3}
19141
+
19142
+ Waiting for authorization\u2026
19143
+ `);
19144
+ }
19145
+ async function deviceLogin(opts) {
19146
+ const f = opts.fetchImpl ?? fetch;
19147
+ const base = opts.base.replace(/\/$/, "");
19148
+ const post = async (path, body) => {
19149
+ const url = `${base}${path}`;
19150
+ const res = await f(url, {
19151
+ method: "POST",
19152
+ headers: { "Content-Type": "application/json" },
19153
+ body: JSON.stringify(body),
19154
+ signal: opts.signal
19155
+ });
19156
+ const text = await res.text();
19157
+ try {
19158
+ return JSON.parse(text);
19159
+ } catch {
19160
+ const looksLikeHtml = /^\s*</.test(text);
19161
+ throw new Error(
19162
+ `${url} answered ${res.status} with ${looksLikeHtml ? "an HTML page" : "a non-JSON body"}, not JSON.` + (res.status === 404 ? `
19163
+ That usually means --base is wrong. The device endpoints live under /api \u2014 try --base ${base.replace(/\/api$/, "")}/api` : "")
19164
+ );
19165
+ }
19166
+ };
19167
+ const dc = await post("/oauth/device/code", { client_id: "corenel-cli", scope: "agent pair" });
19168
+ if (!dc.device_code) throw new Error("device authorization failed");
19169
+ (opts.open ?? defaultOpen)(dc.verification_uri, dc.user_code);
19170
+ let interval = opts.pollMs ?? Math.max(MIN_POLL_MS, (dc.interval ?? 5) * 1e3);
19171
+ const lifetimeMs = (typeof dc.expires_in === "number" && dc.expires_in > 0 ? dc.expires_in : DEFAULT_LIFETIME_S) * 1e3;
19172
+ const deadline = Date.now() + lifetimeMs;
19173
+ for (; ; ) {
19174
+ if (opts.signal?.aborted) throw new Error("login cancelled");
19175
+ if (Date.now() >= deadline) {
19176
+ throw new Error(`the code ${dc.user_code} expired before it was authorized -- run \`corenel login\` again`);
19177
+ }
19178
+ await sleep2(interval);
19179
+ const tok = await post("/oauth/token", {
19180
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
19181
+ device_code: dc.device_code,
19182
+ client_id: "corenel-cli"
19183
+ });
19184
+ if (tok.access_token) {
19185
+ const claimed = typeof tok.account === "string" ? tok.account.trim() : "";
19186
+ const account = claimed || await whoIsThis(base, tok.access_token, f, opts.signal, opts.accountLookupMs);
19187
+ await opts.store(tok.access_token, account || void 0);
19188
+ return tok.access_token;
19189
+ }
19190
+ if (tok.error === "authorization_pending") continue;
19191
+ if (tok.error === "slow_down") {
19192
+ interval += 5e3;
19193
+ continue;
19194
+ }
19195
+ throw new Error(tok.error_description || tok.error || "login failed");
19196
+ }
19197
+ }
19198
+ var sleep2, MIN_POLL_MS, DEFAULT_LIFETIME_S, ACCOUNT_LOOKUP_MS;
19199
+ var init_deviceLogin = __esm({
19200
+ "../tools-node/deviceLogin.ts"() {
19201
+ "use strict";
19202
+ sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
19203
+ MIN_POLL_MS = 1e3;
19204
+ DEFAULT_LIFETIME_S = 900;
19205
+ ACCOUNT_LOOKUP_MS = 5e3;
19206
+ }
19207
+ });
19208
+
18978
19209
  // ../tools-node/index.ts
18979
19210
  var tools_node_exports = {};
18980
19211
  __export(tools_node_exports, {
18981
19212
  API_BASE_ENV: () => API_BASE_ENV,
18982
19213
  NodeFileService: () => NodeFileService,
19214
+ SIGN_IN_KEYS: () => SIGN_IN_KEYS,
18983
19215
  TOKEN_ENV: () => TOKEN_ENV,
18984
19216
  approvalsHomeDir: () => approvalsHomeDir,
18985
19217
  authTokenPath: () => authTokenPath,
19218
+ backupCorruptAuthDoc: () => backupCorruptAuthDoc,
18986
19219
  clearAllCredentials: () => clearAllCredentials,
18987
19220
  clearAuthToken: () => clearAuthToken,
18988
19221
  corenelDir: () => corenelDir,
18989
19222
  credentialDir: () => credentialDir,
18990
19223
  credentialFilePath: () => credentialFilePath,
19224
+ deviceLogin: () => deviceLogin,
18991
19225
  ensurePrivateDir: () => ensurePrivateDir,
18992
19226
  fileTextStore: () => fileTextStore,
18993
19227
  insideAnyRoot: () => insideAnyRoot,
@@ -18997,6 +19231,7 @@ __export(tools_node_exports, {
18997
19231
  nodeAuthToken: () => nodeAuthToken,
18998
19232
  nodePromptHost: () => nodePromptHost,
18999
19233
  openBrowser: () => openBrowser,
19234
+ readAuthDocForWrite: () => readAuthDocForWrite,
19000
19235
  readAuthToken: () => readAuthToken,
19001
19236
  readAuthTokenRead: () => readAuthTokenRead,
19002
19237
  readCredentialFile: () => readCredentialFile,
@@ -19005,8 +19240,11 @@ __export(tools_node_exports, {
19005
19240
  resolveNodeCredential: () => resolveNodeCredential,
19006
19241
  sidecarDir: () => sidecarDir,
19007
19242
  tokenFilePath: () => tokenFilePath,
19243
+ whoIsThis: () => whoIsThis,
19244
+ withAuthDocLock: () => withAuthDocLock,
19008
19245
  workspaceApprovalsId: () => workspaceApprovalsId,
19009
19246
  writeAuthToken: () => writeAuthToken,
19247
+ writeFilePrivateAtomic: () => writeFilePrivateAtomic,
19010
19248
  writeLoginCredential: () => writeLoginCredential
19011
19249
  });
19012
19250
  var init_tools_node = __esm({
@@ -19018,6 +19256,7 @@ var init_tools_node = __esm({
19018
19256
  init_migrateHome();
19019
19257
  init_openBrowser();
19020
19258
  init_approvalsStore();
19259
+ init_deviceLogin();
19021
19260
  }
19022
19261
  });
19023
19262
 
@@ -22828,9 +23067,19 @@ __export(crew_exports, {
22828
23067
  CREW_EXCLUDED_TOOLS: () => CREW_EXCLUDED_TOOLS,
22829
23068
  CREW_SEG: () => CREW_SEG,
22830
23069
  DEPLOYMENT_FILE: () => DEPLOYMENT_FILE,
23070
+ DONTS_DUPLICATE: () => DONTS_DUPLICATE,
23071
+ DONTS_MODEL_INVALID: () => DONTS_MODEL_INVALID,
23072
+ DONTS_TOO_MANY: () => DONTS_TOO_MANY,
23073
+ DONT_BLANK: () => DONT_BLANK,
23074
+ DONT_CONTROL: () => DONT_CONTROL,
23075
+ DONT_TOO_LONG: () => DONT_TOO_LONG,
22831
23076
  MAX_ROOM_BRIEF: () => MAX_ROOM_BRIEF,
23077
+ MEMORY_USERS_DIR: () => MEMORY_USERS_DIR,
23078
+ NOT_IN_YOUR_MEMORY: () => NOT_IN_YOUR_MEMORY,
23079
+ NO_ASKER_MEMORY_READ_ONLY: () => NO_ASKER_MEMORY_READ_ONLY,
22832
23080
  PROJECT_MARKER_FILE: () => PROJECT_MARKER_FILE,
22833
23081
  ROOM_KIND: () => ROOM_KIND,
23082
+ ROOM_MEMORY_READ_ONLY: () => ROOM_MEMORY_READ_ONLY,
22834
23083
  RUN_STATUS_VALUES: () => RUN_STATUS_VALUES,
22835
23084
  RoomReadError: () => RoomReadError,
22836
23085
  RoomStore: () => RoomStore,
@@ -22859,6 +23108,7 @@ __export(crew_exports, {
22859
23108
  deploymentKey: () => deploymentKey,
22860
23109
  deploymentProjectFor: () => deploymentProjectFor,
22861
23110
  deploymentRootFor: () => deploymentRootFor,
23111
+ dontsProblems: () => dontsProblems,
22862
23112
  editFindingByHuman: () => editFindingByHuman,
22863
23113
  emptyDeployment: () => emptyDeployment,
22864
23114
  encodeRoomLine: () => encodeRoomLine,
@@ -22883,6 +23133,8 @@ __export(crew_exports, {
22883
23133
  loadCrewSummaryAt: () => loadCrewSummaryAt,
22884
23134
  makeAgentMemoryProvider: () => makeAgentMemoryProvider,
22885
23135
  makeCrewTurnRuntime: () => makeCrewTurnRuntime,
23136
+ makePartitionedMemoryProvider: () => makePartitionedMemoryProvider,
23137
+ makeReadOnlyMemoryProvider: () => makeReadOnlyMemoryProvider,
22886
23138
  matchFileChange: () => matchFileChange,
22887
23139
  matchRunComplete: () => matchRunComplete,
22888
23140
  newCrewDraft: () => newCrewDraft,
@@ -22921,6 +23173,7 @@ __export(crew_exports, {
22921
23173
  takeTurn: () => takeTurn,
22922
23174
  tightenAutonomy: () => tightenAutonomy,
22923
23175
  turnCosts: () => turnCosts,
23176
+ userMemoryRoot: () => userMemoryRoot,
22924
23177
  validateRoomRequest: () => validateRoomRequest,
22925
23178
  writeCrewAgentAt: () => writeCrewAgentAt
22926
23179
  });
@@ -22945,16 +23198,25 @@ function parseFrontmatter(text) {
22945
23198
 
22946
23199
  // ../crew/policy.ts
22947
23200
  var import_yaml2 = __toESM(require_dist(), 1);
22948
- var LEVELS = ["allow", "ask", "deny"];
23201
+ var LEVELS = ["allow", "ask", "owner", "deny"];
22949
23202
  var UNATTENDED = ["deny", "park", "allow"];
22950
23203
  function asLevel(v) {
22951
23204
  return typeof v === "string" && LEVELS.includes(v) ? v : void 0;
22952
23205
  }
23206
+ function asDefaultLevel(v) {
23207
+ const l = asLevel(v);
23208
+ return l === "owner" ? void 0 : l;
23209
+ }
22953
23210
  function asStringArray(v) {
22954
23211
  if (!Array.isArray(v)) return void 0;
22955
23212
  const out = v.filter((x) => typeof x === "string");
22956
23213
  return out.length ? out : void 0;
22957
23214
  }
23215
+ function asDonts(v) {
23216
+ if (!Array.isArray(v)) return void 0;
23217
+ const out = v.filter((x) => typeof x === "string" || typeof x === "number" || typeof x === "boolean").map((x) => String(x).trim()).filter((s) => s !== "");
23218
+ return out.length ? out : void 0;
23219
+ }
22958
23220
  function coercePermissions(v) {
22959
23221
  const out = {};
22960
23222
  if (v && typeof v === "object" && !Array.isArray(v)) {
@@ -22986,6 +23248,8 @@ function parsePolicyYaml(text, agentName) {
22986
23248
  const rawPush = raw.pushWaitMs;
22987
23249
  const pushWaitMs = typeof rawPush === "number" && Number.isFinite(rawPush) && rawPush > 0 ? rawPush : void 0;
22988
23250
  const skills = Array.isArray(raw.skills) ? raw.skills.filter((s) => typeof s === "string" && s.trim() !== "").map((s) => s.trim()) : void 0;
23251
+ const donts = asDonts(raw.donts);
23252
+ const dontsModel = typeof raw.donts_model === "string" && raw.donts_model.trim() ? raw.donts_model.trim() : void 0;
22989
23253
  const policy = {
22990
23254
  version: 1,
22991
23255
  name: typeof raw.name === "string" ? raw.name : `crew:${agentName}`,
@@ -23005,15 +23269,17 @@ function parsePolicyYaml(text, agentName) {
23005
23269
  * sub-agents and make outbound search calls without being asked. Say it
23006
23270
  * here rather than let the word "reads" imply a narrower grant than the
23007
23271
  * code makes. */
23008
- defaultPermission: asLevel(raw.defaultPermission),
23009
- mutatingDefault: asLevel(raw.mutatingDefault) ?? "deny",
23272
+ defaultPermission: asDefaultLevel(raw.defaultPermission),
23273
+ mutatingDefault: asDefaultLevel(raw.mutatingDefault) ?? "deny",
23010
23274
  permissions: coercePermissions(raw.permissions),
23011
23275
  maxTurns: typeof raw.maxTurns === "number" ? raw.maxTurns : void 0,
23012
23276
  maxToolCalls: typeof raw.maxToolCalls === "number" ? raw.maxToolCalls : void 0,
23013
23277
  unattended,
23014
23278
  parkTimeoutMs,
23015
23279
  ...pushWaitMs !== void 0 ? { pushWaitMs } : {},
23016
- ...skills && skills.length ? { skills } : {}
23280
+ ...skills && skills.length ? { skills } : {},
23281
+ ...donts ? { donts } : {},
23282
+ ...dontsModel ? { donts_model: dontsModel } : {}
23017
23283
  };
23018
23284
  if (toolsAllow || toolsDeny) {
23019
23285
  policy.tools = { ...toolsAllow ? { allow: toolsAllow } : {}, ...toolsDeny ? { deny: toolsDeny } : {} };
@@ -23021,6 +23287,47 @@ function parsePolicyYaml(text, agentName) {
23021
23287
  return policy;
23022
23288
  }
23023
23289
 
23290
+ // ../crew/donts.ts
23291
+ import { DONTS_MAX, DONT_MAX_CHARS, hasControlChars } from "@corenel/protocol";
23292
+ var DONT_BLANK = "A don't cannot be blank.";
23293
+ var DONT_TOO_LONG = `A don't can be at most ${DONT_MAX_CHARS} characters.`;
23294
+ var DONTS_TOO_MANY = `An agent can have at most ${DONTS_MAX} don'ts.`;
23295
+ var DONTS_DUPLICATE = "Each don't must be different from the others.";
23296
+ var DONT_CONTROL = "A don't cannot contain control characters.";
23297
+ var DONTS_MODEL_INVALID = "The model for checking don'ts must be a model id.";
23298
+ function dontsProblems(policy) {
23299
+ const out = /* @__PURE__ */ new Set();
23300
+ const list = policy.donts ?? [];
23301
+ if (list.length > DONTS_MAX) out.add(DONTS_TOO_MANY);
23302
+ const seen = /* @__PURE__ */ new Set();
23303
+ let blank = false;
23304
+ let long = false;
23305
+ let dup = false;
23306
+ let control = false;
23307
+ for (const raw of list) {
23308
+ const t = raw.trim();
23309
+ if (t === "") {
23310
+ blank = true;
23311
+ continue;
23312
+ }
23313
+ if (t.length > DONT_MAX_CHARS) long = true;
23314
+ if (hasControlChars(t)) control = true;
23315
+ const key = t.toLowerCase();
23316
+ if (seen.has(key)) dup = true;
23317
+ seen.add(key);
23318
+ }
23319
+ if (blank) out.add(DONT_BLANK);
23320
+ if (long) out.add(DONT_TOO_LONG);
23321
+ if (dup) out.add(DONTS_DUPLICATE);
23322
+ if (control) out.add(DONT_CONTROL);
23323
+ if (policy.donts_model !== void 0) {
23324
+ const m = policy.donts_model.trim();
23325
+ if (m === "" || hasControlChars(m)) out.add(DONTS_MODEL_INVALID);
23326
+ }
23327
+ const order = [DONTS_TOO_MANY, DONT_BLANK, DONT_TOO_LONG, DONTS_DUPLICATE, DONT_CONTROL, DONTS_MODEL_INVALID];
23328
+ return order.filter((s) => out.has(s));
23329
+ }
23330
+
23024
23331
  // ../crew/paths.ts
23025
23332
  import { stateDirName as stateDirName2 } from "@corenel/protocol";
23026
23333
  var CREW_SEG = "crew";
@@ -23098,7 +23405,7 @@ function applyAutonomy(def, preset) {
23098
23405
  const orchestration = preset === "interactive" ? { ...rest, checkIn: true } : rest;
23099
23406
  return { policy: { ...def.policy, permissions }, orchestration };
23100
23407
  }
23101
- var ORDER = { deny: 0, ask: 1, allow: 2 };
23408
+ var ORDER = { deny: 0, owner: 1, ask: 2, allow: 3 };
23102
23409
  var LABEL = { interactive: "Interactive", hitl: "HITL", autonomous: "Autonomous" };
23103
23410
  var PRESETS = ["interactive", "hitl", "autonomous"];
23104
23411
  function isAutonomyPreset(v) {
@@ -23346,6 +23653,8 @@ function serializePolicy(policy) {
23346
23653
  if (policy.mutatingDefault) ordered.mutatingDefault = policy.mutatingDefault;
23347
23654
  if (policy.permissions && Object.keys(policy.permissions).length) ordered.permissions = policy.permissions;
23348
23655
  if (policy.skills && policy.skills.length) ordered.skills = policy.skills;
23656
+ if (policy.donts && policy.donts.length) ordered.donts = policy.donts;
23657
+ if (policy.donts_model) ordered.donts_model = policy.donts_model;
23349
23658
  if (policy.tools) ordered.tools = policy.tools;
23350
23659
  if (typeof policy.maxTurns === "number") ordered.maxTurns = policy.maxTurns;
23351
23660
  if (typeof policy.maxToolCalls === "number") ordered.maxToolCalls = policy.maxToolCalls;
@@ -23421,9 +23730,24 @@ function stripWorkRoots(settings) {
23421
23730
  const { workRoots: _workRoots, ...rest } = settings;
23422
23731
  return rest;
23423
23732
  }
23733
+ async function dontsUnchanged(files, dir, def) {
23734
+ let text;
23735
+ try {
23736
+ text = await files.read(`${dir}/POLICY.yaml`);
23737
+ } catch {
23738
+ return false;
23739
+ }
23740
+ const stored = parsePolicyYaml(text, def.name).donts ?? [];
23741
+ const next = (def.policy.donts ?? []).map((d) => d.trim());
23742
+ return stored.length === next.length && stored.every((d, i) => d === next[i]);
23743
+ }
23424
23744
  async function writeCrewAgentAt(files, root, def) {
23425
23745
  if (!isValidAgentName(def.name)) throw new Error(`invalid crew agent name: ${def.name}`);
23426
23746
  const dir = `${root}/${def.name}`;
23747
+ if (!await dontsUnchanged(files, dir, def)) {
23748
+ const problems = dontsProblems(def.policy);
23749
+ if (problems.length) throw new Error(problems.join(" "));
23750
+ }
23427
23751
  await put(files, `${dir}/AGENT.md`, serializeAgentMd(def));
23428
23752
  await put(files, `${dir}/SOUL.md`, def.soul);
23429
23753
  await put(files, `${dir}/PERSONA.md`, def.persona);
@@ -23553,6 +23877,7 @@ function crewBudgetConstraints(def) {
23553
23877
  };
23554
23878
  }
23555
23879
  var MEMORY_BLOCK = registerSyncConfig("crew/memory.md");
23880
+ var MEMORY_TOOLS_ONLY_BLOCK = registerSyncConfig("crew/memory-tools-only.md");
23556
23881
  var SELF_DIRECTION_BLOCK = registerSyncConfig("crew/self-direction.md");
23557
23882
  var CHECK_IN_BLOCK = "--- Check-ins ---\nYou are in interactive mode. After each step of the approved plan (each call_agent result, each sidecar_run_check, each git step), write the user a short check-in: what just happened, what is next, and anything that changes the plan. Then continue; do not end your turn to wait. The user can stop the run at any time, and submit_plan, sidecar_git_push and sidecar_open_pr still ask for approval.";
23558
23883
  function identityBlock() {
@@ -23574,10 +23899,17 @@ ${def.soul.trim()}`);
23574
23899
  ${def.persona.trim()}`);
23575
23900
  if (def.instructions.trim()) parts.push(`--- Instructions ---
23576
23901
  ${def.instructions.trim()}`);
23902
+ if (memoryLocation === null) {
23903
+ parts.push(readConfigSyncRendered(MEMORY_TOOLS_ONLY_BLOCK));
23904
+ return withTail(def, parts);
23905
+ }
23577
23906
  const location = memoryLocation ?? "memory/";
23578
23907
  const dir = location.replace(/\/+$/, "");
23579
23908
  const index = `${dir}/MEMORY.md`;
23580
23909
  parts.push(readConfigSyncRendered(MEMORY_BLOCK, { dir, index }));
23910
+ return withTail(def, parts);
23911
+ }
23912
+ function withTail(def, parts) {
23581
23913
  const selfDirection = selfDirectionBlock(def);
23582
23914
  if (selfDirection) parts.push(selfDirection);
23583
23915
  if (def.orchestration?.checkIn === true) parts.push(CHECK_IN_BLOCK);
@@ -23689,6 +24021,9 @@ var RUN_STATUS_ALIASES = [
23689
24021
  ["max_tokens", "stuck"],
23690
24022
  ["refusal", "policy"],
23691
24023
  ["content_filter", "policy"],
24024
+ // Team don'ts (spec 2026-10-07 Part F): the agent's own rules stopped the
24025
+ // run. A guard refusing to let it finish, the same fact `policy` names.
24026
+ ["donts", "policy"],
23692
24027
  ["interrupted", "aborted"]
23693
24028
  ];
23694
24029
  var RUN_STATUS_BY_RAW = new Map([
@@ -23905,21 +24240,27 @@ function grantOfferedTools(policy, offered) {
23905
24240
  }
23906
24241
 
23907
24242
  // ../crew/memory.ts
24243
+ import { MEMORY_USERS_DIR } from "@corenel/protocol";
23908
24244
  import { makeFactStore } from "@corenel/harness/memory/store";
23909
24245
  function agentMemoryRoot(crewRoot2, agentName) {
23910
24246
  return `${crewRoot2}/${agentName}/memory`;
23911
24247
  }
24248
+ var USER_KEY = /^[0-9a-f]{64}$/;
24249
+ function userMemoryRoot(sharedRoot, userKey) {
24250
+ if (!USER_KEY.test(userKey)) throw new Error("invalid memory partition key");
24251
+ return `${sharedRoot}/${MEMORY_USERS_DIR}/${userKey}`;
24252
+ }
24253
+ var toItem = (f) => ({
24254
+ id: f.name,
24255
+ name: f.name,
24256
+ type: f.type,
24257
+ description: f.description,
24258
+ text: f.body,
24259
+ tags: [],
24260
+ createdAt: f.created
24261
+ });
23912
24262
  function makeAgentMemoryProvider(getFiles, memoryRoot) {
23913
24263
  const store = makeFactStore(getFiles, memoryRoot);
23914
- const toItem = (f) => ({
23915
- id: f.name,
23916
- name: f.name,
23917
- type: f.type,
23918
- description: f.description,
23919
- text: f.body,
23920
- tags: [],
23921
- createdAt: f.created
23922
- });
23923
24264
  return {
23924
24265
  async save(input) {
23925
24266
  return toItem(await store.save(input));
@@ -23943,6 +24284,68 @@ function makeAgentMemoryProvider(getFiles, memoryRoot) {
23943
24284
  }
23944
24285
  };
23945
24286
  }
24287
+ var NOT_IN_YOUR_MEMORY = "not in your memory: that name is shared memory, which this run cannot change";
24288
+ var ROOM_MEMORY_READ_ONLY = "in a room, memory is read only: a room has no single asker";
24289
+ var NO_ASKER_MEMORY_READ_ONLY = "memory is read only for this run: it has no verified asker";
24290
+ function makeReadOnlyMemoryProvider(getFiles, memoryRoot, refusal) {
24291
+ const inner = makeAgentMemoryProvider(getFiles, memoryRoot);
24292
+ return {
24293
+ async save() {
24294
+ throw new Error(refusal);
24295
+ },
24296
+ read: (name) => inner.read(name),
24297
+ recall: (query, limit) => inner.recall(query, limit),
24298
+ list: (limit) => inner.list(limit),
24299
+ async forget() {
24300
+ throw new Error(refusal);
24301
+ },
24302
+ core: () => inner.core ? inner.core() : Promise.resolve({ items: [], archivedCount: 0 })
24303
+ };
24304
+ }
24305
+ function makePartitionedMemoryProvider(getFiles, sharedRoot, userKey) {
24306
+ const shared = makeFactStore(getFiles, sharedRoot);
24307
+ const mine = makeFactStore(getFiles, userMemoryRoot(sharedRoot, userKey));
24308
+ const ownNames = async () => new Set(await mine.names());
24309
+ return {
24310
+ async save(input) {
24311
+ return toItem(await mine.save(input));
24312
+ },
24313
+ async read(name) {
24314
+ const hit = await mine.read(name) ?? await shared.read(name);
24315
+ return hit ? toItem(hit) : null;
24316
+ },
24317
+ async recall(query, limit = 5) {
24318
+ const [m, s, own] = await Promise.all([mine.rankCandidates(query, limit), shared.rankCandidates(query, limit), ownNames()]);
24319
+ const merged = [
24320
+ ...m.map((c) => ({ ...c, from: "mine" })),
24321
+ ...s.filter((c) => !own.has(c.name)).map((c) => ({ ...c, from: "shared" }))
24322
+ ].sort((a, b) => b.score - a.score).slice(0, limit);
24323
+ const [mh, sh] = await Promise.all([
24324
+ mine.hydrateRecalled(merged.filter((c) => c.from === "mine").map((c) => c.name)),
24325
+ shared.hydrateRecalled(merged.filter((c) => c.from === "shared").map((c) => c.name))
24326
+ ]);
24327
+ const byName = /* @__PURE__ */ new Map();
24328
+ for (const f of sh) byName.set(f.name, f);
24329
+ for (const f of mh) byName.set(f.name, f);
24330
+ return merged.map((c) => byName.get(c.name)).filter((f) => f !== void 0).map(toItem);
24331
+ },
24332
+ async list(limit) {
24333
+ const [m, s, own] = await Promise.all([mine.list(limit), shared.list(limit), ownNames()]);
24334
+ return [...m, ...s.filter((f) => !own.has(f.name))].sort((a, b) => b.updated - a.updated).slice(0, limit ?? 50).map(toItem);
24335
+ },
24336
+ async forget(name) {
24337
+ if (!(await ownNames()).has(name) && (await shared.names()).includes(name)) throw new Error(NOT_IN_YOUR_MEMORY);
24338
+ return mine.forget(name);
24339
+ },
24340
+ async core() {
24341
+ const [m, s, own] = await Promise.all([mine.core(), shared.core(), ownNames()]);
24342
+ return {
24343
+ items: [...m.facts, ...s.facts.filter((f) => !own.has(f.name))].map(toItem),
24344
+ archivedCount: m.archivedCount + s.archivedCount
24345
+ };
24346
+ }
24347
+ };
24348
+ }
23946
24349
 
23947
24350
  // ../crew/room/types.ts
23948
24351
  function roleOf(p) {
@@ -24808,107 +25211,8 @@ function tokenGuard(getToken) {
24808
25211
  };
24809
25212
  }
24810
25213
 
24811
- // src/login.ts
24812
- var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
24813
- var MIN_POLL_MS = 1e3;
24814
- var DEFAULT_LIFETIME_S = 900;
24815
- var ACCOUNT_LOOKUP_MS = 5e3;
24816
- function deadlineSignal(ms, outer) {
24817
- const ac = new AbortController();
24818
- const onOuter = () => ac.abort();
24819
- const timer = setTimeout(() => ac.abort(), ms);
24820
- if (outer) {
24821
- if (outer.aborted) ac.abort();
24822
- else outer.addEventListener("abort", onOuter, { once: true });
24823
- }
24824
- return {
24825
- signal: ac.signal,
24826
- dispose: () => {
24827
- clearTimeout(timer);
24828
- outer?.removeEventListener("abort", onOuter);
24829
- }
24830
- };
24831
- }
24832
- async function whoIsThis(base, token, f, outer, deadlineMs = ACCOUNT_LOOKUP_MS) {
24833
- const { signal, dispose } = deadlineSignal(deadlineMs, outer);
24834
- try {
24835
- const res = await f(`${base}/auth/me`, {
24836
- headers: { Authorization: `Bearer ${token}` },
24837
- signal
24838
- });
24839
- if (!res.ok) return void 0;
24840
- const body = JSON.parse(await res.text());
24841
- const email = body?.user?.email;
24842
- if (typeof email !== "string") return void 0;
24843
- const trimmed = email.trim();
24844
- return trimmed ? trimmed : void 0;
24845
- } catch {
24846
- return void 0;
24847
- } finally {
24848
- dispose();
24849
- }
24850
- }
24851
- function defaultOpen(uri, code3) {
24852
- process.stdout.write(`
24853
- To authorize, open: ${uri}
24854
- and enter the code: ${code3}
24855
-
24856
- Waiting for authorization\u2026
24857
- `);
24858
- }
24859
- async function deviceLogin(opts) {
24860
- const f = opts.fetchImpl ?? fetch;
24861
- const base = opts.base.replace(/\/$/, "");
24862
- const post = async (path, body) => {
24863
- const url = `${base}${path}`;
24864
- const res = await f(url, {
24865
- method: "POST",
24866
- headers: { "Content-Type": "application/json" },
24867
- body: JSON.stringify(body),
24868
- signal: opts.signal
24869
- });
24870
- const text = await res.text();
24871
- try {
24872
- return JSON.parse(text);
24873
- } catch {
24874
- const looksLikeHtml = /^\s*</.test(text);
24875
- throw new Error(
24876
- `${url} answered ${res.status} with ${looksLikeHtml ? "an HTML page" : "a non-JSON body"}, not JSON.` + (res.status === 404 ? `
24877
- That usually means --base is wrong. The device endpoints live under /api \u2014 try --base ${base.replace(/\/api$/, "")}/api` : "")
24878
- );
24879
- }
24880
- };
24881
- const dc = await post("/oauth/device/code", { client_id: "corenel-cli", scope: "agent pair" });
24882
- if (!dc.device_code) throw new Error("device authorization failed");
24883
- (opts.open ?? defaultOpen)(dc.verification_uri, dc.user_code);
24884
- let interval = opts.pollMs ?? Math.max(MIN_POLL_MS, (dc.interval ?? 5) * 1e3);
24885
- const lifetimeMs = (typeof dc.expires_in === "number" && dc.expires_in > 0 ? dc.expires_in : DEFAULT_LIFETIME_S) * 1e3;
24886
- const deadline = Date.now() + lifetimeMs;
24887
- for (; ; ) {
24888
- if (opts.signal?.aborted) throw new Error("login cancelled");
24889
- if (Date.now() >= deadline) {
24890
- throw new Error(`the code ${dc.user_code} expired before it was authorized -- run \`corenel login\` again`);
24891
- }
24892
- await sleep2(interval);
24893
- const tok = await post("/oauth/token", {
24894
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
24895
- device_code: dc.device_code,
24896
- client_id: "corenel-cli"
24897
- });
24898
- if (tok.access_token) {
24899
- const claimed = typeof tok.account === "string" ? tok.account.trim() : "";
24900
- const account = claimed || await whoIsThis(base, tok.access_token, f, opts.signal, opts.accountLookupMs);
24901
- await opts.store(tok.access_token, account || void 0);
24902
- return tok.access_token;
24903
- }
24904
- if (tok.error === "authorization_pending") continue;
24905
- if (tok.error === "slow_down") {
24906
- interval += 5e3;
24907
- continue;
24908
- }
24909
- throw new Error(tok.error_description || tok.error || "login failed");
24910
- }
24911
- }
25214
+ // src/cli.ts
25215
+ init_tools_node();
24912
25216
 
24913
25217
  // src/runAgent.ts
24914
25218
  import { runAgent } from "@corenel/harness/core/loop";
@@ -24952,11 +25256,13 @@ async function apiBase(explicit, opts = {}) {
24952
25256
  return (await resolveCliApiBase(explicit, opts)).base;
24953
25257
  }
24954
25258
  async function resolveCliApiBase(explicit, opts = {}) {
25259
+ const env = opts.env ?? process.env;
24955
25260
  const resolved = applySourceRunDevBase(
24956
25261
  await resolveApiBase({
24957
25262
  explicit,
24958
- env: opts.env ?? process.env,
24959
- ...opts.stateDir ? { stateDir: opts.stateDir } : {}
25263
+ env,
25264
+ ...opts.stateDir ? { stateDir: opts.stateDir } : {},
25265
+ ...opts.ignoreCredential ? { ignoreCredential: true } : {}
24960
25266
  }),
24961
25267
  opts.sourceRun ?? sourceRunDefault
24962
25268
  );
@@ -25011,6 +25317,13 @@ function guestCrewRoot() {
25011
25317
  function guestAgentMemoryRoot(agent) {
25012
25318
  return agentMemoryRoot(guestCrewRoot(), agent);
25013
25319
  }
25320
+ function guestMemoryFor(files, agent, mode) {
25321
+ const root = guestAgentMemoryRoot(agent);
25322
+ if (mode?.kind === "read-only") {
25323
+ return { provider: makeReadOnlyMemoryProvider(() => files, root, NO_ASKER_MEMORY_READ_ONLY), promptRoot: null };
25324
+ }
25325
+ return { provider: makeAgentMemoryProvider(() => files, root), promptRoot: root };
25326
+ }
25014
25327
  function mutatingCeiling(tools, defaultLevel = "deny") {
25015
25328
  const overrides = {};
25016
25329
  for (const t of tools) if (!t.mutates || t.roomSafe) overrides[toolAddress3(t)] = "allow";
@@ -25027,8 +25340,7 @@ function eventLine(e) {
25027
25340
  async function runCrewAgentCli(opts) {
25028
25341
  const files = new NodeFileService(opts.cwd);
25029
25342
  const root = guestCrewRoot();
25030
- const memoryRoot = guestAgentMemoryRoot(opts.agent);
25031
- const memory = makeAgentMemoryProvider(() => files, memoryRoot);
25343
+ const { provider: memory, promptRoot: memoryRoot } = guestMemoryFor(files, opts.agent, opts.memory);
25032
25344
  const def = await loadCrewAgentAtIfExists(files, root, opts.agent, "workspace");
25033
25345
  if (!def) {
25034
25346
  opts.warn(`no such agent "${opts.agent}" under ${opts.cwd}/${root}`);
@@ -25059,13 +25371,13 @@ ${extra}` : base;
25059
25371
  } catch {
25060
25372
  }
25061
25373
  const realConsole = { log: console.log, info: console.info, warn: console.warn, debug: console.debug };
25062
- const toStderr = (...parts) => {
25374
+ const toStderr2 = (...parts) => {
25063
25375
  opts.warn(parts.map((x) => typeof x === "string" ? x : JSON.stringify(x)).join(" "));
25064
25376
  };
25065
- console.log = toStderr;
25066
- console.info = toStderr;
25067
- console.warn = toStderr;
25068
- console.debug = toStderr;
25377
+ console.log = toStderr2;
25378
+ console.info = toStderr2;
25379
+ console.warn = toStderr2;
25380
+ console.debug = toStderr2;
25069
25381
  const ac = new AbortController();
25070
25382
  const effectivePolicy = opts.policy ?? def.policy ?? STANDARD_POLICY;
25071
25383
  const operatorUnattended = opts.policy?.unattended;
@@ -25239,10 +25551,10 @@ async function runExtension(ext, rest, look = {}) {
25239
25551
  process.on("SIGINT", onInt);
25240
25552
  process.on("SIGTERM", onTerm);
25241
25553
  try {
25242
- return await new Promise((resolve4, reject) => {
25554
+ return await new Promise((resolve5, reject) => {
25243
25555
  child.on("error", reject);
25244
25556
  child.on("exit", (code3, signal) => {
25245
- resolve4(code3 ?? (signal === "SIGINT" ? 130 : signal ? 143 : 1));
25557
+ resolve5(code3 ?? (signal === "SIGINT" ? 130 : signal ? 143 : 1));
25246
25558
  });
25247
25559
  });
25248
25560
  } finally {
@@ -25374,8 +25686,8 @@ init_tools_node();
25374
25686
  function hintOf(token) {
25375
25687
  return maskToken(token);
25376
25688
  }
25377
- async function authState(resolve4 = (env2) => resolveNodeCredential({ env: env2 }), env = process.env) {
25378
- const r = await resolve4(env);
25689
+ async function authState(resolve5 = (env2) => resolveNodeCredential({ env: env2 }), env = process.env) {
25690
+ const r = await resolve5(env);
25379
25691
  if (!r.token) return { source: "none", path: authTokenPath() };
25380
25692
  const source = r.source === "env" ? "env" : "file";
25381
25693
  return { source, path: r.path ?? authTokenPath(), hint: hintOf(r.token) };
@@ -25405,13 +25717,21 @@ async function runAuth(out = (s) => process.stdout.write(s)) {
25405
25717
  }
25406
25718
  async function runLogout(out = (s) => process.stdout.write(s)) {
25407
25719
  const before = await authState();
25408
- await clearAllCredentials();
25720
+ try {
25721
+ await clearAllCredentials();
25722
+ } catch (err) {
25723
+ out(`Sign-out did not complete: ${err instanceof Error ? err.message : String(err)}
25724
+ `);
25725
+ out(" This machine is still signed in. Close whatever holds the file and run `corenel logout` again.\n");
25726
+ return 1;
25727
+ }
25409
25728
  if (before.source === "file") out(`Signed out. Removed ${before.path}
25410
25729
  `);
25411
25730
  else out("Nothing to forget \u2014 no stored token.\n");
25412
25731
  if (process.env.CORENEL_TOKEN) {
25413
25732
  out(" NOTE: CORENEL_TOKEN is still set in this environment, so you are still authenticated.\n");
25414
25733
  }
25734
+ return 0;
25415
25735
  }
25416
25736
 
25417
25737
  // src/doctor.ts
@@ -25486,7 +25806,11 @@ var KNOWN_KEYS = [
25486
25806
  "audit",
25487
25807
  "sendTelemetry",
25488
25808
  "approvalsThreshold",
25489
- "approvalsCaptureRawArgs"
25809
+ "approvalsCaptureRawArgs",
25810
+ "speech",
25811
+ "speechModel",
25812
+ "peerCallTimeoutMs",
25813
+ "peers"
25490
25814
  ];
25491
25815
  function daemonConfigPath(stateDir) {
25492
25816
  return join8(stateDir, "daemon.json");
@@ -25619,12 +25943,19 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
25619
25943
  "allow-shell",
25620
25944
  "allow-mutations",
25621
25945
  // see hasAllowMutationsFlag() below
25946
+ "memory-read-only",
25947
+ // corenel run-agent: the daemon's read-only memory mode
25622
25948
  "global"
25623
25949
  // corenel approvals revoke/clear-log --global
25624
25950
  ]);
25625
25951
  function isBooleanFlag(name) {
25626
25952
  return BOOLEAN_FLAGS.has(name);
25627
25953
  }
25954
+ function consumesNext(parsed, next) {
25955
+ if (parsed.value !== void 0 || isBooleanFlag(parsed.name)) return false;
25956
+ if (parsed.name === "resume") return next !== void 0 && !next.startsWith("--");
25957
+ return true;
25958
+ }
25628
25959
  function classifyToken(tok) {
25629
25960
  if (tok === "=") throw new MalformedFlagError(tok);
25630
25961
  if (!tok.startsWith("--")) return null;
@@ -25638,7 +25969,11 @@ function classifyToken(tok) {
25638
25969
  function flag(argv, name, fallback) {
25639
25970
  for (let i = 0; i < argv.length; i++) {
25640
25971
  const parsed = classifyToken(argv[i]);
25641
- if (!parsed || parsed.name !== name) continue;
25972
+ if (!parsed) continue;
25973
+ if (parsed.name !== name) {
25974
+ if (consumesNext(parsed, argv[i + 1])) i++;
25975
+ continue;
25976
+ }
25642
25977
  if (parsed.value !== void 0) return parsed.value;
25643
25978
  if (!argv[i + 1]) throw new MalformedFlagError(argv[i]);
25644
25979
  return argv[i + 1];
@@ -25650,7 +25985,7 @@ function forEachPositional(argv, visit) {
25650
25985
  const tok = argv[i];
25651
25986
  const parsed = classifyToken(tok);
25652
25987
  if (parsed) {
25653
- if (parsed.value === void 0 && !isBooleanFlag(parsed.name)) i++;
25988
+ if (consumesNext(parsed, argv[i + 1])) i++;
25654
25989
  continue;
25655
25990
  }
25656
25991
  visit(tok);
@@ -25674,11 +26009,10 @@ function hasBooleanFlag(argv, name) {
25674
26009
  for (let i = 0; i < argv.length; i++) {
25675
26010
  const parsed = classifyToken(argv[i]);
25676
26011
  if (!parsed) continue;
25677
- if (parsed.value === void 0 && !isBooleanFlag(parsed.name)) {
25678
- i++;
26012
+ if (parsed.name !== name) {
26013
+ if (consumesNext(parsed, argv[i + 1])) i++;
25679
26014
  continue;
25680
26015
  }
25681
- if (parsed.name !== name) continue;
25682
26016
  if (isOnValue(parsed.value)) return true;
25683
26017
  }
25684
26018
  return false;
@@ -25697,10 +26031,13 @@ function resumeFlag(argv) {
25697
26031
  return void 0;
25698
26032
  }
25699
26033
  function hasValueFlag(argv, name) {
25700
- return argv.some((tok) => {
25701
- const parsed = classifyToken(tok);
25702
- return parsed !== null && parsed.name === name;
25703
- });
26034
+ for (let i = 0; i < argv.length; i++) {
26035
+ const parsed = classifyToken(argv[i]);
26036
+ if (!parsed) continue;
26037
+ if (parsed.name === name) return true;
26038
+ if (consumesNext(parsed, argv[i + 1])) i++;
26039
+ }
26040
+ return false;
25704
26041
  }
25705
26042
 
25706
26043
  // src/doctor.ts
@@ -25980,12 +26317,12 @@ async function runRepl(deps) {
25980
26317
  rl.on("close", () => {
25981
26318
  closed = true;
25982
26319
  });
25983
- const ask = (q) => closed ? Promise.resolve(null) : new Promise((resolve4) => {
25984
- const onClose = () => resolve4(null);
26320
+ const ask = (q) => closed ? Promise.resolve(null) : new Promise((resolve5) => {
26321
+ const onClose = () => resolve5(null);
25985
26322
  rl.once("close", onClose);
25986
26323
  rl.question(q, (answer) => {
25987
26324
  rl.off("close", onClose);
25988
- resolve4(answer);
26325
+ resolve5(answer);
25989
26326
  });
25990
26327
  });
25991
26328
  const askSuggestions = async () => {
@@ -26089,7 +26426,7 @@ async function runRepl(deps) {
26089
26426
 
26090
26427
  // src/approvals.ts
26091
26428
  init_tools_node();
26092
- import { join as join9, resolve as resolve3 } from "node:path";
26429
+ import { join as join9, resolve as resolve4 } from "node:path";
26093
26430
  import { approvalsRoot } from "@corenel/protocol";
26094
26431
  import { createApprovals } from "@corenel/harness/approvals";
26095
26432
  function guardStanding(inner, reason) {
@@ -26106,7 +26443,7 @@ function guardStanding(inner, reason) {
26106
26443
  };
26107
26444
  }
26108
26445
  async function openCliApprovals(opts) {
26109
- const cwd = resolve3(opts.cwd);
26446
+ const cwd = resolve4(opts.cwd);
26110
26447
  const home = approvalsHomeDir(opts.homeBase);
26111
26448
  await ensurePrivateDir(home);
26112
26449
  const hit = await insideAnyRoot([cwd], home);
@@ -26334,7 +26671,7 @@ function killTree(child) {
26334
26671
  }
26335
26672
  }
26336
26673
  function execute(command, opts) {
26337
- return new Promise((resolve4) => {
26674
+ return new Promise((resolve5) => {
26338
26675
  const child = spawn3(command, {
26339
26676
  cwd: opts.cwd,
26340
26677
  shell: true,
@@ -26366,7 +26703,7 @@ function execute(command, opts) {
26366
26703
  const finish = (code3) => {
26367
26704
  clearTimeout(timer);
26368
26705
  opts.signal?.removeEventListener("abort", onAbort);
26369
- resolve4({ code: code3, stdout, stderr, timedOut });
26706
+ resolve5({ code: code3, stdout, stderr, timedOut });
26370
26707
  };
26371
26708
  child.on("error", (e) => {
26372
26709
  stderr += String(e);
@@ -26423,7 +26760,7 @@ import { errMessage } from "@corenel/protocol";
26423
26760
  import { wsChannel } from "@corenel/harness/sidecar/wsChannel";
26424
26761
  function nodeSidecarChannel(opts) {
26425
26762
  const { url, signal } = opts;
26426
- return new Promise((resolve4, reject) => {
26763
+ return new Promise((resolve5, reject) => {
26427
26764
  if (signal?.aborted) {
26428
26765
  reject(new Error("sidecar connection aborted"));
26429
26766
  return;
@@ -26441,7 +26778,7 @@ function nodeSidecarChannel(opts) {
26441
26778
  if (settled) return;
26442
26779
  settled = true;
26443
26780
  cleanup();
26444
- resolve4(wsChannel(ws));
26781
+ resolve5(wsChannel(ws));
26445
26782
  };
26446
26783
  const settleReject = (err) => {
26447
26784
  if (settled) return;
@@ -26647,15 +26984,20 @@ async function sidecarCredential(url, env, store) {
26647
26984
  };
26648
26985
  }
26649
26986
 
26650
- // src/sidecarIdentity.ts
26987
+ // ../tools-node/pairingAssertion.ts
26651
26988
  function cliIdentity(o) {
26989
+ const who = o.who ?? "this CLI";
26990
+ const signIn = o.signIn ?? "corenel login";
26652
26991
  const fetchImpl = o.fetchImpl ?? fetch;
26653
26992
  const timeoutMs = o.timeoutMs ?? 8e3;
26654
26993
  let last = null;
26994
+ let lastDetail = null;
26655
26995
  return {
26656
26996
  problem: () => last,
26997
+ detail: () => lastDetail ?? last,
26657
26998
  answer: async ({ nonce, sidecarId, jkt, orgId }) => {
26658
26999
  last = null;
27000
+ lastDetail = null;
26659
27001
  let token;
26660
27002
  try {
26661
27003
  token = await o.token();
@@ -26663,7 +27005,7 @@ function cliIdentity(o) {
26663
27005
  token = null;
26664
27006
  }
26665
27007
  if (!token) {
26666
- last = "this CLI is not signed in, and the sidecar only admits a verified account. Run `corenel login` (or set CORENEL_TOKEN), then try again.";
27008
+ last = `${who} is not signed in, and the sidecar only admits a verified account. Run \`${signIn}\` (or set CORENEL_TOKEN), then try again.`;
26667
27009
  return null;
26668
27010
  }
26669
27011
  try {
@@ -26682,7 +27024,7 @@ function cliIdentity(o) {
26682
27024
  code3 = void 0;
26683
27025
  }
26684
27026
  if (code3 === "CLI_SCOPE") {
26685
- last = "this CLI sign-in is not allowed to connect to sidecars. Run `corenel login` again and allow sidecar access when you approve it.";
27027
+ last = `${who}'s sign-in is not allowed to connect to sidecars. Run \`${signIn}\` again and allow sidecar access when you approve it.`;
26686
27028
  return null;
26687
27029
  }
26688
27030
  if (orgId) {
@@ -26691,7 +27033,7 @@ function cliIdentity(o) {
26691
27033
  }
26692
27034
  }
26693
27035
  if (res.status === 401) {
26694
- last = "the API refused this CLI sign-in. Run `corenel login` again, then try again.";
27036
+ last = `the API refused ${who}'s sign-in. Run \`${signIn}\` again, then try again.`;
26695
27037
  return null;
26696
27038
  }
26697
27039
  if (!res.ok) {
@@ -26706,7 +27048,9 @@ function cliIdentity(o) {
26706
27048
  }
26707
27049
  return assertion;
26708
27050
  } catch (e) {
26709
- last = `could not reach the API for a pairing assertion (${e instanceof Error ? e.message : String(e)}).`;
27051
+ const reason = `could not reach the API for a pairing assertion (${e instanceof Error ? e.message : String(e)}).`;
27052
+ last = o.unreachable ?? reason;
27053
+ lastDetail = reason;
26710
27054
  return null;
26711
27055
  }
26712
27056
  }
@@ -27358,7 +27702,7 @@ function isMcpTool(value) {
27358
27702
 
27359
27703
  // ../mcp/src/config.ts
27360
27704
  init_tools_node();
27361
- import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, chmod } from "node:fs/promises";
27705
+ import { readFile as readFile3, mkdir as mkdir2 } from "node:fs/promises";
27362
27706
  function isUsableServer(v) {
27363
27707
  if (typeof v !== "object" || v === null) return false;
27364
27708
  const r = v;
@@ -27394,6 +27738,20 @@ async function readAuthJson(stateDir) {
27394
27738
  return {};
27395
27739
  }
27396
27740
  }
27741
+ function locked(stateDir, fn) {
27742
+ return withAuthDocLock(credentialFilePath(stateDir), fn);
27743
+ }
27744
+ async function docForWrite(stateDir) {
27745
+ const path = credentialFilePath(stateDir);
27746
+ const read = await readAuthDocForWrite(path, "save");
27747
+ if (read.kind === "doc") return read.doc;
27748
+ if (read.kind === "corrupt") await backupCorruptAuthDoc(path, read.bytes);
27749
+ return {};
27750
+ }
27751
+ async function docForClear(stateDir) {
27752
+ const read = await readAuthDocForWrite(credentialFilePath(stateDir), "save");
27753
+ return read.kind === "doc" ? read.doc : null;
27754
+ }
27397
27755
  function isMcpAuthSection(v) {
27398
27756
  return typeof v === "object" && v !== null && !Array.isArray(v);
27399
27757
  }
@@ -27430,68 +27788,165 @@ async function readMcpClient(stateDir, server) {
27430
27788
  return isUsableClient(rec) ? rec : null;
27431
27789
  }
27432
27790
  async function writeMcpClient(stateDir, server, client) {
27433
- const doc = await readAuthJson(stateDir);
27434
- const prev = doc.mcpClients;
27435
- const section = typeof prev === "object" && prev !== null && !Array.isArray(prev) ? { ...prev } : {};
27436
- section[server] = client;
27437
- const next = { ...doc, mcpClients: section };
27438
- const path = credentialFilePath(stateDir);
27439
- await mkdir2(credentialDir(stateDir), { recursive: true });
27440
- await writeFile2(path, `${JSON.stringify(next, null, 2)}
27441
- `, { mode: 384 });
27442
- try {
27443
- await chmod(path, 384);
27444
- } catch {
27445
- }
27791
+ assertServerName(server);
27792
+ await locked(stateDir, async () => {
27793
+ const doc = await docForWrite(stateDir);
27794
+ const prev = doc.mcpClients;
27795
+ const section = typeof prev === "object" && prev !== null && !Array.isArray(prev) ? { ...prev } : {};
27796
+ section[server] = client;
27797
+ await writeAuthJson(stateDir, { ...doc, mcpClients: section });
27798
+ });
27446
27799
  }
27447
27800
  async function clearMcpClient(stateDir, server) {
27448
- const doc = await readAuthJson(stateDir);
27449
- const prev = doc.mcpClients;
27450
- if (typeof prev !== "object" || prev === null || Array.isArray(prev)) return;
27451
- const section = { ...prev };
27452
- if (!(server in section)) return;
27453
- delete section[server];
27454
- const path = credentialFilePath(stateDir);
27455
- await mkdir2(credentialDir(stateDir), { recursive: true });
27456
- await writeFile2(path, `${JSON.stringify({ ...doc, mcpClients: section }, null, 2)}
27457
- `, { mode: 384 });
27458
- try {
27459
- await chmod(path, 384);
27460
- } catch {
27461
- }
27801
+ await locked(stateDir, async () => {
27802
+ const doc = await docForClear(stateDir);
27803
+ if (!doc) return;
27804
+ const prev = doc.mcpClients;
27805
+ if (typeof prev !== "object" || prev === null || Array.isArray(prev)) return;
27806
+ const section = { ...prev };
27807
+ if (!(server in section)) return;
27808
+ delete section[server];
27809
+ await writeAuthJson(stateDir, { ...doc, mcpClients: section });
27810
+ });
27462
27811
  }
27463
27812
  async function clearMcpCredential(stateDir, server) {
27464
- const doc = await readAuthJson(stateDir);
27465
- if (!isMcpAuthSection(doc.mcp)) return;
27466
- const section = { ...doc.mcp };
27467
- if (!(server in section)) return;
27468
- delete section[server];
27469
- const path = credentialFilePath(stateDir);
27470
- await mkdir2(credentialDir(stateDir), { recursive: true });
27471
- await writeFile2(path, `${JSON.stringify({ ...doc, mcp: section }, null, 2)}
27472
- `, { mode: 384 });
27473
- try {
27474
- await chmod(path, 384);
27475
- } catch {
27476
- }
27813
+ await locked(stateDir, async () => {
27814
+ const doc = await docForClear(stateDir);
27815
+ if (!doc) return;
27816
+ if (!isMcpAuthSection(doc.mcp)) return;
27817
+ const section = { ...doc.mcp };
27818
+ if (!(server in section)) return;
27819
+ delete section[server];
27820
+ await writeAuthJson(stateDir, { ...doc, mcp: section });
27821
+ });
27477
27822
  }
27478
27823
  async function forgetMcpServerSecrets(stateDir, server) {
27479
27824
  await clearMcpCredential(stateDir, server);
27480
27825
  await clearMcpClient(stateDir, server);
27826
+ await clearServerFromAllAgents(stateDir, server);
27481
27827
  }
27482
27828
  async function writeMcpCredential(stateDir, server, cred) {
27483
- const doc = await readAuthJson(stateDir);
27484
- const section = isMcpAuthSection(doc.mcp) ? { ...doc.mcp } : {};
27485
- section[server] = cred;
27486
- const next = { ...doc, mcp: section };
27487
- const path = credentialFilePath(stateDir);
27829
+ assertServerName(server);
27830
+ await locked(stateDir, async () => {
27831
+ const doc = await docForWrite(stateDir);
27832
+ const section = isMcpAuthSection(doc.mcp) ? { ...doc.mcp } : {};
27833
+ section[server] = cred;
27834
+ await writeAuthJson(stateDir, { ...doc, mcp: section });
27835
+ });
27836
+ }
27837
+ var SERVER_SLOT = { kind: "server" };
27838
+ var MAX_ACCESS_KEY_LEN = 512;
27839
+ var FORBIDDEN_KEY = "__proto__";
27840
+ function assertAccessKey(accessKey) {
27841
+ if (typeof accessKey !== "string" || !accessKey || accessKey.length > MAX_ACCESS_KEY_LEN || accessKey === FORBIDDEN_KEY) {
27842
+ throw new Error("invalid agent access key");
27843
+ }
27844
+ }
27845
+ function assertServerName(server) {
27846
+ if (typeof server !== "string" || !server || server === FORBIDDEN_KEY) {
27847
+ throw new Error("invalid MCP server name");
27848
+ }
27849
+ }
27850
+ function agentsOf(doc) {
27851
+ const s = doc.agents;
27852
+ return typeof s === "object" && s !== null && !Array.isArray(s) ? { ...s } : {};
27853
+ }
27854
+ async function writeAuthJson(stateDir, next) {
27488
27855
  await mkdir2(credentialDir(stateDir), { recursive: true });
27489
- await writeFile2(path, `${JSON.stringify(next, null, 2)}
27490
- `, { mode: 384 });
27491
27856
  try {
27492
- await chmod(path, 384);
27493
- } catch {
27857
+ await writeFilePrivateAtomic(credentialFilePath(stateDir), `${JSON.stringify(next, null, 2)}
27858
+ `);
27859
+ } catch (err) {
27860
+ const code3 = typeof err === "object" && err !== null ? err.code : void 0;
27861
+ if (code3 === "EPERM" || code3 === "EBUSY") {
27862
+ throw Object.assign(new Error(`auth.json is in use; retry (${code3})`), { code: code3 });
27863
+ }
27864
+ throw err;
27865
+ }
27866
+ }
27867
+ async function readMcpCredentialFor(stateDir, server, slot) {
27868
+ if (slot.kind === "server") return readMcpCredential(stateDir, server);
27869
+ assertAccessKey(slot.accessKey);
27870
+ assertServerName(server);
27871
+ const agents = agentsOf(await readAuthJson(stateDir));
27872
+ if (!Object.prototype.hasOwnProperty.call(agents, slot.accessKey)) return null;
27873
+ const perAgent = agents[slot.accessKey];
27874
+ if (!isMcpAuthSection(perAgent)) return null;
27875
+ if (!Object.prototype.hasOwnProperty.call(perAgent, server)) return null;
27876
+ const cred = perAgent[server];
27877
+ return isUsableCredential(cred) ? cred : null;
27878
+ }
27879
+ async function writeMcpCredentialFor(stateDir, server, slot, cred) {
27880
+ if (slot.kind === "server") return writeMcpCredential(stateDir, server, cred);
27881
+ assertAccessKey(slot.accessKey);
27882
+ assertServerName(server);
27883
+ const key = slot.accessKey;
27884
+ await locked(stateDir, async () => {
27885
+ const doc = await docForWrite(stateDir);
27886
+ const agents = agentsOf(doc);
27887
+ const prev = agents[key];
27888
+ const perAgent = isMcpAuthSection(prev) ? { ...prev } : {};
27889
+ perAgent[server] = cred;
27890
+ agents[key] = perAgent;
27891
+ await writeAuthJson(stateDir, { ...doc, agents });
27892
+ });
27893
+ }
27894
+ function sameCredential(stored, expected) {
27895
+ if (stored.kind === "oauth" && expected.kind === "oauth") {
27896
+ return expected.refreshToken !== void 0 ? stored.refreshToken === expected.refreshToken : stored.refreshToken === void 0 && stored.accessToken === expected.accessToken;
27897
+ }
27898
+ if (stored.kind === "headers" && expected.kind === "headers") {
27899
+ return JSON.stringify(stored.headers) === JSON.stringify(expected.headers);
27900
+ }
27901
+ return false;
27902
+ }
27903
+ async function replaceMcpCredentialIfUnchanged(stateDir, server, slot, expected, next) {
27904
+ assertServerName(server);
27905
+ if (slot.kind === "agent") assertAccessKey(slot.accessKey);
27906
+ return locked(stateDir, () => replaceIfUnchangedLocked(stateDir, server, slot, expected, next));
27907
+ }
27908
+ async function replaceIfUnchangedLocked(stateDir, server, slot, expected, next) {
27909
+ const doc = await docForClear(stateDir);
27910
+ if (!doc) return false;
27911
+ if (slot.kind === "server") {
27912
+ if (!isMcpAuthSection(doc.mcp)) return false;
27913
+ const section = { ...doc.mcp };
27914
+ if (!Object.prototype.hasOwnProperty.call(section, server)) return false;
27915
+ const stored2 = section[server];
27916
+ if (!isUsableCredential(stored2) || !sameCredential(stored2, expected)) return false;
27917
+ section[server] = next;
27918
+ await writeAuthJson(stateDir, { ...doc, mcp: section });
27919
+ return true;
27920
+ }
27921
+ const agents = agentsOf(doc);
27922
+ if (!Object.prototype.hasOwnProperty.call(agents, slot.accessKey)) return false;
27923
+ const prev = agents[slot.accessKey];
27924
+ if (!isMcpAuthSection(prev) || !Object.prototype.hasOwnProperty.call(prev, server)) return false;
27925
+ const perAgent = { ...prev };
27926
+ const stored = perAgent[server];
27927
+ if (!isUsableCredential(stored) || !sameCredential(stored, expected)) return false;
27928
+ perAgent[server] = next;
27929
+ agents[slot.accessKey] = perAgent;
27930
+ await writeAuthJson(stateDir, { ...doc, agents });
27931
+ return true;
27932
+ }
27933
+ async function clearServerFromAllAgents(stateDir, server) {
27934
+ await locked(stateDir, () => clearServerFromAllAgentsLocked(stateDir, server));
27935
+ }
27936
+ async function clearServerFromAllAgentsLocked(stateDir, server) {
27937
+ const doc = await docForClear(stateDir);
27938
+ if (!doc) return;
27939
+ const agents = agentsOf(doc);
27940
+ let changed = false;
27941
+ for (const [key, prev] of Object.entries(agents)) {
27942
+ if (!isMcpAuthSection(prev) || !Object.prototype.hasOwnProperty.call(prev, server)) continue;
27943
+ const perAgent = { ...prev };
27944
+ delete perAgent[server];
27945
+ if (Object.keys(perAgent).length) agents[key] = perAgent;
27946
+ else delete agents[key];
27947
+ changed = true;
27494
27948
  }
27949
+ if (changed) await writeAuthJson(stateDir, { ...doc, agents });
27495
27950
  }
27496
27951
 
27497
27952
  // ../mcp/src/oauth/discovery.ts
@@ -27671,7 +28126,7 @@ function isExpired(cred, nowMs) {
27671
28126
  }
27672
28127
 
27673
28128
  // ../mcp/src/auth.ts
27674
- import { DEFAULT_API_BASE as DEFAULT_API_BASE4 } from "@corenel/protocol";
28129
+ import { DEFAULT_API_BASE as DEFAULT_API_BASE4, AGENT_CREDENTIAL_MISSING } from "@corenel/protocol";
27675
28130
  function safeOrigin(url) {
27676
28131
  try {
27677
28132
  return new URL(url).origin;
@@ -27702,22 +28157,30 @@ function isLoopbackHost(hostname2) {
27702
28157
  const h = hostname2.replace(/^\[|\]$/g, "").toLowerCase();
27703
28158
  return h === "localhost" || h === "127.0.0.1" || h === "::1";
27704
28159
  }
27705
- function authProviderFor(stateDir, server, fetchImpl = fetch, accountToken = async () => null, relayBases = async () => [], devApiBases = []) {
28160
+ function authProviderFor(stateDir, server, fetchImpl = fetch, accountToken = async () => null, relayBases = async () => [], devApiBases = [], slot = SERVER_SLOT) {
27706
28161
  async function doRefresh(cred) {
27707
28162
  if (!cred.refreshToken) {
28163
+ if (slot.kind === "agent") {
28164
+ throw new Error(`${AGENT_CREDENTIAL_MISSING}: the agent credential for MCP server "${server.name}" has expired and has no refresh token; the agent's owner must sign the agent in again.`);
28165
+ }
27708
28166
  throw new Error(`"${server.name}" has no refresh token on file -- run: corenel mcp login ${server.name}`);
27709
28167
  }
27710
28168
  const meta = await discoverAuthMetadata(cred.authServer, fetchImpl);
27711
28169
  const refreshed = await refresh(meta, cred, fetchImpl);
27712
- await writeMcpCredential(stateDir, server.name, refreshed);
28170
+ if (!await replaceMcpCredentialIfUnchanged(stateDir, server.name, slot, cred, refreshed)) {
28171
+ throw new Error(`The credential for MCP server "${server.name}" changed while it was being refreshed; retry the call.`);
28172
+ }
27713
28173
  return refreshed;
27714
28174
  }
27715
28175
  return {
27716
28176
  async headers() {
27717
- const cred = await readMcpCredential(stateDir, server.name);
28177
+ const cred = await readMcpCredentialFor(stateDir, server.name, slot);
27718
28178
  if (server.hosted && !isCorenelRelayUrl(server.url, [DEFAULT_API_BASE4, ...await relayBases()], devApiBases)) {
27719
28179
  throw new Error(`"${server.name}" is marked hosted but ${safeOrigin(server.url)} is not a Corenel relay; refusing to send the account token there.`);
27720
28180
  }
28181
+ if (!cred && slot.kind === "agent") {
28182
+ throw new Error(`${AGENT_CREDENTIAL_MISSING}: no agent credential is set for MCP server "${server.name}"`);
28183
+ }
27721
28184
  if (!cred && !server.hosted) {
27722
28185
  throw new Error(`No credential stored for MCP server "${server.name}" -- run: corenel mcp login ${server.name} (or set-header, for a static token).`);
27723
28186
  }
@@ -27728,7 +28191,7 @@ function authProviderFor(stateDir, server, fetchImpl = fetch, accountToken = asy
27728
28191
  return account ? [...moved, { name: "Authorization", value: `Bearer ${account}` }] : moved;
27729
28192
  },
27730
28193
  async onUnauthorized() {
27731
- const cred = await readMcpCredential(stateDir, server.name);
28194
+ const cred = await readMcpCredentialFor(stateDir, server.name, slot);
27732
28195
  if (!cred || cred.kind !== "oauth") return false;
27733
28196
  try {
27734
28197
  await doRefresh(cred);
@@ -27768,15 +28231,15 @@ async function accountTokenFor(stateDir) {
27768
28231
  }
27769
28232
  function mcpClientFactory(opts = {}) {
27770
28233
  const devApiBases = [...opts.devApiBases ?? []];
27771
- return (stateDir, server) => buildClient(stateDir, server, devApiBases);
28234
+ return (stateDir, server, slot = SERVER_SLOT) => buildClient(stateDir, server, devApiBases, slot);
27772
28235
  }
27773
- function defaultClientFactory(stateDir, server) {
27774
- return buildClient(stateDir, server, []);
28236
+ function defaultClientFactory(stateDir, server, slot = SERVER_SLOT) {
28237
+ return buildClient(stateDir, server, [], slot);
27775
28238
  }
27776
- function buildClient(stateDir, server, devApiBases) {
28239
+ function buildClient(stateDir, server, devApiBases, slot) {
27777
28240
  return new McpHttpClient(
27778
28241
  server,
27779
- authProviderFor(stateDir, server, timedFetch(AUTH_REQUEST_TIMEOUT_MS), () => accountTokenFor(stateDir), () => relayBasesFor(stateDir), devApiBases)
28242
+ authProviderFor(stateDir, server, timedFetch(AUTH_REQUEST_TIMEOUT_MS), () => accountTokenFor(stateDir), () => relayBasesFor(stateDir), devApiBases, slot)
27780
28243
  );
27781
28244
  }
27782
28245
  async function relayBasesFor(stateDir) {
@@ -27788,6 +28251,21 @@ async function relayBasesFor(stateDir) {
27788
28251
  }
27789
28252
  }
27790
28253
  var SERVER_CONNECT_TIMEOUT_MS = 3e4;
28254
+ function mcpClientKey(server, slot) {
28255
+ return JSON.stringify(slot.kind === "server" ? [server] : [server, slot.accessKey]);
28256
+ }
28257
+ function accessKeyOf(key) {
28258
+ const parts = JSON.parse(key);
28259
+ return Array.isArray(parts) && parts.length === 2 && typeof parts[1] === "string" ? parts[1] : void 0;
28260
+ }
28261
+ function withDeadline(p, ms, message) {
28262
+ let timer;
28263
+ const deadline = new Promise((_resolve, reject) => {
28264
+ timer = setTimeout(() => reject(new Error(message)), ms);
28265
+ });
28266
+ p.catch(() => void 0);
28267
+ return Promise.race([p, deadline]).finally(() => clearTimeout(timer));
28268
+ }
27791
28269
  var McpRegistry = class {
27792
28270
  constructor(stateDir, clientFactory = defaultClientFactory, configEnv) {
27793
28271
  this.stateDir = stateDir;
@@ -27817,6 +28295,11 @@ var McpRegistry = class {
27817
28295
  // that land on the same cold server in the same turn share one connection
27818
28296
  // instead of racing three.
27819
28297
  connecting = /* @__PURE__ */ new Map();
28298
+ // Agent-slot clients (spec 2026-10-01 Part D), keyed by mcpClientKey. Kept
28299
+ // apart from `connected`: those are the daemon's own credential, and the
28300
+ // two must never be confused for each other.
28301
+ slotted = /* @__PURE__ */ new Map();
28302
+ slotConnecting = /* @__PURE__ */ new Map();
27820
28303
  /**
27821
28304
  * Connect to every configured server and cache its tools. Never throws --
27822
28305
  * a server that fails to initialize, or fails listing tools, is recorded
@@ -27847,8 +28330,8 @@ var McpRegistry = class {
27847
28330
  * tell doctor one thing and the registered tools another. */
27848
28331
  async connectOne(server, timeoutMs, onServer) {
27849
28332
  let timer;
27850
- const deadline = new Promise((resolve4) => {
27851
- timer = setTimeout(() => resolve4("timeout"), timeoutMs);
28333
+ const deadline = new Promise((resolve5) => {
28334
+ timer = setTimeout(() => resolve5("timeout"), timeoutMs);
27852
28335
  });
27853
28336
  const attempt = (async () => {
27854
28337
  const client = this.clientFactory(this.stateDir, server);
@@ -27947,6 +28430,55 @@ var McpRegistry = class {
27947
28430
  }
27948
28431
  return entry.client.callTool(tool, args);
27949
28432
  }
28433
+ /** `server`'s tools as `slot`'s credential sees them. Throws when the
28434
+ * server is not configured or cannot be reached with that credential. */
28435
+ async listAs(server, slot) {
28436
+ return (await this.connectedAs(server, slot)).tools;
28437
+ }
28438
+ /** Call one tool with `slot`'s credential. The server slot is exactly
28439
+ * `call()`; an agent slot never touches the server's own client. */
28440
+ async callAs(server, tool, args, slot) {
28441
+ if (slot.kind === "server") return this.call(server, tool, args);
28442
+ return (await this.connectedAs(server, slot)).client.callTool(tool, args);
28443
+ }
28444
+ /** Drop every agent-slot client of `accessKey` (credentials changed, or the
28445
+ * agent was deleted); the next call connects afresh. In-flight connects are
28446
+ * dropped too: one that settles after this is never cached. */
28447
+ forgetAgentClients(accessKey) {
28448
+ for (const key of [...this.slotted.keys()]) if (accessKeyOf(key) === accessKey) this.slotted.delete(key);
28449
+ for (const key of [...this.slotConnecting.keys()]) if (accessKeyOf(key) === accessKey) this.slotConnecting.delete(key);
28450
+ }
28451
+ async connectedAs(server, slot) {
28452
+ if (slot.kind === "server") {
28453
+ await this.ensureConnected(server);
28454
+ const entry = this.connected.get(server);
28455
+ if (!entry) throw new Error(`MCP server "${server}" is not connected -- run: corenel mcp doctor`);
28456
+ return entry;
28457
+ }
28458
+ const key = mcpClientKey(server, slot);
28459
+ const hit = this.slotted.get(key);
28460
+ if (hit) return hit;
28461
+ const inflight = this.slotConnecting.get(key);
28462
+ if (inflight) return inflight;
28463
+ const self = {};
28464
+ const current = () => self.attempt !== void 0 && this.slotConnecting.get(key) === self.attempt;
28465
+ const attempt = (async () => {
28466
+ const def = (await readMcpServers(this.stateDir, this.configEnv)).find((s) => s.name === server);
28467
+ if (!def) throw new Error(`MCP server "${server}" is not configured on this daemon`);
28468
+ const client = this.clientFactory(this.stateDir, def, slot);
28469
+ const entry = await withDeadline((async () => {
28470
+ await client.initialize();
28471
+ return { client, tools: await client.listTools() };
28472
+ })(), SERVER_CONNECT_TIMEOUT_MS, `MCP server "${server}" timed out after ${SERVER_CONNECT_TIMEOUT_MS / 1e3}s`);
28473
+ if (current()) this.slotted.set(key, entry);
28474
+ return entry;
28475
+ })().finally(() => {
28476
+ if (current()) this.slotConnecting.delete(key);
28477
+ });
28478
+ self.attempt = attempt;
28479
+ this.slotConnecting.set(key, attempt);
28480
+ return attempt;
28481
+ }
27950
28482
  /** Per-server connection state, for `doctor` (Task 10) to print. A server
27951
28483
  * with `connected: false` always carries `error` -- there is no code path
27952
28484
  * that records a failure without a reason, because "no tools" alone tells
@@ -28043,6 +28575,10 @@ function toAgentTool(registry, server, tool) {
28043
28575
  };
28044
28576
  }
28045
28577
 
28578
+ // ../mcp/src/portTools.ts
28579
+ import { MCP_CALL_FAILED, canEncodeMcpWireName as canEncodeMcpWireName2, mcpWireName as mcpWireName2, parseMcpToolAddress as parseMcpToolAddress2 } from "@corenel/protocol";
28580
+ var CALL_FAILED_TEXT = `Error: ${MCP_CALL_FAILED}: the MCP call failed before a result came back; it may or may not have run, so do not repeat it without checking.`;
28581
+
28046
28582
  // ../mcp/src/gatedAgentTools.ts
28047
28583
  function gatedAgentTools(registry, policy) {
28048
28584
  const base = asAgentTools(registry);
@@ -28068,7 +28604,7 @@ function gatedAgentTools(registry, policy) {
28068
28604
 
28069
28605
  // ../mcp/src/cache.ts
28070
28606
  import { createHash as createHash2 } from "node:crypto";
28071
- import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, rename as rename2 } from "node:fs/promises";
28607
+ import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir3, rename as rename2 } from "node:fs/promises";
28072
28608
  import { dirname as dirname8, join as join12 } from "node:path";
28073
28609
  function toolCachePath(stateDir) {
28074
28610
  return join12(stateDir, "mcp-tools.json");
@@ -28101,7 +28637,7 @@ async function writeToolCache(stateDir, cache) {
28101
28637
  const path = toolCachePath(stateDir);
28102
28638
  await mkdir3(dirname8(path), { recursive: true });
28103
28639
  const tmp = `${path}.${process.pid}.tmp`;
28104
- await writeFile3(tmp, `${JSON.stringify(cache, null, 2)}
28640
+ await writeFile2(tmp, `${JSON.stringify(cache, null, 2)}
28105
28641
  `, "utf8");
28106
28642
  await rename2(tmp, path);
28107
28643
  }
@@ -28241,7 +28777,7 @@ var PAGE_FAIL = `<!doctype html><meta charset="utf-8"><title>Sign-in failed</tit
28241
28777
  var DEFAULT_TIMEOUT_MS2 = 5 * 60 * 1e3;
28242
28778
  function awaitRedirect(port, expectedState, opts = {}) {
28243
28779
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
28244
- return new Promise((resolve4, reject) => {
28780
+ return new Promise((resolve5, reject) => {
28245
28781
  let settled = false;
28246
28782
  const v6 = createServer((req, res) => onRequest(req, res));
28247
28783
  const finish = (fn) => {
@@ -28273,7 +28809,7 @@ function awaitRedirect(port, expectedState, opts = {}) {
28273
28809
  return;
28274
28810
  }
28275
28811
  res.writeHead(200, { "content-type": "text/html" }).end(PAGE_OK);
28276
- finish(() => resolve4({ code: code3 }));
28812
+ finish(() => resolve5({ code: code3 }));
28277
28813
  };
28278
28814
  const server = createServer(onRequest);
28279
28815
  const timer = setTimeout(() => {
@@ -28307,21 +28843,21 @@ function awaitRedirect(port, expectedState, opts = {}) {
28307
28843
  });
28308
28844
  }
28309
28845
  function portFree(port) {
28310
- return new Promise((resolve4) => {
28846
+ return new Promise((resolve5) => {
28311
28847
  const probe = createServer();
28312
- probe.once("error", () => resolve4(false));
28848
+ probe.once("error", () => resolve5(false));
28313
28849
  probe.listen(port, "127.0.0.1", () => {
28314
- probe.close(() => resolve4(true));
28850
+ probe.close(() => resolve5(true));
28315
28851
  });
28316
28852
  });
28317
28853
  }
28318
28854
  function getFreePort() {
28319
- return new Promise((resolve4, reject) => {
28855
+ return new Promise((resolve5, reject) => {
28320
28856
  const probe = createServer();
28321
28857
  probe.once("error", reject);
28322
28858
  probe.listen(0, "127.0.0.1", () => {
28323
28859
  const addr = probe.address();
28324
- probe.close(() => resolve4(addr.port));
28860
+ probe.close(() => resolve5(addr.port));
28325
28861
  });
28326
28862
  });
28327
28863
  }
@@ -28431,7 +28967,7 @@ async function loginToMcpServer(stateDir, server, deps = {}) {
28431
28967
  fetchImpl,
28432
28968
  now
28433
28969
  );
28434
- await writeMcpCredential(stateDir, server.name, cred);
28970
+ await writeMcpCredentialFor(stateDir, server.name, deps.slot ?? SERVER_SLOT, cred);
28435
28971
  return { ok: true };
28436
28972
  } catch (e) {
28437
28973
  redirectAbort.abort();
@@ -28511,6 +29047,9 @@ import { lookup as dnsLookup } from "node:dns/promises";
28511
29047
  import { setOutboundResolver } from "@corenel/harness/tools/urlGuard";
28512
29048
  var EXIT_POLICY_PARK_REFUSED = 3;
28513
29049
  var EXIT_BUDGET_EXCEEDED = 4;
29050
+ function loginApiBase(rest, opts = {}) {
29051
+ return apiBase(flag(rest, "base", ""), { ...opts, ignoreCredential: true });
29052
+ }
28514
29053
  function replSessionId(id) {
28515
29054
  return id === UNATTACHED_SESSION ? null : id;
28516
29055
  }
@@ -28941,10 +29480,15 @@ async function main() {
28941
29480
  case "run-agent": {
28942
29481
  const agent = positional(rest)[0];
28943
29482
  if (!agent) {
28944
- process.stderr.write('usage: corenel run-agent <name> --input "<text>" [--model M] [--base URL] [--allow-mutations]\n');
29483
+ process.stderr.write('usage: corenel run-agent <name> --input "<text>" [--model M] [--base URL] [--allow-mutations] [--memory-read-only]\n');
28945
29484
  process.exit(2);
28946
29485
  }
28947
29486
  const policyOverride = hasValueFlag(rest, "policy") ? (await resolvePolicy({ flag: flag(rest, "policy", ""), cwd: process.cwd() })).policy : void 0;
29487
+ if (hasValueFlag(rest, "memory-partition")) {
29488
+ process.stderr.write("corenel: --memory-partition is not supported: a guest run has no per-user memory\n");
29489
+ process.exit(2);
29490
+ }
29491
+ const guestMemory = hasBooleanFlag(rest, "memory-read-only") ? { kind: "read-only" } : void 0;
28948
29492
  const code3 = await runCrewAgentCli({
28949
29493
  cwd: process.cwd(),
28950
29494
  agent,
@@ -28955,6 +29499,7 @@ async function main() {
28955
29499
  ...hasValueFlag(rest, "model") ? { model: flag(rest, "model", "") } : {},
28956
29500
  ...hasValueFlag(rest, "base") ? { base: flag(rest, "base", "") } : {},
28957
29501
  ...hasAllowMutationsFlag(rest) ? { allowMutations: true } : {},
29502
+ ...guestMemory ? { memory: guestMemory } : {},
28958
29503
  ...policyOverride ? { policy: policyOverride } : {},
28959
29504
  emit: (line) => process.stdout.write(`${line}
28960
29505
  `),
@@ -28973,7 +29518,7 @@ async function main() {
28973
29518
  break;
28974
29519
  }
28975
29520
  case "login": {
28976
- const base = await apiBase(flag(rest, "base", ""));
29521
+ const base = await loginApiBase(rest);
28977
29522
  await deviceLogin({
28978
29523
  base,
28979
29524
  store: (token, account) => writeLoginCredential(token, { apiBase: base, ...account ? { account } : {} })
@@ -28988,7 +29533,7 @@ Logged in. Token saved to ${authTokenPath()}
28988
29533
  await runAuth();
28989
29534
  break;
28990
29535
  case "logout":
28991
- await runLogout();
29536
+ process.exitCode = await runLogout();
28992
29537
  break;
28993
29538
  case "doctor": {
28994
29539
  process.exitCode = await runDoctor(void 0, void 0, flag(rest, "model", DEFAULT_MODEL2), resolveDoctorEnv(rest));
@@ -29065,6 +29610,7 @@ export {
29065
29610
  EXIT_BUDGET_EXCEEDED,
29066
29611
  EXIT_POLICY_PARK_REFUSED,
29067
29612
  buildRunPermission,
29613
+ loginApiBase,
29068
29614
  replCompaction,
29069
29615
  replSessionId
29070
29616
  };