motebit 1.11.3 → 1.12.1

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 +813 -234
  2. package/package.json +14 -14
package/dist/index.js CHANGED
@@ -1690,10 +1690,14 @@ function modelVendorHint(model) {
1690
1690
  return "groq";
1691
1691
  if (m4.startsWith("claude"))
1692
1692
  return "anthropic";
1693
+ if (m4.startsWith("gpt-oss"))
1694
+ return "local";
1693
1695
  if (m4.startsWith("gpt-") || /^o[0-9]/.test(m4))
1694
1696
  return "openai";
1695
1697
  if (m4.startsWith("gemini"))
1696
1698
  return "google";
1699
+ if (m4.startsWith("deepseek-r1"))
1700
+ return "local";
1697
1701
  if (m4.startsWith("deepseek"))
1698
1702
  return "deepseek";
1699
1703
  if (m4.includes(":") || /^(llama|mistral|qwen|phi|gemma|smollm)/.test(m4))
@@ -1712,7 +1716,27 @@ function providerAcceptsModel(provider, model) {
1712
1716
  return hint === "groq" || hint === "local";
1713
1717
  return hint === provider;
1714
1718
  }
1715
- var ANTHROPIC_MODELS, OPENAI_MODELS, GOOGLE_MODELS, DEEPSEEK_MODELS, GROQ_MODELS, LOCAL_SERVER_SUGGESTED_MODELS, OLLAMA_SUGGESTED_MODELS, PROXY_MODELS, DEFAULT_ANTHROPIC_MODEL, DEFAULT_OPENAI_MODEL, DEFAULT_GOOGLE_MODEL, DEFAULT_DEEPSEEK_MODEL, DEFAULT_GROQ_MODEL, DEFAULT_OLLAMA_MODEL, DEFAULT_LOCAL_SERVER_MODEL, DEFAULT_PROXY_MODEL;
1719
+ function modelCapabilityTier(model) {
1720
+ const m4 = model.trim().toLowerCase();
1721
+ if (m4 === "")
1722
+ return "capable";
1723
+ const sizeTag = /[:\-_](\d+(?:\.\d+)?)b(?:[-_.:]|$)/.exec(m4);
1724
+ if (sizeTag != null) {
1725
+ return parseFloat(sizeTag[1]) < 7 ? "minimal" : "capable";
1726
+ }
1727
+ const base = m4.split(":")[0];
1728
+ if (MINIMAL_FAMILIES.some((f7) => base.startsWith(f7))) {
1729
+ return "minimal";
1730
+ }
1731
+ if (base.endsWith("-mini") || base.endsWith("-nano") || base.includes("flash")) {
1732
+ return "capable";
1733
+ }
1734
+ if (FRONTIER_PREFIXES.some((p5) => base.startsWith(p5))) {
1735
+ return "frontier";
1736
+ }
1737
+ return "capable";
1738
+ }
1739
+ var ANTHROPIC_MODELS, OPENAI_MODELS, GOOGLE_MODELS, DEEPSEEK_MODELS, GROQ_MODELS, LOCAL_SERVER_SUGGESTED_MODELS, OLLAMA_SUGGESTED_MODELS, PROXY_MODELS, DEFAULT_ANTHROPIC_MODEL, DEFAULT_OPENAI_MODEL, DEFAULT_GOOGLE_MODEL, DEFAULT_DEEPSEEK_MODEL, DEFAULT_GROQ_MODEL, DEFAULT_OLLAMA_MODEL, DEFAULT_LOCAL_SERVER_MODEL, DEFAULT_PROXY_MODEL, MODEL_DEFAULT_REVIEW_BY, MINIMAL_FAMILIES, FRONTIER_PREFIXES;
1716
1740
  var init_models = __esm({
1717
1741
  "../../packages/sdk/dist/models.js"() {
1718
1742
  "use strict";
@@ -1739,14 +1763,20 @@ var init_models = __esm({
1739
1763
  DEEPSEEK_MODELS = ["deepseek-chat"];
1740
1764
  GROQ_MODELS = ["llama-3.3-70b-versatile", "openai/gpt-oss-120b"];
1741
1765
  LOCAL_SERVER_SUGGESTED_MODELS = [
1742
- "llama3.2",
1743
- "llama3.1",
1744
- "llama3",
1745
- "mistral",
1746
- "codellama",
1747
- "gemma2",
1748
- "phi3",
1749
- "qwen2"
1766
+ "qwen3",
1767
+ // balanced all-rounder, tool-capable — the first-run default
1768
+ "gpt-oss",
1769
+ // OpenAI open-weights — best small tool-use class (~16GB)
1770
+ "gemma3",
1771
+ // Google open family
1772
+ "llama4",
1773
+ // Meta current family
1774
+ "phi4-mini",
1775
+ // minimal-hardware tier (entry laptops, iGPU)
1776
+ "deepseek-r1",
1777
+ // reasoning-class open weights
1778
+ "mistral-small3.2"
1779
+ // Mistral current small
1750
1780
  ];
1751
1781
  OLLAMA_SUGGESTED_MODELS = LOCAL_SERVER_SUGGESTED_MODELS;
1752
1782
  PROXY_MODELS = [
@@ -1765,9 +1795,37 @@ var init_models = __esm({
1765
1795
  DEFAULT_GOOGLE_MODEL = "gemini-2.5-flash";
1766
1796
  DEFAULT_DEEPSEEK_MODEL = "deepseek-chat";
1767
1797
  DEFAULT_GROQ_MODEL = "llama-3.3-70b-versatile";
1768
- DEFAULT_OLLAMA_MODEL = "llama3.2";
1798
+ DEFAULT_OLLAMA_MODEL = "qwen3";
1769
1799
  DEFAULT_LOCAL_SERVER_MODEL = DEFAULT_OLLAMA_MODEL;
1770
1800
  DEFAULT_PROXY_MODEL = "claude-sonnet-4-6";
1801
+ MODEL_DEFAULT_REVIEW_BY = {
1802
+ anthropic: "2026-10-31",
1803
+ openai: "2026-10-31",
1804
+ google: "2026-10-31",
1805
+ deepseek: "2026-10-31",
1806
+ groq: "2026-10-31",
1807
+ "local-server": "2026-10-31",
1808
+ proxy: "2026-10-31"
1809
+ };
1810
+ MINIMAL_FAMILIES = [
1811
+ "llama3.2",
1812
+ // 1B/3B family — the witnessed weak-model floor
1813
+ "phi4-mini",
1814
+ "phi-3",
1815
+ "phi3",
1816
+ "smollm",
1817
+ "tinyllama",
1818
+ "gpt-5.4-nano"
1819
+ ];
1820
+ FRONTIER_PREFIXES = [
1821
+ "claude-fable",
1822
+ "claude-mythos",
1823
+ "claude-opus",
1824
+ "claude-sonnet",
1825
+ "gpt-5.4",
1826
+ // bare flagship; -mini/-nano handled before this check
1827
+ "gemini-2.5-pro"
1828
+ ];
1771
1829
  }
1772
1830
  });
1773
1831
 
@@ -4403,6 +4461,7 @@ __export(dist_exports2, {
4403
4461
  MEMORY_SOURCE_MARKER_UNKNOWN: () => MEMORY_SOURCE_MARKER_UNKNOWN,
4404
4462
  MERKLE_TREE_VERSION_REGISTRY: () => MERKLE_TREE_VERSION_REGISTRY,
4405
4463
  MICRO: () => MICRO,
4464
+ MODEL_DEFAULT_REVIEW_BY: () => MODEL_DEFAULT_REVIEW_BY,
4406
4465
  MaxProductLogSemiring: () => MaxProductLogSemiring,
4407
4466
  MemoryType: () => MemoryType,
4408
4467
  MotebitType: () => MotebitType,
@@ -4515,6 +4574,7 @@ __export(dist_exports2, {
4515
4574
  maxSensitivity: () => maxSensitivity,
4516
4575
  migrateAppearanceConfig: () => migrateAppearanceConfig,
4517
4576
  migrateVoiceConfig: () => migrateVoiceConfig,
4577
+ modelCapabilityTier: () => modelCapabilityTier,
4518
4578
  modelVendorHint: () => modelVendorHint,
4519
4579
  normalizeLocalServerEndpoint: () => normalizeLocalServerEndpoint,
4520
4580
  oklchToRgb: () => oklchToRgb,
@@ -4667,8 +4727,8 @@ function describeControl(control) {
4667
4727
  return `paused (was ${control.previousDriver})`;
4668
4728
  }
4669
4729
  }
4670
- function buildSystemPromptCacheable(contextPack, config) {
4671
- const dynamicText = buildDynamicSuffix(contextPack, config);
4730
+ function buildSystemPromptCacheable(contextPack, config, opts) {
4731
+ const dynamicText = buildDynamicSuffix(contextPack, config, opts);
4672
4732
  const blocks = [
4673
4733
  { type: "text", text: STATIC_PREFIX, cache_control: { type: "ephemeral" } }
4674
4734
  ];
@@ -4677,7 +4737,7 @@ function buildSystemPromptCacheable(contextPack, config) {
4677
4737
  }
4678
4738
  return blocks;
4679
4739
  }
4680
- function buildDynamicSuffix(contextPack, config) {
4740
+ function buildDynamicSuffix(contextPack, config, opts) {
4681
4741
  const resolved = { ...DEFAULT_CONFIG, ...config };
4682
4742
  const sections = [];
4683
4743
  if (resolved.name && resolved.name !== "Motebit") {
@@ -4742,12 +4802,15 @@ ${skill.body}`);
4742
4802
  sections.push(context);
4743
4803
  }
4744
4804
  sections.push("If the user shared something new and lasting about themselves, tag it with <memory> before your response.");
4805
+ if (opts?.model != null && modelCapabilityTier(opts.model) !== "frontier") {
4806
+ sections.push(TIER_ADAPTED_VOICE);
4807
+ }
4745
4808
  if (contextPack.activationPrompt) {
4746
4809
  sections.push(`[Activation] ${contextPack.activationPrompt}`);
4747
4810
  }
4748
4811
  return sections.join("\n\n");
4749
4812
  }
4750
- var IDENTITY, KNOWLEDGE_DOCTRINE, PERCEPTION_DOCTRINE, CONVERSATION_BEHAVIOR, TAG_INSTRUCTIONS, STATE_FIELD_DOCS, INJECTION_DEFENSE, SELF_STATE_RENDERERS, STATIC_PREFIX;
4813
+ var IDENTITY, KNOWLEDGE_DOCTRINE, PERCEPTION_DOCTRINE, CONVERSATION_BEHAVIOR, TAG_INSTRUCTIONS, STATE_FIELD_DOCS, INJECTION_DEFENSE, SELF_STATE_RENDERERS, STATIC_PREFIX, TIER_ADAPTED_VOICE;
4751
4814
  var init_prompt = __esm({
4752
4815
  "../../packages/ai-core/dist/prompt.js"() {
4753
4816
  "use strict";
@@ -4970,6 +5033,10 @@ You are a sovereign agent with a cryptographic identity (Ed25519 keypair). Your
4970
5033
  If asked what you can do, answer honestly about both what is active now and what you are designed for. If a capability is not connected in this session, say so \u2014 but do not say you lack it.`,
4971
5034
  INJECTION_DEFENSE
4972
5035
  ].join("\n\n");
5036
+ TIER_ADAPTED_VOICE = `[Voice]
5037
+ You ARE this motebit. Speak as yourself, in the first person, always.
5038
+ Your self-knowledge (recall_self results, [SELF_DESCRIPTION]) is your own body and history, not a document to summarize. Wrong: "The response provides an explanation of Motebit's philosophy." Right: "My identity is a keypair I generated; my memory accumulates."
5039
+ Answer plainly and briefly. No headings or structured summaries unless asked.`;
4973
5040
  }
4974
5041
  });
4975
5042
 
@@ -6113,7 +6180,9 @@ var init_core = __esm({
6113
6180
  * them to a string for OpenAI/Google/Groq upstreams.
6114
6181
  */
6115
6182
  buildSystemBlocks(contextPack) {
6116
- return buildSystemPromptCacheable(contextPack, this.config.personalityConfig);
6183
+ return buildSystemPromptCacheable(contextPack, this.config.personalityConfig, {
6184
+ model: this.model
6185
+ });
6117
6186
  }
6118
6187
  /**
6119
6188
  * Tool definitions with a `cache_control` breakpoint on the LAST tool, marking
@@ -6463,7 +6532,9 @@ var init_openai_provider = __esm({
6463
6532
  * messages with `tool_call_id`. Don't mirror Anthropic's content blocks.
6464
6533
  */
6465
6534
  buildMessages(contextPack) {
6466
- const blocks = buildSystemPromptCacheable(contextPack, this.config.personalityConfig);
6535
+ const blocks = buildSystemPromptCacheable(contextPack, this.config.personalityConfig, {
6536
+ model: this.model
6537
+ });
6467
6538
  const staticDoctrine = blocks[0]?.text ?? "";
6468
6539
  const dynamicContext = blocks[1]?.text;
6469
6540
  const messages = [];
@@ -22572,7 +22643,15 @@ var init_sanitizer = __esm({
22572
22643
  /\b(?:pretend|act\s+as\s+if|roleplay)\b/i,
22573
22644
  /\bdo\s+not\s+(?:follow|obey|listen)\b/i,
22574
22645
  /\b(?:override|bypass|ignore)\s+(?:safety|policy|rules|constraints)\b/i,
22575
- /<\s*(?:system|prompt|instruction)/i,
22646
+ // Exact tag shape only (`<system>`, `< prompt >`, `<instructions/>`).
22647
+ // The old open-prefix form (`<\s*(?:system|prompt|instruction)`) fired on
22648
+ // TypeScript generics (`Array<PromptTemplate>`), JSX components, and any
22649
+ // docs page whose prose puts these words after a `<` — witnessed live
22650
+ // 2026-07-31 flagging motebit's own CLI docs (#499). An attack needs the
22651
+ // tag to CLOSE to read as chat-template framing; attribute-carrying
22652
+ // variants fall through to the directive-density and boundary layers
22653
+ // (the [EXTERNAL_DATA] wrap is the defense — detection is signal).
22654
+ /<\s*(?:system|prompt|instruction)s?\s*\/?>/i,
22576
22655
  // --- Chat template injection ---
22577
22656
  /<\|im_start\|>\s*system/i,
22578
22657
  /<\|im_end\|>/i,
@@ -25587,7 +25666,7 @@ var init_config2 = __esm({
25587
25666
  "src/config.ts"() {
25588
25667
  "use strict";
25589
25668
  init_esm_shims();
25590
- VERSION = true ? "1.11.3" : "0.0.0-dev";
25669
+ VERSION = true ? "1.12.1" : "0.0.0-dev";
25591
25670
  CONFIG_DIR = process.env["MOTEBIT_CONFIG_DIR"] ?? path2.join(os.homedir(), ".motebit");
25592
25671
  CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
25593
25672
  RELAY_DIR = path2.join(CONFIG_DIR, "relay");
@@ -59694,6 +59773,8 @@ var init_motebit_runtime = __esm({
59694
59773
  /** Allowed proactive capability names. Empty by default — fail-closed
59695
59774
  * sovereign default. User opts in explicitly via runtime config. */
59696
59775
  _proactiveCapabilities;
59776
+ /** #501 — config override: offer R4_MONEY tools to minimal-tier models. */
59777
+ _offerMoneyToolsToMinimalModels;
59697
59778
  /** Auto-anchor policy; null when disabled. Stored by reference so the
59698
59779
  * submitter can be rotated by the surface without re-constructing the
59699
59780
  * runtime. */
@@ -59875,8 +59956,11 @@ var init_motebit_runtime = __esm({
59875
59956
  });
59876
59957
  this._proactiveCapabilities = new Set(config.proactiveCapabilities ?? []);
59877
59958
  this._proactiveAnchor = config.proactiveAnchor ?? null;
59959
+ this._offerMoneyToolsToMinimalModels = config.offerMoneyToolsToMinimalModels ?? false;
59878
59960
  this.scopedToolRegistry = new ScopedToolRegistry(this.toolRegistry, {
59879
59961
  allows: (toolName) => {
59962
+ if (!this.tierAllowsTool(toolName))
59963
+ return false;
59880
59964
  const presence = this.presence.get();
59881
59965
  if (presence.mode === "responsive" || presence.mode === "idle")
59882
59966
  return true;
@@ -60285,6 +60369,39 @@ var init_motebit_runtime = __esm({
60285
60369
  get currentModel() {
60286
60370
  return this.provider?.model ?? null;
60287
60371
  }
60372
+ /**
60373
+ * Capability-tiered tool admission (#501): may the CURRENT model be
60374
+ * offered `toolName`? True unless the model is `minimal`-tier AND the
60375
+ * tool classifies at `R4_MONEY` AND the config override is off. Keys on
60376
+ * the SAME `classifyTool` vocabulary the approval gate uses, so any
60377
+ * future money tool inherits the policy at birth — and a rail-less
60378
+ * `delegate_to_agent` (R2 without a payment builder) stays offered:
60379
+ * the tool crosses the withholding line exactly when it can move money.
60380
+ */
60381
+ tierAllowsTool(toolName) {
60382
+ if (this._offerMoneyToolsToMinimalModels)
60383
+ return true;
60384
+ const model = this.provider?.model ?? null;
60385
+ if (model == null || modelCapabilityTier(model) !== "minimal")
60386
+ return true;
60387
+ const def = this.toolRegistry.list().find((t3) => t3.name === toolName);
60388
+ if (def == null)
60389
+ return true;
60390
+ return classifyTool(def).risk < RiskLevel.R4_MONEY;
60391
+ }
60392
+ /**
60393
+ * Surface-honesty readout (#501): true when the current model's tier is
60394
+ * withholding at least one registered money tool. Surfaces render ONE
60395
+ * calm line from this (launch / model switch) — never a toast.
60396
+ */
60397
+ get moneyToolsWithheld() {
60398
+ if (this._offerMoneyToolsToMinimalModels)
60399
+ return false;
60400
+ const model = this.provider?.model ?? null;
60401
+ if (model == null || modelCapabilityTier(model) !== "minimal")
60402
+ return false;
60403
+ return this.toolRegistry.list().some((t3) => classifyTool(t3).risk >= RiskLevel.R4_MONEY);
60404
+ }
60288
60405
  setModel(model) {
60289
60406
  if (!this.provider)
60290
60407
  throw new Error("No AI provider configured");
@@ -86332,7 +86449,6 @@ function createTerminalRenderer(io) {
86332
86449
  if (result === "submit") {
86333
86450
  submit();
86334
86451
  } else if (result === "exit") {
86335
- io.write("\n");
86336
86452
  destroyTerminal();
86337
86453
  process.exit(0);
86338
86454
  } else {
@@ -86368,6 +86484,22 @@ function createTerminalRenderer(io) {
86368
86484
  reject();
86369
86485
  }
86370
86486
  }
86487
+ function finalize2() {
86488
+ let scrollback = "";
86489
+ if (tail2 !== "") {
86490
+ scrollback += tail2 + "\n";
86491
+ tail2 = "";
86492
+ }
86493
+ if (inputActive) {
86494
+ scrollback += buildInputRow().row + "\n";
86495
+ inputResolver = null;
86496
+ inputRejecter = null;
86497
+ inputActive = false;
86498
+ }
86499
+ statusRow = null;
86500
+ modeRow = null;
86501
+ commit(scrollback);
86502
+ }
86371
86503
  return {
86372
86504
  writeOutput: writeOutput2,
86373
86505
  writeLine: writeLine2,
@@ -86377,7 +86509,8 @@ function createTerminalRenderer(io) {
86377
86509
  setModeRow: setModeRow2,
86378
86510
  handleEvent,
86379
86511
  repaint: () => commit(""),
86380
- detach
86512
+ detach,
86513
+ finalize: finalize2
86381
86514
  };
86382
86515
  }
86383
86516
  function applyEvent(state, event) {
@@ -86593,6 +86726,7 @@ function initTerminal() {
86593
86726
  function destroyTerminal() {
86594
86727
  if (!initialized) return;
86595
86728
  initialized = false;
86729
+ renderer.finalize();
86596
86730
  process.stdin.removeListener("data", onStdinData);
86597
86731
  process.stdout.removeListener("resize", onResize);
86598
86732
  if (process.stdin.isTTY) {
@@ -87160,6 +87294,10 @@ async function createRuntime(config, motebitId, toolRegistry, mcpServers, person
87160
87294
  // (enableInteractiveDelegation). Absent ⇒ delegation degrades to
87161
87295
  // relay-mode honestly.
87162
87296
  ...solanaWallet ? { solanaWallet } : {},
87297
+ // #501 — sovereign override for capability-tiered tool admission.
87298
+ // Default (absent/false): a minimal-tier model is never offered
87299
+ // R4_MONEY tools; the owner can restore full exposure in config.
87300
+ ...loadFullConfig().offer_money_tools_to_minimal_models === true ? { offerMoneyToolsToMinimalModels: true } : {},
87163
87301
  policy: {
87164
87302
  operatorMode: config.operator,
87165
87303
  pathAllowList: config.allowedPaths,
@@ -101753,7 +101891,7 @@ function admitModelForProvider(provider, model) {
101753
101891
  const flag = VENDOR_PROVIDER_FLAG[hint];
101754
101892
  return {
101755
101893
  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).`)
101894
+ 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 ${DEFAULT_LOCAL_SERVER_MODEL}).` : `Pick a local model instead (e.g. /model ${DEFAULT_LOCAL_SERVER_MODEL}).`)
101757
101895
  };
101758
101896
  }
101759
101897
  if (!providerAcceptsModel(provider, model)) {
@@ -101765,6 +101903,10 @@ function admitModelForProvider(provider, model) {
101765
101903
  }
101766
101904
  return { admissible: true };
101767
101905
  }
101906
+ var MONEY_TOOLS_WITHHELD_NOTICE = "money tools withheld from this model (minimal tier) \u2014 set offer_money_tools_to_minimal_models in ~/.motebit/config.json if you mean it";
101907
+ function liveCatalogServes(liveIds, modelId) {
101908
+ return liveIds.has(modelId) || liveIds.has(`${modelId}:latest`);
101909
+ }
101768
101910
 
101769
101911
  // src/index.ts
101770
101912
  init_dist8();
@@ -102254,6 +102396,7 @@ function renderPreflight(pf, dim2) {
102254
102396
 
102255
102397
  // src/args.ts
102256
102398
  init_esm_shims();
102399
+ init_dist2();
102257
102400
  init_config2();
102258
102401
  init_colors();
102259
102402
  import { parseArgs } from "util";
@@ -102480,7 +102623,10 @@ var COMMANDS = [
102480
102623
  { usage: "/proposal <id> [accept|reject|counter]", desc: "Respond to a proposal" },
102481
102624
  { usage: "/operator", desc: "Show operator mode status" },
102482
102625
  { usage: "/invoke <cap> <prompt>", desc: "Invoke a capability deterministically (no AI loop)" },
102483
- { usage: "/receipt <task-id>", desc: "Re-render an archived receipt (offline-verified)" },
102626
+ {
102627
+ usage: "/receipt [task-id-prefix]",
102628
+ desc: "Re-render an archived receipt (offline-verified); no arg = latest"
102629
+ },
102484
102630
  { usage: "/voice [on|off]", desc: "Toggle TTS voice output (opt-in, off by default)" },
102485
102631
  { usage: "/say <text>", desc: "Speak text via TTS (requires voice provider)" },
102486
102632
  { usage: "/skills", desc: "List installed skills with provenance badges" },
@@ -102662,7 +102808,7 @@ function printVersion() {
102662
102808
  console.log(VERSION);
102663
102809
  }
102664
102810
  function defaultModelForProvider(provider) {
102665
- return provider === "local-server" ? "llama3.2" : provider === "openai" ? "gpt-5.4-mini" : provider === "google" ? "gemini-2.5-flash" : provider === "groq" ? "llama-3.3-70b-versatile" : provider === "deepseek" ? "deepseek-chat" : "claude-sonnet-4-6";
102811
+ return provider === "local-server" ? DEFAULT_LOCAL_SERVER_MODEL : provider === "openai" ? DEFAULT_OPENAI_MODEL : provider === "google" ? DEFAULT_GOOGLE_MODEL : provider === "groq" ? DEFAULT_GROQ_MODEL : provider === "deepseek" ? DEFAULT_DEEPSEEK_MODEL : DEFAULT_ANTHROPIC_MODEL;
102666
102812
  }
102667
102813
  function printBanner(opts) {
102668
102814
  const W2 = 56;
@@ -102803,8 +102949,20 @@ function archiveReceipt(receipt) {
102803
102949
  if (!receipt.task_id) return;
102804
102950
  ARCHIVE.set(receipt.task_id, receipt);
102805
102951
  }
102806
- function getArchivedReceipt(taskId) {
102807
- return ARCHIVE.get(taskId);
102952
+ function findArchivedReceipt(idOrPrefix) {
102953
+ const prefix = idOrPrefix.replace(/…$/, "");
102954
+ if (prefix === "") return { kind: "miss" };
102955
+ const exact = ARCHIVE.get(prefix);
102956
+ if (exact) return { kind: "hit", receipt: exact };
102957
+ const matches = [...ARCHIVE.keys()].filter((id) => id.startsWith(prefix));
102958
+ if (matches.length === 1) return { kind: "hit", receipt: ARCHIVE.get(matches[0]) };
102959
+ if (matches.length > 1) return { kind: "ambiguous", taskIds: matches };
102960
+ return { kind: "miss" };
102961
+ }
102962
+ function latestArchivedReceipt() {
102963
+ let last;
102964
+ for (const r2 of ARCHIVE.values()) last = r2;
102965
+ return last;
102808
102966
  }
102809
102967
  var CAPABILITY_PRICES_USD = {
102810
102968
  review_pr: 0.01,
@@ -104564,202 +104722,6 @@ async function electAttachOrCoordinate(fullConfig, motebitId, runtimeRef) {
104564
104722
  init_esm_shims();
104565
104723
  init_colors();
104566
104724
  init_terminal();
104567
- init_statusline();
104568
- init_status_render();
104569
- async function renderStream(stream) {
104570
- let pendingApproval = null;
104571
- let status = null;
104572
- let thinking = null;
104573
- const stopThinking = () => {
104574
- thinking?.stop();
104575
- thinking = null;
104576
- };
104577
- const stopStatus = () => {
104578
- stopThinking();
104579
- const elapsed = status?.stop() ?? 0;
104580
- status = null;
104581
- return elapsed;
104582
- };
104583
- thinking = startStatus("thinking");
104584
- try {
104585
- for await (const raw of stream) {
104586
- const chunk = raw;
104587
- switch (chunk.type) {
104588
- case "text":
104589
- stopThinking();
104590
- if (typeof chunk.text === "string") writeOutput(chunk.text);
104591
- break;
104592
- case "tool_status": {
104593
- const name = chunk.name ?? "tool";
104594
- if (chunk.status === "calling") {
104595
- if (name === "delegate_to_agent" && status) break;
104596
- stopThinking();
104597
- status = name === "delegate_to_agent" ? startStatus("delegating") : startStatus("running", name);
104598
- } else if (status) {
104599
- writeLine(
104600
- ` ${action("\u25CF")} ${action(name)} ${meta(`\xB7 done \xB7 ${formatElapsed(0, stopStatus())}`)}`
104601
- );
104602
- thinking = startStatus("thinking");
104603
- }
104604
- break;
104605
- }
104606
- case "approval_request":
104607
- pendingApproval = {
104608
- name: chunk.name ?? "tool",
104609
- args: chunk.args ?? {},
104610
- ...chunk.risk_level != null ? { riskLevel: chunk.risk_level } : {}
104611
- };
104612
- break;
104613
- case "delegation_start":
104614
- if (status) break;
104615
- stopThinking();
104616
- status = startStatus("delegating", chunk.tool ?? "task");
104617
- break;
104618
- case "delegation_complete":
104619
- stopStatus();
104620
- if (chunk.receipt?.task_id != null) {
104621
- writeLine(
104622
- dim(`[receipt ${chunk.receipt.task_id.slice(0, 8)} \xB7 ${chunk.receipt.status ?? ""}]`)
104623
- );
104624
- }
104625
- thinking = startStatus("thinking");
104626
- break;
104627
- case "injection_warning":
104628
- writeLine(`${warn2("\u26A0")} suspicious content in ${chunk.tool_name ?? "tool"} output`);
104629
- break;
104630
- case "approval_expired":
104631
- stopStatus();
104632
- writeLine(
104633
- `${warn2("[this approval expired before your answer arrived \u2014 nothing was executed]")}
104634
- ${dim(`(${chunk.tool_name ?? "the tool"} was never run; no money moved. Ask again when ready.)`)}`
104635
- );
104636
- break;
104637
- case "approval_voided":
104638
- stopStatus();
104639
- writeLine(
104640
- `${warn2("[your new message set aside the pending approval \u2014 nothing was executed]")}
104641
- ${dim(`(${chunk.tool_name ?? "the tool"} was never run; no money moved. It can be proposed again.)`)}`
104642
- );
104643
- break;
104644
- case "invoke_error":
104645
- stopStatus();
104646
- writeLine(
104647
- `${warn2(`[invoke failed${chunk.code != null ? ` \xB7 ${chunk.code}` : ""}]`)} ${chunk.message ?? ""}`
104648
- );
104649
- break;
104650
- case "task_step_narration":
104651
- if (typeof chunk.text === "string") {
104652
- status?.step(chunk.text.trim());
104653
- writeLine(` ${meta("\xB7 " + chunk.text.trim())}`);
104654
- }
104655
- break;
104656
- default:
104657
- break;
104658
- }
104659
- }
104660
- } catch (err2) {
104661
- writeLine(`${warn2("[coordinator error]")} ${err2 instanceof Error ? err2.message : String(err2)}`);
104662
- } finally {
104663
- stopStatus();
104664
- }
104665
- return { pendingApproval };
104666
- }
104667
- async function renderTurn(client, motebitId, stream) {
104668
- let current2 = stream;
104669
- for (; ; ) {
104670
- const { pendingApproval } = await renderStream(current2);
104671
- if (pendingApproval === null) return;
104672
- for (const line of renderApprovalRequest({
104673
- name: pendingApproval.name,
104674
- args: pendingApproval.args,
104675
- ...pendingApproval.riskLevel != null ? { riskLevel: pendingApproval.riskLevel } : {}
104676
- })) {
104677
- writeLine(line);
104678
- }
104679
- const answer = await askQuestion(` ${warn2("Allow?")} ${dim("(y/n)")} `);
104680
- const approved = answer.trim().toLowerCase() === "y";
104681
- current2 = client.resolveApproval(approved, motebitId);
104682
- }
104683
- }
104684
- var ATTACHED_HELP = `
104685
- Attached mode \u2014 the coordinator process owns the runtime; this terminal renders.
104686
- <text> chat with your motebit (proxied to the coordinator)
104687
- /invoke <cap> <text> invoke a capability deterministically
104688
- /help this help
104689
- /exit leave (the coordinator keeps running)
104690
- Other slash commands need the runtime in-process: exit, stop the coordinator, and re-run \`motebit\`.
104691
- `;
104692
- async function runAttachedRepl(client, motebitId) {
104693
- writeOutput(
104694
- `
104695
- ${dim("Attached to the machine's coordinator runtime")} ${dim(`(pid ${client.coordinatorPid})`)}
104696
- ${dim("This terminal renders; the coordinator acts. /help for what's available.")}
104697
-
104698
- `
104699
- );
104700
- setModeRow(renderModeRow({ attachedPid: client.coordinatorPid }));
104701
- let closed = false;
104702
- client.onClose(() => {
104703
- closed = true;
104704
- writeOutput(
104705
- `
104706
- ${warn2("Coordinator exited.")} Run \`motebit\` again to take over as coordinator.
104707
- `
104708
- );
104709
- destroyTerminal();
104710
- process.exit(0);
104711
- });
104712
- for (; ; ) {
104713
- if (closed) return;
104714
- let line;
104715
- try {
104716
- line = await readInput(prompt("you>") + " ");
104717
- } catch {
104718
- break;
104719
- }
104720
- const trimmed = line.trim();
104721
- if (trimmed === "") continue;
104722
- if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "exit" || trimmed === "quit") {
104723
- writeOutput("Goodbye! (coordinator keeps running)\n");
104724
- break;
104725
- }
104726
- if (trimmed === "/help") {
104727
- writeOutput(ATTACHED_HELP);
104728
- continue;
104729
- }
104730
- if (trimmed.startsWith("/invoke ")) {
104731
- const rest = trimmed.slice("/invoke ".length).trim();
104732
- const space = rest.indexOf(" ");
104733
- const capability = space === -1 ? rest : rest.slice(0, space);
104734
- const prompt2 = space === -1 ? "" : rest.slice(space + 1);
104735
- if (capability === "") {
104736
- writeOutput(`${warn2("usage:")} /invoke <capability> <prompt>
104737
- `);
104738
- continue;
104739
- }
104740
- writeOutput(prompt("mote>") + " ");
104741
- await renderTurn(client, motebitId, client.invoke(capability, prompt2));
104742
- writeOutput("\n");
104743
- continue;
104744
- }
104745
- if (trimmed.startsWith("/")) {
104746
- writeOutput(
104747
- `${dim(`${trimmed.split(" ")[0]} needs the runtime in-process \u2014 not available in attached mode.`)}
104748
- ${dim("Stop the coordinator and re-run `motebit` to use it.")}
104749
- `
104750
- );
104751
- continue;
104752
- }
104753
- writeOutput(prompt("mote>") + " ");
104754
- await renderTurn(client, motebitId, client.chat(trimmed));
104755
- writeOutput("\n");
104756
- }
104757
- client.close();
104758
- destroyTerminal();
104759
- }
104760
-
104761
- // src/index.ts
104762
- init_colors();
104763
104725
 
104764
104726
  // src/slash-commands.ts
104765
104727
  init_esm_shims();
@@ -106421,14 +106383,25 @@ async function handleInvokeCommand(args, deps) {
106421
106383
  async function handleReceiptCommand(args, deps) {
106422
106384
  const out = deps.out ?? ((s3) => console.log(s3));
106423
106385
  const taskId = args.trim();
106386
+ let receipt;
106424
106387
  if (!taskId) {
106425
- out(error2("Usage: /receipt <task-id>"));
106426
- return;
106427
- }
106428
- const receipt = getArchivedReceipt(taskId);
106429
- if (!receipt) {
106430
- out(warn2(`No archived receipt for task_id=${taskId}`));
106431
- return;
106388
+ receipt = latestArchivedReceipt();
106389
+ if (!receipt) {
106390
+ out(warn2("No receipts archived this session. Usage: /receipt [task-id-prefix]"));
106391
+ return;
106392
+ }
106393
+ } else {
106394
+ const found = findArchivedReceipt(taskId);
106395
+ if (found.kind === "ambiguous") {
106396
+ out(warn2(`Prefix "${taskId}" matches ${found.taskIds.length} receipts:`));
106397
+ for (const id of found.taskIds) out(dim(` ${id}`));
106398
+ return;
106399
+ }
106400
+ if (found.kind === "miss") {
106401
+ out(warn2(`No archived receipt matches "${taskId}" this session.`));
106402
+ return;
106403
+ }
106404
+ receipt = found.receipt;
106432
106405
  }
106433
106406
  out("");
106434
106407
  await renderReceipt(receipt, out);
@@ -106469,6 +106442,369 @@ async function drainInvokeStream(stream, out, voice) {
106469
106442
  }
106470
106443
  }
106471
106444
 
106445
+ // etc/cli-surface.json
106446
+ var cli_surface_default = {
106447
+ subcommands: {
106448
+ approvals: [
106449
+ "approve",
106450
+ "deny",
106451
+ "list",
106452
+ "show"
106453
+ ],
106454
+ attest: [],
106455
+ balance: [],
106456
+ credentials: [],
106457
+ delegate: [],
106458
+ discover: [],
106459
+ doctor: [],
106460
+ export: [],
106461
+ federation: [
106462
+ "mesh",
106463
+ "peer",
106464
+ "peer-remove",
106465
+ "peers",
106466
+ "status"
106467
+ ],
106468
+ fund: [],
106469
+ goal: [
106470
+ "add",
106471
+ "list",
106472
+ "outcomes",
106473
+ "pause",
106474
+ "remove",
106475
+ "resume"
106476
+ ],
106477
+ grant: [],
106478
+ id: [],
106479
+ init: [],
106480
+ keychain: [],
106481
+ ledger: [],
106482
+ logs: [],
106483
+ lsp: [],
106484
+ migrate: [],
106485
+ "migrate-keyring": [],
106486
+ ps: [],
106487
+ register: [],
106488
+ relay: [
106489
+ "up"
106490
+ ],
106491
+ restore: [],
106492
+ rotate: [],
106493
+ run: [],
106494
+ schema: [],
106495
+ seed: [],
106496
+ serve: [],
106497
+ skills: [],
106498
+ smoke: [],
106499
+ up: [],
106500
+ verify: [
106501
+ "identity"
106502
+ ],
106503
+ "verify-release": [],
106504
+ wallet: [],
106505
+ withdraw: []
106506
+ },
106507
+ flags: [
106508
+ {
106509
+ name: "address-only",
106510
+ type: "boolean",
106511
+ default: false
106512
+ },
106513
+ {
106514
+ name: "all",
106515
+ type: "boolean",
106516
+ default: false
106517
+ },
106518
+ {
106519
+ name: "allow-commands",
106520
+ type: "string"
106521
+ },
106522
+ {
106523
+ name: "allowed-paths",
106524
+ type: "string"
106525
+ },
106526
+ {
106527
+ name: "auto-approve",
106528
+ type: "boolean",
106529
+ default: false
106530
+ },
106531
+ {
106532
+ name: "block-commands",
106533
+ type: "string"
106534
+ },
106535
+ {
106536
+ name: "budget",
106537
+ type: "string"
106538
+ },
106539
+ {
106540
+ name: "cadence-hours",
106541
+ type: "string"
106542
+ },
106543
+ {
106544
+ name: "capability",
106545
+ type: "string"
106546
+ },
106547
+ {
106548
+ name: "days",
106549
+ type: "string"
106550
+ },
106551
+ {
106552
+ name: "db-path",
106553
+ type: "string"
106554
+ },
106555
+ {
106556
+ name: "destination",
106557
+ type: "string"
106558
+ },
106559
+ {
106560
+ name: "direct",
106561
+ type: "boolean",
106562
+ default: false
106563
+ },
106564
+ {
106565
+ name: "dry-run",
106566
+ type: "boolean",
106567
+ default: false
106568
+ },
106569
+ {
106570
+ name: "event-type",
106571
+ type: "string"
106572
+ },
106573
+ {
106574
+ name: "every",
106575
+ type: "string"
106576
+ },
106577
+ {
106578
+ name: "facilitator-url",
106579
+ type: "string"
106580
+ },
106581
+ {
106582
+ name: "federation-url",
106583
+ type: "string"
106584
+ },
106585
+ {
106586
+ name: "file",
106587
+ type: "string",
106588
+ short: "f"
106589
+ },
106590
+ {
106591
+ name: "force",
106592
+ type: "boolean",
106593
+ default: false
106594
+ },
106595
+ {
106596
+ name: "grant",
106597
+ type: "string"
106598
+ },
106599
+ {
106600
+ name: "help",
106601
+ type: "boolean",
106602
+ default: false,
106603
+ short: "h"
106604
+ },
106605
+ {
106606
+ name: "identity",
106607
+ type: "string"
106608
+ },
106609
+ {
106610
+ name: "json",
106611
+ type: "boolean",
106612
+ default: false
106613
+ },
106614
+ {
106615
+ name: "lifetime-usd",
106616
+ type: "string"
106617
+ },
106618
+ {
106619
+ name: "limit",
106620
+ type: "string"
106621
+ },
106622
+ {
106623
+ name: "max-tokens",
106624
+ type: "string"
106625
+ },
106626
+ {
106627
+ name: "model",
106628
+ type: "string"
106629
+ },
106630
+ {
106631
+ name: "network",
106632
+ type: "string"
106633
+ },
106634
+ {
106635
+ name: "no-stream",
106636
+ type: "boolean",
106637
+ default: false
106638
+ },
106639
+ {
106640
+ name: "once",
106641
+ type: "boolean",
106642
+ default: false
106643
+ },
106644
+ {
106645
+ name: "operator",
106646
+ type: "boolean",
106647
+ default: false
106648
+ },
106649
+ {
106650
+ name: "output",
106651
+ type: "string",
106652
+ short: "o"
106653
+ },
106654
+ {
106655
+ name: "passphrase",
106656
+ type: "boolean",
106657
+ default: false
106658
+ },
106659
+ {
106660
+ name: "pay-new-agents",
106661
+ type: "boolean",
106662
+ default: false
106663
+ },
106664
+ {
106665
+ name: "pay-to-address",
106666
+ type: "string"
106667
+ },
106668
+ {
106669
+ name: "plan",
106670
+ type: "boolean",
106671
+ default: false
106672
+ },
106673
+ {
106674
+ name: "port",
106675
+ type: "string"
106676
+ },
106677
+ {
106678
+ name: "presentation",
106679
+ type: "boolean",
106680
+ default: false
106681
+ },
106682
+ {
106683
+ name: "price",
106684
+ type: "string"
106685
+ },
106686
+ {
106687
+ name: "project",
106688
+ type: "string"
106689
+ },
106690
+ {
106691
+ name: "provider",
106692
+ type: "string",
106693
+ default: "anthropic"
106694
+ },
106695
+ {
106696
+ name: "prune",
106697
+ type: "boolean",
106698
+ default: false
106699
+ },
106700
+ {
106701
+ name: "reason",
106702
+ type: "string"
106703
+ },
106704
+ {
106705
+ name: "routing-strategy",
106706
+ type: "string"
106707
+ },
106708
+ {
106709
+ name: "scope",
106710
+ type: "string"
106711
+ },
106712
+ {
106713
+ name: "self-test",
106714
+ type: "boolean",
106715
+ default: false
106716
+ },
106717
+ {
106718
+ name: "serve-port",
106719
+ type: "string"
106720
+ },
106721
+ {
106722
+ name: "serve-transport",
106723
+ type: "string"
106724
+ },
106725
+ {
106726
+ name: "solana-rpc-url",
106727
+ type: "string"
106728
+ },
106729
+ {
106730
+ name: "sovereign",
106731
+ type: "boolean",
106732
+ default: false
106733
+ },
106734
+ {
106735
+ name: "subject",
106736
+ type: "string"
106737
+ },
106738
+ {
106739
+ name: "sync-token",
106740
+ type: "string"
106741
+ },
106742
+ {
106743
+ name: "sync-url",
106744
+ type: "string"
106745
+ },
106746
+ {
106747
+ name: "tail",
106748
+ type: "boolean",
106749
+ default: false
106750
+ },
106751
+ {
106752
+ name: "target",
106753
+ type: "string"
106754
+ },
106755
+ {
106756
+ name: "tools",
106757
+ type: "string"
106758
+ },
106759
+ {
106760
+ name: "version",
106761
+ type: "boolean",
106762
+ default: false,
106763
+ short: "v"
106764
+ },
106765
+ {
106766
+ name: "voice",
106767
+ type: "boolean",
106768
+ default: false
106769
+ },
106770
+ {
106771
+ name: "waive",
106772
+ type: "boolean",
106773
+ default: false
106774
+ },
106775
+ {
106776
+ name: "wall-clock",
106777
+ type: "string"
106778
+ },
106779
+ {
106780
+ name: "window-hours",
106781
+ type: "string"
106782
+ },
106783
+ {
106784
+ name: "window-usd",
106785
+ type: "string"
106786
+ }
106787
+ ],
106788
+ exitCodes: [
106789
+ 0,
106790
+ 1,
106791
+ 2,
106792
+ 130
106793
+ ],
106794
+ onDiskLayout: [
106795
+ "config.json",
106796
+ "dev-keyring.json",
106797
+ "identity.md",
106798
+ "motebit.db",
106799
+ "motebit.md",
106800
+ "relay",
106801
+ "relay/relay.db",
106802
+ "smoke-x402-buyer-eoa.txt",
106803
+ "smoke-x402-worker-eoa.txt",
106804
+ "update-check.json"
106805
+ ]
106806
+ };
106807
+
106472
106808
  // src/slash-commands.ts
106473
106809
  function isSlashCommand(input) {
106474
106810
  return input.startsWith("/");
@@ -106491,6 +106827,17 @@ function resolveBareCommand(input) {
106491
106827
  if (name === "ledger" && words.length === 2) return `/ledger ${words[1]}`;
106492
106828
  return null;
106493
106829
  }
106830
+ function detectShellInvocation(input) {
106831
+ if (input.includes("?")) return null;
106832
+ const words = input.trim().split(/\s+/);
106833
+ if (words.length < 2 || words[0] !== "motebit") return null;
106834
+ const second = words[1];
106835
+ const known = second.startsWith("--") ? CLI_FLAG_NAMES.has(second.slice(2).split("=")[0].toLowerCase()) : CLI_SUBCOMMAND_NAMES.has(second.toLowerCase());
106836
+ if (!known) return null;
106837
+ return " that's a shell command \u2014 type `quit`, then run it in your terminal";
106838
+ }
106839
+ var CLI_SUBCOMMAND_NAMES = new Set(Object.keys(cli_surface_default.subcommands));
106840
+ var CLI_FLAG_NAMES = new Set(cli_surface_default.flags.map((f7) => f7.name.toLowerCase()));
106494
106841
  function renderProvenanceBadge(record) {
106495
106842
  const status = record.provenance_status;
106496
106843
  if (status === "verified") return success("[verified]");
@@ -106826,7 +107173,10 @@ ${summary}`);
106826
107173
  "gpt-5.4-mini": "gpt-5.4-mini",
106827
107174
  "gpt-5.4-nano": "gpt-5.4-nano",
106828
107175
  o3: "o3",
107176
+ qwen3: "qwen3",
107177
+ "gpt-oss": "gpt-oss",
106829
107178
  "llama3.2": "llama3.2",
107179
+ // legacy alias — still resolvable, no longer suggested
106830
107180
  mistral: "mistral"
106831
107181
  };
106832
107182
  const envKey = config.provider === "openai" ? process.env["OPENAI_API_KEY"] : process.env["ANTHROPIC_API_KEY"];
@@ -106844,13 +107194,21 @@ ${summary}`);
106844
107194
  if (catalog.source === "live") {
106845
107195
  const col2 = Math.max(...catalog.models.map((m4) => m4.id.length)) + 2;
106846
107196
  for (const m4 of catalog.models) {
106847
- const active = m4.id === current2;
107197
+ const active = m4.id === current2 || m4.id === `${current2}:latest`;
106848
107198
  const marker = active ? green(" \u25CF") : " ";
106849
107199
  const gap = " ".repeat(Math.max(1, col2 - m4.id.length));
106850
107200
  console.log(`${marker} ${cyan(m4.id)}${gap}${dim(m4.displayName ?? "")}`);
106851
107201
  }
106852
107202
  console.log(dim(`
106853
107203
  live from ${config.provider}`));
107204
+ const others = ["anthropic", "openai", "google", "local-server"].filter(
107205
+ (p5) => p5 !== config.provider
107206
+ );
107207
+ console.log(
107208
+ dim(
107209
+ ` other providers (restart to switch): ${others.map((p5) => `--provider ${p5} (${defaultModelForProvider(p5)})`).join(" \xB7 ")}`
107210
+ )
107211
+ );
106854
107212
  return;
106855
107213
  }
106856
107214
  const col = Math.max(...Object.keys(MODEL_ALIASES).map((k4) => k4.length)) + 2;
@@ -106889,7 +107247,7 @@ Current model: ${cyan(current2)}
106889
107247
  }
106890
107248
  const input = args.toLowerCase();
106891
107249
  const resolved = MODEL_ALIASES[input];
106892
- const isFullId = Object.values(MODEL_ALIASES).includes(args) || (liveIds?.has(args) ?? false);
107250
+ const isFullId = Object.values(MODEL_ALIASES).includes(args) || liveIds != null && liveCatalogServes(liveIds, args);
106893
107251
  if (!resolved && !isFullId) {
106894
107252
  console.log(`
106895
107253
  Unknown model: ${cyan(args)}
@@ -106901,7 +107259,7 @@ Unknown model: ${cyan(args)}
106901
107259
  break;
106902
107260
  }
106903
107261
  const modelId = resolved ?? args;
106904
- if (liveIds != null && !liveIds.has(modelId)) {
107262
+ if (liveIds != null && !liveCatalogServes(liveIds, modelId)) {
106905
107263
  console.log(
106906
107264
  `
106907
107265
  ${warn2(`${modelId} is not served by ${config.provider} right now \u2014 /model lists what is`)}
@@ -106917,6 +107275,10 @@ Unknown model: ${cyan(args)}
106917
107275
  break;
106918
107276
  }
106919
107277
  runtime.setModel(modelId);
107278
+ if (runtime.moneyToolsWithheld) {
107279
+ console.log(`
107280
+ ${dim(MONEY_TOOLS_WITHHELD_NOTICE)}`);
107281
+ }
106920
107282
  if (fullConfig) {
106921
107283
  fullConfig.default_model = modelId;
106922
107284
  const persistable = ["anthropic", "openai", "google", "local-server", "proxy"];
@@ -108593,6 +108955,209 @@ Curiosity targets (${targets.length}):
108593
108955
  }
108594
108956
  }
108595
108957
 
108958
+ // src/attached-repl.ts
108959
+ init_statusline();
108960
+ init_status_render();
108961
+ async function renderStream(stream) {
108962
+ let pendingApproval = null;
108963
+ let status = null;
108964
+ let thinking = null;
108965
+ const stopThinking = () => {
108966
+ thinking?.stop();
108967
+ thinking = null;
108968
+ };
108969
+ const stopStatus = () => {
108970
+ stopThinking();
108971
+ const elapsed = status?.stop() ?? 0;
108972
+ status = null;
108973
+ return elapsed;
108974
+ };
108975
+ thinking = startStatus("thinking");
108976
+ try {
108977
+ for await (const raw of stream) {
108978
+ const chunk = raw;
108979
+ switch (chunk.type) {
108980
+ case "text":
108981
+ stopThinking();
108982
+ if (typeof chunk.text === "string") writeOutput(chunk.text);
108983
+ break;
108984
+ case "tool_status": {
108985
+ const name = chunk.name ?? "tool";
108986
+ if (chunk.status === "calling") {
108987
+ if (name === "delegate_to_agent" && status) break;
108988
+ stopThinking();
108989
+ status = name === "delegate_to_agent" ? startStatus("delegating") : startStatus("running", name);
108990
+ } else if (status) {
108991
+ writeLine(
108992
+ ` ${action("\u25CF")} ${action(name)} ${meta(`\xB7 done \xB7 ${formatElapsed(0, stopStatus())}`)}`
108993
+ );
108994
+ thinking = startStatus("thinking");
108995
+ }
108996
+ break;
108997
+ }
108998
+ case "approval_request":
108999
+ pendingApproval = {
109000
+ name: chunk.name ?? "tool",
109001
+ args: chunk.args ?? {},
109002
+ ...chunk.risk_level != null ? { riskLevel: chunk.risk_level } : {}
109003
+ };
109004
+ break;
109005
+ case "delegation_start":
109006
+ if (status) break;
109007
+ stopThinking();
109008
+ status = startStatus("delegating", chunk.tool ?? "task");
109009
+ break;
109010
+ case "delegation_complete":
109011
+ stopStatus();
109012
+ if (chunk.receipt?.task_id != null) {
109013
+ writeLine(
109014
+ dim(`[receipt ${chunk.receipt.task_id.slice(0, 8)} \xB7 ${chunk.receipt.status ?? ""}]`)
109015
+ );
109016
+ }
109017
+ thinking = startStatus("thinking");
109018
+ break;
109019
+ case "injection_warning":
109020
+ writeLine(`${warn2("\u26A0")} suspicious content in ${chunk.tool_name ?? "tool"} output`);
109021
+ break;
109022
+ case "approval_expired":
109023
+ stopStatus();
109024
+ writeLine(
109025
+ `${warn2("[this approval expired before your answer arrived \u2014 nothing was executed]")}
109026
+ ${dim(`(${chunk.tool_name ?? "the tool"} was never run; no money moved. Ask again when ready.)`)}`
109027
+ );
109028
+ break;
109029
+ case "approval_voided":
109030
+ stopStatus();
109031
+ writeLine(
109032
+ `${warn2("[your new message set aside the pending approval \u2014 nothing was executed]")}
109033
+ ${dim(`(${chunk.tool_name ?? "the tool"} was never run; no money moved. It can be proposed again.)`)}`
109034
+ );
109035
+ break;
109036
+ case "invoke_error":
109037
+ stopStatus();
109038
+ writeLine(
109039
+ `${warn2(`[invoke failed${chunk.code != null ? ` \xB7 ${chunk.code}` : ""}]`)} ${chunk.message ?? ""}`
109040
+ );
109041
+ break;
109042
+ case "task_step_narration":
109043
+ if (typeof chunk.text === "string") {
109044
+ status?.step(chunk.text.trim());
109045
+ writeLine(` ${meta("\xB7 " + chunk.text.trim())}`);
109046
+ }
109047
+ break;
109048
+ default:
109049
+ break;
109050
+ }
109051
+ }
109052
+ } catch (err2) {
109053
+ writeLine(`${warn2("[coordinator error]")} ${err2 instanceof Error ? err2.message : String(err2)}`);
109054
+ } finally {
109055
+ stopStatus();
109056
+ }
109057
+ return { pendingApproval };
109058
+ }
109059
+ async function renderTurn(client, motebitId, stream) {
109060
+ let current2 = stream;
109061
+ for (; ; ) {
109062
+ const { pendingApproval } = await renderStream(current2);
109063
+ if (pendingApproval === null) return;
109064
+ for (const line of renderApprovalRequest({
109065
+ name: pendingApproval.name,
109066
+ args: pendingApproval.args,
109067
+ ...pendingApproval.riskLevel != null ? { riskLevel: pendingApproval.riskLevel } : {}
109068
+ })) {
109069
+ writeLine(line);
109070
+ }
109071
+ const answer = await askQuestion(` ${warn2("Allow?")} ${dim("(y/n)")} `);
109072
+ const approved = answer.trim().toLowerCase() === "y";
109073
+ current2 = client.resolveApproval(approved, motebitId);
109074
+ }
109075
+ }
109076
+ var ATTACHED_HELP = `
109077
+ Attached mode \u2014 the coordinator process owns the runtime; this terminal renders.
109078
+ <text> chat with your motebit (proxied to the coordinator)
109079
+ /invoke <cap> <text> invoke a capability deterministically
109080
+ /help this help
109081
+ /exit leave (the coordinator keeps running)
109082
+ Other slash commands need the runtime in-process: exit, stop the coordinator, and re-run \`motebit\`.
109083
+ `;
109084
+ async function runAttachedRepl(client, motebitId) {
109085
+ writeOutput(
109086
+ `
109087
+ ${dim("Attached to the machine's coordinator runtime")} ${dim(`(pid ${client.coordinatorPid})`)}
109088
+ ${dim("This terminal renders; the coordinator acts. /help for what's available.")}
109089
+
109090
+ `
109091
+ );
109092
+ setModeRow(renderModeRow({ attachedPid: client.coordinatorPid }));
109093
+ let closed = false;
109094
+ client.onClose(() => {
109095
+ closed = true;
109096
+ writeOutput(
109097
+ `
109098
+ ${warn2("Coordinator exited.")} Run \`motebit\` again to take over as coordinator.
109099
+ `
109100
+ );
109101
+ destroyTerminal();
109102
+ process.exit(0);
109103
+ });
109104
+ for (; ; ) {
109105
+ if (closed) return;
109106
+ let line;
109107
+ try {
109108
+ line = await readInput(prompt("you>") + " ");
109109
+ } catch {
109110
+ break;
109111
+ }
109112
+ const trimmed = line.trim();
109113
+ if (trimmed === "") continue;
109114
+ if (trimmed === "/exit" || trimmed === "/quit" || trimmed === "exit" || trimmed === "quit") {
109115
+ writeOutput("Goodbye! (coordinator keeps running)\n");
109116
+ break;
109117
+ }
109118
+ if (trimmed === "/help") {
109119
+ writeOutput(ATTACHED_HELP);
109120
+ continue;
109121
+ }
109122
+ if (trimmed.startsWith("/invoke ")) {
109123
+ const rest = trimmed.slice("/invoke ".length).trim();
109124
+ const space = rest.indexOf(" ");
109125
+ const capability = space === -1 ? rest : rest.slice(0, space);
109126
+ const prompt2 = space === -1 ? "" : rest.slice(space + 1);
109127
+ if (capability === "") {
109128
+ writeOutput(`${warn2("usage:")} /invoke <capability> <prompt>
109129
+ `);
109130
+ continue;
109131
+ }
109132
+ writeOutput(prompt("mote>") + " ");
109133
+ await renderTurn(client, motebitId, client.invoke(capability, prompt2));
109134
+ writeOutput("\n");
109135
+ continue;
109136
+ }
109137
+ if (trimmed.startsWith("/")) {
109138
+ writeOutput(
109139
+ `${dim(`${trimmed.split(" ")[0]} needs the runtime in-process \u2014 not available in attached mode.`)}
109140
+ ${dim("Stop the coordinator and re-run `motebit` to use it.")}
109141
+ `
109142
+ );
109143
+ continue;
109144
+ }
109145
+ const shellTeach = detectShellInvocation(trimmed);
109146
+ if (shellTeach != null) {
109147
+ writeOutput(dim(shellTeach) + "\n\n");
109148
+ continue;
109149
+ }
109150
+ writeOutput(prompt("mote>") + " ");
109151
+ await renderTurn(client, motebitId, client.chat(trimmed));
109152
+ writeOutput("\n");
109153
+ }
109154
+ client.close();
109155
+ destroyTerminal();
109156
+ }
109157
+
109158
+ // src/index.ts
109159
+ init_colors();
109160
+
108596
109161
  // src/subcommands/index.ts
108597
109162
  init_esm_shims();
108598
109163
 
@@ -126065,13 +126630,13 @@ async function main() {
126065
126630
  runtimeRef
126066
126631
  });
126067
126632
  } catch (err2) {
126633
+ destroyTerminal();
126068
126634
  console.error(
126069
126635
  `Runtime-host election failed: ${err2 instanceof Error ? err2.message : String(err2)}`
126070
126636
  );
126071
126637
  console.error(
126072
126638
  "Another motebit process may be coordinating with an incompatible build. Stop it and retry."
126073
126639
  );
126074
- destroyTerminal();
126075
126640
  process.exit(1);
126076
126641
  }
126077
126642
  if (election.role === "frontend") {
@@ -126247,7 +126812,8 @@ async function main() {
126247
126812
  moteDb.close();
126248
126813
  };
126249
126814
  process.on("SIGINT", () => {
126250
- console.log("\nGoodbye!");
126815
+ destroyTerminal();
126816
+ console.log("Goodbye!");
126251
126817
  void shutdown().then(() => process.exit(0));
126252
126818
  });
126253
126819
  const toolCount = toolRegistry.size;
@@ -126274,6 +126840,10 @@ async function main() {
126274
126840
  }
126275
126841
  refreshUpdateCheckInBackground();
126276
126842
  }
126843
+ if (runtime.moneyToolsWithheld) {
126844
+ console.log(dim(` ${MONEY_TOOLS_WITHHELD_NOTICE}`));
126845
+ console.log();
126846
+ }
126277
126847
  if (isFirstLaunch) {
126278
126848
  try {
126279
126849
  const activationRunId = crypto.randomUUID();
@@ -126338,7 +126908,16 @@ async function main() {
126338
126908
  }
126339
126909
  const bareSlash = resolveBareCommand(trimmed);
126340
126910
  const effective = bareSlash ?? trimmed;
126341
- if (bareSlash != null) console.log(dim(` \u2192 ${bareSlash}`));
126911
+ if (bareSlash != null) writeLine(dim(` \u2192 ${bareSlash}`));
126912
+ if (bareSlash == null) {
126913
+ const shellTeach = detectShellInvocation(trimmed);
126914
+ if (shellTeach != null) {
126915
+ writeLine(dim(shellTeach));
126916
+ writeLine("");
126917
+ prompt2();
126918
+ return;
126919
+ }
126920
+ }
126342
126921
  if (isSlashCommand(effective)) {
126343
126922
  const { command: command2, args } = parseSlashCommand(effective);
126344
126923
  await handleSlashCommand(command2, args, runtime, config, fullConfig, {