@pellux/goodvibes-agent 1.1.4 → 1.1.6

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.
@@ -817092,7 +817092,7 @@ var createStyledCell = (char, overrides = {}) => ({
817092
817092
  // src/version.ts
817093
817093
  import { readFileSync } from "fs";
817094
817094
  import { join } from "path";
817095
- var _version = "1.1.4";
817095
+ var _version = "1.1.6";
817096
817096
  try {
817097
817097
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf-8"));
817098
817098
  _version = typeof pkg.version === "string" ? pkg.version : _version;
@@ -849851,13 +849851,13 @@ init_state3();
849851
849851
 
849852
849852
  // src/input/commands/recall-shared.ts
849853
849853
  var VALID_CLASSES = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
849854
- var VALID_SCOPES2 = ["session", "project", "team"];
849854
+ var VALID_SCOPES = ["session", "project", "team"];
849855
849855
  var VALID_REVIEW_STATES = ["fresh", "reviewed", "stale", "contradicted"];
849856
849856
  function isValidClass(s4) {
849857
849857
  return VALID_CLASSES.includes(s4);
849858
849858
  }
849859
849859
  function isValidScope(s4) {
849860
- return VALID_SCOPES2.includes(s4);
849860
+ return VALID_SCOPES.includes(s4);
849861
849861
  }
849862
849862
  function isValidReviewState(s4) {
849863
849863
  return VALID_REVIEW_STATES.includes(s4);
@@ -849900,7 +849900,7 @@ function handleRecallSearch(args2, context) {
849900
849900
  if (isValidScope(scope))
849901
849901
  filter.scope = scope;
849902
849902
  else {
849903
- context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES2.join(", ")}.`);
849903
+ context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
849904
849904
  return;
849905
849905
  }
849906
849906
  }
@@ -850075,7 +850075,7 @@ function handleRecallList(args2, context) {
850075
850075
  if (scopeIdx !== -1 && args2[scopeIdx + 1]) {
850076
850076
  const scope = args2[scopeIdx + 1];
850077
850077
  if (!isValidScope(scope)) {
850078
- context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES2.join(", ")}.`);
850078
+ context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
850079
850079
  return;
850080
850080
  }
850081
850081
  filter.scope = scope;
@@ -850125,7 +850125,7 @@ async function handleRecallAdd(args2, context) {
850125
850125
  const tags = tagsRaw ? tagsRaw.split(",").map((token) => token.trim()).filter(Boolean) : [];
850126
850126
  const scope = scopeRaw && isValidScope(scopeRaw) ? scopeRaw : "project";
850127
850127
  if (scopeRaw && !isValidScope(scopeRaw)) {
850128
- context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${VALID_SCOPES2.join(", ")}.`);
850128
+ context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${VALID_SCOPES.join(", ")}.`);
850129
850129
  return;
850130
850130
  }
850131
850131
  const provenance = [];
@@ -850259,7 +850259,7 @@ function handleRecallExport(args2, context) {
850259
850259
  if (scopeIdx !== -1 && commandArgs[scopeIdx + 1]) {
850260
850260
  const scope = commandArgs[scopeIdx + 1];
850261
850261
  if (!isValidScope(scope)) {
850262
- context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES2.join(", ")}.`);
850262
+ context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
850263
850263
  return;
850264
850264
  }
850265
850265
  filter.scope = scope;
@@ -850338,7 +850338,7 @@ function handleRecallHandoffExport(args2, context) {
850338
850338
  const scopeIdx = commandArgs.indexOf("--scope");
850339
850339
  const scopeRaw = scopeIdx !== -1 ? commandArgs[scopeIdx + 1] : "team";
850340
850340
  if (!scopeRaw || !isValidScope(scopeRaw)) {
850341
- context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${VALID_SCOPES2.join(", ")}.`);
850341
+ context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${VALID_SCOPES.join(", ")}.`);
850342
850342
  return;
850343
850343
  }
850344
850344
  const bundle = memory.exportBundle({ scope: scopeRaw });
@@ -850454,7 +850454,7 @@ function handleRecallPromote(args2, context) {
850454
850454
  const id = parsed.rest[0];
850455
850455
  const scope = parsed.rest[1];
850456
850456
  if (!id || !scope || !isValidScope(scope)) {
850457
- context.print(`[memory] Usage: /memory promote <id> <${VALID_SCOPES2.join("|")}> --yes`);
850457
+ context.print(`[memory] Usage: /memory promote <id> <${VALID_SCOPES.join("|")}> --yes`);
850458
850458
  return;
850459
850459
  }
850460
850460
  if (!parsed.yes) {
@@ -858623,7 +858623,7 @@ var AGENT_WORKSPACE_CATEGORY_IDS = [
858623
858623
  "onboarding-channels",
858624
858624
  "onboarding-voice-media",
858625
858625
  "onboarding-context",
858626
- "onboarding-verify",
858626
+ "onboarding-automation",
858627
858627
  "research",
858628
858628
  "artifacts",
858629
858629
  "conversation",
@@ -862058,6 +862058,7 @@ Use /channels list to see available channel ids.`);
862058
862058
  }
862059
862059
 
862060
862060
  // src/input/agent-workspace-snapshot.ts
862061
+ init_config8();
862061
862062
  import { basename as basename6, sep as sep3 } from "path";
862062
862063
 
862063
862064
  // src/agent/behavior-discovery-summary.ts
@@ -862265,6 +862266,13 @@ function buildAgentWorkspaceSetupChecklist(input) {
862265
862266
  detail: providerReady ? `Current chat route is ${input.provider} / ${input.model}.` : "Choose a provider and model before relying on assistant turns.",
862266
862267
  command: "Setup -> Provider and model"
862267
862268
  },
862269
+ {
862270
+ id: "subscriptions",
862271
+ label: "Provider subscriptions",
862272
+ status: input.activeSubscriptionCount > 0 ? "ready" : input.pendingSubscriptionCount > 0 ? "recommended" : input.availableSubscriptionProviderCount > 0 ? "recommended" : "optional",
862273
+ detail: input.activeSubscriptionCount > 0 ? `${input.activeSubscriptionCount} provider subscription session(s) are active.` : input.pendingSubscriptionCount > 0 ? `${input.pendingSubscriptionCount} provider subscription login(s) are pending completion.` : input.availableSubscriptionProviderCount > 0 ? `${input.availableSubscriptionProviderCount} subscription-capable provider(s) are available. Start login if you want subscription routing.` : "No subscription-capable providers are available yet. Use API keys or add an OAuth provider service.",
862274
+ command: "Start -> Start subscription login"
862275
+ },
862268
862276
  {
862269
862277
  id: "agent-knowledge",
862270
862278
  label: "Agent Knowledge",
@@ -862833,6 +862841,41 @@ function buildAgentWorkspaceRuntimeSnapshot(context) {
862833
862841
  const ttsVoice = readConfigString3(context, "tts.voice", "(voice default)");
862834
862842
  const ttsLlmProvider = readConfigString3(context, "tts.llmProvider", "");
862835
862843
  const ttsLlmModel = readConfigString3(context, "tts.llmModel", "");
862844
+ const embeddingProvider = readConfigString3(context, "provider.embeddingProvider", "(provider default)");
862845
+ const reasoningEffort = readConfigString3(context, "provider.reasoningEffort", "(default)");
862846
+ const helperEnabled = readConfigBoolean3(context, "helper.enabled", false);
862847
+ const toolLlmEnabled = readConfigBoolean3(context, "tools.llmEnabled", false);
862848
+ const providerFailureHints = readConfigBoolean3(context, "behavior.suggestAlternativeOnProviderFail", false);
862849
+ const cacheEnabled = readConfigBoolean3(context, "cache.enabled", true);
862850
+ const cacheStableTtl = readConfigString3(context, "cache.stableTtl", "(default)");
862851
+ const cacheMonitorHitRate = readConfigBoolean3(context, "cache.monitorHitRate", true);
862852
+ const cacheHitRateWarningThreshold = readConfigNumber(context, "cache.hitRateWarningThreshold", 0.3);
862853
+ const hitlMode = readConfigString3(context, "behavior.hitlMode", "(default)");
862854
+ const guidanceMode = readConfigString3(context, "behavior.guidanceMode", "(default)");
862855
+ const saveHistory2 = readConfigBoolean3(context, "behavior.saveHistory", true);
862856
+ const autoApprove = readConfigBoolean3(context, "behavior.autoApprove", false);
862857
+ const autoCompactThreshold = readConfigNumber(context, "behavior.autoCompactThreshold", 0);
862858
+ const staleContextWarnings = readConfigBoolean3(context, "behavior.staleContextWarnings", false);
862859
+ const showThinking = readConfigBoolean3(context, "display.showThinking", false);
862860
+ const showReasoningSummary = readConfigBoolean3(context, "display.showReasoningSummary", false);
862861
+ const theme = readConfigString3(context, "display.theme", "(default)");
862862
+ const stream6 = readConfigBoolean3(context, "display.stream", true);
862863
+ const lineNumbers = readConfigString3(context, "display.lineNumbers", "(default)");
862864
+ const operationalMessages = readConfigString3(context, "ui.operationalMessages", "(default)");
862865
+ const systemMessages = readConfigString3(context, "ui.systemMessages", "(default)");
862866
+ const releaseChannel = readConfigString3(context, "release.channel", "(default)");
862867
+ const permissionMode = readConfigString3(context, "permissions.mode", "(default)");
862868
+ const toolAutoHeal = readConfigBoolean3(context, "tools.autoHeal", false);
862869
+ const toolsDefaultTokenBudget = readConfigNumber(context, "tools.defaultTokenBudget", 5000);
862870
+ const artifactMaxBytes = readConfigNumber(context, "storage.artifacts.maxBytes", 512 * 1024 * 1024);
862871
+ const rawPromptTelemetry = readConfigBoolean3(context, "telemetry.includeRawPrompts", false);
862872
+ const automationEnabled = readConfigBoolean3(context, "automation.enabled", false);
862873
+ const automationMaxConcurrentRuns = readConfigNumber(context, "automation.maxConcurrentRuns", 4);
862874
+ const automationRunHistoryLimit = readConfigNumber(context, "automation.runHistoryLimit", 100);
862875
+ const automationDefaultTimeoutMs = readConfigNumber(context, "automation.defaultTimeoutMs", 15 * 60 * 1000);
862876
+ const automationCatchUpWindowMinutes = readConfigNumber(context, "automation.catchUpWindowMinutes", 30);
862877
+ const automationFailureCooldownMs = readConfigNumber(context, "automation.failureCooldownMs", 5 * 60 * 1000);
862878
+ const automationDeleteAfterRun = readConfigBoolean3(context, "automation.deleteAfterRun", false);
862836
862879
  const runtimeBaseUrl = `http://${host}:${port}`;
862837
862880
  const companionAccess = (() => {
862838
862881
  const homeDirectory = context.workspace?.shellPaths?.homeDirectory ?? "";
@@ -862854,6 +862897,18 @@ function buildAgentWorkspaceRuntimeSnapshot(context) {
862854
862897
  nextStep
862855
862898
  };
862856
862899
  })();
862900
+ const subscriptionSnapshot = (() => {
862901
+ try {
862902
+ const manager5 = context.platform?.subscriptionManager;
862903
+ const services = context.platform?.serviceRegistry;
862904
+ const active = manager5?.list?.().length ?? 0;
862905
+ const pending = manager5?.listPending?.().length ?? 0;
862906
+ const available = services ? listAvailableSubscriptionProviders(services.getAll()).length : 0;
862907
+ return { active, pending, available };
862908
+ } catch {
862909
+ return { active: 0, pending: 0, available: 0 };
862910
+ }
862911
+ })();
862857
862912
  const channels = buildAgentWorkspaceChannels(context);
862858
862913
  const voiceMediaReadiness = buildAgentWorkspaceVoiceMediaReadiness({
862859
862914
  context,
@@ -862864,6 +862919,9 @@ function buildAgentWorkspaceRuntimeSnapshot(context) {
862864
862919
  provider: provider5,
862865
862920
  model,
862866
862921
  runtimeBaseUrl,
862922
+ activeSubscriptionCount: subscriptionSnapshot.active,
862923
+ pendingSubscriptionCount: subscriptionSnapshot.pending,
862924
+ availableSubscriptionProviderCount: subscriptionSnapshot.available,
862867
862925
  sessionMemoryCount,
862868
862926
  localMemoryCount: memorySnapshot.count,
862869
862927
  localMemoryReviewQueueCount: memorySnapshot.reviewQueueCount,
@@ -862891,11 +862949,49 @@ function buildAgentWorkspaceRuntimeSnapshot(context) {
862891
862949
  provider: provider5,
862892
862950
  model,
862893
862951
  modelDisplayName: currentModel?.displayName ?? model,
862952
+ embeddingProvider,
862953
+ reasoningEffort,
862954
+ helperEnabled,
862955
+ toolLlmEnabled,
862956
+ providerFailureHints,
862957
+ cacheEnabled,
862958
+ cacheStableTtl,
862959
+ cacheMonitorHitRate,
862960
+ cacheHitRateWarningThreshold,
862961
+ hitlMode,
862962
+ guidanceMode,
862963
+ saveHistory: saveHistory2,
862964
+ autoApprove,
862965
+ autoCompactThreshold,
862966
+ staleContextWarnings,
862967
+ showThinking,
862968
+ showReasoningSummary,
862969
+ theme,
862970
+ stream: stream6,
862971
+ lineNumbers,
862972
+ operationalMessages,
862973
+ systemMessages,
862974
+ releaseChannel,
862975
+ permissionMode,
862976
+ toolAutoHeal,
862977
+ toolsDefaultTokenBudget,
862978
+ artifactMaxBytes,
862979
+ rawPromptTelemetry,
862980
+ automationEnabled,
862981
+ automationMaxConcurrentRuns,
862982
+ automationRunHistoryLimit,
862983
+ automationDefaultTimeoutMs,
862984
+ automationCatchUpWindowMinutes,
862985
+ automationFailureCooldownMs,
862986
+ automationDeleteAfterRun,
862894
862987
  sessionId: context.session?.runtime?.sessionId ?? "unknown",
862895
862988
  workingDirectory: context.workspace?.shellPaths?.workingDirectory ?? "unavailable",
862896
862989
  homeDirectory: context.workspace?.shellPaths?.homeDirectory ?? "unavailable",
862897
862990
  runtimeBaseUrl,
862898
862991
  runtimeOwnership: "external",
862992
+ activeSubscriptionCount: subscriptionSnapshot.active,
862993
+ pendingSubscriptionCount: subscriptionSnapshot.pending,
862994
+ availableSubscriptionProviderCount: subscriptionSnapshot.available,
862899
862995
  sessionMemoryCount,
862900
862996
  localMemoryCount: memorySnapshot.count,
862901
862997
  localMemoryReviewQueueCount: memorySnapshot.reviewQueueCount,
@@ -866599,7 +866695,7 @@ import { mkdirSync as mkdirSync66, readFileSync as readFileSync88, writeFileSync
866599
866695
  import { dirname as dirname64, resolve as resolve39 } from "path";
866600
866696
  init_state3();
866601
866697
  var VALID_CLASSES2 = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
866602
- var VALID_SCOPES4 = ["session", "project", "team"];
866698
+ var VALID_SCOPES3 = ["session", "project", "team"];
866603
866699
  var VALID_REVIEW_STATES3 = ["fresh", "reviewed", "stale", "contradicted"];
866604
866700
  var VALID_PROVENANCE_KINDS = ["session", "turn", "task", "event", "file"];
866605
866701
  var VALUE_OPTIONS = new Set([
@@ -866699,7 +866795,7 @@ function isMemoryClass2(value) {
866699
866795
  return VALID_CLASSES2.includes(value);
866700
866796
  }
866701
866797
  function isMemoryScope2(value) {
866702
- return VALID_SCOPES4.includes(value);
866798
+ return VALID_SCOPES3.includes(value);
866703
866799
  }
866704
866800
  function isReviewState(value) {
866705
866801
  return VALID_REVIEW_STATES3.includes(value);
@@ -866714,7 +866810,7 @@ function requireClass(value) {
866714
866810
  }
866715
866811
  function requireScope(value) {
866716
866812
  if (!value || !isMemoryScope2(value))
866717
- throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${VALID_SCOPES4.join(", ")}`);
866813
+ throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${VALID_SCOPES3.join(", ")}`);
866718
866814
  return value;
866719
866815
  }
866720
866816
  function optionalScope(value) {
@@ -881486,7 +881582,7 @@ function createAgentWorkspaceBasicCommandEditor(kind2) {
881486
881582
  selectedFieldIndex: 0,
881487
881583
  message: "Inspect one provider subscription route without starting login or printing token values.",
881488
881584
  fields: [
881489
- { id: "provider", label: "Provider", value: "openai", required: true, multiline: false, hint: "Subscription provider id from /subscription providers." }
881585
+ { id: "provider", label: "Provider", value: "openai", required: true, multiline: false, hint: "Subscription provider id, such as openai." }
881490
881586
  ]
881491
881587
  };
881492
881588
  }
@@ -881496,12 +881592,11 @@ function createAgentWorkspaceBasicCommandEditor(kind2) {
881496
881592
  mode: "create",
881497
881593
  title: "Start Provider Subscription Login",
881498
881594
  selectedFieldIndex: 0,
881499
- message: "Begin one provider subscription OAuth login. Type yes on the final field to confirm browser and pending-login side effects.",
881595
+ message: "Begin one provider subscription OAuth login, save the pending session, and return here for completion.",
881500
881596
  fields: [
881501
- { id: "provider", label: "Provider", value: "openai", required: true, multiline: false, hint: "Subscription provider id from /subscription providers." },
881597
+ { id: "provider", label: "Provider", value: "openai", required: true, multiline: false, hint: "Subscription provider id, such as openai." },
881502
881598
  { id: "openBrowser", label: "Open browser", value: "yes", required: false, multiline: false, hint: "yes/no. Use no to print the authorization URL only." },
881503
- { id: "manual", label: "Manual completion", value: "yes", required: false, multiline: false, hint: "Agent always finishes subscription login through the explicit finish form." },
881504
- { id: "confirm", label: "Confirm", value: "", required: true, multiline: false, hint: "Type yes to run /subscription login <provider> start with --yes." }
881599
+ { id: "confirm", label: "Confirm", value: "", required: true, multiline: false, hint: "Type yes to save pending login state and open or show the authorization URL." }
881505
881600
  ]
881506
881601
  };
881507
881602
  }
@@ -881511,11 +881606,11 @@ function createAgentWorkspaceBasicCommandEditor(kind2) {
881511
881606
  mode: "update",
881512
881607
  title: "Finish Provider Subscription Login",
881513
881608
  selectedFieldIndex: 0,
881514
- message: "Finish a pending provider subscription OAuth login from a code or redirected URL. Type yes on the final field to confirm token storage.",
881609
+ message: "Finish a pending provider subscription OAuth login from a code or redirected URL and save the subscription session.",
881515
881610
  fields: [
881516
881611
  { id: "provider", label: "Provider", value: "openai", required: true, multiline: false, hint: "Provider id used when starting login." },
881517
881612
  { id: "code", label: "Code or redirect URL", value: "", required: true, multiline: false, hint: "OAuth code or full redirect URL containing code=..." },
881518
- { id: "confirm", label: "Confirm", value: "", required: true, multiline: false, hint: "Type yes to run /subscription login <provider> finish with --yes." }
881613
+ { id: "confirm", label: "Confirm", value: "", required: true, multiline: false, hint: "Type yes to exchange the code and save the provider subscription session." }
881519
881614
  ]
881520
881615
  };
881521
881616
  }
@@ -881525,10 +881620,10 @@ function createAgentWorkspaceBasicCommandEditor(kind2) {
881525
881620
  mode: "delete",
881526
881621
  title: "Logout Provider Subscription",
881527
881622
  selectedFieldIndex: 0,
881528
- message: "Remove one stored provider subscription session. Ambient API key resolution applies again if configured. Type yes to confirm.",
881623
+ message: "Remove one active or pending provider subscription session. Ambient API key resolution applies again if configured.",
881529
881624
  fields: [
881530
881625
  { id: "provider", label: "Provider", value: "openai", required: true, multiline: false, hint: "Stored subscription provider id." },
881531
- { id: "confirm", label: "Confirm", value: "", required: true, multiline: false, hint: "Type yes to run /subscription logout with --yes." }
881626
+ { id: "confirm", label: "Confirm", value: "", required: true, multiline: false, hint: "Type yes to remove active or pending subscription state." }
881532
881627
  ]
881533
881628
  };
881534
881629
  }
@@ -890312,6 +890407,14 @@ function activateAgentWorkspaceSelection(workspace, requestRender) {
890312
890407
  workspace.importTuiSettings(requestRender);
890313
890408
  return;
890314
890409
  }
890410
+ if (action2.kind === "model-picker") {
890411
+ workspace.openModelPickerAction(action2, requestRender);
890412
+ return;
890413
+ }
890414
+ if (action2.kind === "settings-modal") {
890415
+ workspace.openSettingsModalAction(action2, requestRender);
890416
+ return;
890417
+ }
890315
890418
  if (action2.kind === "editor" && action2.editorKind) {
890316
890419
  const editor = createAgentWorkspaceEditor(action2.editorKind, {
890317
890420
  runtimeStarterTemplates: workspace.runtimeSnapshot?.runtimeStarterTemplates ?? [],
@@ -890481,6 +890584,63 @@ function settingAction(options) {
890481
890584
  };
890482
890585
  }
890483
890586
 
890587
+ // src/input/agent-workspace-host-category.ts
890588
+ var AGENT_WORKSPACE_HOST_CATEGORY = {
890589
+ id: "host",
890590
+ group: "OPERATIONS",
890591
+ label: "Connected Host",
890592
+ summary: "Connected-host health, tasks, sessions, channels, and automation.",
890593
+ detail: "Use this workspace to inspect the GoodVibes host surfaces that Agent can see: system health, remote routes, host tasks, sessions, channels, schedules, knowledge, media, MCP, provider auth, support bundles, and telemetry/config posture.",
890594
+ actions: [
890595
+ { id: "host-overview", label: "Host overview", detail: "Review connected-host, provider, settings, continuity, and Agent health without starting or mutating host work.", command: "/health review", kind: "command", safety: "read-only" },
890596
+ { id: "host-services", label: "Host services", detail: "Inspect connected-host service readiness, credentials, and integration issues without changing them.", command: "/health host", kind: "command", safety: "read-only" },
890597
+ { id: "host-setup", label: "Setup review", detail: "Inspect setup issues that affect connected-host and Agent runtime readiness.", command: "/health setup", kind: "command", safety: "read-only" },
890598
+ { id: "host-remote", label: "Remote routes", detail: "Inspect remote build-host route health and recovery signals without starting build work.", command: "/health remote", kind: "command", safety: "read-only" },
890599
+ { id: "host-maintenance", label: "Session maintenance", detail: "Review continuity, compaction, and current-session maintenance posture.", command: "/health maintenance", kind: "command", safety: "read-only" },
890600
+ { id: "host-continuity", label: "Continuity", detail: "Inspect last-session pointer, recovery-file posture, and return-context state.", command: "/health continuity", kind: "command", safety: "read-only" },
890601
+ { id: "host-tasks", label: "Host tasks", detail: "Inspect connected-host task state without creating, retrying, or mutating tasks.", command: "/tasks list", kind: "command", safety: "read-only" },
890602
+ { id: "host-task-filter", label: "Filter host tasks", detail: "Open a status/type form for read-only connected-host task filtering.", editorKind: "task-list-filter", kind: "editor", safety: "read-only" },
890603
+ { id: "host-task-show", label: "Inspect host task", detail: "Open a task-id form for read-only connected-host task metadata.", editorKind: "task-show", kind: "editor", safety: "read-only" },
890604
+ { id: "host-task-output", label: "Show task output", detail: "Open a task-id form for read-only connected-host task output.", editorKind: "task-output", kind: "editor", safety: "read-only" },
890605
+ { id: "host-sessions", label: "Host session routes", detail: "Browse saved Agent sessions and return-context posture exposed through the runtime session surface.", command: "/session list", kind: "command", safety: "read-only" },
890606
+ { id: "host-session-graph", label: "Session graph", detail: "Open a read-only form for cross-session graph inspection; graph mutation remains blocked in Agent.", editorKind: "session-graph", kind: "editor", safety: "read-only" },
890607
+ { id: "host-channels-status", label: "Channel status", detail: "Inspect connected channel runtime status from the host without mutating delivery state.", command: "/channels status", kind: "command", safety: "read-only" },
890608
+ { id: "host-channel-accounts", label: "Channel accounts", detail: "Inspect connected channel accounts without printing secret values or sending messages.", command: "/channels accounts", kind: "command", safety: "read-only" },
890609
+ { id: "host-channel-policies", label: "Channel policies", detail: "Inspect channel delivery policy posture without changing routing.", command: "/channels policies", kind: "command", safety: "read-only" },
890610
+ { id: "host-channels", label: "Open Channels", detail: "Jump to the channel setup and delivery workspace.", targetCategoryId: "channels", kind: "workspace", safety: "safe" },
890611
+ { id: "host-automation", label: "Open Automation", detail: "Jump to connected schedules, reminders, receipts, and explicit run controls.", targetCategoryId: "automation", kind: "workspace", safety: "safe" },
890612
+ { id: "host-schedules", label: "Schedule status", detail: "Inspect schedules and run history without running or mutating them.", command: "/schedule list", kind: "command", safety: "read-only" },
890613
+ { id: "host-schedule-reconcile", label: "Reconcile schedules", detail: "Compare local promotion receipts with live connected schedules.", command: "/schedule reconcile", kind: "command", safety: "read-only" },
890614
+ { id: "host-knowledge", label: "Knowledge route", detail: "Inspect isolated Agent Knowledge route readiness and source counts.", command: "/knowledge status", kind: "command", safety: "read-only" },
890615
+ { id: "host-knowledge-open", label: "Open Knowledge", detail: "Jump to Agent Knowledge source, graph, review, and ingest controls.", targetCategoryId: "knowledge", kind: "workspace", safety: "safe" },
890616
+ { id: "host-media", label: "Media providers", detail: "Inspect configured media providers before generating media with confirmation.", command: "/media providers", kind: "command", safety: "read-only" },
890617
+ { id: "host-media-open", label: "Open Voice and Media", detail: "Jump to voice, TTS, image input, media provider, and browser-tool setup actions.", targetCategoryId: "voice-media", kind: "workspace", safety: "safe" },
890618
+ { id: "host-mcp-health", label: "MCP health", detail: "Inspect MCP lifecycle issues without changing trust posture or approving tool-definition review.", command: "/health mcp", kind: "command", safety: "read-only" },
890619
+ { id: "host-mcp-review", label: "MCP review", detail: "Inspect MCP server connection, trust, role, and review posture.", command: "/mcp review", kind: "command", safety: "read-only" },
890620
+ { id: "host-tools-open", label: "Open Tools and MCP", detail: "Jump to MCP server setup, tool inventory, secrets, and security review.", targetCategoryId: "tools", kind: "workspace", safety: "safe" },
890621
+ { id: "host-provider-accounts", label: "Provider accounts", detail: "Review provider account routes, subscription windows, and billing-path safety.", command: "/accounts review", kind: "command", safety: "read-only" },
890622
+ { id: "host-provider-detail", label: "Provider detail", detail: "Open a provider-id form for account and provider configuration review.", editorKind: "provider-inspect", kind: "editor", safety: "read-only" },
890623
+ { id: "host-provider-routes", label: "Provider routes", detail: "Open a provider-id form for account, subscription, and route inspection.", editorKind: "provider-routes", kind: "editor", safety: "read-only" },
890624
+ { id: "host-provider-repair", label: "Provider repair guidance", detail: "Open a provider-id form for read-only provider repair guidance.", editorKind: "provider-account-repair", kind: "editor", safety: "read-only" },
890625
+ { id: "host-auth-owner", label: "Connected-host auth owner", detail: "Show that connected-host auth administration belongs to the owning GoodVibes host, not Agent.", command: "/auth local", kind: "command", safety: "read-only" },
890626
+ { id: "host-auth-review", label: "Provider auth review", detail: "Review provider auth posture without printing token values.", command: "/auth review", kind: "command", safety: "read-only" },
890627
+ { id: "host-auth-detail", label: "Provider auth detail", detail: "Open a provider-id form for read-only provider auth inspection.", editorKind: "auth-show", kind: "editor", safety: "read-only" },
890628
+ { id: "host-auth-repair", label: "Provider auth repair", detail: "Open a provider-id form for provider auth repair guidance.", editorKind: "auth-repair", kind: "editor", safety: "read-only" },
890629
+ { id: "host-auth-bundle-export", label: "Export auth bundle", detail: "Open a confirmed form that exports a redacted provider auth review bundle.", editorKind: "auth-bundle-export", kind: "editor", safety: "safe" },
890630
+ { id: "host-auth-bundle-inspect", label: "Inspect auth bundle", detail: "Open a form that inspects a provider auth review bundle before sharing or import.", editorKind: "auth-bundle-inspect", kind: "editor", safety: "read-only" },
890631
+ { id: "host-subscription-bundle-export", label: "Export subscription bundle", detail: "Open a confirmed form that exports redacted provider subscription state for review.", editorKind: "subscription-bundle-export", kind: "editor", safety: "safe" },
890632
+ { id: "host-subscription-bundle-inspect", label: "Inspect subscription bundle", detail: "Open a form that inspects provider subscription state before sharing.", editorKind: "subscription-bundle-inspect", kind: "editor", safety: "read-only" },
890633
+ { id: "host-security", label: "Security review", detail: "Inspect token posture, MCP attack paths, policy lint, plugin risk, and incident pressure.", command: "/security review", kind: "command", safety: "read-only" },
890634
+ { id: "host-trust", label: "Trust review", detail: "Review permission, secret, plugin, and MCP trust posture without exporting bundles or changing trust.", command: "/trust review", kind: "command", safety: "read-only" },
890635
+ { id: "host-support-bundle-export", label: "Export support bundle", detail: "Export a redacted Agent support bundle from the Host page.", editorKind: "support-bundle-export", kind: "editor", safety: "safe" },
890636
+ { id: "host-support-bundle-inspect", label: "Inspect support bundle", detail: "Inspect a support bundle before import or sharing.", editorKind: "support-bundle-inspect", kind: "editor", safety: "read-only" },
890637
+ { id: "host-support-bundle-import", label: "Import support bundle", detail: "Import reviewed, non-redacted config values from a support bundle.", editorKind: "support-bundle-import", kind: "editor", safety: "safe" },
890638
+ { id: "host-telemetry", label: "Telemetry settings", detail: "Open telemetry payload policy settings for connected-host and Agent observability review.", command: "/config telemetry", kind: "command", safety: "safe" },
890639
+ { id: "host-config", label: "Control-plane settings", detail: "Open configuration controls for service, channels, tools, automation, provider, and storage posture.", command: "/config", kind: "command", safety: "safe" },
890640
+ { id: "host-safety", label: "Host mutation policy", detail: "Agent may inspect connected-host surfaces and run explicit confirmed actions, but does not silently create tasks, send channel messages, mutate auth, or start automation.", kind: "guidance", safety: "blocked" }
890641
+ ]
890642
+ };
890643
+
890484
890644
  // src/input/agent-workspace-categories.ts
890485
890645
  var AGENT_WORKSPACE_CATEGORIES = [
890486
890646
  {
@@ -890493,6 +890653,7 @@ var AGENT_WORKSPACE_CATEGORIES = [
890493
890653
  { id: "chat", label: "Continue assistant chat", detail: "Return to the main composer and type a normal message. Agent work stays serial in the main conversation.", kind: "guidance", safety: "safe" },
890494
890654
  { id: "brief", label: "Operator briefing", detail: "Show a concise Agent status and next-actions briefing without mutating the connected host.", command: "/brief", kind: "command", safety: "read-only" },
890495
890655
  { id: "model", label: "Choose model", detail: "Open the model/provider workspace for the Agent chat route.", command: "/model", kind: "command", safety: "safe" },
890656
+ { id: "model-refresh", label: "Refresh model catalog", detail: "Refresh locally cached provider model lists without changing the selected route.", command: "/refresh-models", kind: "command", safety: "safe" },
890496
890657
  { id: "setup-home", label: "Onboarding", detail: "Jump to first-run configuration for account, model, tools, channels, voice, local context, and finish.", targetCategoryId: "setup", kind: "workspace", safety: "safe" },
890497
890658
  { id: "channels-home", label: "Channels", detail: "Jump to companion pairing and channel readiness without changing connected-host routing.", targetCategoryId: "channels", kind: "workspace", safety: "read-only" },
890498
890659
  { id: "mode-show", label: "Interaction mode", detail: "Review Agent interaction noise level and per-domain verbosity.", command: "/mode show", kind: "command", safety: "read-only" },
@@ -890519,63 +890680,55 @@ var AGENT_WORKSPACE_CATEGORIES = [
890519
890680
  id: "setup",
890520
890681
  group: "ONBOARDING",
890521
890682
  label: "Start",
890522
- summary: "Import existing GoodVibes preferences, choose a main model, and sign in.",
890523
- detail: "Use this first. Every row either imports existing settings, edits a setting inline, or opens a concrete sign-in/provider form.",
890683
+ summary: "Import preferences, sign in, and choose the main model.",
890684
+ detail: "Start here on a fresh install. Every row either saves state, opens the shared model picker, or opens a confirmed in-modal form.",
890524
890685
  actions: [
890525
- { id: "import-goodvibes-tui-settings", label: "Import GoodVibes settings", detail: "Copy existing GoodVibes TUI display, provider, behavior, permission, TTS, channel, helper, tool, release, and automation values into Agent-owned settings.", kind: "settings-import", safety: "safe" },
890526
- settingAction({ id: "setup-provider-model", label: "Main model", detail: "Set the provider-qualified model used for normal assistant turns.", key: "provider.model" }),
890527
- settingAction({ id: "setup-reasoning-effort", label: "Reasoning effort", detail: "Cycle the reasoning level used when the selected model supports it.", key: "provider.reasoningEffort" }),
890528
- { id: "subscription-login-start", label: "Start subscription login", detail: "Open a confirmed form for one provider OAuth login start, with browser/manual completion controls.", editorKind: "subscription-login-start", kind: "editor", safety: "safe" },
890529
- { id: "subscription-login-finish", label: "Finish subscription login", detail: "Open a confirmed form that stores one completed provider subscription session from a code or redirect URL.", editorKind: "subscription-login-finish", kind: "editor", safety: "safe" },
890530
- { id: "subscription-logout", label: "Logout subscription", detail: "Open a confirmed form that removes one stored provider subscription session.", editorKind: "subscription-logout", kind: "editor", safety: "safe" },
890531
- { id: "subscription-inspect", label: "Inspect subscription route", detail: "Inspect one provider subscription OAuth route before starting or finishing login.", editorKind: "subscription-inspect", kind: "editor", safety: "read-only" },
890532
- { id: "subscription-bundle-export", label: "Export subscription bundle", detail: "Export a redacted provider subscription bundle for setup review.", editorKind: "subscription-bundle-export", kind: "editor", safety: "safe" },
890533
- { id: "subscription-bundle-inspect", label: "Inspect subscription bundle", detail: "Inspect a provider subscription bundle before setup review.", editorKind: "subscription-bundle-inspect", kind: "editor", safety: "read-only" },
890686
+ { id: "import-goodvibes-tui-settings", label: "Import GoodVibes settings", detail: "Copy existing display, provider, subscription, behavior, permission, TTS, channel, helper, tool, release, and automation values into Agent-owned state.", kind: "settings-import", safety: "safe" },
890687
+ { id: "subscription-login-start", label: "Start subscription login", detail: "Start one provider sign-in flow, save pending state, and return here.", editorKind: "subscription-login-start", kind: "editor", safety: "safe" },
890688
+ { id: "subscription-login-finish", label: "Finish subscription login", detail: "Exchange a code or redirect URL and save the provider subscription session.", editorKind: "subscription-login-finish", kind: "editor", safety: "safe" },
890689
+ { id: "setup-provider-model", label: "Choose main model", detail: "Open the shared provider/model picker for normal assistant turns.", kind: "model-picker", modelPickerFlow: "providerModel", modelPickerTarget: "main", safety: "safe" },
890690
+ settingAction({ id: "setup-reasoning-effort", label: "Reasoning effort", detail: "Cycle reasoning level when the selected model supports it.", key: "provider.reasoningEffort" }),
890691
+ { id: "subscription-logout", label: "Logout subscription", detail: "Remove one stored or pending provider subscription session after confirmation.", editorKind: "subscription-logout", kind: "editor", safety: "safe" },
890534
890692
  { id: "provider-add", label: "Add custom provider", detail: "Add one OpenAI-compatible provider for Agent model routing.", editorKind: "provider-add", kind: "editor", safety: "safe" },
890535
- { id: "secret-set-provider", label: "Store API secret", detail: "Store a provider or channel credential through the Agent secret manager.", editorKind: "secret-set", kind: "editor", safety: "safe" },
890536
- settingAction({ id: "setup-secret-policy", label: "Secret storage policy", detail: "Choose whether secret persistence prefers or requires secure storage.", key: "storage.secretPolicy" }),
890537
- settingAction({ id: "setup-save-history", label: "Save conversation history", detail: "Toggle local conversation history persistence.", key: "behavior.saveHistory" })
890693
+ { id: "secret-set-provider", label: "Store secret", detail: "Store a provider or channel credential through the Agent secret manager.", editorKind: "secret-set", kind: "editor", safety: "safe" },
890694
+ settingAction({ id: "setup-secret-policy", label: "Secret storage", detail: "Choose how strongly Agent prefers secure secret storage.", key: "storage.secretPolicy" }),
890695
+ settingAction({ id: "setup-save-history", label: "Save history", detail: "Toggle local conversation history persistence.", key: "behavior.saveHistory" })
890538
890696
  ]
890539
890697
  },
890540
890698
  {
890541
890699
  id: "account-model",
890542
890700
  group: "ONBOARDING",
890543
- label: "Account & Model",
890544
- summary: "Model routing for chat, helpers, tools, embeddings, and spoken turns.",
890545
- detail: "Use this after sign-in to make model routing explicit for each workload.",
890701
+ label: "Model Routing",
890702
+ summary: "Set chat, embedding, helper, tool, and spoken-turn routes.",
890703
+ detail: "Use this after sign-in. Routes are saved settings, so selections persist across launches.",
890546
890704
  actions: [
890547
- { id: "provider-use", label: "Switch provider", detail: "Open a provider-id form that switches the Agent chat provider.", editorKind: "provider-use", kind: "editor", safety: "safe" },
890548
- { id: "provider-inspect", label: "Inspect provider auth", detail: "Inspect one provider auth/setup route without storing tokens or starting login.", editorKind: "provider-inspect", kind: "editor", safety: "read-only" },
890549
- { id: "provider-routes", label: "Inspect provider routes", detail: "Inspect provider route and fallback posture for one provider.", editorKind: "provider-routes", kind: "editor", safety: "read-only" },
890550
- { id: "provider-account-repair", label: "Provider repair guidance", detail: "Show provider account repair guidance without mutating credentials.", editorKind: "provider-account-repair", kind: "editor", safety: "read-only" },
890551
- { id: "provider-remove", label: "Remove custom provider", detail: "Remove one Agent custom provider config after confirmation.", editorKind: "provider-remove", kind: "editor", safety: "safe" },
890552
- { id: "model-refresh", label: "Refresh model catalog", detail: "Refresh model catalog, benchmark, and token-limit metadata.", command: "/refresh-models", kind: "command", safety: "safe" },
890553
- { id: "model-pin", label: "Pin model", detail: "Pin one model registry key for quick reuse.", editorKind: "model-pin", kind: "editor", safety: "safe" },
890554
- { id: "model-unpin", label: "Unpin model", detail: "Remove one pinned model registry key.", editorKind: "model-unpin", kind: "editor", safety: "safe" },
890555
- { id: "auth-show", label: "Show provider auth", detail: "Inspect provider auth state without printing token values.", editorKind: "auth-show", kind: "editor", safety: "read-only" },
890556
- { id: "auth-repair", label: "Auth repair guidance", detail: "Show auth repair guidance without storing tokens or starting login.", editorKind: "auth-repair", kind: "editor", safety: "read-only" },
890557
- { id: "auth-bundle-export", label: "Export auth bundle", detail: "Export a redacted auth review bundle after confirmation.", editorKind: "auth-bundle-export", kind: "editor", safety: "safe" },
890558
- { id: "auth-bundle-inspect", label: "Inspect auth bundle", detail: "Inspect an auth review bundle before setup review.", editorKind: "auth-bundle-inspect", kind: "editor", safety: "read-only" },
890559
- settingAction({ id: "account-main-model", label: "Main model", detail: "Edit the provider-qualified model registry key for normal assistant turns.", key: "provider.model" }),
890560
- settingAction({ id: "account-reasoning", label: "Reasoning effort", detail: "Cycle instant, low, medium, or high reasoning effort.", key: "provider.reasoningEffort" }),
890561
- settingAction({ id: "account-embedding", label: "Embedding provider", detail: "Set the embedding provider used by local memory and retrieval.", key: "provider.embeddingProvider" }),
890705
+ { id: "provider-use", label: "Choose provider and model", detail: "Open the shared provider/model picker for the main chat route.", kind: "model-picker", modelPickerFlow: "providerModel", modelPickerTarget: "main", safety: "safe" },
890706
+ { id: "provider-remove", label: "Remove custom provider", detail: "Remove one custom provider config after confirmation.", editorKind: "provider-remove", kind: "editor", safety: "safe" },
890707
+ { id: "account-main-model", label: "Choose main model", detail: "Open the shared model picker for normal assistant turns.", kind: "model-picker", modelPickerFlow: "model", modelPickerTarget: "main", safety: "safe" },
890708
+ settingAction({ id: "account-reasoning", label: "Reasoning effort", detail: "Cycle the reasoning effort used when supported.", key: "provider.reasoningEffort" }),
890709
+ settingAction({ id: "account-embedding", label: "Embedding provider", detail: "Set the embedding provider for memory and retrieval.", key: "provider.embeddingProvider" }),
890562
890710
  settingAction({ id: "account-system-prompt", label: "System prompt file", detail: "Set an optional local system prompt file path.", key: "provider.systemPromptFile" }),
890563
890711
  settingAction({ id: "account-helper-enabled", label: "Enable helper model", detail: "Toggle dedicated helper routing for background utility work.", key: "helper.enabled" }),
890564
- settingAction({ id: "account-helper-provider", label: "Helper provider", detail: "Set the helper provider id.", key: "helper.globalProvider", visibleWhenKey: "helper.enabled", visibleWhenValue: true }),
890565
- settingAction({ id: "account-helper-model", label: "Helper model", detail: "Set the helper model registry key.", key: "helper.globalModel", visibleWhenKey: "helper.enabled", visibleWhenValue: true }),
890712
+ { id: "account-helper-provider", label: "Choose helper provider/model", detail: "Open the shared provider/model picker for helper routing.", kind: "model-picker", modelPickerFlow: "providerModel", modelPickerTarget: "helper", visibleWhenSettingKey: "helper.enabled", visibleWhenSettingValue: true, safety: "safe" },
890713
+ { id: "account-helper-model", label: "Choose helper model", detail: "Open the shared model picker for helper routing.", kind: "model-picker", modelPickerFlow: "model", modelPickerTarget: "helper", visibleWhenSettingKey: "helper.enabled", visibleWhenSettingValue: true, safety: "safe" },
890566
890714
  settingAction({ id: "account-tool-llm-enabled", label: "Enable tool LLM", detail: "Toggle a dedicated tool LLM for internal operations.", key: "tools.llmEnabled" }),
890567
- settingAction({ id: "account-tool-provider", label: "Tool LLM provider", detail: "Set the provider for dedicated tool LLM calls.", key: "tools.llmProvider", visibleWhenKey: "tools.llmEnabled", visibleWhenValue: true }),
890568
- settingAction({ id: "account-tool-model", label: "Tool LLM model", detail: "Set the model for dedicated tool LLM calls.", key: "tools.llmModel", visibleWhenKey: "tools.llmEnabled", visibleWhenValue: true }),
890569
- settingAction({ id: "account-tts-llm-provider", label: "Spoken-turn provider", detail: "Set an optional provider override for spoken-response turns.", key: "tts.llmProvider" }),
890570
- settingAction({ id: "account-tts-llm-model", label: "Spoken-turn model", detail: "Set an optional model override for spoken-response turns.", key: "tts.llmModel" })
890715
+ { id: "account-tool-provider", label: "Choose tool provider/model", detail: "Open the shared provider/model picker for dedicated tool LLM calls.", kind: "model-picker", modelPickerFlow: "providerModel", modelPickerTarget: "tool", visibleWhenSettingKey: "tools.llmEnabled", visibleWhenSettingValue: true, safety: "safe" },
890716
+ { id: "account-tool-model", label: "Choose tool model", detail: "Open the shared model picker for dedicated tool LLM calls.", kind: "model-picker", modelPickerFlow: "model", modelPickerTarget: "tool", visibleWhenSettingKey: "tools.llmEnabled", visibleWhenSettingValue: true, safety: "safe" },
890717
+ { id: "account-tts-llm-provider", label: "Choose spoken-turn provider/model", detail: "Open the shared provider/model picker for spoken-response turns.", kind: "model-picker", modelPickerFlow: "providerModel", modelPickerTarget: "tts", safety: "safe" },
890718
+ { id: "account-tts-llm-model", label: "Choose spoken-turn model", detail: "Open the shared model picker for spoken-response turns.", kind: "model-picker", modelPickerFlow: "model", modelPickerTarget: "tts", safety: "safe" },
890719
+ settingAction({ id: "account-provider-fallback-hint", label: "Provider failure hints", detail: "Toggle alternative model suggestions when the current provider fails non-transiently.", key: "behavior.suggestAlternativeOnProviderFail" }),
890720
+ settingAction({ id: "account-cache-enabled", label: "Prompt cache", detail: "Toggle prompt caching for eligible providers.", key: "cache.enabled" }),
890721
+ settingAction({ id: "account-cache-ttl", label: "Cache TTL", detail: "Cycle ephemeral or persistent cache TTL for stable content.", key: "cache.stableTtl", visibleWhenKey: "cache.enabled", visibleWhenValue: true }),
890722
+ settingAction({ id: "account-cache-monitor", label: "Cache hit-rate monitor", detail: "Toggle cache hit-rate monitoring.", key: "cache.monitorHitRate", visibleWhenKey: "cache.enabled", visibleWhenValue: true }),
890723
+ settingAction({ id: "account-cache-threshold", label: "Cache warning threshold", detail: "Set the cache hit-rate warning threshold.", key: "cache.hitRateWarningThreshold", visibleWhenKey: "cache.monitorHitRate", visibleWhenValue: true })
890571
890724
  ]
890572
890725
  },
890573
890726
  {
890574
890727
  id: "assistant-behavior",
890575
890728
  group: "ONBOARDING",
890576
- label: "Assistant Behavior",
890577
- summary: "Interaction, context, notifications, and reasoning display.",
890578
- detail: "Use this page to decide how much the assistant talks while it works and how it handles context pressure.",
890729
+ label: "Behavior",
890730
+ summary: "Set interaction style, context pressure behavior, and visible reasoning.",
890731
+ detail: "Use this to choose how chat feels during normal work and long-running tasks.",
890579
890732
  actions: [
890580
890733
  settingAction({ id: "behavior-hitl-mode", label: "Interaction mode", detail: "Cycle quiet, balanced, or operator interaction mode.", key: "behavior.hitlMode" }),
890581
890734
  settingAction({ id: "behavior-guidance-mode", label: "Guidance mode", detail: "Cycle off, minimal, or guided operational guidance.", key: "behavior.guidanceMode" }),
@@ -890591,28 +890744,41 @@ var AGENT_WORKSPACE_CATEGORIES = [
890591
890744
  id: "tools-permissions",
890592
890745
  group: "ONBOARDING",
890593
890746
  label: "Tools & Permissions",
890594
- summary: "Tool approval policy, MCP trust entry points, and secret handling.",
890595
- detail: "Use this page to set how Agent asks before file, shell, network, MCP, workflow, and delegation operations.",
890747
+ summary: "Set approval policy, MCP entry points, and secret references.",
890748
+ detail: "Use this before enabling tool-heavy work. Permission rows write policy; MCP and secret rows open setup forms.",
890596
890749
  actions: [
890597
890750
  settingAction({ id: "permissions-mode", label: "Permission mode", detail: "Cycle prompt, allow-all, or custom permission mode.", key: "permissions.mode" }),
890751
+ settingAction({ id: "permissions-auto-approve", label: "Auto-approve tools", detail: "Toggle automatic approval for all tool permission requests.", key: "behavior.autoApprove" }),
890752
+ settingAction({ id: "permissions-read", label: "File reads", detail: "Cycle allow, prompt, or deny for file read operations.", key: "permissions.tools.read" }),
890753
+ settingAction({ id: "permissions-find", label: "File search", detail: "Cycle allow, prompt, or deny for file and directory search operations.", key: "permissions.tools.find" }),
890598
890754
  settingAction({ id: "permissions-write", label: "File writes", detail: "Cycle allow, prompt, or deny for file write operations.", key: "permissions.tools.write" }),
890599
890755
  settingAction({ id: "permissions-edit", label: "File edits", detail: "Cycle allow, prompt, or deny for edit and patch operations.", key: "permissions.tools.edit" }),
890600
890756
  settingAction({ id: "permissions-exec", label: "Shell commands", detail: "Cycle allow, prompt, or deny for shell execution.", key: "permissions.tools.exec" }),
890601
890757
  settingAction({ id: "permissions-fetch", label: "Network fetches", detail: "Cycle allow, prompt, or deny for outbound fetch requests.", key: "permissions.tools.fetch" }),
890758
+ settingAction({ id: "permissions-analyze", label: "Code analysis", detail: "Cycle allow, prompt, or deny for code and project analysis operations.", key: "permissions.tools.analyze" }),
890759
+ settingAction({ id: "permissions-inspect", label: "Runtime inspect", detail: "Cycle allow, prompt, or deny for runtime state inspection.", key: "permissions.tools.inspect" }),
890760
+ settingAction({ id: "permissions-state", label: "State reads", detail: "Cycle allow, prompt, or deny for reading runtime and session state.", key: "permissions.tools.state" }),
890761
+ settingAction({ id: "permissions-registry", label: "Registry reads", detail: "Cycle allow, prompt, or deny for tool and skill registry queries.", key: "permissions.tools.registry" }),
890602
890762
  settingAction({ id: "permissions-mcp", label: "MCP tools", detail: "Cycle allow, prompt, or deny for external MCP tool calls.", key: "permissions.tools.mcp" }),
890603
890763
  settingAction({ id: "permissions-agent", label: "Agent delegation", detail: "Cycle allow, prompt, or deny for subagent and delegation operations.", key: "permissions.tools.agent" }),
890604
890764
  settingAction({ id: "permissions-workflow", label: "Workflow automation", detail: "Cycle allow, prompt, or deny for multi-step workflow automation.", key: "permissions.tools.workflow" }),
890765
+ settingAction({ id: "permissions-delegate", label: "Unknown tools", detail: "Cycle allow, prompt, or deny for unknown or unregistered tools.", key: "permissions.tools.delegate" }),
890605
890766
  { id: "onboarding-mcp-server", label: "Add MCP server", detail: "Add or update one MCP server with command, args, role, trust, env refs, paths, and hosts.", editorKind: "mcp-server", kind: "editor", safety: "safe" },
890606
890767
  { id: "onboarding-secret-link", label: "Link secret reference", detail: "Link one config key to a goodvibes secret reference.", editorKind: "secret-link", kind: "editor", safety: "safe" },
890607
- { id: "onboarding-secret-test", label: "Test secret reference", detail: "Check one stored secret reference without printing the value.", editorKind: "secret-test", kind: "editor", safety: "safe" }
890768
+ { id: "onboarding-secret-test", label: "Test secret reference", detail: "Check one stored secret reference without printing the value.", editorKind: "secret-test", kind: "editor", safety: "safe" },
890769
+ settingAction({ id: "tools-auto-heal", label: "Tool auto-heal", detail: "Toggle automatic syntax repair for precision write and edit operations.", key: "tools.autoHeal" }),
890770
+ settingAction({ id: "tools-token-budget", label: "Tool token budget", detail: "Set the default token budget for precision read operations.", key: "tools.defaultTokenBudget" }),
890771
+ settingAction({ id: "tools-hooks-file", label: "Hooks file", detail: "Set the hook configuration file name relative to host data.", key: "tools.hooksFile" }),
890772
+ settingAction({ id: "storage-artifact-limit", label: "Artifact storage limit", detail: "Set the maximum artifact size for file, URL, multipart, and raw upload ingest.", key: "storage.artifacts.maxBytes" }),
890773
+ settingAction({ id: "telemetry-raw-prompts", label: "Raw prompt telemetry", detail: "Toggle raw prompt and response telemetry; leave off unless debugging locally.", key: "telemetry.includeRawPrompts" })
890608
890774
  ]
890609
890775
  },
890610
890776
  {
890611
890777
  id: "onboarding-display",
890612
890778
  group: "ONBOARDING",
890613
890779
  label: "Interface",
890614
- summary: "Display, streaming, operational messages, and release channel.",
890615
- detail: "Use this page to make the terminal readable for normal daily work.",
890780
+ summary: "Display, streaming, message placement, and release channel.",
890781
+ detail: "Use this page to make the terminal readable for daily operation.",
890616
890782
  actions: [
890617
890783
  settingAction({ id: "display-stream", label: "Stream tokens", detail: "Toggle streaming assistant tokens as they arrive.", key: "display.stream" }),
890618
890784
  settingAction({ id: "display-theme", label: "Theme", detail: "Set the color theme name.", key: "display.theme" }),
@@ -890629,18 +890795,20 @@ var AGENT_WORKSPACE_CATEGORIES = [
890629
890795
  id: "onboarding-channels",
890630
890796
  group: "ONBOARDING",
890631
890797
  label: "Messaging",
890632
- summary: "Messaging surfaces and settings for enabled channels.",
890633
- detail: "Start by enabling the channels you want. Channel-specific fields appear below each enabled channel.",
890798
+ summary: "Choose remote channels, then fill only the fields for enabled channels.",
890799
+ detail: "Enable the channels you actually want. Channel credentials and default targets are saved as Agent settings or secret refs.",
890634
890800
  actions: [
890635
- { id: "onboarding-pair-companion", label: "Pair companion app", detail: "Open the QR pairing view for companion access.", command: "/pair", kind: "command", safety: "safe" },
890636
890801
  settingAction({ id: "channel-ntfy-enabled", label: "Use ntfy", detail: "Toggle ntfy notifications and chat routing.", key: "surfaces.ntfy.enabled" }),
890637
890802
  settingAction({ id: "channel-ntfy-base-url", label: "ntfy base URL", detail: "Set the ntfy server URL.", key: "surfaces.ntfy.baseUrl", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890638
890803
  settingAction({ id: "channel-ntfy-chat-topic", label: "ntfy chat topic", detail: "Set the topic routed into active terminal chat.", key: "surfaces.ntfy.chatTopic", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890639
890804
  settingAction({ id: "channel-ntfy-agent-topic", label: "ntfy agent topic", detail: "Set the topic routed to Agent work.", key: "surfaces.ntfy.agentTopic", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890805
+ settingAction({ id: "channel-ntfy-default-topic", label: "ntfy default topic", detail: "Set the default outbound notification topic.", key: "surfaces.ntfy.topic", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890806
+ settingAction({ id: "channel-ntfy-remote-topic", label: "ntfy remote topic", detail: "Set the daemon-owned remote chat topic.", key: "surfaces.ntfy.remoteTopic", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890640
890807
  settingAction({ id: "channel-ntfy-token", label: "ntfy token", detail: "Store the ntfy access token or secret reference.", key: "surfaces.ntfy.token", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890641
890808
  settingAction({ id: "channel-ntfy-priority", label: "ntfy priority", detail: "Set default ntfy priority from 1 to 5.", key: "surfaces.ntfy.defaultPriority", visibleWhenKey: "surfaces.ntfy.enabled", visibleWhenValue: true }),
890642
890809
  settingAction({ id: "channel-slack-enabled", label: "Use Slack", detail: "Toggle Slack adapter configuration.", key: "surfaces.slack.enabled" }),
890643
890810
  settingAction({ id: "channel-slack-bot-token", label: "Slack bot token", detail: "Store the Slack bot token or secret reference.", key: "surfaces.slack.botToken", visibleWhenKey: "surfaces.slack.enabled", visibleWhenValue: true }),
890811
+ settingAction({ id: "channel-slack-app-token", label: "Slack app token", detail: "Store the Slack app token or secret reference.", key: "surfaces.slack.appToken", visibleWhenKey: "surfaces.slack.enabled", visibleWhenValue: true }),
890644
890812
  settingAction({ id: "channel-slack-signing-secret", label: "Slack signing secret", detail: "Store the Slack signing secret or secret reference.", key: "surfaces.slack.signingSecret", visibleWhenKey: "surfaces.slack.enabled", visibleWhenValue: true }),
890645
890813
  settingAction({ id: "channel-slack-default-channel", label: "Slack default channel", detail: "Set the default Slack channel for notifications and replies.", key: "surfaces.slack.defaultChannel", visibleWhenKey: "surfaces.slack.enabled", visibleWhenValue: true }),
890646
890814
  settingAction({ id: "channel-slack-workspace", label: "Slack workspace ID", detail: "Set the Slack workspace id for route binding.", key: "surfaces.slack.workspaceId", visibleWhenKey: "surfaces.slack.enabled", visibleWhenValue: true }),
@@ -890648,49 +890816,67 @@ var AGENT_WORKSPACE_CATEGORIES = [
890648
890816
  settingAction({ id: "channel-discord-bot-token", label: "Discord bot token", detail: "Store the Discord bot token or secret reference.", key: "surfaces.discord.botToken", visibleWhenKey: "surfaces.discord.enabled", visibleWhenValue: true }),
890649
890817
  settingAction({ id: "channel-discord-application", label: "Discord application ID", detail: "Set the Discord application id.", key: "surfaces.discord.applicationId", visibleWhenKey: "surfaces.discord.enabled", visibleWhenValue: true }),
890650
890818
  settingAction({ id: "channel-discord-public-key", label: "Discord public key", detail: "Set the Discord application public key.", key: "surfaces.discord.publicKey", visibleWhenKey: "surfaces.discord.enabled", visibleWhenValue: true }),
890819
+ settingAction({ id: "channel-discord-guild", label: "Discord guild ID", detail: "Set the default Discord guild id.", key: "surfaces.discord.guildId", visibleWhenKey: "surfaces.discord.enabled", visibleWhenValue: true }),
890651
890820
  settingAction({ id: "channel-discord-default-channel", label: "Discord default channel", detail: "Set the default Discord channel id.", key: "surfaces.discord.defaultChannelId", visibleWhenKey: "surfaces.discord.enabled", visibleWhenValue: true }),
890652
890821
  settingAction({ id: "channel-webhook-enabled", label: "Use webhooks", detail: "Toggle generic webhook delivery.", key: "surfaces.webhook.enabled" }),
890653
890822
  settingAction({ id: "channel-webhook-target", label: "Webhook target URL", detail: "Set the default outbound webhook target URL.", key: "surfaces.webhook.defaultTarget", visibleWhenKey: "surfaces.webhook.enabled", visibleWhenValue: true }),
890654
890823
  settingAction({ id: "channel-webhook-secret", label: "Webhook secret", detail: "Store the shared webhook secret or secret reference.", key: "surfaces.webhook.secret", visibleWhenKey: "surfaces.webhook.enabled", visibleWhenValue: true }),
890824
+ settingAction({ id: "channel-webhook-timeout", label: "Webhook timeout", detail: "Set outbound webhook timeout in milliseconds.", key: "surfaces.webhook.timeoutMs", visibleWhenKey: "surfaces.webhook.enabled", visibleWhenValue: true }),
890655
890825
  settingAction({ id: "channel-telegram-enabled", label: "Use Telegram", detail: "Toggle Telegram bot delivery.", key: "surfaces.telegram.enabled" }),
890656
890826
  settingAction({ id: "channel-telegram-bot-token", label: "Telegram bot token", detail: "Store the Telegram bot token or secret reference.", key: "surfaces.telegram.botToken", visibleWhenKey: "surfaces.telegram.enabled", visibleWhenValue: true }),
890827
+ settingAction({ id: "channel-telegram-bot-username", label: "Telegram bot username", detail: "Set the Telegram bot username.", key: "surfaces.telegram.botUsername", visibleWhenKey: "surfaces.telegram.enabled", visibleWhenValue: true }),
890657
890828
  settingAction({ id: "channel-telegram-chat", label: "Telegram chat ID", detail: "Set the default Telegram chat, group, or channel id.", key: "surfaces.telegram.defaultChatId", visibleWhenKey: "surfaces.telegram.enabled", visibleWhenValue: true }),
890658
890829
  settingAction({ id: "channel-telegram-mode", label: "Telegram mode", detail: "Cycle webhook or polling ingress mode.", key: "surfaces.telegram.mode", visibleWhenKey: "surfaces.telegram.enabled", visibleWhenValue: true }),
890830
+ settingAction({ id: "channel-telegram-webhook-secret", label: "Telegram webhook secret", detail: "Store the Telegram webhook secret or secret reference.", key: "surfaces.telegram.webhookSecret", visibleWhenKey: "surfaces.telegram.enabled", visibleWhenValue: true }),
890659
890831
  settingAction({ id: "channel-googlechat-enabled", label: "Use Google Chat", detail: "Toggle Google Chat delivery.", key: "surfaces.googleChat.enabled" }),
890660
890832
  settingAction({ id: "channel-googlechat-webhook", label: "Google Chat webhook", detail: "Set the Google Chat webhook or app callback URL.", key: "surfaces.googleChat.webhookUrl", visibleWhenKey: "surfaces.googleChat.enabled", visibleWhenValue: true }),
890833
+ settingAction({ id: "channel-googlechat-space", label: "Google Chat space", detail: "Set the default Google Chat space id.", key: "surfaces.googleChat.spaceId", visibleWhenKey: "surfaces.googleChat.enabled", visibleWhenValue: true }),
890834
+ settingAction({ id: "channel-googlechat-app-id", label: "Google Chat app ID", detail: "Set the Google Chat app id.", key: "surfaces.googleChat.appId", visibleWhenKey: "surfaces.googleChat.enabled", visibleWhenValue: true }),
890835
+ settingAction({ id: "channel-googlechat-verification", label: "Google Chat verification", detail: "Store the Google Chat verification token or secret reference.", key: "surfaces.googleChat.verificationToken", visibleWhenKey: "surfaces.googleChat.enabled", visibleWhenValue: true }),
890661
890836
  settingAction({ id: "channel-homeassistant-enabled", label: "Use home automation", detail: "Toggle home automation conversation and event delivery.", key: "surfaces.homeassistant.enabled" }),
890662
890837
  settingAction({ id: "channel-homeassistant-url", label: "Home automation URL", detail: "Set the home automation instance URL.", key: "surfaces.homeassistant.instanceUrl", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890663
890838
  settingAction({ id: "channel-homeassistant-token", label: "Home automation token", detail: "Store the home automation access token or secret reference.", key: "surfaces.homeassistant.accessToken", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890839
+ settingAction({ id: "channel-homeassistant-webhook-secret", label: "Home automation webhook secret", detail: "Store the shared secret used to verify inbound callbacks.", key: "surfaces.homeassistant.webhookSecret", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890840
+ settingAction({ id: "channel-homeassistant-conversation", label: "Home automation conversation", detail: "Set the default conversation id.", key: "surfaces.homeassistant.defaultConversationId", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890841
+ settingAction({ id: "channel-homeassistant-device-id", label: "Home automation device ID", detail: "Set the stable device id exposed to the home automation surface.", key: "surfaces.homeassistant.deviceId", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890842
+ settingAction({ id: "channel-homeassistant-device-name", label: "Home automation device name", detail: "Set the device display name exposed to the home automation surface.", key: "surfaces.homeassistant.deviceName", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890843
+ settingAction({ id: "channel-homeassistant-event-type", label: "Home automation event type", detail: "Set the event type used for daemon-to-home automation delivery.", key: "surfaces.homeassistant.eventType", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890844
+ settingAction({ id: "channel-homeassistant-session-ttl", label: "Home automation session TTL", detail: "Set the idle TTL for remote home automation conversation sessions.", key: "surfaces.homeassistant.remoteSessionTtlMs", visibleWhenKey: "surfaces.homeassistant.enabled", visibleWhenValue: true }),
890664
890845
  settingAction({ id: "channel-signal-enabled", label: "Use Signal", detail: "Toggle Signal bridge delivery.", key: "surfaces.signal.enabled" }),
890665
890846
  settingAction({ id: "channel-signal-bridge", label: "Signal bridge URL", detail: "Set the Signal bridge base URL.", key: "surfaces.signal.bridgeUrl", visibleWhenKey: "surfaces.signal.enabled", visibleWhenValue: true }),
890847
+ settingAction({ id: "channel-signal-account", label: "Signal account", detail: "Set the Signal account id or phone number.", key: "surfaces.signal.account", visibleWhenKey: "surfaces.signal.enabled", visibleWhenValue: true }),
890848
+ settingAction({ id: "channel-signal-token", label: "Signal token", detail: "Store the Signal bridge token or secret reference.", key: "surfaces.signal.token", visibleWhenKey: "surfaces.signal.enabled", visibleWhenValue: true }),
890666
890849
  settingAction({ id: "channel-signal-recipient", label: "Signal recipient", detail: "Set the default Signal recipient or group.", key: "surfaces.signal.defaultRecipient", visibleWhenKey: "surfaces.signal.enabled", visibleWhenValue: true }),
890667
890850
  settingAction({ id: "channel-whatsapp-enabled", label: "Use WhatsApp", detail: "Toggle WhatsApp delivery.", key: "surfaces.whatsapp.enabled" }),
890668
890851
  settingAction({ id: "channel-whatsapp-provider", label: "WhatsApp provider", detail: "Cycle Meta Cloud API or bridge provider mode.", key: "surfaces.whatsapp.provider", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890669
890852
  settingAction({ id: "channel-whatsapp-token", label: "WhatsApp access token", detail: "Store the WhatsApp provider access token or secret reference.", key: "surfaces.whatsapp.accessToken", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890853
+ settingAction({ id: "channel-whatsapp-phone-id", label: "WhatsApp phone number ID", detail: "Set the WhatsApp phone number id.", key: "surfaces.whatsapp.phoneNumberId", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890854
+ settingAction({ id: "channel-whatsapp-business-id", label: "WhatsApp business ID", detail: "Set the WhatsApp business account id.", key: "surfaces.whatsapp.businessAccountId", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890855
+ settingAction({ id: "channel-whatsapp-verify", label: "WhatsApp verify token", detail: "Store the WhatsApp verify token or secret reference.", key: "surfaces.whatsapp.verifyToken", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890856
+ settingAction({ id: "channel-whatsapp-signing", label: "WhatsApp signing secret", detail: "Store the WhatsApp signing secret or secret reference.", key: "surfaces.whatsapp.signingSecret", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890670
890857
  settingAction({ id: "channel-whatsapp-recipient", label: "WhatsApp recipient", detail: "Set the default WhatsApp recipient or chat id.", key: "surfaces.whatsapp.defaultRecipient", visibleWhenKey: "surfaces.whatsapp.enabled", visibleWhenValue: true }),
890671
890858
  settingAction({ id: "channel-imessage-enabled", label: "Use iMessage", detail: "Toggle iMessage bridge delivery.", key: "surfaces.imessage.enabled" }),
890672
890859
  settingAction({ id: "channel-imessage-bridge", label: "iMessage bridge URL", detail: "Set the iMessage bridge base URL.", key: "surfaces.imessage.bridgeUrl", visibleWhenKey: "surfaces.imessage.enabled", visibleWhenValue: true }),
890673
- settingAction({ id: "channel-imessage-chat", label: "iMessage chat ID", detail: "Set the default iMessage chat id.", key: "surfaces.imessage.defaultChatId", visibleWhenKey: "surfaces.imessage.enabled", visibleWhenValue: true }),
890674
- { id: "onboarding-notify-webhook", label: "Add notification webhook", detail: "Add one webhook target for explicit reminder and routine delivery.", editorKind: "notify-webhook", kind: "editor", safety: "safe" },
890675
- { id: "onboarding-test-webhook", label: "Test notification webhooks", detail: "Send one confirmed test notification to configured webhook targets.", editorKind: "notify-webhook-test", kind: "editor", safety: "safe" }
890860
+ settingAction({ id: "channel-imessage-account", label: "iMessage account", detail: "Set the iMessage account id.", key: "surfaces.imessage.account", visibleWhenKey: "surfaces.imessage.enabled", visibleWhenValue: true }),
890861
+ settingAction({ id: "channel-imessage-token", label: "iMessage token", detail: "Store the iMessage bridge token or secret reference.", key: "surfaces.imessage.token", visibleWhenKey: "surfaces.imessage.enabled", visibleWhenValue: true }),
890862
+ settingAction({ id: "channel-imessage-chat", label: "iMessage chat ID", detail: "Set the default iMessage chat id.", key: "surfaces.imessage.defaultChatId", visibleWhenKey: "surfaces.imessage.enabled", visibleWhenValue: true })
890676
890863
  ]
890677
890864
  },
890678
890865
  {
890679
890866
  id: "onboarding-voice-media",
890680
890867
  group: "ONBOARDING",
890681
890868
  label: "Voice & Phone",
890682
- summary: "Configure spoken replies, TTS routing, media prompts, and telephony.",
890683
- detail: "Use this page for local voice, spoken-output defaults, image/media prompts, and SMS or phone-call delivery.",
890869
+ summary: "Configure voice controls, spoken output, and SMS or phone-call delivery.",
890870
+ detail: "Use this page for saved voice/TTS settings and telephony credentials. Generating media is available later from Voice & Media.",
890684
890871
  actions: [
890685
890872
  settingAction({ id: "voice-enabled", label: "Use voice controls", detail: "Toggle the optional local voice control surface.", key: "ui.voiceEnabled" }),
890686
890873
  settingAction({ id: "voice-tts-provider", label: "TTS provider", detail: "Set the default text-to-speech provider id.", key: "tts.provider" }),
890687
890874
  settingAction({ id: "voice-tts-voice", label: "TTS voice", detail: "Set the default text-to-speech voice id.", key: "tts.voice" }),
890688
- { id: "voice-speak-prompt", label: "Try spoken prompt", detail: "Open a prompt form for a spoken assistant reply through configured TTS.", editorKind: "tts-prompt", kind: "editor", safety: "safe" },
890689
- { id: "media-attach-image", label: "Attach image", detail: "Attach a local image path and optional prompt to the next assistant turn.", editorKind: "image-input", kind: "editor", safety: "safe" },
890690
- { id: "onboarding-media-generate", label: "Generate media", detail: "Generate image or video artifacts through a configured media provider after confirmation.", editorKind: "media-generate", kind: "editor", safety: "safe" },
890691
890875
  settingAction({ id: "telephony-enabled", label: "Use telephony", detail: "Toggle SMS, voice call, or telephony bridge delivery.", key: "surfaces.telephony.enabled" }),
890692
890876
  settingAction({ id: "telephony-provider", label: "Telephony provider", detail: "Cycle direct provider or bridge mode.", key: "surfaces.telephony.provider", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
890693
890877
  settingAction({ id: "telephony-mode", label: "Telephony mode", detail: "Cycle SMS, voice call, or bridge delivery mode.", key: "surfaces.telephony.mode", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
890878
+ settingAction({ id: "telephony-bridge-url", label: "Telephony bridge URL", detail: "Set the telephony bridge base URL.", key: "surfaces.telephony.bridgeUrl", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
890879
+ settingAction({ id: "telephony-token", label: "Telephony bridge token", detail: "Store the telephony bridge token or secret reference.", key: "surfaces.telephony.token", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
890694
890880
  settingAction({ id: "telephony-account-sid", label: "Twilio account SID", detail: "Set the Twilio account SID for provider-direct delivery.", key: "surfaces.telephony.accountSid", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
890695
890881
  settingAction({ id: "telephony-auth-token", label: "Twilio auth token", detail: "Store the Twilio auth token or secret reference.", key: "surfaces.telephony.authToken", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
890696
890882
  settingAction({ id: "telephony-from", label: "Sender phone number", detail: "Set the default caller or sender phone number.", key: "surfaces.telephony.fromNumber", visibleWhenKey: "surfaces.telephony.enabled", visibleWhenValue: true }),
@@ -890702,9 +890888,9 @@ var AGENT_WORKSPACE_CATEGORIES = [
890702
890888
  {
890703
890889
  id: "onboarding-context",
890704
890890
  group: "ONBOARDING",
890705
- label: "Context",
890706
- summary: "Local persona, memory, skills, routines, notes, and Agent Knowledge.",
890707
- detail: "Use this page to make the assistant useful with durable local context and source-backed knowledge.",
890891
+ label: "Local Context",
890892
+ summary: "Create/import memory, personas, skills, routines, notes, and Knowledge.",
890893
+ detail: "Use this page to seed the assistant with durable local context. These forms create Agent-owned records or ingest reviewed sources.",
890708
890894
  actions: [
890709
890895
  { id: "context-profile-from-discovered", label: "Profile from discovered files", detail: "Create an isolated Agent profile from reviewed local persona, skill, and routine files.", editorKind: "profile-from-discovered", kind: "editor", safety: "safe" },
890710
890896
  { id: "context-persona-discovery", label: "Import persona files", detail: "Import discovered persona files into the Agent persona registry.", editorKind: "persona-discovery-import", kind: "editor", safety: "safe" },
@@ -890722,20 +890908,19 @@ var AGENT_WORKSPACE_CATEGORIES = [
890722
890908
  ]
890723
890909
  },
890724
890910
  {
890725
- id: "onboarding-verify",
890911
+ id: "onboarding-automation",
890726
890912
  group: "ONBOARDING",
890727
- label: "Verify",
890728
- summary: "Review the configured runtime before finishing onboarding.",
890729
- detail: "Use this page to inspect health, model/provider state, channels, MCP, and knowledge before applying the completion marker.",
890913
+ label: "Automation Setup",
890914
+ summary: "Set scheduled work limits for reminders and routines.",
890915
+ detail: "Use this page to enable automation and set bounded run limits. Creating actual reminders and routines remains a separate confirmed action.",
890730
890916
  actions: [
890731
- { id: "verify-brief", label: "Operator briefing", detail: "Show a concise status and next-action briefing.", command: "/brief", kind: "command", safety: "read-only" },
890732
- { id: "verify-health", label: "Health review", detail: "Review local runtime and connected-host status.", command: "/health review", kind: "command", safety: "read-only" },
890733
- { id: "verify-compat", label: "Compatibility", detail: "Inspect connected-host compatibility and Agent Knowledge route readiness.", command: "/compat", kind: "command", safety: "read-only" },
890734
- { id: "verify-provider", label: "Provider status", detail: "Inspect provider/model routing state.", command: "/provider", kind: "command", safety: "read-only" },
890735
- { id: "verify-subscription", label: "Subscription status", detail: "Inspect active and pending provider subscription sessions.", command: "/subscription review", kind: "command", safety: "read-only" },
890736
- { id: "verify-channels", label: "Channel readiness", detail: "Inspect channel readiness and enabled-channel attention items.", command: "/channels attention", kind: "command", safety: "read-only" },
890737
- { id: "verify-mcp", label: "MCP review", detail: "Inspect MCP server connection, role, trust, and quarantine posture.", command: "/mcp review", kind: "command", safety: "read-only" },
890738
- { id: "verify-knowledge", label: "Knowledge status", detail: "Inspect isolated Agent Knowledge readiness and counts.", command: "/knowledge status", kind: "command", safety: "read-only" }
890917
+ settingAction({ id: "automation-enabled", label: "Use automation", detail: "Toggle the automation subsystem for reminders and scheduled routines.", key: "automation.enabled" }),
890918
+ settingAction({ id: "automation-max-concurrent", label: "Concurrent runs", detail: "Set the maximum automation runs that can execute concurrently.", key: "automation.maxConcurrentRuns", visibleWhenKey: "automation.enabled", visibleWhenValue: true }),
890919
+ settingAction({ id: "automation-history-limit", label: "Run history limit", detail: "Set the number of run history entries retained per automation job.", key: "automation.runHistoryLimit", visibleWhenKey: "automation.enabled", visibleWhenValue: true }),
890920
+ settingAction({ id: "automation-default-timeout", label: "Default timeout", detail: "Set the default automation run timeout in milliseconds.", key: "automation.defaultTimeoutMs", visibleWhenKey: "automation.enabled", visibleWhenValue: true }),
890921
+ settingAction({ id: "automation-catch-up", label: "Catch-up window", detail: "Set how long startup should catch up missed automation runs.", key: "automation.catchUpWindowMinutes", visibleWhenKey: "automation.enabled", visibleWhenValue: true }),
890922
+ settingAction({ id: "automation-failure-cooldown", label: "Failure cooldown", detail: "Set the cooldown after a failed automation run.", key: "automation.failureCooldownMs", visibleWhenKey: "automation.enabled", visibleWhenValue: true }),
890923
+ settingAction({ id: "automation-delete-after-run", label: "Delete one-shot jobs", detail: "Toggle deleting one-shot automation jobs after their first successful run.", key: "automation.deleteAfterRun", visibleWhenKey: "automation.enabled", visibleWhenValue: true })
890739
890924
  ]
890740
890925
  },
890741
890926
  {
@@ -891171,52 +891356,7 @@ var AGENT_WORKSPACE_CATEGORIES = [
891171
891356
  { id: "approval-cancel", label: "Cancel request", detail: "Open a confirmed form for cancelling one pending connected-host approval request by id.", editorKind: "approval-cancel", kind: "editor", safety: "safe" }
891172
891357
  ]
891173
891358
  },
891174
- {
891175
- id: "host",
891176
- group: "OPERATIONS",
891177
- label: "Connected Host",
891178
- summary: "Connected-host health, tasks, sessions, channels, and automation.",
891179
- detail: "Use this workspace to inspect the GoodVibes host surfaces that Agent can see: system health, remote routes, host tasks, sessions, channels, schedules, knowledge, media, MCP, provider auth, support bundles, and telemetry/config posture.",
891180
- actions: [
891181
- { id: "host-overview", label: "Host overview", detail: "Review connected-host, provider, settings, continuity, and Agent health without starting or mutating host work.", command: "/health review", kind: "command", safety: "read-only" },
891182
- { id: "host-services", label: "Host services", detail: "Inspect connected-host service readiness, credentials, and integration issues without changing them.", command: "/health host", kind: "command", safety: "read-only" },
891183
- { id: "host-setup", label: "Setup review", detail: "Inspect setup issues that affect connected-host and Agent runtime readiness.", command: "/health setup", kind: "command", safety: "read-only" },
891184
- { id: "host-remote", label: "Remote routes", detail: "Inspect remote build-host route health and recovery signals without starting build work.", command: "/health remote", kind: "command", safety: "read-only" },
891185
- { id: "host-maintenance", label: "Session maintenance", detail: "Review continuity, compaction, and current-session maintenance posture.", command: "/health maintenance", kind: "command", safety: "read-only" },
891186
- { id: "host-continuity", label: "Continuity", detail: "Inspect last-session pointer, recovery-file posture, and return-context state.", command: "/health continuity", kind: "command", safety: "read-only" },
891187
- { id: "host-tasks", label: "Host tasks", detail: "Inspect connected-host task state without creating, retrying, or mutating tasks.", command: "/tasks list", kind: "command", safety: "read-only" },
891188
- { id: "host-task-filter", label: "Filter host tasks", detail: "Open a status/type form for read-only connected-host task filtering.", editorKind: "task-list-filter", kind: "editor", safety: "read-only" },
891189
- { id: "host-task-show", label: "Inspect host task", detail: "Open a task-id form for read-only connected-host task metadata.", editorKind: "task-show", kind: "editor", safety: "read-only" },
891190
- { id: "host-task-output", label: "Show task output", detail: "Open a task-id form for read-only connected-host task output.", editorKind: "task-output", kind: "editor", safety: "read-only" },
891191
- { id: "host-sessions", label: "Host session routes", detail: "Browse saved Agent sessions and return-context posture exposed through the runtime session surface.", command: "/session list", kind: "command", safety: "read-only" },
891192
- { id: "host-session-graph", label: "Session graph", detail: "Open a read-only form for cross-session graph inspection; graph mutation remains blocked in Agent.", editorKind: "session-graph", kind: "editor", safety: "read-only" },
891193
- { id: "host-channels-status", label: "Channel status", detail: "Inspect connected channel runtime status from the host without mutating delivery state.", command: "/channels status", kind: "command", safety: "read-only" },
891194
- { id: "host-channel-accounts", label: "Channel accounts", detail: "Inspect connected channel accounts without printing secret values or sending messages.", command: "/channels accounts", kind: "command", safety: "read-only" },
891195
- { id: "host-channel-policies", label: "Channel policies", detail: "Inspect channel delivery policy posture without changing routing.", command: "/channels policies", kind: "command", safety: "read-only" },
891196
- { id: "host-channels", label: "Open Channels", detail: "Jump to the channel setup and delivery workspace.", targetCategoryId: "channels", kind: "workspace", safety: "safe" },
891197
- { id: "host-automation", label: "Open Automation", detail: "Jump to connected schedules, reminders, receipts, and explicit run controls.", targetCategoryId: "automation", kind: "workspace", safety: "safe" },
891198
- { id: "host-schedules", label: "Schedule status", detail: "Inspect schedules and run history without running or mutating them.", command: "/schedule list", kind: "command", safety: "read-only" },
891199
- { id: "host-schedule-reconcile", label: "Reconcile schedules", detail: "Compare local promotion receipts with live connected schedules.", command: "/schedule reconcile", kind: "command", safety: "read-only" },
891200
- { id: "host-knowledge", label: "Knowledge route", detail: "Inspect isolated Agent Knowledge route readiness and source counts.", command: "/knowledge status", kind: "command", safety: "read-only" },
891201
- { id: "host-knowledge-open", label: "Open Knowledge", detail: "Jump to Agent Knowledge source, graph, review, and ingest controls.", targetCategoryId: "knowledge", kind: "workspace", safety: "safe" },
891202
- { id: "host-media", label: "Media providers", detail: "Inspect configured media providers before generating media with confirmation.", command: "/media providers", kind: "command", safety: "read-only" },
891203
- { id: "host-media-open", label: "Open Voice and Media", detail: "Jump to voice, TTS, image input, media provider, and browser-tool setup actions.", targetCategoryId: "voice-media", kind: "workspace", safety: "safe" },
891204
- { id: "host-mcp-health", label: "MCP health", detail: "Inspect MCP lifecycle issues without changing trust posture or approving tool-definition review.", command: "/health mcp", kind: "command", safety: "read-only" },
891205
- { id: "host-mcp-review", label: "MCP review", detail: "Inspect MCP server connection, trust, role, and review posture.", command: "/mcp review", kind: "command", safety: "read-only" },
891206
- { id: "host-tools-open", label: "Open Tools and MCP", detail: "Jump to MCP server setup, tool inventory, secrets, and security review.", targetCategoryId: "tools", kind: "workspace", safety: "safe" },
891207
- { id: "host-provider-accounts", label: "Provider accounts", detail: "Review provider account routes, subscription windows, and billing-path safety.", command: "/accounts review", kind: "command", safety: "read-only" },
891208
- { id: "host-auth-owner", label: "Connected-host auth owner", detail: "Show that connected-host auth administration belongs to the owning GoodVibes host, not Agent.", command: "/auth local", kind: "command", safety: "read-only" },
891209
- { id: "host-auth-review", label: "Provider auth review", detail: "Review provider auth posture without printing token values.", command: "/auth review", kind: "command", safety: "read-only" },
891210
- { id: "host-security", label: "Security review", detail: "Inspect token posture, MCP attack paths, policy lint, plugin risk, and incident pressure.", command: "/security review", kind: "command", safety: "read-only" },
891211
- { id: "host-trust", label: "Trust review", detail: "Review permission, secret, plugin, and MCP trust posture without exporting bundles or changing trust.", command: "/trust review", kind: "command", safety: "read-only" },
891212
- { id: "host-support-bundle-export", label: "Export support bundle", detail: "Export a redacted Agent support bundle from the Host page.", editorKind: "support-bundle-export", kind: "editor", safety: "safe" },
891213
- { id: "host-support-bundle-inspect", label: "Inspect support bundle", detail: "Inspect a support bundle before import or sharing.", editorKind: "support-bundle-inspect", kind: "editor", safety: "read-only" },
891214
- { id: "host-support-bundle-import", label: "Import support bundle", detail: "Import reviewed, non-redacted config values from a support bundle.", editorKind: "support-bundle-import", kind: "editor", safety: "safe" },
891215
- { id: "host-telemetry", label: "Telemetry settings", detail: "Open telemetry payload policy settings for connected-host and Agent observability review.", command: "/config telemetry", kind: "command", safety: "safe" },
891216
- { id: "host-config", label: "Control-plane settings", detail: "Open configuration controls for service, channels, tools, automation, provider, and storage posture.", command: "/config", kind: "command", safety: "safe" },
891217
- { id: "host-safety", label: "Host mutation policy", detail: "Agent may inspect connected-host surfaces and run explicit confirmed actions, but does not silently create tasks, send channel messages, mutate auth, or start automation.", kind: "guidance", safety: "blocked" }
891218
- ]
891219
- },
891359
+ AGENT_WORKSPACE_HOST_CATEGORY,
891220
891360
  {
891221
891361
  id: "automation",
891222
891362
  group: "OPERATIONS",
@@ -891287,6 +891427,9 @@ function searchAgentWorkspaceActions(categories, query2) {
891287
891427
  action2.command ?? "",
891288
891428
  action2.editorKind ?? "",
891289
891429
  action2.targetCategoryId ?? "",
891430
+ action2.modelPickerFlow ?? "",
891431
+ action2.modelPickerTarget ?? "",
891432
+ action2.settingsTarget ?? "",
891290
891433
  action2.localOperation ?? "",
891291
891434
  action2.safety
891292
891435
  ].join(" ").toLowerCase();
@@ -891317,6 +891460,9 @@ function scoreActionSearchResult(result2, exactQuery, terms) {
891317
891460
  score += scoreField(action2.editorKind, terms, exactQuery, 85, 26);
891318
891461
  score += scoreField(action2.command, terms, exactQuery, 75, 22);
891319
891462
  score += scoreField(action2.label, terms, exactQuery, 65, 18);
891463
+ score += scoreField(action2.modelPickerTarget, terms, exactQuery, 58, 18);
891464
+ score += scoreField(action2.settingsTarget, terms, exactQuery, 58, 18);
891465
+ score += scoreField(action2.modelPickerFlow, terms, exactQuery, 54, 16);
891320
891466
  score += scoreField(action2.localOperation, terms, exactQuery, 50, 16);
891321
891467
  score += scoreField(action2.targetCategoryId, terms, exactQuery, 40, 12);
891322
891468
  score += scoreField(action2.detail, terms, exactQuery, 24, 6);
@@ -895422,6 +895568,163 @@ function handleMcpWorkspaceToken(workspace, token, handleEscape2, requestRender)
895422
895568
  return true;
895423
895569
  }
895424
895570
 
895571
+ // src/input/agent-workspace-requirements.ts
895572
+ function buildAgentWorkspaceRequirements(readField) {
895573
+ return buildAgentSkillRequirements({
895574
+ env: splitList5(readField("requiresEnv")),
895575
+ commands: splitList5(readField("requiresCommands"))
895576
+ });
895577
+ }
895578
+
895579
+ // src/input/agent-workspace-local-editor-submission.ts
895580
+ function submitAgentWorkspaceLocalRegistryEditor(shellPaths3, editor, callbacks) {
895581
+ const field = callbacks.readField;
895582
+ if (editor.mode === "delete") {
895583
+ callbacks.submitDeleteEditor();
895584
+ return;
895585
+ }
895586
+ if (editor.kind === "learned-behavior") {
895587
+ const created = createAgentWorkspaceLearnedBehavior(shellPaths3, {
895588
+ target: callbacks.learnedBehaviorTarget(),
895589
+ name: field("name"),
895590
+ description: field("description"),
895591
+ notes: field("notes"),
895592
+ tags: splitList5(field("tags")),
895593
+ triggers: splitList5(field("triggers")),
895594
+ enable: isAffirmative15(field("enable"))
895595
+ });
895596
+ callbacks.finishLocalEditor(created.kind, created.id, created.name, "Created");
895597
+ } else if (editor.kind === "profile") {
895598
+ const template = field("template");
895599
+ const templateId = template && template.toLowerCase() !== "none" ? template : undefined;
895600
+ const profile5 = createAgentRuntimeProfile(shellPaths3.homeDirectory, field("name"), {
895601
+ ...templateId ? { templateId } : {}
895602
+ });
895603
+ callbacks.finishProfileEditor(profile5);
895604
+ } else if (editor.kind === "note") {
895605
+ submitNoteEditor(shellPaths3, editor, field, callbacks.finishLocalEditor);
895606
+ } else if (editor.kind === "persona") {
895607
+ submitPersonaEditor(shellPaths3, editor, field, callbacks.finishLocalEditor);
895608
+ } else if (editor.kind === "skill") {
895609
+ submitSkillEditor(shellPaths3, editor, field, callbacks.finishLocalEditor);
895610
+ } else {
895611
+ submitRoutineEditor(shellPaths3, editor, field, callbacks.finishLocalEditor);
895612
+ }
895613
+ }
895614
+ function submitNoteEditor(shellPaths3, editor, field, finish) {
895615
+ const registry5 = AgentNoteRegistry.fromShellPaths(shellPaths3);
895616
+ if (editor.mode === "update" && editor.recordId) {
895617
+ const updated = registry5.update(editor.recordId, {
895618
+ title: field("title"),
895619
+ body: field("body"),
895620
+ sourceUrl: field("sourceUrl"),
895621
+ tags: splitList5(field("tags")),
895622
+ provenance: "Workspace"
895623
+ });
895624
+ finish("note", updated.id, updated.title, "Updated");
895625
+ return;
895626
+ }
895627
+ const created = registry5.create({
895628
+ title: field("title"),
895629
+ body: field("body"),
895630
+ sourceUrl: field("sourceUrl"),
895631
+ tags: splitList5(field("tags")),
895632
+ source: "user",
895633
+ provenance: "Workspace"
895634
+ });
895635
+ finish("note", created.id, created.title, "Created");
895636
+ }
895637
+ function submitPersonaEditor(shellPaths3, editor, field, finish) {
895638
+ const registry5 = AgentPersonaRegistry.fromShellPaths(shellPaths3);
895639
+ if (editor.mode === "update" && editor.recordId) {
895640
+ const wasActive = registry5.snapshot().activePersonaId === editor.recordId;
895641
+ const updated = registry5.update(editor.recordId, {
895642
+ name: field("name"),
895643
+ description: field("description"),
895644
+ body: field("body"),
895645
+ tags: splitList5(field("tags")),
895646
+ triggers: splitList5(field("triggers")),
895647
+ provenance: "Workspace"
895648
+ });
895649
+ if (isAffirmative15(field("activate")))
895650
+ registry5.setActive(updated.id);
895651
+ else if (wasActive)
895652
+ registry5.clearActive();
895653
+ finish("persona", updated.id, updated.name, "Updated");
895654
+ return;
895655
+ }
895656
+ const created = registry5.create({
895657
+ name: field("name"),
895658
+ description: field("description"),
895659
+ body: field("body"),
895660
+ tags: splitList5(field("tags")),
895661
+ triggers: splitList5(field("triggers")),
895662
+ source: "user",
895663
+ provenance: "Workspace"
895664
+ });
895665
+ if (isAffirmative15(field("activate")))
895666
+ registry5.setActive(created.id);
895667
+ finish("persona", created.id, created.name, "Created");
895668
+ }
895669
+ function submitSkillEditor(shellPaths3, editor, field, finish) {
895670
+ const registry5 = AgentSkillRegistry.fromShellPaths(shellPaths3);
895671
+ if (editor.mode === "update" && editor.recordId) {
895672
+ const updated = registry5.update(editor.recordId, {
895673
+ name: field("name"),
895674
+ description: field("description"),
895675
+ procedure: field("procedure"),
895676
+ triggers: splitList5(field("triggers")),
895677
+ tags: splitList5(field("tags")),
895678
+ requirements: buildAgentWorkspaceRequirements(field),
895679
+ provenance: "Workspace"
895680
+ });
895681
+ registry5.setEnabled(updated.id, isAffirmative15(field("enabled")));
895682
+ finish("skill", updated.id, updated.name, "Updated");
895683
+ return;
895684
+ }
895685
+ const created = registry5.create({
895686
+ name: field("name"),
895687
+ description: field("description"),
895688
+ procedure: field("procedure"),
895689
+ triggers: splitList5(field("triggers")),
895690
+ tags: splitList5(field("tags")),
895691
+ requirements: buildAgentWorkspaceRequirements(field),
895692
+ enabled: isAffirmative15(field("enabled")),
895693
+ source: "user",
895694
+ provenance: "Workspace"
895695
+ });
895696
+ finish("skill", created.id, created.name, "Created");
895697
+ }
895698
+ function submitRoutineEditor(shellPaths3, editor, field, finish) {
895699
+ const registry5 = AgentRoutineRegistry.fromShellPaths(shellPaths3);
895700
+ if (editor.mode === "update" && editor.recordId) {
895701
+ const updated = registry5.update(editor.recordId, {
895702
+ name: field("name"),
895703
+ description: field("description"),
895704
+ steps: field("steps"),
895705
+ triggers: splitList5(field("triggers")),
895706
+ tags: splitList5(field("tags")),
895707
+ requirements: buildAgentWorkspaceRequirements(field),
895708
+ provenance: "Workspace"
895709
+ });
895710
+ registry5.setEnabled(updated.id, isAffirmative15(field("enabled")));
895711
+ finish("routine", updated.id, updated.name, "Updated");
895712
+ return;
895713
+ }
895714
+ const created = registry5.create({
895715
+ name: field("name"),
895716
+ description: field("description"),
895717
+ steps: field("steps"),
895718
+ triggers: splitList5(field("triggers")),
895719
+ tags: splitList5(field("tags")),
895720
+ requirements: buildAgentWorkspaceRequirements(field),
895721
+ enabled: isAffirmative15(field("enabled")),
895722
+ source: "user",
895723
+ provenance: "Workspace"
895724
+ });
895725
+ finish("routine", created.id, created.name, "Created");
895726
+ }
895727
+
895425
895728
  // src/input/agent-workspace-local-selection.ts
895426
895729
  function agentWorkspaceLocalLibraryItems(snapshot2, kind2) {
895427
895730
  if (kind2 === "memory")
@@ -895782,14 +896085,6 @@ function selectAgentWorkspaceCategory(host, categoryIdOrLabel) {
895782
896085
  return true;
895783
896086
  }
895784
896087
 
895785
- // src/input/agent-workspace-requirements.ts
895786
- function buildAgentWorkspaceRequirements(readField) {
895787
- return buildAgentSkillRequirements({
895788
- env: splitList5(readField("requiresEnv")),
895789
- commands: splitList5(readField("requiresCommands"))
895790
- });
895791
- }
895792
-
895793
896088
  // src/input/agent-workspace-settings.ts
895794
896089
  import { existsSync as existsSync87, readFileSync as readFileSync94 } from "fs";
895795
896090
  var GOODVIBES_TUI_SURFACE_ROOT = "tui";
@@ -895831,6 +896126,62 @@ function canImportTuiSetting(key) {
895831
896126
  function valuesMatch(left, right) {
895832
896127
  return JSON.stringify(left) === JSON.stringify(right);
895833
896128
  }
896129
+ function readRecordMap(record2, key) {
896130
+ const value = record2[key];
896131
+ return isRecord45(value) ? Object.values(value) : [];
896132
+ }
896133
+ function isProviderSubscription(value) {
896134
+ return isRecord45(value) && typeof value.provider === "string" && typeof value.accessToken === "string" && typeof value.tokenType === "string" && value.authMode === "oauth" && typeof value.overrideAmbientApiKeys === "boolean" && typeof value.createdAt === "number" && typeof value.updatedAt === "number";
896135
+ }
896136
+ function isPendingSubscriptionLogin(value) {
896137
+ return isRecord45(value) && typeof value.provider === "string" && typeof value.state === "string" && typeof value.verifier === "string" && typeof value.redirectUri === "string" && typeof value.createdAt === "number";
896138
+ }
896139
+ function importTuiSubscriptions(context, parseErrors) {
896140
+ const shellPaths3 = context.workspace.shellPaths;
896141
+ const manager5 = context.platform.subscriptionManager;
896142
+ const result2 = {
896143
+ importedActive: [],
896144
+ importedPending: [],
896145
+ unchangedActive: [],
896146
+ unchangedPending: [],
896147
+ skipped: []
896148
+ };
896149
+ if (!shellPaths3 || !manager5)
896150
+ return result2;
896151
+ const sourcePath = shellPaths3.resolveUserPath(GOODVIBES_TUI_SURFACE_ROOT, "subscriptions.json");
896152
+ try {
896153
+ const store2 = readJsonRecord(sourcePath);
896154
+ if (!store2)
896155
+ return result2;
896156
+ for (const entry of readRecordMap(store2, "subscriptions")) {
896157
+ if (!isProviderSubscription(entry)) {
896158
+ result2.skipped.push("active subscription with invalid shape");
896159
+ continue;
896160
+ }
896161
+ if (valuesMatch(manager5.get(entry.provider), entry)) {
896162
+ result2.unchangedActive.push(entry.provider);
896163
+ continue;
896164
+ }
896165
+ manager5.saveSubscription(entry);
896166
+ result2.importedActive.push(entry.provider);
896167
+ }
896168
+ for (const entry of readRecordMap(store2, "pending")) {
896169
+ if (!isPendingSubscriptionLogin(entry)) {
896170
+ result2.skipped.push("pending subscription with invalid shape");
896171
+ continue;
896172
+ }
896173
+ if (valuesMatch(manager5.getPending(entry.provider), entry)) {
896174
+ result2.unchangedPending.push(entry.provider);
896175
+ continue;
896176
+ }
896177
+ manager5.savePending(entry);
896178
+ result2.importedPending.push(entry.provider);
896179
+ }
896180
+ } catch (error53) {
896181
+ parseErrors.push(`subscriptions: ${error53 instanceof Error ? error53.message : String(error53)}`);
896182
+ }
896183
+ return result2;
896184
+ }
895834
896185
  function agentWorkspaceSettingSchema(context, key) {
895835
896186
  return context?.platform?.configManager?.getSchema().find((setting) => setting.key === key) ?? null;
895836
896187
  }
@@ -895934,7 +896285,7 @@ async function applyAgentWorkspaceSettingValue(context, setting, value) {
895934
896285
  async function importAgentWorkspaceTuiSettings(context) {
895935
896286
  const shellPaths3 = context?.workspace?.shellPaths;
895936
896287
  const configManager = context?.platform?.configManager;
895937
- if (!shellPaths3 || !configManager) {
896288
+ if (!context || !shellPaths3 || !configManager) {
895938
896289
  return {
895939
896290
  status: "GoodVibes TUI settings import is unavailable in this runtime.",
895940
896291
  runtimeSnapshot: null,
@@ -895968,8 +896319,11 @@ async function importAgentWorkspaceTuiSettings(context) {
895968
896319
  parseErrors.push(`${source.label}: ${error53 instanceof Error ? error53.message : String(error53)}`);
895969
896320
  }
895970
896321
  }
895971
- if (values2.size === 0) {
895972
- const detail = parseErrors.length > 0 ? `No importable settings found. ${parseErrors.join("; ")}` : "No GoodVibes TUI settings file with importable Agent-owned settings was found.";
896322
+ const subscriptions = importTuiSubscriptions(context, parseErrors);
896323
+ const subscriptionImports = subscriptions.importedActive.length + subscriptions.importedPending.length;
896324
+ const subscriptionUnchanged = subscriptions.unchangedActive.length + subscriptions.unchangedPending.length;
896325
+ if (values2.size === 0 && subscriptionImports === 0 && subscriptionUnchanged === 0 && subscriptions.skipped.length === 0) {
896326
+ const detail = parseErrors.length > 0 ? `No importable settings or subscriptions found. ${parseErrors.join("; ")}` : "No GoodVibes TUI settings or subscription store with importable Agent-owned state was found.";
895973
896327
  return {
895974
896328
  status: "No GoodVibes TUI settings imported.",
895975
896329
  runtimeSnapshot: null,
@@ -895999,15 +896353,20 @@ async function importAgentWorkspaceTuiSettings(context) {
895999
896353
  skipped.push(`${setting.key}: ${error53 instanceof Error ? error53.message : String(error53)}`);
896000
896354
  }
896001
896355
  }
896356
+ skipped.push(...subscriptions.skipped);
896357
+ const changedCount = imported.length + subscriptionImports;
896358
+ const unchangedCount = unchanged.length + subscriptionUnchanged;
896002
896359
  return {
896003
- status: imported.length > 0 ? `Imported ${imported.length} GoodVibes TUI setting(s).` : "No GoodVibes TUI settings changed.",
896360
+ status: changedCount > 0 ? `Imported ${changedCount} GoodVibes TUI item(s).` : "No GoodVibes TUI settings changed.",
896004
896361
  runtimeSnapshot: context ? buildAgentWorkspaceRuntimeSnapshot(context) : null,
896005
896362
  result: {
896006
- kind: skipped.length > 0 && imported.length === 0 ? "error" : imported.length > 0 ? "refreshed" : "guidance",
896007
- title: imported.length > 0 ? "GoodVibes TUI settings imported" : "No settings changed",
896363
+ kind: skipped.length > 0 && changedCount === 0 ? "error" : changedCount > 0 ? "refreshed" : "guidance",
896364
+ title: changedCount > 0 ? "GoodVibes TUI settings imported" : "No settings changed",
896008
896365
  detail: [
896009
896366
  imported.length > 0 ? `Imported: ${imported.slice(0, 10).join(", ")}${imported.length > 10 ? `, +${imported.length - 10} more` : ""}.` : "",
896010
- unchanged.length > 0 ? `${unchanged.length} setting(s) already matched.` : "",
896367
+ subscriptions.importedActive.length > 0 ? `Imported active subscription(s): ${subscriptions.importedActive.join(", ")}.` : "",
896368
+ subscriptions.importedPending.length > 0 ? `Imported pending subscription(s): ${subscriptions.importedPending.join(", ")}.` : "",
896369
+ unchangedCount > 0 ? `${unchangedCount} item(s) already matched.` : "",
896011
896370
  skipped.length > 0 ? `Skipped: ${skipped.slice(0, 5).join("; ")}${skipped.length > 5 ? `; +${skipped.length - 5} more` : ""}.` : "",
896012
896371
  parseErrors.length > 0 ? `Parse issues: ${parseErrors.join("; ")}.` : ""
896013
896372
  ].filter((line2) => line2.length > 0).join(" "),
@@ -896037,6 +896396,166 @@ function createSettingEditor(setting, currentValue, action2) {
896037
896396
  ]
896038
896397
  };
896039
896398
  }
896399
+
896400
+ // src/input/agent-workspace-subscription-editor.ts
896401
+ init_config8();
896402
+ async function submitAgentWorkspaceSubscriptionLoginStartEditor(host, editor, context, readField) {
896403
+ if (!isAffirmative15(readField("confirm"))) {
896404
+ host.localEditor = { ...editor, message: "Subscription login start not confirmed. Type yes, then press Enter." };
896405
+ host.status = "Subscription login start not confirmed.";
896406
+ return;
896407
+ }
896408
+ try {
896409
+ const provider5 = readField("provider") || "openai";
896410
+ const openBrowser2 = isAffirmative15(readField("openBrowser"));
896411
+ const { manager: manager5, services } = subscriptionServices(context);
896412
+ const resolved = getSubscriptionProviderConfig(provider5, services.get(provider5));
896413
+ if (!resolved) {
896414
+ throw new Error(`OAuth is not configured for ${provider5}. Add an OAuth provider service or choose a built-in subscription provider.`);
896415
+ }
896416
+ const started = provider5 === "openai" && resolved.source === "builtin" ? await beginOpenAICodexLogin() : null;
896417
+ const authorizationUrl = started ? started.authorizationUrl : (await manager5.beginOAuthLogin(provider5, resolveManualLoginConfig2(resolved.oauth))).authorizationUrl;
896418
+ if (started) {
896419
+ manager5.savePending({
896420
+ provider: provider5,
896421
+ state: started.state,
896422
+ verifier: started.verifier,
896423
+ redirectUri: started.redirectUri,
896424
+ createdAt: Date.now()
896425
+ });
896426
+ }
896427
+ const browserOpened = openBrowser2 ? await openExternalUrl(authorizationUrl) : false;
896428
+ host.localEditor = null;
896429
+ host.runtimeSnapshot = context ? buildAgentWorkspaceRuntimeSnapshot(context) : host.runtimeSnapshot;
896430
+ host.status = `Subscription login started for ${provider5}.`;
896431
+ host.lastActionResult = {
896432
+ kind: "refreshed",
896433
+ title: "Subscription login started",
896434
+ detail: [
896435
+ `Provider: ${provider5}.`,
896436
+ `Browser: ${openBrowser2 ? browserOpened ? "opened" : "open failed" : "skipped"}.`,
896437
+ "Use Finish subscription login with the callback code or redirect URL.",
896438
+ `Authorization URL: ${authorizationUrl}`
896439
+ ].join(" "),
896440
+ safety: "safe"
896441
+ };
896442
+ host.clampSelection();
896443
+ } catch (error53) {
896444
+ const detail = error53 instanceof Error ? error53.message : String(error53);
896445
+ host.localEditor = { ...editor, message: detail };
896446
+ host.status = detail;
896447
+ host.lastActionResult = { kind: "error", title: "Subscription login start failed", detail, safety: "safe" };
896448
+ }
896449
+ }
896450
+ async function submitAgentWorkspaceSubscriptionLoginFinishEditor(host, editor, context, readField) {
896451
+ if (!isAffirmative15(readField("confirm"))) {
896452
+ host.localEditor = { ...editor, message: "Subscription login finish not confirmed. Type yes, then press Enter." };
896453
+ host.status = "Subscription login finish not confirmed.";
896454
+ return;
896455
+ }
896456
+ try {
896457
+ const provider5 = readField("provider") || "openai";
896458
+ const codeInput = readField("code");
896459
+ const code2 = extractAuthorizationCode3(codeInput) ?? codeInput;
896460
+ const { manager: manager5, services } = subscriptionServices(context);
896461
+ const resolved = getSubscriptionProviderConfig(provider5, services.get(provider5));
896462
+ if (!resolved) {
896463
+ throw new Error(`OAuth is not configured for ${provider5}. Start with a configured or built-in subscription provider.`);
896464
+ }
896465
+ const record2 = provider5 === "openai" && resolved.source === "builtin" ? (() => {
896466
+ const pending = manager5.getPending(provider5);
896467
+ if (!pending)
896468
+ throw new Error(`No pending OAuth login for ${provider5}. Start subscription login first.`);
896469
+ return exchangeOpenAICodexCode(code2, pending.verifier).then((token) => {
896470
+ const now5 = Date.now();
896471
+ return manager5.saveSubscription({
896472
+ provider: provider5,
896473
+ accessToken: token.accessToken,
896474
+ tokenType: token.tokenType,
896475
+ ...typeof token.refreshToken === "string" && token.refreshToken.length > 0 ? { refreshToken: token.refreshToken } : {},
896476
+ ...typeof token.expiresAt === "number" && Number.isFinite(token.expiresAt) ? { expiresAt: token.expiresAt } : {},
896477
+ ...token.scopes ? { scopes: token.scopes } : {},
896478
+ authMode: "oauth",
896479
+ overrideAmbientApiKeys: false,
896480
+ createdAt: manager5.get(provider5)?.createdAt ?? now5,
896481
+ updatedAt: now5
896482
+ });
896483
+ });
896484
+ })() : manager5.completeOAuthLogin(provider5, resolveManualLoginConfig2(resolved.oauth), code2);
896485
+ const saved = await record2;
896486
+ host.localEditor = null;
896487
+ host.runtimeSnapshot = context ? buildAgentWorkspaceRuntimeSnapshot(context) : host.runtimeSnapshot;
896488
+ host.status = `Subscription session saved for ${provider5}.`;
896489
+ host.lastActionResult = {
896490
+ kind: "refreshed",
896491
+ title: "Subscription session saved",
896492
+ detail: [
896493
+ `Provider: ${provider5}.`,
896494
+ `Token type: ${saved.tokenType}.`,
896495
+ `Expires: ${saved.expiresAt ? new Date(saved.expiresAt).toISOString() : "n/a"}.`,
896496
+ describeSubscriptionPrecedence(saved)
896497
+ ].join(" "),
896498
+ safety: "safe"
896499
+ };
896500
+ host.clampSelection();
896501
+ } catch (error53) {
896502
+ const detail = error53 instanceof Error ? error53.message : String(error53);
896503
+ host.localEditor = { ...editor, message: detail };
896504
+ host.status = detail;
896505
+ host.lastActionResult = { kind: "error", title: "Subscription login finish failed", detail, safety: "safe" };
896506
+ }
896507
+ }
896508
+ function submitAgentWorkspaceSubscriptionLogoutEditor(host, editor, context, readField) {
896509
+ if (!isAffirmative15(readField("confirm"))) {
896510
+ host.localEditor = { ...editor, message: "Subscription logout not confirmed. Type yes, then press Enter." };
896511
+ host.status = "Subscription logout not confirmed.";
896512
+ return;
896513
+ }
896514
+ try {
896515
+ const provider5 = readField("provider") || "openai";
896516
+ const { manager: manager5 } = subscriptionServices(context);
896517
+ const removed = manager5.logout(provider5);
896518
+ host.localEditor = null;
896519
+ host.runtimeSnapshot = context ? buildAgentWorkspaceRuntimeSnapshot(context) : host.runtimeSnapshot;
896520
+ host.status = removed ? `Logged out of ${provider5}.` : `No subscription session existed for ${provider5}.`;
896521
+ host.lastActionResult = {
896522
+ kind: removed ? "refreshed" : "guidance",
896523
+ title: removed ? "Subscription session removed" : "No subscription session found",
896524
+ detail: removed ? `Removed active or pending subscription state for ${provider5}. Ambient API key resolution applies if configured.` : `No active or pending subscription state existed for ${provider5}.`,
896525
+ safety: "safe"
896526
+ };
896527
+ host.clampSelection();
896528
+ } catch (error53) {
896529
+ const detail = error53 instanceof Error ? error53.message : String(error53);
896530
+ host.localEditor = { ...editor, message: detail };
896531
+ host.status = detail;
896532
+ host.lastActionResult = { kind: "error", title: "Subscription logout failed", detail, safety: "safe" };
896533
+ }
896534
+ }
896535
+ function subscriptionServices(context) {
896536
+ const manager5 = context?.platform.subscriptionManager;
896537
+ const services = context?.platform.serviceRegistry;
896538
+ if (!manager5 || !services)
896539
+ throw new Error("Subscription services are unavailable in this runtime.");
896540
+ return { manager: manager5, services };
896541
+ }
896542
+ function extractAuthorizationCode3(input) {
896543
+ const trimmed2 = input.trim();
896544
+ if (!trimmed2)
896545
+ return null;
896546
+ try {
896547
+ const url2 = new URL(trimmed2);
896548
+ return url2.searchParams.get("code");
896549
+ } catch {
896550
+ return null;
896551
+ }
896552
+ }
896553
+ function resolveManualLoginConfig2(config6) {
896554
+ return config6.manualRedirectUri ? { ...config6, redirectUri: config6.manualRedirectUri } : config6;
896555
+ }
896556
+ function describeSubscriptionPrecedence(record2) {
896557
+ return record2.overrideAmbientApiKeys ? "Subscription routing now overrides ambient API keys for this provider." : "Subscription session is stored for subscription-backed flows; ambient API keys are unchanged.";
896558
+ }
896040
896559
  // src/input/agent-workspace-token.ts
896041
896560
  function handleAgentWorkspaceToken(workspace, token, handleEscape2, requestRender) {
896042
896561
  if (!workspace.active)
@@ -896405,6 +896924,51 @@ class AgentWorkspace {
896405
896924
  this.lastActionResult = { kind: "error", title: "Onboarding completion failed", detail, safety: "safe" };
896406
896925
  }
896407
896926
  }
896927
+ openModelPickerAction(action2, requestRender) {
896928
+ const target = action2.modelPickerTarget ?? "main";
896929
+ const opened2 = action2.modelPickerFlow === "model" ? this.context?.openModelPickerWithTarget?.(target) : this.context?.openProviderModelPickerWithTarget?.(target);
896930
+ if (!opened2) {
896931
+ this.status = "Model picker is unavailable.";
896932
+ this.lastActionResult = {
896933
+ kind: "error",
896934
+ title: "Model picker unavailable",
896935
+ detail: "This runtime cannot open the model picker from Agent workspace.",
896936
+ safety: action2.safety
896937
+ };
896938
+ requestRender?.();
896939
+ return;
896940
+ }
896941
+ this.status = `Opening ${action2.label}.`;
896942
+ this.lastActionResult = {
896943
+ kind: "dispatched",
896944
+ title: `Opening ${action2.label}`,
896945
+ detail: "Opened the shared provider/model picker for this setup target.",
896946
+ safety: action2.safety
896947
+ };
896948
+ requestRender?.();
896949
+ }
896950
+ openSettingsModalAction(action2, requestRender) {
896951
+ if (!this.context?.openSettingsModal) {
896952
+ this.status = "Settings are unavailable.";
896953
+ this.lastActionResult = {
896954
+ kind: "error",
896955
+ title: "Settings unavailable",
896956
+ detail: "This runtime cannot open settings from Agent workspace.",
896957
+ safety: action2.safety
896958
+ };
896959
+ requestRender?.();
896960
+ return;
896961
+ }
896962
+ this.context.openSettingsModal(action2.settingsTarget);
896963
+ this.status = `Opening ${action2.label}.`;
896964
+ this.lastActionResult = {
896965
+ kind: "dispatched",
896966
+ title: `Opening ${action2.label}`,
896967
+ detail: "Opened the shared settings surface for this setup area.",
896968
+ safety: action2.safety
896969
+ };
896970
+ requestRender?.();
896971
+ }
896408
896972
  applySettingAction(action2, requestRender) {
896409
896973
  const effect = buildAgentWorkspaceSettingActionEffect(this.context, action2);
896410
896974
  if (effect.kind === "result") {
@@ -896515,6 +897079,19 @@ class AgentWorkspace {
896515
897079
  this.status = `${missing.label} is required.`;
896516
897080
  return;
896517
897081
  }
897082
+ if (editor.kind === "subscription-login-start") {
897083
+ submitAgentWorkspaceSubscriptionLoginStartEditor(this, editor, this.context, (id) => this.editorField(id)).finally(() => requestRender?.());
897084
+ return;
897085
+ }
897086
+ if (editor.kind === "subscription-login-finish") {
897087
+ submitAgentWorkspaceSubscriptionLoginFinishEditor(this, editor, this.context, (id) => this.editorField(id)).finally(() => requestRender?.());
897088
+ return;
897089
+ }
897090
+ if (editor.kind === "subscription-logout") {
897091
+ submitAgentWorkspaceSubscriptionLogoutEditor(this, editor, this.context, (id) => this.editorField(id));
897092
+ requestRender?.();
897093
+ return;
897094
+ }
896518
897095
  if (isAgentWorkspaceCommandEditorKind(editor.kind)) {
896519
897096
  this.submitCommandEditor(editor);
896520
897097
  requestRender?.();
@@ -896565,138 +897142,13 @@ class AgentWorkspace {
896565
897142
  return;
896566
897143
  }
896567
897144
  try {
896568
- if (editor.mode === "delete") {
896569
- this.submitLocalDeleteEditor(shellPaths3, editor);
896570
- return;
896571
- }
896572
- if (editor.kind === "learned-behavior") {
896573
- const created = createAgentWorkspaceLearnedBehavior(shellPaths3, {
896574
- target: this.learnedBehaviorTarget(),
896575
- name: this.editorField("name"),
896576
- description: this.editorField("description"),
896577
- notes: this.editorField("notes"),
896578
- tags: splitList5(this.editorField("tags")),
896579
- triggers: splitList5(this.editorField("triggers")),
896580
- enable: isAffirmative15(this.editorField("enable"))
896581
- });
896582
- this.finishLocalEditor(created.kind, created.id, created.name, "Created");
896583
- } else if (editor.kind === "profile") {
896584
- const template = this.editorField("template");
896585
- const templateId = template && template.toLowerCase() !== "none" ? template : undefined;
896586
- const profile5 = createAgentRuntimeProfile(shellPaths3.homeDirectory, this.editorField("name"), {
896587
- ...templateId ? { templateId } : {}
896588
- });
896589
- this.finishProfileEditor(profile5);
896590
- } else if (editor.kind === "note") {
896591
- const registry5 = AgentNoteRegistry.fromShellPaths(shellPaths3);
896592
- if (editor.mode === "update" && editor.recordId) {
896593
- const updated = registry5.update(editor.recordId, {
896594
- title: this.editorField("title"),
896595
- body: this.editorField("body"),
896596
- sourceUrl: this.editorField("sourceUrl"),
896597
- tags: splitList5(this.editorField("tags")),
896598
- provenance: "Workspace"
896599
- });
896600
- this.finishLocalEditor(editor.kind, updated.id, updated.title, "Updated");
896601
- return;
896602
- }
896603
- const created = registry5.create({
896604
- title: this.editorField("title"),
896605
- body: this.editorField("body"),
896606
- sourceUrl: this.editorField("sourceUrl"),
896607
- tags: splitList5(this.editorField("tags")),
896608
- source: "user",
896609
- provenance: "Workspace"
896610
- });
896611
- this.finishLocalEditor(editor.kind, created.id, created.title, "Created");
896612
- } else if (editor.kind === "persona") {
896613
- const registry5 = AgentPersonaRegistry.fromShellPaths(shellPaths3);
896614
- if (editor.mode === "update" && editor.recordId) {
896615
- const wasActive = registry5.snapshot().activePersonaId === editor.recordId;
896616
- const updated = registry5.update(editor.recordId, {
896617
- name: this.editorField("name"),
896618
- description: this.editorField("description"),
896619
- body: this.editorField("body"),
896620
- tags: splitList5(this.editorField("tags")),
896621
- triggers: splitList5(this.editorField("triggers")),
896622
- provenance: "Workspace"
896623
- });
896624
- if (isAffirmative15(this.editorField("activate")))
896625
- registry5.setActive(updated.id);
896626
- else if (wasActive)
896627
- registry5.clearActive();
896628
- this.finishLocalEditor(editor.kind, updated.id, updated.name, "Updated");
896629
- return;
896630
- }
896631
- const created = registry5.create({
896632
- name: this.editorField("name"),
896633
- description: this.editorField("description"),
896634
- body: this.editorField("body"),
896635
- tags: splitList5(this.editorField("tags")),
896636
- triggers: splitList5(this.editorField("triggers")),
896637
- source: "user",
896638
- provenance: "Workspace"
896639
- });
896640
- if (isAffirmative15(this.editorField("activate")))
896641
- registry5.setActive(created.id);
896642
- this.finishLocalEditor(editor.kind, created.id, created.name, "Created");
896643
- } else if (editor.kind === "skill") {
896644
- const registry5 = AgentSkillRegistry.fromShellPaths(shellPaths3);
896645
- if (editor.mode === "update" && editor.recordId) {
896646
- const updated = registry5.update(editor.recordId, {
896647
- name: this.editorField("name"),
896648
- description: this.editorField("description"),
896649
- procedure: this.editorField("procedure"),
896650
- triggers: splitList5(this.editorField("triggers")),
896651
- tags: splitList5(this.editorField("tags")),
896652
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
896653
- provenance: "Workspace"
896654
- });
896655
- registry5.setEnabled(updated.id, isAffirmative15(this.editorField("enabled")));
896656
- this.finishLocalEditor(editor.kind, updated.id, updated.name, "Updated");
896657
- return;
896658
- }
896659
- const created = registry5.create({
896660
- name: this.editorField("name"),
896661
- description: this.editorField("description"),
896662
- procedure: this.editorField("procedure"),
896663
- triggers: splitList5(this.editorField("triggers")),
896664
- tags: splitList5(this.editorField("tags")),
896665
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
896666
- enabled: isAffirmative15(this.editorField("enabled")),
896667
- source: "user",
896668
- provenance: "Workspace"
896669
- });
896670
- this.finishLocalEditor(editor.kind, created.id, created.name, "Created");
896671
- } else {
896672
- const registry5 = AgentRoutineRegistry.fromShellPaths(shellPaths3);
896673
- if (editor.mode === "update" && editor.recordId) {
896674
- const updated = registry5.update(editor.recordId, {
896675
- name: this.editorField("name"),
896676
- description: this.editorField("description"),
896677
- steps: this.editorField("steps"),
896678
- triggers: splitList5(this.editorField("triggers")),
896679
- tags: splitList5(this.editorField("tags")),
896680
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
896681
- provenance: "Workspace"
896682
- });
896683
- registry5.setEnabled(updated.id, isAffirmative15(this.editorField("enabled")));
896684
- this.finishLocalEditor(editor.kind, updated.id, updated.name, "Updated");
896685
- return;
896686
- }
896687
- const created = registry5.create({
896688
- name: this.editorField("name"),
896689
- description: this.editorField("description"),
896690
- steps: this.editorField("steps"),
896691
- triggers: splitList5(this.editorField("triggers")),
896692
- tags: splitList5(this.editorField("tags")),
896693
- requirements: buildAgentWorkspaceRequirements((id) => this.editorField(id)),
896694
- enabled: isAffirmative15(this.editorField("enabled")),
896695
- source: "user",
896696
- provenance: "Workspace"
896697
- });
896698
- this.finishLocalEditor(editor.kind, created.id, created.name, "Created");
896699
- }
897145
+ submitAgentWorkspaceLocalRegistryEditor(shellPaths3, editor, {
897146
+ readField: (id) => this.editorField(id),
897147
+ learnedBehaviorTarget: () => this.learnedBehaviorTarget(),
897148
+ submitDeleteEditor: () => this.submitLocalDeleteEditor(shellPaths3, editor),
897149
+ finishLocalEditor: (kind2, id, name51, verb) => this.finishLocalEditor(kind2, id, name51, verb),
897150
+ finishProfileEditor: (profile5) => this.finishProfileEditor(profile5)
897151
+ });
896700
897152
  } catch (error53) {
896701
897153
  const detail = error53 instanceof Error ? error53.message : String(error53);
896702
897154
  this.localEditor = { ...editor, message: detail };
@@ -898540,6 +898992,17 @@ function handleModalTokenRoutes(state4, token) {
898540
898992
  }, token)) {
898541
898993
  return withState(state4, true);
898542
898994
  }
898995
+ if (handleModelPickerToken({
898996
+ modelPicker: state4.modelPicker,
898997
+ modalStack: state4.modalStack,
898998
+ commandContext: state4.commandContext,
898999
+ getViewportHeight: state4.getViewportHeight,
899000
+ requestRender: state4.requestRender,
899001
+ handleEscape: state4.handleEscape,
899002
+ onModelPickerCommit: state4.onModelPickerCommit
899003
+ }, token)) {
899004
+ return withState(state4, true);
899005
+ }
898543
899006
  if (handleSettingsModalToken({
898544
899007
  settingsModal: state4.settingsModal,
898545
899008
  commandContext: state4.commandContext,
@@ -898592,17 +899055,6 @@ function handleModalTokenRoutes(state4, token) {
898592
899055
  if (handleHistorySearchToken(historyState, token)) {
898593
899056
  return withState(state4, true, historyState);
898594
899057
  }
898595
- if (handleModelPickerToken({
898596
- modelPicker: state4.modelPicker,
898597
- modalStack: state4.modalStack,
898598
- commandContext: state4.commandContext,
898599
- getViewportHeight: state4.getViewportHeight,
898600
- requestRender: state4.requestRender,
898601
- handleEscape: state4.handleEscape,
898602
- onModelPickerCommit: state4.onModelPickerCommit
898603
- }, token)) {
898604
- return withState(state4, true);
898605
- }
898606
899058
  if (handleLiveTailToken({
898607
899059
  liveTailModal: state4.liveTailModal,
898608
899060
  processModal: state4.processModal,
@@ -899541,7 +899993,8 @@ class InputHandler {
899541
899993
  handleCtrlCForHandler(this);
899542
899994
  }
899543
899995
  modalOpened(name51) {
899544
- if (name51 !== "agentWorkspace" && this.agentWorkspace.active) {
899996
+ const keepAgentWorkspaceUnderlay = name51 === "modelPicker" || name51 === "settings";
899997
+ if (name51 !== "agentWorkspace" && !keepAgentWorkspaceUnderlay && this.agentWorkspace.active) {
899545
899998
  this.closeAgentWorkspaceModal();
899546
899999
  }
899547
900000
  modalOpenedForHandler(this, name51);
@@ -899904,232 +900357,6 @@ class PermissionPromptUI {
899904
900357
  }
899905
900358
  }
899906
900359
 
899907
- // src/renderer/layout-engine.ts
899908
- function clamp4(value, min2, max2) {
899909
- return Math.max(min2, Math.min(max2, value));
899910
- }
899911
- function createShellLayout(request2) {
899912
- const width = Math.max(1, request2.width);
899913
- const height = Math.max(1, request2.height);
899914
- const headerHeight = clamp4(request2.headerHeight, 0, height);
899915
- const footerHeight = clamp4(request2.footerHeight, 0, Math.max(0, height - headerHeight));
899916
- const bodyHeight = Math.max(0, height - headerHeight - footerHeight);
899917
- const hasPanel = typeof request2.panelWidth === "number" && request2.panelWidth > 0;
899918
- const safePanelWidth = hasPanel ? clamp4(request2.panelWidth, 1, Math.max(1, width - 2)) : 0;
899919
- const separatorWidth = hasPanel ? 1 : 0;
899920
- const conversationWidth = hasPanel ? Math.max(1, width - safePanelWidth - separatorWidth) : width;
899921
- return {
899922
- screen: { x: 0, y: 0, width, height },
899923
- header: { x: 0, y: 0, width, height: headerHeight },
899924
- body: { x: 0, y: headerHeight, width, height: bodyHeight },
899925
- footer: { x: 0, y: headerHeight + bodyHeight, width, height: footerHeight },
899926
- conversation: { x: 0, y: headerHeight, width: conversationWidth, height: bodyHeight },
899927
- panel: hasPanel ? {
899928
- x: conversationWidth + separatorWidth,
899929
- y: headerHeight,
899930
- width: safePanelWidth,
899931
- height: bodyHeight
899932
- } : undefined,
899933
- separatorX: hasPanel ? conversationWidth : undefined
899934
- };
899935
- }
899936
- function createSplitPaneLayout(totalRows, ratio, options = {}) {
899937
- const topTabRows = options.topTabRows ?? 1;
899938
- const bottomTabRows = options.bottomTabRows ?? 1;
899939
- const separatorRows = options.separatorRows ?? 1;
899940
- const chromeRows = topTabRows + bottomTabRows + separatorRows;
899941
- const contentRows = Math.max(0, totalRows - chromeRows);
899942
- const normalizedRatio = clamp4(ratio, 0.2, 0.8);
899943
- const topContentRows = contentRows <= 1 ? contentRows : Math.max(1, Math.floor(contentRows * normalizedRatio));
899944
- const bottomContentRows = contentRows <= 1 ? 0 : Math.max(1, contentRows - topContentRows);
899945
- return {
899946
- totalRows,
899947
- topTabRows,
899948
- bottomTabRows,
899949
- separatorRows,
899950
- topContentRows,
899951
- bottomContentRows
899952
- };
899953
- }
899954
-
899955
- // src/renderer/process-indicator.ts
899956
- function truncateToWidth2(text, maxWidth) {
899957
- let width = 0;
899958
- let i5 = 0;
899959
- for (const char of text) {
899960
- const cw = getDisplayWidth(char);
899961
- if (width + cw > maxWidth)
899962
- break;
899963
- width += cw;
899964
- i5 += char.length;
899965
- }
899966
- return text.slice(0, i5);
899967
- }
899968
- function renderProcessIndicator(width, agentCount, toolCount, focused = false, agentProgress) {
899969
- const total = agentCount + toolCount;
899970
- const delegationLabel = (count) => `${count} delegation${count !== 1 ? "s" : ""}`;
899971
- const renderPlainStatus = (text, style) => [UIFactory.stringToLine(` ${text}`, width, style)];
899972
- const renderFocusedStatus = (text) => {
899973
- const bg = "#31506f";
899974
- const fg = "#eefaff";
899975
- const markerFg = "#7dd3fc";
899976
- const line2 = UIFactory.stringToLine(" ".repeat(width), width, { fg: "238" });
899977
- const prefix = `${GLYPHS.navigation.selected} `;
899978
- const body2 = truncateToWidth2(text, Math.max(0, width - 8));
899979
- const highlighted = ` ${prefix}${body2} `;
899980
- const startX = 2;
899981
- for (let i5 = 0;i5 < highlighted.length && startX + i5 < width - 2; i5++) {
899982
- const ch = highlighted[i5];
899983
- const isMarker = i5 < prefix.length + 1;
899984
- line2[startX + i5].char = ch;
899985
- line2[startX + i5].fg = isMarker ? markerFg : fg;
899986
- line2[startX + i5].bg = bg;
899987
- line2[startX + i5].bold = true;
899988
- line2[startX + i5].dim = false;
899989
- }
899990
- return [line2];
899991
- };
899992
- if (focused) {
899993
- const parts3 = [];
899994
- if (agentCount > 0)
899995
- parts3.push(delegationLabel(agentCount));
899996
- if (toolCount > 0)
899997
- parts3.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""} running`);
899998
- const label2 = total === 0 ? `No runtime activity ${GLYPHS.status.pending} back to input` : `${parts3.join(` ${GLYPHS.navigation.pipeSeparator} `)} ${GLYPHS.status.pending} Enter to open ${GLYPHS.status.pending} back to input`;
899999
- return renderFocusedStatus(label2);
900000
- }
900001
- if (total === 0) {
900002
- return renderPlainStatus("No runtime activity", { fg: "238", dim: true });
900003
- }
900004
- const parts2 = [];
900005
- if (agentCount > 0) {
900006
- parts2.push(delegationLabel(agentCount));
900007
- }
900008
- if (toolCount > 0) {
900009
- parts2.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""} running`);
900010
- }
900011
- const PROGRESS_RESERVED_CHARS = 43;
900012
- const progressMaxLen = Math.max(0, width - PROGRESS_RESERVED_CHARS);
900013
- const progressSuffix = agentProgress && progressMaxLen > 10 ? ` | ${agentProgress.length > progressMaxLen ? agentProgress.slice(0, Math.max(0, progressMaxLen - 3)) + "..." : agentProgress}` : "";
900014
- const label = `${parts2.join(` ${GLYPHS.navigation.pipeSeparator} `)}${progressSuffix}`;
900015
- const hint = ` ${GLYPHS.status.pending} Enter to view`;
900016
- return renderPlainStatus(`${label}${hint}`, { fg: "#00ffff", bold: true });
900017
- }
900018
-
900019
- // src/renderer/shell-surface.ts
900020
- var FOOTER_BASE_ROWS = 9;
900021
- var CONTEXT_PROGRESS_ROWS = 2;
900022
- var PROCESS_INDICATOR_ROWS = 1;
900023
- function estimateShellFooterHeight(promptLineCount, contextWindow) {
900024
- const safePromptLines = Math.max(1, promptLineCount);
900025
- const progressRows = contextWindow && contextWindow > 0 ? CONTEXT_PROGRESS_ROWS : 0;
900026
- return FOOTER_BASE_ROWS + safePromptLines + progressRows + PROCESS_INDICATOR_ROWS;
900027
- }
900028
- function buildShellFooter(options) {
900029
- const lines = UIFactory.createFooter(options.width, options.promptText, options.usage, options.showExitNotice, options.lastCopyTime, options.model, options.toolCount, options.promptCursorPos, options.workingDir, options.provider, options.contextWindow, options.compactThreshold, options.dangerMode, options.lastInputTokens, options.commandArgsHint, options.hitlMode, options.promptFocused ?? !options.indicatorFocused, options.composerMode, options.composerStatus, options.composerFlags, options.composerPendingRisk);
900030
- const processIndicator = renderProcessIndicator(options.width, options.runningAgentCount, options.runningProcessCount, options.indicatorFocused, options.runningAgentProgress);
900031
- const inputBoxRows = Math.max(1, options.promptLineCount) + 2;
900032
- lines.splice(inputBoxRows, 0, ...processIndicator);
900033
- return { lines, height: lines.length };
900034
- }
900035
-
900036
- // src/renderer/conversation-layout.ts
900037
- function buildConversationViewport(request2) {
900038
- const overlayRows = request2.overlayRows ?? 0;
900039
- const effectiveHeight = Math.max(0, request2.viewportHeight - overlayRows);
900040
- const lineCount = request2.conversation.history.getLineCount();
900041
- const maxScroll = Math.max(0, lineCount - effectiveHeight);
900042
- const nextScrollTop = request2.scrollLocked ? maxScroll : Math.max(0, Math.min(request2.scrollTop, maxScroll));
900043
- const viewport = request2.conversation.history.getSnapshot(nextScrollTop, effectiveHeight, request2.width);
900044
- return {
900045
- effectiveHeight,
900046
- maxScroll,
900047
- nextScrollTop,
900048
- viewport
900049
- };
900050
- }
900051
- function overlayViewportBottom(viewport, overlay, width, viewportHeight, bottomInset = 0) {
900052
- if (overlay.length === 0)
900053
- return [...viewport];
900054
- const next = [...viewport];
900055
- const targetStart = Math.max(0, viewportHeight - bottomInset - overlay.length);
900056
- next.length = Math.min(next.length, targetStart);
900057
- while (next.length < targetStart)
900058
- next.push(createEmptyLine(width));
900059
- next.push(...overlay);
900060
- return next;
900061
- }
900062
- function replaceViewportWithOverlay(overlay, width, viewportHeight) {
900063
- const next = [];
900064
- const pad = Math.max(0, viewportHeight - overlay.length);
900065
- for (let i5 = 0;i5 < pad; i5++)
900066
- next.push(createEmptyLine(width));
900067
- next.push(...overlay);
900068
- return next;
900069
- }
900070
-
900071
- // src/renderer/file-picker-overlay.ts
900072
- var FILE_PICKER_TITLE = "Select File";
900073
- var FILE_PICKER_SEARCH_PREFIX = "@ ";
900074
- var FILE_PICKER_EMPTY_MESSAGE = "No matching files";
900075
- var FILE_PICKER_HINTS = "[Up/Down] Navigate [/] Search [Enter] Select [Esc] Cancel";
900076
- function renderFilePickerOverlay(picker, width, viewportHeight = 24) {
900077
- const lines = [];
900078
- const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
900079
- chromeRows: 4,
900080
- maxWidth: 70,
900081
- minContentRows: 6,
900082
- maxContentRows: 10
900083
- });
900084
- const layout = createOverlayBoxLayout(width, metrics.margin, metrics.boxWidth);
900085
- const contentW = layout.innerWidth;
900086
- const borderFg = DEFAULT_OVERLAY_PALETTE.borderFg;
900087
- const titleFg = DEFAULT_OVERLAY_PALETTE.titleFg;
900088
- const bodyFg = DEFAULT_OVERLAY_PALETTE.bodyFg;
900089
- const mutedFg = DEFAULT_OVERLAY_PALETTE.mutedFg;
900090
- const selectedBg = DEFAULT_OVERLAY_PALETTE.selectedBg;
900091
- const titleLine = createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.topLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.topRight, borderFg, DEFAULT_OVERLAY_PALETTE.titleBg);
900092
- putOverlayText(titleLine, layout.margin + 2, layout.width - 4, FILE_PICKER_TITLE, { fg: titleFg, bold: true });
900093
- lines.push(titleLine);
900094
- const queryDisplay = picker.query || "";
900095
- const searchLine = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.inputBg);
900096
- const searchPrefix = FILE_PICKER_SEARCH_PREFIX;
900097
- const queryText = fitDisplay(`${queryDisplay}${picker.searchFocused ? OVERLAY_GLYPHS.cursor : ""}`, Math.max(0, contentW - getDisplayWidth(searchPrefix)));
900098
- putOverlayText(searchLine, layout.margin + 2, getDisplayWidth(searchPrefix), searchPrefix, { fg: picker.searchFocused ? bodyFg : mutedFg });
900099
- putOverlayText(searchLine, layout.margin + 2 + getDisplayWidth(searchPrefix), contentW - getDisplayWidth(searchPrefix), queryText, { fg: picker.query.length > 0 || picker.searchFocused ? bodyFg : mutedFg });
900100
- lines.push(searchLine);
900101
- lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.teeLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.teeRight, borderFg, DEFAULT_OVERLAY_PALETTE.sectionBg));
900102
- if (picker.results.length === 0) {
900103
- const noResults = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.bodyBg);
900104
- putOverlayText(noResults, layout.margin + 2, contentW, fitDisplay(FILE_PICKER_EMPTY_MESSAGE, contentW), { fg: "244", dim: true });
900105
- lines.push(noResults);
900106
- } else {
900107
- const maxVisible = metrics.contentRows;
900108
- let startIdx = 0;
900109
- if (picker.results.length > maxVisible) {
900110
- startIdx = Math.max(0, Math.min(picker.selectedIndex - Math.floor(maxVisible / 2), picker.results.length - maxVisible));
900111
- }
900112
- const endIdx = Math.min(startIdx + maxVisible, picker.results.length);
900113
- for (let i5 = startIdx;i5 < endIdx; i5++) {
900114
- const file2 = picker.results[i5];
900115
- const isSelected = i5 === picker.selectedIndex;
900116
- const indicator = isSelected ? `${OVERLAY_GLYPHS.selected} ` : " ";
900117
- const displayFile = getDisplayWidth(file2) > contentW - 2 ? truncateDisplay(file2, contentW - 2) : file2;
900118
- const line2 = createOverlayContentLine(width, layout, borderFg, isSelected ? selectedBg : DEFAULT_OVERLAY_PALETTE.bodyBg);
900119
- putOverlayText(line2, layout.margin + 2, contentW, fitDisplay(indicator + fitDisplay(displayFile, contentW - 2), contentW), {
900120
- fg: isSelected ? titleFg : file2.endsWith("/") ? titleFg : bodyFg,
900121
- bg: isSelected ? selectedBg : DEFAULT_OVERLAY_PALETTE.bodyBg,
900122
- bold: isSelected
900123
- });
900124
- lines.push(line2);
900125
- }
900126
- }
900127
- const bottomLine = createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.bottomLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.bottomRight, borderFg, DEFAULT_OVERLAY_PALETTE.sectionBg);
900128
- putOverlayText(bottomLine, layout.margin + 2, layout.width - 4, truncateDisplay(FILE_PICKER_HINTS, layout.width - 4), { fg: mutedFg, dim: true });
900129
- lines.push(bottomLine);
900130
- return lines;
900131
- }
900132
-
900133
900360
  // src/renderer/model-workspace.ts
900134
900361
  var PALETTE = {
900135
900362
  border: "#64748b",
@@ -900175,7 +900402,7 @@ var MODEL_WORKSPACE_CONTEXT_CAP_INPUT_HELP = "Type digits to set a cap. Enter co
900175
900402
  var MODEL_WORKSPACE_FOOTER_SEARCH_ACTIVE = "Typing filters search; Esc clears search";
900176
900403
  var MODEL_WORKSPACE_FOOTER_SEARCH_INACTIVE = "/ search";
900177
900404
  var MODEL_WORKSPACE_FOOTER_CONTROLS = "Up/Down navigate \u2022 Left/Right pane \u2022 Enter select \u2022 <search> \u2022 Tab price \u2022 C caps \u2022 A available \u2022 B benchmark \u2022 G group \u2022 Esc close";
900178
- function clamp5(value, min2, max2) {
900405
+ function clamp4(value, min2, max2) {
900179
900406
  return Math.max(min2, Math.min(max2, value));
900180
900407
  }
900181
900408
  function fillRange2(line2, startX, endX, bg) {
@@ -900264,10 +900491,10 @@ function padDisplay2(text, width) {
900264
900491
  function stableWindow2(total, selected, visible) {
900265
900492
  if (total <= 0 || visible <= 0)
900266
900493
  return { start: 0, end: 0 };
900267
- const clamped = clamp5(selected, 0, total - 1);
900494
+ const clamped = clamp4(selected, 0, total - 1);
900268
900495
  const half = Math.floor(visible / 2);
900269
900496
  const maxStart = Math.max(0, total - visible);
900270
- const start2 = clamp5(clamped - half, 0, maxStart);
900497
+ const start2 = clamp4(clamped - half, 0, maxStart);
900271
900498
  return { start: start2, end: Math.min(total, start2 + visible) };
900272
900499
  }
900273
900500
  function formatContext(value) {
@@ -900405,11 +900632,11 @@ function renderProviderRows(picker, lines, rows, startX, width) {
900405
900632
  function renderModelRows(picker, lines, rows, startX, width) {
900406
900633
  const models = picker.getFilteredModels();
900407
900634
  const { start: start2, end } = stableWindow2(models.length, picker.selectedIndex, rows);
900408
- const providerW = clamp5(Math.floor(width * 0.14), 10, 18);
900635
+ const providerW = clamp4(Math.floor(width * 0.14), 10, 18);
900409
900636
  const ctxW = 8;
900410
900637
  const tierW = 8;
900411
900638
  const capsW = 14;
900412
- const nameW = clamp5(Math.floor(width * 0.28), 16, 36);
900639
+ const nameW = clamp4(Math.floor(width * 0.28), 16, 36);
900413
900640
  const keyW = Math.max(10, width - providerW - ctxW - tierW - capsW - nameW - 10);
900414
900641
  for (let visibleRow = 0;visibleRow < rows; visibleRow += 1) {
900415
900642
  const absolute = start2 + visibleRow;
@@ -900489,11 +900716,11 @@ function writeTableHeader(line2, picker, startX, width) {
900489
900716
  writeText3(line2, startX + 1, width - 2, MODEL_WORKSPACE_TABLE_HEADERS.contextCap, style);
900490
900717
  return;
900491
900718
  }
900492
- const providerW = clamp5(Math.floor(width * 0.14), 10, 18);
900719
+ const providerW = clamp4(Math.floor(width * 0.14), 10, 18);
900493
900720
  const ctxW = 8;
900494
900721
  const tierW = 8;
900495
900722
  const capsW = 14;
900496
- const nameW = clamp5(Math.floor(width * 0.28), 16, 36);
900723
+ const nameW = clamp4(Math.floor(width * 0.28), 16, 36);
900497
900724
  const keyW = Math.max(10, width - providerW - ctxW - tierW - capsW - nameW - 10);
900498
900725
  let x2 = startX + 3;
900499
900726
  writeText3(line2, x2, keyW, padDisplay2(MODEL_WORKSPACE_TABLE_HEADERS.modelKey, keyW), style);
@@ -900516,7 +900743,7 @@ function renderModelWorkspace(picker, width, viewportHeight) {
900516
900743
  const safeWidth = Math.max(20, width);
900517
900744
  const safeHeight = Math.max(12, viewportHeight);
900518
900745
  const lines = [];
900519
- const targetW = clamp5(Math.round(safeWidth * 0.18), 24, 34);
900746
+ const targetW = clamp4(Math.round(safeWidth * 0.18), 24, 34);
900520
900747
  const contentX = targetW + 1;
900521
900748
  const contentW = Math.max(1, safeWidth - contentX - 1);
900522
900749
  const top = borderLine2(safeWidth, GLYPHS.frame.topLeft, GLYPHS.frame.horizontal, GLYPHS.frame.topRight);
@@ -900536,7 +900763,7 @@ function renderModelWorkspace(picker, width, viewportHeight) {
900536
900763
  const bodyRows = safeHeight - 3 - footerRows;
900537
900764
  const maxDetailRows = Math.max(3, bodyRows - 2);
900538
900765
  const minDetailRows = Math.min(6, maxDetailRows);
900539
- const detailRows = clamp5(Math.round(bodyRows * 0.32), minDetailRows, maxDetailRows);
900766
+ const detailRows = clamp4(Math.round(bodyRows * 0.32), minDetailRows, maxDetailRows);
900540
900767
  const listRows = Math.max(1, bodyRows - detailRows - 1);
900541
900768
  const details = detailLines(picker, contentW - 2).slice(0, detailRows);
900542
900769
  for (let row = 0;row < bodyRows; row += 1) {
@@ -900644,292 +900871,6 @@ function keyForTargets(values2) {
900644
900871
  ].join("\x1D")).join("\x1F");
900645
900872
  }
900646
900873
 
900647
- // src/renderer/selection-modal-overlay.ts
900648
- var BORDER_FG = DEFAULT_OVERLAY_PALETTE.borderFg;
900649
- var TITLE_FG = DEFAULT_OVERLAY_PALETTE.titleFg;
900650
- var BODY_FG = DEFAULT_OVERLAY_PALETTE.bodyFg;
900651
- var MUTED_FG = DEFAULT_OVERLAY_PALETTE.mutedFg;
900652
- var CATEGORY_FG = "#4488cc";
900653
- var SELECTED_BG = DEFAULT_OVERLAY_PALETTE.selectedBg;
900654
- var SELECTION_MODAL_SEARCH_LABEL = " Search";
900655
- var SELECTION_MODAL_RESULTS_LABEL = " Results";
900656
- var SELECTION_MODAL_NO_MATCHING_ITEMS = "No matching items";
900657
- var SELECTION_MODAL_NO_ITEMS = "No items";
900658
- var SELECTION_MODAL_NAVIGATION_HINT = "[Up/Down] Navigate";
900659
- var SELECTION_MODAL_CLOSE_HINT = "[Esc] Close";
900660
- var SELECTION_MODAL_SEARCH_HINT = "[/] Search";
900661
- var SELECTION_MODAL_SPACE_TOGGLE_HINT = "[Space] Toggle";
900662
- function putText(line2, startX, maxWidth, text, style) {
900663
- putOverlayText(line2, startX, maxWidth, text, style);
900664
- }
900665
- function primaryVerbForAction(primaryAction) {
900666
- return primaryAction === "toggle" ? "[Enter] Toggle" : primaryAction === "edit" ? "[Enter] Edit" : primaryAction === "delete" ? "[Enter] Delete" : "[Enter] Select";
900667
- }
900668
- function renderSelectionModalOverlay(modal, width, viewportHeight = 24) {
900669
- const lines = [];
900670
- const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
900671
- margin: 4,
900672
- maxWidth: 72,
900673
- chromeRows: modal.allowSearch ? 5 : 4,
900674
- minContentRows: 6,
900675
- maxContentRows: 10
900676
- });
900677
- const layout = createOverlayBoxLayout(width, metrics.margin, metrics.boxWidth);
900678
- lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.topLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.topRight, BORDER_FG, DEFAULT_OVERLAY_PALETTE.titleBg));
900679
- const titleLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.titleBg);
900680
- putText(titleLine, layout.margin + 2, layout.innerWidth, fitDisplay(truncateDisplay(modal.title, layout.innerWidth), layout.innerWidth), { fg: TITLE_FG, bold: true });
900681
- lines.push(titleLine);
900682
- if (modal.allowSearch) {
900683
- const labelLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
900684
- putText(labelLine, layout.margin + 2, layout.innerWidth, fitDisplay(SELECTION_MODAL_SEARCH_LABEL, layout.innerWidth), {
900685
- fg: CATEGORY_FG,
900686
- dim: true
900687
- });
900688
- lines.push(labelLine);
900689
- const searchLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.inputBg);
900690
- const prefix = "/ ";
900691
- const queryAreaWidth = layout.innerWidth - getDisplayWidth(prefix);
900692
- const queryValue = modal.query + (modal.searchFocused ? OVERLAY_GLYPHS.cursor : "");
900693
- const queryText = fitDisplay(truncateDisplay(queryValue, queryAreaWidth), queryAreaWidth);
900694
- putText(searchLine, layout.margin + 2, getDisplayWidth(prefix), prefix, { fg: modal.searchFocused ? BODY_FG : MUTED_FG });
900695
- putText(searchLine, layout.margin + 2 + getDisplayWidth(prefix), queryAreaWidth, queryText, {
900696
- fg: modal.query.length > 0 || modal.searchFocused ? BODY_FG : MUTED_FG
900697
- });
900698
- lines.push(searchLine);
900699
- lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.teeLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.teeRight, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg));
900700
- } else {
900701
- lines.push(createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg));
900702
- }
900703
- const listTitle = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
900704
- putText(listTitle, layout.margin + 2, layout.innerWidth, fitDisplay(SELECTION_MODAL_RESULTS_LABEL, layout.innerWidth), {
900705
- fg: CATEGORY_FG,
900706
- dim: true
900707
- });
900708
- lines.push(listTitle);
900709
- const items = modal.filteredItems;
900710
- if (items.length === 0) {
900711
- const line2 = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.bodyBg);
900712
- const message = modal.query ? SELECTION_MODAL_NO_MATCHING_ITEMS : SELECTION_MODAL_NO_ITEMS;
900713
- putText(line2, layout.margin + 2, layout.innerWidth, fitDisplay(message, layout.innerWidth), { fg: MUTED_FG, dim: true });
900714
- lines.push(line2);
900715
- } else {
900716
- const maxVisible = metrics.contentRows;
900717
- let startIdx = 0;
900718
- if (items.length > maxVisible) {
900719
- startIdx = Math.max(0, Math.min(modal.selectedIndex - Math.floor(maxVisible / 2), items.length - maxVisible));
900720
- }
900721
- const endIdx = Math.min(startIdx + maxVisible, items.length);
900722
- let lastCategory;
900723
- for (let i5 = startIdx;i5 < endIdx; i5++) {
900724
- const item = items[i5];
900725
- const isSelected = i5 === modal.selectedIndex;
900726
- if (item.category && item.category !== lastCategory) {
900727
- lastCategory = item.category;
900728
- const categoryLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
900729
- putText(categoryLine, layout.margin + 2, layout.innerWidth, fitDisplay(` ${item.category}`, layout.innerWidth), {
900730
- fg: CATEGORY_FG,
900731
- dim: true
900732
- });
900733
- lines.push(categoryLine);
900734
- }
900735
- const indicator = isSelected ? `${OVERLAY_GLYPHS.selected} ` : " ";
900736
- const indicatorWidth = 2;
900737
- const remaining = layout.innerWidth - indicatorWidth;
900738
- const labelColor = isSelected ? TITLE_FG : item.fg ?? BODY_FG;
900739
- const detailColor = isSelected ? BODY_FG : MUTED_FG;
900740
- const labelWidth = item.detail ? fitLabelDetailColumns(item.label, item.detail, remaining).labelWidth : remaining;
900741
- const labelLine = createOverlayContentLine(width, layout, BORDER_FG, isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg);
900742
- putText(labelLine, layout.margin + 2, indicatorWidth, indicator, {
900743
- fg: isSelected ? TITLE_FG : MUTED_FG,
900744
- bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg,
900745
- bold: isSelected
900746
- });
900747
- putText(labelLine, layout.margin + 2 + indicatorWidth, labelWidth, fitDisplay(truncateDisplay(item.label, labelWidth), labelWidth), {
900748
- fg: labelColor,
900749
- bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg,
900750
- bold: isSelected
900751
- });
900752
- if (item.detail) {
900753
- const detailWidth = fitLabelDetailColumns(item.label, item.detail, remaining).detailWidth;
900754
- if (detailWidth >= 12) {
900755
- putText(labelLine, layout.margin + 2 + indicatorWidth + labelWidth, 2, " ", {
900756
- fg: BODY_FG,
900757
- bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg
900758
- });
900759
- putText(labelLine, layout.margin + 2 + indicatorWidth + labelWidth + 2, detailWidth, fitDisplay(truncateDisplay(item.detail, detailWidth), detailWidth), {
900760
- fg: detailColor,
900761
- bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg
900762
- });
900763
- lines.push(labelLine);
900764
- } else {
900765
- lines.push(labelLine);
900766
- const wrappedDetails = wrapWithHangingIndent(item.detail, Math.max(8, remaining), "", 2);
900767
- for (const detailLineText of wrappedDetails) {
900768
- const detailLine = createOverlayContentLine(width, layout, BORDER_FG, isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg);
900769
- putText(detailLine, layout.margin + 2 + indicatorWidth, remaining, fitDisplay(truncateDisplay(detailLineText, remaining), remaining), {
900770
- fg: detailColor,
900771
- bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg,
900772
- dim: !isSelected
900773
- });
900774
- lines.push(detailLine);
900775
- }
900776
- }
900777
- } else {
900778
- lines.push(labelLine);
900779
- }
900780
- }
900781
- if (items.length > maxVisible) {
900782
- const above = startIdx;
900783
- const below = items.length - endIdx;
900784
- const scrollHint = above > 0 && below > 0 ? `(${above} above, ${below} below)` : below > 0 ? `(${below} below)` : `(${above} above)`;
900785
- const hintLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
900786
- putText(hintLine, layout.margin + 2, layout.innerWidth, fitDisplay(scrollHint, layout.innerWidth), { fg: MUTED_FG, dim: true });
900787
- lines.push(hintLine);
900788
- }
900789
- }
900790
- const footerLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
900791
- const selectedItem = modal.getSelected();
900792
- const primaryVerb = primaryVerbForAction(selectedItem?.primaryAction);
900793
- let hints = `${SELECTION_MODAL_NAVIGATION_HINT} ${primaryVerb} ${SELECTION_MODAL_CLOSE_HINT}`;
900794
- if (modal.allowSearch)
900795
- hints += ` ${SELECTION_MODAL_SEARCH_HINT}`;
900796
- if (selectedItem?.primaryAction === "toggle" && !selectedItem.actions)
900797
- hints += ` ${SELECTION_MODAL_SPACE_TOGGLE_HINT}`;
900798
- if (selectedItem?.actions)
900799
- hints += ` ${selectedItem.actions}`;
900800
- putText(footerLine, layout.margin + 2, layout.innerWidth, fitDisplay(truncateDisplay(hints, layout.innerWidth), layout.innerWidth), { fg: MUTED_FG, dim: true });
900801
- lines.push(footerLine);
900802
- lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.bottomLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.bottomRight, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg));
900803
- return lines;
900804
- }
900805
-
900806
- // src/renderer/bottom-bar.ts
900807
- function createBottomBarLine(width, style) {
900808
- const line2 = createEmptyLine(width);
900809
- for (let col = 0;col < width; col++) {
900810
- line2[col] = createStyledCell(" ", {
900811
- fg: style.fg,
900812
- bg: style.bg,
900813
- bold: style.bold ?? false,
900814
- dim: style.dim ?? false,
900815
- underline: style.underline ?? false
900816
- });
900817
- }
900818
- return line2;
900819
- }
900820
- function writeBottomBarText(line2, startX, maxWidth, text, style) {
900821
- let x2 = startX;
900822
- let used = 0;
900823
- for (const ch of text) {
900824
- const cellWidth = getDisplayWidth(ch);
900825
- if (cellWidth <= 0)
900826
- continue;
900827
- if (used + cellWidth > maxWidth || x2 >= line2.length)
900828
- break;
900829
- line2[x2] = createStyledCell(ch, {
900830
- fg: style.fg,
900831
- bg: style.bg,
900832
- bold: style.bold ?? false,
900833
- dim: style.dim ?? false,
900834
- underline: style.underline ?? false
900835
- });
900836
- if (cellWidth > 1 && x2 + 1 < line2.length) {
900837
- line2[x2 + 1] = createStyledCell(" ", {
900838
- fg: style.fg,
900839
- bg: style.bg,
900840
- bold: style.bold ?? false,
900841
- dim: style.dim ?? false,
900842
- underline: style.underline ?? false
900843
- });
900844
- }
900845
- x2 += cellWidth;
900846
- used += cellWidth;
900847
- }
900848
- }
900849
-
900850
- // src/renderer/search-overlay.ts
900851
- var SEARCH_OVERLAY_LABEL = " Find: ";
900852
- var SEARCH_OVERLAY_NO_MATCHES = "No matches";
900853
- var SEARCH_OVERLAY_COUNT_SUFFIX = "up/down";
900854
- var SEARCH_OVERLAY_LOCKED_HINTS = " [Up/Down] or [jk] navigate [Bksp] edit [Esc] close";
900855
- var SEARCH_OVERLAY_UNLOCKED_HINTS = " [Enter/Tab] lock [Esc] close";
900856
- function searchOverlayMatchCount(current, total) {
900857
- return `${current}/${total} ${SEARCH_OVERLAY_COUNT_SUFFIX}`;
900858
- }
900859
- function renderSearchOverlay(manager5, width) {
900860
- const matchCount = manager5.matches?.length > 0 ? searchOverlayMatchCount(manager5.currentMatch + 1, manager5.matches.length) : manager5.query.length > 0 ? SEARCH_OVERLAY_NO_MATCHES : "";
900861
- const locked = manager5.locked;
900862
- const cursor = locked ? "" : "\u2588";
900863
- const queryDisplay = manager5.query + cursor;
900864
- const hints = locked ? SEARCH_OVERLAY_LOCKED_HINTS : SEARCH_OVERLAY_UNLOCKED_HINTS;
900865
- const label = SEARCH_OVERLAY_LABEL;
900866
- const matchStr = matchCount ? ` ${matchCount}` : "";
900867
- const leftPart = label + queryDisplay;
900868
- const hintsW = getDisplayWidth(hints);
900869
- const matchStrW = getDisplayWidth(matchStr);
900870
- const leftWidth = width - hintsW - matchStrW - 2;
900871
- const truncatedLeft = fitDisplay(getDisplayWidth(leftPart) > leftWidth ? truncateDisplay(leftPart, leftWidth) : leftPart, leftWidth);
900872
- const fullLine = truncatedLeft + matchStr + hints + " ";
900873
- const line2 = createBottomBarLine(width, { fg: "#000000", bg: "#00ffcc" });
900874
- writeBottomBarText(line2, 0, width, fitDisplay(truncateDisplay(fullLine, width), width), { fg: "#000000", bg: "#00ffcc" });
900875
- if (matchStr.length > 0) {
900876
- const matchStart = getDisplayWidth(truncatedLeft);
900877
- writeBottomBarText(line2, matchStart, matchStrW, matchStr, { fg: "#888888", bg: "#00ffcc", dim: true });
900878
- }
900879
- return [line2];
900880
- }
900881
-
900882
- // src/renderer/history-search-overlay.ts
900883
- var HISTORY_SEARCH_PREFIX = "(reverse-i-search)`";
900884
- var HISTORY_SEARCH_FAILED_PREFIX = "(failed reverse-i-search)`";
900885
- var HISTORY_SEARCH_QUERY_SUFFIX = "': ";
900886
- function historySearchLabel(prefix, query2) {
900887
- return prefix + query2 + HISTORY_SEARCH_QUERY_SUFFIX;
900888
- }
900889
- function truncateToWidth3(text, maxWidth) {
900890
- let usedWidth = 0;
900891
- let result2 = "";
900892
- let i5 = 0;
900893
- while (i5 < text.length) {
900894
- const code2 = text.codePointAt(i5);
900895
- const charLen = code2 > 65535 ? 2 : 1;
900896
- const charWidth = getDisplayWidth(text.slice(i5, i5 + charLen));
900897
- if (usedWidth + charWidth > maxWidth)
900898
- break;
900899
- result2 += text.slice(i5, i5 + charLen);
900900
- usedWidth += charWidth;
900901
- i5 += charLen;
900902
- }
900903
- return result2 + " ".repeat(maxWidth - usedWidth);
900904
- }
900905
- function renderHistorySearchOverlay(historySearch, width) {
900906
- if (width <= 0)
900907
- return [];
900908
- const match = historySearch.currentMatch;
900909
- const hasMatch = match !== null && historySearch.query.length > 0;
900910
- const noMatch = historySearch.query.length > 0 && !hasMatch;
900911
- const prefix = noMatch ? HISTORY_SEARCH_FAILED_PREFIX : HISTORY_SEARCH_PREFIX;
900912
- const matchText = hasMatch ? match?.entry ?? "" : "";
900913
- const label = historySearchLabel(prefix, historySearch.query);
900914
- const full = truncateToWidth3(label + matchText, width);
900915
- const line2 = createBottomBarLine(width, { fg: "#000000", bg: "#00ffcc" });
900916
- writeBottomBarText(line2, 0, width, full, { fg: "#000000", bg: "#00ffcc" });
900917
- if (hasMatch && match) {
900918
- const labelW = getDisplayWidth(label);
900919
- const matchStartCol = labelW + match.matchStart;
900920
- const matchEndCol = matchStartCol + match.matchLength;
900921
- const highlightWidth = Math.max(0, matchEndCol - matchStartCol);
900922
- const matchedSlice = truncateToWidth3(match.entry.slice(match.matchStart, match.matchStart + match.matchLength), highlightWidth);
900923
- writeBottomBarText(line2, matchStartCol, highlightWidth, matchedSlice, {
900924
- fg: "#000000",
900925
- bg: "#00ffcc",
900926
- bold: true,
900927
- underline: true
900928
- });
900929
- }
900930
- return [line2];
900931
- }
900932
-
900933
900874
  // src/renderer/settings-modal-helpers.ts
900934
900875
  function maskSecretValue(value) {
900935
900876
  if (value.length === 0)
@@ -901604,6 +901545,518 @@ function renderSettingsModal(modal, width, viewportHeight = 24) {
901604
901545
  });
901605
901546
  }
901606
901547
 
901548
+ // src/renderer/layout-engine.ts
901549
+ function clamp5(value, min2, max2) {
901550
+ return Math.max(min2, Math.min(max2, value));
901551
+ }
901552
+ function createShellLayout(request2) {
901553
+ const width = Math.max(1, request2.width);
901554
+ const height = Math.max(1, request2.height);
901555
+ const headerHeight = clamp5(request2.headerHeight, 0, height);
901556
+ const footerHeight = clamp5(request2.footerHeight, 0, Math.max(0, height - headerHeight));
901557
+ const bodyHeight = Math.max(0, height - headerHeight - footerHeight);
901558
+ const hasPanel = typeof request2.panelWidth === "number" && request2.panelWidth > 0;
901559
+ const safePanelWidth = hasPanel ? clamp5(request2.panelWidth, 1, Math.max(1, width - 2)) : 0;
901560
+ const separatorWidth = hasPanel ? 1 : 0;
901561
+ const conversationWidth = hasPanel ? Math.max(1, width - safePanelWidth - separatorWidth) : width;
901562
+ return {
901563
+ screen: { x: 0, y: 0, width, height },
901564
+ header: { x: 0, y: 0, width, height: headerHeight },
901565
+ body: { x: 0, y: headerHeight, width, height: bodyHeight },
901566
+ footer: { x: 0, y: headerHeight + bodyHeight, width, height: footerHeight },
901567
+ conversation: { x: 0, y: headerHeight, width: conversationWidth, height: bodyHeight },
901568
+ panel: hasPanel ? {
901569
+ x: conversationWidth + separatorWidth,
901570
+ y: headerHeight,
901571
+ width: safePanelWidth,
901572
+ height: bodyHeight
901573
+ } : undefined,
901574
+ separatorX: hasPanel ? conversationWidth : undefined
901575
+ };
901576
+ }
901577
+ function createSplitPaneLayout(totalRows, ratio, options = {}) {
901578
+ const topTabRows = options.topTabRows ?? 1;
901579
+ const bottomTabRows = options.bottomTabRows ?? 1;
901580
+ const separatorRows = options.separatorRows ?? 1;
901581
+ const chromeRows = topTabRows + bottomTabRows + separatorRows;
901582
+ const contentRows = Math.max(0, totalRows - chromeRows);
901583
+ const normalizedRatio = clamp5(ratio, 0.2, 0.8);
901584
+ const topContentRows = contentRows <= 1 ? contentRows : Math.max(1, Math.floor(contentRows * normalizedRatio));
901585
+ const bottomContentRows = contentRows <= 1 ? 0 : Math.max(1, contentRows - topContentRows);
901586
+ return {
901587
+ totalRows,
901588
+ topTabRows,
901589
+ bottomTabRows,
901590
+ separatorRows,
901591
+ topContentRows,
901592
+ bottomContentRows
901593
+ };
901594
+ }
901595
+
901596
+ // src/renderer/process-indicator.ts
901597
+ function truncateToWidth2(text, maxWidth) {
901598
+ let width = 0;
901599
+ let i5 = 0;
901600
+ for (const char of text) {
901601
+ const cw = getDisplayWidth(char);
901602
+ if (width + cw > maxWidth)
901603
+ break;
901604
+ width += cw;
901605
+ i5 += char.length;
901606
+ }
901607
+ return text.slice(0, i5);
901608
+ }
901609
+ function renderProcessIndicator(width, agentCount, toolCount, focused = false, agentProgress) {
901610
+ const total = agentCount + toolCount;
901611
+ const delegationLabel = (count) => `${count} delegation${count !== 1 ? "s" : ""}`;
901612
+ const renderPlainStatus = (text, style) => [UIFactory.stringToLine(` ${text}`, width, style)];
901613
+ const renderFocusedStatus = (text) => {
901614
+ const bg = "#31506f";
901615
+ const fg = "#eefaff";
901616
+ const markerFg = "#7dd3fc";
901617
+ const line2 = UIFactory.stringToLine(" ".repeat(width), width, { fg: "238" });
901618
+ const prefix = `${GLYPHS.navigation.selected} `;
901619
+ const body2 = truncateToWidth2(text, Math.max(0, width - 8));
901620
+ const highlighted = ` ${prefix}${body2} `;
901621
+ const startX = 2;
901622
+ for (let i5 = 0;i5 < highlighted.length && startX + i5 < width - 2; i5++) {
901623
+ const ch = highlighted[i5];
901624
+ const isMarker = i5 < prefix.length + 1;
901625
+ line2[startX + i5].char = ch;
901626
+ line2[startX + i5].fg = isMarker ? markerFg : fg;
901627
+ line2[startX + i5].bg = bg;
901628
+ line2[startX + i5].bold = true;
901629
+ line2[startX + i5].dim = false;
901630
+ }
901631
+ return [line2];
901632
+ };
901633
+ if (focused) {
901634
+ const parts3 = [];
901635
+ if (agentCount > 0)
901636
+ parts3.push(delegationLabel(agentCount));
901637
+ if (toolCount > 0)
901638
+ parts3.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""} running`);
901639
+ const label2 = total === 0 ? `No runtime activity ${GLYPHS.status.pending} back to input` : `${parts3.join(` ${GLYPHS.navigation.pipeSeparator} `)} ${GLYPHS.status.pending} Enter to open ${GLYPHS.status.pending} back to input`;
901640
+ return renderFocusedStatus(label2);
901641
+ }
901642
+ if (total === 0) {
901643
+ return renderPlainStatus("No runtime activity", { fg: "238", dim: true });
901644
+ }
901645
+ const parts2 = [];
901646
+ if (agentCount > 0) {
901647
+ parts2.push(delegationLabel(agentCount));
901648
+ }
901649
+ if (toolCount > 0) {
901650
+ parts2.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""} running`);
901651
+ }
901652
+ const PROGRESS_RESERVED_CHARS = 43;
901653
+ const progressMaxLen = Math.max(0, width - PROGRESS_RESERVED_CHARS);
901654
+ const progressSuffix = agentProgress && progressMaxLen > 10 ? ` | ${agentProgress.length > progressMaxLen ? agentProgress.slice(0, Math.max(0, progressMaxLen - 3)) + "..." : agentProgress}` : "";
901655
+ const label = `${parts2.join(` ${GLYPHS.navigation.pipeSeparator} `)}${progressSuffix}`;
901656
+ const hint = ` ${GLYPHS.status.pending} Enter to view`;
901657
+ return renderPlainStatus(`${label}${hint}`, { fg: "#00ffff", bold: true });
901658
+ }
901659
+
901660
+ // src/renderer/shell-surface.ts
901661
+ var FOOTER_BASE_ROWS = 9;
901662
+ var CONTEXT_PROGRESS_ROWS = 2;
901663
+ var PROCESS_INDICATOR_ROWS = 1;
901664
+ function estimateShellFooterHeight(promptLineCount, contextWindow) {
901665
+ const safePromptLines = Math.max(1, promptLineCount);
901666
+ const progressRows = contextWindow && contextWindow > 0 ? CONTEXT_PROGRESS_ROWS : 0;
901667
+ return FOOTER_BASE_ROWS + safePromptLines + progressRows + PROCESS_INDICATOR_ROWS;
901668
+ }
901669
+ function buildShellFooter(options) {
901670
+ const lines = UIFactory.createFooter(options.width, options.promptText, options.usage, options.showExitNotice, options.lastCopyTime, options.model, options.toolCount, options.promptCursorPos, options.workingDir, options.provider, options.contextWindow, options.compactThreshold, options.dangerMode, options.lastInputTokens, options.commandArgsHint, options.hitlMode, options.promptFocused ?? !options.indicatorFocused, options.composerMode, options.composerStatus, options.composerFlags, options.composerPendingRisk);
901671
+ const processIndicator = renderProcessIndicator(options.width, options.runningAgentCount, options.runningProcessCount, options.indicatorFocused, options.runningAgentProgress);
901672
+ const inputBoxRows = Math.max(1, options.promptLineCount) + 2;
901673
+ lines.splice(inputBoxRows, 0, ...processIndicator);
901674
+ return { lines, height: lines.length };
901675
+ }
901676
+
901677
+ // src/renderer/conversation-layout.ts
901678
+ function buildConversationViewport(request2) {
901679
+ const overlayRows = request2.overlayRows ?? 0;
901680
+ const effectiveHeight = Math.max(0, request2.viewportHeight - overlayRows);
901681
+ const lineCount = request2.conversation.history.getLineCount();
901682
+ const maxScroll = Math.max(0, lineCount - effectiveHeight);
901683
+ const nextScrollTop = request2.scrollLocked ? maxScroll : Math.max(0, Math.min(request2.scrollTop, maxScroll));
901684
+ const viewport = request2.conversation.history.getSnapshot(nextScrollTop, effectiveHeight, request2.width);
901685
+ return {
901686
+ effectiveHeight,
901687
+ maxScroll,
901688
+ nextScrollTop,
901689
+ viewport
901690
+ };
901691
+ }
901692
+ function overlayViewportBottom(viewport, overlay, width, viewportHeight, bottomInset = 0) {
901693
+ if (overlay.length === 0)
901694
+ return [...viewport];
901695
+ const next = [...viewport];
901696
+ const targetStart = Math.max(0, viewportHeight - bottomInset - overlay.length);
901697
+ next.length = Math.min(next.length, targetStart);
901698
+ while (next.length < targetStart)
901699
+ next.push(createEmptyLine(width));
901700
+ next.push(...overlay);
901701
+ return next;
901702
+ }
901703
+ function replaceViewportWithOverlay(overlay, width, viewportHeight) {
901704
+ const next = [];
901705
+ const pad = Math.max(0, viewportHeight - overlay.length);
901706
+ for (let i5 = 0;i5 < pad; i5++)
901707
+ next.push(createEmptyLine(width));
901708
+ next.push(...overlay);
901709
+ return next;
901710
+ }
901711
+
901712
+ // src/renderer/file-picker-overlay.ts
901713
+ var FILE_PICKER_TITLE = "Select File";
901714
+ var FILE_PICKER_SEARCH_PREFIX = "@ ";
901715
+ var FILE_PICKER_EMPTY_MESSAGE = "No matching files";
901716
+ var FILE_PICKER_HINTS = "[Up/Down] Navigate [/] Search [Enter] Select [Esc] Cancel";
901717
+ function renderFilePickerOverlay(picker, width, viewportHeight = 24) {
901718
+ const lines = [];
901719
+ const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
901720
+ chromeRows: 4,
901721
+ maxWidth: 70,
901722
+ minContentRows: 6,
901723
+ maxContentRows: 10
901724
+ });
901725
+ const layout = createOverlayBoxLayout(width, metrics.margin, metrics.boxWidth);
901726
+ const contentW = layout.innerWidth;
901727
+ const borderFg = DEFAULT_OVERLAY_PALETTE.borderFg;
901728
+ const titleFg = DEFAULT_OVERLAY_PALETTE.titleFg;
901729
+ const bodyFg = DEFAULT_OVERLAY_PALETTE.bodyFg;
901730
+ const mutedFg = DEFAULT_OVERLAY_PALETTE.mutedFg;
901731
+ const selectedBg = DEFAULT_OVERLAY_PALETTE.selectedBg;
901732
+ const titleLine = createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.topLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.topRight, borderFg, DEFAULT_OVERLAY_PALETTE.titleBg);
901733
+ putOverlayText(titleLine, layout.margin + 2, layout.width - 4, FILE_PICKER_TITLE, { fg: titleFg, bold: true });
901734
+ lines.push(titleLine);
901735
+ const queryDisplay = picker.query || "";
901736
+ const searchLine = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.inputBg);
901737
+ const searchPrefix = FILE_PICKER_SEARCH_PREFIX;
901738
+ const queryText = fitDisplay(`${queryDisplay}${picker.searchFocused ? OVERLAY_GLYPHS.cursor : ""}`, Math.max(0, contentW - getDisplayWidth(searchPrefix)));
901739
+ putOverlayText(searchLine, layout.margin + 2, getDisplayWidth(searchPrefix), searchPrefix, { fg: picker.searchFocused ? bodyFg : mutedFg });
901740
+ putOverlayText(searchLine, layout.margin + 2 + getDisplayWidth(searchPrefix), contentW - getDisplayWidth(searchPrefix), queryText, { fg: picker.query.length > 0 || picker.searchFocused ? bodyFg : mutedFg });
901741
+ lines.push(searchLine);
901742
+ lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.teeLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.teeRight, borderFg, DEFAULT_OVERLAY_PALETTE.sectionBg));
901743
+ if (picker.results.length === 0) {
901744
+ const noResults = createOverlayContentLine(width, layout, borderFg, DEFAULT_OVERLAY_PALETTE.bodyBg);
901745
+ putOverlayText(noResults, layout.margin + 2, contentW, fitDisplay(FILE_PICKER_EMPTY_MESSAGE, contentW), { fg: "244", dim: true });
901746
+ lines.push(noResults);
901747
+ } else {
901748
+ const maxVisible = metrics.contentRows;
901749
+ let startIdx = 0;
901750
+ if (picker.results.length > maxVisible) {
901751
+ startIdx = Math.max(0, Math.min(picker.selectedIndex - Math.floor(maxVisible / 2), picker.results.length - maxVisible));
901752
+ }
901753
+ const endIdx = Math.min(startIdx + maxVisible, picker.results.length);
901754
+ for (let i5 = startIdx;i5 < endIdx; i5++) {
901755
+ const file2 = picker.results[i5];
901756
+ const isSelected = i5 === picker.selectedIndex;
901757
+ const indicator = isSelected ? `${OVERLAY_GLYPHS.selected} ` : " ";
901758
+ const displayFile = getDisplayWidth(file2) > contentW - 2 ? truncateDisplay(file2, contentW - 2) : file2;
901759
+ const line2 = createOverlayContentLine(width, layout, borderFg, isSelected ? selectedBg : DEFAULT_OVERLAY_PALETTE.bodyBg);
901760
+ putOverlayText(line2, layout.margin + 2, contentW, fitDisplay(indicator + fitDisplay(displayFile, contentW - 2), contentW), {
901761
+ fg: isSelected ? titleFg : file2.endsWith("/") ? titleFg : bodyFg,
901762
+ bg: isSelected ? selectedBg : DEFAULT_OVERLAY_PALETTE.bodyBg,
901763
+ bold: isSelected
901764
+ });
901765
+ lines.push(line2);
901766
+ }
901767
+ }
901768
+ const bottomLine = createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.bottomLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.bottomRight, borderFg, DEFAULT_OVERLAY_PALETTE.sectionBg);
901769
+ putOverlayText(bottomLine, layout.margin + 2, layout.width - 4, truncateDisplay(FILE_PICKER_HINTS, layout.width - 4), { fg: mutedFg, dim: true });
901770
+ lines.push(bottomLine);
901771
+ return lines;
901772
+ }
901773
+
901774
+ // src/renderer/selection-modal-overlay.ts
901775
+ var BORDER_FG = DEFAULT_OVERLAY_PALETTE.borderFg;
901776
+ var TITLE_FG = DEFAULT_OVERLAY_PALETTE.titleFg;
901777
+ var BODY_FG = DEFAULT_OVERLAY_PALETTE.bodyFg;
901778
+ var MUTED_FG = DEFAULT_OVERLAY_PALETTE.mutedFg;
901779
+ var CATEGORY_FG = "#4488cc";
901780
+ var SELECTED_BG = DEFAULT_OVERLAY_PALETTE.selectedBg;
901781
+ var SELECTION_MODAL_SEARCH_LABEL = " Search";
901782
+ var SELECTION_MODAL_RESULTS_LABEL = " Results";
901783
+ var SELECTION_MODAL_NO_MATCHING_ITEMS = "No matching items";
901784
+ var SELECTION_MODAL_NO_ITEMS = "No items";
901785
+ var SELECTION_MODAL_NAVIGATION_HINT = "[Up/Down] Navigate";
901786
+ var SELECTION_MODAL_CLOSE_HINT = "[Esc] Close";
901787
+ var SELECTION_MODAL_SEARCH_HINT = "[/] Search";
901788
+ var SELECTION_MODAL_SPACE_TOGGLE_HINT = "[Space] Toggle";
901789
+ function putText(line2, startX, maxWidth, text, style) {
901790
+ putOverlayText(line2, startX, maxWidth, text, style);
901791
+ }
901792
+ function primaryVerbForAction(primaryAction) {
901793
+ return primaryAction === "toggle" ? "[Enter] Toggle" : primaryAction === "edit" ? "[Enter] Edit" : primaryAction === "delete" ? "[Enter] Delete" : "[Enter] Select";
901794
+ }
901795
+ function renderSelectionModalOverlay(modal, width, viewportHeight = 24) {
901796
+ const lines = [];
901797
+ const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
901798
+ margin: 4,
901799
+ maxWidth: 72,
901800
+ chromeRows: modal.allowSearch ? 5 : 4,
901801
+ minContentRows: 6,
901802
+ maxContentRows: 10
901803
+ });
901804
+ const layout = createOverlayBoxLayout(width, metrics.margin, metrics.boxWidth);
901805
+ lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.topLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.topRight, BORDER_FG, DEFAULT_OVERLAY_PALETTE.titleBg));
901806
+ const titleLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.titleBg);
901807
+ putText(titleLine, layout.margin + 2, layout.innerWidth, fitDisplay(truncateDisplay(modal.title, layout.innerWidth), layout.innerWidth), { fg: TITLE_FG, bold: true });
901808
+ lines.push(titleLine);
901809
+ if (modal.allowSearch) {
901810
+ const labelLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
901811
+ putText(labelLine, layout.margin + 2, layout.innerWidth, fitDisplay(SELECTION_MODAL_SEARCH_LABEL, layout.innerWidth), {
901812
+ fg: CATEGORY_FG,
901813
+ dim: true
901814
+ });
901815
+ lines.push(labelLine);
901816
+ const searchLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.inputBg);
901817
+ const prefix = "/ ";
901818
+ const queryAreaWidth = layout.innerWidth - getDisplayWidth(prefix);
901819
+ const queryValue = modal.query + (modal.searchFocused ? OVERLAY_GLYPHS.cursor : "");
901820
+ const queryText = fitDisplay(truncateDisplay(queryValue, queryAreaWidth), queryAreaWidth);
901821
+ putText(searchLine, layout.margin + 2, getDisplayWidth(prefix), prefix, { fg: modal.searchFocused ? BODY_FG : MUTED_FG });
901822
+ putText(searchLine, layout.margin + 2 + getDisplayWidth(prefix), queryAreaWidth, queryText, {
901823
+ fg: modal.query.length > 0 || modal.searchFocused ? BODY_FG : MUTED_FG
901824
+ });
901825
+ lines.push(searchLine);
901826
+ lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.teeLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.teeRight, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg));
901827
+ } else {
901828
+ lines.push(createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg));
901829
+ }
901830
+ const listTitle = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
901831
+ putText(listTitle, layout.margin + 2, layout.innerWidth, fitDisplay(SELECTION_MODAL_RESULTS_LABEL, layout.innerWidth), {
901832
+ fg: CATEGORY_FG,
901833
+ dim: true
901834
+ });
901835
+ lines.push(listTitle);
901836
+ const items = modal.filteredItems;
901837
+ if (items.length === 0) {
901838
+ const line2 = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.bodyBg);
901839
+ const message = modal.query ? SELECTION_MODAL_NO_MATCHING_ITEMS : SELECTION_MODAL_NO_ITEMS;
901840
+ putText(line2, layout.margin + 2, layout.innerWidth, fitDisplay(message, layout.innerWidth), { fg: MUTED_FG, dim: true });
901841
+ lines.push(line2);
901842
+ } else {
901843
+ const maxVisible = metrics.contentRows;
901844
+ let startIdx = 0;
901845
+ if (items.length > maxVisible) {
901846
+ startIdx = Math.max(0, Math.min(modal.selectedIndex - Math.floor(maxVisible / 2), items.length - maxVisible));
901847
+ }
901848
+ const endIdx = Math.min(startIdx + maxVisible, items.length);
901849
+ let lastCategory;
901850
+ for (let i5 = startIdx;i5 < endIdx; i5++) {
901851
+ const item = items[i5];
901852
+ const isSelected = i5 === modal.selectedIndex;
901853
+ if (item.category && item.category !== lastCategory) {
901854
+ lastCategory = item.category;
901855
+ const categoryLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
901856
+ putText(categoryLine, layout.margin + 2, layout.innerWidth, fitDisplay(` ${item.category}`, layout.innerWidth), {
901857
+ fg: CATEGORY_FG,
901858
+ dim: true
901859
+ });
901860
+ lines.push(categoryLine);
901861
+ }
901862
+ const indicator = isSelected ? `${OVERLAY_GLYPHS.selected} ` : " ";
901863
+ const indicatorWidth = 2;
901864
+ const remaining = layout.innerWidth - indicatorWidth;
901865
+ const labelColor = isSelected ? TITLE_FG : item.fg ?? BODY_FG;
901866
+ const detailColor = isSelected ? BODY_FG : MUTED_FG;
901867
+ const labelWidth = item.detail ? fitLabelDetailColumns(item.label, item.detail, remaining).labelWidth : remaining;
901868
+ const labelLine = createOverlayContentLine(width, layout, BORDER_FG, isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg);
901869
+ putText(labelLine, layout.margin + 2, indicatorWidth, indicator, {
901870
+ fg: isSelected ? TITLE_FG : MUTED_FG,
901871
+ bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg,
901872
+ bold: isSelected
901873
+ });
901874
+ putText(labelLine, layout.margin + 2 + indicatorWidth, labelWidth, fitDisplay(truncateDisplay(item.label, labelWidth), labelWidth), {
901875
+ fg: labelColor,
901876
+ bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg,
901877
+ bold: isSelected
901878
+ });
901879
+ if (item.detail) {
901880
+ const detailWidth = fitLabelDetailColumns(item.label, item.detail, remaining).detailWidth;
901881
+ if (detailWidth >= 12) {
901882
+ putText(labelLine, layout.margin + 2 + indicatorWidth + labelWidth, 2, " ", {
901883
+ fg: BODY_FG,
901884
+ bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg
901885
+ });
901886
+ putText(labelLine, layout.margin + 2 + indicatorWidth + labelWidth + 2, detailWidth, fitDisplay(truncateDisplay(item.detail, detailWidth), detailWidth), {
901887
+ fg: detailColor,
901888
+ bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg
901889
+ });
901890
+ lines.push(labelLine);
901891
+ } else {
901892
+ lines.push(labelLine);
901893
+ const wrappedDetails = wrapWithHangingIndent(item.detail, Math.max(8, remaining), "", 2);
901894
+ for (const detailLineText of wrappedDetails) {
901895
+ const detailLine = createOverlayContentLine(width, layout, BORDER_FG, isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg);
901896
+ putText(detailLine, layout.margin + 2 + indicatorWidth, remaining, fitDisplay(truncateDisplay(detailLineText, remaining), remaining), {
901897
+ fg: detailColor,
901898
+ bg: isSelected ? SELECTED_BG : DEFAULT_OVERLAY_PALETTE.bodyBg,
901899
+ dim: !isSelected
901900
+ });
901901
+ lines.push(detailLine);
901902
+ }
901903
+ }
901904
+ } else {
901905
+ lines.push(labelLine);
901906
+ }
901907
+ }
901908
+ if (items.length > maxVisible) {
901909
+ const above = startIdx;
901910
+ const below = items.length - endIdx;
901911
+ const scrollHint = above > 0 && below > 0 ? `(${above} above, ${below} below)` : below > 0 ? `(${below} below)` : `(${above} above)`;
901912
+ const hintLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
901913
+ putText(hintLine, layout.margin + 2, layout.innerWidth, fitDisplay(scrollHint, layout.innerWidth), { fg: MUTED_FG, dim: true });
901914
+ lines.push(hintLine);
901915
+ }
901916
+ }
901917
+ const footerLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
901918
+ const selectedItem = modal.getSelected();
901919
+ const primaryVerb = primaryVerbForAction(selectedItem?.primaryAction);
901920
+ let hints = `${SELECTION_MODAL_NAVIGATION_HINT} ${primaryVerb} ${SELECTION_MODAL_CLOSE_HINT}`;
901921
+ if (modal.allowSearch)
901922
+ hints += ` ${SELECTION_MODAL_SEARCH_HINT}`;
901923
+ if (selectedItem?.primaryAction === "toggle" && !selectedItem.actions)
901924
+ hints += ` ${SELECTION_MODAL_SPACE_TOGGLE_HINT}`;
901925
+ if (selectedItem?.actions)
901926
+ hints += ` ${selectedItem.actions}`;
901927
+ putText(footerLine, layout.margin + 2, layout.innerWidth, fitDisplay(truncateDisplay(hints, layout.innerWidth), layout.innerWidth), { fg: MUTED_FG, dim: true });
901928
+ lines.push(footerLine);
901929
+ lines.push(createOverlayFilledBorderLine(width, layout, OVERLAY_GLYPHS.bottomLeft, OVERLAY_GLYPHS.horizontal, OVERLAY_GLYPHS.bottomRight, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg));
901930
+ return lines;
901931
+ }
901932
+
901933
+ // src/renderer/bottom-bar.ts
901934
+ function createBottomBarLine(width, style) {
901935
+ const line2 = createEmptyLine(width);
901936
+ for (let col = 0;col < width; col++) {
901937
+ line2[col] = createStyledCell(" ", {
901938
+ fg: style.fg,
901939
+ bg: style.bg,
901940
+ bold: style.bold ?? false,
901941
+ dim: style.dim ?? false,
901942
+ underline: style.underline ?? false
901943
+ });
901944
+ }
901945
+ return line2;
901946
+ }
901947
+ function writeBottomBarText(line2, startX, maxWidth, text, style) {
901948
+ let x2 = startX;
901949
+ let used = 0;
901950
+ for (const ch of text) {
901951
+ const cellWidth = getDisplayWidth(ch);
901952
+ if (cellWidth <= 0)
901953
+ continue;
901954
+ if (used + cellWidth > maxWidth || x2 >= line2.length)
901955
+ break;
901956
+ line2[x2] = createStyledCell(ch, {
901957
+ fg: style.fg,
901958
+ bg: style.bg,
901959
+ bold: style.bold ?? false,
901960
+ dim: style.dim ?? false,
901961
+ underline: style.underline ?? false
901962
+ });
901963
+ if (cellWidth > 1 && x2 + 1 < line2.length) {
901964
+ line2[x2 + 1] = createStyledCell(" ", {
901965
+ fg: style.fg,
901966
+ bg: style.bg,
901967
+ bold: style.bold ?? false,
901968
+ dim: style.dim ?? false,
901969
+ underline: style.underline ?? false
901970
+ });
901971
+ }
901972
+ x2 += cellWidth;
901973
+ used += cellWidth;
901974
+ }
901975
+ }
901976
+
901977
+ // src/renderer/search-overlay.ts
901978
+ var SEARCH_OVERLAY_LABEL = " Find: ";
901979
+ var SEARCH_OVERLAY_NO_MATCHES = "No matches";
901980
+ var SEARCH_OVERLAY_COUNT_SUFFIX = "up/down";
901981
+ var SEARCH_OVERLAY_LOCKED_HINTS = " [Up/Down] or [jk] navigate [Bksp] edit [Esc] close";
901982
+ var SEARCH_OVERLAY_UNLOCKED_HINTS = " [Enter/Tab] lock [Esc] close";
901983
+ function searchOverlayMatchCount(current, total) {
901984
+ return `${current}/${total} ${SEARCH_OVERLAY_COUNT_SUFFIX}`;
901985
+ }
901986
+ function renderSearchOverlay(manager5, width) {
901987
+ const matchCount = manager5.matches?.length > 0 ? searchOverlayMatchCount(manager5.currentMatch + 1, manager5.matches.length) : manager5.query.length > 0 ? SEARCH_OVERLAY_NO_MATCHES : "";
901988
+ const locked = manager5.locked;
901989
+ const cursor = locked ? "" : "\u2588";
901990
+ const queryDisplay = manager5.query + cursor;
901991
+ const hints = locked ? SEARCH_OVERLAY_LOCKED_HINTS : SEARCH_OVERLAY_UNLOCKED_HINTS;
901992
+ const label = SEARCH_OVERLAY_LABEL;
901993
+ const matchStr = matchCount ? ` ${matchCount}` : "";
901994
+ const leftPart = label + queryDisplay;
901995
+ const hintsW = getDisplayWidth(hints);
901996
+ const matchStrW = getDisplayWidth(matchStr);
901997
+ const leftWidth = width - hintsW - matchStrW - 2;
901998
+ const truncatedLeft = fitDisplay(getDisplayWidth(leftPart) > leftWidth ? truncateDisplay(leftPart, leftWidth) : leftPart, leftWidth);
901999
+ const fullLine = truncatedLeft + matchStr + hints + " ";
902000
+ const line2 = createBottomBarLine(width, { fg: "#000000", bg: "#00ffcc" });
902001
+ writeBottomBarText(line2, 0, width, fitDisplay(truncateDisplay(fullLine, width), width), { fg: "#000000", bg: "#00ffcc" });
902002
+ if (matchStr.length > 0) {
902003
+ const matchStart = getDisplayWidth(truncatedLeft);
902004
+ writeBottomBarText(line2, matchStart, matchStrW, matchStr, { fg: "#888888", bg: "#00ffcc", dim: true });
902005
+ }
902006
+ return [line2];
902007
+ }
902008
+
902009
+ // src/renderer/history-search-overlay.ts
902010
+ var HISTORY_SEARCH_PREFIX = "(reverse-i-search)`";
902011
+ var HISTORY_SEARCH_FAILED_PREFIX = "(failed reverse-i-search)`";
902012
+ var HISTORY_SEARCH_QUERY_SUFFIX = "': ";
902013
+ function historySearchLabel(prefix, query2) {
902014
+ return prefix + query2 + HISTORY_SEARCH_QUERY_SUFFIX;
902015
+ }
902016
+ function truncateToWidth3(text, maxWidth) {
902017
+ let usedWidth = 0;
902018
+ let result2 = "";
902019
+ let i5 = 0;
902020
+ while (i5 < text.length) {
902021
+ const code2 = text.codePointAt(i5);
902022
+ const charLen = code2 > 65535 ? 2 : 1;
902023
+ const charWidth = getDisplayWidth(text.slice(i5, i5 + charLen));
902024
+ if (usedWidth + charWidth > maxWidth)
902025
+ break;
902026
+ result2 += text.slice(i5, i5 + charLen);
902027
+ usedWidth += charWidth;
902028
+ i5 += charLen;
902029
+ }
902030
+ return result2 + " ".repeat(maxWidth - usedWidth);
902031
+ }
902032
+ function renderHistorySearchOverlay(historySearch, width) {
902033
+ if (width <= 0)
902034
+ return [];
902035
+ const match = historySearch.currentMatch;
902036
+ const hasMatch = match !== null && historySearch.query.length > 0;
902037
+ const noMatch = historySearch.query.length > 0 && !hasMatch;
902038
+ const prefix = noMatch ? HISTORY_SEARCH_FAILED_PREFIX : HISTORY_SEARCH_PREFIX;
902039
+ const matchText = hasMatch ? match?.entry ?? "" : "";
902040
+ const label = historySearchLabel(prefix, historySearch.query);
902041
+ const full = truncateToWidth3(label + matchText, width);
902042
+ const line2 = createBottomBarLine(width, { fg: "#000000", bg: "#00ffcc" });
902043
+ writeBottomBarText(line2, 0, width, full, { fg: "#000000", bg: "#00ffcc" });
902044
+ if (hasMatch && match) {
902045
+ const labelW = getDisplayWidth(label);
902046
+ const matchStartCol = labelW + match.matchStart;
902047
+ const matchEndCol = matchStartCol + match.matchLength;
902048
+ const highlightWidth = Math.max(0, matchEndCol - matchStartCol);
902049
+ const matchedSlice = truncateToWidth3(match.entry.slice(match.matchStart, match.matchStart + match.matchLength), highlightWidth);
902050
+ writeBottomBarText(line2, matchStartCol, highlightWidth, matchedSlice, {
902051
+ fg: "#000000",
902052
+ bg: "#00ffcc",
902053
+ bold: true,
902054
+ underline: true
902055
+ });
902056
+ }
902057
+ return [line2];
902058
+ }
902059
+
901607
902060
  // src/renderer/mcp-workspace.ts
901608
902061
  var MCP_WORKSPACE_TITLE = "MCP Workspace / Servers";
901609
902062
  var MCP_WORKSPACE_LEFT_HEADER = "Servers";
@@ -902813,17 +903266,23 @@ function buildLeftRows2(workspace, height) {
902813
903266
  }
902814
903267
  function actionCommand(action2) {
902815
903268
  if (action2.kind === "workspace")
902816
- return action2.targetCategoryId ? `open ${action2.targetCategoryId}` : "(workspace)";
903269
+ return "open area";
902817
903270
  if (action2.kind === "editor")
902818
- return action2.editorKind ? `edit ${action2.editorKind}` : "(editor)";
903271
+ return action2.editorKind ? `edit ${action2.editorKind}` : "edit form";
902819
903272
  if (action2.kind === "setting")
902820
- return action2.settingKey ? `set ${action2.settingKey}` : "set setting";
903273
+ return action2.settingKey ? `setting ${action2.settingKey}` : "setting";
902821
903274
  if (action2.kind === "settings-import")
902822
903275
  return "import GoodVibes settings";
903276
+ if (action2.kind === "model-picker")
903277
+ return action2.modelPickerFlow === "model" ? "model picker" : "provider/model picker";
903278
+ if (action2.kind === "settings-modal")
903279
+ return action2.settingsTarget ? `settings ${action2.settingsTarget}` : "settings";
902823
903280
  if (action2.kind === "local-selection")
902824
903281
  return action2.selectionDelta && action2.selectionDelta < 0 ? "select previous" : "select next";
902825
903282
  if (action2.kind === "local-operation")
902826
903283
  return action2.localOperation ?? "(local action)";
903284
+ if (action2.kind === "onboarding-complete")
903285
+ return "apply and close";
902827
903286
  return action2.command ?? "(guidance)";
902828
903287
  }
902829
903288
  function setupCounts(snapshot2) {
@@ -902837,6 +903296,11 @@ function setupCounts(snapshot2) {
902837
903296
  function setupStatusLabel(status) {
902838
903297
  return status === "ready" ? "Ready" : status === "recommended" ? "Recommended" : status === "blocked" ? "Blocked" : "Optional";
902839
903298
  }
903299
+ function formatMegabytes(bytes) {
903300
+ if (!Number.isFinite(bytes) || bytes <= 0)
903301
+ return "0 MB";
903302
+ return `${Math.round(bytes / (1024 * 1024))} MB`;
903303
+ }
902840
903304
  function compactText3(text, maxWidth = 104) {
902841
903305
  const normalized3 = text.replace(/\s+/g, " ").trim();
902842
903306
  if (normalized3.length === 0)
@@ -903007,6 +903471,27 @@ function snapshotLines(workspace, category, snapshot2) {
903007
903471
  base2.push({ text: `Chat route: ${snapshot2.provider} / ${snapshot2.modelDisplayName}`, fg: FULLSCREEN_PALETTE.info }, { text: `Session: ${snapshot2.sessionId}`, fg: FULLSCREEN_PALETTE.muted }, { text: `Policy: ${snapshot2.executionPolicy}; delegated review ${snapshot2.delegatedReviewPolicy}`, fg: FULLSCREEN_PALETTE.good }, conciseSetupLine(snapshot2), ...homeNextActionLines(snapshot2));
903008
903472
  } else if (category.id === "setup") {
903009
903473
  base2.push(...setupOverviewLines(snapshot2));
903474
+ } else if (category.id === "account-model") {
903475
+ base2.push({ text: `Chat route: ${snapshot2.provider} / ${snapshot2.modelDisplayName}`, fg: FULLSCREEN_PALETTE.info }, { text: `Subscriptions: ${snapshot2.activeSubscriptionCount} active; ${snapshot2.pendingSubscriptionCount} pending; ${snapshot2.availableSubscriptionProviderCount} available.`, fg: snapshot2.activeSubscriptionCount > 0 ? FULLSCREEN_PALETTE.good : snapshot2.pendingSubscriptionCount > 0 ? FULLSCREEN_PALETTE.warn : FULLSCREEN_PALETTE.muted }, { text: `Embedding: ${snapshot2.embeddingProvider}; reasoning ${snapshot2.reasoningEffort}.`, fg: FULLSCREEN_PALETTE.info }, { text: `Helper: ${snapshot2.helperEnabled ? "enabled" : "disabled"}; Tool LLM: ${snapshot2.toolLlmEnabled ? "enabled" : "disabled"}.`, fg: snapshot2.helperEnabled || snapshot2.toolLlmEnabled ? FULLSCREEN_PALETTE.good : FULLSCREEN_PALETTE.muted }, { text: `Cache: ${snapshot2.cacheEnabled ? snapshot2.cacheStableTtl : "off"}; monitor ${snapshot2.cacheMonitorHitRate ? snapshot2.cacheHitRateWarningThreshold : "off"}; failure hints ${snapshot2.providerFailureHints ? "on" : "off"}.`, fg: snapshot2.cacheEnabled ? FULLSCREEN_PALETTE.info : FULLSCREEN_PALETTE.muted });
903476
+ } else if (category.id === "assistant-behavior") {
903477
+ base2.push({ text: `Interaction: ${snapshot2.hitlMode}; guidance ${snapshot2.guidanceMode}; history ${snapshot2.saveHistory ? "saved" : "off"}.`, fg: FULLSCREEN_PALETTE.info }, { text: `Context: compact at ${snapshot2.autoCompactThreshold}; stale warnings ${snapshot2.staleContextWarnings ? "on" : "off"}.`, fg: FULLSCREEN_PALETTE.info }, { text: `Reasoning display: thinking ${snapshot2.showThinking ? "on" : "off"}; summaries ${snapshot2.showReasoningSummary ? "on" : "off"}.`, fg: FULLSCREEN_PALETTE.muted });
903478
+ } else if (category.id === "tools-permissions") {
903479
+ base2.push({ text: `Permission mode: ${snapshot2.permissionMode}.`, fg: snapshot2.permissionMode === "allow-all" ? FULLSCREEN_PALETTE.warn : FULLSCREEN_PALETTE.info }, { text: `Auto-approve ${snapshot2.autoApprove ? "on" : "off"}; tool auto-heal ${snapshot2.toolAutoHeal ? "on" : "off"}; token budget ${snapshot2.toolsDefaultTokenBudget}.`, fg: snapshot2.autoApprove ? FULLSCREEN_PALETTE.warn : FULLSCREEN_PALETTE.info }, { text: `Artifact limit ${formatMegabytes(snapshot2.artifactMaxBytes)}; raw prompt telemetry ${snapshot2.rawPromptTelemetry ? "on" : "off"}.`, fg: snapshot2.rawPromptTelemetry ? FULLSCREEN_PALETTE.warn : FULLSCREEN_PALETTE.muted }, { text: `MCP servers: ${snapshot2.mcpConnectedServerCount}/${snapshot2.mcpServerCount} connected; quarantined ${snapshot2.mcpQuarantinedServerCount}.`, fg: snapshot2.mcpQuarantinedServerCount > 0 ? FULLSCREEN_PALETTE.warn : FULLSCREEN_PALETTE.info }, { text: "MCP and secret setup use forms; selecting a row does not run arbitrary tools.", fg: FULLSCREEN_PALETTE.good });
903480
+ } else if (category.id === "onboarding-display") {
903481
+ base2.push({ text: `Theme: ${snapshot2.theme}; streaming ${snapshot2.stream ? "on" : "off"}; line numbers ${snapshot2.lineNumbers}.`, fg: FULLSCREEN_PALETTE.info }, { text: `Messages: operational ${snapshot2.operationalMessages}; system ${snapshot2.systemMessages}.`, fg: FULLSCREEN_PALETTE.info }, { text: `Release channel: ${snapshot2.releaseChannel}.`, fg: FULLSCREEN_PALETTE.muted });
903482
+ } else if (category.id === "onboarding-channels") {
903483
+ const enabledCount = snapshot2.channels.filter((channel) => channel.enabled).length;
903484
+ const readyCount = snapshot2.channels.filter((channel) => channel.ready).length;
903485
+ const needsConfig = snapshot2.channels.filter((channel) => channel.setupState === "needs-config");
903486
+ const needsTarget = snapshot2.channels.filter((channel) => channel.setupState === "needs-target");
903487
+ base2.push({ text: `Channels: ${readyCount}/${snapshot2.channels.length} ready; ${enabledCount} enabled.`, fg: enabledCount > 0 ? FULLSCREEN_PALETTE.info : FULLSCREEN_PALETTE.muted }, { text: `Needs config: ${needsConfig.length}; needs target: ${needsTarget.length}.`, fg: needsConfig.length > 0 || needsTarget.length > 0 ? FULLSCREEN_PALETTE.warn : FULLSCREEN_PALETTE.good }, { text: "Enable only the channels you want; hidden channel fields appear after the channel is enabled.", fg: FULLSCREEN_PALETTE.good });
903488
+ } else if (category.id === "onboarding-voice-media") {
903489
+ const readiness = snapshot2.voiceMediaReadiness;
903490
+ base2.push({ text: `Voice: ${snapshot2.voiceSurfaceEnabled ? "enabled" : "disabled"}; TTS ${snapshot2.ttsProvider}; voice ${snapshot2.ttsVoice}.`, fg: snapshot2.voiceSurfaceEnabled ? FULLSCREEN_PALETTE.good : FULLSCREEN_PALETTE.info }, { text: `Media readiness: ${readiness.readyMediaProviderCount}/${snapshot2.mediaProviderCount}; generation providers ${snapshot2.mediaGenerationProviderCount}.`, fg: readiness.readyMediaProviderCount > 0 ? FULLSCREEN_PALETTE.good : FULLSCREEN_PALETTE.muted }, { text: `Telephony channel: ${snapshot2.channels.find((channel) => channel.id === "telephony")?.setupState ?? "disabled"}.`, fg: FULLSCREEN_PALETTE.info });
903491
+ } else if (category.id === "onboarding-context") {
903492
+ base2.push({ text: `Local context: ${snapshot2.localMemoryCount} memories, ${snapshot2.localNoteCount} notes, ${snapshot2.localPersonaCount} personas.`, fg: FULLSCREEN_PALETTE.info }, { text: `Skills: ${snapshot2.enabledSkillCount}/${snapshot2.localSkillCount} enabled; routines ${snapshot2.enabledRoutineCount}/${snapshot2.localRoutineCount} enabled.`, fg: FULLSCREEN_PALETTE.info }, { text: `Discovered files: personas ${snapshot2.discoveredBehavior.personas.count}, skills ${snapshot2.discoveredBehavior.skills.count}, routines ${snapshot2.discoveredBehavior.routines.count}.`, fg: FULLSCREEN_PALETTE.muted });
903493
+ } else if (category.id === "onboarding-automation") {
903494
+ base2.push({ text: `Automation: ${snapshot2.automationEnabled ? "enabled" : "disabled"}; max ${snapshot2.automationMaxConcurrentRuns} concurrent; history ${snapshot2.automationRunHistoryLimit}.`, fg: snapshot2.automationEnabled ? FULLSCREEN_PALETTE.good : FULLSCREEN_PALETTE.muted }, { text: `Timeout ${snapshot2.automationDefaultTimeoutMs} ms; catch-up ${snapshot2.automationCatchUpWindowMinutes} min; cooldown ${snapshot2.automationFailureCooldownMs} ms.`, fg: FULLSCREEN_PALETTE.info }, { text: `Delete one-shot jobs after success: ${snapshot2.automationDeleteAfterRun ? "yes" : "no"}.`, fg: snapshot2.automationDeleteAfterRun ? FULLSCREEN_PALETTE.info : FULLSCREEN_PALETTE.muted });
903010
903495
  } else if (category.id === "artifacts") {
903011
903496
  const mediaReady = snapshot2.voiceMediaReadiness.readyMediaProviderCount;
903012
903497
  base2.push({ text: `Chat: ${snapshot2.provider} / ${snapshot2.modelDisplayName}; Knowledge: ${snapshot2.knowledgeRoute}`, fg: FULLSCREEN_PALETTE.info }, { text: `Media: ${mediaReady}/${snapshot2.mediaProviderCount} ready; generation ${snapshot2.mediaGenerationProviderCount}.`, fg: mediaReady > 0 ? FULLSCREEN_PALETTE.good : FULLSCREEN_PALETTE.warn }, { text: "Files: attach, export, inspect, ingest reviewed sources, or generate media.", fg: FULLSCREEN_PALETTE.good }, { text: "Knowledge ingest and media generation require explicit actions.", fg: FULLSCREEN_PALETTE.warn });
@@ -903263,17 +903748,20 @@ function normalizeFullscreenViewport(lines, width, height) {
903263
903748
  viewport.push(createEmptyLine(width));
903264
903749
  return viewport;
903265
903750
  }
903266
- function createAgentWorkspaceFullscreenComposite(workspace, width, height) {
903751
+ function createFullscreenCompositeFromLines(lines, width, height) {
903267
903752
  return {
903268
903753
  width,
903269
903754
  height,
903270
903755
  header: [],
903271
- viewport: normalizeFullscreenViewport(renderAgentWorkspace(workspace, width, height), width, height),
903756
+ viewport: normalizeFullscreenViewport(lines, width, height),
903272
903757
  footer: [],
903273
903758
  forceFullRedraw: true,
903274
903759
  panelWidth: 0
903275
903760
  };
903276
903761
  }
903762
+ function createAgentWorkspaceFullscreenComposite(workspace, width, height) {
903763
+ return createFullscreenCompositeFromLines(renderAgentWorkspace(workspace, width, height), width, height);
903764
+ }
903277
903765
 
903278
903766
  // src/shell/service-settings-sync.ts
903279
903767
  var AGENT_EXTERNAL_HOST_SERVICE_MESSAGE = "GoodVibes Agent uses a connected GoodVibes host and does not install, start, stop, restart, or uninstall it. Manage host lifecycle outside Agent.";
@@ -905782,6 +906270,14 @@ async function main() {
905782
906270
  input.setPanelMouseLayout(null);
905783
906271
  activeConversationWidth = width;
905784
906272
  conversation.setSplashSuppressed(true);
906273
+ if (input.modelPicker.active) {
906274
+ compositor.composite(createFullscreenCompositeFromLines(renderModelWorkspace(input.modelPicker, width, height), width, height));
906275
+ return;
906276
+ }
906277
+ if (input.settingsModal.active) {
906278
+ compositor.composite(createFullscreenCompositeFromLines(renderSettingsModal(input.settingsModal, width, height), width, height));
906279
+ return;
906280
+ }
905785
906281
  compositor.composite(createAgentWorkspaceFullscreenComposite(input.agentWorkspace, width, height));
905786
906282
  return;
905787
906283
  }