llm-party-cli 0.7.0 → 0.9.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
@@ -37551,8 +37551,11 @@ var render = async (node, rendererOrConfig = {}) => {
37551
37551
  import { query } from "@anthropic-ai/claude-agent-sdk";
37552
37552
 
37553
37553
  // src/adapters/base.ts
37554
- function formatTranscript(messages) {
37555
- return messages.map((m2) => `[${m2.from}]: ${m2.text}`).join(`
37554
+ function formatTranscript(messages, agentName, humanName) {
37555
+ return messages.map((m2) => {
37556
+ const role = m2.from === agentName ? "you" : m2.from === humanName ? "user" : "agent";
37557
+ return `${m2.from} (${role}):: ${m2.text}`;
37558
+ }).join(`
37556
37559
 
37557
37560
  `);
37558
37561
  }
@@ -37561,13 +37564,15 @@ function formatTranscript(messages) {
37561
37564
  class ClaudeBaseAdapter {
37562
37565
  name;
37563
37566
  model;
37567
+ humanName;
37564
37568
  systemPrompt = "";
37565
37569
  sessionId = "";
37566
37570
  runtimeEnv = {};
37567
37571
  claudeExecutable;
37568
- constructor(name, model) {
37572
+ constructor(name, model, humanName) {
37569
37573
  this.name = name;
37570
37574
  this.model = model;
37575
+ this.humanName = humanName;
37571
37576
  }
37572
37577
  async init(config) {
37573
37578
  this.systemPrompt = config.resolvedPrompt ?? "";
@@ -37575,10 +37580,23 @@ class ClaudeBaseAdapter {
37575
37580
  this.claudeExecutable = config.executablePath ?? process.env.CLAUDE_CODE_EXECUTABLE;
37576
37581
  }
37577
37582
  async buildEnv(config) {
37578
- return { ...process.env, ...config.env ?? {} };
37583
+ const configEnv = config.env ?? {};
37584
+ const mapped = {};
37585
+ if (configEnv.AUTH_URL) {
37586
+ mapped.ANTHROPIC_BASE_URL = configEnv.AUTH_URL;
37587
+ }
37588
+ if (configEnv.AUTH_TOKEN) {
37589
+ mapped.ANTHROPIC_AUTH_TOKEN = configEnv.AUTH_TOKEN;
37590
+ }
37591
+ if (configEnv.AUTH_URL || configEnv.AUTH_TOKEN) {
37592
+ mapped.ANTHROPIC_DEFAULT_HAIKU_MODEL = this.model;
37593
+ mapped.ANTHROPIC_DEFAULT_SONNET_MODEL = this.model;
37594
+ mapped.ANTHROPIC_DEFAULT_OPUS_MODEL = this.model;
37595
+ }
37596
+ return { ...process.env, ...mapped, ...configEnv };
37579
37597
  }
37580
37598
  async send(messages, signal) {
37581
- return await this.querySDK(formatTranscript(messages), signal);
37599
+ return await this.querySDK(formatTranscript(messages, this.name, this.humanName), signal);
37582
37600
  }
37583
37601
  async destroy() {
37584
37602
  return;
@@ -38061,11 +38079,13 @@ class CodexAdapter {
38061
38079
  name;
38062
38080
  provider = "codex";
38063
38081
  model;
38082
+ humanName;
38064
38083
  codex;
38065
38084
  thread;
38066
- constructor(name, model) {
38085
+ constructor(name, model, humanName) {
38067
38086
  this.name = name;
38068
38087
  this.model = model;
38088
+ this.humanName = humanName;
38069
38089
  }
38070
38090
  async init(config) {
38071
38091
  const cliPath = config.executablePath ?? process.env.CODEX_CLI_EXECUTABLE;
@@ -38090,7 +38110,7 @@ class CodexAdapter {
38090
38110
  if (signal?.aborted) {
38091
38111
  return "[Aborted] Codex was cancelled";
38092
38112
  }
38093
- const turn = await this.thread.run(formatTranscript(messages));
38113
+ const turn = await this.thread.run(formatTranscript(messages, this.name, this.humanName));
38094
38114
  if (turn.finalResponse && turn.finalResponse.length > 0) {
38095
38115
  return turn.finalResponse;
38096
38116
  }
@@ -39276,14 +39296,16 @@ class CopilotAdapter {
39276
39296
  name;
39277
39297
  provider = "copilot";
39278
39298
  model;
39299
+ humanName;
39279
39300
  client;
39280
39301
  session;
39281
39302
  systemPrompt = "";
39282
39303
  cliPath;
39283
39304
  timeout = 600000;
39284
- constructor(name, model) {
39305
+ constructor(name, model, humanName) {
39285
39306
  this.name = name;
39286
39307
  this.model = model;
39308
+ this.humanName = humanName;
39287
39309
  }
39288
39310
  async init(config) {
39289
39311
  this.systemPrompt = config.resolvedPrompt ?? "";
@@ -39300,7 +39322,7 @@ class CopilotAdapter {
39300
39322
  if (signal?.aborted) {
39301
39323
  return "[Aborted] Copilot was cancelled";
39302
39324
  }
39303
- const transcript = formatTranscript(messages);
39325
+ const transcript = formatTranscript(messages, this.name, this.humanName);
39304
39326
  try {
39305
39327
  return await this.sendToSession(transcript);
39306
39328
  } catch (err) {
@@ -39354,62 +39376,9 @@ class CopilotAdapter {
39354
39376
  }
39355
39377
  }
39356
39378
 
39357
- // src/adapters/glm.ts
39358
- import { spawn as spawn3 } from "child_process";
39359
- class GlmAdapter extends ClaudeBaseAdapter {
39360
- provider = "glm";
39361
- async buildEnv(config) {
39362
- const aliasEnv = await loadGlmAliasEnv();
39363
- return { ...process.env, ...aliasEnv, ...config.env ?? {} };
39364
- }
39365
- }
39366
- function detectShell() {
39367
- if (process.env.SHELL) {
39368
- return process.env.SHELL;
39369
- }
39370
- return "/bin/sh";
39371
- }
39372
- async function loadGlmAliasEnv() {
39373
- const shell = detectShell();
39374
- const isInteractive = shell.endsWith("zsh") || shell.endsWith("bash");
39375
- const args = isInteractive ? ["-ic", "alias glm"] : ["-c", "alias glm"];
39376
- return new Promise((resolve4) => {
39377
- const child = spawn3(shell, args, {
39378
- cwd: process.cwd(),
39379
- env: process.env,
39380
- stdio: ["ignore", "pipe", "pipe"]
39381
- });
39382
- let stdout = "";
39383
- const timeout = setTimeout(() => {
39384
- child.kill("SIGTERM");
39385
- resolve4({});
39386
- }, 5000);
39387
- child.stdout.on("data", (chunk) => {
39388
- stdout += String(chunk);
39389
- });
39390
- child.on("close", (code) => {
39391
- clearTimeout(timeout);
39392
- if (code !== 0) {
39393
- resolve4({});
39394
- return;
39395
- }
39396
- const env2 = {};
39397
- const tokens = stdout.match(/[A-Z_]+="[^"]*"/g) ?? [];
39398
- for (const token of tokens) {
39399
- const eqIdx = token.indexOf("=");
39400
- if (eqIdx === -1)
39401
- continue;
39402
- const key = token.slice(0, eqIdx);
39403
- const raw = token.slice(eqIdx + 1);
39404
- env2[key] = raw.replace(/^"|"$/g, "");
39405
- }
39406
- resolve4(env2);
39407
- });
39408
- child.on("error", () => {
39409
- clearTimeout(timeout);
39410
- resolve4({});
39411
- });
39412
- });
39379
+ // src/adapters/custom.ts
39380
+ class CustomAdapter extends ClaudeBaseAdapter {
39381
+ provider = "custom";
39413
39382
  }
39414
39383
 
39415
39384
  // src/config/loader.ts
@@ -39448,27 +39417,11 @@ var PROVIDERS = [
39448
39417
  defaultTag: "copilot",
39449
39418
  detectCommand: "copilot",
39450
39419
  detectType: "binary"
39451
- },
39452
- {
39453
- id: "glm",
39454
- displayName: "GLM",
39455
- description: "glm alias configured on Claude Code CLI",
39456
- unavailableHint: "glm shell alias not configured",
39457
- defaultModel: "glm-5",
39458
- defaultTag: "glm",
39459
- detectCommand: "glm",
39460
- detectType: "alias",
39461
- env: {
39462
- ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
39463
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
39464
- ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-4.5",
39465
- ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5"
39466
- }
39467
39420
  }
39468
39421
  ];
39469
39422
 
39470
39423
  // src/config/loader.ts
39471
- var VALID_PROVIDER_IDS = new Set(PROVIDERS.map((p) => p.id));
39424
+ var VALID_PROVIDER_IDS = new Set([...PROVIDERS.map((p) => p.id), "custom"]);
39472
39425
  var LLM_PARTY_HOME = path8.join(homedir(), ".llm-party");
39473
39426
  var MIND_MAP_INDEX = `# Living Memory Neural Network
39474
39427
 
@@ -39654,9 +39607,23 @@ async function configExists() {
39654
39607
  return false;
39655
39608
  }
39656
39609
  }
39610
+ function migrateProviders(data) {
39611
+ if (!Array.isArray(data.agents))
39612
+ return;
39613
+ for (const agent of data.agents) {
39614
+ if (!agent || typeof agent !== "object")
39615
+ continue;
39616
+ if (typeof agent.provider === "string" && !VALID_PROVIDER_IDS.has(agent.provider)) {
39617
+ agent.provider = "custom";
39618
+ if (!agent.cli)
39619
+ agent.cli = "claude";
39620
+ }
39621
+ }
39622
+ }
39657
39623
  async function loadConfig2(configPath) {
39658
39624
  const raw = await readFile3(configPath, "utf8");
39659
39625
  const parsed = JSON.parse(raw);
39626
+ migrateProviders(parsed);
39660
39627
  validateConfig(parsed);
39661
39628
  return normalizeConfig(parsed);
39662
39629
  }
@@ -39861,14 +39828,7 @@ class Orchestrator {
39861
39828
  return this.agentTimeouts.get(agentName) ?? this.defaultTimeout;
39862
39829
  }
39863
39830
  buildInputForAgent(agentName, unseen) {
39864
- const recent = this.conversation.slice(-this.contextWindowSize);
39865
- const merged = [...recent, ...unseen];
39866
- const dedupById = new Map;
39867
- for (const msg of merged) {
39868
- dedupById.set(msg.id, msg);
39869
- }
39870
- const ordered = Array.from(dedupById.values()).sort((a, b2) => a.id - b2.id);
39871
- const filtered = ordered.filter((msg) => msg.from.toUpperCase() !== agentName.toUpperCase());
39831
+ const filtered = unseen.filter((msg) => msg.from.toUpperCase() !== agentName.toUpperCase());
39872
39832
  if (this.reminderInterval <= 0 || filtered.length < this.reminderInterval) {
39873
39833
  return filtered;
39874
39834
  }
@@ -39899,7 +39859,7 @@ function createSessionId() {
39899
39859
  }
39900
39860
 
39901
39861
  // src/ui/App.tsx
39902
- import { spawn as spawn5 } from "child_process";
39862
+ import { spawn as spawn4 } from "child_process";
39903
39863
 
39904
39864
  // src/ui/useOrchestrator.ts
39905
39865
  import { execFile } from "child_process";
@@ -40321,7 +40281,7 @@ function InputLine(props) {
40321
40281
  useKeyboard((key) => {
40322
40282
  if (props.disabled)
40323
40283
  return;
40324
- if (key.shift && (key.name === "enter" || key.name === "return")) {
40284
+ if ((key.shift || key.option || key.meta) && (key.name === "enter" || key.name === "return")) {
40325
40285
  update(value.slice(0, cursor) + `
40326
40286
  ` + value.slice(cursor), cursor + 1);
40327
40287
  return;
@@ -40536,15 +40496,15 @@ function InputLine(props) {
40536
40496
  }
40537
40497
 
40538
40498
  // src/ui/ConfigWizard.tsx
40539
- import { userInfo as userInfo3 } from "os";
40499
+ import { userInfo as userInfo2 } from "os";
40540
40500
 
40541
40501
  // src/config/detector.ts
40542
- import { spawn as spawn4 } from "child_process";
40502
+ import { spawn as spawn3 } from "child_process";
40543
40503
  var DETECT_TIMEOUT = 5000;
40544
40504
  function detectBinary(command) {
40545
40505
  return new Promise((resolve4) => {
40546
40506
  const timer = setTimeout(() => resolve4({ available: false }), DETECT_TIMEOUT);
40547
- const proc = spawn4(command, ["--version"], {
40507
+ const proc = spawn3(command, ["--version"], {
40548
40508
  stdio: ["ignore", "pipe", "ignore"],
40549
40509
  shell: true,
40550
40510
  timeout: DETECT_TIMEOUT,
@@ -40569,33 +40529,13 @@ function detectBinary(command) {
40569
40529
  });
40570
40530
  });
40571
40531
  }
40572
- function detectAlias(command) {
40573
- return new Promise((resolve4) => {
40574
- const shell = process.env.SHELL || "/bin/bash";
40575
- const timer = setTimeout(() => resolve4({ available: false }), DETECT_TIMEOUT);
40576
- const proc = spawn4(shell, ["-ic", `type ${command}`], {
40577
- stdio: ["ignore", "pipe", "ignore"],
40578
- timeout: DETECT_TIMEOUT,
40579
- detached: true
40580
- });
40581
- proc.unref();
40582
- proc.on("close", (code) => {
40583
- clearTimeout(timer);
40584
- resolve4({ available: code === 0 });
40585
- });
40586
- proc.on("error", () => {
40587
- clearTimeout(timer);
40588
- resolve4({ available: false });
40589
- });
40590
- });
40591
- }
40592
40532
  async function detectProviders() {
40593
40533
  const results = await Promise.allSettled(PROVIDERS.map(async (provider) => {
40594
- const result = provider.detectType === "alias" ? await detectAlias(provider.detectCommand) : await detectBinary(provider.detectCommand);
40534
+ const result = await detectBinary(provider.detectCommand);
40595
40535
  return {
40596
40536
  id: provider.id,
40597
40537
  available: result.available,
40598
- version: "version" in result ? result.version : undefined
40538
+ version: result.version
40599
40539
  };
40600
40540
  }));
40601
40541
  return results.map((result, i) => {
@@ -40607,57 +40547,8 @@ async function detectProviders() {
40607
40547
 
40608
40548
  // src/config/writer.ts
40609
40549
  import { writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
40610
- import { userInfo as userInfo2 } from "os";
40611
40550
  import path10 from "path";
40612
- async function writeWizardConfig(selectedIds, overrides, existingConfig) {
40613
- const overrideMap = new Map((overrides || []).map((o) => [o.id, o]));
40614
- const existingByProvider = new Map;
40615
- if (existingConfig?.agents) {
40616
- for (const agent of existingConfig.agents) {
40617
- existingByProvider.set(agent.provider, agent);
40618
- }
40619
- }
40620
- const agents = selectedIds.map((id) => {
40621
- const def = PROVIDERS.find((p) => p.id === id);
40622
- if (!def)
40623
- throw new Error(`Unknown provider: ${id}`);
40624
- const override = overrideMap.get(id);
40625
- const existing = existingByProvider.get(id);
40626
- const agent = {
40627
- name: override?.name || def.displayName,
40628
- tag: override?.tag || def.defaultTag,
40629
- provider: def.id,
40630
- model: override?.model || def.defaultModel
40631
- };
40632
- if (existing?.env) {
40633
- agent.env = { ...existing.env };
40634
- } else if (def.env) {
40635
- agent.env = { ...def.env };
40636
- }
40637
- if (existing?.prompts)
40638
- agent.prompts = existing.prompts;
40639
- if (existing?.preloadSkills)
40640
- agent.preloadSkills = existing.preloadSkills;
40641
- if (existing?.executablePath)
40642
- agent.executablePath = existing.executablePath;
40643
- if (existing?.timeout)
40644
- agent.timeout = existing.timeout;
40645
- return agent;
40646
- });
40647
- const config = {
40648
- humanName: existingConfig?.humanName || userInfo2().username || "USER",
40649
- humanTag: existingConfig?.humanTag,
40650
- maxAutoHops: existingConfig?.maxAutoHops ?? 15,
40651
- timeout: existingConfig?.timeout,
40652
- reminderInterval: existingConfig?.reminderInterval,
40653
- agents
40654
- };
40655
- if (!config.humanTag)
40656
- delete config.humanTag;
40657
- if (config.timeout === undefined)
40658
- delete config.timeout;
40659
- if (config.reminderInterval === undefined)
40660
- delete config.reminderInterval;
40551
+ async function writeConfig(config) {
40661
40552
  await mkdir6(LLM_PARTY_HOME, { recursive: true });
40662
40553
  const configPath = path10.join(LLM_PARTY_HOME, "config.json");
40663
40554
  await writeFile6(configPath, JSON.stringify(config, null, 2) + `
@@ -40670,16 +40561,114 @@ function MultiSelect(props) {
40670
40561
  const [focused, setFocused] = createSignal(props.items.findIndex((item) => !item.disabled));
40671
40562
  const [selected, setSelected] = createSignal(new Set(props.initialSelected || []));
40672
40563
  const [error, setError] = createSignal("");
40564
+ const [addingCustom, setAddingCustom] = createSignal(false);
40565
+ const [customName, setCustomName] = createSignal("");
40566
+ const [customCursor, setCustomCursor] = createSignal(0);
40567
+ const tuiRenderer = useRenderer();
40568
+ createEffect(() => {
40569
+ const handlePaste = (event) => {
40570
+ if (!addingCustom())
40571
+ return;
40572
+ const text = new TextDecoder().decode(event.bytes);
40573
+ if (!text)
40574
+ return;
40575
+ const cleaned = text.replace(/\n/g, "");
40576
+ if (!cleaned)
40577
+ return;
40578
+ const c = customCursor();
40579
+ const val = customName();
40580
+ setCustomName(val.slice(0, c) + cleaned + val.slice(c));
40581
+ setCustomCursor(c + cleaned.length);
40582
+ };
40583
+ tuiRenderer.keyInput.on("paste", handlePaste);
40584
+ onCleanup(() => {
40585
+ tuiRenderer.keyInput.off("paste", handlePaste);
40586
+ });
40587
+ });
40588
+ const totalRows = () => props.items.length + (props.addCustom ? 1 : 0);
40589
+ const isAddCustomRow = (idx) => props.addCustom && idx === props.items.length;
40673
40590
  const findNextEnabled = (from, direction) => {
40591
+ const total = totalRows();
40674
40592
  let idx = from;
40675
- for (let i = 0;i < props.items.length; i++) {
40676
- idx = (idx + direction + props.items.length) % props.items.length;
40593
+ for (let i = 0;i < total; i++) {
40594
+ idx = (idx + direction + total) % total;
40595
+ if (isAddCustomRow(idx))
40596
+ return idx;
40677
40597
  if (!props.items[idx].disabled)
40678
40598
  return idx;
40679
40599
  }
40680
40600
  return from;
40681
40601
  };
40682
40602
  useKeyboard((key) => {
40603
+ if (addingCustom()) {
40604
+ if (key.name === "escape") {
40605
+ setAddingCustom(false);
40606
+ setCustomName("");
40607
+ setCustomCursor(0);
40608
+ return;
40609
+ }
40610
+ if (key.name === "enter" || key.name === "return") {
40611
+ const name = customName().trim();
40612
+ if (name.length > 0) {
40613
+ props.addCustom.onAdd(name);
40614
+ setAddingCustom(false);
40615
+ setCustomName("");
40616
+ setCustomCursor(0);
40617
+ }
40618
+ return;
40619
+ }
40620
+ if (key.name === "backspace") {
40621
+ const c = customCursor();
40622
+ if (c > 0) {
40623
+ const val = customName();
40624
+ setCustomName(val.slice(0, c - 1) + val.slice(c));
40625
+ setCustomCursor(c - 1);
40626
+ }
40627
+ return;
40628
+ }
40629
+ if (key.name === "delete") {
40630
+ const c = customCursor();
40631
+ const val = customName();
40632
+ if (c < val.length) {
40633
+ setCustomName(val.slice(0, c) + val.slice(c + 1));
40634
+ }
40635
+ return;
40636
+ }
40637
+ if (key.name === "left") {
40638
+ setCustomCursor((c) => Math.max(0, c - 1));
40639
+ return;
40640
+ }
40641
+ if (key.name === "right") {
40642
+ setCustomCursor((c) => Math.min(customName().length, c + 1));
40643
+ return;
40644
+ }
40645
+ if (key.name === "home" || key.ctrl && key.name === "a") {
40646
+ setCustomCursor(0);
40647
+ return;
40648
+ }
40649
+ if (key.name === "end" || key.ctrl && key.name === "e") {
40650
+ setCustomCursor(customName().length);
40651
+ return;
40652
+ }
40653
+ if (key.ctrl && key.name === "u") {
40654
+ setCustomName("");
40655
+ setCustomCursor(0);
40656
+ return;
40657
+ }
40658
+ if (key.ctrl || key.name === "up" || key.name === "down" || key.name === "pageup" || key.name === "pagedown" || key.name === "tab") {
40659
+ return;
40660
+ }
40661
+ const ch = key.sequence;
40662
+ if (ch && ch.length > 0 && !ch.startsWith("\x1B")) {
40663
+ if (ch === "'" || ch === '"' || ch === "`")
40664
+ return;
40665
+ const c = customCursor();
40666
+ const val = customName();
40667
+ setCustomName(val.slice(0, c) + ch + val.slice(c));
40668
+ setCustomCursor(c + ch.length);
40669
+ }
40670
+ return;
40671
+ }
40683
40672
  if (key.name === "up" || key.name === "k") {
40684
40673
  setFocused((f) => findNextEnabled(f, -1));
40685
40674
  setError("");
@@ -40691,13 +40680,20 @@ function MultiSelect(props) {
40691
40680
  return;
40692
40681
  }
40693
40682
  if (key.name === "space" || key.sequence === " ") {
40694
- if (focused() >= 0 && !props.items[focused()].disabled) {
40683
+ const f = focused();
40684
+ if (isAddCustomRow(f)) {
40685
+ setAddingCustom(true);
40686
+ setCustomName("");
40687
+ setCustomCursor(0);
40688
+ return;
40689
+ }
40690
+ if (f >= 0 && !props.items[f].disabled) {
40695
40691
  setSelected((prev) => {
40696
40692
  const next = new Set(prev);
40697
- if (next.has(focused())) {
40698
- next.delete(focused());
40693
+ if (next.has(f)) {
40694
+ next.delete(f);
40699
40695
  } else {
40700
- next.add(focused());
40696
+ next.add(f);
40701
40697
  }
40702
40698
  return next;
40703
40699
  });
@@ -40706,6 +40702,13 @@ function MultiSelect(props) {
40706
40702
  return;
40707
40703
  }
40708
40704
  if (key.name === "enter" || key.name === "return") {
40705
+ const f = focused();
40706
+ if (isAddCustomRow(f)) {
40707
+ setAddingCustom(true);
40708
+ setCustomName("");
40709
+ setCustomCursor(0);
40710
+ return;
40711
+ }
40709
40712
  if (selected().size === 0) {
40710
40713
  setError("Select at least one agent");
40711
40714
  return;
@@ -40727,7 +40730,7 @@ function MultiSelect(props) {
40727
40730
  },
40728
40731
  children: (item, i) => {
40729
40732
  const isSelected = () => selected().has(i());
40730
- const isFocused = () => i() === focused();
40733
+ const isFocused = () => i() === focused() && !addingCustom();
40731
40734
  const isDisabled = () => !!item.disabled;
40732
40735
  const bullet = () => isSelected() ? "\u25CF" : "\u25CB";
40733
40736
  const bulletColor = () => isDisabled() ? COLORS.textFaint : isSelected() ? COLORS.success : COLORS.textSecondary;
@@ -40735,29 +40738,29 @@ function MultiSelect(props) {
40735
40738
  const descColor = () => isDisabled() ? COLORS.textFaint : COLORS.textMuted;
40736
40739
  const bgColor = () => isFocused() && !isDisabled() ? COLORS.bgFocus : undefined;
40737
40740
  return (() => {
40738
- var _el$4 = createElement("text"), _el$5 = createElement("span"), _el$6 = createTextNode(` `), _el$7 = createTextNode(` `), _el$8 = createElement("span"), _el$9 = createElement("span"), _el$0 = createTextNode(` `);
40739
- insertNode(_el$4, _el$5);
40740
- insertNode(_el$4, _el$8);
40741
- insertNode(_el$4, _el$9);
40742
- setProp(_el$4, "selectable", false);
40743
- insertNode(_el$5, _el$6);
40744
- insertNode(_el$5, _el$7);
40745
- insert(_el$5, bullet, _el$7);
40746
- insert(_el$8, () => item.label);
40747
- insertNode(_el$9, _el$0);
40748
- insert(_el$9, () => item.description, null);
40741
+ var _el$0 = createElement("text"), _el$1 = createElement("span"), _el$10 = createTextNode(` `), _el$11 = createTextNode(` `), _el$12 = createElement("span"), _el$13 = createElement("span"), _el$14 = createTextNode(` `);
40742
+ insertNode(_el$0, _el$1);
40743
+ insertNode(_el$0, _el$12);
40744
+ insertNode(_el$0, _el$13);
40745
+ setProp(_el$0, "selectable", false);
40746
+ insertNode(_el$1, _el$10);
40747
+ insertNode(_el$1, _el$11);
40748
+ insert(_el$1, bullet, _el$11);
40749
+ insert(_el$12, () => item.label);
40750
+ insertNode(_el$13, _el$14);
40751
+ insert(_el$13, () => item.description, null);
40749
40752
  effect((_p$) => {
40750
- var _v$ = bgColor(), _v$2 = {
40753
+ var _v$4 = bgColor(), _v$5 = {
40751
40754
  fg: bulletColor()
40752
- }, _v$3 = {
40755
+ }, _v$6 = {
40753
40756
  fg: labelColor()
40754
- }, _v$4 = {
40757
+ }, _v$7 = {
40755
40758
  fg: descColor()
40756
40759
  };
40757
- _v$ !== _p$.e && (_p$.e = setProp(_el$4, "bg", _v$, _p$.e));
40758
- _v$2 !== _p$.t && (_p$.t = setProp(_el$5, "style", _v$2, _p$.t));
40759
- _v$3 !== _p$.a && (_p$.a = setProp(_el$8, "style", _v$3, _p$.a));
40760
- _v$4 !== _p$.o && (_p$.o = setProp(_el$9, "style", _v$4, _p$.o));
40760
+ _v$4 !== _p$.e && (_p$.e = setProp(_el$0, "bg", _v$4, _p$.e));
40761
+ _v$5 !== _p$.t && (_p$.t = setProp(_el$1, "style", _v$5, _p$.t));
40762
+ _v$6 !== _p$.a && (_p$.a = setProp(_el$12, "style", _v$6, _p$.a));
40763
+ _v$7 !== _p$.o && (_p$.o = setProp(_el$13, "style", _v$7, _p$.o));
40761
40764
  return _p$;
40762
40765
  }, {
40763
40766
  e: undefined,
@@ -40765,21 +40768,96 @@ function MultiSelect(props) {
40765
40768
  a: undefined,
40766
40769
  o: undefined
40767
40770
  });
40768
- return _el$4;
40771
+ return _el$0;
40769
40772
  })();
40770
40773
  }
40771
40774
  }), null);
40775
+ insert(_el$, createComponent2(Show, {
40776
+ get when() {
40777
+ return props.addCustom;
40778
+ },
40779
+ get children() {
40780
+ return createComponent2(Show, {
40781
+ get when() {
40782
+ return addingCustom();
40783
+ },
40784
+ get fallback() {
40785
+ return (() => {
40786
+ var _el$15 = createElement("text"), _el$16 = createElement("span"), _el$18 = createElement("span");
40787
+ insertNode(_el$15, _el$16);
40788
+ insertNode(_el$15, _el$18);
40789
+ setProp(_el$15, "selectable", false);
40790
+ insertNode(_el$16, createTextNode(` + `));
40791
+ insertNode(_el$18, createTextNode(`Add Custom...`));
40792
+ effect((_p$) => {
40793
+ var _v$8 = focused() === props.items.length && !addingCustom() ? COLORS.bgFocus : undefined, _v$9 = {
40794
+ fg: COLORS.primary
40795
+ }, _v$0 = {
40796
+ fg: COLORS.textSecondary
40797
+ };
40798
+ _v$8 !== _p$.e && (_p$.e = setProp(_el$15, "bg", _v$8, _p$.e));
40799
+ _v$9 !== _p$.t && (_p$.t = setProp(_el$16, "style", _v$9, _p$.t));
40800
+ _v$0 !== _p$.a && (_p$.a = setProp(_el$18, "style", _v$0, _p$.a));
40801
+ return _p$;
40802
+ }, {
40803
+ e: undefined,
40804
+ t: undefined,
40805
+ a: undefined
40806
+ });
40807
+ return _el$15;
40808
+ })();
40809
+ },
40810
+ get children() {
40811
+ var _el$2 = createElement("text"), _el$3 = createElement("span"), _el$5 = createElement("span"), _el$7 = createElement("span");
40812
+ insertNode(_el$2, _el$3);
40813
+ insertNode(_el$2, _el$5);
40814
+ insertNode(_el$2, _el$7);
40815
+ setProp(_el$2, "selectable", false);
40816
+ insertNode(_el$3, createTextNode(` + `));
40817
+ insertNode(_el$5, createTextNode(`Name: `));
40818
+ insert(_el$2, () => customName().slice(0, customCursor()), _el$7);
40819
+ insert(_el$7, (() => {
40820
+ var _c$ = memo2(() => customCursor() < customName().length);
40821
+ return () => _c$() ? customName()[customCursor()] : " ";
40822
+ })());
40823
+ insert(_el$2, (() => {
40824
+ var _c$2 = memo2(() => customCursor() < customName().length);
40825
+ return () => _c$2() ? customName().slice(customCursor() + 1) : "";
40826
+ })(), null);
40827
+ effect((_p$) => {
40828
+ var _v$ = {
40829
+ fg: COLORS.primary
40830
+ }, _v$2 = {
40831
+ fg: COLORS.textSecondary
40832
+ }, _v$3 = {
40833
+ bg: COLORS.textPrimary,
40834
+ fg: "#000000"
40835
+ };
40836
+ _v$ !== _p$.e && (_p$.e = setProp(_el$3, "style", _v$, _p$.e));
40837
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$5, "style", _v$2, _p$.t));
40838
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$7, "style", _v$3, _p$.a));
40839
+ return _p$;
40840
+ }, {
40841
+ e: undefined,
40842
+ t: undefined,
40843
+ a: undefined
40844
+ });
40845
+ return _el$2;
40846
+ }
40847
+ });
40848
+ }
40849
+ }), null);
40772
40850
  insert(_el$, createComponent2(Show, {
40773
40851
  get when() {
40774
40852
  return error();
40775
40853
  },
40776
40854
  get children() {
40777
- var _el$2 = createElement("text"), _el$3 = createTextNode(` `);
40778
- insertNode(_el$2, _el$3);
40779
- setProp(_el$2, "marginTop", 1);
40780
- insert(_el$2, error, null);
40781
- effect((_$p) => setProp(_el$2, "fg", COLORS.error, _$p));
40782
- return _el$2;
40855
+ var _el$8 = createElement("text"), _el$9 = createTextNode(` `);
40856
+ insertNode(_el$8, _el$9);
40857
+ setProp(_el$8, "marginTop", 1);
40858
+ insert(_el$8, error, null);
40859
+ effect((_$p) => setProp(_el$8, "fg", COLORS.error, _$p));
40860
+ return _el$8;
40783
40861
  }
40784
40862
  }), null);
40785
40863
  return _el$;
@@ -40800,7 +40878,6 @@ var SWEEP_CHARS = ["\u2591", "\u2592", "\u2593", "\u2588", "\u2593", "\u2592", "
40800
40878
  var BAR_WIDTH = 6;
40801
40879
  function SweepBar(props) {
40802
40880
  const glow = SWEEP_CHARS.length;
40803
- const totalWidth = BAR_WIDTH * 2 + props.title.length + 2;
40804
40881
  const [pos, setPos] = createSignal(0);
40805
40882
  createEffect(() => {
40806
40883
  const interval = setInterval(() => setPos((p) => (p + 1) % (BAR_WIDTH + glow)), 50);
@@ -40876,22 +40953,105 @@ function useDiscoColor() {
40876
40953
  });
40877
40954
  return () => PARTY_COLORS[idx()];
40878
40955
  }
40956
+ function personaToEntry(agent) {
40957
+ const env2 = agent.env ?? {};
40958
+ return {
40959
+ name: agent.name,
40960
+ tag: agent.tag,
40961
+ model: agent.model,
40962
+ provider: agent.provider,
40963
+ active: agent.active !== false,
40964
+ authUrl: env2.AUTH_URL ?? "",
40965
+ authToken: env2.AUTH_TOKEN ?? "",
40966
+ cli: agent.cli ?? "claude",
40967
+ prompts: agent.prompts,
40968
+ preloadSkills: agent.preloadSkills,
40969
+ executablePath: agent.executablePath,
40970
+ timeout: agent.timeout,
40971
+ extraEnv: extractExtraEnv(env2)
40972
+ };
40973
+ }
40974
+ function extractExtraEnv(env2) {
40975
+ const extra = {};
40976
+ for (const [k2, v2] of Object.entries(env2)) {
40977
+ if (k2 !== "AUTH_URL" && k2 !== "AUTH_TOKEN") {
40978
+ extra[k2] = v2;
40979
+ }
40980
+ }
40981
+ return Object.keys(extra).length > 0 ? extra : undefined;
40982
+ }
40983
+ function entryToPersona(entry) {
40984
+ const config = {
40985
+ name: entry.name.trim(),
40986
+ tag: entry.tag.trim(),
40987
+ provider: entry.provider,
40988
+ model: entry.model.trim(),
40989
+ active: entry.active
40990
+ };
40991
+ if (entry.provider === "custom") {
40992
+ config.cli = entry.cli || "claude";
40993
+ const env2 = {};
40994
+ if (entry.authUrl)
40995
+ env2.AUTH_URL = entry.authUrl;
40996
+ if (entry.authToken)
40997
+ env2.AUTH_TOKEN = entry.authToken;
40998
+ if (entry.extraEnv)
40999
+ Object.assign(env2, entry.extraEnv);
41000
+ if (Object.keys(env2).length > 0)
41001
+ config.env = env2;
41002
+ }
41003
+ if (entry.prompts)
41004
+ config.prompts = entry.prompts;
41005
+ if (entry.preloadSkills)
41006
+ config.preloadSkills = entry.preloadSkills;
41007
+ if (entry.executablePath)
41008
+ config.executablePath = entry.executablePath;
41009
+ if (entry.timeout)
41010
+ config.timeout = entry.timeout;
41011
+ return config;
41012
+ }
40879
41013
  function ConfigWizard(props) {
40880
41014
  const [step, setStep] = createSignal("detect");
40881
41015
  const [detection, setDetection] = createSignal([]);
40882
- const [selectedIds, setSelectedIds] = createSignal([]);
40883
- const [, setAgentConfigs] = createSignal([]);
40884
41016
  const [activeTab, setActiveTab] = createSignal(0);
40885
41017
  const [focusedField, setFocusedField] = createSignal(0);
40886
41018
  const [error, setError] = createSignal("");
40887
41019
  const [tick, setTick] = createSignal(0);
40888
- let inputRefsData = [];
40889
- let humanData = {
40890
- name: props.existingConfig?.humanName || userInfo3().username || "USER",
40891
- tag: props.existingConfig?.humanTag || toTag(props.existingConfig?.humanName || userInfo3().username || "USER")
41020
+ const [customItems, setCustomItems] = createSignal([]);
41021
+ let agentEntries = [];
41022
+ let settingsData = {
41023
+ name: props.existingConfig?.humanName || userInfo2().username || "USER",
41024
+ tag: props.existingConfig?.humanTag || toTag(props.existingConfig?.humanName || userInfo2().username || "USER"),
41025
+ maxHops: String(props.existingConfig?.maxAutoHops ?? 15),
41026
+ timeout: String(props.existingConfig?.timeout ?? 600)
40892
41027
  };
40893
41028
  let cursorPos = 0;
40894
41029
  const spinner = useSpinner();
41030
+ const tuiRenderer = useRenderer();
41031
+ createEffect(() => {
41032
+ const handlePaste = (event) => {
41033
+ if (step() !== "configure")
41034
+ return;
41035
+ const text = new TextDecoder().decode(event.bytes);
41036
+ if (!text)
41037
+ return;
41038
+ const cleaned = text.replace(/\n/g, "");
41039
+ if (!cleaned)
41040
+ return;
41041
+ const fieldValue = getCurrentFieldValue();
41042
+ const cursor = cursorPos;
41043
+ updateField(fieldValue.slice(0, cursor) + cleaned + fieldValue.slice(cursor), cursor + cleaned.length);
41044
+ };
41045
+ tuiRenderer.keyInput.on("paste", handlePaste);
41046
+ onCleanup(() => {
41047
+ tuiRenderer.keyInput.off("paste", handlePaste);
41048
+ });
41049
+ });
41050
+ createEffect(() => {
41051
+ const existing = props.existingConfig?.agents ?? [];
41052
+ const customs = existing.filter((a) => a.provider === "custom").map(personaToEntry);
41053
+ setCustomItems(customs);
41054
+ });
40895
41055
  createEffect(() => {
40896
41056
  detectProviders().then((results) => {
40897
41057
  setDetection(results);
@@ -40901,103 +41061,255 @@ function ConfigWizard(props) {
40901
41061
  setStep("providers");
40902
41062
  });
40903
41063
  });
40904
- const existingByProvider = () => new Map((props.existingConfig?.agents || []).map((a) => [a.provider, a]));
40905
- const initialSelected = () => props.existingConfig ? PROVIDERS.map((p, i) => existingByProvider().has(p.id) ? i : -1).filter((i) => i >= 0) : undefined;
40906
- const multiSelectItems = () => PROVIDERS.map((provider) => {
40907
- const result = detection().find((d2) => d2.id === provider.id);
40908
- const available = result?.available ?? false;
40909
- return {
40910
- label: provider.displayName,
40911
- description: available ? provider.description : provider.unavailableHint,
40912
- disabled: !available
40913
- };
40914
- });
40915
- const handleProviderConfirm = (selectedIndices) => {
40916
- const ids = selectedIndices.map((i) => PROVIDERS[i].id);
40917
- setSelectedIds(ids);
40918
- const configs = ids.map((id) => {
40919
- const def = PROVIDERS.find((p) => p.id === id);
40920
- const existing = existingByProvider().get(id);
41064
+ const existingNative = () => {
41065
+ const map = new Map;
41066
+ for (const a of props.existingConfig?.agents ?? []) {
41067
+ if (a.provider !== "custom")
41068
+ map.set(a.provider, a);
41069
+ }
41070
+ return map;
41071
+ };
41072
+ const multiSelectItems = () => {
41073
+ const nativeItems = PROVIDERS.map((provider) => {
41074
+ const result = detection().find((d2) => d2.id === provider.id);
41075
+ const available = result?.available ?? false;
40921
41076
  return {
40922
- id: def.id,
40923
- name: existing?.name || def.displayName,
40924
- tag: existing?.tag || def.defaultTag,
40925
- model: existing?.model || def.defaultModel
41077
+ label: provider.displayName,
41078
+ description: available ? provider.description : provider.unavailableHint,
41079
+ disabled: !available
40926
41080
  };
40927
41081
  });
40928
- setAgentConfigs(configs);
40929
- inputRefsData = configs.map((c) => ({
40930
- ...c
41082
+ const customs = customItems();
41083
+ if (customs.length > 0) {
41084
+ nativeItems.push({
41085
+ label: "\u2500\u2500 Custom \u2500\u2500",
41086
+ description: "",
41087
+ disabled: true
41088
+ });
41089
+ }
41090
+ const customEntries = customs.map((c) => ({
41091
+ label: c.name,
41092
+ description: "custom provider"
40931
41093
  }));
41094
+ return [...nativeItems, ...customEntries];
41095
+ };
41096
+ const initialSelected = () => {
41097
+ if (!props.existingConfig)
41098
+ return;
41099
+ const indices = [];
41100
+ const nativeMap = existingNative();
41101
+ PROVIDERS.forEach((p, i) => {
41102
+ const existing = nativeMap.get(p.id);
41103
+ if (existing && existing.active !== false)
41104
+ indices.push(i);
41105
+ });
41106
+ const customs = customItems();
41107
+ if (customs.length > 0) {
41108
+ const offset = PROVIDERS.length + 1;
41109
+ customs.forEach((c, i) => {
41110
+ if (c.active)
41111
+ indices.push(offset + i);
41112
+ });
41113
+ }
41114
+ return indices.length > 0 ? indices : undefined;
41115
+ };
41116
+ const handleAddCustom = (name) => {
41117
+ const entry = {
41118
+ name,
41119
+ tag: toTag(name),
41120
+ model: "",
41121
+ provider: "custom",
41122
+ active: true,
41123
+ authUrl: "",
41124
+ authToken: "",
41125
+ cli: "claude"
41126
+ };
41127
+ setCustomItems((prev) => [...prev, entry]);
41128
+ };
41129
+ const handleProviderConfirm = (selectedIndices) => {
41130
+ const nativeMap = existingNative();
41131
+ const customs = customItems();
41132
+ const hasCustoms = customs.length > 0;
41133
+ const customOffset = PROVIDERS.length + (hasCustoms ? 1 : 0);
41134
+ const entries = [];
41135
+ for (let i = 0;i < PROVIDERS.length; i++) {
41136
+ const def = PROVIDERS[i];
41137
+ const isSelected = selectedIndices.includes(i);
41138
+ const existing = nativeMap.get(def.id);
41139
+ if (isSelected || existing) {
41140
+ entries.push({
41141
+ name: existing?.name || def.displayName,
41142
+ tag: existing?.tag || def.defaultTag,
41143
+ model: existing?.model || def.defaultModel,
41144
+ provider: def.id,
41145
+ active: isSelected,
41146
+ authUrl: "",
41147
+ authToken: "",
41148
+ cli: "claude",
41149
+ prompts: existing?.prompts,
41150
+ preloadSkills: existing?.preloadSkills,
41151
+ executablePath: existing?.executablePath,
41152
+ timeout: existing?.timeout
41153
+ });
41154
+ }
41155
+ }
41156
+ for (let i = 0;i < customs.length; i++) {
41157
+ const isSelected = selectedIndices.includes(customOffset + i);
41158
+ entries.push({
41159
+ ...customs[i],
41160
+ active: isSelected
41161
+ });
41162
+ }
41163
+ agentEntries = entries;
40932
41164
  setActiveTab(0);
40933
41165
  setFocusedField(0);
40934
- cursorPos = configs[0]?.name.length || 0;
41166
+ cursorPos = settingsData.name.length;
40935
41167
  setStep("configure");
40936
41168
  };
40937
41169
  const handleProviderCancel = () => {
40938
41170
  if (props.onCancel)
40939
41171
  props.onCancel();
40940
41172
  };
40941
- const isYouTab = () => activeTab() === 0;
41173
+ const activeEntries = () => agentEntries.filter((e) => e.active);
41174
+ const isSettingsTab = () => activeTab() === 0;
40942
41175
  const agentTabIndex = () => activeTab() - 1;
40943
- const totalTabs = () => (inputRefsData?.length || 0) + 1;
40944
- const maxFieldIndex = () => isYouTab() ? 1 : 2;
41176
+ const totalTabs = () => activeEntries().length + 1;
41177
+ const isCustomAgent = () => {
41178
+ if (isSettingsTab())
41179
+ return false;
41180
+ const entry = activeEntries()[agentTabIndex()];
41181
+ return entry?.provider === "custom";
41182
+ };
41183
+ const maxFieldIndex = () => {
41184
+ if (isSettingsTab())
41185
+ return 3;
41186
+ return isCustomAgent() ? 4 : 2;
41187
+ };
41188
+ const getFieldValueForEntry = (entry, field) => {
41189
+ if (entry.provider === "custom") {
41190
+ return [entry.name, entry.tag, entry.model, entry.authUrl, entry.authToken][field];
41191
+ }
41192
+ return [entry.name, entry.tag, entry.model][field];
41193
+ };
41194
+ const getSettingsFieldValue = (field) => {
41195
+ return [settingsData.name, settingsData.tag, settingsData.maxHops, settingsData.timeout][field];
41196
+ };
41197
+ const getCurrentFieldValue = () => {
41198
+ if (isSettingsTab())
41199
+ return getSettingsFieldValue(focusedField());
41200
+ const entry = activeEntries()[agentTabIndex()];
41201
+ if (!entry)
41202
+ return "";
41203
+ return getFieldValueForEntry(entry, focusedField());
41204
+ };
41205
+ const updateField = (value, newCursor) => {
41206
+ if (isSettingsTab()) {
41207
+ if (focusedField() === 0)
41208
+ settingsData.name = value;
41209
+ else if (focusedField() === 1)
41210
+ settingsData.tag = value;
41211
+ else if (focusedField() === 2)
41212
+ settingsData.maxHops = value;
41213
+ else
41214
+ settingsData.timeout = value;
41215
+ } else {
41216
+ const entry = activeEntries()[agentTabIndex()];
41217
+ if (!entry)
41218
+ return;
41219
+ if (entry.provider === "custom") {
41220
+ if (focusedField() === 0)
41221
+ entry.name = value;
41222
+ else if (focusedField() === 1)
41223
+ entry.tag = value;
41224
+ else if (focusedField() === 2)
41225
+ entry.model = value;
41226
+ else if (focusedField() === 3)
41227
+ entry.authUrl = value;
41228
+ else
41229
+ entry.authToken = value;
41230
+ } else {
41231
+ if (focusedField() === 0)
41232
+ entry.name = value;
41233
+ else if (focusedField() === 1)
41234
+ entry.tag = value;
41235
+ else
41236
+ entry.model = value;
41237
+ }
41238
+ }
41239
+ cursorPos = newCursor;
41240
+ setError("");
41241
+ setTick((n) => n + 1);
41242
+ };
41243
+ const isTagField = () => focusedField() === 1;
41244
+ const isNumberField = () => isSettingsTab() && (focusedField() === 2 || focusedField() === 3);
40945
41245
  const saveConfig = async () => {
40946
- const configs = inputRefsData;
40947
- const human = humanData;
40948
- if (!human.name.trim()) {
40949
- setError("Your name cannot be empty");
41246
+ if (!settingsData.name.trim()) {
41247
+ setError("Name cannot be empty");
41248
+ return;
41249
+ }
41250
+ if (!settingsData.tag.trim()) {
41251
+ setError("Tag cannot be empty");
40950
41252
  return;
40951
41253
  }
40952
- if (!human.tag.trim()) {
40953
- setError("Your tag cannot be empty");
41254
+ if (!TAG_PATTERN.test(settingsData.tag.trim())) {
41255
+ setError("Tag can only contain letters, numbers, hyphens, underscores");
40954
41256
  return;
40955
41257
  }
40956
- if (!TAG_PATTERN.test(human.tag.trim())) {
40957
- setError("Your tag can only contain letters, numbers, hyphens, underscores");
41258
+ const maxHops = parseInt(settingsData.maxHops, 10);
41259
+ if (isNaN(maxHops) || maxHops < 0) {
41260
+ setError("Max Hops must be 0 or a positive number");
40958
41261
  return;
40959
41262
  }
40960
- for (const c of configs) {
40961
- if (!c.name.trim()) {
40962
- setError(`Name cannot be empty for ${c.id}`);
41263
+ const timeout = parseInt(settingsData.timeout, 10);
41264
+ if (isNaN(timeout) || timeout < 0) {
41265
+ setError("Timeout must be 0 or a positive number");
41266
+ return;
41267
+ }
41268
+ const tags = new Set;
41269
+ tags.add(settingsData.tag.trim().toLowerCase());
41270
+ for (const entry of agentEntries) {
41271
+ if (!entry.name.trim()) {
41272
+ setError(`Name cannot be empty for an agent`);
40963
41273
  return;
40964
41274
  }
40965
- if (!c.tag.trim()) {
40966
- setError(`Tag cannot be empty for ${c.id}`);
41275
+ if (!entry.tag.trim()) {
41276
+ setError(`Tag cannot be empty for ${entry.name}`);
40967
41277
  return;
40968
41278
  }
40969
- if (!TAG_PATTERN.test(c.tag.trim())) {
40970
- setError(`Tag for ${c.name} can only contain letters, numbers, hyphens, underscores`);
41279
+ if (!TAG_PATTERN.test(entry.tag.trim())) {
41280
+ setError(`Tag for ${entry.name} can only contain letters, numbers, hyphens, underscores`);
40971
41281
  return;
40972
41282
  }
40973
- if (!c.model.trim()) {
40974
- setError(`Model cannot be empty for ${c.id}`);
41283
+ if (!entry.model.trim()) {
41284
+ setError(`Model cannot be empty for ${entry.name}`);
40975
41285
  return;
40976
41286
  }
40977
- }
40978
- const names = new Set;
40979
- for (const c of configs) {
40980
- const lower = c.name.trim().toLowerCase();
40981
- if (names.has(lower)) {
40982
- setError(`Duplicate agent name: ${c.name}`);
41287
+ const tagLower = entry.tag.trim().toLowerCase();
41288
+ if (tags.has(tagLower)) {
41289
+ setError(`Duplicate tag: ${entry.tag}`);
41290
+ return;
41291
+ }
41292
+ tags.add(tagLower);
41293
+ if (entry.provider === "custom" && entry.active && !entry.authUrl.trim()) {
41294
+ setError(`URL is required for ${entry.name}`);
40983
41295
  return;
40984
41296
  }
40985
- names.add(lower);
40986
41297
  }
40987
- const overrides = configs.map((c) => ({
40988
- id: c.id,
40989
- name: c.name.trim(),
40990
- tag: c.tag.trim(),
40991
- model: c.model.trim()
40992
- }));
40993
- const mergedExisting = {
40994
- ...props.existingConfig || {},
40995
- humanName: human.name.trim(),
40996
- humanTag: human.tag.trim(),
40997
- agents: props.existingConfig?.agents || []
41298
+ const agents = agentEntries.map(entryToPersona);
41299
+ const config = {
41300
+ humanName: settingsData.name.trim(),
41301
+ humanTag: settingsData.tag.trim(),
41302
+ maxAutoHops: maxHops,
41303
+ timeout: timeout > 0 ? timeout : undefined,
41304
+ reminderInterval: props.existingConfig?.reminderInterval,
41305
+ agents
40998
41306
  };
41307
+ if (config.timeout === undefined)
41308
+ delete config.timeout;
41309
+ if (config.reminderInterval === undefined)
41310
+ delete config.reminderInterval;
40999
41311
  try {
41000
- await writeWizardConfig(selectedIds(), overrides, mergedExisting);
41312
+ await writeConfig(config);
41001
41313
  setStep("done");
41002
41314
  } catch (err) {
41003
41315
  setError(`Failed to save: ${err.message}`);
@@ -41009,9 +41321,8 @@ function ConfigWizard(props) {
41009
41321
  return;
41010
41322
  }
41011
41323
  if (step() !== "configure") {
41012
- if (step() === "detect") {
41324
+ if (step() === "detect")
41013
41325
  return;
41014
- }
41015
41326
  if (step() === "done") {
41016
41327
  if (key.name === "enter" || key.name === "return" || key.name === "space") {
41017
41328
  props.onComplete();
@@ -41020,49 +41331,20 @@ function ConfigWizard(props) {
41020
41331
  }
41021
41332
  return;
41022
41333
  }
41023
- const configs = inputRefsData;
41024
- const human = humanData;
41025
- let fieldValue;
41026
- if (isYouTab()) {
41027
- fieldValue = focusedField() === 0 ? human.name : human.tag;
41028
- } else {
41029
- const current = configs[agentTabIndex()];
41030
- if (!current)
41031
- return;
41032
- fieldValue = [current.name, current.tag, current.model][focusedField()];
41033
- }
41334
+ const fieldValue = getCurrentFieldValue();
41034
41335
  const cursor = cursorPos;
41035
- const updateField = (value, newCursor) => {
41036
- if (isYouTab()) {
41037
- if (focusedField() === 0)
41038
- human.name = value;
41039
- else
41040
- human.tag = value;
41041
- } else {
41042
- const current = configs[agentTabIndex()];
41043
- if (focusedField() === 0)
41044
- current.name = value;
41045
- else if (focusedField() === 1)
41046
- current.tag = value;
41047
- else
41048
- current.model = value;
41049
- }
41050
- cursorPos = newCursor;
41051
- setError("");
41052
- setTick((n) => n + 1);
41053
- };
41054
41336
  if (key.sequence === "[" || key.sequence === "]") {
41055
41337
  const dir = key.sequence === "[" ? -1 : 1;
41056
41338
  const next = (activeTab() + dir + totalTabs()) % totalTabs();
41057
41339
  setActiveTab(next);
41058
- const newMax = next === 0 ? 1 : 2;
41340
+ const newMax = next === 0 ? 3 : activeEntries()[next - 1]?.provider === "custom" ? 4 : 2;
41059
41341
  const newField = Math.min(focusedField(), newMax);
41060
41342
  setFocusedField(newField);
41061
41343
  let newVal;
41062
41344
  if (next === 0) {
41063
- newVal = newField === 0 ? human.name : human.tag;
41345
+ newVal = getSettingsFieldValue(newField);
41064
41346
  } else {
41065
- newVal = getFieldValue(configs[next - 1], newField);
41347
+ newVal = getFieldValueForEntry(activeEntries()[next - 1], newField);
41066
41348
  }
41067
41349
  cursorPos = newVal.length;
41068
41350
  setTick((n) => n + 1);
@@ -41072,12 +41354,7 @@ function ConfigWizard(props) {
41072
41354
  const fieldCount = maxFieldIndex() + 1;
41073
41355
  const nextField = key.shift ? (focusedField() - 1 + fieldCount) % fieldCount : (focusedField() + 1) % fieldCount;
41074
41356
  setFocusedField(nextField);
41075
- let newVal;
41076
- if (isYouTab()) {
41077
- newVal = nextField === 0 ? human.name : human.tag;
41078
- } else {
41079
- newVal = getFieldValue(configs[agentTabIndex()], nextField);
41080
- }
41357
+ const newVal = isSettingsTab() ? getSettingsFieldValue(nextField) : getFieldValueForEntry(activeEntries()[agentTabIndex()], nextField);
41081
41358
  cursorPos = newVal.length;
41082
41359
  setTick((n) => n + 1);
41083
41360
  return;
@@ -41132,7 +41409,7 @@ function ConfigWizard(props) {
41132
41409
  return;
41133
41410
  }
41134
41411
  if (key.name === "space" || key.sequence === " ") {
41135
- if (focusedField() !== 1) {
41412
+ if (!isTagField() && !isNumberField()) {
41136
41413
  updateField(fieldValue.slice(0, cursor) + " " + fieldValue.slice(cursor), cursor + 1);
41137
41414
  }
41138
41415
  return;
@@ -41141,7 +41418,9 @@ function ConfigWizard(props) {
41141
41418
  if (ch && ch.length > 0 && !ch.startsWith("\x1B")) {
41142
41419
  if (ch === "'" || ch === '"' || ch === "`")
41143
41420
  return;
41144
- if (focusedField() === 1 && !TAG_PATTERN.test(ch))
41421
+ if (isTagField() && !TAG_PATTERN.test(ch))
41422
+ return;
41423
+ if (isNumberField() && !/^[0-9]$/.test(ch))
41145
41424
  return;
41146
41425
  updateField(fieldValue.slice(0, cursor) + ch + fieldValue.slice(cursor), cursor + ch.length);
41147
41426
  }
@@ -41159,7 +41438,7 @@ function ConfigWizard(props) {
41159
41438
  })();
41160
41439
  }
41161
41440
  const discoColor = useDiscoColor();
41162
- function renderField(label, value, isFocused) {
41441
+ function renderField(label, value, isFocused, hint) {
41163
41442
  tick();
41164
41443
  const cursor = cursorPos;
41165
41444
  const labelColor = isFocused ? COLORS.primary : COLORS.textDim;
@@ -41182,7 +41461,7 @@ function ConfigWizard(props) {
41182
41461
  setProp(_el$12, "style", {
41183
41462
  fg: valueColor
41184
41463
  });
41185
- insert(_el$12, value);
41464
+ insert(_el$12, value || (hint ? hint : ""));
41186
41465
  effect((_$p) => setProp(_el$0, "style", {
41187
41466
  fg: COLORS.borderStrong
41188
41467
  }, _$p));
@@ -41217,6 +41496,13 @@ function ConfigWizard(props) {
41217
41496
  return _el$13;
41218
41497
  })();
41219
41498
  }
41499
+ function renderTokenField(label, value, isFocused) {
41500
+ if (!isFocused && value.length > 0) {
41501
+ const masked = value.length > 4 ? "*".repeat(value.length - 4) + value.slice(-4) : "*".repeat(value.length);
41502
+ return renderField(label, masked, false);
41503
+ }
41504
+ return renderField(label, value, isFocused);
41505
+ }
41220
41506
  const DetectStep = () => (() => {
41221
41507
  var _el$19 = createElement("box"), _el$20 = createElement("box"), _el$21 = createElement("box"), _el$22 = createElement("text"), _el$23 = createTextNode(` Scanning for installed CLIs...`);
41222
41508
  insertNode(_el$19, _el$20);
@@ -41332,6 +41618,9 @@ function ConfigWizard(props) {
41332
41618
  },
41333
41619
  get initialSelected() {
41334
41620
  return initialSelected();
41621
+ },
41622
+ addCustom: {
41623
+ onAdd: handleAddCustom
41335
41624
  }
41336
41625
  }));
41337
41626
  effect((_p$) => {
@@ -41369,12 +41658,8 @@ function ConfigWizard(props) {
41369
41658
  })();
41370
41659
  const ConfigureStep = () => {
41371
41660
  tick();
41372
- const configs = inputRefsData;
41373
- const human = humanData;
41374
- const tabLabels = ["You", ...selectedIds().map((id) => {
41375
- const def = PROVIDERS.find((p) => p.id === id);
41376
- return def?.displayName || id;
41377
- })];
41661
+ const active = activeEntries();
41662
+ const tabLabels = ["Settings", ...active.map((e) => e.name)];
41378
41663
  return (() => {
41379
41664
  var _el$48 = createElement("box"), _el$49 = createElement("box"), _el$50 = createElement("box"), _el$51 = createElement("box"), _el$52 = createElement("box"), _el$53 = createElement("text"), _el$55 = createElement("box"), _el$56 = createElement("box"), _el$60 = createElement("box"), _el$61 = createElement("text"), _el$62 = createElement("span"), _el$64 = createElement("span"), _el$66 = createElement("span"), _el$68 = createElement("span"), _el$70 = createElement("span"), _el$72 = createElement("span"), _el$74 = createElement("span"), _el$76 = createElement("span"), _el$78 = createElement("span"), _el$80 = createElement("span"), _el$82 = createElement("span"), _el$84 = createElement("span"), _el$86 = createElement("span");
41380
41665
  insertNode(_el$48, _el$49);
@@ -41446,7 +41731,7 @@ function ConfigWizard(props) {
41446
41731
  setProp(_el$56, "flexDirection", "column");
41447
41732
  insert(_el$56, createComponent2(Show, {
41448
41733
  get when() {
41449
- return isYouTab();
41734
+ return isSettingsTab();
41450
41735
  },
41451
41736
  get fallback() {
41452
41737
  return [(() => {
@@ -41455,19 +41740,39 @@ function ConfigWizard(props) {
41455
41740
  setProp(_el$94, "marginBottom", 1);
41456
41741
  insertNode(_el$95, _el$96);
41457
41742
  insert(_el$95, () => tabLabels[activeTab()], _el$96);
41743
+ insert(_el$94, createComponent2(Show, {
41744
+ get when() {
41745
+ return activeEntries()[agentTabIndex()]?.provider === "custom";
41746
+ },
41747
+ get children() {
41748
+ var _el$97 = createElement("span");
41749
+ insertNode(_el$97, createTextNode(` (custom)`));
41750
+ effect((_$p) => setProp(_el$97, "style", {
41751
+ fg: COLORS.textFaint
41752
+ }, _$p));
41753
+ return _el$97;
41754
+ }
41755
+ }), null);
41458
41756
  effect((_$p) => setProp(_el$94, "fg", COLORS.success, _$p));
41459
41757
  return _el$94;
41460
- })(), memo2(() => renderField("Name ", configs[agentTabIndex()].name, focusedField() === 0)), memo2(() => renderField("Tag ", configs[agentTabIndex()].tag, focusedField() === 1)), memo2(() => renderField("Model", configs[agentTabIndex()].model, focusedField() === 2))];
41758
+ })(), memo2(() => renderField("Name ", active[agentTabIndex()].name, focusedField() === 0)), memo2(() => renderField("Tag ", active[agentTabIndex()].tag, focusedField() === 1)), memo2(() => renderField("Model", active[agentTabIndex()].model, focusedField() === 2)), createComponent2(Show, {
41759
+ get when() {
41760
+ return active[agentTabIndex()]?.provider === "custom";
41761
+ },
41762
+ get children() {
41763
+ return [memo2(() => renderField("URL ", active[agentTabIndex()].authUrl, focusedField() === 3)), memo2(() => renderTokenField("Token", active[agentTabIndex()].authToken, focusedField() === 4))];
41764
+ }
41765
+ })];
41461
41766
  },
41462
41767
  get children() {
41463
41768
  return [(() => {
41464
41769
  var _el$57 = createElement("text"), _el$58 = createElement("strong");
41465
41770
  insertNode(_el$57, _el$58);
41466
41771
  setProp(_el$57, "marginBottom", 1);
41467
- insertNode(_el$58, createTextNode(`Your Identity`));
41772
+ insertNode(_el$58, createTextNode(`General Settings`));
41468
41773
  effect((_$p) => setProp(_el$57, "fg", COLORS.agent, _$p));
41469
41774
  return _el$57;
41470
- })(), memo2(() => renderField("Name", human.name, focusedField() === 0)), memo2(() => renderField("Tag ", human.tag, focusedField() === 1))];
41775
+ })(), memo2(() => renderField("Name ", settingsData.name, focusedField() === 0)), memo2(() => renderField("Tag ", settingsData.tag, focusedField() === 1)), memo2(() => renderField("Max Hops", settingsData.maxHops, focusedField() === 2, "0 = unlimited")), memo2(() => renderField("Timeout ", settingsData.timeout, focusedField() === 3, "0 = unlimited"))];
41471
41776
  }
41472
41777
  }));
41473
41778
  insertNode(_el$60, _el$61);
@@ -41527,7 +41832,7 @@ function ConfigWizard(props) {
41527
41832
  }
41528
41833
  }), null);
41529
41834
  effect((_p$) => {
41530
- var _v$13 = discoColor(), _v$14 = COLORS.bgPanel, _v$15 = COLORS.borderStrong, _v$16 = isYouTab() ? COLORS.agent : COLORS.success, _v$17 = COLORS.bgContent, _v$18 = {
41835
+ var _v$13 = discoColor(), _v$14 = COLORS.bgPanel, _v$15 = COLORS.borderStrong, _v$16 = isSettingsTab() ? COLORS.agent : COLORS.success, _v$17 = COLORS.bgContent, _v$18 = {
41531
41836
  fg: COLORS.textFaint
41532
41837
  }, _v$19 = {
41533
41838
  fg: COLORS.success
@@ -41597,57 +41902,57 @@ function ConfigWizard(props) {
41597
41902
  })();
41598
41903
  };
41599
41904
  const DoneStep = () => (() => {
41600
- var _el$97 = createElement("box"), _el$98 = createElement("box"), _el$99 = createElement("box"), _el$100 = createElement("text"), _el$102 = createElement("text"), _el$104 = createElement("text"), _el$106 = createElement("text"), _el$108 = createElement("text"), _el$110 = createElement("text"), _el$111 = createTextNode(`Press `), _el$112 = createElement("span"), _el$113 = createElement("strong"), _el$115 = createTextNode(` to continue`);
41601
- insertNode(_el$97, _el$98);
41602
- setProp(_el$97, "flexDirection", "column");
41603
- setProp(_el$97, "width", "100%");
41604
- setProp(_el$97, "height", "100%");
41605
- setProp(_el$97, "justifyContent", "center");
41606
- setProp(_el$97, "alignItems", "center");
41607
- insertNode(_el$98, _el$99);
41608
- setProp(_el$98, "border", true);
41609
- setProp(_el$98, "borderStyle", "double");
41610
- setProp(_el$98, "paddingX", 4);
41611
- setProp(_el$98, "paddingY", 2);
41905
+ var _el$99 = createElement("box"), _el$100 = createElement("box"), _el$101 = createElement("box"), _el$102 = createElement("text"), _el$104 = createElement("text"), _el$106 = createElement("text"), _el$108 = createElement("text"), _el$110 = createElement("text"), _el$112 = createElement("text"), _el$113 = createTextNode(`Press `), _el$114 = createElement("span"), _el$115 = createElement("strong"), _el$117 = createTextNode(` to continue`);
41612
41906
  insertNode(_el$99, _el$100);
41613
- insertNode(_el$99, _el$102);
41614
- insertNode(_el$99, _el$104);
41615
- insertNode(_el$99, _el$106);
41616
- insertNode(_el$99, _el$108);
41617
- insertNode(_el$99, _el$110);
41618
41907
  setProp(_el$99, "flexDirection", "column");
41908
+ setProp(_el$99, "width", "100%");
41909
+ setProp(_el$99, "height", "100%");
41910
+ setProp(_el$99, "justifyContent", "center");
41619
41911
  setProp(_el$99, "alignItems", "center");
41620
- insert(_el$99, createComponent2(SweepBar, {
41912
+ insertNode(_el$100, _el$101);
41913
+ setProp(_el$100, "border", true);
41914
+ setProp(_el$100, "borderStyle", "double");
41915
+ setProp(_el$100, "paddingX", 4);
41916
+ setProp(_el$100, "paddingY", 2);
41917
+ insertNode(_el$101, _el$102);
41918
+ insertNode(_el$101, _el$104);
41919
+ insertNode(_el$101, _el$106);
41920
+ insertNode(_el$101, _el$108);
41921
+ insertNode(_el$101, _el$110);
41922
+ insertNode(_el$101, _el$112);
41923
+ setProp(_el$101, "flexDirection", "column");
41924
+ setProp(_el$101, "alignItems", "center");
41925
+ insert(_el$101, createComponent2(SweepBar, {
41621
41926
  title: "Config Saved"
41622
- }), _el$100);
41623
- insertNode(_el$100, createTextNode(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
41624
- setProp(_el$100, "marginTop", 1);
41625
- insertNode(_el$102, createTextNode(`Written to ~/.llm-party/config.json`));
41927
+ }), _el$102);
41928
+ insertNode(_el$102, createTextNode(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
41626
41929
  setProp(_el$102, "marginTop", 1);
41627
- insertNode(_el$104, createTextNode(`Edit this file anytime to add prompts,`));
41930
+ insertNode(_el$104, createTextNode(`Written to ~/.llm-party/config.json`));
41628
41931
  setProp(_el$104, "marginTop", 1);
41629
- insertNode(_el$106, createTextNode(`env vars, or tweak settings.`));
41630
- insertNode(_el$108, createTextNode(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
41631
- setProp(_el$108, "marginTop", 1);
41632
- insertNode(_el$110, _el$111);
41633
- insertNode(_el$110, _el$112);
41634
- insertNode(_el$110, _el$115);
41932
+ insertNode(_el$106, createTextNode(`Edit this file anytime to add prompts,`));
41933
+ setProp(_el$106, "marginTop", 1);
41934
+ insertNode(_el$108, createTextNode(`env vars, or tweak settings.`));
41935
+ insertNode(_el$110, createTextNode(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
41635
41936
  setProp(_el$110, "marginTop", 1);
41636
41937
  insertNode(_el$112, _el$113);
41637
- insertNode(_el$113, createTextNode(`Enter`));
41938
+ insertNode(_el$112, _el$114);
41939
+ insertNode(_el$112, _el$117);
41940
+ setProp(_el$112, "marginTop", 1);
41941
+ insertNode(_el$114, _el$115);
41942
+ insertNode(_el$115, createTextNode(`Enter`));
41638
41943
  effect((_p$) => {
41639
41944
  var _v$33 = discoColor(), _v$34 = COLORS.bgPanel, _v$35 = COLORS.textSubtle, _v$36 = COLORS.textMuted, _v$37 = COLORS.textSubtle, _v$38 = COLORS.textSubtle, _v$39 = COLORS.textSubtle, _v$40 = COLORS.primary, _v$41 = {
41640
41945
  fg: COLORS.success
41641
41946
  };
41642
- _v$33 !== _p$.e && (_p$.e = setProp(_el$98, "borderColor", _v$33, _p$.e));
41643
- _v$34 !== _p$.t && (_p$.t = setProp(_el$98, "backgroundColor", _v$34, _p$.t));
41644
- _v$35 !== _p$.a && (_p$.a = setProp(_el$100, "fg", _v$35, _p$.a));
41645
- _v$36 !== _p$.o && (_p$.o = setProp(_el$102, "fg", _v$36, _p$.o));
41646
- _v$37 !== _p$.i && (_p$.i = setProp(_el$104, "fg", _v$37, _p$.i));
41647
- _v$38 !== _p$.n && (_p$.n = setProp(_el$106, "fg", _v$38, _p$.n));
41648
- _v$39 !== _p$.s && (_p$.s = setProp(_el$108, "fg", _v$39, _p$.s));
41649
- _v$40 !== _p$.h && (_p$.h = setProp(_el$110, "fg", _v$40, _p$.h));
41650
- _v$41 !== _p$.r && (_p$.r = setProp(_el$112, "style", _v$41, _p$.r));
41947
+ _v$33 !== _p$.e && (_p$.e = setProp(_el$100, "borderColor", _v$33, _p$.e));
41948
+ _v$34 !== _p$.t && (_p$.t = setProp(_el$100, "backgroundColor", _v$34, _p$.t));
41949
+ _v$35 !== _p$.a && (_p$.a = setProp(_el$102, "fg", _v$35, _p$.a));
41950
+ _v$36 !== _p$.o && (_p$.o = setProp(_el$104, "fg", _v$36, _p$.o));
41951
+ _v$37 !== _p$.i && (_p$.i = setProp(_el$106, "fg", _v$37, _p$.i));
41952
+ _v$38 !== _p$.n && (_p$.n = setProp(_el$108, "fg", _v$38, _p$.n));
41953
+ _v$39 !== _p$.s && (_p$.s = setProp(_el$110, "fg", _v$39, _p$.s));
41954
+ _v$40 !== _p$.h && (_p$.h = setProp(_el$112, "fg", _v$40, _p$.h));
41955
+ _v$41 !== _p$.r && (_p$.r = setProp(_el$114, "style", _v$41, _p$.r));
41651
41956
  return _p$;
41652
41957
  }, {
41653
41958
  e: undefined,
@@ -41660,7 +41965,7 @@ function ConfigWizard(props) {
41660
41965
  h: undefined,
41661
41966
  r: undefined
41662
41967
  });
41663
- return _el$97;
41968
+ return _el$99;
41664
41969
  })();
41665
41970
  return [createComponent2(Show, {
41666
41971
  get when() {
@@ -41692,13 +41997,6 @@ function ConfigWizard(props) {
41692
41997
  }
41693
41998
  })];
41694
41999
  }
41695
- function getFieldValue(config, field) {
41696
- if (field === 0)
41697
- return config.name;
41698
- if (field === 1)
41699
- return config.tag;
41700
- return config.model;
41701
- }
41702
42000
 
41703
42001
  // src/ui/AgentsPanel.tsx
41704
42002
  function AgentsPanel(props) {
@@ -42003,7 +42301,7 @@ function InfoPanel(props) {
42003
42301
 
42004
42302
  // src/ui/App.tsx
42005
42303
  function copyToClipboard(text) {
42006
- const proc = spawn5("pbcopy", [], {
42304
+ const proc = spawn4("pbcopy", [], {
42007
42305
  stdio: ["pipe", "ignore", "ignore"]
42008
42306
  });
42009
42307
  proc.stdin?.write(text);
@@ -42035,6 +42333,7 @@ function App(props) {
42035
42333
  const agents = props.orchestrator.listAgents();
42036
42334
  let scrollRef = null;
42037
42335
  const [screen, setScreen] = createSignal("chat");
42336
+ const [freshConfig, setFreshConfig] = createSignal(props.config);
42038
42337
  const [showAgents, setShowAgents] = createSignal(false);
42039
42338
  const [showInfo, setShowInfo] = createSignal(false);
42040
42339
  process.on("SIGINT", () => renderer.destroy());
@@ -42108,6 +42407,10 @@ Transcript: ${props.orchestrator.getTranscriptPath()}`);
42108
42407
  return;
42109
42408
  }
42110
42409
  if (line === "/config") {
42410
+ try {
42411
+ const reloaded = await loadConfig2(props.configPath);
42412
+ setFreshConfig(reloaded);
42413
+ } catch {}
42111
42414
  setScreen("config");
42112
42415
  return;
42113
42416
  }
@@ -42148,7 +42451,7 @@ ${lines.join(`
42148
42451
  return createComponent2(ConfigWizard, {
42149
42452
  isFirstRun: false,
42150
42453
  get existingConfig() {
42151
- return props.config;
42454
+ return freshConfig();
42152
42455
  },
42153
42456
  onComplete: () => {
42154
42457
  addSystemMessage("Config saved. Restart llm-party to apply changes.");
@@ -42265,7 +42568,8 @@ async function bootApp(appRoot, rendererConfig) {
42265
42568
  ` + obsidianPrompt;
42266
42569
  const availableSkills = await discoverSkills();
42267
42570
  const agentVerifiedSkills = new Map;
42268
- for (const agent of config.agents) {
42571
+ const activeAgents = config.agents.filter((a) => a.active !== false);
42572
+ for (const agent of activeAgents) {
42269
42573
  if (agent.preloadSkills && agent.preloadSkills.length > 0) {
42270
42574
  const verified = [];
42271
42575
  const report = [];
@@ -42286,7 +42590,7 @@ async function bootApp(appRoot, rendererConfig) {
42286
42590
  const resolveFromConfig = (value) => {
42287
42591
  return path11.isAbsolute(value) ? value : path11.resolve(configDir, value);
42288
42592
  };
42289
- const adapters = await Promise.all(config.agents.map(async (agent, _index, allAgents) => {
42593
+ const adapters = await Promise.all(activeAgents.map(async (agent, _index, allAgents) => {
42290
42594
  const promptParts = [mergedBase];
42291
42595
  if (agent.prompts && agent.prompts.length > 0) {
42292
42596
  const extraPaths = agent.prompts.map((p) => resolveFromConfig(p));
@@ -42325,7 +42629,7 @@ ${mySkills.map((s) => `- ${s}`).join(`
42325
42629
  agentCount: String(allAgents.length),
42326
42630
  preloadedSkills
42327
42631
  });
42328
- const adapter = agent.provider === "claude" ? new ClaudeAdapter(agent.name, agent.model) : agent.provider === "codex" ? new CodexAdapter(agent.name, agent.model) : agent.provider === "copilot" ? new CopilotAdapter(agent.name, agent.model) : agent.provider === "glm" ? new GlmAdapter(agent.name, agent.model) : null;
42632
+ const adapter = agent.provider === "claude" ? new ClaudeAdapter(agent.name, agent.model, humanName) : agent.provider === "codex" ? new CodexAdapter(agent.name, agent.model, humanName) : agent.provider === "copilot" ? new CopilotAdapter(agent.name, agent.model, humanName) : agent.provider === "custom" ? new CustomAdapter(agent.name, agent.model, humanName) : null;
42329
42633
  if (!adapter) {
42330
42634
  throw new Error(`Unsupported provider: ${agent.provider}`);
42331
42635
  }
@@ -42333,12 +42637,12 @@ ${mySkills.map((s) => `- ${s}`).join(`
42333
42637
  return adapter;
42334
42638
  }));
42335
42639
  const defaultTimeout = typeof config.timeout === "number" && config.timeout > 0 ? config.timeout * 1000 : 600000;
42336
- const agentTimeouts = Object.fromEntries(config.agents.filter((agent) => typeof agent.timeout === "number" && agent.timeout > 0).map((agent) => [agent.name, agent.timeout * 1000]));
42337
- const orchestrator = new Orchestrator(adapters, humanName, Object.fromEntries(config.agents.map((agent) => [agent.name, agent.tag?.trim() || toTag(agent.name)])), humanTag, defaultTimeout, agentTimeouts, { reminderInterval: config.reminderInterval });
42338
- await render(() => App({ orchestrator, maxAutoHops, config }), rendererConfig);
42640
+ const agentTimeouts = Object.fromEntries(activeAgents.filter((agent) => typeof agent.timeout === "number" && agent.timeout > 0).map((agent) => [agent.name, agent.timeout * 1000]));
42641
+ const orchestrator = new Orchestrator(adapters, humanName, Object.fromEntries(activeAgents.map((agent) => [agent.name, agent.tag?.trim() || toTag(agent.name)])), humanTag, defaultTimeout, agentTimeouts, { reminderInterval: config.reminderInterval });
42642
+ await render(() => App({ orchestrator, maxAutoHops, config, configPath }), rendererConfig);
42339
42643
  }
42340
42644
  function resolveMaxAutoHops(value) {
42341
- if (value === "unlimited") {
42645
+ if (typeof value === "number" && value === 0) {
42342
42646
  return Number.POSITIVE_INFINITY;
42343
42647
  }
42344
42648
  if (typeof value === "number" && Number.isFinite(value) && value >= 1) {