@tuned-tensor/cli 0.13.0 → 0.13.1-beta.0

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,134 @@ 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(provider) {
664
+ return `Provider "${provider}" is not authenticated. Open the tt shell and run /login ${provider} to save a key, then try again.`;
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 readStoredProviderSecrets(authPath = getAgentAuthPath()) {
680
+ if (!existsSync2(authPath)) return [];
681
+ let parsed;
682
+ try {
683
+ parsed = JSON.parse(readFileSync2(authPath, "utf-8"));
684
+ } catch {
685
+ return [];
686
+ }
687
+ if (!isRecord(parsed)) return [];
688
+ const secrets = [];
689
+ for (const credential of Object.values(parsed)) {
690
+ if (!isRecord(credential)) continue;
691
+ if (credential.type === "api_key") {
692
+ pushSecret(secrets, credential.key);
693
+ } else if (credential.type === "oauth") {
694
+ pushSecret(secrets, credential.access);
695
+ pushSecret(secrets, credential.refresh);
696
+ }
697
+ }
698
+ return secrets;
699
+ }
582
700
  function resolveAgentModel(runtime, selection) {
583
701
  const resolved = resolveAgentModelDefinition(runtime, selection);
584
702
  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
- );
703
+ throw new Error(missingProviderAuthMessage(selection.provider));
588
704
  }
589
705
  return resolved;
590
706
  }
@@ -610,25 +726,46 @@ function resolveAgentModelDefinition(runtime, selection) {
610
726
  }
611
727
  return { model, thinking: selection.thinking };
612
728
  }
729
+ function getAgentAuthPath() {
730
+ return join3(getAgentConfigDir(), "auth.json");
731
+ }
732
+ function getAgentModelsPath() {
733
+ return join3(getAgentConfigDir(), "models.json");
734
+ }
613
735
  async function createPiModelRuntime() {
614
- const agentDir = getAgentDir();
615
- const modelsPath = join2(agentDir, "models.json");
736
+ const agentDir = getAgentConfigDir();
737
+ ensurePrivateDir(agentDir);
738
+ const modelsPath = getAgentModelsPath();
739
+ const authPath = getAgentAuthPath();
616
740
  ensureOpenRouterAppHeaders(modelsPath);
617
- return await ModelRuntime.create({
618
- authPath: join2(agentDir, "auth.json"),
741
+ const runtime = await ModelRuntime.create({
742
+ authPath,
619
743
  modelsPath,
620
744
  allowModelNetwork: false
621
745
  });
746
+ if (existsSync2(authPath)) chmodSync2(authPath, 384);
747
+ return runtime;
622
748
  }
623
749
  var OPENROUTER_APP_HEADERS = {
624
750
  "HTTP-Referer": "https://tunedtensor.com",
751
+ "X-OpenRouter-Title": "Tuned Tensor",
752
+ "X-OpenRouter-Categories": "cli-agent",
625
753
  "X-Title": "Tuned Tensor"
626
754
  };
755
+ function ensurePrivateDir(dir) {
756
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
757
+ chmodSync2(dir, 448);
758
+ }
759
+ function writePrivateJson(path, value) {
760
+ ensurePrivateDir(dirname(path));
761
+ writeFileSync2(path, JSON.stringify(value, null, 2) + "\n", { mode: 384 });
762
+ chmodSync2(path, 384);
763
+ }
627
764
  function ensureOpenRouterAppHeaders(modelsPath) {
628
765
  let config = {};
629
- if (existsSync(modelsPath)) {
766
+ if (existsSync2(modelsPath)) {
630
767
  try {
631
- const parsed = JSON.parse(readFileSync(modelsPath, "utf-8"));
768
+ const parsed = JSON.parse(readFileSync2(modelsPath, "utf-8"));
632
769
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
633
770
  config = parsed;
634
771
  }
@@ -642,10 +779,15 @@ function ensureOpenRouterAppHeaders(modelsPath) {
642
779
  const providers = asRecord(config.providers) ?? (config.providers = empty());
643
780
  const openrouter = asRecord(providers.openrouter) ?? (providers.openrouter = empty());
644
781
  const headers = asRecord(openrouter.headers) ?? (openrouter.headers = empty());
782
+ const attributionNames = new Set(
783
+ Object.keys(OPENROUTER_APP_HEADERS).map((name) => name.toLowerCase())
784
+ );
785
+ for (const name of Object.keys(headers)) {
786
+ if (attributionNames.has(name.toLowerCase())) delete headers[name];
787
+ }
645
788
  Object.assign(headers, OPENROUTER_APP_HEADERS);
646
789
  if (JSON.stringify(config) === before) return;
647
- mkdirSync(dirname(modelsPath), { recursive: true });
648
- writeFileSync(modelsPath, JSON.stringify(config, null, 2) + "\n");
790
+ writePrivateJson(modelsPath, config);
649
791
  }
650
792
 
651
793
  // src/agent-tools.ts
@@ -1335,7 +1477,7 @@ import {
1335
1477
  writeFile as writeFile2
1336
1478
  } from "fs/promises";
1337
1479
  import { randomUUID as randomUUID3 } from "crypto";
1338
- import { join as join3 } from "path";
1480
+ import { join as join4 } from "path";
1339
1481
  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
1482
  var DEFAULT_MAX_THREAD_BYTES = 1e6;
1341
1483
  var DEFAULT_MAX_MESSAGES = 200;
@@ -1345,7 +1487,7 @@ var DEFAULT_MAX_CLAIMS = 1e3;
1345
1487
  function assertThreadId(id) {
1346
1488
  if (!UUID.test(id)) throw new Error("Invalid local agent thread ID.");
1347
1489
  }
1348
- function isRecord(value) {
1490
+ function isRecord2(value) {
1349
1491
  return value !== null && typeof value === "object" && !Array.isArray(value);
1350
1492
  }
1351
1493
  function scrub(value, secrets) {
@@ -1356,7 +1498,7 @@ function scrub(value, secrets) {
1356
1498
  );
1357
1499
  }
1358
1500
  if (Array.isArray(value)) return value.map((entry) => scrub(entry, secrets));
1359
- if (isRecord(value)) {
1501
+ if (isRecord2(value)) {
1360
1502
  return Object.fromEntries(
1361
1503
  Object.entries(value).map(([key, entry]) => [key, scrub(entry, secrets)])
1362
1504
  );
@@ -1366,9 +1508,10 @@ function scrub(value, secrets) {
1366
1508
  var LocalAgentStore = class {
1367
1509
  constructor(rootDir, options = {}) {
1368
1510
  this.rootDir = rootDir;
1369
- this.threadsDir = join3(rootDir, "threads");
1370
- this.claimsDir = join3(rootDir, "action-claims");
1511
+ this.threadsDir = join4(rootDir, "threads");
1512
+ this.claimsDir = join4(rootDir, "action-claims");
1371
1513
  this.secrets = options.secretValues?.filter(Boolean) ?? [];
1514
+ this.secretValueProvider = options.secretValueProvider;
1372
1515
  this.maxThreadBytes = options.maxThreadBytes ?? DEFAULT_MAX_THREAD_BYTES;
1373
1516
  this.maxMessages = options.maxMessages ?? DEFAULT_MAX_MESSAGES;
1374
1517
  this.maxActions = options.maxActions ?? DEFAULT_MAX_ACTIONS;
@@ -1378,13 +1521,20 @@ var LocalAgentStore = class {
1378
1521
  threadsDir;
1379
1522
  claimsDir;
1380
1523
  secrets;
1524
+ secretValueProvider;
1381
1525
  maxThreadBytes;
1382
1526
  maxMessages;
1383
1527
  maxActions;
1384
1528
  maxThreads;
1385
1529
  maxClaims;
1386
1530
  redact(value) {
1387
- return scrub(value, this.secrets);
1531
+ let extra = [];
1532
+ try {
1533
+ extra = this.secretValueProvider?.() ?? [];
1534
+ } catch {
1535
+ extra = [];
1536
+ }
1537
+ return scrub(value, [...this.secrets, ...extra]);
1388
1538
  }
1389
1539
  async ensureDirectories() {
1390
1540
  await mkdir2(this.rootDir, { recursive: true, mode: 448 });
@@ -1396,7 +1546,7 @@ var LocalAgentStore = class {
1396
1546
  }
1397
1547
  path(id) {
1398
1548
  assertThreadId(id);
1399
- return join3(this.threadsDir, `${id}.json`);
1549
+ return join4(this.threadsDir, `${id}.json`);
1400
1550
  }
1401
1551
  serializeWithinQuota(state) {
1402
1552
  const active = state.actions.filter(
@@ -1421,11 +1571,11 @@ var LocalAgentStore = class {
1421
1571
  return serialized;
1422
1572
  }
1423
1573
  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 })));
1574
+ 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
1575
  entries.sort((a, b) => a.modified - b.modified);
1426
1576
  const keepOther = Math.max(0, maximum - (preserve ? 1 : 0));
1427
1577
  for (const entry of entries.slice(0, Math.max(0, entries.length - keepOther))) {
1428
- await unlink(join3(directory, entry.name));
1578
+ await unlink(join4(directory, entry.name));
1429
1579
  }
1430
1580
  }
1431
1581
  async pruneThreads(preserve) {
@@ -1445,19 +1595,19 @@ var LocalAgentStore = class {
1445
1595
  }
1446
1596
  const candidates = await Promise.all(names.filter((name) => !protectedNames.has(name)).map(async (name) => ({
1447
1597
  name,
1448
- modified: (await stat(join3(this.threadsDir, name))).mtimeMs
1598
+ modified: (await stat(join4(this.threadsDir, name))).mtimeMs
1449
1599
  })));
1450
1600
  candidates.sort((a, b) => a.modified - b.modified);
1451
1601
  const keep = Math.max(0, this.maxThreads - protectedNames.size);
1452
1602
  for (const entry of candidates.slice(0, Math.max(0, candidates.length - keep))) {
1453
- await unlink(join3(this.threadsDir, entry.name));
1603
+ await unlink(join4(this.threadsDir, entry.name));
1454
1604
  }
1455
1605
  }
1456
1606
  async save(state) {
1457
1607
  assertThreadId(state.thread.id);
1458
1608
  await this.ensureDirectories();
1459
1609
  const destination = this.path(state.thread.id);
1460
- const temporary = join3(
1610
+ const temporary = join4(
1461
1611
  this.threadsDir,
1462
1612
  `.${state.thread.id}.${randomUUID3()}.tmp`
1463
1613
  );
@@ -1477,7 +1627,7 @@ var LocalAgentStore = class {
1477
1627
  async claimAction(actionId) {
1478
1628
  assertThreadId(actionId);
1479
1629
  await this.ensureDirectories();
1480
- const path = join3(this.claimsDir, `${actionId}.claim`);
1630
+ const path = join4(this.claimsDir, `${actionId}.claim`);
1481
1631
  try {
1482
1632
  await writeFile2(path, `${(/* @__PURE__ */ new Date()).toISOString()}
1483
1633
  `, {
@@ -1505,7 +1655,7 @@ var LocalAgentStore = class {
1505
1655
  throw new Error("Invalid local agent thread file.");
1506
1656
  }
1507
1657
  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)) {
1658
+ if (!isRecord2(parsed) || !isRecord2(parsed.thread) || parsed.thread.id !== id || !Array.isArray(parsed.messages) || !Array.isArray(parsed.actions)) {
1509
1659
  throw new Error("Local agent thread file is malformed.");
1510
1660
  }
1511
1661
  return parsed;
@@ -1528,88 +1678,6 @@ var LocalAgentStore = class {
1528
1678
  }
1529
1679
  };
1530
1680
 
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
1681
  // src/agent.ts
1614
1682
  import chalk3 from "chalk";
1615
1683
  import { stripVTControlCharacters as stripVTControlCharacters3 } from "util";
@@ -1619,7 +1687,7 @@ import chalk from "chalk";
1619
1687
  import { stat as stat3 } from "fs/promises";
1620
1688
  import { homedir as homedir3 } from "os";
1621
1689
  import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
1622
- import { createInterface } from "readline";
1690
+ import { createInterface as createInterface2 } from "readline";
1623
1691
  import { stripVTControlCharacters } from "util";
1624
1692
 
1625
1693
  // src/command-catalog.ts
@@ -1659,7 +1727,8 @@ var SLASH_COMMANDS = [
1659
1727
  { path: "/help", description: "Show commands; add a word to filter." },
1660
1728
  { path: "/status", description: "Show lightweight workflow status." },
1661
1729
  { path: "/context", description: "Show the current project context." },
1662
- { path: "/model", description: "Show or change the TT agent model." },
1730
+ { path: "/model", description: "Show or change the TT agent provider and model." },
1731
+ { path: "/login", description: "Save a provider API key; asks which provider if omitted." },
1663
1732
  { path: "/cd", description: "Change the shell's working directory." },
1664
1733
  { path: "/clear", description: "Clear the terminal." },
1665
1734
  { path: "/exit", description: "Exit the TT shell." }
@@ -1901,6 +1970,24 @@ function formatShellStatus(context) {
1901
1970
  }
1902
1971
 
1903
1972
  // src/agent-control.ts
1973
+ import { chmodSync as chmodSync3, existsSync as existsSync3 } from "fs";
1974
+ var FEATURED_AGENT_PROVIDERS = [
1975
+ "openai",
1976
+ "openrouter"
1977
+ ];
1978
+ var RECOMMENDED_AGENT_MODELS = [
1979
+ { provider: "openai", ids: ["gpt-5.6-sol"] },
1980
+ {
1981
+ provider: "openrouter",
1982
+ ids: ["deepseek/deepseek-v4-flash-0731", "deepseek/deepseek-v4-flash"]
1983
+ }
1984
+ ];
1985
+ var FEATURED_PROVIDER_INDEX = new Map(
1986
+ FEATURED_AGENT_PROVIDERS.map((id, index) => [id, index])
1987
+ );
1988
+ function isFeaturedProvider(id) {
1989
+ return FEATURED_PROVIDER_INDEX.has(id);
1990
+ }
1904
1991
  function modelKey(model) {
1905
1992
  return `${model.provider}/${model.id}`;
1906
1993
  }
@@ -1917,9 +2004,28 @@ function modelRelevance(model, query) {
1917
2004
  if (name.includes(q)) return 4;
1918
2005
  return void 0;
1919
2006
  }
2007
+ function listAgentProviders(runtime, options = {}) {
2008
+ return runtime.getProviders().filter((provider) => !options.featuredOnly || isFeaturedProvider(provider.id)).map((provider) => ({
2009
+ id: provider.id,
2010
+ name: provider.name ?? provider.id,
2011
+ authenticated: runtime.hasConfiguredAuth(provider.id)
2012
+ })).sort((left, right) => {
2013
+ const leftRank = FEATURED_PROVIDER_INDEX.get(left.id);
2014
+ const rightRank = FEATURED_PROVIDER_INDEX.get(right.id);
2015
+ if (leftRank !== void 0 || rightRank !== void 0) {
2016
+ return (leftRank ?? Number.MAX_SAFE_INTEGER) - (rightRank ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id);
2017
+ }
2018
+ return left.id.localeCompare(right.id);
2019
+ });
2020
+ }
2021
+ function findAgentProvider(runtime, id) {
2022
+ const needle = id.trim().toLowerCase();
2023
+ if (!needle) return void 0;
2024
+ return listAgentProviders(runtime).find((provider) => provider.id.toLowerCase() === needle);
2025
+ }
1920
2026
  function listAgentModels(runtime, options = {}) {
1921
2027
  let models = runtime.getModels(options.provider).filter(
1922
- (model) => options.includeUnauthenticated || runtime.hasConfiguredAuth(model.provider)
2028
+ (model) => (options.includeUnauthenticated || runtime.hasConfiguredAuth(model.provider)) && (!options.featuredOnly || isFeaturedProvider(model.provider))
1923
2029
  ).map((model) => ({
1924
2030
  provider: model.provider,
1925
2031
  id: model.id,
@@ -1946,6 +2052,19 @@ function listAgentModels(runtime, options = {}) {
1946
2052
  }
1947
2053
  return models;
1948
2054
  }
2055
+ function recommendAgentModels(runtime, options = {}) {
2056
+ const models = listAgentModels(runtime, {
2057
+ includeUnauthenticated: options.includeUnauthenticated ?? true
2058
+ });
2059
+ const picks = [];
2060
+ for (const recommendation of RECOMMENDED_AGENT_MODELS) {
2061
+ const match = recommendation.ids.map((id) => models.find(
2062
+ (model) => model.provider === recommendation.provider && model.id === id
2063
+ )).find((model) => model !== void 0);
2064
+ if (match) picks.push(match);
2065
+ }
2066
+ return picks;
2067
+ }
1949
2068
  function describeAgentModel(runtime, env) {
1950
2069
  const selection = getAgentSelection(env);
1951
2070
  if (!selection) return void 0;
@@ -1961,6 +2080,23 @@ function describeAgentModel(runtime, env) {
1961
2080
  supportsThinking: resolved.model.reasoning !== false
1962
2081
  };
1963
2082
  }
2083
+ async function loginAgentProvider(runtime, provider, apiKey) {
2084
+ const choice = findAgentProvider(runtime, provider);
2085
+ if (!choice) {
2086
+ throw new Error(unknownAgentProviderMessage(provider));
2087
+ }
2088
+ const key = apiKey.trim();
2089
+ if (!key) {
2090
+ throw new Error("API key cannot be empty.");
2091
+ }
2092
+ if (typeof runtime.setRuntimeApiKey !== "function") {
2093
+ throw new Error("This session cannot store provider credentials.");
2094
+ }
2095
+ await runtime.setRuntimeApiKey(choice.id, key);
2096
+ const authPath = getAgentAuthPath();
2097
+ if (existsSync3(authPath)) chmodSync3(authPath, 384);
2098
+ return { provider: choice.id };
2099
+ }
1964
2100
  function setAgentModel(runtime, env, provider, model, options = {}) {
1965
2101
  const current = getAgentSelection(env);
1966
2102
  const requestedThinking = options.thinking ?? current?.thinking ?? "medium";
@@ -1997,6 +2133,65 @@ function setAgentModel(runtime, env, provider, model, options = {}) {
1997
2133
  return { selection, adjustedThinking: effectiveThinking !== requestedThinking };
1998
2134
  }
1999
2135
 
2136
+ // src/secret-prompt.ts
2137
+ import { createInterface } from "readline/promises";
2138
+ import { stdin, stdout } from "process";
2139
+ import { Writable } from "stream";
2140
+ async function promptVisibleInput(message, input = stdin, output = stdout) {
2141
+ return await promptInput(message, false, input, output);
2142
+ }
2143
+ async function promptHiddenInput(message, input = stdin, output = stdout) {
2144
+ return await promptInput(message, true, input, output);
2145
+ }
2146
+ async function withDetachedKeypress(input, run) {
2147
+ const listeners = [...input.listeners("keypress")];
2148
+ input.removeAllListeners("keypress");
2149
+ try {
2150
+ return await run();
2151
+ } finally {
2152
+ input.removeAllListeners("keypress");
2153
+ for (const listener of listeners) {
2154
+ input.on("keypress", listener);
2155
+ }
2156
+ }
2157
+ }
2158
+ async function promptInput(message, hidden, input, output) {
2159
+ if (input.isTTY !== true || output.isTTY !== true) {
2160
+ throw new Error("Provider login needs an interactive tt session.");
2161
+ }
2162
+ return await withDetachedKeypress(input, async () => {
2163
+ if (!hidden) {
2164
+ const rl2 = createInterface({ input, output, terminal: true });
2165
+ try {
2166
+ return await rl2.question(message);
2167
+ } finally {
2168
+ rl2.close();
2169
+ }
2170
+ }
2171
+ let muted = false;
2172
+ const maskedOutput = new Writable({
2173
+ write(chunk, _encoding, callback) {
2174
+ if (!muted) output.write(chunk);
2175
+ callback();
2176
+ }
2177
+ });
2178
+ const rl = createInterface({
2179
+ input,
2180
+ output: maskedOutput,
2181
+ terminal: true
2182
+ });
2183
+ try {
2184
+ const pending = rl.question(message);
2185
+ muted = true;
2186
+ return await pending;
2187
+ } finally {
2188
+ muted = false;
2189
+ output.write("\n");
2190
+ rl.close();
2191
+ }
2192
+ });
2193
+ }
2194
+
2000
2195
  // src/shell.ts
2001
2196
  var AGENT_SLASH_COMMANDS = /* @__PURE__ */ new Set([
2002
2197
  "new",
@@ -2155,6 +2350,7 @@ var SLASH_NAMES = /* @__PURE__ */ new Set([
2155
2350
  "status",
2156
2351
  "context",
2157
2352
  "model",
2353
+ "login",
2158
2354
  "clear",
2159
2355
  "cd",
2160
2356
  "exit"
@@ -2324,6 +2520,9 @@ function agentModelLabel(context) {
2324
2520
  }
2325
2521
  function renderShellBanner(snapshot) {
2326
2522
  const heading = snapshot.version ? `${accent.bold("tt")} ${chalk.dim(`v${snapshot.version}`)}` : accent.bold("tt");
2523
+ const configured = Boolean(
2524
+ snapshot.context.agent?.provider && snapshot.context.agent?.model
2525
+ );
2327
2526
  const lines = [
2328
2527
  heading,
2329
2528
  chalk.dim(
@@ -2331,7 +2530,9 @@ function renderShellBanner(snapshot) {
2331
2530
  ),
2332
2531
  chalk.dim("ctrl+c stop/clear \xB7 ctrl+d exit \xB7 /help commands \xB7 tab complete"),
2333
2532
  "",
2334
- chalk.dim("Ask TT anything. Known commands run directly.")
2533
+ chalk.dim(
2534
+ configured ? "Ask TT anything. Known commands run directly." : "Use /model to choose a provider and model. Workflow commands work now."
2535
+ )
2335
2536
  ];
2336
2537
  return `${lines.join("\n")}
2337
2538
 
@@ -2440,45 +2641,74 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2440
2641
  const lines = styleDetailLines([
2441
2642
  detailLine("Agent model", label),
2442
2643
  detailLine("Change", "/model <provider>/<model>"),
2443
- detailLine("Search", "/model <query>")
2644
+ detailLine("List", "/model <provider>"),
2645
+ detailLine("Search", "/model <query>"),
2646
+ detailLine("Login", "/login <provider>")
2444
2647
  ]);
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"));
2648
+ const catalog = { includeUnauthenticated: true };
2649
+ const featuredCatalog = { ...catalog, featuredOnly: true };
2650
+ const provider = query ? findAgentProvider(runtime, query) : void 0;
2651
+ if (provider) {
2652
+ const matches = listAgentModels(runtime, { ...catalog, provider: provider.id });
2653
+ lines.push("", chalk.bold(`Models from ${provider.id}`));
2654
+ this.appendAgentModelChoices(lines, matches, summary, 20, "/model <query> to search");
2655
+ if (matches.length === 0) {
2656
+ lines.push(chalk.dim(" no models"));
2657
+ }
2448
2658
  return lines;
2449
2659
  }
2450
2660
  if (query) {
2451
- const matches = listAgentModels(runtime, { query });
2661
+ const matches = listAgentModels(runtime, { ...catalog, query });
2452
2662
  lines.push("", chalk.bold(`Models matching ${JSON.stringify(query)}`));
2663
+ this.appendAgentModelChoices(lines, matches, summary, 10, "refine the query");
2453
2664
  if (matches.length === 0) {
2454
2665
  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
2666
  }
2464
2667
  return lines;
2465
2668
  }
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"));
2669
+ const providers = listAgentProviders(runtime, { featuredOnly: true });
2670
+ lines.push("", chalk.bold("Providers"));
2671
+ if (providers.length === 0) {
2672
+ lines.push(chalk.dim(" no providers in the catalog"));
2673
+ } else {
2674
+ for (const choice of providers) {
2675
+ lines.push(this.formatAgentProviderChoice(choice));
2676
+ }
2677
+ lines.push(chalk.dim(" Other providers: /login <id> or /model <id>"));
2678
+ if (providers.some((choice) => !choice.authenticated)) {
2679
+ lines.push(chalk.dim(" Use /login <provider> to save a key."));
2680
+ }
2681
+ }
2682
+ const suggestions = recommendAgentModels(runtime, featuredCatalog);
2683
+ lines.push("", chalk.bold("Suggestions"));
2684
+ this.appendAgentModelChoices(lines, suggestions, summary);
2685
+ if (suggestions.length === 0) {
2686
+ lines.push(chalk.dim(" no models in the catalog"));
2687
+ } else {
2688
+ lines.push(chalk.dim(" Use /model <provider> to list models, /model <query> to search"));
2689
+ }
2690
+ return lines;
2691
+ }
2692
+ appendAgentModelChoices(lines, models, summary, limit, moreHint) {
2693
+ const shown = limit ? models.slice(0, limit) : models;
2470
2694
  for (const model of shown) {
2471
2695
  lines.push(this.formatAgentModelChoice(model, summary));
2472
2696
  }
2473
- if (all.length > shown.length) {
2474
- lines.push(chalk.dim(` \u2026 ${all.length - shown.length} more \u2014 /model <query> to search`));
2697
+ if (limit && models.length > shown.length) {
2698
+ const hint = moreHint ? ` \u2014 ${moreHint}` : "";
2699
+ lines.push(chalk.dim(` \u2026 ${models.length - shown.length} more${hint}`));
2475
2700
  }
2476
- return lines;
2701
+ }
2702
+ formatAgentProviderChoice(provider) {
2703
+ const displayName = provider.name !== provider.id ? ` ${provider.name}` : "";
2704
+ const auth = provider.authenticated ? "" : chalk.dim(" auth required");
2705
+ return ` ${accent(provider.id)}${displayName}${auth}`;
2477
2706
  }
2478
2707
  formatAgentModelChoice(model, summary) {
2479
2708
  const active = summary?.provider === model.provider && summary?.model === model.id;
2480
2709
  const displayName = model.name !== model.id ? ` ${model.name}` : "";
2481
- return ` ${accent(`${model.provider}/${model.id}`)}${displayName}${active ? chalk.dim(" (active)") : ""}`;
2710
+ const auth = model.authenticated ? "" : chalk.dim(" auth required");
2711
+ return ` ${accent(`${model.provider}/${model.id}`)}${displayName}${active ? chalk.dim(" (active)") : ""}${auth}`;
2482
2712
  }
2483
2713
  async handleSlash(command) {
2484
2714
  switch (command.name) {
@@ -2506,7 +2736,7 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2506
2736
  return "continue";
2507
2737
  }
2508
2738
  if (command.args.length !== 1) {
2509
- throw new ShellParseError("Usage: /model [<query> | <provider>/<model>]");
2739
+ throw new ShellParseError("Usage: /model [<query> | <provider> | <provider>/<model>]");
2510
2740
  }
2511
2741
  const target = command.args[0];
2512
2742
  const slash = target.indexOf("/");
@@ -2527,6 +2757,48 @@ var TunedTensorShellSession = class _TunedTensorShellSession {
2527
2757
  const note = result2.adjustedThinking ? " \u2014 thinking set to off for this model" : "";
2528
2758
  this.io.write(
2529
2759
  `${successMark()} Agent model: ${result2.selection.provider}/${result2.selection.model} (thinking ${result2.selection.thinking})${note}.
2760
+ `
2761
+ );
2762
+ return "continue";
2763
+ }
2764
+ case "login": {
2765
+ const runtime = await this.requireAgentModelRuntime();
2766
+ if (command.args.length > 1) {
2767
+ throw new ShellParseError("Usage: /login [<provider>]");
2768
+ }
2769
+ let providerId = command.args[0]?.trim();
2770
+ if (!providerId) {
2771
+ const providers = listAgentProviders(runtime, { featuredOnly: true });
2772
+ if (providers.length === 0) {
2773
+ throw new ShellParseError("No providers in the catalog.");
2774
+ }
2775
+ this.writeLines([
2776
+ "",
2777
+ chalk.bold("Providers"),
2778
+ ...providers.map((choice) => this.formatAgentProviderChoice(choice)),
2779
+ chalk.dim(" Other providers: /login <id>")
2780
+ ]);
2781
+ if (!this.io.promptLine) {
2782
+ throw new ShellParseError("Usage: /login <provider>");
2783
+ }
2784
+ providerId = (await this.io.promptLine("Provider: ")).trim();
2785
+ if (!providerId) {
2786
+ throw new ShellParseError("Usage: /login <provider>");
2787
+ }
2788
+ }
2789
+ const provider = findAgentProvider(runtime, providerId);
2790
+ if (!provider) {
2791
+ throw new ShellParseError(unknownAgentProviderMessage(providerId));
2792
+ }
2793
+ if (!this.io.promptSecret) {
2794
+ throw new ShellParseError("Provider login needs an interactive tt session.");
2795
+ }
2796
+ const label = provider.name !== provider.id ? provider.name : provider.id;
2797
+ const apiKey = await this.io.promptSecret(`${label} API key: `);
2798
+ const result2 = await loginAgentProvider(runtime, provider.id, apiKey);
2799
+ await this.refreshContext();
2800
+ this.io.write(
2801
+ `${successMark()} Saved ${result2.provider} credentials. Chat and /model can use this provider now.
2530
2802
  `
2531
2803
  );
2532
2804
  return "continue";
@@ -2653,6 +2925,26 @@ async function startInteractiveShell(options) {
2653
2925
  }
2654
2926
  let readline;
2655
2927
  const terminalInput = input;
2928
+ const withPrompt = async (message, prompt) => {
2929
+ readline?.pause();
2930
+ const restoreRawMode = terminalInput.isRaw === true && typeof terminalInput.setRawMode === "function";
2931
+ if (restoreRawMode) terminalInput.setRawMode(false);
2932
+ try {
2933
+ return await prompt(message, input, output);
2934
+ } finally {
2935
+ if (restoreRawMode) terminalInput.setRawMode(true);
2936
+ readline?.resume();
2937
+ }
2938
+ };
2939
+ const io = {
2940
+ ...streamIO(output, error),
2941
+ async promptLine(message) {
2942
+ return await withPrompt(message, promptVisibleInput);
2943
+ },
2944
+ async promptSecret(message) {
2945
+ return await withPrompt(message, promptHiddenInput);
2946
+ }
2947
+ };
2656
2948
  const foregroundRunner = (request) => withForegroundSignalHandoff(
2657
2949
  {
2658
2950
  input: terminalInput,
@@ -2663,7 +2955,7 @@ async function startInteractiveShell(options) {
2663
2955
  );
2664
2956
  const session = await createShellSession({
2665
2957
  runner: foregroundRunner,
2666
- io: streamIO(output, error),
2958
+ io,
2667
2959
  agent: options.agent,
2668
2960
  cwd: options.cwd,
2669
2961
  env: options.env,
@@ -2671,7 +2963,7 @@ async function startInteractiveShell(options) {
2671
2963
  agentModelRuntime: options.agentModelRuntime
2672
2964
  });
2673
2965
  const completer = createCommandCompleter(() => session.snapshot().mode);
2674
- readline = createInterface({
2966
+ readline = createInterface2({
2675
2967
  input,
2676
2968
  output,
2677
2969
  terminal: true,
@@ -2844,7 +3136,7 @@ var StreamingTerminalMarkdown = class {
2844
3136
  var accent3 = chalk3.hex("#8B5CF6");
2845
3137
  var successMark2 = () => chalk3.green("\u2713");
2846
3138
  var errorMark2 = () => chalk3.red("\u2717");
2847
- function isRecord2(value) {
3139
+ function isRecord3(value) {
2848
3140
  return value !== null && typeof value === "object" && !Array.isArray(value);
2849
3141
  }
2850
3142
  function stringValue(value) {
@@ -2885,7 +3177,7 @@ function formatThread(thread, activeId) {
2885
3177
  return `${active} ${chalk3.dim(thread.id.slice(0, 8))} ${sanitizeTerminalText2(thread.title)} ${chalk3.dim(dateLabel)}`;
2886
3178
  }
2887
3179
  function actionFromPayload(payload) {
2888
- const value = isRecord2(payload.action) ? payload.action : isRecord2(payload.approval) ? payload.approval : payload;
3180
+ const value = isRecord3(payload.action) ? payload.action : isRecord3(payload.approval) ? payload.approval : payload;
2889
3181
  const id = stringValue(value.id);
2890
3182
  const title = stringValue(value.title);
2891
3183
  if (!id || !title) return null;
@@ -3018,7 +3310,7 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
3018
3310
  if (event.type === "tool_call") {
3019
3311
  this.flushResponse();
3020
3312
  this.endOpenLine();
3021
- const name = stringValue(payload.name) ?? stringValue(isRecord2(payload.tool_call) ? payload.tool_call.name : null) ?? "tool";
3313
+ const name = stringValue(payload.name) ?? stringValue(isRecord3(payload.tool_call) ? payload.tool_call.name : null) ?? "tool";
3022
3314
  this.options.io.write(chalk3.dim(` \u25CB ${name}
3023
3315
  `));
3024
3316
  return;
@@ -3276,7 +3568,7 @@ ${chalk3.yellow.bold("Approval required")} ${chalk3.dim(action.id.slice(0, 8))}
3276
3568
  import { constants as osConstants } from "os";
3277
3569
  import { randomUUID as randomUUID4 } from "crypto";
3278
3570
  import { spawn } from "child_process";
3279
- import { existsSync as existsSync3 } from "fs";
3571
+ import { existsSync as existsSync4 } from "fs";
3280
3572
  import { fileURLToPath } from "url";
3281
3573
  import {
3282
3574
  readFile as readFile3,
@@ -3454,7 +3746,7 @@ function shortId(id) {
3454
3746
  }
3455
3747
 
3456
3748
  // src/local-output.ts
3457
- function isRecord3(value) {
3749
+ function isRecord4(value) {
3458
3750
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3459
3751
  }
3460
3752
  function text(value) {
@@ -3468,10 +3760,10 @@ function number(value) {
3468
3760
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3469
3761
  }
3470
3762
  function record3(value) {
3471
- return isRecord3(value) ? value : void 0;
3763
+ return isRecord4(value) ? value : void 0;
3472
3764
  }
3473
3765
  function rows(value) {
3474
- return Array.isArray(value) ? value.filter(isRecord3) : [];
3766
+ return Array.isArray(value) ? value.filter(isRecord4) : [];
3475
3767
  }
3476
3768
  function displayId(value) {
3477
3769
  const id = text(value);
@@ -3761,19 +4053,19 @@ function renderServePlan(value) {
3761
4053
  function renderKnownJson(args2, value) {
3762
4054
  const command = args2[0];
3763
4055
  const subcommand = args2[1];
3764
- if (command === "doctor" && isRecord3(value)) {
4056
+ if (command === "doctor" && isRecord4(value)) {
3765
4057
  renderDoctor(value);
3766
4058
  return true;
3767
4059
  }
3768
- if (command === "init" && isRecord3(value)) {
4060
+ if (command === "init" && isRecord4(value)) {
3769
4061
  renderInit(value);
3770
4062
  return true;
3771
4063
  }
3772
- if (command === "validate" && isRecord3(value)) {
4064
+ if (command === "validate" && isRecord4(value)) {
3773
4065
  renderValidate(value);
3774
4066
  return true;
3775
4067
  }
3776
- if (command === "run" && isRecord3(value)) {
4068
+ if (command === "run" && isRecord4(value)) {
3777
4069
  renderRun(value);
3778
4070
  return true;
3779
4071
  }
@@ -3786,15 +4078,15 @@ function renderKnownJson(args2, value) {
3786
4078
  renderRunEvents(value);
3787
4079
  return true;
3788
4080
  }
3789
- if (subcommand === "get" && isRecord3(value)) {
4081
+ if (subcommand === "get" && isRecord4(value)) {
3790
4082
  renderRunRecord(value);
3791
4083
  return true;
3792
4084
  }
3793
- if (subcommand === "report" && isRecord3(value)) {
4085
+ if (subcommand === "report" && isRecord4(value)) {
3794
4086
  renderRunReport(value);
3795
4087
  return true;
3796
4088
  }
3797
- if (subcommand === "compare" && isRecord3(value)) {
4089
+ if (subcommand === "compare" && isRecord4(value)) {
3798
4090
  renderRunComparison(value);
3799
4091
  return true;
3800
4092
  }
@@ -3804,25 +4096,25 @@ function renderKnownJson(args2, value) {
3804
4096
  renderModelsList(value);
3805
4097
  return true;
3806
4098
  }
3807
- if ((subcommand === "get" || subcommand === "verify") && isRecord3(value)) {
4099
+ if ((subcommand === "get" || subcommand === "verify") && isRecord4(value)) {
3808
4100
  if (subcommand === "verify") printSuccess("Local model artifact is valid");
3809
4101
  renderModelRecord(value);
3810
4102
  return true;
3811
4103
  }
3812
- if ((subcommand === "prefetch" || subcommand === "verify-base") && isRecord3(value)) {
4104
+ if ((subcommand === "prefetch" || subcommand === "verify-base") && isRecord4(value)) {
3813
4105
  renderModelPrefetch(value);
3814
4106
  return true;
3815
4107
  }
3816
- if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord3(value)) {
4108
+ if (["active", "activate", "rollback"].includes(subcommand ?? "") && isRecord4(value)) {
3817
4109
  renderActiveModel(value);
3818
4110
  return true;
3819
4111
  }
3820
- if (subcommand === "serve" && isRecord3(value)) {
4112
+ if (subcommand === "serve" && isRecord4(value)) {
3821
4113
  renderServePlan(value);
3822
4114
  return true;
3823
4115
  }
3824
4116
  }
3825
- if (command === "serve" && isRecord3(value)) {
4117
+ if (command === "serve" && isRecord4(value)) {
3826
4118
  renderServePlan(value);
3827
4119
  return true;
3828
4120
  }
@@ -4096,7 +4388,7 @@ async function projectSpecArguments(originalArgs, cwd) {
4096
4388
  }
4097
4389
  function resolveLocalCliEntrypoint() {
4098
4390
  const entrypoint = fileURLToPath(new URL("./local-runtime.js", import.meta.url));
4099
- if (!existsSync3(entrypoint)) {
4391
+ if (!existsSync4(entrypoint)) {
4100
4392
  throw new Error(
4101
4393
  "The bundled local runtime is missing. Rebuild or reinstall @tuned-tensor/cli."
4102
4394
  );
@@ -4107,10 +4399,10 @@ function localCommandStreamsStdout(args2) {
4107
4399
  const serves = args2[0] === "serve" || args2[0] === "models" && args2[1] === "serve";
4108
4400
  return serves && !args2.includes("--print-command") && !args2.includes("--help") && !args2.includes("-h");
4109
4401
  }
4110
- function parseJsonOutput(stdout) {
4111
- if (!stdout.trim()) return { hasJson: false, json: void 0 };
4402
+ function parseJsonOutput(stdout2) {
4403
+ if (!stdout2.trim()) return { hasJson: false, json: void 0 };
4112
4404
  try {
4113
- return { hasJson: true, json: JSON.parse(stdout) };
4405
+ return { hasJson: true, json: JSON.parse(stdout2) };
4114
4406
  } catch {
4115
4407
  return { hasJson: false, json: void 0 };
4116
4408
  }
@@ -4156,7 +4448,7 @@ async function spawnLocalCli(args2) {
4156
4448
  }
4157
4449
  );
4158
4450
  if (pipeInput && args2.stdin && child.stdin) args2.stdin.pipe(child.stdin);
4159
- let stdout = "";
4451
+ let stdout2 = "";
4160
4452
  let stderr = "";
4161
4453
  child.stdout?.setEncoding("utf8");
4162
4454
  child.stderr?.setEncoding("utf8");
@@ -4166,7 +4458,7 @@ async function spawnLocalCli(args2) {
4166
4458
  args2.pathReplacements
4167
4459
  );
4168
4460
  if (args2.streamingStdout) writeStream(output, normalized);
4169
- else stdout += normalized;
4461
+ else stdout2 += normalized;
4170
4462
  });
4171
4463
  child.stderr?.on("data", (chunk) => {
4172
4464
  const normalized = normalizeProjectedPaths(
@@ -4211,7 +4503,7 @@ async function spawnLocalCli(args2) {
4211
4503
  resolveChild({
4212
4504
  exitCode: spawnError ? 1 : code2 ?? signalExitCode(signal),
4213
4505
  signal,
4214
- stdout,
4506
+ stdout: stdout2,
4215
4507
  stderr,
4216
4508
  ...spawnError ? { errorMessage: spawnError.message } : {}
4217
4509
  });
@@ -4426,7 +4718,7 @@ function registerAgentCommands(parent, options) {
4426
4718
  }
4427
4719
 
4428
4720
  // src/commands/pipeline.ts
4429
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
4721
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
4430
4722
  import { resolve as resolve4 } from "path";
4431
4723
  var DEFAULT_PIPELINE_FILE = "tunedtensor.pipeline.json";
4432
4724
  function parseList(value) {
@@ -4437,7 +4729,7 @@ function parseList(value) {
4437
4729
  }
4438
4730
  function loadPipeline(path) {
4439
4731
  const resolved = resolve4(path);
4440
- if (!existsSync4(resolved)) throw new Error(`Pipeline file not found: ${path}. Run \`tt pipeline init\` to create one.`);
4732
+ if (!existsSync5(resolved)) throw new Error(`Pipeline file not found: ${path}. Run \`tt pipeline init\` to create one.`);
4441
4733
  try {
4442
4734
  return JSON.parse(readFileSync4(resolved, "utf8"));
4443
4735
  } catch (error) {
@@ -4456,7 +4748,7 @@ function registerPipelineCommands(parent) {
4456
4748
  const pipeline = parent.command("pipeline").description("Create, validate, inspect, and safely dry-run ordered pipeline recipes");
4457
4749
  pipeline.command("init").description("Write a canonical v1 pipeline recipe").option("-f, --file <path>", "Output file", DEFAULT_PIPELINE_FILE).action((options) => {
4458
4750
  const path = resolve4(options.file);
4459
- if (existsSync4(path)) throw new Error(`Pipeline file already exists: ${options.file}.`);
4751
+ if (existsSync5(path)) throw new Error(`Pipeline file already exists: ${options.file}.`);
4460
4752
  const recipe = canonicalPipeline("local");
4461
4753
  writeFileSync3(path, `${JSON.stringify(recipe, null, 2)}
4462
4754
  `);
@@ -4611,7 +4903,7 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4611
4903
  const selection = getAgentSelection(env);
4612
4904
  if (!selection) {
4613
4905
  throw new Error(
4614
- "The laptop-local agent is not configured. Run `tt agent models --all`, then `tt agent configure --provider <provider> --model <model>`."
4906
+ "The laptop-local agent is not configured. Use /model to choose a provider and model, then try again."
4615
4907
  );
4616
4908
  }
4617
4909
  const modelRuntime = await getModelRuntime();
@@ -4620,8 +4912,9 @@ async function createDefaultAgentClient(runtime, env, workspaceRoot, getModelRun
4620
4912
  const secret = getApiKey({ apiKey: env.TUNED_TENSOR_API_KEY });
4621
4913
  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
4914
  return createLocalAgentClient({
4623
- store: runtime.agentStore ?? new LocalAgentStore(join7(getConfigDir(), "agent"), {
4624
- secretValues: [...secret ? [secret] : [], ...providerSecrets]
4915
+ store: runtime.agentStore ?? new LocalAgentStore(getAgentConfigDir(), {
4916
+ secretValues: [...secret ? [secret] : [], ...providerSecrets],
4917
+ secretValueProvider: readStoredProviderSecrets
4625
4918
  }),
4626
4919
  workspaceRoot,
4627
4920
  selection,
@@ -4893,7 +5186,7 @@ async function runCli(version, runtime = {}) {
4893
5186
  }
4894
5187
 
4895
5188
  // src/index.ts
4896
- runCli("0.13.0").catch((err) => {
5189
+ runCli("0.13.1-beta.0").catch((err) => {
4897
5190
  reportError(err);
4898
5191
  process.exit(1);
4899
5192
  });