@tuned-tensor/cli 0.13.0 → 0.13.1-beta.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.
package/dist/index.js CHANGED
@@ -573,18 +573,157 @@ async function rejectPreparedAction(action, persist) {
573
573
  }
574
574
 
575
575
  // src/agent-model.ts
576
+ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
577
+ import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
578
+ import { dirname, join as join3 } from "path";
579
+
580
+ // src/config.ts
576
581
  import {
577
- getAgentDir,
578
- ModelRuntime
579
- } from "@earendil-works/pi-coding-agent";
580
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
581
- import { dirname, join as join2 } from "path";
582
+ chmodSync,
583
+ existsSync,
584
+ mkdirSync,
585
+ readFileSync,
586
+ writeFileSync
587
+ } from "fs";
588
+ import { homedir } from "os";
589
+ import { join as join2 } from "path";
590
+ var AGENT_THINKING_LEVELS = [
591
+ "off",
592
+ "minimal",
593
+ "low",
594
+ "medium",
595
+ "high",
596
+ "xhigh",
597
+ "max"
598
+ ];
599
+ var CONFIG_DIR_NAME = "tuned-tensor";
600
+ var CONFIG_FILE_NAME = "config.json";
601
+ function getConfigDir() {
602
+ const xdg = process.env.XDG_CONFIG_HOME;
603
+ const base = xdg || join2(homedir(), ".config");
604
+ return join2(base, CONFIG_DIR_NAME);
605
+ }
606
+ function getAgentConfigDir() {
607
+ return join2(getConfigDir(), "agent");
608
+ }
609
+ function getConfigPath() {
610
+ return join2(getConfigDir(), CONFIG_FILE_NAME);
611
+ }
612
+ function readConfig() {
613
+ const path = getConfigPath();
614
+ if (!existsSync(path)) return {};
615
+ try {
616
+ return JSON.parse(readFileSync(path, "utf-8"));
617
+ } catch {
618
+ return {};
619
+ }
620
+ }
621
+ var configRevision = 0;
622
+ function getConfigRevision() {
623
+ return configRevision;
624
+ }
625
+ function writeConfig(config) {
626
+ const dir = getConfigDir();
627
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
628
+ chmodSync(dir, 448);
629
+ const path = getConfigPath();
630
+ writeFileSync(path, JSON.stringify(config, null, 2) + "\n", {
631
+ mode: 384
632
+ });
633
+ chmodSync(path, 384);
634
+ configRevision += 1;
635
+ }
636
+ function updateConfig(partial) {
637
+ const current = readConfig();
638
+ writeConfig({ ...current, ...partial });
639
+ }
640
+ function getApiKey(opts) {
641
+ return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;
642
+ }
643
+ function getAgentSelection(env = process.env) {
644
+ const stored = readConfig().agent;
645
+ const provider = env.TUNED_TENSOR_AGENT_PROVIDER || stored?.provider;
646
+ const model = env.TUNED_TENSOR_AGENT_MODEL || stored?.model;
647
+ const rawThinking = env.TUNED_TENSOR_AGENT_THINKING || stored?.thinking || "medium";
648
+ if (!AGENT_THINKING_LEVELS.includes(rawThinking)) {
649
+ throw new Error(
650
+ "Agent thinking must be off, minimal, low, medium, high, xhigh, or max."
651
+ );
652
+ }
653
+ if (!provider && !model) return void 0;
654
+ if (!provider || !model) {
655
+ throw new Error(
656
+ "Both an agent provider and model are required. Run `tt agent configure --provider <provider> --model <model>`."
657
+ );
658
+ }
659
+ return { provider, model, thinking: rawThinking };
660
+ }
661
+
662
+ // src/agent-model.ts
663
+ function missingProviderAuthMessage() {
664
+ return "No API key is saved for the selected model. Run /login, or /model to pick a different one.";
665
+ }
666
+ function unknownAgentProviderMessage(provider) {
667
+ return `Unknown provider "${provider}". Use /login <id> or /model <id>.`;
668
+ }
669
+ var MIN_REDACTED_SECRET_LENGTH = 8;
670
+ function isRecord(value) {
671
+ return value !== null && typeof value === "object" && !Array.isArray(value);
672
+ }
673
+ function pushSecret(secrets, value) {
674
+ if (typeof value !== "string") return;
675
+ const secret = value.trim();
676
+ if (secret.length < MIN_REDACTED_SECRET_LENGTH) return;
677
+ if (!secrets.includes(secret)) secrets.push(secret);
678
+ }
679
+ function persistProviderApiKey(provider, apiKey, authPath = getAgentAuthPath()) {
680
+ const key = apiKey.trim();
681
+ if (!key) {
682
+ throw new Error("API key cannot be empty.");
683
+ }
684
+ let stored = {};
685
+ if (existsSync2(authPath)) {
686
+ let parsed;
687
+ try {
688
+ parsed = JSON.parse(readFileSync2(authPath, "utf-8"));
689
+ } catch (error) {
690
+ throw new Error(
691
+ `Failed to read ${authPath}: ${error instanceof Error ? error.message : String(error)}`
692
+ );
693
+ }
694
+ if (!isRecord(parsed)) {
695
+ throw new Error(`Invalid auth.json: expected an object at ${authPath}`);
696
+ }
697
+ stored = parsed;
698
+ }
699
+ stored[provider] = { type: "api_key", key };
700
+ writePrivateJson(authPath, stored);
701
+ }
702
+ function readStoredProviderSecrets(authPath = getAgentAuthPath()) {
703
+ if (!existsSync2(authPath)) return [];
704
+ let parsed;
705
+ try {
706
+ parsed = JSON.parse(readFileSync2(authPath, "utf-8"));
707
+ } catch {
708
+ return [];
709
+ }
710
+ if (!isRecord(parsed)) return [];
711
+ const secrets = [];
712
+ for (const credential of Object.values(parsed)) {
713
+ if (!isRecord(credential)) continue;
714
+ if (credential.type === "api_key") {
715
+ pushSecret(secrets, credential.key);
716
+ } else if (credential.type === "oauth") {
717
+ pushSecret(secrets, credential.access);
718
+ pushSecret(secrets, credential.refresh);
719
+ }
720
+ }
721
+ return secrets;
722
+ }
582
723
  function resolveAgentModel(runtime, selection) {
583
724
  const resolved = resolveAgentModelDefinition(runtime, selection);
584
725
  if (!runtime.hasConfiguredAuth(selection.provider)) {
585
- throw new Error(
586
- `Authenticate provider "${selection.provider}" first, then retry. Provider secrets are never accepted by tt flags.`
587
- );
726
+ throw new Error(missingProviderAuthMessage());
588
727
  }
589
728
  return resolved;
590
729
  }
@@ -610,25 +749,46 @@ function resolveAgentModelDefinition(runtime, selection) {
610
749
  }
611
750
  return { model, thinking: selection.thinking };
612
751
  }
752
+ function getAgentAuthPath() {
753
+ return join3(getAgentConfigDir(), "auth.json");
754
+ }
755
+ function getAgentModelsPath() {
756
+ return join3(getAgentConfigDir(), "models.json");
757
+ }
613
758
  async function createPiModelRuntime() {
614
- const agentDir = getAgentDir();
615
- const modelsPath = join2(agentDir, "models.json");
759
+ const agentDir = getAgentConfigDir();
760
+ ensurePrivateDir(agentDir);
761
+ const modelsPath = getAgentModelsPath();
762
+ const authPath = getAgentAuthPath();
616
763
  ensureOpenRouterAppHeaders(modelsPath);
617
- return await ModelRuntime.create({
618
- authPath: join2(agentDir, "auth.json"),
764
+ const runtime = await ModelRuntime.create({
765
+ authPath,
619
766
  modelsPath,
620
767
  allowModelNetwork: false
621
768
  });
769
+ if (existsSync2(authPath)) chmodSync2(authPath, 384);
770
+ return runtime;
622
771
  }
623
772
  var OPENROUTER_APP_HEADERS = {
624
773
  "HTTP-Referer": "https://tunedtensor.com",
774
+ "X-OpenRouter-Title": "Tuned Tensor",
775
+ "X-OpenRouter-Categories": "cli-agent",
625
776
  "X-Title": "Tuned Tensor"
626
777
  };
778
+ function ensurePrivateDir(dir) {
779
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
780
+ chmodSync2(dir, 448);
781
+ }
782
+ function writePrivateJson(path, value) {
783
+ ensurePrivateDir(dirname(path));
784
+ writeFileSync2(path, JSON.stringify(value, null, 2) + "\n", { mode: 384 });
785
+ chmodSync2(path, 384);
786
+ }
627
787
  function ensureOpenRouterAppHeaders(modelsPath) {
628
788
  let config = {};
629
- if (existsSync(modelsPath)) {
789
+ if (existsSync2(modelsPath)) {
630
790
  try {
631
- const parsed = JSON.parse(readFileSync(modelsPath, "utf-8"));
791
+ const parsed = JSON.parse(readFileSync2(modelsPath, "utf-8"));
632
792
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
633
793
  config = parsed;
634
794
  }
@@ -642,10 +802,15 @@ function ensureOpenRouterAppHeaders(modelsPath) {
642
802
  const providers = asRecord(config.providers) ?? (config.providers = empty());
643
803
  const openrouter = asRecord(providers.openrouter) ?? (providers.openrouter = empty());
644
804
  const headers = asRecord(openrouter.headers) ?? (openrouter.headers = empty());
805
+ const attributionNames = new Set(
806
+ Object.keys(OPENROUTER_APP_HEADERS).map((name) => name.toLowerCase())
807
+ );
808
+ for (const name of Object.keys(headers)) {
809
+ if (attributionNames.has(name.toLowerCase())) delete headers[name];
810
+ }
645
811
  Object.assign(headers, OPENROUTER_APP_HEADERS);
646
812
  if (JSON.stringify(config) === before) return;
647
- mkdirSync(dirname(modelsPath), { recursive: true });
648
- writeFileSync(modelsPath, JSON.stringify(config, null, 2) + "\n");
813
+ writePrivateJson(modelsPath, config);
649
814
  }
650
815
 
651
816
  // src/agent-tools.ts
@@ -1335,7 +1500,7 @@ import {
1335
1500
  writeFile as writeFile2
1336
1501
  } from "fs/promises";
1337
1502
  import { randomUUID as randomUUID3 } from "crypto";
1338
- import { join as join3 } from "path";
1503
+ import { join as join4 } from "path";
1339
1504
  var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1340
1505
  var DEFAULT_MAX_THREAD_BYTES = 1e6;
1341
1506
  var DEFAULT_MAX_MESSAGES = 200;
@@ -1345,7 +1510,7 @@ var DEFAULT_MAX_CLAIMS = 1e3;
1345
1510
  function assertThreadId(id) {
1346
1511
  if (!UUID.test(id)) throw new Error("Invalid local agent thread ID.");
1347
1512
  }
1348
- function isRecord(value) {
1513
+ function isRecord2(value) {
1349
1514
  return value !== null && typeof value === "object" && !Array.isArray(value);
1350
1515
  }
1351
1516
  function scrub(value, secrets) {
@@ -1356,7 +1521,7 @@ function scrub(value, secrets) {
1356
1521
  );
1357
1522
  }
1358
1523
  if (Array.isArray(value)) return value.map((entry) => scrub(entry, secrets));
1359
- if (isRecord(value)) {
1524
+ if (isRecord2(value)) {
1360
1525
  return Object.fromEntries(
1361
1526
  Object.entries(value).map(([key, entry]) => [key, scrub(entry, secrets)])
1362
1527
  );
@@ -1366,9 +1531,10 @@ function scrub(value, secrets) {
1366
1531
  var LocalAgentStore = class {
1367
1532
  constructor(rootDir, options = {}) {
1368
1533
  this.rootDir = rootDir;
1369
- this.threadsDir = join3(rootDir, "threads");
1370
- this.claimsDir = join3(rootDir, "action-claims");
1534
+ this.threadsDir = join4(rootDir, "threads");
1535
+ this.claimsDir = join4(rootDir, "action-claims");
1371
1536
  this.secrets = options.secretValues?.filter(Boolean) ?? [];
1537
+ this.secretValueProvider = options.secretValueProvider;
1372
1538
  this.maxThreadBytes = options.maxThreadBytes ?? DEFAULT_MAX_THREAD_BYTES;
1373
1539
  this.maxMessages = options.maxMessages ?? DEFAULT_MAX_MESSAGES;
1374
1540
  this.maxActions = options.maxActions ?? DEFAULT_MAX_ACTIONS;
@@ -1378,13 +1544,20 @@ var LocalAgentStore = class {
1378
1544
  threadsDir;
1379
1545
  claimsDir;
1380
1546
  secrets;
1547
+ secretValueProvider;
1381
1548
  maxThreadBytes;
1382
1549
  maxMessages;
1383
1550
  maxActions;
1384
1551
  maxThreads;
1385
1552
  maxClaims;
1386
1553
  redact(value) {
1387
- return scrub(value, this.secrets);
1554
+ let extra = [];
1555
+ try {
1556
+ extra = this.secretValueProvider?.() ?? [];
1557
+ } catch {
1558
+ extra = [];
1559
+ }
1560
+ return scrub(value, [...this.secrets, ...extra]);
1388
1561
  }
1389
1562
  async ensureDirectories() {
1390
1563
  await mkdir2(this.rootDir, { recursive: true, mode: 448 });
@@ -1396,7 +1569,7 @@ var LocalAgentStore = class {
1396
1569
  }
1397
1570
  path(id) {
1398
1571
  assertThreadId(id);
1399
- return join3(this.threadsDir, `${id}.json`);
1572
+ return join4(this.threadsDir, `${id}.json`);
1400
1573
  }
1401
1574
  serializeWithinQuota(state) {
1402
1575
  const active = state.actions.filter(
@@ -1421,11 +1594,11 @@ var LocalAgentStore = class {
1421
1594
  return serialized;
1422
1595
  }
1423
1596
  async pruneDirectory(directory, suffix, maximum, preserve) {
1424
- const entries = await Promise.all((await readdir(directory)).filter((name) => suffix.test(name) && name !== preserve).map(async (name) => ({ name, modified: (await stat(join3(directory, name))).mtimeMs })));
1597
+ const entries = await Promise.all((await readdir(directory)).filter((name) => suffix.test(name) && name !== preserve).map(async (name) => ({ name, modified: (await stat(join4(directory, name))).mtimeMs })));
1425
1598
  entries.sort((a, b) => a.modified - b.modified);
1426
1599
  const keepOther = Math.max(0, maximum - (preserve ? 1 : 0));
1427
1600
  for (const entry of entries.slice(0, Math.max(0, entries.length - keepOther))) {
1428
- await unlink(join3(directory, entry.name));
1601
+ await unlink(join4(directory, entry.name));
1429
1602
  }
1430
1603
  }
1431
1604
  async pruneThreads(preserve) {
@@ -1445,19 +1618,19 @@ var LocalAgentStore = class {
1445
1618
  }
1446
1619
  const candidates = await Promise.all(names.filter((name) => !protectedNames.has(name)).map(async (name) => ({
1447
1620
  name,
1448
- modified: (await stat(join3(this.threadsDir, name))).mtimeMs
1621
+ modified: (await stat(join4(this.threadsDir, name))).mtimeMs
1449
1622
  })));
1450
1623
  candidates.sort((a, b) => a.modified - b.modified);
1451
1624
  const keep = Math.max(0, this.maxThreads - protectedNames.size);
1452
1625
  for (const entry of candidates.slice(0, Math.max(0, candidates.length - keep))) {
1453
- await unlink(join3(this.threadsDir, entry.name));
1626
+ await unlink(join4(this.threadsDir, entry.name));
1454
1627
  }
1455
1628
  }
1456
1629
  async save(state) {
1457
1630
  assertThreadId(state.thread.id);
1458
1631
  await this.ensureDirectories();
1459
1632
  const destination = this.path(state.thread.id);
1460
- const temporary = join3(
1633
+ const temporary = join4(
1461
1634
  this.threadsDir,
1462
1635
  `.${state.thread.id}.${randomUUID3()}.tmp`
1463
1636
  );
@@ -1477,7 +1650,7 @@ var LocalAgentStore = class {
1477
1650
  async claimAction(actionId) {
1478
1651
  assertThreadId(actionId);
1479
1652
  await this.ensureDirectories();
1480
- const path = join3(this.claimsDir, `${actionId}.claim`);
1653
+ const path = join4(this.claimsDir, `${actionId}.claim`);
1481
1654
  try {
1482
1655
  await writeFile2(path, `${(/* @__PURE__ */ new Date()).toISOString()}
1483
1656
  `, {
@@ -1505,7 +1678,7 @@ var LocalAgentStore = class {
1505
1678
  throw new Error("Invalid local agent thread file.");
1506
1679
  }
1507
1680
  const parsed = JSON.parse(await readFile(path, "utf8"));
1508
- if (!isRecord(parsed) || !isRecord(parsed.thread) || parsed.thread.id !== id || !Array.isArray(parsed.messages) || !Array.isArray(parsed.actions)) {
1681
+ if (!isRecord2(parsed) || !isRecord2(parsed.thread) || parsed.thread.id !== id || !Array.isArray(parsed.messages) || !Array.isArray(parsed.actions)) {
1509
1682
  throw new Error("Local agent thread file is malformed.");
1510
1683
  }
1511
1684
  return parsed;
@@ -1528,88 +1701,6 @@ var LocalAgentStore = class {
1528
1701
  }
1529
1702
  };
1530
1703
 
1531
- // src/config.ts
1532
- import {
1533
- chmodSync,
1534
- existsSync as existsSync2,
1535
- mkdirSync as mkdirSync2,
1536
- readFileSync as readFileSync2,
1537
- writeFileSync as writeFileSync2
1538
- } from "fs";
1539
- import { homedir } from "os";
1540
- import { join as join4 } from "path";
1541
- var AGENT_THINKING_LEVELS = [
1542
- "off",
1543
- "minimal",
1544
- "low",
1545
- "medium",
1546
- "high",
1547
- "xhigh",
1548
- "max"
1549
- ];
1550
- var CONFIG_DIR_NAME = "tuned-tensor";
1551
- var CONFIG_FILE_NAME = "config.json";
1552
- function getConfigDir() {
1553
- const xdg = process.env.XDG_CONFIG_HOME;
1554
- const base = xdg || join4(homedir(), ".config");
1555
- return join4(base, CONFIG_DIR_NAME);
1556
- }
1557
- function getConfigPath() {
1558
- return join4(getConfigDir(), CONFIG_FILE_NAME);
1559
- }
1560
- function readConfig() {
1561
- const path = getConfigPath();
1562
- if (!existsSync2(path)) return {};
1563
- try {
1564
- return JSON.parse(readFileSync2(path, "utf-8"));
1565
- } catch {
1566
- return {};
1567
- }
1568
- }
1569
- var configRevision = 0;
1570
- function getConfigRevision() {
1571
- return configRevision;
1572
- }
1573
- function writeConfig(config) {
1574
- const dir = getConfigDir();
1575
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
1576
- chmodSync(dir, 448);
1577
- const path = getConfigPath();
1578
- writeFileSync2(path, JSON.stringify(config, null, 2) + "\n", {
1579
- mode: 384
1580
- });
1581
- chmodSync(path, 384);
1582
- configRevision += 1;
1583
- }
1584
- function updateConfig(partial) {
1585
- const current = readConfig();
1586
- writeConfig({ ...current, ...partial });
1587
- }
1588
- function getApiKey(opts) {
1589
- return opts?.apiKey || process.env.TUNED_TENSOR_API_KEY || readConfig().api_key;
1590
- }
1591
- function getAgentSelection(env = process.env) {
1592
- const stored = readConfig().agent;
1593
- const provider = env.TUNED_TENSOR_AGENT_PROVIDER || stored?.provider;
1594
- const model = env.TUNED_TENSOR_AGENT_MODEL || stored?.model;
1595
- const rawThinking = env.TUNED_TENSOR_AGENT_THINKING || stored?.thinking || "medium";
1596
- if (!AGENT_THINKING_LEVELS.includes(rawThinking)) {
1597
- throw new Error(
1598
- "Agent thinking must be off, minimal, low, medium, high, xhigh, or max."
1599
- );
1600
- }
1601
- if (!provider && !model) return void 0;
1602
- if (!provider || !model) {
1603
- throw new Error(
1604
- "Both an agent provider and model are required. Run `tt agent configure --provider <provider> --model <model>`."
1605
- );
1606
- }
1607
- return { provider, model, thinking: rawThinking };
1608
- }
1609
-
1610
- // src/cli.ts
1611
- import { join as join7 } from "path";
1612
-
1613
1704
  // src/agent.ts
1614
1705
  import chalk3 from "chalk";
1615
1706
  import { stripVTControlCharacters as stripVTControlCharacters3 } from "util";
@@ -1619,7 +1710,7 @@ import chalk from "chalk";
1619
1710
  import { stat as stat3 } from "fs/promises";
1620
1711
  import { homedir as homedir3 } from "os";
1621
1712
  import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1622
- import { createInterface } from "readline";
1713
+ import { createInterface as createInterface2 } from "readline";
1623
1714
  import { stripVTControlCharacters } from "util";
1624
1715
 
1625
1716
  // src/command-catalog.ts
@@ -1659,7 +1750,8 @@ var SLASH_COMMANDS = [
1659
1750
  { path: "/help", description: "Show commands; add a word to filter." },
1660
1751
  { path: "/status", description: "Show lightweight workflow status." },
1661
1752
  { path: "/context", description: "Show the current project context." },
1662
- { path: "/model", description: "Show or change the TT agent model." },
1753
+ { path: "/model", description: "Show or change the TT agent provider and model." },
1754
+ { path: "/login", description: "Save a provider API key; asks which provider if omitted." },
1663
1755
  { path: "/cd", description: "Change the shell's working directory." },
1664
1756
  { path: "/clear", description: "Clear the terminal." },
1665
1757
  { path: "/exit", description: "Exit the TT shell." }
@@ -1901,6 +1993,23 @@ function formatShellStatus(context) {
1901
1993
  }
1902
1994
 
1903
1995
  // src/agent-control.ts
1996
+ var FEATURED_AGENT_PROVIDERS = [
1997
+ "openai",
1998
+ "openrouter"
1999
+ ];
2000
+ var RECOMMENDED_AGENT_MODELS = [
2001
+ { provider: "openai", ids: ["gpt-5.6-sol"] },
2002
+ {
2003
+ provider: "openrouter",
2004
+ ids: ["deepseek/deepseek-v4-flash-0731", "deepseek/deepseek-v4-flash"]
2005
+ }
2006
+ ];
2007
+ var FEATURED_PROVIDER_INDEX = new Map(
2008
+ FEATURED_AGENT_PROVIDERS.map((id, index) => [id, index])
2009
+ );
2010
+ function isFeaturedProvider(id) {
2011
+ return FEATURED_PROVIDER_INDEX.has(id);
2012
+ }
1904
2013
  function modelKey(model) {
1905
2014
  return `${model.provider}/${model.id}`;
1906
2015
  }
@@ -1917,9 +2026,28 @@ function modelRelevance(model, query) {
1917
2026
  if (name.includes(q)) return 4;
1918
2027
  return void 0;
1919
2028
  }
2029
+ function listAgentProviders(runtime, options = {}) {
2030
+ return runtime.getProviders().filter((provider) => !options.featuredOnly || isFeaturedProvider(provider.id)).map((provider) => ({
2031
+ id: provider.id,
2032
+ name: provider.name ?? provider.id,
2033
+ authenticated: runtime.hasConfiguredAuth(provider.id)
2034
+ })).sort((left, right) => {
2035
+ const leftRank = FEATURED_PROVIDER_INDEX.get(left.id);
2036
+ const rightRank = FEATURED_PROVIDER_INDEX.get(right.id);
2037
+ if (leftRank !== void 0 || rightRank !== void 0) {
2038
+ return (leftRank ?? Number.MAX_SAFE_INTEGER) - (rightRank ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id);
2039
+ }
2040
+ return left.id.localeCompare(right.id);
2041
+ });
2042
+ }
2043
+ function findAgentProvider(runtime, id) {
2044
+ const needle = id.trim().toLowerCase();
2045
+ if (!needle) return void 0;
2046
+ return listAgentProviders(runtime).find((provider) => provider.id.toLowerCase() === needle);
2047
+ }
1920
2048
  function listAgentModels(runtime, options = {}) {
1921
2049
  let models = runtime.getModels(options.provider).filter(
1922
- (model) => options.includeUnauthenticated || runtime.hasConfiguredAuth(model.provider)
2050
+ (model) => (options.includeUnauthenticated || runtime.hasConfiguredAuth(model.provider)) && (!options.featuredOnly || isFeaturedProvider(model.provider))
1923
2051
  ).map((model) => ({
1924
2052
  provider: model.provider,
1925
2053
  id: model.id,
@@ -1946,6 +2074,19 @@ function listAgentModels(runtime, options = {}) {
1946
2074
  }
1947
2075
  return models;
1948
2076
  }
2077
+ function recommendAgentModels(runtime, options = {}) {
2078
+ const models = listAgentModels(runtime, {
2079
+ includeUnauthenticated: options.includeUnauthenticated ?? true
2080
+ });
2081
+ const picks = [];
2082
+ for (const recommendation of RECOMMENDED_AGENT_MODELS) {
2083
+ const match = recommendation.ids.map((id) => models.find(
2084
+ (model) => model.provider === recommendation.provider && model.id === id
2085
+ )).find((model) => model !== void 0);
2086
+ if (match) picks.push(match);
2087
+ }
2088
+ return picks;
2089
+ }
1949
2090
  function describeAgentModel(runtime, env) {
1950
2091
  const selection = getAgentSelection(env);
1951
2092
  if (!selection) return void 0;
@@ -1961,6 +2102,21 @@ function describeAgentModel(runtime, env) {
1961
2102
  supportsThinking: resolved.model.reasoning !== false
1962
2103
  };
1963
2104
  }
2105
+ async function loginAgentProvider(runtime, provider, apiKey) {
2106
+ const choice = findAgentProvider(runtime, provider);
2107
+ if (!choice) {
2108
+ throw new Error(unknownAgentProviderMessage(provider));
2109
+ }
2110
+ const key = apiKey.trim();
2111
+ if (!key) {
2112
+ throw new Error("API key cannot be empty.");
2113
+ }
2114
+ persistProviderApiKey(choice.id, key);
2115
+ if (typeof runtime.setRuntimeApiKey === "function") {
2116
+ await runtime.setRuntimeApiKey(choice.id, key);
2117
+ }
2118
+ return { provider: choice.id };
2119
+ }
1964
2120
  function setAgentModel(runtime, env, provider, model, options = {}) {
1965
2121
  const current = getAgentSelection(env);
1966
2122
  const requestedThinking = options.thinking ?? current?.thinking ?? "medium";
@@ -1997,6 +2153,65 @@ function setAgentModel(runtime, env, provider, model, options = {}) {
1997
2153
  return { selection, adjustedThinking: effectiveThinking !== requestedThinking };
1998
2154
  }
1999
2155
 
2156
+ // src/secret-prompt.ts
2157
+ import { createInterface } from "readline/promises";
2158
+ import { stdin, stdout } from "process";
2159
+ import { Writable } from "stream";
2160
+ async function promptVisibleInput(message, input = stdin, output = stdout) {
2161
+ return await promptInput(message, false, input, output);
2162
+ }
2163
+ async function promptHiddenInput(message, input = stdin, output = stdout) {
2164
+ return await promptInput(message, true, input, output);
2165
+ }
2166
+ async function withDetachedKeypress(input, run) {
2167
+ const listeners = [...input.listeners("keypress")];
2168
+ input.removeAllListeners("keypress");
2169
+ try {
2170
+ return await run();
2171
+ } finally {
2172
+ input.removeAllListeners("keypress");
2173
+ for (const listener of listeners) {
2174
+ input.on("keypress", listener);
2175
+ }
2176
+ }
2177
+ }
2178
+ async function promptInput(message, hidden, input, output) {
2179
+ if (input.isTTY !== true || output.isTTY !== true) {
2180
+ throw new Error("Provider login needs an interactive tt session.");
2181
+ }
2182
+ return await withDetachedKeypress(input, async () => {
2183
+ if (!hidden) {
2184
+ const rl2 = createInterface({ input, output, terminal: true });
2185
+ try {
2186
+ return await rl2.question(message);
2187
+ } finally {
2188
+ rl2.close();
2189
+ }
2190
+ }
2191
+ let muted = false;
2192
+ const maskedOutput = new Writable({
2193
+ write(chunk, _encoding, callback) {
2194
+ if (!muted) output.write(chunk);
2195
+ callback();
2196
+ }
2197
+ });
2198
+ const rl = createInterface({
2199
+ input,
2200
+ output: maskedOutput,
2201
+ terminal: true
2202
+ });
2203
+ try {
2204
+ const pending = rl.question(message);
2205
+ muted = true;
2206
+ return await pending;
2207
+ } finally {
2208
+ muted = false;
2209
+ output.write("\n");
2210
+ rl.close();
2211
+ }
2212
+ });
2213
+ }
2214
+
2000
2215
  // src/shell.ts
2001
2216
  var AGENT_SLASH_COMMANDS = /* @__PURE__ */ new Set([
2002
2217
  "new",
@@ -2155,6 +2370,7 @@ var SLASH_NAMES = /* @__PURE__ */ new Set([
2155
2370
  "status",
2156
2371
  "context",
2157
2372
  "model",
2373
+ "login",
2158
2374
  "clear",
2159
2375
  "cd",
2160
2376
  "exit"
@@ -2324,6 +2540,9 @@ function agentModelLabel(context) {
2324
2540
  }
2325
2541
  function renderShellBanner(snapshot) {
2326
2542
  const heading = snapshot.version ? `${accent.bold("tt")} ${chalk.dim(`v${snapshot.version}`)}` : accent.bold("tt");
2543
+ const configured = Boolean(
2544
+ snapshot.context.agent?.provider && snapshot.context.agent?.model
2545
+ );
2327
2546
  const lines = [
2328
2547
  heading,
2329
2548
  chalk.dim(
@@ -2331,7 +2550,9 @@ function renderShellBanner(snapshot) {
2331
2550
  ),
2332
2551
  chalk.dim("ctrl+c stop/clear \xB7 ctrl+d exit \xB7 /help commands \xB7 tab complete"),
2333
2552
  "",
2334
- chalk.dim("Ask TT anything. Known commands run directly.")
2553
+ chalk.dim(
2554
+ configured ? "Ask TT anything. Known commands run directly." : "Use /model to choose a provider and model. Workflow commands work now."
2555
+ )
2335
2556
  ];
2336
2557
  return `${lines.join("\n")}
2337
2558
 
@@ -2440,45 +2661,74 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2440
2661
  const lines = styleDetailLines([
2441
2662
  detailLine("Agent model", label),
2442
2663
  detailLine("Change", "/model <provider>/<model>"),
2443
- detailLine("Search", "/model <query>")
2664
+ detailLine("List", "/model <provider>"),
2665
+ detailLine("Search", "/model <query>"),
2666
+ detailLine("Login", "/login <provider>")
2444
2667
  ]);
2445
- const all = listAgentModels(runtime);
2446
- if (all.length === 0) {
2447
- lines.push("", chalk.dim(" no authenticated models \u2014 run `tt agent models --all` for the full catalog"));
2668
+ const catalog = { includeUnauthenticated: true };
2669
+ const featuredCatalog = { ...catalog, featuredOnly: true };
2670
+ const provider = query ? findAgentProvider(runtime, query) : void 0;
2671
+ if (provider) {
2672
+ const matches = listAgentModels(runtime, { ...catalog, provider: provider.id });
2673
+ lines.push("", chalk.bold(`Models from ${provider.id}`));
2674
+ this.appendAgentModelChoices(lines, matches, summary, 20, "/model <query> to search");
2675
+ if (matches.length === 0) {
2676
+ lines.push(chalk.dim(" no models"));
2677
+ }
2448
2678
  return lines;
2449
2679
  }
2450
2680
  if (query) {
2451
- const matches = listAgentModels(runtime, { query });
2681
+ const matches = listAgentModels(runtime, { ...catalog, query });
2452
2682
  lines.push("", chalk.bold(`Models matching ${JSON.stringify(query)}`));
2683
+ this.appendAgentModelChoices(lines, matches, summary, 10, "refine the query");
2453
2684
  if (matches.length === 0) {
2454
2685
  lines.push(chalk.dim(" no matches"));
2455
- } else {
2456
- const shown2 = matches.slice(0, 10);
2457
- for (const model of shown2) {
2458
- lines.push(this.formatAgentModelChoice(model, summary));
2459
- }
2460
- if (matches.length > shown2.length) {
2461
- lines.push(chalk.dim(` \u2026 ${matches.length - shown2.length} more \u2014 refine the query`));
2462
- }
2463
2686
  }
2464
2687
  return lines;
2465
2688
  }
2466
- const preferred = summary ? all.filter((model) => model.provider === summary.provider) : [];
2467
- const rest = summary ? all.filter((model) => model.provider !== summary.provider) : all;
2468
- const shown = [...preferred, ...rest].slice(0, 8);
2469
- lines.push("", chalk.bold(summary ? "Suggestions" : "Available models"));
2689
+ const providers = listAgentProviders(runtime, { featuredOnly: true });
2690
+ lines.push("", chalk.bold("Providers"));
2691
+ if (providers.length === 0) {
2692
+ lines.push(chalk.dim(" no providers in the catalog"));
2693
+ } else {
2694
+ for (const choice of providers) {
2695
+ lines.push(this.formatAgentProviderChoice(choice));
2696
+ }
2697
+ lines.push(chalk.dim(" Other providers: /login <id> or /model <id>"));
2698
+ if (providers.some((choice) => !choice.authenticated)) {
2699
+ lines.push(chalk.dim(" Use /login <provider> to save a key."));
2700
+ }
2701
+ }
2702
+ const suggestions = recommendAgentModels(runtime, featuredCatalog);
2703
+ lines.push("", chalk.bold("Suggestions"));
2704
+ this.appendAgentModelChoices(lines, suggestions, summary);
2705
+ if (suggestions.length === 0) {
2706
+ lines.push(chalk.dim(" no models in the catalog"));
2707
+ } else {
2708
+ lines.push(chalk.dim(" Use /model <provider> to list models, /model <query> to search"));
2709
+ }
2710
+ return lines;
2711
+ }
2712
+ appendAgentModelChoices(lines, models, summary, limit, moreHint) {
2713
+ const shown = limit ? models.slice(0, limit) : models;
2470
2714
  for (const model of shown) {
2471
2715
  lines.push(this.formatAgentModelChoice(model, summary));
2472
2716
  }
2473
- if (all.length > shown.length) {
2474
- lines.push(chalk.dim(` \u2026 ${all.length - shown.length} more \u2014 /model <query> to search`));
2717
+ if (limit && models.length > shown.length) {
2718
+ const hint = moreHint ? ` \u2014 ${moreHint}` : "";
2719
+ lines.push(chalk.dim(` \u2026 ${models.length - shown.length} more${hint}`));
2475
2720
  }
2476
- return lines;
2721
+ }
2722
+ formatAgentProviderChoice(provider) {
2723
+ const displayName = provider.name !== provider.id ? ` ${provider.name}` : "";
2724
+ const auth = provider.authenticated ? "" : chalk.dim(" auth required");
2725
+ return ` ${accent(provider.id)}${displayName}${auth}`;
2477
2726
  }
2478
2727
  formatAgentModelChoice(model, summary) {
2479
2728
  const active = summary?.provider === model.provider && summary?.model === model.id;
2480
2729
  const displayName = model.name !== model.id ? ` ${model.name}` : "";
2481
- return ` ${accent(`${model.provider}/${model.id}`)}${displayName}${active ? chalk.dim(" (active)") : ""}`;
2730
+ const auth = model.authenticated ? "" : chalk.dim(" auth required");
2731
+ return ` ${accent(`${model.provider}/${model.id}`)}${displayName}${active ? chalk.dim(" (active)") : ""}${auth}`;
2482
2732
  }
2483
2733
  async handleSlash(command) {
2484
2734
  switch (command.name) {
@@ -2506,7 +2756,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2506
2756
  return "continue";
2507
2757
  }
2508
2758
  if (command.args.length !== 1) {
2509
- throw new ShellParseError("Usage: /model [<query> | <provider>/<model>]");
2759
+ throw new ShellParseError("Usage: /model [<query> | <provider> | <provider>/<model>]");
2510
2760
  }
2511
2761
  const target = command.args[0];
2512
2762
  const slash = target.indexOf("/");
@@ -2527,6 +2777,48 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2527
2777
  const note = result2.adjustedThinking ? " \u2014 thinking set to off for this model" : "";
2528
2778
  this.io.write(
2529
2779
  `${successMark()} Agent model: ${result2.selection.provider}/${result2.selection.model} (thinking ${result2.selection.thinking})${note}.
2780
+ `
2781
+ );
2782
+ return "continue";
2783
+ }
2784
+ case "login": {
2785
+ const runtime = await this.requireAgentModelRuntime();
2786
+ if (command.args.length > 1) {
2787
+ throw new ShellParseError("Usage: /login [<provider>]");
2788
+ }
2789
+ let providerId = command.args[0]?.trim();
2790
+ if (!providerId) {
2791
+ const providers = listAgentProviders(runtime, { featuredOnly: true });
2792
+ if (providers.length === 0) {
2793
+ throw new ShellParseError("No providers in the catalog.");
2794
+ }
2795
+ this.writeLines([
2796
+ "",
2797
+ chalk.bold("Providers"),
2798
+ ...providers.map((choice) => this.formatAgentProviderChoice(choice)),
2799
+ chalk.dim(" Other providers: /login <id>")
2800
+ ]);
2801
+ if (!this.io.promptLine) {
2802
+ throw new ShellParseError("Usage: /login <provider>");
2803
+ }
2804
+ providerId = (await this.io.promptLine("Provider: ")).trim();
2805
+ if (!providerId) {
2806
+ throw new ShellParseError("Usage: /login <provider>");
2807
+ }
2808
+ }
2809
+ const provider = findAgentProvider(runtime, providerId);
2810
+ if (!provider) {
2811
+ throw new ShellParseError(unknownAgentProviderMessage(providerId));
2812
+ }
2813
+ if (!this.io.promptSecret) {
2814
+ throw new ShellParseError("Provider login needs an interactive tt session.");
2815
+ }
2816
+ const label = provider.name !== provider.id ? provider.name : provider.id;
2817
+ const apiKey = await this.io.promptSecret(`${label} API key: `);
2818
+ const result2 = await loginAgentProvider(runtime, provider.id, apiKey);
2819
+ await this.refreshContext();
2820
+ this.io.write(
2821
+ `${successMark()} Saved ${result2.provider} credentials. Chat and /model can use this provider now.
2530
2822
  `
2531
2823
  );
2532
2824
  return "continue";
@@ -2653,6 +2945,26 @@ async function startInteractiveShell(options) {
2653
2945
  }
2654
2946
  let readline;
2655
2947
  const terminalInput = input;
2948
+ const withPrompt = async (message, prompt) => {
2949
+ readline?.pause();
2950
+ const restoreRawMode = terminalInput.isRaw === true && typeof terminalInput.setRawMode === "function";
2951
+ if (restoreRawMode) terminalInput.setRawMode(false);
2952
+ try {
2953
+ return await prompt(message, input, output);
2954
+ } finally {
2955
+ if (restoreRawMode) terminalInput.setRawMode(true);
2956
+ readline?.resume();
2957
+ }
2958
+ };
2959
+ const io = {
2960
+ ...streamIO(output, error),
2961
+ async promptLine(message) {
2962
+ return await withPrompt(message, promptVisibleInput);
2963
+ },
2964
+ async promptSecret(message) {
2965
+ return await withPrompt(message, promptHiddenInput);
2966
+ }
2967
+ };
2656
2968
  const foregroundRunner = (request) => withForegroundSignalHandoff(
2657
2969
  {
2658
2970
  input: terminalInput,
@@ -2663,7 +2975,7 @@ async function startInteractiveShell(options) {
2663
2975
  );
2664
2976
  const session = await createShellSession({
2665
2977
  runner: foregroundRunner,
2666
- io: streamIO(output, error),
2978
+ io,
2667
2979
  agent: options.agent,
2668
2980
  cwd: options.cwd,
2669
2981
  env: options.env,
@@ -2671,7 +2983,7 @@ async function startInteractiveShell(options) {
2671
2983
  agentModelRuntime: options.agentModelRuntime
2672
2984
  });
2673
2985
  const completer = createCommandCompleter(() => session.snapshot().mode);
2674
- readline = createInterface({
2986
+ readline = createInterface2({
2675
2987
  input,
2676
2988
  output,
2677
2989
  terminal: true,
@@ -2844,7 +3156,7 @@ var StreamingTerminalMarkdown = class {
2844
3156
  var accent3 = chalk3.hex("#8B5CF6");
2845
3157
  var successMark2 = () => chalk3.green("\u2713");
2846
3158
  var errorMark2 = () => chalk3.red("\u2717");
2847
- function isRecord2(value) {
3159
+ function isRecord3(value) {
2848
3160
  return value !== null && typeof value === "object" && !Array.isArray(value);
2849
3161
  }
2850
3162
  function stringValue(value) {
@@ -2885,7 +3197,7 @@ function formatThread(thread, activeId) {
2885
3197
  return `${active} ${chalk3.dim(thread.id.slice(0, 8))} ${sanitizeTerminalText2(thread.title)} ${chalk3.dim(dateLabel)}`;
2886
3198
  }
2887
3199
  function actionFromPayload(payload) {
2888
- const value = isRecord2(payload.action) ? payload.action : isRecord2(payload.approval) ? payload.approval : payload;
3200
+ const value = isRecord3(payload.action) ? payload.action : isRecord3(payload.approval) ? payload.approval : payload;
2889
3201
  const id = stringValue(value.id);
2890
3202
  const title = stringValue(value.title);
2891
3203
  if (!id || !title) return null;
@@ -3018,7 +3330,7 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
3018
3330
  if (event.type === "tool_call") {
3019
3331
  this.flushResponse();
3020
3332
  this.endOpenLine();
3021
- const name = stringValue(payload.name) ?? stringValue(isRecord2(payload.tool_call) ? payload.tool_call.name : null) ?? "tool";
3333
+ const name = stringValue(payload.name) ?? stringValue(isRecord3(payload.tool_call) ? payload.tool_call.name : null) ?? "tool";
3022
3334
  this.options.io.write(chalk3.dim(` \u25CB ${name}
3023
3335
  `));
3024
3336
  return;
@@ -3454,7 +3766,7 @@ function shortId(id) {
3454
3766
  }
3455
3767
 
3456
3768
  // src/local-output.ts
3457
- function isRecord3(value) {
3769
+ function isRecord4(value) {
3458
3770
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3459
3771
  }
3460
3772
  function text(value) {
@@ -3468,10 +3780,10 @@ function number(value) {
3468
3780
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3469
3781
  }
3470
3782
  function record3(value) {
3471
- return isRecord3(value) ? value : void 0;
3783
+ return isRecord4(value) ? value : void 0;
3472
3784
  }
3473
3785
  function rows(value) {
3474
- return Array.isArray(value) ? value.filter(isRecord3) : [];
3786
+ return Array.isArray(value) ? value.filter(isRecord4) : [];
3475
3787
  }
3476
3788
  function displayId(value) {
3477
3789
  const id = text(value);
@@ -3761,19 +4073,19 @@ function renderServePlan(value) {
3761
4073
  function renderKnownJson(args2, value) {
3762
4074
  const command = args2[0];
3763
4075
  const subcommand = args2[1];
3764
- if (command === "doctor" && isRecord3(value)) {
4076
+ if (command === "doctor" && isRecord4(value)) {
3765
4077
  renderDoctor(value);
3766
4078
  return true;
3767
4079
  }
3768
- if (command === "init" && isRecord3(value)) {
4080
+ if (command === "init" && isRecord4(value)) {
3769
4081
  renderInit(value);
3770
4082
  return true;
3771
4083
  }
3772
- if (command === "validate" && isRecord3(value)) {
4084
+ if (command === "validate" && isRecord4(value)) {
3773
4085
  renderValidate(value);
3774
4086
  return true;
3775
4087
  }
3776
- if (command === "run" && isRecord3(value)) {
4088
+ if (command === "run" && isRecord4(value)) {
3777
4089
  renderRun(value);
3778
4090
  return true;
3779
4091
  }
@@ -3786,15 +4098,15 @@ function renderKnownJson(args2, value) {
3786
4098
  renderRunEvents(value);
3787
4099
  return true;
3788
4100
  }
3789
- if (subcommand === "get" && isRecord3(value)) {
4101
+ if (subcommand === "get" && isRecord4(value)) {
3790
4102
  renderRunRecord(value);
3791
4103
  return true;
3792
4104
  }
3793
- if (subcommand === "report" && isRecord3(value)) {
4105
+ if (subcommand === "report" && isRecord4(value)) {
3794
4106
  renderRunReport(value);
3795
4107
  return true;
3796
4108
  }
3797
- if (subcommand === "compare" && isRecord3(value)) {
4109
+ if (subcommand === "compare" && isRecord4(value)) {
3798
4110
  renderRunComparison(value);
3799
4111
  return true;
3800
4112
  }
@@ -3804,25 +4116,25 @@ function renderKnownJson(args2, value) {
3804
4116
  renderModelsList(value);
3805
4117
  return true;
3806
4118
  }
3807
- if ((subcommand === "get" || subcommand === "verify") && isRecord3(value)) {
4119
+ if ((subcommand === "get" || subcommand === "verify") && isRecord4(value)) {
3808
4120
  if (subcommand === "verify") printSuccess("Local model artifact is valid");
3809
4121
  renderModelRecord(value);
3810
4122
  return true;
3811
4123
  }
3812
- if ((subcommand === "prefetch" || subcommand === "verify-base") && isRecord3(value)) {
4124
+ if ((subcommand === "prefetch" || subcommand === "verify-base") && isRecord4(value)) {
3813
4125
  renderModelPrefetch(value);
3814
4126
  return true;
3815
4127
  }
3816
- if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord3(value)) {
4128
+ if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord4(value)) {
3817
4129
  renderActiveModel(value);
3818
4130
  return true;
3819
4131
  }
3820
- if (subcommand === "serve" && isRecord3(value)) {
4132
+ if (subcommand === "serve" && isRecord4(value)) {
3821
4133
  renderServePlan(value);
3822
4134
  return true;
3823
4135
  }
3824
4136
  }
3825
- if (command === "serve" && isRecord3(value)) {
4137
+ if (command === "serve" && isRecord4(value)) {
3826
4138
  renderServePlan(value);
3827
4139
  return true;
3828
4140
  }
@@ -4107,10 +4419,10 @@ function localCommandStreamsStdout(args2) {
4107
4419
  const serves = args2[0] === "serve" || args2[0] === "models" && args2[1] === "serve";
4108
4420
  return serves && !args2.includes("--print-command") && !args2.includes("--help") && !args2.includes("-h");
4109
4421
  }
4110
- function parseJsonOutput(stdout) {
4111
- if (!stdout.trim()) return { hasJson: false, json: void 0 };
4422
+ function parseJsonOutput(stdout2) {
4423
+ if (!stdout2.trim()) return { hasJson: false, json: void 0 };
4112
4424
  try {
4113
- return { hasJson: true, json: JSON.parse(stdout) };
4425
+ return { hasJson: true, json: JSON.parse(stdout2) };
4114
4426
  } catch {
4115
4427
  return { hasJson: false, json: void 0 };
4116
4428
  }
@@ -4156,7 +4468,7 @@ async function spawnLocalCli(args2) {
4156
4468
  }
4157
4469
  );
4158
4470
  if (pipeInput && args2.stdin && child.stdin) args2.stdin.pipe(child.stdin);
4159
- let stdout = "";
4471
+ let stdout2 = "";
4160
4472
  let stderr = "";
4161
4473
  child.stdout?.setEncoding("utf8");
4162
4474
  child.stderr?.setEncoding("utf8");
@@ -4166,7 +4478,7 @@ async function spawnLocalCli(args2) {
4166
4478
  args2.pathReplacements
4167
4479
  );
4168
4480
  if (args2.streamingStdout) writeStream(output, normalized);
4169
- else stdout += normalized;
4481
+ else stdout2 += normalized;
4170
4482
  });
4171
4483
  child.stderr?.on("data", (chunk) => {
4172
4484
  const normalized = normalizeProjectedPaths(
@@ -4211,7 +4523,7 @@ async function spawnLocalCli(args2) {
4211
4523
  resolveChild({
4212
4524
  exitCode: spawnError ? 1 : code2 ?? signalExitCode(signal),
4213
4525
  signal,
4214
- stdout,
4526
+ stdout: stdout2,
4215
4527
  stderr,
4216
4528
  ...spawnError ? { errorMessage: spawnError.message } : {}
4217
4529
  });
@@ -4611,7 +4923,7 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4611
4923
  const selection = getAgentSelection(env);
4612
4924
  if (!selection) {
4613
4925
  throw new Error(
4614
- "The laptop-local agent is not configured. Run `tt agent models --all`, then `tt agent configure --provider <provider> --model <model>`."
4926
+ "The laptop-local agent is not configured. Use /model to choose a provider and model, then try again."
4615
4927
  );
4616
4928
  }
4617
4929
  const modelRuntime = await getModelRuntime();
@@ -4620,8 +4932,9 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4620
4932
  const secret = getApiKey({ apiKey: env.TUNED_TENSOR_API_KEY });
4621
4933
  const providerSecrets = Object.entries(env).filter(([name, value]) => value && /(?:API_KEY|TOKEN|SECRET)$/i.test(name)).map(([, value]) => value).filter((value) => value.length >= 8 && value !== secret);
4622
4934
  return createLocalAgentClient({
4623
- store: runtime.agentStore ?? new LocalAgentStore(join7(getConfigDir(), "agent"), {
4624
- secretValues: [...secret ? [secret] : [], ...providerSecrets]
4935
+ store: runtime.agentStore ?? new LocalAgentStore(getAgentConfigDir(), {
4936
+ secretValues: [...secret ? [secret] : [], ...providerSecrets],
4937
+ secretValueProvider: readStoredProviderSecrets
4625
4938
  }),
4626
4939
  workspaceRoot,
4627
4940
  selection,
@@ -4893,7 +5206,7 @@ async function runCli(version, runtime = {}) {
4893
5206
  }
4894
5207
 
4895
5208
  // src/index.ts
4896
- runCli("0.13.0").catch((err) => {
5209
+ runCli("0.13.1-beta.1").catch((err) => {
4897
5210
  reportError(err);
4898
5211
  process.exit(1);
4899
5212
  });