@lazyingart/agintiflow 0.20.182 → 0.20.184

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -153,7 +153,7 @@ aginti --language de
153
153
  | Save provider keys | `aginti auth`, `/auth`, `/login` |
154
154
  | Review current repo | `/review [focus]` |
155
155
  | Toggle SCS quality gate | `/scs` |
156
- | Use SCS only for complex work | `/scs auto` or `aginti --scs auto "task"` |
156
+ | Keep default auto SCS routing | `/scs auto` or `aginti --scs auto "task"` |
157
157
  | Disable SCS for simple work | `/scs off` or `aginti --no-scs "task"` |
158
158
  | Control dynamic step budgets | `--dynamic-steps auto\|on\|off` |
159
159
  | Work with AAPS workflows | `aginti aaps status`, `/aaps validate` |
@@ -61,13 +61,13 @@ The current implementation is deliberately bounded:
61
61
 
62
62
  ## Auto Mode
63
63
 
64
- `/scs auto` and `--scs auto` are still available when a user wants cheaper routing for simple turns. Auto mode activates SCS for complex, risky, or long-running work. Signals include:
64
+ `/scs auto` and `--scs auto` are the default for users who want cheap simple turns without losing strict validation on risky work. Auto mode activates SCS for high-risk, evidence-heavy, or long-running work. Signals include:
65
65
 
66
- - high smart-routing complexity score;
67
- - profiles such as code, app, Android/iOS, large-codebase, GitHub, maintenance, security, LaTeX, research, and supervision;
68
- - prompts mentioning multi-file work, regressions, builds, deployments, tmux, emulators, Docker, PDFs, or similar high-friction workflows.
66
+ - very high smart-routing complexity score;
67
+ - profiles such as app, Android/iOS, large-codebase, GitHub, maintenance, QA, security, supervision, pipeline, and website;
68
+ - prompts mentioning failing tests, repo-wide changes, browser uploads, releases, deployments, tmux, emulators, Docker, PDFs, or similar high-friction workflows.
69
69
 
70
- Auto mode stays off for simple turns, but it is not the default.
70
+ Auto mode is the default. It stays off for simple turns, uses the main model for moderate complexity, and activates SCS only for high-risk or evidence-heavy turns.
71
71
 
72
72
  ## Dynamic Step Budgets
73
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.182",
3
+ "version": "0.20.184",
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
@@ -145,6 +145,14 @@ const translations = {
145
145
  wrappersOffLabel: "wrapper tools off",
146
146
  wrapperCapabilityTitle: "Agent wrappers",
147
147
  workspaceCapabilityTitle: "Workspace files",
148
+ workspaceExplorerTitle: "Workspace explorer",
149
+ workspaceExplorerHelp: "Browse, classify, drag into chat, and open project files.",
150
+ workspaceRefreshButton: "Refresh",
151
+ workspaceAutomapButton: "Auto map",
152
+ workspaceWorkbenchTitle: "Workspace workbench",
153
+ workspaceNoFileSelected: "Select a file from the explorer.",
154
+ workspaceSaveButton: "Save file",
155
+ workspaceRevertButton: "Revert",
148
156
  mcpCapabilityTitle: "MCP servers",
149
157
  mcpEmpty: "No MCP servers configured.",
150
158
  workspaceToolsLabel: "File tools",
@@ -746,6 +754,19 @@ const wrapperGridEl = document.querySelector("#wrapper-grid");
746
754
  const workspaceStatusEl = document.querySelector("#workspace-status");
747
755
  const workspaceToolsEl = document.querySelector("#workspace-tools");
748
756
  const workspaceChangesEl = document.querySelector("#workspace-changes");
757
+ const workspaceExplorerEl = document.querySelector("#workspace-explorer");
758
+ const workspaceBrowserStatusEl = document.querySelector("#workspace-browser-status");
759
+ const workspaceLanesEl = document.querySelector("#workspace-lanes");
760
+ const workspaceTreeEl = document.querySelector("#workspace-tree");
761
+ const workspaceRefreshButton = document.querySelector("#workspace-refresh");
762
+ const workspaceAutomapButton = document.querySelector("#workspace-automap");
763
+ const workspaceActiveTitleEl = document.querySelector("#workspace-active-title");
764
+ const workspaceActiveMetaEl = document.querySelector("#workspace-active-meta");
765
+ const workspacePreviewEl = document.querySelector("#workspace-preview");
766
+ const workspaceEditorEl = document.querySelector("#workspace-editor");
767
+ const workspaceSaveButton = document.querySelector("#workspace-save");
768
+ const workspaceRevertButton = document.querySelector("#workspace-revert");
769
+ const workspaceEditorStatusEl = document.querySelector("#workspace-editor-status");
749
770
  const mcpStatusEl = document.querySelector("#mcp-status");
750
771
  const mcpGridEl = document.querySelector("#mcp-grid");
751
772
  const mcpWarningsEl = document.querySelector("#mcp-warnings");
@@ -835,6 +856,31 @@ let currentArtifactTab = "canvas";
835
856
  let artifactUnreadCount = 0;
836
857
  let lastAnnouncedRunKey = "";
837
858
  let quickVeniceModeActive = false;
859
+ const WORKSPACE_LANES = [
860
+ { id: "all", label: "All" },
861
+ { id: "source", label: "Source" },
862
+ { id: "scripts", label: "Scripts" },
863
+ { id: "docs", label: "Docs" },
864
+ { id: "data", label: "Data" },
865
+ { id: "media", label: "Media" },
866
+ { id: "references", label: "Refs" },
867
+ { id: "artifacts", label: "Artifacts" },
868
+ { id: "logs", label: "Logs" },
869
+ { id: "other", label: "Other" },
870
+ ];
871
+ let workspaceBrowser = {
872
+ root: "",
873
+ files: [],
874
+ warnings: [],
875
+ activeLane: "all",
876
+ activePath: "",
877
+ activeContent: "",
878
+ savedContent: "",
879
+ overrides: {},
880
+ loading: false,
881
+ error: "",
882
+ initialized: false,
883
+ };
838
884
 
839
885
  function normalizeLanguage(language) {
840
886
  const lower = String(language || "").toLowerCase();
@@ -1156,6 +1202,278 @@ function renderWorkspacePanel(workspace = lastWorkspace, activity = lastWorkspac
1156
1202
  .join("");
1157
1203
  }
1158
1204
 
1205
+ function workspaceRoot() {
1206
+ return commandCwdField?.value.trim() || projectInfo?.commandCwd || projectInfo?.root || "";
1207
+ }
1208
+
1209
+ function workspaceExt(filePath = "") {
1210
+ const name = String(filePath || "").toLowerCase();
1211
+ const index = name.lastIndexOf(".");
1212
+ return index >= 0 ? name.slice(index) : "";
1213
+ }
1214
+
1215
+ function workspaceDir(filePath = "") {
1216
+ const normalized = String(filePath || "").replace(/\\/g, "/");
1217
+ const index = normalized.lastIndexOf("/");
1218
+ return index >= 0 ? normalized.slice(0, index) : "";
1219
+ }
1220
+
1221
+ function workspaceBaseName(filePath = "") {
1222
+ return String(filePath || "").replace(/\\/g, "/").split("/").filter(Boolean).pop() || filePath;
1223
+ }
1224
+
1225
+ function inferWorkspaceLane(file = {}) {
1226
+ const filePath = String(file.path || "").toLowerCase();
1227
+ const first = filePath.split("/")[0] || "";
1228
+ const ext = workspaceExt(filePath);
1229
+ if (workspaceBrowser.overrides[file.path]) return workspaceBrowser.overrides[file.path];
1230
+ if (["artifacts", "runs", "outputs", "reports"].includes(first)) return "artifacts";
1231
+ if (["logs", "log", "events"].includes(first) || ext === ".log") return "logs";
1232
+ if (["data", "datasets", "tables"].includes(first) || [".csv", ".tsv", ".jsonl", ".xlsx", ".parquet"].includes(ext)) return "data";
1233
+ if (["docs", "doc", "references", "reference"].includes(first) || [".md", ".txt", ".tex", ".rst"].includes(ext)) return "docs";
1234
+ if (["refs", "bibliography"].includes(first) || [".bib", ".ris", ".enw"].includes(ext)) return "references";
1235
+ if (["scripts", "bin"].includes(first) || [".py", ".r", ".sh", ".m", ".pl"].includes(ext)) return "scripts";
1236
+ if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".pdf", ".mp4", ".mov", ".wav", ".mp3"].includes(ext)) return "media";
1237
+ if ([".js", ".mjs", ".ts", ".tsx", ".jsx", ".css", ".html", ".go", ".rs", ".c", ".cpp", ".h", ".hpp", ".java", ".rb", ".php", ".lua"].includes(ext)) {
1238
+ return "source";
1239
+ }
1240
+ return "other";
1241
+ }
1242
+
1243
+ function workspaceFilesByLane() {
1244
+ const groups = Object.fromEntries(WORKSPACE_LANES.map((lane) => [lane.id, []]));
1245
+ for (const file of workspaceBrowser.files) {
1246
+ groups.all.push(file);
1247
+ const lane = inferWorkspaceLane(file);
1248
+ if (!groups[lane]) groups.other.push(file);
1249
+ else groups[lane].push(file);
1250
+ }
1251
+ return groups;
1252
+ }
1253
+
1254
+ function workspaceActiveFile() {
1255
+ return workspaceBrowser.files.find((file) => file.path === workspaceBrowser.activePath) || null;
1256
+ }
1257
+
1258
+ function formatBytes(size = 0) {
1259
+ const value = Number(size || 0);
1260
+ if (value < 1024) return `${value} B`;
1261
+ if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
1262
+ return `${(value / 1024 / 1024).toFixed(1)} MB`;
1263
+ }
1264
+
1265
+ function workspaceRawUrl(filePath = "") {
1266
+ const params = new URLSearchParams({ commandCwd: workspaceBrowser.root || workspaceRoot(), path: filePath });
1267
+ return `/api/workspace/raw?${params.toString()}`;
1268
+ }
1269
+
1270
+ function setWorkspaceEditorStatus(message = "", kind = "info") {
1271
+ if (!workspaceEditorStatusEl) return;
1272
+ workspaceEditorStatusEl.textContent = message;
1273
+ workspaceEditorStatusEl.dataset.kind = kind;
1274
+ }
1275
+
1276
+ function renderWorkspaceExplorer() {
1277
+ if (!workspaceExplorerEl || !workspaceLanesEl || !workspaceTreeEl || !workspaceBrowserStatusEl) return;
1278
+ const groups = workspaceFilesByLane();
1279
+ const activeLane = workspaceBrowser.activeLane || "all";
1280
+ const warnings = workspaceBrowser.warnings?.length ? ` · ${workspaceBrowser.warnings.length} warning(s)` : "";
1281
+ const truncated = workspaceBrowser.truncated ? " · truncated" : "";
1282
+ const status = workspaceBrowser.loading
1283
+ ? "Loading workspace..."
1284
+ : workspaceBrowser.error
1285
+ ? `Workspace unavailable: ${workspaceBrowser.error}`
1286
+ : `${workspaceBrowser.files.length} files · ${workspaceBrowser.root || workspaceRoot()}${warnings}${truncated}`;
1287
+ workspaceBrowserStatusEl.textContent = status;
1288
+
1289
+ workspaceLanesEl.innerHTML = WORKSPACE_LANES.map((lane) => {
1290
+ const count = groups[lane.id]?.length || 0;
1291
+ return `
1292
+ <button
1293
+ type="button"
1294
+ class="workspace-lane"
1295
+ data-workspace-lane="${escapeHtml(lane.id)}"
1296
+ data-selected="${lane.id === activeLane}"
1297
+ title="Drop a file here to classify it as ${escapeHtml(lane.label)}"
1298
+ >
1299
+ <span>${escapeHtml(lane.label)}</span>
1300
+ <strong>${count}</strong>
1301
+ </button>
1302
+ `;
1303
+ }).join("");
1304
+
1305
+ const visibleFiles = (groups[activeLane] || []).slice().sort((left, right) => left.path.localeCompare(right.path));
1306
+ if (!visibleFiles.length) {
1307
+ workspaceTreeEl.innerHTML = `<p class="subtle workspace-empty">No files in this category.</p>`;
1308
+ return;
1309
+ }
1310
+
1311
+ workspaceTreeEl.innerHTML = visibleFiles
1312
+ .map((file) => {
1313
+ const lane = inferWorkspaceLane(file);
1314
+ const active = file.path === workspaceBrowser.activePath;
1315
+ const meta = [lane, formatBytes(file.size), file.binary ? "binary" : "text"].join(" · ");
1316
+ return `
1317
+ <button
1318
+ type="button"
1319
+ class="workspace-file-row"
1320
+ data-workspace-file="${escapeHtml(file.path)}"
1321
+ data-selected="${active}"
1322
+ draggable="true"
1323
+ >
1324
+ <span class="workspace-file-main">
1325
+ <strong>${escapeHtml(workspaceBaseName(file.path))}</strong>
1326
+ <small>${escapeHtml(workspaceDir(file.path) || ".")}</small>
1327
+ </span>
1328
+ <span class="workspace-file-meta">${escapeHtml(meta)}</span>
1329
+ </button>
1330
+ `;
1331
+ })
1332
+ .join("");
1333
+ }
1334
+
1335
+ function renderWorkspaceWorkbench() {
1336
+ if (!workspaceActiveTitleEl || !workspaceActiveMetaEl || !workspacePreviewEl || !workspaceEditorEl) return;
1337
+ const file = workspaceActiveFile();
1338
+ const dirty = workspaceBrowser.activeContent !== workspaceBrowser.savedContent;
1339
+
1340
+ if (!file) {
1341
+ workspaceActiveTitleEl.textContent = t("workspaceNoFileSelected");
1342
+ workspaceActiveMetaEl.textContent = t("workspaceNoFileSelected");
1343
+ workspacePreviewEl.innerHTML = `<p class="subtle">${t("workspaceNoFileSelected")}</p>`;
1344
+ workspaceEditorEl.hidden = true;
1345
+ if (workspaceSaveButton) workspaceSaveButton.disabled = true;
1346
+ if (workspaceRevertButton) workspaceRevertButton.disabled = true;
1347
+ setWorkspaceEditorStatus("");
1348
+ return;
1349
+ }
1350
+
1351
+ workspaceActiveTitleEl.textContent = file.path;
1352
+ workspaceActiveMetaEl.textContent = `${inferWorkspaceLane(file)} · ${formatBytes(file.size)} · ${
1353
+ file.binary ? "preview-only" : dirty ? "edited" : "saved"
1354
+ }`;
1355
+
1356
+ const ext = workspaceExt(file.path);
1357
+ const rawUrl = workspaceRawUrl(file.path);
1358
+ if (file.binary) {
1359
+ workspaceEditorEl.hidden = true;
1360
+ if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
1361
+ workspacePreviewEl.innerHTML = `<img class="workspace-media-preview" src="${escapeHtml(rawUrl)}" alt="${escapeHtml(file.path)}" />`;
1362
+ } else if (ext === ".pdf") {
1363
+ workspacePreviewEl.innerHTML = `<iframe class="workspace-pdf-preview" src="${escapeHtml(rawUrl)}" title="${escapeHtml(file.path)}"></iframe>`;
1364
+ } else {
1365
+ workspacePreviewEl.innerHTML = `<p class="subtle">Binary file preview is unavailable. Use the raw file path or artifact canvas for specialized previews.</p>`;
1366
+ }
1367
+ if (workspaceSaveButton) workspaceSaveButton.disabled = true;
1368
+ if (workspaceRevertButton) workspaceRevertButton.disabled = true;
1369
+ setWorkspaceEditorStatus("Preview-only binary file.");
1370
+ return;
1371
+ }
1372
+
1373
+ workspacePreviewEl.innerHTML = `<pre class="workspace-code-preview">${escapeHtml(workspaceBrowser.activeContent).slice(
1374
+ 0,
1375
+ 12000
1376
+ )}</pre>`;
1377
+ workspaceEditorEl.hidden = false;
1378
+ if (document.activeElement !== workspaceEditorEl && workspaceEditorEl.value !== workspaceBrowser.activeContent) {
1379
+ workspaceEditorEl.value = workspaceBrowser.activeContent;
1380
+ }
1381
+ if (workspaceSaveButton) workspaceSaveButton.disabled = !dirty;
1382
+ if (workspaceRevertButton) workspaceRevertButton.disabled = !dirty;
1383
+ setWorkspaceEditorStatus(dirty ? "Unsaved editor changes." : "File loaded.");
1384
+ }
1385
+
1386
+ function renderWorkspaceBrowser() {
1387
+ renderWorkspaceExplorer();
1388
+ renderWorkspaceWorkbench();
1389
+ }
1390
+
1391
+ async function refreshWorkspaceSnapshot({ preserveActive = true } = {}) {
1392
+ if (!workspaceExplorerEl) return;
1393
+ workspaceBrowser.loading = true;
1394
+ workspaceBrowser.error = "";
1395
+ renderWorkspaceExplorer();
1396
+ try {
1397
+ const response = await fetch("/api/workspace/snapshot", {
1398
+ method: "POST",
1399
+ headers: { "Content-Type": "application/json" },
1400
+ body: JSON.stringify({ commandCwd: workspaceRoot() }),
1401
+ });
1402
+ const data = await response.json().catch(() => ({}));
1403
+ if (!response.ok) throw new Error(data.error || "Workspace snapshot failed.");
1404
+ workspaceBrowser = {
1405
+ ...workspaceBrowser,
1406
+ root: data.root || workspaceRoot(),
1407
+ files: data.files || [],
1408
+ warnings: data.warnings || [],
1409
+ truncated: Boolean(data.truncated),
1410
+ loading: false,
1411
+ error: "",
1412
+ initialized: true,
1413
+ };
1414
+ if (!preserveActive || !workspaceBrowser.files.some((file) => file.path === workspaceBrowser.activePath)) {
1415
+ workspaceBrowser.activePath = workspaceBrowser.files.find((file) => !file.binary)?.path || workspaceBrowser.files[0]?.path || "";
1416
+ const active = workspaceActiveFile();
1417
+ workspaceBrowser.activeContent = active?.content || "";
1418
+ workspaceBrowser.savedContent = active?.content || "";
1419
+ } else {
1420
+ const active = workspaceActiveFile();
1421
+ if (!active?.binary && workspaceBrowser.activeContent === workspaceBrowser.savedContent) {
1422
+ workspaceBrowser.activeContent = active?.content || "";
1423
+ workspaceBrowser.savedContent = active?.content || "";
1424
+ }
1425
+ }
1426
+ } catch (error) {
1427
+ workspaceBrowser.loading = false;
1428
+ workspaceBrowser.error = error instanceof Error ? error.message : String(error);
1429
+ }
1430
+ renderWorkspaceBrowser();
1431
+ }
1432
+
1433
+ function selectWorkspaceFile(filePath = "") {
1434
+ const file = workspaceBrowser.files.find((item) => item.path === filePath);
1435
+ if (!file) return;
1436
+ workspaceBrowser.activePath = file.path;
1437
+ workspaceBrowser.activeContent = file.content || "";
1438
+ workspaceBrowser.savedContent = file.content || "";
1439
+ renderWorkspaceBrowser();
1440
+ }
1441
+
1442
+ async function saveWorkspaceEditor() {
1443
+ const file = workspaceActiveFile();
1444
+ if (!file || file.binary || !workspaceEditorEl) return;
1445
+ const content = workspaceEditorEl.value;
1446
+ setWorkspaceEditorStatus("Saving...");
1447
+ const response = await fetch("/api/workspace/write", {
1448
+ method: "POST",
1449
+ headers: { "Content-Type": "application/json" },
1450
+ body: JSON.stringify({ commandCwd: workspaceBrowser.root || workspaceRoot(), path: file.path, content }),
1451
+ });
1452
+ const data = await response.json().catch(() => ({}));
1453
+ if (!response.ok) {
1454
+ setWorkspaceEditorStatus(data.error || "Save failed.", "error");
1455
+ return;
1456
+ }
1457
+ workspaceBrowser.activeContent = content;
1458
+ workspaceBrowser.savedContent = content;
1459
+ showToast(`Saved ${file.path}.`);
1460
+ await refreshWorkspaceSnapshot({ preserveActive: true });
1461
+ }
1462
+
1463
+ function revertWorkspaceEditor() {
1464
+ workspaceBrowser.activeContent = workspaceBrowser.savedContent;
1465
+ if (workspaceEditorEl) workspaceEditorEl.value = workspaceBrowser.savedContent;
1466
+ renderWorkspaceWorkbench();
1467
+ }
1468
+
1469
+ function insertWorkspaceReference(filePath = "") {
1470
+ if (!chatInputEl || !filePath) return;
1471
+ const reference = `@file ${filePath}`;
1472
+ const current = chatInputEl.value.trimEnd();
1473
+ chatInputEl.value = current ? `${current}\n${reference}` : reference;
1474
+ chatInputEl.focus();
1475
+ }
1476
+
1159
1477
  function renderMcpPanel(mcp = lastMcp) {
1160
1478
  lastMcp = mcp || null;
1161
1479
  if (!mcpStatusEl || !mcpGridEl || !mcpWarningsEl) return;
@@ -1402,7 +1720,7 @@ function syncQuickModeControls() {
1402
1720
  if (aapsModeToggle) aapsModeToggle.checked = (taskProfileField?.value || "auto") === "aaps";
1403
1721
  if (veniceModeToggle) veniceModeToggle.checked = quickVeniceModeActive;
1404
1722
  if (quickModeStatusEl) {
1405
- const scs = enableScsField?.value || "on";
1723
+ const scs = enableScsField?.value || "auto";
1406
1724
  const profile = taskProfileField?.value || "auto";
1407
1725
  const route = `${routeProviderField?.value || "deepseek"}/${fieldValue(routeModelField) || "auto"}`;
1408
1726
  const main = `${mainProviderField?.value || "deepseek"}/${fieldValue(mainModelField) || "auto"}`;
@@ -1451,8 +1769,8 @@ function applyVeniceMode(enabled) {
1451
1769
  syncQuickModeControls();
1452
1770
  }
1453
1771
 
1454
- function applyScsMode(mode = enableScsField?.value || "on") {
1455
- if (enableScsField) enableScsField.value = ["on", "auto", "off"].includes(mode) ? mode : "on";
1772
+ function applyScsMode(mode = enableScsField?.value || "auto") {
1773
+ if (enableScsField) enableScsField.value = ["on", "auto", "off"].includes(mode) ? mode : "auto";
1456
1774
  if (allowParallelScoutsField && enableScsField?.value === "on") {
1457
1775
  allowParallelScoutsField.checked = false;
1458
1776
  }
@@ -1663,6 +1981,7 @@ function applyLanguage(language, { persist = true } = {}) {
1663
1981
  renderProjectStatus();
1664
1982
  renderWrapperStatus();
1665
1983
  renderWorkspacePanel();
1984
+ renderWorkspaceBrowser();
1666
1985
  renderSandboxStatus();
1667
1986
  updateRoutingHint();
1668
1987
  updatePermissionHint();
@@ -1707,7 +2026,7 @@ function formPayload() {
1707
2026
  allowWebSearch: allowWebSearchField?.checked ?? true,
1708
2027
  allowMcpTools: allowMcpToolsField?.checked ?? true,
1709
2028
  allowParallelScouts: allowParallelScoutsField?.checked ?? true,
1710
- enableScs: enableScsField?.value || "on",
2029
+ enableScs: enableScsField?.value || "auto",
1711
2030
  dynamicSteps: dynamicStepsField?.value || "auto",
1712
2031
  veniceMode: quickVeniceModeActive,
1713
2032
  parallelScoutCount: Math.min(Math.max(Number(parallelScoutCountField?.value) || 3, 1), 10),
@@ -3085,6 +3404,7 @@ async function refreshRun() {
3085
3404
  await refreshChat();
3086
3405
  await refreshSessions();
3087
3406
  await refreshWorkspaceChanges();
3407
+ await refreshWorkspaceSnapshot({ preserveActive: true });
3088
3408
  await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
3089
3409
  if (run.status === "finished") await flushAfterFinishQueue();
3090
3410
  }
@@ -3335,6 +3655,7 @@ async function startRunFromGoal(goal, { clearComposer = false } = {}) {
3335
3655
  sessionSelectEl.value = currentSessionId;
3336
3656
  await refreshChat();
3337
3657
  await refreshWorkspaceChanges();
3658
+ await refreshWorkspaceSnapshot({ preserveActive: true });
3338
3659
  await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
3339
3660
 
3340
3661
  if (pollTimer) clearInterval(pollTimer);
@@ -3589,6 +3910,77 @@ commandCwdField?.addEventListener("input", () => {
3589
3910
  commandCwdField?.addEventListener("change", () => {
3590
3911
  schedulePreferenceSave();
3591
3912
  refreshCommandCwdSuggestions(commandCwdField.value).catch(() => {});
3913
+ refreshWorkspaceSnapshot({ preserveActive: false }).catch(() => {});
3914
+ });
3915
+
3916
+ workspaceRefreshButton?.addEventListener("click", () => {
3917
+ refreshWorkspaceSnapshot({ preserveActive: true }).catch((error) => {
3918
+ setWorkspaceEditorStatus(String(error), "error");
3919
+ });
3920
+ });
3921
+
3922
+ workspaceAutomapButton?.addEventListener("click", () => {
3923
+ workspaceBrowser.overrides = {};
3924
+ renderWorkspaceBrowser();
3925
+ showToast("Workspace files auto-mapped by general category.");
3926
+ });
3927
+
3928
+ workspaceLanesEl?.addEventListener("click", (event) => {
3929
+ const button = event.target.closest("[data-workspace-lane]");
3930
+ if (!button) return;
3931
+ workspaceBrowser.activeLane = button.dataset.workspaceLane || "all";
3932
+ renderWorkspaceBrowser();
3933
+ });
3934
+
3935
+ workspaceLanesEl?.addEventListener("dragover", (event) => {
3936
+ if (event.target.closest("[data-workspace-lane]")) event.preventDefault();
3937
+ });
3938
+
3939
+ workspaceLanesEl?.addEventListener("drop", (event) => {
3940
+ const button = event.target.closest("[data-workspace-lane]");
3941
+ const filePath = event.dataTransfer?.getData("text/agintiflow-workspace-file") || event.dataTransfer?.getData("text/plain");
3942
+ const lane = button?.dataset.workspaceLane;
3943
+ if (!button || !filePath || !lane || lane === "all") return;
3944
+ event.preventDefault();
3945
+ workspaceBrowser.overrides[filePath] = lane;
3946
+ workspaceBrowser.activeLane = lane;
3947
+ renderWorkspaceBrowser();
3948
+ });
3949
+
3950
+ workspaceTreeEl?.addEventListener("click", (event) => {
3951
+ const row = event.target.closest("[data-workspace-file]");
3952
+ if (!row) return;
3953
+ selectWorkspaceFile(row.dataset.workspaceFile || "");
3954
+ });
3955
+
3956
+ workspaceTreeEl?.addEventListener("dragstart", (event) => {
3957
+ const row = event.target.closest("[data-workspace-file]");
3958
+ if (!row || !event.dataTransfer) return;
3959
+ event.dataTransfer.setData("text/agintiflow-workspace-file", row.dataset.workspaceFile || "");
3960
+ event.dataTransfer.setData("text/plain", row.dataset.workspaceFile || "");
3961
+ event.dataTransfer.effectAllowed = "copyMove";
3962
+ });
3963
+
3964
+ workspaceEditorEl?.addEventListener("input", () => {
3965
+ workspaceBrowser.activeContent = workspaceEditorEl.value;
3966
+ renderWorkspaceWorkbench();
3967
+ });
3968
+
3969
+ workspaceSaveButton?.addEventListener("click", () => {
3970
+ saveWorkspaceEditor().catch((error) => setWorkspaceEditorStatus(String(error), "error"));
3971
+ });
3972
+
3973
+ workspaceRevertButton?.addEventListener("click", revertWorkspaceEditor);
3974
+
3975
+ chatInputEl?.addEventListener("dragover", (event) => {
3976
+ if (event.dataTransfer?.types?.includes("text/agintiflow-workspace-file")) event.preventDefault();
3977
+ });
3978
+
3979
+ chatInputEl?.addEventListener("drop", (event) => {
3980
+ const filePath = event.dataTransfer?.getData("text/agintiflow-workspace-file");
3981
+ if (!filePath) return;
3982
+ event.preventDefault();
3983
+ insertWorkspaceReference(filePath);
3592
3984
  });
3593
3985
 
3594
3986
  chatInputEl.addEventListener("keydown", (event) => {
@@ -3724,6 +4116,7 @@ chatFormEl.addEventListener("submit", async (event) => {
3724
4116
  await refreshSessions();
3725
4117
  await refreshChat();
3726
4118
  await refreshWorkspaceChanges();
4119
+ await refreshWorkspaceSnapshot({ preserveActive: true });
3727
4120
  await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
3728
4121
 
3729
4122
  if (pollTimer) clearInterval(pollTimer);
@@ -3818,7 +4211,7 @@ async function loadConfig() {
3818
4211
  if (allowWebSearchField) allowWebSearchField.checked = prefs.allowWebSearch ?? true;
3819
4212
  if (allowMcpToolsField) allowMcpToolsField.checked = prefs.allowMcpTools ?? true;
3820
4213
  if (allowParallelScoutsField) allowParallelScoutsField.checked = prefs.allowParallelScouts ?? true;
3821
- if (enableScsField) enableScsField.value = prefs.enableScs || "on";
4214
+ if (enableScsField) enableScsField.value = prefs.enableScs || "auto";
3822
4215
  if (dynamicStepsField) dynamicStepsField.value = prefs.dynamicSteps || "auto";
3823
4216
  if (parallelScoutCountField) parallelScoutCountField.value = String(prefs.parallelScoutCount || 3);
3824
4217
  allowWrapperToolsField.checked = prefs.allowWrapperTools ?? false;
@@ -3835,6 +4228,7 @@ async function loadConfig() {
3835
4228
  renderWorkspacePanel(data.workspace, []);
3836
4229
  renderMcpPanel(data.mcp || null);
3837
4230
  await refreshWorkspaceChanges();
4231
+ await refreshWorkspaceSnapshot({ preserveActive: false });
3838
4232
  renderSandboxLogs(data.sandbox?.logs || []);
3839
4233
  if (quickVeniceModeActive) applyVeniceMode(true);
3840
4234
  updateRoutingHint();
package/public/index.html CHANGED
@@ -223,6 +223,22 @@
223
223
  <button id="open-settings" type="button" class="secondary settings-button">Advanced settings</button>
224
224
  </form>
225
225
  </details>
226
+
227
+ <section id="workspace-explorer" class="workspace-explorer-card" aria-labelledby="workspace-explorer-title">
228
+ <div class="workspace-explorer-head">
229
+ <div>
230
+ <h2 id="workspace-explorer-title" data-i18n="workspaceExplorerTitle">Workspace explorer</h2>
231
+ <p class="subtle" data-i18n="workspaceExplorerHelp">Browse, classify, drag into chat, and open project files.</p>
232
+ </div>
233
+ <div class="workspace-explorer-actions">
234
+ <button id="workspace-refresh" type="button" class="secondary" data-i18n="workspaceRefreshButton">Refresh</button>
235
+ <button id="workspace-automap" type="button" class="secondary" data-i18n="workspaceAutomapButton">Auto map</button>
236
+ </div>
237
+ </div>
238
+ <div id="workspace-browser-status" class="subtle workspace-browser-status" aria-live="polite">Loading workspace...</div>
239
+ <div id="workspace-lanes" class="workspace-lanes" aria-label="Workspace file categories"></div>
240
+ <div id="workspace-tree" class="workspace-tree" aria-live="polite"></div>
241
+ </section>
226
242
  </section>
227
243
 
228
244
  <section class="stack">
@@ -264,8 +280,8 @@
264
280
  <label class="quick-mode-select">
265
281
  <span data-i18n="scsModeLabel">SCS</span>
266
282
  <select id="enableScs" name="enableScs">
267
- <option value="on" data-i18n="scsOnOption">On</option>
268
283
  <option value="auto" data-i18n="scsAutoOption">Auto</option>
284
+ <option value="on" data-i18n="scsOnOption">On</option>
269
285
  <option value="off" data-i18n="scsOffOption">Off</option>
270
286
  </select>
271
287
  </label>
@@ -306,6 +322,23 @@
306
322
  <div id="chat-pending" class="chat-pending" hidden></div>
307
323
  </section>
308
324
 
325
+ <section id="workspace-workbench" class="panel workspace-workbench">
326
+ <div class="header-row">
327
+ <div>
328
+ <h2 data-i18n="workspaceWorkbenchTitle">Workspace workbench</h2>
329
+ <p id="workspace-active-meta" class="subtle" data-i18n="workspaceNoFileSelected">Select a file from the explorer.</p>
330
+ </div>
331
+ <div class="workspace-editor-actions">
332
+ <button id="workspace-revert" type="button" class="secondary" disabled data-i18n="workspaceRevertButton">Revert</button>
333
+ <button id="workspace-save" type="button" disabled data-i18n="workspaceSaveButton">Save file</button>
334
+ </div>
335
+ </div>
336
+ <div id="workspace-active-title" class="workspace-active-title" data-i18n="workspaceNoFileSelected">Select a file from the explorer.</div>
337
+ <div id="workspace-preview" class="workspace-preview"></div>
338
+ <textarea id="workspace-editor" class="workspace-editor" spellcheck="false" hidden></textarea>
339
+ <div id="workspace-editor-status" class="subtle workspace-editor-status"></div>
340
+ </section>
341
+
309
342
  <section class="panel output-panel">
310
343
  <div class="header-row">
311
344
  <h2 data-i18n="runOutputTitle">Run output</h2>