orion-super-agent-dev 0.1.34 → 0.1.35

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.
Binary file
Binary file
Binary file
Binary file
package/out/main.js CHANGED
@@ -29984,6 +29984,81 @@ var require_http = __commonJS({
29984
29984
  }
29985
29985
  });
29986
29986
 
29987
+ // ../packages/orion-client-core/dist/auth/refreshOnRefusal.js
29988
+ var require_refreshOnRefusal = __commonJS({
29989
+ "../packages/orion-client-core/dist/auth/refreshOnRefusal.js"(exports2) {
29990
+ "use strict";
29991
+ Object.defineProperty(exports2, "__esModule", { value: true });
29992
+ exports2.RefreshOnRefusal = exports2.FAILED_REFRESH_HOLDOFF_MS = exports2.REFUSED_FRESH_TOKEN_HOLDOFF_MS = exports2.REFUSED_FRESH_TOKEN_WINDOW_MS = void 0;
29993
+ exports2.REFUSED_FRESH_TOKEN_WINDOW_MS = 2 * 6e4;
29994
+ exports2.REFUSED_FRESH_TOKEN_HOLDOFF_MS = 5 * 6e4;
29995
+ exports2.FAILED_REFRESH_HOLDOFF_MS = 2e4;
29996
+ var RefreshOnRefusal = class {
29997
+ deps;
29998
+ inFlight = null;
29999
+ holdoffUntil = 0;
30000
+ /** The token in hand right after this policy's last successful refresh, and when. */
30001
+ obtained = null;
30002
+ obtainedAt = 0;
30003
+ constructor(deps2) {
30004
+ this.deps = deps2;
30005
+ }
30006
+ /** Answer a 401 that refused `refused` — the bearer the request carried, or null when it
30007
+ * carried none. Resolves true when a token the caller should retry with is now in hand. */
30008
+ refresh(refused) {
30009
+ if (this.inFlight)
30010
+ return this.inFlight;
30011
+ const run3 = this.decide(refused).finally(() => {
30012
+ if (this.inFlight === run3)
30013
+ this.inFlight = null;
30014
+ });
30015
+ this.inFlight = run3;
30016
+ return run3;
30017
+ }
30018
+ async decide(refused) {
30019
+ const current2 = await this.deps.currentToken();
30020
+ if (current2 !== null && current2 !== refused) {
30021
+ this.obtained = current2;
30022
+ this.obtainedAt = this.now();
30023
+ this.trace("refusal: the token in hand was already replaced \u2014 retrying with it, no mint");
30024
+ return true;
30025
+ }
30026
+ const now2 = this.now();
30027
+ if (now2 < this.holdoffUntil) {
30028
+ this.trace(`refusal: holding off ${Math.ceil((this.holdoffUntil - now2) / 1e3)}s \u2014 no mint`);
30029
+ return false;
30030
+ }
30031
+ if (refused !== null && refused === this.obtained && now2 - this.obtainedAt < exports2.REFUSED_FRESH_TOKEN_WINDOW_MS) {
30032
+ this.holdoffUntil = now2 + exports2.REFUSED_FRESH_TOKEN_HOLDOFF_MS;
30033
+ this.trace(`refusal: the token minted ${Math.round((now2 - this.obtainedAt) / 1e3)}s ago was refused too \u2014 not a freshness problem, holding off ${exports2.REFUSED_FRESH_TOKEN_HOLDOFF_MS / 6e4}min`);
30034
+ return false;
30035
+ }
30036
+ const ok2 = await this.deps.refresh();
30037
+ const after = this.now();
30038
+ if (!ok2) {
30039
+ this.holdoffUntil = after + exports2.FAILED_REFRESH_HOLDOFF_MS;
30040
+ this.trace(`refusal: refresh did not produce a token \u2014 holding off ${exports2.FAILED_REFRESH_HOLDOFF_MS / 1e3}s`);
30041
+ return false;
30042
+ }
30043
+ this.obtained = await this.deps.currentToken();
30044
+ this.obtainedAt = after;
30045
+ this.trace("refusal: refreshed \u2014 retrying with the new token");
30046
+ return this.obtained !== null;
30047
+ }
30048
+ now() {
30049
+ return this.deps.now?.() ?? Date.now();
30050
+ }
30051
+ trace(line) {
30052
+ try {
30053
+ this.deps.trace?.(line);
30054
+ } catch {
30055
+ }
30056
+ }
30057
+ };
30058
+ exports2.RefreshOnRefusal = RefreshOnRefusal;
30059
+ }
30060
+ });
30061
+
29987
30062
  // ../packages/orion-client-core/dist/atomicWrite.js
29988
30063
  var require_atomicWrite = __commonJS({
29989
30064
  "../packages/orion-client-core/dist/atomicWrite.js"(exports2) {
@@ -34513,6 +34588,7 @@ var require_customProviders = __commonJS({
34513
34588
  delete config.extraBody;
34514
34589
  else
34515
34590
  config.extraBody = extraBody;
34591
+ normalizeNumber(config, "firstTokenTimeoutS", 0, 86400);
34516
34592
  return config;
34517
34593
  }
34518
34594
  function normalizeDeclaredReasoning(raw) {
@@ -35099,6 +35175,7 @@ var require_customProviders = __commonJS({
35099
35175
  ...knob("topP"),
35100
35176
  ...knob("topK"),
35101
35177
  ...knob("repetitionPenalty"),
35178
+ ...knob("firstTokenTimeoutS"),
35102
35179
  ...word("promptCache"),
35103
35180
  ...word("responseCache"),
35104
35181
  ...extraBody !== void 0 ? { extraBody } : {}
@@ -35373,6 +35450,7 @@ var require_httpTransport = __commonJS({
35373
35450
  Object.defineProperty(exports2, "__esModule", { value: true });
35374
35451
  exports2.HttpSyncTransport = exports2.REQUEST_TIMEOUT_MS = exports2.SyncHttpError = exports2.SyncAuthRequiredError = void 0;
35375
35452
  var http_js_1 = require_http();
35453
+ var refreshOnRefusal_js_1 = require_refreshOnRefusal();
35376
35454
  var SyncAuthRequiredError = class extends Error {
35377
35455
  constructor() {
35378
35456
  super("o-vault sync requires a signed-in Orion account");
@@ -35394,8 +35472,14 @@ var require_httpTransport = __commonJS({
35394
35472
  auth;
35395
35473
  /** fingerprint → bank_id, so the engine's resolve inside `sync()` never re-hits the API. */
35396
35474
  bankIds = /* @__PURE__ */ new Map();
35475
+ /** The verdict on a 401 — the same policy the bridge applies: once per refused token, never for
35476
+ * a token a sibling already replaced, and holdoffs when a fresh token is refused too or the
35477
+ * refresh fails. Absent when the host wired no refresh. */
35478
+ refreshOnRefusal;
35397
35479
  constructor(auth) {
35398
35480
  this.auth = auth;
35481
+ const refresh = auth.refresh;
35482
+ this.refreshOnRefusal = refresh ? new refreshOnRefusal_js_1.RefreshOnRefusal({ currentToken: () => auth.accessToken(), refresh }) : null;
35399
35483
  }
35400
35484
  async request(method, path15, body2, retried = false) {
35401
35485
  const token = await this.auth.accessToken();
@@ -35410,8 +35494,8 @@ var require_httpTransport = __commonJS({
35410
35494
  body: body2 === void 0 ? void 0 : JSON.stringify(body2),
35411
35495
  signal: AbortSignal.timeout(exports2.REQUEST_TIMEOUT_MS)
35412
35496
  });
35413
- if (res.status === 401 && !retried && this.auth.refresh) {
35414
- if (await this.auth.refresh())
35497
+ if (res.status === 401 && !retried && this.refreshOnRefusal) {
35498
+ if (await this.refreshOnRefusal.refresh(token))
35415
35499
  return this.request(method, path15, body2, true);
35416
35500
  throw new SyncAuthRequiredError();
35417
35501
  }
@@ -36648,6 +36732,7 @@ var require_segmentDriver = __commonJS({
36648
36732
  exports2.turnAbortController = turnAbortController;
36649
36733
  exports2.jitteredDelayMs = jitteredDelayMs;
36650
36734
  exports2.effectiveWirePermissionMode = effectiveWirePermissionMode2;
36735
+ exports2.abortTurnNow = abortTurnNow;
36651
36736
  exports2.armGracefulCancel = armGracefulCancel2;
36652
36737
  exports2.isStreamingNdjsonBody = isStreamingNdjsonBody;
36653
36738
  exports2.readNdjson = readNdjson;
@@ -36704,6 +36789,13 @@ var require_segmentDriver = __commonJS({
36704
36789
  function effectiveWirePermissionMode2(turbo, configured2) {
36705
36790
  return turbo ? "full_access" : configured2;
36706
36791
  }
36792
+ function abortTurnNow(opts) {
36793
+ try {
36794
+ void Promise.resolve(opts.sendCancel()).catch(() => void 0);
36795
+ } catch {
36796
+ }
36797
+ opts.controller.abort();
36798
+ }
36707
36799
  function armGracefulCancel2(opts) {
36708
36800
  try {
36709
36801
  void Promise.resolve(opts.sendCancel()).catch(() => void 0);
@@ -38667,12 +38759,14 @@ var require_bridge = __commonJS({
38667
38759
  Object.defineProperty(exports2, "__esModule", { value: true });
38668
38760
  exports2.OrionBridge = exports2.WATCH_IDLE_TIMEOUT_MS = exports2.WATCH_RECONNECT_MAX_MS = exports2.WATCH_RECONNECT_MIN_MS = exports2.MAX_PROXY_STREAM_RETRIES = void 0;
38669
38761
  exports2.streamErrorIsProxyAuth = streamErrorIsProxyAuth;
38762
+ exports2.streamErrorIsAuthRefusal = streamErrorIsAuthRefusal;
38670
38763
  exports2.describeHttpFailure = describeHttpFailure;
38671
38764
  var zlib = __importStar(__require("node:zlib"));
38672
38765
  var eventsource_1 = __importDefault(require_eventsource());
38673
38766
  var dial_1 = require_dial();
38674
38767
  var networkFailure_1 = require_networkFailure();
38675
38768
  var http_1 = require_http();
38769
+ var refreshOnRefusal_1 = require_refreshOnRefusal();
38676
38770
  var segmentDriver_1 = require_segmentDriver();
38677
38771
  var enhancedRetrieval_1 = require_enhancedRetrieval();
38678
38772
  var GZIP_MIN_BYTES = 1500;
@@ -38687,6 +38781,18 @@ var require_bridge = __commonJS({
38687
38781
  const details = event;
38688
38782
  return details?.status === 407 || (0, dial_1.isProxyAuthMessage)(details?.message);
38689
38783
  }
38784
+ function streamErrorIsAuthRefusal(event) {
38785
+ const details = event;
38786
+ return details?.status === 401;
38787
+ }
38788
+ function describeStreamError(event) {
38789
+ const details = event;
38790
+ if (typeof details?.status === "number")
38791
+ return `status ${details.status}`;
38792
+ if (typeof details?.message === "string" && details.message)
38793
+ return details.message;
38794
+ return "no status";
38795
+ }
38690
38796
  function describeHttpFailure(context, status) {
38691
38797
  if (status === 413) {
38692
38798
  return `${context} http 413: this conversation has outgrown the backend's request-size limit \u2014 start a new chat (or remove very large pasted content) to continue`;
@@ -38719,6 +38825,11 @@ var require_bridge = __commonJS({
38719
38825
  // backend's refresh-reuse detection — the classic "token cycled mid-chat → logged out" race.
38720
38826
  // Cleared once the refresh settles so a later expiry can refresh again.
38721
38827
  refreshInFlight = null;
38828
+ /** The verdict on every 401 — request/response and stream alike: refresh once per refused
38829
+ * token, never for a token a sibling already replaced, and hold off when a fresh token is
38830
+ * refused too or the refresh itself fails. See `RefreshOnRefusal`. */
38831
+ refreshOnRefusal;
38832
+ trace;
38722
38833
  _onDidChangeFocusedSession;
38723
38834
  onDidChangeFocusedSession;
38724
38835
  _onDidStartSession;
@@ -38738,10 +38849,21 @@ var require_bridge = __commonJS({
38738
38849
  * the next turn would tell the backend "I've seen this file" even
38739
38850
  * though the contents on disk are now newer than what was read. */
38740
38851
  onDidChangeWorkspaceFile;
38741
- constructor(apiUrl, authToken, emitters, refreshAuth = async () => false) {
38852
+ constructor(apiUrl, authToken, emitters, refreshAuth = async () => false, options = {}) {
38742
38853
  this.apiUrl = apiUrl;
38743
38854
  this.authToken = authToken;
38744
38855
  this.refreshAuth = refreshAuth;
38856
+ this.trace = (line) => {
38857
+ try {
38858
+ options.trace?.(line);
38859
+ } catch {
38860
+ }
38861
+ };
38862
+ this.refreshOnRefusal = new refreshOnRefusal_1.RefreshOnRefusal({
38863
+ currentToken: () => this.authToken(),
38864
+ refresh: () => this.ensureRefreshed(),
38865
+ trace: this.trace
38866
+ });
38745
38867
  this._onDidChangeFocusedSession = emitters();
38746
38868
  this.onDidChangeFocusedSession = this._onDidChangeFocusedSession.event;
38747
38869
  this._onDidStartSession = emitters();
@@ -38818,8 +38940,10 @@ var require_bridge = __commonJS({
38818
38940
  * then re-prompts for sign-in). */
38819
38941
  async authedFetch(url, opts) {
38820
38942
  const wire = opts.body !== void 0 ? maybeGzip(opts.body) : null;
38943
+ let sent = null;
38821
38944
  const send = async () => {
38822
38945
  const token = await this.authToken();
38946
+ sent = token;
38823
38947
  const cookie = this.cookieHeader();
38824
38948
  const resp2 = await (0, http_1.httpFetch)(url, {
38825
38949
  method: opts.method,
@@ -38845,7 +38969,7 @@ var require_bridge = __commonJS({
38845
38969
  }
38846
38970
  throw error;
38847
38971
  }
38848
- if (resp.status === 401 && await this.ensureRefreshed()) {
38972
+ if (resp.status === 401 && await this.refreshOnRefusal.refresh(sent)) {
38849
38973
  return send();
38850
38974
  }
38851
38975
  return resp;
@@ -39238,7 +39362,10 @@ var require_bridge = __commonJS({
39238
39362
  reconnect();
39239
39363
  return;
39240
39364
  }
39241
- void this.ensureRefreshed().then(async () => {
39365
+ const refusal = streamErrorIsAuthRefusal(event);
39366
+ this.trace(`stream ${path15}: died before its first event (${describeStreamError(event)})${refusal ? " \u2014 a refusal, asking for a refresh" : " \u2014 not a refusal, no refresh"}`);
39367
+ const settled = refusal ? this.refreshOnRefusal.refresh(token) : Promise.resolve(false);
39368
+ void settled.then(async () => {
39242
39369
  if (!closed && await this.authToken() !== null)
39243
39370
  reconnect();
39244
39371
  });
@@ -39392,7 +39519,10 @@ var require_bridge = __commonJS({
39392
39519
  this.scheduleReconnect(sessionId);
39393
39520
  return;
39394
39521
  }
39395
- void this.ensureRefreshed().then(async () => {
39522
+ const refusal = streamErrorIsAuthRefusal(event);
39523
+ this.trace(`stream /events/${sessionId}: died before its first event (${describeStreamError(event)})${refusal ? " \u2014 a refusal, asking for a refresh" : " \u2014 not a refusal, no refresh"}`);
39524
+ const settled = refusal ? this.refreshOnRefusal.refresh(token) : Promise.resolve(false);
39525
+ void settled.then(async () => {
39396
39526
  if (await this.authToken() !== null) {
39397
39527
  this.scheduleReconnect(sessionId);
39398
39528
  } else {
@@ -48177,7 +48307,7 @@ var require_agentAuth = __commonJS({
48177
48307
  "../packages/orion-client-core/dist/auth/agentAuth.js"(exports2) {
48178
48308
  "use strict";
48179
48309
  Object.defineProperty(exports2, "__esModule", { value: true });
48180
- exports2.USAGE_TOPUP_COVERING_LABEL = exports2.NO_USAGE_LIMIT_LABEL = exports2.AgentSession = exports2.BRAIN_RENEW_DEADLINE_MS = exports2.RENEW_BUDGET_MS = exports2.SECRET_READ_TIMEOUT_MS = exports2.TOKEN_REQUEST_TIMEOUT_MS = exports2.AgentAuthError = void 0;
48310
+ exports2.USAGE_TOPUP_COVERING_LABEL = exports2.NO_USAGE_LIMIT_LABEL = exports2.AgentSession = exports2.PROACTIVE_REFRESH_HOLDOFF_MS = exports2.BRAIN_RENEW_DEADLINE_MS = exports2.RENEW_BUDGET_MS = exports2.SECRET_READ_TIMEOUT_MS = exports2.TOKEN_REQUEST_TIMEOUT_MS = exports2.AgentAuthError = void 0;
48181
48311
  exports2.isAgentSessionKey = isAgentSessionKey;
48182
48312
  exports2.pkcePair = pkcePair;
48183
48313
  exports2.buildConnectUrl = buildConnectUrl;
@@ -48362,6 +48492,7 @@ var require_agentAuth = __commonJS({
48362
48492
  exports2.BRAIN_RENEW_DEADLINE_MS = 9e4;
48363
48493
  var RENEWED_TOKEN_MIN_REMAINING_MS = 5 * 6e4;
48364
48494
  var READ_TIMED_OUT = Symbol("orion.secret-read-timed-out");
48495
+ exports2.PROACTIVE_REFRESH_HOLDOFF_MS = 2e4;
48365
48496
  var AgentSession = class {
48366
48497
  store;
48367
48498
  apiBaseUrl;
@@ -48373,6 +48504,9 @@ var require_agentAuth = __commonJS({
48373
48504
  * START a refresh: a cached refresh token may be rotated-away by a sibling, and replaying
48374
48505
  * one late revokes the whole token family server-side. */
48375
48506
  lastSaved = null;
48507
+ /** When a refresh last failed transiently — the proactive read's holdoff clock
48508
+ * ({@link PROACTIVE_REFRESH_HOLDOFF_MS}). Zero after any success. */
48509
+ lastRefreshFailedAt = 0;
48376
48510
  constructor(store, apiBaseUrl, options = {}) {
48377
48511
  this.store = store;
48378
48512
  this.apiBaseUrl = apiBaseUrl;
@@ -48419,7 +48553,9 @@ var require_agentAuth = __commonJS({
48419
48553
  * - under the buffer (or no readable `exp`): refresh-then-use, so a segment never starts
48420
48554
  * on a token about to expire when a fresh one is a round trip away.
48421
48555
  * Refresh failure degrades to the stored token (the caller's error path is no worse than
48422
- * today). */
48556
+ * today) — and for {@link PROACTIVE_REFRESH_HOLDOFF_MS} afterwards the stored token is handed
48557
+ * out without another attempt, so a backend asking for room is not asked again at every
48558
+ * segment start. */
48423
48559
  async currentFreshAccessToken() {
48424
48560
  const token = await this.currentAccessToken();
48425
48561
  if (!token)
@@ -48427,6 +48563,8 @@ var require_agentAuth = __commonJS({
48427
48563
  const remainingMs = remainingValidityMs(token);
48428
48564
  if (remainingMs > EARLY_REFRESH_MS)
48429
48565
  return token;
48566
+ if (Date.now() - this.lastRefreshFailedAt < exports2.PROACTIVE_REFRESH_HOLDOFF_MS)
48567
+ return token;
48430
48568
  if (remainingMs > FRESH_TOKEN_BUFFER_MS) {
48431
48569
  void this.refresh();
48432
48570
  return token;
@@ -48629,6 +48767,14 @@ var require_agentAuth = __commonJS({
48629
48767
  }
48630
48768
  async save(tokens) {
48631
48769
  this.lastSaved = tokens;
48770
+ if (this.store.storeMany) {
48771
+ await this.store.storeMany({
48772
+ [ACCESS_KEY]: tokens.accessToken,
48773
+ [REFRESH_KEY]: tokens.refreshToken,
48774
+ [TIER_KEY]: tokens.tier
48775
+ });
48776
+ return;
48777
+ }
48632
48778
  await this.store.store(ACCESS_KEY, tokens.accessToken);
48633
48779
  await this.store.store(REFRESH_KEY, tokens.refreshToken);
48634
48780
  await this.store.store(TIER_KEY, tokens.tier);
@@ -48648,15 +48794,44 @@ var require_agentAuth = __commonJS({
48648
48794
  this.inflightRefresh = inflight;
48649
48795
  return inflight;
48650
48796
  }
48797
+ /** The refresh's critical section — read, mint, save — runs under the store's exclusive
48798
+ * section when the store offers one (the encrypted file shared by every terminal on the
48799
+ * machine): N processes waking for the same expiry then produce ONE mint, and the ones that
48800
+ * waited find it and adopt it instead of replaying a token the server has already rotated.
48801
+ * A keychain or the editor's SecretStorage offers no lock, and the section runs as before —
48802
+ * the rotation-race adoption in {@link refreshWith} still catches the collision after the fact. */
48651
48803
  async doRefresh() {
48804
+ const store = this.store;
48805
+ if (!store.withExclusive)
48806
+ return this.refreshWith(null);
48807
+ const before = await this.boundedRead(REFRESH_KEY);
48808
+ if (before === READ_TIMED_OUT) {
48809
+ this.trace("refresh: store read unanswered before the lock \u2014 transient");
48810
+ this.lastRefreshFailedAt = Date.now();
48811
+ return false;
48812
+ }
48813
+ if (!before)
48814
+ return false;
48815
+ return store.withExclusive(() => this.refreshWith(before));
48816
+ }
48817
+ /** The refresh proper. `before` is the refresh token read BEFORE waiting for the store's lock
48818
+ * (null when there is no lock): a different one now means a sibling refreshed while we waited,
48819
+ * and its result is adopted without a mint. */
48820
+ async refreshWith(before) {
48652
48821
  const started = Date.now();
48653
48822
  const refreshToken = await this.boundedRead(REFRESH_KEY);
48654
48823
  if (refreshToken === READ_TIMED_OUT) {
48655
48824
  this.trace(`refresh: store read unanswered after ${Date.now() - started}ms \u2014 transient`);
48825
+ this.lastRefreshFailedAt = Date.now();
48656
48826
  return false;
48657
48827
  }
48658
48828
  if (!refreshToken)
48659
48829
  return false;
48830
+ if (before !== null && refreshToken !== before) {
48831
+ this.trace("refresh: a sibling refreshed while we waited for the store \u2014 adopted, no mint");
48832
+ this.lastRefreshFailedAt = 0;
48833
+ return true;
48834
+ }
48660
48835
  try {
48661
48836
  const minted = await refreshTokens({
48662
48837
  apiBaseUrl: this.apiBaseUrl(),
@@ -48665,6 +48840,7 @@ var require_agentAuth = __commonJS({
48665
48840
  });
48666
48841
  if (!minted.accessToken) {
48667
48842
  this.trace("refresh: malformed 200 (empty access token) \u2014 transient");
48843
+ this.lastRefreshFailedAt = Date.now();
48668
48844
  return false;
48669
48845
  }
48670
48846
  const tokens = minted.refreshToken ? minted : { ...minted, refreshToken };
@@ -48673,16 +48849,19 @@ var require_agentAuth = __commonJS({
48673
48849
  sleep(3 * (this.options.secretReadTimeoutMs ?? exports2.SECRET_READ_TIMEOUT_MS)).then(() => false)
48674
48850
  ]);
48675
48851
  this.trace(written ? `refresh: minted in ${Date.now() - started}ms` : `refresh: minted in ${Date.now() - started}ms, store write still pending \u2014 tokens live in the process cache`);
48852
+ this.lastRefreshFailedAt = 0;
48676
48853
  return true;
48677
48854
  } catch (err2) {
48678
48855
  if (err2 instanceof AgentAuthError && isDefinitiveAuthRejection(err2)) {
48679
48856
  const current2 = await this.boundedRead(REFRESH_KEY);
48680
48857
  if (current2 === READ_TIMED_OUT) {
48681
48858
  this.trace("refresh: rejected, but the store is unreachable \u2014 clear deferred");
48859
+ this.lastRefreshFailedAt = Date.now();
48682
48860
  return false;
48683
48861
  }
48684
48862
  if (current2 && current2 !== refreshToken) {
48685
48863
  this.trace("refresh: rejection was a rotation-race replay \u2014 sibling's session adopted");
48864
+ this.lastRefreshFailedAt = 0;
48686
48865
  return true;
48687
48866
  }
48688
48867
  this.trace("refresh: definitively rejected \u2014 session cleared");
@@ -48690,6 +48869,7 @@ var require_agentAuth = __commonJS({
48690
48869
  return false;
48691
48870
  }
48692
48871
  this.trace(`refresh: transient failure (${err2 instanceof AgentAuthError ? err2.status : "malformed reply"})`);
48872
+ this.lastRefreshFailedAt = Date.now();
48693
48873
  return false;
48694
48874
  }
48695
48875
  }
@@ -48702,6 +48882,10 @@ var require_agentAuth = __commonJS({
48702
48882
  }
48703
48883
  async clear() {
48704
48884
  this.lastSaved = null;
48885
+ if (this.store.deleteMany) {
48886
+ await this.store.deleteMany([ACCESS_KEY, REFRESH_KEY, TIER_KEY]);
48887
+ return;
48888
+ }
48705
48889
  await this.store.delete(ACCESS_KEY);
48706
48890
  await this.store.delete(REFRESH_KEY);
48707
48891
  await this.store.delete(TIER_KEY);
@@ -58052,12 +58236,19 @@ var require_steerPromotion = __commonJS({
58052
58236
  "use strict";
58053
58237
  Object.defineProperty(exports2, "__esModule", { value: true });
58054
58238
  exports2.promoteLeftoverSteers = promoteLeftoverSteers2;
58239
+ exports2.settleSteers = settleSteers;
58055
58240
  function promoteLeftoverSteers2(leftovers) {
58056
58241
  const kept = leftovers.map((text) => text.trim()).filter((text) => text.length > 0);
58057
58242
  if (kept.length === 0)
58058
58243
  return null;
58059
58244
  return kept.join("\n\n");
58060
58245
  }
58246
+ function settleSteers(sendNow, leftovers) {
58247
+ const prompt = sendNow?.trim() ?? "";
58248
+ if (prompt.length > 0)
58249
+ return { prompt, leftoversKept: true };
58250
+ return { prompt: promoteLeftoverSteers2(leftovers), leftoversKept: false };
58251
+ }
58061
58252
  }
58062
58253
  });
58063
58254
 
@@ -63745,7 +63936,7 @@ var require_codebaseIndex = __commonJS({
63745
63936
  "../packages/orion-client-core/dist/codebaseIndex.js"(exports2) {
63746
63937
  "use strict";
63747
63938
  Object.defineProperty(exports2, "__esModule", { value: true });
63748
- exports2.WorkspaceIndexStore = exports2.CONTENT_PROBE_DEADLINE_MS = exports2.RETRIEVAL_LEVELS = exports2.DEFAULT_RETRIEVAL_LEVEL = void 0;
63939
+ exports2.WorkspaceIndexStore = exports2.CONTENT_PROBE_DEADLINE_MS = exports2.RETRIEVAL_LEVEL_UI_VISIBLE = exports2.RETRIEVAL_LEVELS = exports2.DEFAULT_RETRIEVAL_LEVEL = void 0;
63749
63940
  exports2.normalizeRetrievalLevel = normalizeRetrievalLevel3;
63750
63941
  exports2.parseRetrievalLevelArg = parseRetrievalLevelArg2;
63751
63942
  exports2.normalizeRetrievalJudge = normalizeRetrievalJudge2;
@@ -63892,6 +64083,7 @@ var require_codebaseIndex = __commonJS({
63892
64083
  }
63893
64084
  exports2.DEFAULT_RETRIEVAL_LEVEL = "normal";
63894
64085
  exports2.RETRIEVAL_LEVELS = ["normal", "enhanced"];
64086
+ exports2.RETRIEVAL_LEVEL_UI_VISIBLE = false;
63895
64087
  function normalizeRetrievalLevel3(value, fallback = exports2.DEFAULT_RETRIEVAL_LEVEL) {
63896
64088
  return value === "normal" || value === "enhanced" ? value : fallback;
63897
64089
  }
@@ -73293,12 +73485,12 @@ ${notes.join("\n\n")}`;
73293
73485
  const params = req.params;
73294
73486
  const pathParam = typeof params.path === "string" && params.path ? params.path : null;
73295
73487
  if (req.name === "write_file" && pathParam) {
73296
- this.fileHistory.record(sessionId, snapshotId, this.fileHistoryPath(pathParam, params), payload.pre_write_content ?? null, { pinOriginal: true, pinKey: this.pinKeyFor(pathParam, params) });
73488
+ this.fileHistory.record(sessionId, snapshotId, this.fileHistoryPath(pathParam, params), payload.pre_write_content ?? null, { pinOriginal: true, pinKey: this.pinKeyFor(pathParam, params, sessionId) });
73297
73489
  return;
73298
73490
  }
73299
73491
  if ((req.name === "edit_file" || req.name === "delete_file") && pathParam) {
73300
73492
  if (payload.pre_write_content !== void 0) {
73301
- this.fileHistory.record(sessionId, snapshotId, this.fileHistoryPath(pathParam, params), payload.pre_write_content, { pinOriginal: true, pinKey: this.pinKeyFor(pathParam, params) });
73493
+ this.fileHistory.record(sessionId, snapshotId, this.fileHistoryPath(pathParam, params), payload.pre_write_content, { pinOriginal: true, pinKey: this.pinKeyFor(pathParam, params, sessionId) });
73302
73494
  }
73303
73495
  return;
73304
73496
  }
@@ -73319,8 +73511,18 @@ ${notes.join("\n\n")}`;
73319
73511
  * same way the write itself resolved (worktree `_root` wins, then the
73320
73512
  * workspace root, then home — the shell default). Pinning by absolute path
73321
73513
  * keeps Reject working even if the workspace root changes mid-session;
73322
- * manifest keys stay param-spelled (wire parity). */
73323
- pinKeyFor(p, params) {
73514
+ * manifest keys stay param-spelled (wire parity).
73515
+ *
73516
+ * `resolveToolPath` first, because it is the resolution the write ACTUALLY
73517
+ * used: it expands `~`, so a skill/hook write pinned under
73518
+ * `<workspace>/~/.orion/…` — a directory that exists nowhere — now pins under
73519
+ * the real home, where the host's Reject looks for it. The lexical fallback
73520
+ * below is unchanged for every path the working set cannot place, and both
73521
+ * agree for an ordinary relative or absolute path. */
73522
+ pinKeyFor(p, params, sessionId) {
73523
+ const resolved = this.resolveToolPath(p, sessionId, params._root);
73524
+ if (resolved)
73525
+ return resolved;
73324
73526
  if (path15.isAbsolute(p))
73325
73527
  return p;
73326
73528
  const root2 = typeof params._root === "string" && params._root || this.getWorkspaceRoot() || os9.homedir();
@@ -73478,6 +73680,35 @@ ${notes.join("\n\n")}`;
73478
73680
  scratchZones: this.scratchZones(access, sessionId)
73479
73681
  });
73480
73682
  }
73683
+ /**
73684
+ * The absolute path a tool path names on THIS machine, resolved exactly the way
73685
+ * the write that produced it resolved: `~` expanded, the working-directory SET
73686
+ * consulted (primary root + host-approved additional directories + this
73687
+ * session's scratchpad), a worktree-isolated child's `_root` honoured.
73688
+ * `null` when the path cannot be placed — outside the approved set, a symlink
73689
+ * escape, or empty.
73690
+ *
73691
+ * Exists so that UI does not re-derive it. A `file_modified` event carries the
73692
+ * path the MODEL wrote (the engine reads `input["path"]` verbatim), so a
73693
+ * consumer that re-joins that string onto the primary workspace folder answers
73694
+ * differently than the write did for every `~` spelling, every added directory
73695
+ * and every remote window — which is how a working-set row came to open the
73696
+ * editor's "Create File" placeholder for a file that had been written perfectly
73697
+ * well somewhere else. One resolver: the side that owns the disk.
73698
+ *
73699
+ * Never throws. A caller decorating a row has no better answer to a resolution
73700
+ * failure than to fall back to the raw spelling, and an exception on that path
73701
+ * would take the whole row down with it.
73702
+ */
73703
+ resolveToolPath(p, sessionId, rootOverride) {
73704
+ if (typeof p !== "string" || p.length === 0)
73705
+ return null;
73706
+ try {
73707
+ return this.resolveWithinWorkspace(p, sessionId, rootOverride);
73708
+ } catch {
73709
+ return null;
73710
+ }
73711
+ }
73481
73712
  /** True when `p` names a file inside THIS session's own scratchpad.
73482
73713
  *
73483
73714
  * Used to exempt scratch writes from the read-only/plan mode clamp, because a
@@ -75730,12 +75961,56 @@ var require_encryptedFileStore = __commonJS({
75730
75961
  };
75731
75962
  }();
75732
75963
  Object.defineProperty(exports2, "__esModule", { value: true });
75733
- exports2.EncryptedFileSecretStore = void 0;
75964
+ exports2.EncryptedFileSecretStore = exports2.LOCK_STALE_MS = exports2.LOCK_WAIT_MS = void 0;
75734
75965
  exports2.deriveKey = deriveKey;
75735
75966
  var crypto2 = __importStar(__require("crypto"));
75736
75967
  var fs13 = __importStar(__require("fs"));
75737
75968
  var os9 = __importStar(__require("os"));
75738
75969
  var path15 = __importStar(__require("path"));
75970
+ exports2.LOCK_WAIT_MS = 1e4;
75971
+ exports2.LOCK_STALE_MS = 3e4;
75972
+ var LOCK_POLL_MIN_MS = 25;
75973
+ var LOCK_POLL_JITTER_MS = 50;
75974
+ function sleep(ms) {
75975
+ return new Promise((resolve5) => {
75976
+ const timer = setTimeout(resolve5, ms);
75977
+ timer.unref?.();
75978
+ });
75979
+ }
75980
+ async function acquireLock(lockPath, waitMs) {
75981
+ const deadline = Date.now() + waitMs;
75982
+ for (; ; ) {
75983
+ try {
75984
+ const fd = fs13.openSync(lockPath, "wx", 384);
75985
+ try {
75986
+ fs13.writeSync(fd, `${process.pid} ${Date.now()}
75987
+ `);
75988
+ } finally {
75989
+ fs13.closeSync(fd);
75990
+ }
75991
+ return () => {
75992
+ try {
75993
+ fs13.rmSync(lockPath, { force: true });
75994
+ } catch {
75995
+ }
75996
+ };
75997
+ } catch (error) {
75998
+ if (error.code !== "EEXIST")
75999
+ return null;
76000
+ try {
76001
+ if (Date.now() - fs13.statSync(lockPath).mtimeMs > exports2.LOCK_STALE_MS) {
76002
+ fs13.rmSync(lockPath, { force: true });
76003
+ continue;
76004
+ }
76005
+ } catch {
76006
+ continue;
76007
+ }
76008
+ if (Date.now() >= deadline)
76009
+ return null;
76010
+ await sleep(LOCK_POLL_MIN_MS + Math.random() * LOCK_POLL_JITTER_MS);
76011
+ }
76012
+ }
76013
+ }
75739
76014
  function deriveKey(keyContext, fallbackIdent) {
75740
76015
  let ident = fallbackIdent;
75741
76016
  try {
@@ -75748,10 +76023,19 @@ var require_encryptedFileStore = __commonJS({
75748
76023
  file;
75749
76024
  legacy;
75750
76025
  key;
76026
+ lockWaitMs;
76027
+ /** In-process serialization of the exclusive section: a second caller in this process queues
76028
+ * behind the first instead of contending for its own lock file. */
76029
+ exclusive = Promise.resolve();
75751
76030
  constructor(config) {
75752
76031
  this.file = config.file;
75753
76032
  this.legacy = config.legacy;
75754
76033
  this.key = deriveKey(config.keyContext, config.fallbackIdent);
76034
+ this.lockWaitMs = config.lockWaitMs ?? exports2.LOCK_WAIT_MS;
76035
+ }
76036
+ /** The lock file beside the store — see {@link withExclusive}. */
76037
+ get lockFile() {
76038
+ return `${this.file}.lock`;
75755
76039
  }
75756
76040
  encrypt(data) {
75757
76041
  const iv = crypto2.randomBytes(12);
@@ -75786,8 +76070,19 @@ var require_encryptedFileStore = __commonJS({
75786
76070
  }
75787
76071
  writeAll(data) {
75788
76072
  fs13.mkdirSync(path15.dirname(this.file), { recursive: true });
75789
- fs13.writeFileSync(this.file, `${this.encrypt(data)}
75790
- `, { mode: 384 });
76073
+ const payload = `${this.encrypt(data)}
76074
+ `;
76075
+ const tmp = `${this.file}.${process.pid}.${crypto2.randomBytes(4).toString("hex")}.tmp`;
76076
+ try {
76077
+ fs13.writeFileSync(tmp, payload, { mode: 384 });
76078
+ fs13.renameSync(tmp, this.file);
76079
+ } catch {
76080
+ try {
76081
+ fs13.rmSync(tmp, { force: true });
76082
+ } catch {
76083
+ }
76084
+ fs13.writeFileSync(this.file, payload, { mode: 384 });
76085
+ }
75791
76086
  try {
75792
76087
  fs13.chmodSync(this.file, 384);
75793
76088
  } catch {
@@ -75813,6 +76108,38 @@ var require_encryptedFileStore = __commonJS({
75813
76108
  delete all[key];
75814
76109
  this.writeAll(all);
75815
76110
  }
76111
+ /** Several keys in ONE write — a session's access/refresh pair lands together. */
76112
+ async storeMany(entries) {
76113
+ const all = this.readAll();
76114
+ Object.assign(all, entries);
76115
+ this.writeAll(all);
76116
+ }
76117
+ /** Several keys removed in ONE write. */
76118
+ async deleteMany(keys) {
76119
+ const all = this.readAll();
76120
+ for (const key of keys)
76121
+ delete all[key];
76122
+ this.writeAll(all);
76123
+ }
76124
+ /** Run `fn` as the only holder of this store's exclusive section, across every process on
76125
+ * the machine that shares the file (the lock file beside it) and every caller in this one
76126
+ * (an in-process queue). What the token refresh runs under, so N terminals waking for the
76127
+ * same expiry produce one mint and N-1 adoptions instead of N rotations racing the server.
76128
+ * A holder that died leaves a stale lock, which is broken; a live holder that outlasts the
76129
+ * wait is not fought — `fn` then runs unlocked, the pre-lock behaviour. */
76130
+ async withExclusive(fn) {
76131
+ const run3 = this.exclusive.then(() => this.locked(fn), () => this.locked(fn));
76132
+ this.exclusive = run3.catch(() => void 0);
76133
+ return run3;
76134
+ }
76135
+ async locked(fn) {
76136
+ const release = await acquireLock(this.lockFile, this.lockWaitMs);
76137
+ try {
76138
+ return await fn();
76139
+ } finally {
76140
+ release?.();
76141
+ }
76142
+ }
75816
76143
  };
75817
76144
  exports2.EncryptedFileSecretStore = EncryptedFileSecretStore2;
75818
76145
  }
@@ -81672,6 +81999,7 @@ var require_dist = __commonJS({
81672
81999
  __exportStar(require_workspaceIndex(), exports2);
81673
82000
  __exportStar(require_processWorkspaceIndex(), exports2);
81674
82001
  __exportStar(require_agentAuth(), exports2);
82002
+ __exportStar(require_refreshOnRefusal(), exports2);
81675
82003
  __exportStar(require_loopbackReceiver(), exports2);
81676
82004
  __exportStar(require_browserLogin(), exports2);
81677
82005
  __exportStar(require_deviceLogin(), exports2);
@@ -81836,15 +82164,20 @@ var require_interruptedTurn = __commonJS({
81836
82164
  "../packages/orion-client-ui-core/dist/interruptedTurn.js"(exports2) {
81837
82165
  "use strict";
81838
82166
  Object.defineProperty(exports2, "__esModule", { value: true });
82167
+ exports2.MOVED_ON_NOTE = void 0;
81839
82168
  exports2.interruptedTurnMessages = interruptedTurnMessages2;
81840
82169
  var textBlock = (text) => ({ type: "text", text });
81841
- function interruptedTurnMessages2(messages, prompt, partialReply) {
82170
+ exports2.MOVED_ON_NOTE = "[Reply cut off here \u2014 the user sent their next message before it finished. That message is what they want now: pick up from this point as it directs, without restarting this reply or remarking on the cut.]";
82171
+ function interruptedTurnMessages2(messages, prompt, partialReply, cause = "stopped") {
81842
82172
  const out2 = [...messages];
81843
82173
  if (prompt && prompt.trim().length > 0) {
81844
82174
  out2.push({ role: "user", content: [textBlock(prompt)] });
81845
82175
  }
81846
82176
  if (partialReply && partialReply.trim().length > 0) {
81847
- out2.push({ role: "assistant", content: [textBlock(partialReply)] });
82177
+ const content = [textBlock(partialReply)];
82178
+ if (cause === "moved_on")
82179
+ content.push(textBlock(exports2.MOVED_ON_NOTE));
82180
+ out2.push({ role: "assistant", content });
81848
82181
  }
81849
82182
  return out2;
81850
82183
  }
@@ -81942,7 +82275,11 @@ var require_permissionCard = __commonJS({
81942
82275
  exports2.LABEL_SUBJECT_MAX_CHARS = exports2.DESTINATIONS = exports2.TURBO_CONFIRM_MESSAGE = void 0;
81943
82276
  exports2.elideLabelSubject = elideLabelSubject;
81944
82277
  exports2.buildPermissionCard = buildPermissionCard2;
81945
- exports2.editDiffLines = editDiffLines2;
82278
+ exports2.changeLines = changeLines2;
82279
+ exports2.unifiedDiffText = unifiedDiffText;
82280
+ exports2.changeCounts = changeCounts2;
82281
+ exports2.subjectTrailText = subjectTrailText;
82282
+ exports2.changeFlagLabels = changeFlagLabels2;
81946
82283
  exports2.permissionDecisionFor = permissionDecisionFor2;
81947
82284
  exports2.resolveCardChoice = resolveCardChoice2;
81948
82285
  var client_core_1 = require_dist();
@@ -81967,14 +82304,6 @@ var require_permissionCard = __commonJS({
81967
82304
  return `${chars.slice(0, head).join("")}\u2026${chars.slice(chars.length - tail).join("")}`;
81968
82305
  }
81969
82306
  var COMMAND_TOOLS = /* @__PURE__ */ new Set(["bash", "bash_background", "powershell"]);
81970
- function firstLine(text) {
81971
- return text.split("\n", 1)[0] ?? text;
81972
- }
81973
- function basename2(path15) {
81974
- const normalized = path15.replace(/\\/g, "/").replace(/\/+$/, "");
81975
- const i2 = normalized.lastIndexOf("/");
81976
- return i2 === -1 ? normalized : normalized.slice(i2 + 1);
81977
- }
81978
82307
  function urlHost(url) {
81979
82308
  const afterScheme = url.includes("://") ? url.slice(url.indexOf("://") + 3) : url;
81980
82309
  const end = afterScheme.search(/[/?#]/);
@@ -81982,6 +82311,23 @@ var require_permissionCard = __commonJS({
81982
82311
  const hostPort = authority.includes("@") ? authority.slice(authority.lastIndexOf("@") + 1) : authority;
81983
82312
  return hostPort.split(":")[0] ?? hostPort;
81984
82313
  }
82314
+ function splitSubject(path15) {
82315
+ const normalized = path15.replace(/\\/g, "/").replace(/\/+$/, "");
82316
+ if (normalized === "")
82317
+ return null;
82318
+ const i2 = normalized.lastIndexOf("/");
82319
+ if (i2 === -1)
82320
+ return { name: normalized, dir: null };
82321
+ return { name: normalized.slice(i2 + 1), dir: normalized.slice(0, i2) || "/" };
82322
+ }
82323
+ function textLines(text) {
82324
+ if (text === "")
82325
+ return [];
82326
+ const lines = text.split("\n");
82327
+ if (lines.length > 1 && lines[lines.length - 1] === "")
82328
+ lines.pop();
82329
+ return lines;
82330
+ }
81985
82331
  function inputPreview(input, maxChars = 220) {
81986
82332
  const parts2 = Object.entries(input).filter(([key]) => !key.startsWith("_")).map(([key, value]) => {
81987
82333
  const text = typeof value === "string" ? value : JSON.stringify(value);
@@ -81994,50 +82340,53 @@ var require_permissionCard = __commonJS({
81994
82340
  function buildPermissionCard2(req, opts = {}) {
81995
82341
  const input = req.input ?? {};
81996
82342
  const tool = req.tool_name;
81997
- let header;
81998
82343
  let body2;
82344
+ let question;
81999
82345
  if (COMMAND_TOOLS.has(tool)) {
82000
82346
  const command = String(input.command ?? "");
82001
- header = { verb: "Run", subject: firstLine(command) };
82347
+ question = "Run this command?";
82002
82348
  body2 = { kind: "command", command };
82003
82349
  } else if (tool === "edit_file") {
82004
82350
  const path15 = String(input.path ?? "");
82005
- header = { verb: "Edit", subject: basename2(path15) };
82351
+ question = "Apply this edit?";
82006
82352
  body2 = {
82007
82353
  kind: "edit",
82008
82354
  path: path15,
82009
82355
  oldString: String(input.old_string ?? ""),
82010
- newString: String(input.new_string ?? "")
82356
+ newString: String(input.new_string ?? ""),
82357
+ replaceAll: input.replace_all === true
82011
82358
  };
82012
82359
  } else if (tool === "write_file") {
82013
82360
  const path15 = String(input.path ?? "");
82014
- header = { verb: "Write", subject: basename2(path15) };
82015
- body2 = { kind: "content", path: path15, content: String(input.content ?? "") };
82361
+ const forceOverwrite = input.force_overwrite === true;
82362
+ question = forceOverwrite ? "Overwrite this file?" : "Create this file?";
82363
+ body2 = { kind: "content", path: path15, content: String(input.content ?? ""), forceOverwrite };
82016
82364
  } else if (tool === "delete_file" || tool === "delete_dir") {
82017
82365
  const path15 = String(input.path ?? "");
82018
- header = { verb: "Delete", subject: basename2(path15) };
82366
+ question = tool === "delete_dir" ? "Delete this folder?" : "Delete this file?";
82019
82367
  body2 = { kind: "path", path: path15 };
82020
82368
  } else if (tool === "copy_dir") {
82021
82369
  const dst = String(input.dst ?? "");
82022
- header = { verb: "Copy", subject: basename2(dst) };
82023
- body2 = { kind: "path", path: dst };
82370
+ question = "Copy this folder?";
82371
+ body2 = { kind: "copy", src: String(input.src ?? ""), dst };
82024
82372
  } else if (tool === "read_file") {
82025
82373
  const path15 = String(input.path ?? "");
82026
- header = { verb: "Read", subject: basename2(path15) };
82374
+ question = "Read this file?";
82027
82375
  body2 = { kind: "path", path: path15 };
82028
82376
  } else if (tool === "web_fetch") {
82029
82377
  const url = String(input.url ?? "");
82030
- header = { verb: "Fetch", subject: urlHost(url) };
82378
+ question = "Fetch this URL?";
82031
82379
  body2 = { kind: "fetch", url };
82032
82380
  } else {
82033
- header = { verb: "Use", subject: (0, toolLabel_1.toolDisplayName)(tool) };
82381
+ question = `Use ${(0, toolLabel_1.toolDisplayName)(tool)}?`;
82034
82382
  body2 = { kind: "tool", toolName: tool, inputPreview: inputPreview(input) };
82035
82383
  }
82036
82384
  const suggestions = req.suggestions ?? [];
82037
82385
  const grantDir = suggestions.find((s) => s.kind === "add_directories")?.directories[0];
82038
82386
  return {
82039
- askTitle: opts.agentLabel ? `${opts.agentLabel} is asking for permission` : "Orion is asking for permission",
82040
- header,
82387
+ question,
82388
+ subject: subjectFor(body2),
82389
+ change: changeFor(body2),
82041
82390
  body: body2,
82042
82391
  reason: req.reason_code && req.reason_code !== "no_matching_rule" ? req.reason?.trim() || null : null,
82043
82392
  outside: req.reason_code === "outside_working_directories" && req.blocked_path ? { blockedPath: req.blocked_path, grantDir: grantDir ?? req.blocked_path } : void 0,
@@ -82098,12 +82447,92 @@ var require_permissionCard = __commonJS({
82098
82447
  }
82099
82448
  return options;
82100
82449
  }
82101
- function editDiffLines2(body2, maxLines = 60) {
82102
- const lines = [
82103
- ...body2.oldString.split("\n").map((line) => `- ${line}`),
82104
- ...body2.newString.split("\n").map((line) => `+ ${line}`)
82105
- ];
82106
- return lines.length > maxLines ? [...lines.slice(0, maxLines), `\u2026 (+${lines.length - maxLines} more lines)`] : lines;
82450
+ function subjectFor(body2) {
82451
+ switch (body2.kind) {
82452
+ case "edit":
82453
+ case "content":
82454
+ case "path":
82455
+ return splitSubject(body2.path);
82456
+ case "copy":
82457
+ return splitSubject(body2.dst);
82458
+ case "fetch":
82459
+ return { name: urlHost(body2.url), dir: null };
82460
+ case "command":
82461
+ case "tool":
82462
+ return null;
82463
+ }
82464
+ }
82465
+ function changeFor(body2) {
82466
+ if (body2.kind === "edit") {
82467
+ return {
82468
+ added: textLines(body2.newString).length,
82469
+ removed: textLines(body2.oldString).length,
82470
+ everyOccurrence: body2.replaceAll,
82471
+ overwrite: false
82472
+ };
82473
+ }
82474
+ if (body2.kind === "content") {
82475
+ return {
82476
+ added: textLines(body2.content).length,
82477
+ removed: 0,
82478
+ everyOccurrence: false,
82479
+ overwrite: body2.forceOverwrite
82480
+ };
82481
+ }
82482
+ return null;
82483
+ }
82484
+ function changeLines2(body2, maxLines = 60) {
82485
+ const all = [];
82486
+ if (body2.kind === "edit") {
82487
+ for (const text of textLines(body2.oldString))
82488
+ all.push({ sign: "-", text });
82489
+ for (const text of textLines(body2.newString))
82490
+ all.push({ sign: "+", text });
82491
+ } else if (body2.kind === "content") {
82492
+ for (const text of textLines(body2.content))
82493
+ all.push({ sign: "+", text });
82494
+ }
82495
+ return all.length > maxLines ? { lines: all.slice(0, maxLines), more: all.length - maxLines } : { lines: all, more: 0 };
82496
+ }
82497
+ var UNIFIED_DIFF_MAX_LINES = 200;
82498
+ function unifiedDiffText(body2) {
82499
+ const { lines, more } = changeLines2(body2, UNIFIED_DIFF_MAX_LINES);
82500
+ if (lines.length === 0)
82501
+ return null;
82502
+ const removed = lines.filter((l3) => l3.sign === "-").length;
82503
+ const added = lines.length - removed;
82504
+ const header = body2.kind === "content" ? `@@ -0,0 +1,${added} @@` : `@@ -1,${removed} +1,${added} @@`;
82505
+ const out2 = [header, ...lines.map((l3) => `${l3.sign}${l3.text}`)];
82506
+ if (more > 0)
82507
+ out2.push("... (truncated)");
82508
+ return out2.join("\n");
82509
+ }
82510
+ function changeCounts2(change) {
82511
+ const out2 = [];
82512
+ if (change.added > 0)
82513
+ out2.push({ sign: "+", label: `+${change.added}` });
82514
+ if (change.removed > 0)
82515
+ out2.push({ sign: "-", label: `\u2212${change.removed}` });
82516
+ return out2;
82517
+ }
82518
+ function subjectTrailText(card) {
82519
+ if (!card.subject)
82520
+ return "";
82521
+ const parts2 = [];
82522
+ if (card.subject.dir)
82523
+ parts2.push(card.subject.dir);
82524
+ const counts = card.change ? changeCounts2(card.change).map((c3) => c3.label) : [];
82525
+ if (counts.length > 0)
82526
+ parts2.push(counts.join(" "));
82527
+ return parts2.join(" \xB7 ");
82528
+ }
82529
+ function changeFlagLabels2(change) {
82530
+ const out2 = [];
82531
+ if (change.everyOccurrence)
82532
+ out2.push("every occurrence");
82533
+ if (change.overwrite)
82534
+ out2.push("overwrites the existing file");
82535
+ return out2;
82107
82536
  }
82108
82537
  function permissionDecisionFor2(resolved) {
82109
82538
  return {
@@ -103184,7 +103613,11 @@ function indexDataView(detail, mode = "on") {
103184
103613
  ...distributionRows("Symbol kinds", detail.byKind),
103185
103614
  ...distributionRows("Relationship types", detail.byEdge)
103186
103615
  ],
103187
- footer: `Normal retrieval ranks ${detail.pathIndexFiles} path-indexed files + ${detail.chunks} content chunks; Enhanced adds semantic embeddings and model reranking \u2014 switch with /retrieval.`
103616
+ // ORION: the footer names the level in force and stops there. It used to describe
103617
+ // Enhanced and point at `/retrieval`, which is hidden while the level is not a
103618
+ // user-facing choice (RETRIEVAL_LEVEL_UI_VISIBLE) — a hint pointing at a command that
103619
+ // no longer exists is worse than no hint. Restore both together.
103620
+ footer: import_client_core20.RETRIEVAL_LEVEL_UI_VISIBLE ? `Normal retrieval ranks ${detail.pathIndexFiles} path-indexed files + ${detail.chunks} content chunks; Enhanced adds semantic embeddings and model reranking \u2014 switch with /retrieval.` : `Retrieval ranks ${detail.pathIndexFiles} path-indexed files + ${detail.chunks} content chunks.`
103188
103621
  };
103189
103622
  }
103190
103623
  function configView(cfg, path15) {
@@ -104374,6 +104807,45 @@ async function openRetrievalVotesPicker(ctx, workspaceRoot) {
104374
104807
  }
104375
104808
  }));
104376
104809
  }
104810
+ var retrievalModelsCommand = {
104811
+ kind: "local",
104812
+ name: "retrieval-models",
104813
+ aliases: ["retrieval-model"],
104814
+ description: "Choose the models behind the enhanced codebase search \u2014 embedding, rewrite, rerank, linking",
104815
+ keywords: ["model", "retrieval", "embedding", "search", "index", "enhanced"],
104816
+ run: async (_args, ctx) => {
104817
+ const workspaceRoot = ctx.config?.workspaceRoot;
104818
+ const config = await (0, import_client_core25.loadRetrievalModels)({ workspaceRoot });
104819
+ ctx.appStore.setState((s) => ({
104820
+ ...s,
104821
+ activeDialog: {
104822
+ kind: "select",
104823
+ title: "Enhanced retrieval models",
104824
+ options: [
104825
+ ...import_client_core25.RETRIEVAL_SLOTS.map((slot) => ({
104826
+ label: slot.label,
104827
+ value: slot.key,
104828
+ hint: config[slot.key] ? formatModelName(config[slot.key].model) : slot.defaultLabel
104829
+ })),
104830
+ {
104831
+ label: import_client_core25.RETRIEVAL_LINKING_VOTES.label,
104832
+ value: import_client_core25.RETRIEVAL_LINKING_VOTES.key,
104833
+ hint: (0, import_client_core25.retrievalLinkingVoteLabel)(config.linkingVotes)
104834
+ }
104835
+ ],
104836
+ onSelect: (key) => {
104837
+ ctx.appStore.setState((s2) => ({ ...s2, activeDialog: null }));
104838
+ if (key === import_client_core25.RETRIEVAL_LINKING_VOTES.key) {
104839
+ void openRetrievalVotesPicker(ctx, workspaceRoot);
104840
+ return;
104841
+ }
104842
+ const slot = import_client_core25.RETRIEVAL_SLOTS.find((entry) => entry.key === key);
104843
+ if (slot) void openRetrievalModelPicker(ctx, slot, workspaceRoot);
104844
+ }
104845
+ }
104846
+ }));
104847
+ }
104848
+ };
104377
104849
  function promptReasoning(ctx, sid, modelLabel, reasoning) {
104378
104850
  const levels = reasoning.levels.filter((l3) => l3 !== "off");
104379
104851
  if (levels.length === 0) return;
@@ -104418,6 +104890,22 @@ function applyRetrievalLevel(ctx, level) {
104418
104890
  ctx.appStore.setState((s) => ({ ...s, retrievalLevel: level }));
104419
104891
  ctx.print(`Retrieval level: ${(0, import_client_core23.retrievalLevelLabel)(level)}`, "success");
104420
104892
  }
104893
+ var retrievalLevelCommand = {
104894
+ kind: "local",
104895
+ name: "retrieval",
104896
+ description: "Set the retrieval level: normal or enhanced",
104897
+ argumentHint: "[normal|enhanced|toggle]",
104898
+ run: (args2, ctx) => {
104899
+ const current2 = ctx.appStore.getState().retrievalLevel;
104900
+ const requested = args2.trim();
104901
+ if (!requested || requested.toLowerCase() === "status") {
104902
+ return ctx.printInfo(retrievalView(current2));
104903
+ }
104904
+ const next = (0, import_client_core23.parseRetrievalLevelArg)(requested, current2);
104905
+ if (!next) return ctx.print("Usage: /retrieval [normal|enhanced|toggle]", "warning");
104906
+ applyRetrievalLevel(ctx, next);
104907
+ }
104908
+ };
104421
104909
  function channelGuards(prefix, guards) {
104422
104910
  return guards.filter(({ id }) => import_client_core23.GUARD_FIELDS[id].startsWith(prefix));
104423
104911
  }
@@ -104897,45 +105385,7 @@ function registerBuiltins(registry) {
104897
105385
  }));
104898
105386
  }
104899
105387
  },
104900
- {
104901
- kind: "local",
104902
- name: "retrieval-models",
104903
- aliases: ["retrieval-model"],
104904
- description: "Choose the models behind the enhanced codebase search \u2014 embedding, rewrite, rerank, linking",
104905
- keywords: ["model", "retrieval", "embedding", "search", "index", "enhanced"],
104906
- run: async (_args, ctx) => {
104907
- const workspaceRoot = ctx.config?.workspaceRoot;
104908
- const config = await (0, import_client_core25.loadRetrievalModels)({ workspaceRoot });
104909
- ctx.appStore.setState((s) => ({
104910
- ...s,
104911
- activeDialog: {
104912
- kind: "select",
104913
- title: "Enhanced retrieval models",
104914
- options: [
104915
- ...import_client_core25.RETRIEVAL_SLOTS.map((slot) => ({
104916
- label: slot.label,
104917
- value: slot.key,
104918
- hint: config[slot.key] ? formatModelName(config[slot.key].model) : slot.defaultLabel
104919
- })),
104920
- {
104921
- label: import_client_core25.RETRIEVAL_LINKING_VOTES.label,
104922
- value: import_client_core25.RETRIEVAL_LINKING_VOTES.key,
104923
- hint: (0, import_client_core25.retrievalLinkingVoteLabel)(config.linkingVotes)
104924
- }
104925
- ],
104926
- onSelect: (key) => {
104927
- ctx.appStore.setState((s2) => ({ ...s2, activeDialog: null }));
104928
- if (key === import_client_core25.RETRIEVAL_LINKING_VOTES.key) {
104929
- void openRetrievalVotesPicker(ctx, workspaceRoot);
104930
- return;
104931
- }
104932
- const slot = import_client_core25.RETRIEVAL_SLOTS.find((entry) => entry.key === key);
104933
- if (slot) void openRetrievalModelPicker(ctx, slot, workspaceRoot);
104934
- }
104935
- }
104936
- }));
104937
- }
104938
- },
105388
+ ...import_client_core23.RETRIEVAL_LEVEL_UI_VISIBLE ? [retrievalModelsCommand] : [],
104939
105389
  {
104940
105390
  kind: "local",
104941
105391
  name: "duet-executor",
@@ -105652,22 +106102,15 @@ ${(0, import_client_core25.duetRoutingSummaryLine)(await (0, import_client_core2
105652
106102
  if (requested !== "off") ctx.print(`Index mode: ${requested}`, "success");
105653
106103
  }
105654
106104
  },
105655
- {
105656
- kind: "local",
105657
- name: "retrieval",
105658
- description: "Set the retrieval level: normal or enhanced",
105659
- argumentHint: "[normal|enhanced|toggle]",
105660
- run: (args2, ctx) => {
105661
- const current2 = ctx.appStore.getState().retrievalLevel;
105662
- const requested = args2.trim();
105663
- if (!requested || requested.toLowerCase() === "status") {
105664
- return ctx.printInfo(retrievalView(current2));
105665
- }
105666
- const next = (0, import_client_core23.parseRetrievalLevelArg)(requested, current2);
105667
- if (!next) return ctx.print("Usage: /retrieval [normal|enhanced|toggle]", "warning");
105668
- applyRetrievalLevel(ctx, next);
105669
- }
105670
- },
106105
+ // ORION: `/retrieval` and `/retrieval-models` are OFFERED only while the retrieval
106106
+ // level is a user-facing choice (RETRIEVAL_LEVEL_UI_VISIBLE, false today). Out of the
106107
+ // registry entirely — no row in the list, no completion, no help entry — rather than
106108
+ // commands that answer "not available", which would advertise the very thing being
106109
+ // withheld. Only the registration is gated: both command objects are declared above,
106110
+ // unchanged and still tested, and the session still RUNS whatever level it has (the
106111
+ // config key and ORION_RETRIEVAL_LEVEL are untouched), so flipping the flag back
106112
+ // restores the commands and every saved preference with them.
106113
+ ...import_client_core23.RETRIEVAL_LEVEL_UI_VISIBLE ? [retrievalLevelCommand] : [],
105671
106114
  {
105672
106115
  kind: "local",
105673
106116
  name: "theme",
@@ -125321,7 +125764,7 @@ function OrionRing() {
125321
125764
 
125322
125765
  // src/services/version.ts
125323
125766
  function tuiVersion() {
125324
- return "0.1.34".length > 0 ? "0.1.34" : null;
125767
+ return "0.1.35".length > 0 ? "0.1.35" : null;
125325
125768
  }
125326
125769
 
125327
125770
  // src/components/layout/WelcomeCard.tsx
@@ -127987,7 +128430,7 @@ function SelectList({
127987
128430
  const end = windowed ? Math.min(start2 + maxRows, view.length) : view.length;
127988
128431
  const totalSelectable = filterable ? options.filter((o) => !o.separator).length : 0;
127989
128432
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Box2, { flexDirection: "column", marginTop: 1, children: [
127990
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(ThemedText, { colorKey: "listSelection", bold: true, children: title }),
128433
+ title ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(ThemedText, { colorKey: "listSelection", bold: true, children: title }) : null,
127991
128434
  body2 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Box2, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(ThemedText, { children: body2 }) }) : null,
127992
128435
  filterable ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Box2, { children: [
127993
128436
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(ThemedText, { dim: true, children: "Search: " }),
@@ -128107,42 +128550,33 @@ function PermissionPrompt({
128107
128550
  ] }) : null
128108
128551
  ] });
128109
128552
  }
128110
- if (body2.kind === "edit") {
128111
- const { shown, more } = cap((0, import_client_ui_core26.editDiffLines)(body2, 60));
128553
+ if (body2.kind === "edit" || body2.kind === "content") {
128554
+ const { lines, more } = (0, import_client_ui_core26.changeLines)(body2, 60);
128555
+ const shown = lines.slice(0, BODY_CAP);
128556
+ const hidden = more + Math.max(0, lines.length - BODY_CAP);
128112
128557
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box2, { flexDirection: "column", children: [
128113
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { dim: true, wrap: "truncate-middle", children: body2.path }),
128114
128558
  shown.map((line2, i2) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
128115
128559
  ThemedText,
128116
128560
  {
128117
- colorKey: line2.startsWith("+") ? "success" : line2.startsWith("-") ? "error" : void 0,
128561
+ colorKey: line2.sign === "+" ? "success" : "error",
128118
128562
  wrap: "truncate-end",
128119
- children: line2 || " "
128563
+ children: `${line2.sign} ${line2.text}`
128120
128564
  },
128121
128565
  i2
128122
128566
  )),
128123
- more > 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(ThemedText, { dim: true, children: [
128124
- "\u2026 (+",
128125
- more,
128126
- " more lines)"
128127
- ] }) : null
128128
- ] });
128129
- }
128130
- if (body2.kind === "content") {
128131
- const { shown, more } = cap(body2.content.split("\n"));
128132
- return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box2, { flexDirection: "column", children: [
128133
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { dim: true, wrap: "truncate-middle", children: body2.path }),
128134
- shown.map((line2, i2) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "toolArgs", wrap: "truncate-end", children: line2 || " " }, i2)),
128135
- more > 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(ThemedText, { dim: true, children: [
128567
+ hidden > 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(ThemedText, { dim: true, children: [
128136
128568
  "\u2026 (+",
128137
- more,
128569
+ hidden,
128138
128570
  " more lines)"
128139
128571
  ] }) : null
128140
128572
  ] });
128141
128573
  }
128142
- const line = body2.kind === "path" ? body2.path : body2.kind === "fetch" ? body2.url : body2.inputPreview;
128574
+ const line = (
128575
+ // A delete/read names its target on the subject row — a second copy says nothing.
128576
+ body2.kind === "path" ? null : body2.kind === "copy" ? `from ${body2.src}` : body2.kind === "fetch" ? body2.url : body2.inputPreview
128577
+ );
128143
128578
  return line ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "toolArgs", wrap: "truncate-end", children: line }) : null;
128144
128579
  })();
128145
- const question = body2.kind === "command" ? "Do you want to run this command?" : body2.kind === "edit" ? "Do you want to make this edit?" : body2.kind === "content" ? "Do you want to create this file?" : "Do you want to proceed?";
128146
128580
  const theme = useTheme();
128147
128581
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
128148
128582
  Box2,
@@ -128153,10 +128587,20 @@ function PermissionPrompt({
128153
128587
  borderColor: theme.permissionMarker,
128154
128588
  paddingX: 1,
128155
128589
  children: [
128156
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box2, { flexDirection: "row", children: [
128157
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "warning", bold: true, children: `${model.attribution ? `${model.attribution} \u2014 ` : ""}${model.header.verb}${count > 1 ? ` (1 of ${count})` : ""} ` }),
128158
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "toolName", bold: true, wrap: "truncate-end", children: model.header.subject })
128159
- ] }),
128590
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Box2, { flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "warning", bold: true, children: `${model.attribution ? `${model.attribution} \u2014 ` : ""}${model.question}${count > 1 ? ` (1 of ${count})` : ""}` }) }),
128591
+ model.subject ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box2, { flexDirection: "row", paddingLeft: 2, children: [
128592
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "toolName", wrap: "truncate-end", children: model.subject.name }),
128593
+ model.subject.dir ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { dim: true, wrap: "truncate-middle", children: ` ${model.subject.dir}` }) : null,
128594
+ model.change ? (0, import_client_ui_core26.changeCounts)(model.change).map((count2) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
128595
+ ThemedText,
128596
+ {
128597
+ colorKey: count2.sign === "+" ? "success" : "error",
128598
+ children: ` ${count2.label}`
128599
+ },
128600
+ count2.sign
128601
+ )) : null,
128602
+ model.change ? (0, import_client_ui_core26.changeFlagLabels)(model.change).map((flag) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { colorKey: "warning", children: ` ${flag}` }, flag)) : null
128603
+ ] }) : null,
128160
128604
  bodyBlock ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Box2, { paddingLeft: 2, marginTop: 1, flexDirection: "column", children: bodyBlock }) : null,
128161
128605
  model.reason ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Box2, { paddingLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { dim: true, wrap: "wrap", children: model.reason }) }) : null,
128162
128606
  model.outside ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Box2, { paddingLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(ThemedText, { dim: true, children: [
@@ -128170,10 +128614,7 @@ function PermissionPrompt({
128170
128614
  data.consecutive_denials,
128171
128615
  "\xD7 in a row)"
128172
128616
  ] }) }) : null,
128173
- stage.kind === "choose" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box2, { flexDirection: "column", marginTop: 1, children: [
128174
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(ThemedText, { bold: true, children: question }),
128175
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(SelectList, { title: "", options, onSelect: choose, numbered: true, maxVisible: 6 })
128176
- ] }) : stage.kind === "edit-rule" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
128617
+ stage.kind === "choose" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(SelectList, { title: "", options, onSelect: choose, numbered: true, maxVisible: 6 }) : stage.kind === "edit-rule" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
128177
128618
  LineEditor,
128178
128619
  {
128179
128620
  label: "Don't ask again for",
@@ -132807,7 +133248,8 @@ async function runHeadless(flags2) {
132807
133248
  () => config.apiUrl,
132808
133249
  authToken,
132809
133250
  import_client_core53.nodeEmitterFactory,
132810
- () => session.refresh().then(() => true, () => false)
133251
+ () => session.refresh().then(() => true, () => false),
133252
+ { trace: (line) => console.error(`[orion-bridge] ${line}`) }
132811
133253
  );
132812
133254
  const brainLog = (message, error) => console.error(`[orion-brain] ${message}${error ? `: ${String(error)}` : ""}`);
132813
133255
  const brain = (0, import_client_core53.createHostBrain)({
@@ -133307,7 +133749,11 @@ async function main() {
133307
133749
  // token cycling mid-chat never logs the user out. `refreshAndSyncGate` also re-derives the
133308
133750
  // projected account slice so a definitively-revoked session re-gates the chat cleanly (no
133309
133751
  // stale "signed in") and a rotated token's plan lands in the status line.
133310
- () => refreshAndSyncGate(session, appStore)
133752
+ () => refreshAndSyncGate(session, appStore),
133753
+ // The bridge's auth breadcrumbs (a stream that died before its first event, a refusal's
133754
+ // verdict) ride the same debug log as the session's — the evidence the 2026-09-14 refresh
133755
+ // storm left nowhere.
133756
+ { trace: (line) => console.error(`[orion-bridge] ${line}`) }
133311
133757
  );
133312
133758
  const brainLog = (message, error) => console.error(`[orion-brain] ${message}${error ? `: ${String(error)}` : ""}`);
133313
133759
  let brainInstance;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orion-super-agent-dev",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "[dev] Orion \u2014 a terminal AI super-agent, strongest at coding. Local brain, local disk: the agent loop runs on your machine; only the metered model call leaves it.",
5
5
  "license": "MIT",
6
6
  "type": "module",