motebit 1.11.0 → 1.11.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/index.js +478 -87
  2. package/package.json +13 -13
package/dist/index.js CHANGED
@@ -1718,9 +1718,17 @@ var init_models = __esm({
1718
1718
  "use strict";
1719
1719
  init_esm_shims();
1720
1720
  ANTHROPIC_MODELS = [
1721
+ "claude-fable-5",
1722
+ "claude-opus-5",
1723
+ "claude-opus-4-8",
1721
1724
  "claude-opus-4-7",
1725
+ "claude-opus-4-6",
1726
+ "claude-sonnet-5",
1722
1727
  "claude-sonnet-4-6",
1723
- "claude-haiku-4-5-20251001"
1728
+ "claude-haiku-4-5-20251001",
1729
+ "claude-opus-4-5-20251101",
1730
+ "claude-sonnet-4-5-20250929",
1731
+ "claude-opus-4-1-20250805"
1724
1732
  ];
1725
1733
  OPENAI_MODELS = ["gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano"];
1726
1734
  GOOGLE_MODELS = [
@@ -5126,11 +5134,11 @@ function resolveModelTier(tier, currentModel) {
5126
5134
  if (currentModel.includes("claude") || currentModel.includes("anthropic")) {
5127
5135
  switch (tier) {
5128
5136
  case "strongest":
5129
- return "claude-opus";
5137
+ return "claude-opus-5";
5130
5138
  case "default":
5131
- return "claude-sonnet";
5139
+ return "claude-sonnet-5";
5132
5140
  case "fast":
5133
- return "claude-haiku";
5141
+ return "claude-haiku-4-5";
5134
5142
  }
5135
5143
  }
5136
5144
  if (currentModel.includes("gpt") || currentModel.includes("o1") || currentModel.includes("o3") || currentModel.includes("o4")) {
@@ -5628,6 +5636,9 @@ ${tagged}` : n2;
5628
5636
  function modelSupportsExtendedThinking(model) {
5629
5637
  return /claude-(?:3-7-sonnet|(?:opus|sonnet)-(?:[4-9]|\d\d))/i.test(model);
5630
5638
  }
5639
+ function modelRejectsSamplingParams(model) {
5640
+ return /claude-(?:opus-(?:4-[7-9]|[5-9]\b|\d\d)|sonnet-(?:[5-9]\b|\d\d)|fable|mythos)/i.test(model);
5641
+ }
5631
5642
  function stripTags(text) {
5632
5643
  return text.replace(/<memory\s+[^>]*>[\s\S]*?<\/memory>/g, "").replace(/<thinking>[\s\S]*?<\/thinking>/g, "").replace(/<state\s+[^>]*\/>/g, "").replace(/<narration\s*>[\s\S]*?<\/narration\s*>/g, "").replace(/\[EXTERNAL_DATA[^\]]*\][\s\S]*?\[\/EXTERNAL_DATA\]/g, "").replace(/\[MEMORY_DATA\][\s\S]*?\[\/MEMORY_DATA\]/g, "").replace(/\[EXTERNAL_DATA[^\]]*\]/g, "").replace(/\[\/EXTERNAL_DATA\]/g, "").replace(/\[MEMORY_DATA\]/g, "").replace(/\[\/MEMORY_DATA\]/g, "").replace(/\*[^*]+\*/g, "").replace(/\n{3,}/g, "\n\n").replace(/\s{2,}/g, " ").trim();
5633
5644
  }
@@ -5693,7 +5704,6 @@ var init_core = __esm({
5693
5704
  init_esm_shims();
5694
5705
  init_dist2();
5695
5706
  init_prompt();
5696
- init_prompt();
5697
5707
  init_context_window();
5698
5708
  init_config();
5699
5709
  init_summarizer();
@@ -5761,11 +5771,15 @@ var init_core = __esm({
5761
5771
  const body = {
5762
5772
  model: this.config.model,
5763
5773
  max_tokens: this.config.max_tokens ?? 4096,
5764
- // Only send temperature when the user explicitly configured it. Claude
5765
- // Opus 4.7+ deprecates the parameter entirely and returns HTTP 400 when
5766
- // it's present. Omitting it lets each model use its own default; users
5767
- // who want to tune sampling still can via `config.temperature`.
5768
- ...this.config.temperature !== void 0 && { temperature: this.config.temperature },
5774
+ // Only send temperature when the user explicitly configured it AND the
5775
+ // model still accepts it — the Opus 4.7+/Claude-5 family returns HTTP
5776
+ // 400 when the parameter is present at all (witnessed live 2026-07-29:
5777
+ // the CLI's personality default 0.7 400'd every claude-opus-5 turn).
5778
+ // Omitting it lets each model use its own default; users on models
5779
+ // that accept sampling still tune via `config.temperature`.
5780
+ ...this.config.temperature !== void 0 && !modelRejectsSamplingParams(this.config.model) && {
5781
+ temperature: this.config.temperature
5782
+ },
5769
5783
  // Cacheable system blocks (see buildSystemBlocks) — the static prefix
5770
5784
  // caches at 1/10th input cost, the lever for cost on the agentic loop.
5771
5785
  system: this.buildSystemBlocks(contextPack),
@@ -5828,11 +5842,15 @@ var init_core = __esm({
5828
5842
  const body = {
5829
5843
  model: this.config.model,
5830
5844
  max_tokens: this.config.max_tokens ?? 4096,
5831
- // Only send temperature when the user explicitly configured it. Claude
5832
- // Opus 4.7+ deprecates the parameter entirely and returns HTTP 400 when
5833
- // it's present. Omitting it lets each model use its own default; users
5834
- // who want to tune sampling still can via `config.temperature`.
5835
- ...this.config.temperature !== void 0 && { temperature: this.config.temperature },
5845
+ // Only send temperature when the user explicitly configured it AND the
5846
+ // model still accepts it — the Opus 4.7+/Claude-5 family returns HTTP
5847
+ // 400 when the parameter is present at all (witnessed live 2026-07-29:
5848
+ // the CLI's personality default 0.7 400'd every claude-opus-5 turn).
5849
+ // Omitting it lets each model use its own default; users on models
5850
+ // that accept sampling still tune via `config.temperature`.
5851
+ ...this.config.temperature !== void 0 && !modelRejectsSamplingParams(this.config.model) && {
5852
+ temperature: this.config.temperature
5853
+ },
5836
5854
  // Cacheable system blocks (see buildSystemBlocks) — same caching lever as
5837
5855
  // generate(); the streaming path is the one the chat surface actually uses.
5838
5856
  system: this.buildSystemBlocks(contextPack),
@@ -5990,7 +6008,7 @@ var init_core = __esm({
5990
6008
  if (!et || !modelSupportsExtendedThinking(this.config.model))
5991
6009
  return;
5992
6010
  const budget = Math.max(1024, Math.floor(et.budgetTokens));
5993
- body.thinking = { type: "enabled", budget_tokens: budget };
6011
+ body.thinking = modelRejectsSamplingParams(this.config.model) ? { type: "adaptive" } : { type: "enabled", budget_tokens: budget };
5994
6012
  const configured = typeof body.max_tokens === "number" ? body.max_tokens : 4096;
5995
6013
  body.max_tokens = Math.max(configured, budget + 4096);
5996
6014
  delete body.temperature;
@@ -6536,6 +6554,81 @@ var init_openai_provider = __esm({
6536
6554
  }
6537
6555
  });
6538
6556
 
6557
+ // ../../packages/ai-core/dist/model-catalog.js
6558
+ function fallbackFor(provider, reason) {
6559
+ const table = provider === "anthropic" ? ANTHROPIC_MODELS : provider === "openai" ? OPENAI_MODELS : provider === "local-server" || provider === "ollama" ? LOCAL_SERVER_SUGGESTED_MODELS : [];
6560
+ return {
6561
+ provider,
6562
+ source: "fallback",
6563
+ models: table.map((id) => ({ id })),
6564
+ fallbackReason: reason
6565
+ };
6566
+ }
6567
+ async function fetchJson(url, headers, timeoutMs, fetchFn) {
6568
+ const controller = new AbortController();
6569
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
6570
+ try {
6571
+ const resp = await fetchFn(url, { headers, signal: controller.signal });
6572
+ if (!resp.ok)
6573
+ throw new Error(`${resp.status} from ${url}`);
6574
+ return await resp.json();
6575
+ } finally {
6576
+ clearTimeout(timer);
6577
+ }
6578
+ }
6579
+ async function discoverModels(params) {
6580
+ const { provider } = params;
6581
+ const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
6582
+ const fetchFn = params.fetchFn ?? fetch;
6583
+ try {
6584
+ if (provider === "anthropic") {
6585
+ if (!params.apiKey)
6586
+ return fallbackFor(provider, "no API key for live catalog");
6587
+ const body = await fetchJson("https://api.anthropic.com/v1/models?limit=100", { "x-api-key": params.apiKey, "anthropic-version": "2023-06-01" }, timeoutMs, fetchFn);
6588
+ const models = (body.data ?? []).filter((m4) => typeof m4.id === "string").map((m4) => ({ id: m4.id, ...m4.display_name ? { displayName: m4.display_name } : {} }));
6589
+ if (models.length === 0)
6590
+ return fallbackFor(provider, "live catalog came back empty");
6591
+ return { provider, source: "live", models };
6592
+ }
6593
+ if (provider === "openai") {
6594
+ if (!params.apiKey)
6595
+ return fallbackFor(provider, "no API key for live catalog");
6596
+ const body = await fetchJson("https://api.openai.com/v1/models", { Authorization: `Bearer ${params.apiKey}` }, timeoutMs, fetchFn);
6597
+ const models = (body.data ?? []).filter((m4) => typeof m4.id === "string").filter((m4) => /^(gpt-|o[0-9]|chatgpt)/.test(m4.id)).map((m4) => ({ id: m4.id }));
6598
+ if (models.length === 0)
6599
+ return fallbackFor(provider, "live catalog came back empty");
6600
+ return { provider, source: "live", models };
6601
+ }
6602
+ if (provider === "local-server" || provider === "ollama") {
6603
+ const base = (params.baseUrl ?? "http://localhost:11434").replace(/\/$/, "");
6604
+ try {
6605
+ const body2 = await fetchJson(`${base}/api/tags`, {}, timeoutMs, fetchFn);
6606
+ const models2 = (body2.models ?? []).filter((m4) => typeof m4.name === "string").map((m4) => ({ id: m4.name }));
6607
+ if (models2.length > 0)
6608
+ return { provider, source: "live", models: models2 };
6609
+ } catch {
6610
+ }
6611
+ const body = await fetchJson(`${base}/v1/models`, {}, timeoutMs, fetchFn);
6612
+ const models = (body.data ?? []).filter((m4) => typeof m4.id === "string").map((m4) => ({ id: m4.id }));
6613
+ if (models.length === 0)
6614
+ return fallbackFor(provider, "local server lists no models");
6615
+ return { provider, source: "live", models };
6616
+ }
6617
+ return fallbackFor(provider, `no live catalog adapter for ${provider}`);
6618
+ } catch (err2) {
6619
+ return fallbackFor(provider, err2 instanceof Error ? err2.message : String(err2));
6620
+ }
6621
+ }
6622
+ var DEFAULT_TIMEOUT_MS;
6623
+ var init_model_catalog = __esm({
6624
+ "../../packages/ai-core/dist/model-catalog.js"() {
6625
+ "use strict";
6626
+ init_esm_shims();
6627
+ init_dist2();
6628
+ DEFAULT_TIMEOUT_MS = 4e3;
6629
+ }
6630
+ });
6631
+
6539
6632
  // ../../packages/memory-graph/dist/consolidation.js
6540
6633
  function buildConsolidationPrompt(newContent, existing) {
6541
6634
  const memoryList = existing.map((m4, i) => ` ${i + 1}. [id=${m4.node_id}] (confidence=${m4.confidence.toFixed(2)}) "${m4.content}"`).join("\n");
@@ -9288,6 +9381,7 @@ var init_dist4 = __esm({
9288
9381
  init_esm_shims();
9289
9382
  init_core();
9290
9383
  init_openai_provider();
9384
+ init_model_catalog();
9291
9385
  init_loop();
9292
9386
  }
9293
9387
  });
@@ -25493,7 +25587,7 @@ var init_config2 = __esm({
25493
25587
  "src/config.ts"() {
25494
25588
  "use strict";
25495
25589
  init_esm_shims();
25496
- VERSION = true ? "1.11.0" : "0.0.0-dev";
25590
+ VERSION = true ? "1.11.2" : "0.0.0-dev";
25497
25591
  CONFIG_DIR = process.env["MOTEBIT_CONFIG_DIR"] ?? path2.join(os.homedir(), ".motebit");
25498
25592
  CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
25499
25593
  RELAY_DIR = path2.join(CONFIG_DIR, "relay");
@@ -45363,7 +45457,7 @@ function classifyRelayError(status, body, retryAfterHeader) {
45363
45457
  return { code: "unknown", message: message2, status };
45364
45458
  }
45365
45459
  async function submitAndPollDelegation(params) {
45366
- const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
45460
+ const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
45367
45461
  const startedAt = Date.now();
45368
45462
  let submitHeader;
45369
45463
  try {
@@ -45501,7 +45595,7 @@ async function pollForReceipt(args) {
45501
45595
  };
45502
45596
  }
45503
45597
  async function submitP2pDelegation(params) {
45504
- const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
45598
+ const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
45505
45599
  const startedAt = Date.now();
45506
45600
  let submitHeader;
45507
45601
  try {
@@ -45957,14 +46051,14 @@ async function selectAndRunDelegation(params) {
45957
46051
  ...params.signal ? { signal: params.signal } : {}
45958
46052
  });
45959
46053
  }
45960
- var DEFAULT_TIMEOUT_MS, POLL_INTERVAL_MS, fail;
46054
+ var DEFAULT_TIMEOUT_MS2, POLL_INTERVAL_MS, fail;
45961
46055
  var init_relay_delegation = __esm({
45962
46056
  "../../packages/runtime/dist/relay-delegation.js"() {
45963
46057
  "use strict";
45964
46058
  init_esm_shims();
45965
46059
  init_dist();
45966
46060
  init_dist5();
45967
- DEFAULT_TIMEOUT_MS = 12e4;
46061
+ DEFAULT_TIMEOUT_MS2 = 12e4;
45968
46062
  POLL_INTERVAL_MS = 2e3;
45969
46063
  fail = (code2, message2, status) => ({
45970
46064
  ok: false,
@@ -57023,6 +57117,21 @@ ${footnotes}`
57023
57117
  pushReceipt(receipt) {
57024
57118
  this.receipts.push(receipt);
57025
57119
  }
57120
+ /**
57121
+ * Non-draining view of the stash for the streaming layer's
57122
+ * `delegation_complete` emission (#493): mark the count before a
57123
+ * `delegate_to_agent` call, peek what arrived after it. MUST NOT drain —
57124
+ * `getAndResetReceipts` composes the same bucket into the parent
57125
+ * receipt's `delegation_receipts` chain, and a draining reader here
57126
+ * would silently sever that chain (composition-preserves-enforcement).
57127
+ */
57128
+ get stashedReceiptCount() {
57129
+ return this.receipts.length;
57130
+ }
57131
+ /** See {@link stashedReceiptCount} — the peek half of the pair. */
57132
+ peekReceiptsSince(count2) {
57133
+ return this.receipts.slice(count2);
57134
+ }
57026
57135
  };
57027
57136
  }
57028
57137
  });
@@ -57120,8 +57229,8 @@ function deniedIntentKey(toolName, args) {
57120
57229
  return `${toolName}:${canonical}`;
57121
57230
  }
57122
57231
  function stripDisplayTags(text) {
57123
- const clean2 = text.replace(/<memory\s+[^>]*>[\s\S]*?<\/memory>/g, "").replace(/<thinking>[\s\S]*?<\/thinking>/g, "").replace(/<state\s+[^>]*\/>/g, "").replace(/<parameter\s+[^>]*>[\s\S]*?<\/parameter>/g, "").replace(/<\/?(?:artifact|function_calls|invoke|antml)[^>]*>/g, "").replace(/\[EXTERNAL_DATA[^\]]*\][\s\S]*?\[\/EXTERNAL_DATA\]/g, "").replace(/\[MEMORY_DATA\][\s\S]*?\[\/MEMORY_DATA\]/g, "").replace(/\[EXTERNAL_DATA[^\]]*\]/g, "").replace(/\[\/EXTERNAL_DATA\]/g, "").replace(/\[MEMORY_DATA\]/g, "").replace(/\[\/MEMORY_DATA\]/g, "").replace(/\*{1,3}/g, "").replace(/ {2,}/g, " ");
57124
- for (const tag2 of ["<memory", "<thinking", "<parameter"]) {
57232
+ const clean2 = text.replace(/<memory\s+[^>]*>[\s\S]*?<\/memory>/g, "").replace(/<thinking>[\s\S]*?<\/thinking>/g, "").replace(/<narration>[\s\S]*?<\/narration>/g, "").replace(/<state\s+[^>]*\/>/g, "").replace(/<parameter\s+[^>]*>[\s\S]*?<\/parameter>/g, "").replace(/<\/?(?:artifact|function_calls|invoke|antml)[^>]*>/g, "").replace(/\[EXTERNAL_DATA[^\]]*\][\s\S]*?\[\/EXTERNAL_DATA\]/g, "").replace(/\[MEMORY_DATA\][\s\S]*?\[\/MEMORY_DATA\]/g, "").replace(/\[EXTERNAL_DATA[^\]]*\]/g, "").replace(/\[\/EXTERNAL_DATA\]/g, "").replace(/\[MEMORY_DATA\]/g, "").replace(/\[\/MEMORY_DATA\]/g, "").replace(/\*{1,3}/g, "").replace(/ {2,}/g, " ");
57233
+ for (const tag2 of ["<memory", "<thinking", "<parameter", "<narration"]) {
57125
57234
  const lastOpen2 = clean2.lastIndexOf(tag2);
57126
57235
  if (lastOpen2 !== -1) {
57127
57236
  const closeTag = `</${tag2.slice(1)}>`;
@@ -57168,6 +57277,28 @@ var init_streaming = __esm({
57168
57277
  * exit.
57169
57278
  */
57170
57279
  deniedIntents = /* @__PURE__ */ new Map();
57280
+ /**
57281
+ * Tools the human refused THIS exchange — since their last message (#470).
57282
+ *
57283
+ * The exact-args ledger above deliberately lets a reworked proposal ask
57284
+ * once more ("a model that meaningfully reworks its arguments gets to ask
57285
+ * once more; the prompt text it just received tells it not to"). That bet
57286
+ * lost, live, 2026-07-29: a weak model reworded the denied paid hire
57287
+ * trivially — folding one more sentence of the user's own words into the
57288
+ * prompt — and re-prompted seconds after the "no", with the terminal
57289
+ * REFUSED_BY_HUMAN instruction in its context. Model compliance cannot
57290
+ * carry a money boundary.
57291
+ *
57292
+ * So the exact ledger gets a complement, not a replacement: within one
57293
+ * exchange, a human denial is terminal for the TOOL, however the arguments
57294
+ * are reworded. The set clears on the next genuine user message
57295
+ * (`beginExchange`) — a human-initiated follow-up can always re-open the
57296
+ * line it closed, so no future request is silently swallowed. Fuzzy arg
57297
+ * matching was rejected for the same reason exact was chosen: a false
57298
+ * match breaks the approval gate; tool-until-next-message cannot
57299
+ * mis-classify.
57300
+ */
57301
+ deniedToolsThisExchange = /* @__PURE__ */ new Set();
57171
57302
  approvalExpiredCallback = null;
57172
57303
  /** Fired when a NEW turn voids a pending approval (#462) — the owning surface renders the void. */
57173
57304
  approvalVoidedCallback = null;
@@ -57230,6 +57361,16 @@ var init_streaming = __esm({
57230
57361
  onApprovalVoided(cb) {
57231
57362
  this.approvalVoidedCallback = cb;
57232
57363
  }
57364
+ /**
57365
+ * Mark the start of a genuine user turn (#470). The exchange-scoped denial
57366
+ * brake stops re-asks BETWEEN user messages; the user's own next message
57367
+ * always releases it — the human can re-open any line they closed, and
57368
+ * only the human can. Callers: user-initiated turns only, never proactive
57369
+ * or system-triggered ones (a consolidation tick must not launder a "no").
57370
+ */
57371
+ beginExchange() {
57372
+ this.deniedToolsThisExchange.clear();
57373
+ }
57233
57374
  /** Shared stream processing — extracts state tags, handles tool/approval/injection chunks. */
57234
57375
  async *processStream(stream, userMessage, runId, options) {
57235
57376
  let result = null;
@@ -57238,6 +57379,7 @@ var init_streaming = __esm({
57238
57379
  let pendingStateUpdates = {};
57239
57380
  const motebitToolServers = this.deps.getMotebitToolServers();
57240
57381
  const pendingToolCalls = /* @__PURE__ */ new Map();
57382
+ let delegateStashMark = null;
57241
57383
  for await (let chunk of stream) {
57242
57384
  if (chunk.type === "memory_formation_deferred")
57243
57385
  continue;
@@ -57263,6 +57405,9 @@ var init_streaming = __esm({
57263
57405
  this.deps.setDelegating(true);
57264
57406
  yield { type: "delegation_start", server: motebitServer, tool: chunk.name };
57265
57407
  }
57408
+ if (chunk.name === "delegate_to_agent") {
57409
+ delegateStashMark = this.deps.delegationReceiptStash?.count() ?? null;
57410
+ }
57266
57411
  } else if (chunk.status === "done") {
57267
57412
  if (chunk.result != null && typeof chunk.result === "string") {
57268
57413
  const redacted = this.deps.redactText(chunk.result);
@@ -57322,6 +57467,26 @@ var init_streaming = __esm({
57322
57467
  full_receipt: fullReceipt
57323
57468
  };
57324
57469
  }
57470
+ if (chunk.name === "delegate_to_agent" && delegateStashMark != null) {
57471
+ const mark = delegateStashMark;
57472
+ delegateStashMark = null;
57473
+ const stashed = this.deps.delegationReceiptStash?.peekSince(mark) ?? [];
57474
+ for (const receipt of stashed) {
57475
+ yield {
57476
+ type: "delegation_complete",
57477
+ server: "relay",
57478
+ tool: receipt.tools_used?.[0] ?? "delegate_to_agent",
57479
+ ...typeof receipt.task_id === "string" && typeof receipt.status === "string" ? {
57480
+ receipt: {
57481
+ task_id: receipt.task_id,
57482
+ status: receipt.status,
57483
+ tools_used: receipt.tools_used ?? []
57484
+ }
57485
+ } : {},
57486
+ full_receipt: receipt
57487
+ };
57488
+ }
57489
+ }
57325
57490
  }
57326
57491
  }
57327
57492
  if (chunk.type === "approval_request" && this.deniedIntents.has(deniedIntentKey(chunk.name, chunk.args))) {
@@ -57342,6 +57507,27 @@ var init_streaming = __esm({
57342
57507
  type: "text",
57343
57508
  text: `
57344
57509
  [refused already \u2014 not asking again: ${chunk.name}]
57510
+ `
57511
+ };
57512
+ continue;
57513
+ }
57514
+ if (chunk.type === "approval_request" && this.deniedToolsThisExchange.has(chunk.name)) {
57515
+ this.deps.injectIntermediateMessages({
57516
+ role: "assistant",
57517
+ content: `[tool_use: ${chunk.name}(${JSON.stringify(chunk.args)})]`
57518
+ }, {
57519
+ role: "user",
57520
+ content: `[tool_result: ${JSON.stringify({
57521
+ ok: false,
57522
+ error: "REFUSED_BY_HUMAN_THIS_EXCHANGE",
57523
+ terminal: true,
57524
+ message: `The human refused a ${chunk.name} call moments ago in this same exchange. Rewording the arguments does not make it a new request \u2014 this proposal was NOT shown to them. Do not propose ${chunk.name} again until the human sends a new message. Tell the user plainly what you wanted to do and that they declined, then ask what they would like instead.`
57525
+ })}]`
57526
+ });
57527
+ yield {
57528
+ type: "text",
57529
+ text: `
57530
+ [you already declined ${chunk.name} this turn \u2014 not asking again]
57345
57531
  `
57346
57532
  };
57347
57533
  continue;
@@ -57414,6 +57600,7 @@ var init_streaming = __esm({
57414
57600
  await this.signAndEmitApprovalDecision(pending, approved);
57415
57601
  if (approved) {
57416
57602
  yield { type: "tool_status", name: pending.toolName, status: "calling" };
57603
+ const approvedStashMark = pending.toolName === "delegate_to_agent" ? this.deps.delegationReceiptStash?.count() ?? null : null;
57417
57604
  const toolRegistry = this.deps.getToolRegistry();
57418
57605
  const result = await toolRegistry.execute(pending.toolName, pending.args);
57419
57606
  const check = this.deps.sanitizeToolResult(result, pending.toolName);
@@ -57425,6 +57612,24 @@ var init_streaming = __esm({
57425
57612
  patterns: check.injectionPatterns
57426
57613
  };
57427
57614
  }
57615
+ if (approvedStashMark != null) {
57616
+ const stashed = this.deps.delegationReceiptStash?.peekSince(approvedStashMark) ?? [];
57617
+ for (const receipt of stashed) {
57618
+ yield {
57619
+ type: "delegation_complete",
57620
+ server: "relay",
57621
+ tool: receipt.tools_used?.[0] ?? "delegate_to_agent",
57622
+ ...typeof receipt.task_id === "string" && typeof receipt.status === "string" ? {
57623
+ receipt: {
57624
+ task_id: receipt.task_id,
57625
+ status: receipt.status,
57626
+ tools_used: receipt.tools_used ?? []
57627
+ }
57628
+ } : {},
57629
+ full_receipt: receipt
57630
+ };
57631
+ }
57632
+ }
57428
57633
  yield {
57429
57634
  type: "tool_status",
57430
57635
  name: pending.toolName,
@@ -57439,6 +57644,7 @@ var init_streaming = __esm({
57439
57644
  } else {
57440
57645
  const key = deniedIntentKey(pending.toolName, pending.args);
57441
57646
  this.deniedIntents.set(key, (this.deniedIntents.get(key) ?? 0) + 1);
57647
+ this.deniedToolsThisExchange.add(pending.toolName);
57442
57648
  this.deps.injectIntermediateMessages({
57443
57649
  role: "assistant",
57444
57650
  content: `[tool_use: ${pending.toolName}(${JSON.stringify(pending.args)})]`
@@ -59958,6 +60164,14 @@ var init_motebit_runtime = __esm({
59958
60164
  assertSensitivityPermitsAiCall: (entry, toolName) => this.assertSensitivityPermitsAiCall(entry, toolName),
59959
60165
  getLatestCues: () => this.latestCues,
59960
60166
  getApprovalStore: () => this.approvalStore,
60167
+ // #493: non-draining stash view for the delegate_to_agent receipt
60168
+ // beat. Lazy closures — interactiveDelegation is constructed before
60169
+ // the StreamingManager, but the indirection keeps that ordering a
60170
+ // non-constraint.
60171
+ delegationReceiptStash: {
60172
+ count: () => this.interactiveDelegation.stashedReceiptCount,
60173
+ peekSince: (n2) => this.interactiveDelegation.peekReceiptsSince(n2)
60174
+ },
59961
60175
  redactText: (text) => {
59962
60176
  if (typeof this.policy.redact === "function") {
59963
60177
  return this.policy.redact(text);
@@ -60750,6 +60964,7 @@ var init_motebit_runtime = __esm({
60750
60964
  this.preemptCycleForUserMessage();
60751
60965
  this._lastUserMessageAt = Date.now();
60752
60966
  this._currentTypedIntent = options?.userActionAttestation ?? null;
60967
+ this.streaming.beginExchange();
60753
60968
  const voidedApproval = this.streaming.voidPendingApproval();
60754
60969
  this.state.pushUpdate({ processing: 0.9, attention: 0.8 });
60755
60970
  this.behavior.setSpeaking(true);
@@ -85979,26 +86194,13 @@ function createTerminalRenderer(io) {
85979
86194
  let inputRejecter = null;
85980
86195
  let tail2 = "";
85981
86196
  let statusRow = null;
86197
+ let modeRow = null;
85982
86198
  let painted = { widths: [], cursorCol: 0 };
85983
- function buildInputRow(cols) {
85984
- const avail = Math.max(1, cols - 1 - inputState.promptWidth);
85985
- if (inputState.line.length <= avail) {
85986
- return {
85987
- row: inputState.prompt + inputState.line,
85988
- width: inputState.promptWidth + inputState.line.length,
85989
- cursorCol: inputState.promptWidth + inputState.cursor
85990
- };
85991
- }
85992
- const start = Math.max(
85993
- 0,
85994
- Math.min(inputState.cursor - Math.floor(avail / 2), inputState.line.length - avail)
85995
- );
85996
- const windowText = inputState.line.slice(start, start + avail);
85997
- const shown = start > 0 ? "\u2026" + windowText.slice(1) : windowText;
86199
+ function buildInputRow() {
85998
86200
  return {
85999
- row: inputState.prompt + shown,
86000
- width: inputState.promptWidth + shown.length,
86001
- cursorCol: inputState.promptWidth + (inputState.cursor - start)
86201
+ row: inputState.prompt + inputState.line,
86202
+ width: inputState.promptWidth + inputState.line.length,
86203
+ cursorCol: inputState.promptWidth + inputState.cursor
86002
86204
  };
86003
86205
  }
86004
86206
  function commit(scrollback) {
@@ -86024,9 +86226,14 @@ function createTerminalRenderer(io) {
86024
86226
  rows.push(truncated + (io.isTTY ? RESET : ""));
86025
86227
  widths.push(visibleLength(truncated));
86026
86228
  }
86229
+ if (modeRow !== null) {
86230
+ const truncated = sliceVisible(modeRow, 0, cols - 1);
86231
+ rows.push(truncated + (io.isTTY ? RESET : ""));
86232
+ widths.push(visibleLength(truncated));
86233
+ }
86027
86234
  let cursorCol = 0;
86028
86235
  if (inputActive) {
86029
- const built = buildInputRow(cols);
86236
+ const built = buildInputRow();
86030
86237
  rows.push(built.row);
86031
86238
  widths.push(built.width);
86032
86239
  cursorCol = built.cursorCol;
@@ -86035,8 +86242,12 @@ function createTerminalRenderer(io) {
86035
86242
  }
86036
86243
  if (rows.length > 0) {
86037
86244
  out += rows.join("\n");
86245
+ const lastWidth = widths[widths.length - 1];
86246
+ const upWithin = cursorVisRow(lastWidth, cols) - cursorVisRow(cursorCol, cols);
86247
+ if (upWithin > 0) out += `\x1B[${upWithin}A`;
86038
86248
  out += "\r";
86039
- if (cursorCol > 0) out += `\x1B[${cursorCol}C`;
86249
+ const colInRow = cursorCol - cursorVisRow(cursorCol, cols) * cols;
86250
+ if (colInRow > 0) out += `\x1B[${colInRow}C`;
86040
86251
  }
86041
86252
  const hadOrHasRegion = painted.widths.length > 0 || rows.length > 0;
86042
86253
  painted = { widths, cursorCol };
@@ -86044,9 +86255,21 @@ function createTerminalRenderer(io) {
86044
86255
  io.write(io.isTTY && hadOrHasRegion ? SYNC_START + out + SYNC_END : out);
86045
86256
  }
86046
86257
  let atLineStart = true;
86258
+ let trailing = 2;
86259
+ function noteScrollback(flushed, tailAfter) {
86260
+ if (tailAfter !== "") {
86261
+ trailing = 0;
86262
+ return;
86263
+ }
86264
+ if (flushed === "") return;
86265
+ let n2 = 0;
86266
+ for (let i = flushed.length - 1; i >= 0 && flushed[i] === "\n" && n2 < 2; i--) n2++;
86267
+ trailing = n2 === flushed.length ? Math.min(2, trailing + n2) : n2;
86268
+ }
86047
86269
  function writeOutput2(text) {
86048
86270
  if (!io.isTTY && statusRow === null && !inputActive && tail2 === "") {
86049
86271
  if (text.length > 0) atLineStart = text.endsWith("\n");
86272
+ noteScrollback(text, "");
86050
86273
  io.write(text);
86051
86274
  return;
86052
86275
  }
@@ -86064,21 +86287,33 @@ function createTerminalRenderer(io) {
86064
86287
  buffer = sliceVisible(buffer, cols, Number.MAX_SAFE_INTEGER);
86065
86288
  }
86066
86289
  tail2 = buffer;
86290
+ noteScrollback(scrollback, tail2);
86067
86291
  commit(scrollback);
86068
86292
  }
86069
86293
  function writeLine2(text) {
86070
86294
  const midLine = io.isTTY ? tail2 !== "" : !atLineStart;
86071
86295
  writeOutput2((midLine ? "\n" : "") + text + "\n");
86072
86296
  }
86297
+ function writeGap2() {
86298
+ const need = tail2 !== "" ? 2 : Math.max(0, 2 - trailing);
86299
+ if (need > 0) writeOutput2("\n".repeat(need));
86300
+ }
86073
86301
  function setStatusRow2(row) {
86074
86302
  if (!io.isTTY) return;
86075
86303
  if (row === statusRow) return;
86076
86304
  statusRow = row;
86077
86305
  commit("");
86078
86306
  }
86307
+ function setModeRow2(row) {
86308
+ if (!io.isTTY) return;
86309
+ if (row === modeRow) return;
86310
+ modeRow = row;
86311
+ commit("");
86312
+ }
86079
86313
  function submit() {
86080
86314
  const line = inputState.line;
86081
86315
  inputActive = false;
86316
+ trailing = 1;
86082
86317
  commit(inputState.prompt + line + "\n");
86083
86318
  if (inputResolver) {
86084
86319
  const resolve14 = inputResolver;
@@ -86136,8 +86371,10 @@ function createTerminalRenderer(io) {
86136
86371
  return {
86137
86372
  writeOutput: writeOutput2,
86138
86373
  writeLine: writeLine2,
86374
+ writeGap: writeGap2,
86139
86375
  readInput: readInput2,
86140
86376
  setStatusRow: setStatusRow2,
86377
+ setModeRow: setModeRow2,
86141
86378
  handleEvent,
86142
86379
  repaint: () => commit(""),
86143
86380
  detach
@@ -86377,9 +86614,15 @@ function readInput(promptText) {
86377
86614
  function askQuestion(promptText) {
86378
86615
  return readInput(promptText);
86379
86616
  }
86617
+ function writeGap() {
86618
+ renderer.writeGap();
86619
+ }
86380
86620
  function setStatusRow(row) {
86381
86621
  renderer.setStatusRow(row);
86382
86622
  }
86623
+ function setModeRow(row) {
86624
+ renderer.setModeRow(row);
86625
+ }
86383
86626
  var ANSI_RE, RESET, SYNC_START, SYNC_END, PASTE_OPEN, PASTE_CLOSE, lastEscTime, pendingEscTimer, pasteBuffer, pasteSplitCarry, stdoutIO, renderer, initialized, resizeTimer;
86384
86627
  var init_terminal = __esm({
86385
86628
  "src/terminal.ts"() {
@@ -86412,6 +86655,7 @@ function spinnerFrame(frame) {
86412
86655
  }
86413
86656
  function formatElapsed(startedAt, nowMs) {
86414
86657
  const totalSec = Math.max(0, Math.floor((nowMs - startedAt) / 1e3));
86658
+ if (totalSec === 0) return "<1s";
86415
86659
  if (totalSec < 60) return `${totalSec}s`;
86416
86660
  const min = Math.floor(totalSec / 60);
86417
86661
  const sec = totalSec % 60;
@@ -86523,11 +86767,17 @@ function createCliLogger() {
86523
86767
  }
86524
86768
  return;
86525
86769
  }
86526
- writeLine(` ${warn2("\xB7")} ${meta(message2 + formatContext(context))}`);
86770
+ const indent2 = activeStatus() ? " " : " ";
86771
+ const designed = DESIGNED_SENTENCES[message2]?.(context);
86772
+ if (designed != null) {
86773
+ writeLine(`${indent2}${warn2("\xB7")} ${meta(designed)}`);
86774
+ return;
86775
+ }
86776
+ writeLine(`${indent2}${warn2("\xB7")} ${meta(message2 + formatContext(context))}`);
86527
86777
  }
86528
86778
  };
86529
86779
  }
86530
- var POLL_TROUBLE;
86780
+ var POLL_TROUBLE, DESIGNED_SENTENCES;
86531
86781
  var init_cli_logger = __esm({
86532
86782
  "src/cli-logger.ts"() {
86533
86783
  "use strict";
@@ -86536,6 +86786,19 @@ var init_cli_logger = __esm({
86536
86786
  init_statusline();
86537
86787
  init_colors();
86538
86788
  POLL_TROUBLE = /* @__PURE__ */ new Set(["delegation poll failed", "delegation poll token mint failed"]);
86789
+ DESIGNED_SENTENCES = {
86790
+ "delegation.route_degraded": (ctx) => {
86791
+ if (ctx?.from !== "p2p" || ctx?.to !== "relay") return null;
86792
+ const code2 = typeof ctx.code === "string" ? ` (${ctx.code})` : "";
86793
+ return `direct payment route unavailable${code2} \u2014 this task goes through the relay; no onchain payment leaves the wallet`;
86794
+ },
86795
+ "money_meter.volatile_spend_store": () => "grant spending is metered in memory this session \u2014 the lifetime ceiling re-arms on restart",
86796
+ "relay_key_pin.rotated": (ctx) => {
86797
+ const to = typeof ctx?.toKeyPrefix === "string" ? ` (now ${ctx.toKeyPrefix}\u2026)` : "";
86798
+ return `the relay's signing key rotated${to} \u2014 the new key is pinned`;
86799
+ },
86800
+ "relay_key_pin.mismatch": () => "the relay's signing key does not match the pinned key \u2014 refusing to trust it"
86801
+ };
86539
86802
  }
86540
86803
  });
86541
86804
 
@@ -90417,10 +90680,12 @@ function createTaskRouter(deps) {
90417
90680
  const candidates = settled.filter((r2) => r2.status === "fulfilled").flatMap((r2) => r2.value).slice(0, MAX_TOTAL_FEDERATED);
90418
90681
  return { candidates, federationEdges: allFederationEdges, peerRelayNodes };
90419
90682
  }
90420
- function queryLocalAgents(capability, motebitId, limit = 20, federatedOnly = false) {
90683
+ function queryLocalAgents(capability, motebitId, limit = 20, federatedOnly = false, includeDelisted = false) {
90421
90684
  const now = Date.now();
90422
90685
  const revokedFilter = " AND (revoked IS NULL OR revoked = 0)";
90423
90686
  const fedFilter = federatedOnly ? " AND (federation_visible IS NULL OR federation_visible != 0)" : "";
90687
+ const delistCutoff = Math.floor(now - FRESHNESS_DELISTED_MS);
90688
+ const staleFilter = includeDelisted ? "" : ` AND COALESCE(NULLIF(last_heartbeat, 0), registered_at, 0) >= ${delistCutoff}`;
90424
90689
  let rows;
90425
90690
  if (capability && motebitId) {
90426
90691
  rows = db.prepare(`
@@ -90432,7 +90697,7 @@ function createTaskRouter(deps) {
90432
90697
  } else if (capability) {
90433
90698
  rows = db.prepare(`
90434
90699
  SELECT * FROM agent_registry
90435
- WHERE EXISTS (SELECT 1 FROM json_each(capabilities) WHERE value = ?)${revokedFilter}${fedFilter}
90700
+ WHERE EXISTS (SELECT 1 FROM json_each(capabilities) WHERE value = ?)${revokedFilter}${fedFilter}${staleFilter}
90436
90701
  LIMIT ?
90437
90702
  `).all(capability, limit);
90438
90703
  } else if (motebitId) {
@@ -90441,7 +90706,7 @@ function createTaskRouter(deps) {
90441
90706
  `).all(motebitId, limit);
90442
90707
  } else {
90443
90708
  rows = db.prepare(`
90444
- SELECT * FROM agent_registry WHERE 1=1${revokedFilter}${fedFilter} LIMIT ?
90709
+ SELECT * FROM agent_registry WHERE 1=1${revokedFilter}${fedFilter}${staleFilter} LIMIT ?
90445
90710
  `).all(limit);
90446
90711
  }
90447
90712
  const ids = rows.map((r2) => r2.motebit_id);
@@ -90822,7 +91087,7 @@ async function evaluateSettlementEligibility(db, delegatorId, workerId, delegato
90822
91087
  reason: trustRow ? `Trust ${score} / ${interactions} interactions below established-pair threshold (${P2P_MIN_TRUST_SCORE} / ${P2P_MIN_INTERACTIONS}); delegator did not acknowledge cold-start risk` : "No trust history between agents; delegator did not acknowledge cold-start risk"
90823
91088
  };
90824
91089
  }
90825
- var logger13, circuitBreakerLogger, FRESHNESS_AWAKE_MS, FRESHNESS_RECENT_MS, FRESHNESS_DORMANT_MS, P2P_MIN_TRUST_SCORE, P2P_MIN_INTERACTIONS, P2P_SOVEREIGN_MIN_TRUST_SCORE, P2P_SOVEREIGN_MIN_INTERACTIONS, P2P_BONDED_MIN_TRUST_SCORE, P2P_BONDED_MIN_INTERACTIONS, REFERENCE_BOND_COVERAGE_MULTIPLE;
91090
+ var logger13, circuitBreakerLogger, FRESHNESS_AWAKE_MS, FRESHNESS_RECENT_MS, FRESHNESS_DORMANT_MS, FRESHNESS_DELISTED_MS, P2P_MIN_TRUST_SCORE, P2P_MIN_INTERACTIONS, P2P_SOVEREIGN_MIN_TRUST_SCORE, P2P_SOVEREIGN_MIN_INTERACTIONS, P2P_BONDED_MIN_TRUST_SCORE, P2P_BONDED_MIN_INTERACTIONS, REFERENCE_BOND_COVERAGE_MULTIPLE;
90826
91091
  var init_task_routing = __esm({
90827
91092
  "../../services/relay/dist/task-routing.js"() {
90828
91093
  "use strict";
@@ -90841,6 +91106,7 @@ var init_task_routing = __esm({
90841
91106
  FRESHNESS_AWAKE_MS = 6 * 60 * 1e3;
90842
91107
  FRESHNESS_RECENT_MS = 30 * 60 * 1e3;
90843
91108
  FRESHNESS_DORMANT_MS = 24 * 60 * 60 * 1e3;
91109
+ FRESHNESS_DELISTED_MS = 7 * 24 * 60 * 60 * 1e3;
90844
91110
  P2P_MIN_TRUST_SCORE = 0.6;
90845
91111
  P2P_MIN_INTERACTIONS = 5;
90846
91112
  P2P_SOVEREIGN_MIN_TRUST_SCORE = 0.3;
@@ -96249,9 +96515,13 @@ function registerAgentRoutes(deps) {
96249
96515
  const motebitId = c5.req.query("motebit_id");
96250
96516
  const limitParam = Number(c5.req.query("limit") ?? "20");
96251
96517
  const limit = Math.min(Math.max(1, limitParam), 100);
96252
- const localAgents = taskRouter.queryLocalAgents(capability ?? void 0, motebitId ?? void 0, limit);
96518
+ const includeAll = c5.req.query("include") === "all";
96519
+ const localAgents = taskRouter.queryLocalAgents(capability ?? void 0, motebitId ?? void 0, limit, false, includeAll).filter((a4) => includeAll || a4.motebit_id !== relayIdentity.relayMotebitId);
96253
96520
  const localResults = localAgents.map((a4) => ({
96254
96521
  ...a4,
96522
+ // The relay's own registration reads as a zero-capability ghost in the
96523
+ // agent listing — mark its role when the full view includes it.
96524
+ ...a4.motebit_id === relayIdentity.relayMotebitId ? { role: "relay" } : {},
96255
96525
  source_relay: relayIdentity.relayMotebitId,
96256
96526
  relay_name: federationConfig?.displayName ?? null,
96257
96527
  hop_distance: 0
@@ -101467,8 +101737,36 @@ init_esm_shims();
101467
101737
  init_dist4();
101468
101738
  init_dist6();
101469
101739
  init_dist7();
101470
- init_dist4();
101740
+
101741
+ // src/model-admission.ts
101742
+ init_esm_shims();
101471
101743
  init_dist2();
101744
+ var HOSTED_VENDORS = /* @__PURE__ */ new Set(["anthropic", "openai", "google", "deepseek"]);
101745
+ var VENDOR_PROVIDER_FLAG = {
101746
+ anthropic: "anthropic",
101747
+ openai: "openai",
101748
+ google: "google"
101749
+ };
101750
+ function admitModelForProvider(provider, model) {
101751
+ const hint = modelVendorHint(model);
101752
+ if ((provider === "local-server" || provider === "ollama") && HOSTED_VENDORS.has(hint)) {
101753
+ const flag = VENDOR_PROVIDER_FLAG[hint];
101754
+ return {
101755
+ admissible: false,
101756
+ teach: `${model} is a hosted ${hint} model \u2014 a local server can't serve it. ` + (flag != null ? `Restart with --provider ${flag}, or pick a local model (e.g. /model llama3.2).` : `Pick a local model instead (e.g. /model llama3.2).`)
101757
+ };
101758
+ }
101759
+ if (!providerAcceptsModel(provider, model)) {
101760
+ const flag = VENDOR_PROVIDER_FLAG[hint];
101761
+ return {
101762
+ admissible: false,
101763
+ teach: `${model} belongs to ${hint === "unknown" ? "another provider" : hint} \u2014 the active provider is ${provider}. ` + (flag != null ? `Restart with --provider ${flag} to use it.` : `Pick a ${provider} model instead.`)
101764
+ };
101765
+ }
101766
+ return { admissible: true };
101767
+ }
101768
+
101769
+ // src/index.ts
101472
101770
  init_dist8();
101473
101771
 
101474
101772
  // src/grant-preflight.ts
@@ -102413,7 +102711,6 @@ init_runtime_factory();
102413
102711
 
102414
102712
  // src/stream.ts
102415
102713
  init_esm_shims();
102416
- init_dist4();
102417
102714
  init_colors();
102418
102715
  init_terminal();
102419
102716
  init_statusline();
@@ -102497,7 +102794,6 @@ function renderReceiptLine(receipt, depth, last) {
102497
102794
  async function renderReceipt(receipt, out = (s3) => console.log(s3), trustedAnchor) {
102498
102795
  const lines = renderReceiptLine(receipt, 0, true);
102499
102796
  const header = `${dim("\u2500 receipt ")}${dim("\xB7")} ${cyan(shortHash2(receipt.task_id))}`;
102500
- out("");
102501
102797
  out(header);
102502
102798
  for (const line of lines) out(line);
102503
102799
  let verifiedFlag = false;
@@ -102528,7 +102824,6 @@ async function renderReceipt(receipt, out = (s3) => console.log(s3), trustedAnch
102528
102824
  )}${errorMsg ? dim(" \xB7 " + errorMsg) : ""}`
102529
102825
  );
102530
102826
  }
102531
- out("");
102532
102827
  const ret = { verified: verifiedFlag };
102533
102828
  if (errorMsg !== void 0) ret.error = errorMsg;
102534
102829
  return ret;
@@ -102666,11 +102961,18 @@ function toolDoneLine(name, elapsedMs) {
102666
102961
  async function consumeStream(stream, runtime, opts) {
102667
102962
  let pendingApproval = null;
102668
102963
  let status = null;
102964
+ let thinking = null;
102965
+ const stopThinking = () => {
102966
+ thinking?.stop();
102967
+ thinking = null;
102968
+ };
102669
102969
  const stopStatus = () => {
102970
+ stopThinking();
102670
102971
  const elapsed = status?.stop() ?? 0;
102671
102972
  status = null;
102672
102973
  return elapsed;
102673
102974
  };
102975
+ thinking = startStatus("thinking");
102674
102976
  let lastCompletedReceiptResult = null;
102675
102977
  let sawAnyChunk = false;
102676
102978
  try {
@@ -102678,6 +102980,7 @@ async function consumeStream(stream, runtime, opts) {
102678
102980
  sawAnyChunk = true;
102679
102981
  switch (chunk.type) {
102680
102982
  case "text":
102983
+ stopThinking();
102681
102984
  writeOutput(chunk.text);
102682
102985
  break;
102683
102986
  case "tool_status":
@@ -102686,15 +102989,18 @@ async function consumeStream(stream, runtime, opts) {
102686
102989
  for (const line of renderAuthorityDelta(chunk.name, chunk.missing_authority)) {
102687
102990
  writeLine(meta(line));
102688
102991
  }
102992
+ thinking = startStatus("thinking");
102689
102993
  break;
102690
102994
  }
102691
102995
  if (chunk.name === "delegate_to_agent" && chunk.status === "calling" && status) {
102692
102996
  break;
102693
102997
  }
102694
102998
  if (chunk.status === "calling") {
102999
+ stopThinking();
102695
103000
  status = chunk.name === "delegate_to_agent" ? startStatus("delegating") : startStatus("running", chunk.name);
102696
103001
  } else if (status) {
102697
103002
  writeLine(toolDoneLine(chunk.name, stopStatus()));
103003
+ thinking = startStatus("thinking");
102698
103004
  }
102699
103005
  break;
102700
103006
  case "approval_request":
@@ -102710,6 +103016,7 @@ async function consumeStream(stream, runtime, opts) {
102710
103016
  break;
102711
103017
  case "delegation_start":
102712
103018
  if (status) break;
103019
+ stopThinking();
102713
103020
  status = startStatus("delegating", chunk.tool);
102714
103021
  break;
102715
103022
  case "task_step_narration":
@@ -102717,10 +103024,15 @@ async function consumeStream(stream, runtime, opts) {
102717
103024
  writeLine(` ${meta("\xB7 " + chunk.text.trim())}`);
102718
103025
  break;
102719
103026
  case "delegation_complete":
102720
- if (status) writeLine(toolDoneLine(chunk.tool, stopStatus()));
103027
+ if (status) {
103028
+ writeLine(toolDoneLine(chunk.tool, stopStatus()));
103029
+ thinking = startStatus("thinking");
103030
+ }
102721
103031
  if (chunk.full_receipt) {
102722
103032
  archiveReceipt(chunk.full_receipt);
103033
+ writeGap();
102723
103034
  await renderReceipt(chunk.full_receipt, (line) => writeOutput(line + "\n"));
103035
+ writeGap();
102724
103036
  if (chunk.full_receipt.status === "completed" && chunk.full_receipt.result) {
102725
103037
  lastCompletedReceiptResult = chunk.full_receipt.result;
102726
103038
  }
@@ -102751,23 +103063,15 @@ async function consumeStream(stream, runtime, opts) {
102751
103063
  case "result": {
102752
103064
  const result = chunk.result;
102753
103065
  stopStatus();
102754
- writeOutput("\n\n");
103066
+ writeGap();
102755
103067
  if (result.memoriesFormed.length > 0) {
102756
- writeOutput(
103068
+ writeLine(
102757
103069
  meta(
102758
103070
  ` [memories: ${result.memoriesFormed.map((m4) => m4.content).join(", ")}]`
102759
- ) + "\n"
103071
+ )
102760
103072
  );
103073
+ writeGap();
102761
103074
  }
102762
- const s3 = result.stateAfter;
102763
- writeOutput(
102764
- meta(
102765
- ` [state: attention=${s3.attention.toFixed(2)} confidence=${s3.confidence.toFixed(2)} valence=${s3.affect_valence.toFixed(2)} curiosity=${s3.curiosity.toFixed(2)}]`
102766
- ) + "\n"
102767
- );
102768
- const bodyLine = formatBodyAwareness(result.cues);
102769
- if (bodyLine) writeOutput(meta(` ${bodyLine}`) + "\n");
102770
- writeOutput("\n");
102771
103075
  break;
102772
103076
  }
102773
103077
  }
@@ -102809,6 +103113,17 @@ async function consumeStream(stream, runtime, opts) {
102809
103113
  // src/index.ts
102810
103114
  init_terminal();
102811
103115
 
103116
+ // src/mode-render.ts
103117
+ init_esm_shims();
103118
+ init_colors();
103119
+ function renderModeRow(state) {
103120
+ const parts = [];
103121
+ if (state.attachedPid != null) parts.push(`attached \xB7 coordinator pid ${state.attachedPid}`);
103122
+ if (state.model) parts.push(state.model);
103123
+ if (state.provider) parts.push(state.provider);
103124
+ return meta(` \u2500\u2500 ${parts.join(" \xB7 ")}`);
103125
+ }
103126
+
102812
103127
  // src/runtime-host.ts
102813
103128
  init_esm_shims();
102814
103129
 
@@ -104176,27 +104491,37 @@ init_status_render();
104176
104491
  async function renderStream(stream) {
104177
104492
  let pendingApproval = null;
104178
104493
  let status = null;
104494
+ let thinking = null;
104495
+ const stopThinking = () => {
104496
+ thinking?.stop();
104497
+ thinking = null;
104498
+ };
104179
104499
  const stopStatus = () => {
104500
+ stopThinking();
104180
104501
  const elapsed = status?.stop() ?? 0;
104181
104502
  status = null;
104182
104503
  return elapsed;
104183
104504
  };
104505
+ thinking = startStatus("thinking");
104184
104506
  try {
104185
104507
  for await (const raw of stream) {
104186
104508
  const chunk = raw;
104187
104509
  switch (chunk.type) {
104188
104510
  case "text":
104511
+ stopThinking();
104189
104512
  if (typeof chunk.text === "string") writeOutput(chunk.text);
104190
104513
  break;
104191
104514
  case "tool_status": {
104192
104515
  const name = chunk.name ?? "tool";
104193
104516
  if (chunk.status === "calling") {
104194
104517
  if (name === "delegate_to_agent" && status) break;
104518
+ stopThinking();
104195
104519
  status = name === "delegate_to_agent" ? startStatus("delegating") : startStatus("running", name);
104196
104520
  } else if (status) {
104197
104521
  writeLine(
104198
104522
  ` ${action("\u25CF")} ${action(name)} ${meta(`\xB7 done \xB7 ${formatElapsed(0, stopStatus())}`)}`
104199
104523
  );
104524
+ thinking = startStatus("thinking");
104200
104525
  }
104201
104526
  break;
104202
104527
  }
@@ -104209,6 +104534,7 @@ async function renderStream(stream) {
104209
104534
  break;
104210
104535
  case "delegation_start":
104211
104536
  if (status) break;
104537
+ stopThinking();
104212
104538
  status = startStatus("delegating", chunk.tool ?? "task");
104213
104539
  break;
104214
104540
  case "delegation_complete":
@@ -104218,6 +104544,7 @@ async function renderStream(stream) {
104218
104544
  dim(`[receipt ${chunk.receipt.task_id.slice(0, 8)} \xB7 ${chunk.receipt.status ?? ""}]`)
104219
104545
  );
104220
104546
  }
104547
+ thinking = startStatus("thinking");
104221
104548
  break;
104222
104549
  case "injection_warning":
104223
104550
  writeLine(`${warn2("\u26A0")} suspicious content in ${chunk.tool_name ?? "tool"} output`);
@@ -104292,6 +104619,7 @@ async function runAttachedRepl(client, motebitId) {
104292
104619
 
104293
104620
  `
104294
104621
  );
104622
+ setModeRow(renderModeRow({ attachedPid: client.coordinatorPid }));
104295
104623
  let closed = false;
104296
104624
  client.onClose(() => {
104297
104625
  closed = true;
@@ -104358,6 +104686,7 @@ init_colors();
104358
104686
  // src/slash-commands.ts
104359
104687
  init_esm_shims();
104360
104688
  init_dist2();
104689
+ init_dist4();
104361
104690
  init_dist8();
104362
104691
 
104363
104692
  // src/subcommands/id.ts
@@ -106023,7 +106352,9 @@ async function handleReceiptCommand(args, deps) {
106023
106352
  out(warn2(`No archived receipt for task_id=${taskId}`));
106024
106353
  return;
106025
106354
  }
106355
+ out("");
106026
106356
  await renderReceipt(receipt, out);
106357
+ out("");
106027
106358
  }
106028
106359
  async function drainInvokeStream(stream, out, voice) {
106029
106360
  let textBuf = "";
@@ -106039,7 +106370,9 @@ async function drainInvokeStream(stream, out, voice) {
106039
106370
  if (textBuf.trim()) out(textBuf.trim());
106040
106371
  if (chunk.full_receipt) {
106041
106372
  archiveReceipt(chunk.full_receipt);
106373
+ out("");
106042
106374
  await renderReceipt(chunk.full_receipt, out);
106375
+ out("");
106043
106376
  if (voice && chunk.full_receipt.status === "completed") {
106044
106377
  const spoken = chunk.full_receipt.result ?? "Task complete.";
106045
106378
  void voice.speakIfEnabled(spoken);
@@ -106408,9 +106741,9 @@ ${summary}`);
106408
106741
  }
106409
106742
  case "model": {
106410
106743
  const MODEL_ALIASES = {
106411
- opus: "claude-opus-4-6-20250414",
106412
- sonnet: "claude-sonnet-4-5-latest",
106413
- haiku: "claude-haiku-4-5-20251001",
106744
+ opus: "claude-opus-5",
106745
+ sonnet: "claude-sonnet-5",
106746
+ haiku: "claude-haiku-4-5",
106414
106747
  "gpt-5.4": "gpt-5.4",
106415
106748
  "gpt-5.4-mini": "gpt-5.4-mini",
106416
106749
  "gpt-5.4-nano": "gpt-5.4-nano",
@@ -106418,14 +106751,52 @@ ${summary}`);
106418
106751
  "llama3.2": "llama3.2",
106419
106752
  mistral: "mistral"
106420
106753
  };
106754
+ const envKey = config.provider === "openai" ? process.env["OPENAI_API_KEY"] : process.env["ANTHROPIC_API_KEY"];
106755
+ const catalog = await discoverModels({
106756
+ provider: config.provider,
106757
+ ...envKey != null && envKey !== "" ? { apiKey: envKey } : {},
106758
+ ...config.provider === "local-server" ? { baseUrl: "http://127.0.0.1:11434" } : {}
106759
+ });
106760
+ const liveIds = catalog.source === "live" ? new Set(catalog.models.map((m4) => m4.id)) : null;
106761
+ const providerLabel = (modelId2) => {
106762
+ const hint = modelVendorHint(modelId2);
106763
+ return hint === "local" ? "local-server" : hint;
106764
+ };
106421
106765
  const showModelList = (current2) => {
106766
+ if (catalog.source === "live") {
106767
+ const col2 = Math.max(...catalog.models.map((m4) => m4.id.length)) + 2;
106768
+ for (const m4 of catalog.models) {
106769
+ const active = m4.id === current2;
106770
+ const marker = active ? green(" \u25CF") : " ";
106771
+ const gap = " ".repeat(Math.max(1, col2 - m4.id.length));
106772
+ console.log(`${marker} ${cyan(m4.id)}${gap}${dim(m4.displayName ?? "")}`);
106773
+ }
106774
+ console.log(dim(`
106775
+ live from ${config.provider}`));
106776
+ return;
106777
+ }
106422
106778
  const col = Math.max(...Object.keys(MODEL_ALIASES).map((k4) => k4.length)) + 2;
106423
106779
  for (const [alias, modelId2] of Object.entries(MODEL_ALIASES)) {
106424
106780
  const active = modelId2 === current2 || alias === current2;
106781
+ const servable = admitModelForProvider(config.provider, modelId2).admissible;
106425
106782
  const marker = active ? green(" \u25CF") : " ";
106426
106783
  const gap = " ".repeat(Math.max(1, col - alias.length));
106427
- console.log(`${marker} ${cyan(alias)}${gap}${dim(modelId2)}`);
106784
+ if (servable) {
106785
+ console.log(
106786
+ `${marker} ${cyan(alias)}${gap}${dim(modelId2 + " \xB7 " + providerLabel(modelId2))}`
106787
+ );
106788
+ } else {
106789
+ console.log(
106790
+ dim(
106791
+ `${marker} ${alias}${gap}${modelId2} \xB7 needs --provider ${providerLabel(modelId2)}`
106792
+ )
106793
+ );
106794
+ }
106428
106795
  }
106796
+ console.log(
106797
+ dim(`
106798
+ offline list \u2014 live catalog unavailable (${catalog.fallbackReason ?? "?"})`)
106799
+ );
106429
106800
  };
106430
106801
  if (!args) {
106431
106802
  const current2 = runtime.currentModel ?? "unknown";
@@ -106440,7 +106811,7 @@ Current model: ${cyan(current2)}
106440
106811
  }
106441
106812
  const input = args.toLowerCase();
106442
106813
  const resolved = MODEL_ALIASES[input];
106443
- const isFullId = Object.values(MODEL_ALIASES).includes(args);
106814
+ const isFullId = Object.values(MODEL_ALIASES).includes(args) || (liveIds?.has(args) ?? false);
106444
106815
  if (!resolved && !isFullId) {
106445
106816
  console.log(`
106446
106817
  Unknown model: ${cyan(args)}
@@ -106452,9 +106823,28 @@ Unknown model: ${cyan(args)}
106452
106823
  break;
106453
106824
  }
106454
106825
  const modelId = resolved ?? args;
106826
+ if (liveIds != null && !liveIds.has(modelId)) {
106827
+ console.log(
106828
+ `
106829
+ ${warn2(`${modelId} is not served by ${config.provider} right now \u2014 /model lists what is`)}
106830
+ `
106831
+ );
106832
+ break;
106833
+ }
106834
+ const admission = admitModelForProvider(config.provider, modelId);
106835
+ if (!admission.admissible) {
106836
+ console.log(`
106837
+ ${warn2(admission.teach ?? "model not servable on this provider")}
106838
+ `);
106839
+ break;
106840
+ }
106455
106841
  runtime.setModel(modelId);
106456
106842
  if (fullConfig) {
106457
106843
  fullConfig.default_model = modelId;
106844
+ const persistable = ["anthropic", "openai", "google", "local-server", "proxy"];
106845
+ if (persistable.includes(config.provider)) {
106846
+ fullConfig.default_provider = config.provider;
106847
+ }
106458
106848
  saveFullConfig(fullConfig);
106459
106849
  }
106460
106850
  console.log();
@@ -125425,7 +125815,7 @@ async function main() {
125425
125815
  }
125426
125816
  }
125427
125817
  if (personalityConfig.default_model != null && personalityConfig.default_model !== "" && !process.argv.includes("--model")) {
125428
- if (providerAcceptsModel(config.provider, personalityConfig.default_model)) {
125818
+ if (admitModelForProvider(config.provider, personalityConfig.default_model).admissible) {
125429
125819
  config.model = personalityConfig.default_model;
125430
125820
  } else {
125431
125821
  console.log(
@@ -125435,11 +125825,14 @@ async function main() {
125435
125825
  );
125436
125826
  }
125437
125827
  }
125438
- if (process.argv.includes("--model") && !providerAcceptsModel(config.provider, config.model)) {
125439
- console.error(
125440
- `Model "${config.model}" does not belong to provider "${config.provider}" \u2014 pick a matching pair (e.g. --provider anthropic --model claude-sonnet-4-6), or drop --model to use the provider's default.`
125441
- );
125442
- process.exit(1);
125828
+ if (process.argv.includes("--model")) {
125829
+ const admission = admitModelForProvider(config.provider, config.model);
125830
+ if (!admission.admissible) {
125831
+ console.error(
125832
+ `Model "${config.model}" does not belong to provider "${config.provider}" \u2014 ${admission.teach ?? "pick a matching pair, or drop --model to use the provider's default."}`
125833
+ );
125834
+ process.exit(1);
125835
+ }
125443
125836
  }
125444
125837
  if (fullConfig.max_tokens != null && !process.argv.includes("--max-tokens")) {
125445
125838
  config.maxTokens = fullConfig.max_tokens;
@@ -125829,6 +126222,12 @@ async function main() {
125829
126222
  }
125830
126223
  }
125831
126224
  const prompt2 = () => {
126225
+ setModeRow(
126226
+ renderModeRow({
126227
+ model: runtime.currentModel ?? config.model,
126228
+ provider: config.provider
126229
+ })
126230
+ );
125832
126231
  readInput(prompt("you>") + " ").then(
125833
126232
  (line) => void handleLine(line),
125834
126233
  () => {
@@ -125875,16 +126274,8 @@ ${prompt("mote>")} ${result.response}
125875
126274
  console.log(
125876
126275
  meta(` [memories: ${result.memoriesFormed.map((m4) => m4.content).join(", ")}]`)
125877
126276
  );
126277
+ console.log();
125878
126278
  }
125879
- const s3 = result.stateAfter;
125880
- console.log(
125881
- meta(
125882
- ` [state: attention=${s3.attention.toFixed(2)} confidence=${s3.confidence.toFixed(2)} valence=${s3.affect_valence.toFixed(2)} curiosity=${s3.curiosity.toFixed(2)}]`
125883
- )
125884
- );
125885
- const bodyLine = formatBodyAwareness(result.cues);
125886
- if (bodyLine) console.log(meta(` ${bodyLine}`));
125887
- console.log();
125888
126279
  } else {
125889
126280
  writeOutput("\n" + prompt("mote>") + " ");
125890
126281
  const grantOptions = grantPresenter?.delegationForTurn() ?? void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motebit",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "type": "module",
5
5
  "description": "Reference runtime and operator console for sovereign AI agents — REPL, daemon, delegation, MCP server. Persistent Ed25519 identity, signed execution receipts, governance at the boundary.",
6
6
  "main": "./dist/index.js",
@@ -76,35 +76,35 @@
76
76
  "typescript": "^5.6.0",
77
77
  "vitest": "^4.1.10",
78
78
  "@motebit/ai-core": "0.0.0-private",
79
- "@motebit/behavior-engine": "0.0.0-private",
80
- "@motebit/core-identity": "0.0.0-private",
81
79
  "@motebit/crypto": "3.19.1",
80
+ "@motebit/core-identity": "0.0.0-private",
81
+ "@motebit/behavior-engine": "0.0.0-private",
82
82
  "@motebit/encryption": "0.0.0-private",
83
- "@motebit/event-log": "0.0.0-private",
84
- "@motebit/gradient": "0.0.0-private",
85
83
  "@motebit/identity-file": "0.0.0-private",
84
+ "@motebit/event-log": "0.0.0-private",
86
85
  "@motebit/mcp-client": "0.0.0-private",
87
86
  "@motebit/mcp-server": "0.0.0-private",
87
+ "@motebit/gradient": "0.0.0-private",
88
88
  "@motebit/memory-graph": "0.0.0-private",
89
- "@motebit/persistence": "0.0.0-private",
90
89
  "@motebit/protocol": "3.15.1",
91
90
  "@motebit/planner": "0.0.0-private",
92
- "@motebit/policy": "0.0.0-private",
91
+ "@motebit/persistence": "0.0.0-private",
93
92
  "@motebit/privacy-layer": "0.0.0-private",
93
+ "@motebit/policy": "0.0.0-private",
94
94
  "@motebit/relay": "0.0.0-private",
95
- "@motebit/relay-client": "0.0.0-private",
96
95
  "@motebit/runtime": "0.0.0-private",
97
96
  "@motebit/runtime-host": "0.0.0-private",
98
- "@motebit/sdk": "2.5.4",
99
- "@motebit/self-knowledge": "0.0.0-private",
97
+ "@motebit/sdk": "2.6.0",
98
+ "@motebit/relay-client": "0.0.0-private",
100
99
  "@motebit/skills": "0.0.0-private",
101
- "@motebit/state-vector": "0.0.0-private",
100
+ "@motebit/self-knowledge": "0.0.0-private",
102
101
  "@motebit/sync-engine": "0.0.0-private",
102
+ "@motebit/state-vector": "0.0.0-private",
103
103
  "@motebit/tools": "0.0.0-private",
104
104
  "@motebit/verify": "1.8.12",
105
105
  "@motebit/voice": "0.0.0-private",
106
- "@motebit/wallet-solana": "0.0.0-private",
107
- "@motebit/wire-schemas": "0.0.0-private"
106
+ "@motebit/wire-schemas": "0.0.0-private",
107
+ "@motebit/wallet-solana": "0.0.0-private"
108
108
  },
109
109
  "scripts": {
110
110
  "start": "tsx src/index.ts",