@lazyingart/agintiflow 0.20.166 → 0.20.170

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.
@@ -31,6 +31,8 @@ The npm trust setup may require the package to exist before trust can be attache
31
31
  4. Push the release commit and create a GitHub Release, or run the publish workflow manually.
32
32
  5. Confirm the npm package page shows provenance.
33
33
 
34
+ If local token publishing fails with `E401` or `E404`, do not treat that as terminal when Trusted Publishing is configured. Use the GitHub Actions workflow with an account that can dispatch workflows for `lazyingart/AgInTiFlow`, then verify the registry and install the published package. See `references/agintiflow-npm-publication-memory.md` for the 2026-05-20 `0.20.169` publication evidence and the known working command sequence.
35
+
34
36
  ## Local Token Fallback
35
37
 
36
38
  Trusted Publishing is preferred. A local automation token can be used only for bootstrap or emergency fallback:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.166",
3
+ "version": "0.20.170",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
package/public/app.js CHANGED
@@ -52,6 +52,7 @@ const translations = {
52
52
  goalPlaceholder: "Open a site and summarize it, or use run_command for simple terminal inspection.",
53
53
  runDefaultsTitle: "Run defaults",
54
54
  runDefaultsHelp: "The chat box is the first goal for a new run. These controls set how that run starts.",
55
+ runDefaultsFoldHint: "Routing, safety, tool toggles, and key status.",
55
56
  scsModeLabel: "SCS",
56
57
  scsOnOption: "On",
57
58
  scsAutoOption: "Auto",
@@ -64,6 +65,8 @@ const translations = {
64
65
  allowedDomainsLabel: "Allowed domains",
65
66
  allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
66
67
  commandCwdLabel: "Working directory",
68
+ commandCwdHelp: "Runs start from this working directory.",
69
+ commandCwdPlaceholder: "/home/user/project",
67
70
  maxStepsLabel: "Max steps",
68
71
  sandboxModeLabel: "Sandbox mode",
69
72
  permissionModeLabel: "Permission shortcut",
@@ -718,6 +721,8 @@ const setupApiKeyField = document.querySelector("#setup-api-key");
718
721
  const saveApiKeyButton = document.querySelector("#save-api-key");
719
722
  const setupStatusEl = document.querySelector("#setup-status");
720
723
  const taskProfileField = document.querySelector("#taskProfile");
724
+ const commandCwdField = document.querySelector("#commandCwd");
725
+ const commandCwdSuggestionsEl = document.querySelector("#command-cwd-suggestions");
721
726
  const permissionModeField = document.querySelector("#permissionMode");
722
727
  const permissionHintEl = document.querySelector("#permission-hint");
723
728
  const sandboxModeField = document.querySelector("#sandboxMode");
@@ -727,6 +732,7 @@ const logsEl = document.querySelector("#logs");
727
732
  const runMetaEl = document.querySelector("#run-meta");
728
733
  const stopRunButton = document.querySelector("#stop-run");
729
734
  const keyStatusEl = document.querySelector("#key-status");
735
+ const keyStatusListEl = document.querySelector("#key-status-list");
730
736
  const allowAuxiliaryToolsField = document.querySelector("#allowAuxiliaryTools");
731
737
  const allowWebSearchField = document.querySelector("#allowWebSearch");
732
738
  const allowMcpToolsField = document.querySelector("#allowMcpTools");
@@ -828,6 +834,7 @@ let selectedArtifactId = "";
828
834
  let currentArtifactTab = "canvas";
829
835
  let artifactUnreadCount = 0;
830
836
  let lastAnnouncedRunKey = "";
837
+ let quickVeniceModeActive = false;
831
838
 
832
839
  function normalizeLanguage(language) {
833
840
  const lower = String(language || "").toLowerCase();
@@ -928,31 +935,99 @@ function setRunStatus(status = "", { sessionId, detail = "", announce = false }
928
935
  function renderKeyStatus(status = lastKeyStatus) {
929
936
  lastKeyStatus = status;
930
937
  if (!status) return;
931
- keyStatusEl.textContent = `${t("keysLabel")}: OpenAI ${
932
- status.openai ? t("availableLabel") : t("missingLabel")
933
- } · DeepSeek ${status.deepseek ? t("availableLabel") : t("missingLabel")} · Qwen ${
934
- status.qwen ? t("availableLabel") : t("missingLabel")
935
- } · Venice ${
936
- status.venice ? t("availableLabel") : t("missingLabel")
937
- } · GRS AI ${
938
- status.grsai ? t("availableLabel") : t("missingLabel")
939
- } · ${t("mockLabel")} ${
940
- status.mock ? t("availableLabel") : t("missingLabel")
941
- }`;
938
+ const providers = [
939
+ ["OpenAI", status.openai],
940
+ ["DeepSeek", status.deepseek],
941
+ ["Qwen", status.qwen],
942
+ ["Venice", status.venice],
943
+ ["GRS AI", status.grsai],
944
+ [t("mockLabel"), status.mock],
945
+ ];
946
+ const available = providers.filter(([, ready]) => Boolean(ready)).length;
947
+ if (keyStatusEl) {
948
+ keyStatusEl.textContent = `${t("keysLabel")} ${available}/${providers.length}`;
949
+ keyStatusEl.dataset.ready = String(available > 0);
950
+ }
951
+ if (keyStatusListEl) {
952
+ keyStatusListEl.replaceChildren(
953
+ ...providers.map(([name, ready]) => {
954
+ const chip = document.createElement("div");
955
+ chip.className = "key-status-chip";
956
+ chip.dataset.ready = String(Boolean(ready));
957
+ const title = document.createElement("strong");
958
+ title.textContent = name;
959
+ const value = document.createElement("span");
960
+ value.textContent = ready ? t("availableLabel") : t("missingLabel");
961
+ chip.append(title, value);
962
+ return chip;
963
+ })
964
+ );
965
+ }
942
966
  if (setupCardEl) setupCardEl.hidden = Boolean(status.openai || status.deepseek || status.qwen || status.venice);
943
967
  }
944
968
 
945
969
  function renderProjectStatus(info = projectInfo) {
946
970
  projectInfo = info;
947
971
  if (!projectStatusEl || !info) return;
948
- projectStatusEl.textContent = [
949
- info.platform?.label ? `os=${info.platform.label}` : "",
950
- `root=${info.root || ""}`,
951
- `cwd=${info.commandCwd || ""}`,
952
- `sessions=${info.sessionsDir || ""}`,
953
- `db=${info.sessionDbPath || ""}`,
954
- `shared=${info.sharedSessionFolder ? "yes" : "no"}`,
955
- ].join(" · ");
972
+ const rows = [
973
+ ["OS", info.platform?.label || ""],
974
+ ["Root", info.root || ""],
975
+ ["CWD", info.commandCwd || ""],
976
+ ["Sessions", info.sessionsDir || ""],
977
+ ["Database", info.sessionDbPath || ""],
978
+ ["Shared", info.sharedSessionFolder ? "yes" : "no"],
979
+ ].filter(([, value]) => value !== "");
980
+ projectStatusEl.replaceChildren(
981
+ ...rows.map(([label, value]) => {
982
+ const chip = document.createElement("div");
983
+ chip.className = "project-status-chip";
984
+ if (String(value).length > 54) chip.dataset.wide = "true";
985
+ const title = document.createElement("strong");
986
+ title.textContent = label;
987
+ const body = document.createElement("span");
988
+ body.textContent = value;
989
+ chip.append(title, body);
990
+ return chip;
991
+ })
992
+ );
993
+ }
994
+
995
+ function baseDirectorySuggestions(info = projectInfo) {
996
+ const projectLfsRoot = info?.root?.includes("/ProjectsLFS/")
997
+ ? `${info.root.split("/ProjectsLFS/")[0]}/ProjectsLFS`
998
+ : "";
999
+ const values = [
1000
+ info?.commandCwd,
1001
+ info?.root,
1002
+ info?.sessionsDir ? info.sessionsDir.replace(/\/\.agintiflow\/sessions$/, "") : "",
1003
+ projectLfsRoot,
1004
+ ].filter(Boolean);
1005
+ return [...new Set(values)];
1006
+ }
1007
+
1008
+ function renderCommandCwdSuggestions(suggestions = []) {
1009
+ if (!commandCwdSuggestionsEl) return;
1010
+ commandCwdSuggestionsEl.replaceChildren(
1011
+ ...suggestions.slice(0, 16).map((value) => {
1012
+ const option = document.createElement("option");
1013
+ option.value = value;
1014
+ return option;
1015
+ })
1016
+ );
1017
+ }
1018
+
1019
+ async function refreshCommandCwdSuggestions(query = "") {
1020
+ const fallback = baseDirectorySuggestions();
1021
+ renderCommandCwdSuggestions(fallback);
1022
+ try {
1023
+ const response = await fetch(`/api/path-suggestions?q=${encodeURIComponent(query || "")}`);
1024
+ const data = await response.json();
1025
+ if (response.ok && Array.isArray(data.suggestions)) {
1026
+ renderCommandCwdSuggestions([...new Set([...data.suggestions, ...fallback])]);
1027
+ }
1028
+ } catch {
1029
+ renderCommandCwdSuggestions(fallback);
1030
+ }
956
1031
  }
957
1032
 
958
1033
  function renderTaskProfiles(selected = "auto") {
@@ -1192,6 +1267,31 @@ function providerModelOptions(provider = providerField.value) {
1192
1267
  return (modelCatalog[provider] || []).filter((item) => !item.hidden);
1193
1268
  }
1194
1269
 
1270
+ function modelIdsForProvider(provider = "") {
1271
+ return new Set(providerModelOptions(provider).map((item) => String(item.id || "")));
1272
+ }
1273
+
1274
+ function modelDefaultValues() {
1275
+ return new Set(Object.values(defaults).filter(Boolean));
1276
+ }
1277
+
1278
+ function fallbackModelForProvider(provider = "deepseek", role = "primary") {
1279
+ if (provider === "deepseek" && role === "main") return "deepseek-v4-pro";
1280
+ if (provider === "deepseek") return "deepseek-v4-flash";
1281
+ if (provider === "openai" && role === "spare") return "gpt-5.4";
1282
+ return defaults[provider] || providerModelOptions(provider)[0]?.id || "";
1283
+ }
1284
+
1285
+ function coerceModelForProvider(provider = "deepseek", currentModel = "", fallbackModel = "") {
1286
+ const current = String(currentModel || "").trim();
1287
+ const fallback = String(fallbackModel || fallbackModelForProvider(provider)).trim();
1288
+ const providerModelIds = modelIdsForProvider(provider);
1289
+ const knownDefault = modelDefaultValues().has(current);
1290
+ const wrongProviderModel = providerModelIds.size > 0 && current && !providerModelIds.has(current);
1291
+ if (!current || knownDefault || wrongProviderModel) return fallback;
1292
+ return current;
1293
+ }
1294
+
1195
1295
  function mergeModelOptions(...groups) {
1196
1296
  const seen = new Set();
1197
1297
  const merged = [];
@@ -1253,24 +1353,32 @@ function setSelectOptions(select, options, selectedValue = "", fallbackValue = "
1253
1353
  function refreshModelDropdowns() {
1254
1354
  refreshProviderDropdowns();
1255
1355
  const provider = providerField?.value || "deepseek";
1256
- setSelectOptions(modelField, providerModelOptions(provider), fieldValue(modelField), defaults[provider]);
1356
+ setSelectOptions(
1357
+ modelField,
1358
+ providerModelOptions(provider),
1359
+ coerceModelForProvider(provider, fieldValue(modelField), fallbackModelForProvider(provider, "primary")),
1360
+ fallbackModelForProvider(provider, "primary")
1361
+ );
1362
+ const routeProvider = routeProviderField?.value || "deepseek";
1257
1363
  setSelectOptions(
1258
1364
  routeModelField,
1259
- providerModelOptions(routeProviderField?.value || "deepseek"),
1260
- fieldValue(routeModelField),
1261
- modelRoles.route?.model || "deepseek-v4-flash"
1365
+ providerModelOptions(routeProvider),
1366
+ coerceModelForProvider(routeProvider, fieldValue(routeModelField), fallbackModelForProvider(routeProvider, "route")),
1367
+ fallbackModelForProvider(routeProvider, "route")
1262
1368
  );
1369
+ const mainProvider = mainProviderField?.value || "deepseek";
1263
1370
  setSelectOptions(
1264
1371
  mainModelField,
1265
- providerModelOptions(mainProviderField?.value || "deepseek"),
1266
- fieldValue(mainModelField),
1267
- modelRoles.main?.model || "deepseek-v4-pro"
1372
+ providerModelOptions(mainProvider),
1373
+ coerceModelForProvider(mainProvider, fieldValue(mainModelField), fallbackModelForProvider(mainProvider, "main")),
1374
+ fallbackModelForProvider(mainProvider, "main")
1268
1375
  );
1376
+ const spareProvider = spareProviderField?.value || "openai";
1269
1377
  setSelectOptions(
1270
1378
  spareModelField,
1271
- providerModelOptions(spareProviderField?.value || "openai"),
1272
- fieldValue(spareModelField),
1273
- modelRoles.spare?.model || "gpt-5.4"
1379
+ providerModelOptions(spareProvider),
1380
+ coerceModelForProvider(spareProvider, fieldValue(spareModelField), fallbackModelForProvider(spareProvider, "spare")),
1381
+ fallbackModelForProvider(spareProvider, "spare")
1274
1382
  );
1275
1383
  setSelectOptions(wrapperModelField, wrapperModelOptions(), fieldValue(wrapperModelField), modelRoles.wrapper?.model || "gpt-5.5");
1276
1384
  setSelectOptions(
@@ -1290,13 +1398,9 @@ function selectedVeniceTextModel() {
1290
1398
  );
1291
1399
  }
1292
1400
 
1293
- function inferVeniceMode() {
1294
- return [providerField?.value, routeProviderField?.value, mainProviderField?.value].includes("venice");
1295
- }
1296
-
1297
1401
  function syncQuickModeControls() {
1298
1402
  if (aapsModeToggle) aapsModeToggle.checked = (taskProfileField?.value || "auto") === "aaps";
1299
- if (veniceModeToggle) veniceModeToggle.checked = inferVeniceMode();
1403
+ if (veniceModeToggle) veniceModeToggle.checked = quickVeniceModeActive;
1300
1404
  if (quickModeStatusEl) {
1301
1405
  const scs = enableScsField?.value || "on";
1302
1406
  const profile = taskProfileField?.value || "auto";
@@ -1314,10 +1418,13 @@ function applyAapsMode(enabled) {
1314
1418
  }
1315
1419
 
1316
1420
  function applyVeniceMode(enabled) {
1421
+ quickVeniceModeActive = Boolean(enabled);
1317
1422
  if (enabled) {
1318
1423
  const model = selectedVeniceTextModel();
1319
1424
  routingModeField.value = "smart";
1320
- providerField.value = "deepseek";
1425
+ providerField.value = "venice";
1426
+ refreshModelDropdowns();
1427
+ if (modelField) setSelectOptions(modelField, providerModelOptions("venice"), model, defaults.venice);
1321
1428
  if (routeProviderField) routeProviderField.value = "venice";
1322
1429
  refreshModelDropdowns();
1323
1430
  if (routeModelField) setSelectOptions(routeModelField, providerModelOptions("venice"), model, defaults.venice);
@@ -1326,10 +1433,13 @@ function applyVeniceMode(enabled) {
1326
1433
  if (mainModelField) setSelectOptions(mainModelField, providerModelOptions("venice"), model, defaults.venice);
1327
1434
  } else {
1328
1435
  routingModeField.value = "smart";
1329
- providerField.value = "deepseek";
1436
+ if (providerField?.value === "venice") providerField.value = "deepseek";
1330
1437
  if (routeProviderField?.value === "venice") routeProviderField.value = "deepseek";
1331
1438
  if (mainProviderField?.value === "venice") mainProviderField.value = "deepseek";
1332
1439
  refreshModelDropdowns();
1440
+ if (modelField && providerField?.value === "deepseek") {
1441
+ setSelectOptions(modelField, providerModelOptions("deepseek"), "deepseek-v4-flash", "deepseek-v4-flash");
1442
+ }
1333
1443
  if (routeModelField && routeProviderField?.value === "deepseek") {
1334
1444
  setSelectOptions(routeModelField, providerModelOptions("deepseek"), "deepseek-v4-flash", "deepseek-v4-flash");
1335
1445
  }
@@ -1584,7 +1694,7 @@ function formPayload() {
1584
1694
  auxiliaryModel: fieldValue(auxiliaryModelField) || "nano-banana-2",
1585
1695
  startUrl: document.querySelector("#startUrl").value.trim(),
1586
1696
  allowedDomains: document.querySelector("#allowedDomains").value.trim(),
1587
- commandCwd: document.querySelector("#commandCwd").value.trim(),
1697
+ commandCwd: commandCwdField?.value.trim() || "",
1588
1698
  maxSteps: Number(document.querySelector("#maxSteps").value) || 24,
1589
1699
  permissionMode: permissionModeField?.value || "normal",
1590
1700
  sandboxMode: sandboxModeField.value,
@@ -1599,6 +1709,7 @@ function formPayload() {
1599
1709
  allowParallelScouts: allowParallelScoutsField?.checked ?? true,
1600
1710
  enableScs: enableScsField?.value || "on",
1601
1711
  dynamicSteps: dynamicStepsField?.value || "auto",
1712
+ veniceMode: quickVeniceModeActive,
1602
1713
  parallelScoutCount: Math.min(Math.max(Number(parallelScoutCountField?.value) || 3, 1), 10),
1603
1714
  allowWrapperTools: allowWrapperToolsField.checked,
1604
1715
  preferredWrapper: preferredWrapperField.value,
@@ -3048,16 +3159,8 @@ providerField.addEventListener("change", () => {
3048
3159
  routingModeField.value = "manual";
3049
3160
  }
3050
3161
 
3051
- if (
3052
- !fieldValue(modelField) ||
3053
- modelField.value === defaults.openai ||
3054
- modelField.value === defaults.deepseek ||
3055
- modelField.value === defaults.qwen ||
3056
- modelField.value === defaults.venice ||
3057
- modelField.value === defaults.mock
3058
- ) {
3059
- modelField.value = defaults[providerField.value] || "";
3060
- }
3162
+ if (providerField.value !== "venice") quickVeniceModeActive = false;
3163
+ refreshModelDropdowns();
3061
3164
  updateRoutingHint();
3062
3165
  syncQuickModeControls();
3063
3166
  schedulePreferenceSave();
@@ -3146,6 +3249,22 @@ saveApiKeyButton?.addEventListener("click", async () => {
3146
3249
  form.addEventListener("input", schedulePreferenceSave);
3147
3250
  form.addEventListener("change", schedulePreferenceSave);
3148
3251
 
3252
+ let commandCwdSuggestTimer = null;
3253
+ commandCwdField?.addEventListener("focus", () => {
3254
+ refreshCommandCwdSuggestions(commandCwdField.value).catch(() => {});
3255
+ });
3256
+ commandCwdField?.addEventListener("input", () => {
3257
+ schedulePreferenceSave();
3258
+ clearTimeout(commandCwdSuggestTimer);
3259
+ commandCwdSuggestTimer = setTimeout(() => {
3260
+ refreshCommandCwdSuggestions(commandCwdField.value).catch(() => {});
3261
+ }, 120);
3262
+ });
3263
+ commandCwdField?.addEventListener("change", () => {
3264
+ schedulePreferenceSave();
3265
+ refreshCommandCwdSuggestions(commandCwdField.value).catch(() => {});
3266
+ });
3267
+
3149
3268
  chatInputEl.addEventListener("keydown", (event) => {
3150
3269
  if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey) {
3151
3270
  event.preventDefault();
@@ -3304,6 +3423,7 @@ async function loadConfig() {
3304
3423
  defaults.mock = data.defaults?.mock?.model || defaults.mock;
3305
3424
 
3306
3425
  applyLanguage(prefs.language || normalizeLanguage(navigator.language || "en"), { persist: false });
3426
+ quickVeniceModeActive = prefs.veniceMode === true;
3307
3427
  refreshProviderDropdowns();
3308
3428
 
3309
3429
  routingModeField.value = prefs.routingMode || "smart";
@@ -3314,24 +3434,41 @@ async function loadConfig() {
3314
3434
  if (spareReasoningField) spareReasoningField.value = prefs.spareReasoning || modelRoles.spare?.reasoning || "medium";
3315
3435
  if (wrapperReasoningField) wrapperReasoningField.value = prefs.wrapperReasoning || modelRoles.wrapper?.reasoning || "medium";
3316
3436
  if (auxiliaryProviderField) auxiliaryProviderField.value = prefs.auxiliaryProvider || modelRoles.auxiliary?.provider || "grsai";
3317
- setSelectOptions(modelField, providerModelOptions(providerField.value), prefs.model || defaults[providerField.value], defaults[providerField.value]);
3437
+ setSelectOptions(
3438
+ modelField,
3439
+ providerModelOptions(providerField.value),
3440
+ coerceModelForProvider(providerField.value, prefs.model, fallbackModelForProvider(providerField.value, "primary")),
3441
+ fallbackModelForProvider(providerField.value, "primary")
3442
+ );
3318
3443
  setSelectOptions(
3319
3444
  routeModelField,
3320
3445
  providerModelOptions(routeProviderField?.value || "deepseek"),
3321
- prefs.routeModel || modelRoles.route?.model,
3322
- "deepseek-v4-flash"
3446
+ coerceModelForProvider(
3447
+ routeProviderField?.value || "deepseek",
3448
+ prefs.routeModel || modelRoles.route?.model,
3449
+ fallbackModelForProvider(routeProviderField?.value || "deepseek", "route")
3450
+ ),
3451
+ fallbackModelForProvider(routeProviderField?.value || "deepseek", "route")
3323
3452
  );
3324
3453
  setSelectOptions(
3325
3454
  mainModelField,
3326
3455
  providerModelOptions(mainProviderField?.value || "deepseek"),
3327
- prefs.mainModel || modelRoles.main?.model,
3328
- "deepseek-v4-pro"
3456
+ coerceModelForProvider(
3457
+ mainProviderField?.value || "deepseek",
3458
+ prefs.mainModel || modelRoles.main?.model,
3459
+ fallbackModelForProvider(mainProviderField?.value || "deepseek", "main")
3460
+ ),
3461
+ fallbackModelForProvider(mainProviderField?.value || "deepseek", "main")
3329
3462
  );
3330
3463
  setSelectOptions(
3331
3464
  spareModelField,
3332
3465
  providerModelOptions(spareProviderField?.value || "openai"),
3333
- prefs.spareModel || modelRoles.spare?.model,
3334
- "gpt-5.4"
3466
+ coerceModelForProvider(
3467
+ spareProviderField?.value || "openai",
3468
+ prefs.spareModel || modelRoles.spare?.model,
3469
+ fallbackModelForProvider(spareProviderField?.value || "openai", "spare")
3470
+ ),
3471
+ fallbackModelForProvider(spareProviderField?.value || "openai", "spare")
3335
3472
  );
3336
3473
  setSelectOptions(wrapperModelField, wrapperModelOptions(), prefs.wrapperModel || modelRoles.wrapper?.model, "gpt-5.5");
3337
3474
  setSelectOptions(
@@ -3343,7 +3480,7 @@ async function loadConfig() {
3343
3480
  renderTaskProfiles(prefs.taskProfile || "auto");
3344
3481
  document.querySelector("#startUrl").value = prefs.startUrl || "";
3345
3482
  document.querySelector("#allowedDomains").value = prefs.allowedDomains || "";
3346
- document.querySelector("#commandCwd").value = prefs.commandCwd || data.project?.root || "";
3483
+ if (commandCwdField) commandCwdField.value = prefs.commandCwd || data.project?.root || "";
3347
3484
  document.querySelector("#headless").checked = prefs.headless ?? data.defaults.headless;
3348
3485
  document.querySelector("#maxSteps").value = prefs.maxSteps ?? data.defaults.maxSteps;
3349
3486
  if (permissionModeField) permissionModeField.value = prefs.permissionMode || "normal";
@@ -3366,12 +3503,14 @@ async function loadConfig() {
3366
3503
 
3367
3504
  renderKeyStatus(data.keyStatus);
3368
3505
  renderProjectStatus(data.project);
3506
+ await refreshCommandCwdSuggestions(commandCwdField?.value || "");
3369
3507
  renderWrapperStatus(data.wrappers || []);
3370
3508
  refreshProviderDropdowns();
3371
3509
  renderWorkspacePanel(data.workspace, []);
3372
3510
  renderMcpPanel(data.mcp || null);
3373
3511
  await refreshWorkspaceChanges();
3374
3512
  renderSandboxLogs(data.sandbox?.logs || []);
3513
+ if (quickVeniceModeActive) applyVeniceMode(true);
3375
3514
  updateRoutingHint();
3376
3515
  updatePermissionHint();
3377
3516
  updatePackageWarning();
package/public/index.html CHANGED
@@ -36,13 +36,30 @@
36
36
  <p class="subtle" data-i18n="intro">
37
37
  Project-aware, low-cost agent for real problems
38
38
  </p>
39
- <details class="project-card">
40
- <summary>
41
- <strong data-i18n="projectStatusTitle">Project folder</strong>
42
- <span class="project-card-toggle" aria-hidden="true"></span>
43
- </summary>
44
- <p id="project-status" class="subtle">Loading project context...</p>
45
- </details>
39
+ <section class="workspace-search-card" aria-labelledby="workspace-search-title">
40
+ <div class="workspace-search-head">
41
+ <div>
42
+ <strong id="workspace-search-title" data-i18n="projectStatusTitle">Project folder</strong>
43
+ <p class="subtle" data-i18n="commandCwdHelp">Runs start from this working directory.</p>
44
+ </div>
45
+ </div>
46
+ <label class="workspace-search-field">
47
+ <span data-i18n="commandCwdLabel">Working directory</span>
48
+ <input
49
+ id="commandCwd"
50
+ name="commandCwd"
51
+ type="search"
52
+ value="/home/lachlan/ProjectsLFS/Agent"
53
+ list="command-cwd-suggestions"
54
+ autocomplete="off"
55
+ spellcheck="false"
56
+ data-i18n-placeholder="commandCwdPlaceholder"
57
+ placeholder="/home/lachlan/ProjectsLFS/Agent"
58
+ />
59
+ <datalist id="command-cwd-suggestions"></datalist>
60
+ </label>
61
+ <div id="project-status" class="project-status-grid" aria-live="polite">Loading project context...</div>
62
+ </section>
46
63
 
47
64
  <section id="setup-card" class="setup-card" hidden>
48
65
  <div>
@@ -84,14 +101,20 @@
84
101
  </div>
85
102
  </section>
86
103
 
87
- <form id="run-form" class="run-defaults-form">
88
- <div class="run-defaults-head">
89
- <div>
104
+ <details id="run-defaults-card" class="run-defaults-card">
105
+ <summary class="run-defaults-summary">
106
+ <span>
90
107
  <strong data-i18n="runDefaultsTitle">Run defaults</strong>
108
+ <span class="subtle" data-i18n="runDefaultsFoldHint">Routing, safety, tool toggles, and key status.</span>
109
+ </span>
110
+ <span id="key-status" class="key-status-summary"></span>
111
+ <span class="project-card-toggle" aria-hidden="true"></span>
112
+ </summary>
113
+ <form id="run-form" class="run-defaults-form">
114
+ <div class="run-defaults-head">
91
115
  <p class="subtle" data-i18n="runDefaultsHelp">The chat box is the first goal for a new run. These controls set how that run starts.</p>
116
+ <div id="key-status-list" class="key-status-list" aria-live="polite"></div>
92
117
  </div>
93
- <span id="key-status" class="subtle"></span>
94
- </div>
95
118
 
96
119
  <label>
97
120
  <span data-i18n="routingModeLabel">Routing policy</span>
@@ -135,10 +158,6 @@
135
158
  </label>
136
159
 
137
160
  <div class="grid">
138
- <label>
139
- <span data-i18n="commandCwdLabel">Working directory</span>
140
- <input id="commandCwd" name="commandCwd" type="text" value="/home/lachlan/ProjectsLFS/Agent" />
141
- </label>
142
161
  <label>
143
162
  <span data-i18n="maxStepsLabel">Max steps</span>
144
163
  <input id="maxSteps" name="maxSteps" type="number" min="1" max="50" value="24" />
@@ -202,7 +221,8 @@
202
221
  </div>
203
222
 
204
223
  <button id="open-settings" type="button" class="secondary settings-button">Advanced settings</button>
205
- </form>
224
+ </form>
225
+ </details>
206
226
  </section>
207
227
 
208
228
  <section class="stack">