agentlas 0.9.10 → 1.0.2

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +319 -341
  3. package/bin/agentlas.cjs +32 -9
  4. package/engine/agentlas-banner.cjs +24 -3
  5. package/engine/agentlas-composer.cjs +239 -31
  6. package/engine/agentlas-config.cjs +103 -7
  7. package/engine/agentlas-core-harness.cjs +48 -4
  8. package/engine/agentlas-evolution.cjs +3 -4
  9. package/engine/agentlas-i18n.cjs +88 -32
  10. package/engine/agentlas-input.cjs +234 -18
  11. package/engine/agentlas-memory-governance.cjs +3 -5
  12. package/engine/agentlas-memory-import.cjs +3 -3
  13. package/engine/agentlas-native-host.cjs +200 -9
  14. package/engine/agentlas-onboard.cjs +112 -20
  15. package/engine/agentlas-permissions.cjs +14 -7
  16. package/engine/agentlas-sqlite-policy.cjs +34 -0
  17. package/engine/agentlas-ui.cjs +93 -42
  18. package/engine/agentlas-workforce.cjs +472 -58
  19. package/engine/agentlas-workload-routing.cjs +8 -3
  20. package/engine/agentlas.cjs +140 -12367
  21. package/engine/agents/files.cjs +61 -0
  22. package/engine/agents/import-local.cjs +237 -0
  23. package/engine/agents/registry.cjs +158 -0
  24. package/engine/agents/router.cjs +565 -0
  25. package/engine/agents/routes.cjs +43 -0
  26. package/engine/architecture.data.json +2 -2
  27. package/engine/automation/daemon.cjs +335 -0
  28. package/engine/automation/schedule.cjs +181 -0
  29. package/engine/automation/store.cjs +209 -0
  30. package/engine/cloud/auth.cjs +279 -0
  31. package/engine/cloud/hub-client.cjs +239 -0
  32. package/engine/cloud-assets/cargo.cjs +49 -0
  33. package/engine/cloud-assets/cas.cjs +235 -0
  34. package/engine/cloud-assets/commands.cjs +273 -0
  35. package/engine/cloud-assets/package.cjs +936 -0
  36. package/engine/cloud-assets/restore.cjs +172 -0
  37. package/engine/cloud-assets/state.cjs +268 -0
  38. package/engine/commands/automation.cjs +195 -0
  39. package/engine/commands/billing.cjs +96 -0
  40. package/engine/commands/browser.cjs +20 -0
  41. package/engine/commands/build.cjs +33 -0
  42. package/engine/commands/call.cjs +24 -0
  43. package/engine/commands/career-graph.cjs +51 -0
  44. package/engine/commands/cd.cjs +22 -0
  45. package/engine/commands/chat.cjs +12 -0
  46. package/engine/commands/chats.cjs +28 -0
  47. package/engine/commands/cloud.cjs +17 -0
  48. package/engine/commands/connect.cjs +20 -0
  49. package/engine/commands/context.cjs +66 -0
  50. package/engine/commands/creds.cjs +203 -0
  51. package/engine/commands/doctor.cjs +68 -0
  52. package/engine/commands/env.cjs +33 -0
  53. package/engine/commands/evolve.cjs +23 -0
  54. package/engine/commands/experience.cjs +34 -0
  55. package/engine/commands/film.cjs +8 -0
  56. package/engine/commands/firm.cjs +115 -0
  57. package/engine/commands/help.cjs +65 -0
  58. package/engine/commands/hep.cjs +23 -0
  59. package/engine/commands/import.cjs +35 -0
  60. package/engine/commands/index.cjs +136 -0
  61. package/engine/commands/install.cjs +27 -0
  62. package/engine/commands/journal.cjs +31 -0
  63. package/engine/commands/legacy-network.cjs +29 -0
  64. package/engine/commands/list.cjs +49 -0
  65. package/engine/commands/login.cjs +68 -0
  66. package/engine/commands/logout.cjs +28 -0
  67. package/engine/commands/mcp.cjs +91 -0
  68. package/engine/commands/memory.cjs +21 -0
  69. package/engine/commands/multimodal.cjs +91 -0
  70. package/engine/commands/native.cjs +34 -0
  71. package/engine/commands/netadmin.cjs +31 -0
  72. package/engine/commands/oberon.cjs +70 -0
  73. package/engine/commands/ontology.cjs +24 -0
  74. package/engine/commands/open.cjs +48 -0
  75. package/engine/commands/plugin.cjs +101 -0
  76. package/engine/commands/project.cjs +47 -0
  77. package/engine/commands/research.cjs +36 -0
  78. package/engine/commands/route.cjs +37 -0
  79. package/engine/commands/run.cjs +149 -0
  80. package/engine/commands/search.cjs +55 -0
  81. package/engine/commands/setup.cjs +45 -0
  82. package/engine/commands/storm.cjs +75 -0
  83. package/engine/commands/swarm.cjs +75 -0
  84. package/engine/commands/telegram.cjs +32 -0
  85. package/engine/commands/uninstall.cjs +68 -0
  86. package/engine/commands/update.cjs +54 -0
  87. package/engine/commands/upload.cjs +18 -0
  88. package/engine/commands/usage.cjs +35 -0
  89. package/engine/commands/variant.cjs +25 -0
  90. package/engine/commands/version.cjs +9 -0
  91. package/engine/commands/whoami.cjs +38 -0
  92. package/engine/commands/workforce.cjs +103 -0
  93. package/engine/core/db.cjs +160 -0
  94. package/engine/core/paths.cjs +35 -0
  95. package/engine/experience/build.cjs +181 -0
  96. package/engine/experience/intents.cjs +492 -0
  97. package/engine/experience/runtime.cjs +242 -0
  98. package/engine/experience/variant.cjs +196 -0
  99. package/engine/firms/orchestrate.cjs +333 -0
  100. package/engine/hephaestus/runtime.cjs +697 -0
  101. package/engine/hub/install.cjs +872 -0
  102. package/engine/hub/plugins.cjs +213 -0
  103. package/engine/mcp/consent.cjs +289 -0
  104. package/engine/mcp/contract.cjs +202 -0
  105. package/engine/mcp/index.cjs +43 -0
  106. package/engine/mcp/inventory.cjs +322 -0
  107. package/engine/mcp/plan.cjs +286 -0
  108. package/engine/mcp/probe.cjs +151 -0
  109. package/engine/memory-cli/curate.cjs +163 -0
  110. package/engine/oberon/common.cjs +69 -0
  111. package/engine/oberon/manifest.cjs +164 -0
  112. package/engine/oberon/outputs.cjs +70 -0
  113. package/engine/oberon/render.cjs +164 -0
  114. package/engine/project/career-graph.cjs +249 -0
  115. package/engine/project/credentials.cjs +262 -0
  116. package/engine/project/env-file.cjs +46 -0
  117. package/engine/project/index.cjs +27 -0
  118. package/engine/project/memory-context.cjs +453 -0
  119. package/engine/project/ontology.cjs +467 -0
  120. package/engine/project/paths.cjs +39 -0
  121. package/engine/project/seed.cjs +200 -0
  122. package/engine/project/state.cjs +403 -0
  123. package/engine/project/super-ontology-seed.json +3288 -0
  124. package/engine/runtimes/detect.cjs +54 -0
  125. package/engine/runtimes/overrides.cjs +139 -0
  126. package/engine/runtimes/resolve.cjs +64 -0
  127. package/engine/sessions/apply-fences.cjs +188 -0
  128. package/engine/sessions/fences.cjs +362 -0
  129. package/engine/sessions/orchestrator.cjs +170 -0
  130. package/engine/sessions/prompt.cjs +212 -0
  131. package/engine/sessions/session.cjs +245 -0
  132. package/engine/sessions/sink.cjs +54 -0
  133. package/engine/sessions/store.cjs +79 -0
  134. package/engine/storm/deps.cjs +88 -0
  135. package/engine/storm/storm.cjs +218 -0
  136. package/engine/storm/swarm.cjs +422 -0
  137. package/engine/ui/palette.cjs +105 -0
  138. package/engine/ui/renderer.cjs +85 -0
  139. package/engine/ui/repl.cjs +444 -0
  140. package/engine/workforce/capture.cjs +701 -0
  141. package/engine/workforce/deps.cjs +472 -0
  142. package/package.json +2 -6
  143. package/engine/agentlas-experience-mcp.cjs +0 -1709
  144. package/engine/agentlas-parity.cjs +0 -1499
  145. package/engine/agentlas-repl.cjs +0 -1780
@@ -23,39 +23,177 @@ function userDataDir() {
23
23
  }
24
24
 
25
25
  const HISTORY_MAX = 500;
26
+ const HISTORY_SCHEMA_VERSION = 2;
27
+ const HISTORY_LOCK_WAIT_MS = 2_000;
28
+ const HISTORY_LOCK_STALE_MS = 30_000;
26
29
  function historyPath() {
27
30
  return path.join(userDataDir(), "cli-history.json");
28
31
  }
29
- // readline keeps history with index 0 = most-recent. We persist that array verbatim.
30
- function loadHistory() {
32
+
33
+ function historyBackupPath() {
34
+ return historyPath() + ".bak";
35
+ }
36
+
37
+ function normalizeHistoryDocument(raw) {
38
+ if (Array.isArray(raw)) {
39
+ // Legacy history had no project identity. Keep it quarantined on disk so
40
+ // migration is non-destructive, but never surface it in another project.
41
+ return {
42
+ version: HISTORY_SCHEMA_VERSION,
43
+ entries: [],
44
+ legacyUnscoped: raw.filter((x) => typeof x === "string").slice(0, HISTORY_MAX),
45
+ };
46
+ }
47
+ if (raw && raw.version === HISTORY_SCHEMA_VERSION && Array.isArray(raw.entries)) {
48
+ return {
49
+ version: HISTORY_SCHEMA_VERSION,
50
+ entries: raw.entries,
51
+ legacyUnscoped: Array.isArray(raw.legacyUnscoped) ? raw.legacyUnscoped.slice(0, HISTORY_MAX) : [],
52
+ };
53
+ }
54
+ return null;
55
+ }
56
+
57
+ function readHistoryFile(file) {
31
58
  try {
32
- const a = JSON.parse(fs.readFileSync(historyPath(), "utf8"));
33
- return Array.isArray(a) ? a.filter((x) => typeof x === "string").slice(0, HISTORY_MAX) : [];
59
+ return normalizeHistoryDocument(JSON.parse(fs.readFileSync(file, "utf8")));
34
60
  } catch {
35
- return [];
61
+ return null;
62
+ }
63
+ }
64
+
65
+ function historyScope(cwd = process.cwd()) {
66
+ try { return path.resolve(String(cwd || process.cwd())); } catch { return String(cwd || process.cwd()); }
67
+ }
68
+
69
+ function readHistoryDocument() {
70
+ const current = readHistoryFile(historyPath());
71
+ if (current) return current;
72
+ const backup = readHistoryFile(historyBackupPath());
73
+ if (backup) return backup;
74
+ return { version: HISTORY_SCHEMA_VERSION, entries: [], legacyUnscoped: [] };
75
+ }
76
+
77
+ function sleepSync(ms) {
78
+ try {
79
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
80
+ } catch {
81
+ const until = Date.now() + ms;
82
+ while (Date.now() < until) { /* old Node fallback */ }
83
+ }
84
+ }
85
+
86
+ function withHistoryLock(callback) {
87
+ const lock = historyPath() + ".lock";
88
+ const deadline = Date.now() + HISTORY_LOCK_WAIT_MS;
89
+ while (true) {
90
+ try {
91
+ fs.mkdirSync(lock, { mode: 0o700 });
92
+ break;
93
+ } catch (error) {
94
+ if (!error || error.code !== "EEXIST") throw error;
95
+ try {
96
+ const stat = fs.statSync(lock);
97
+ if (Date.now() - stat.mtimeMs > HISTORY_LOCK_STALE_MS) {
98
+ fs.rmdirSync(lock);
99
+ continue;
100
+ }
101
+ } catch {
102
+ continue;
103
+ }
104
+ if (Date.now() >= deadline) throw new Error("history lock timeout");
105
+ sleepSync(10);
106
+ }
107
+ }
108
+ try {
109
+ return callback();
110
+ } finally {
111
+ try { fs.rmdirSync(lock); } catch { /* another process will recover a stale lock */ }
36
112
  }
37
113
  }
38
- function saveHistory(list) {
114
+
115
+ function atomicWriteHistoryFile(file, document) {
116
+ const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
117
+ let fd;
118
+ try {
119
+ fd = fs.openSync(tmp, "wx", 0o600);
120
+ fs.writeFileSync(fd, JSON.stringify(document), "utf8");
121
+ fs.fsyncSync(fd);
122
+ fs.closeSync(fd);
123
+ fd = null;
124
+ fs.renameSync(tmp, file);
125
+ try { fs.chmodSync(file, 0o600); } catch { /* win32 */ }
126
+ } finally {
127
+ if (fd != null) {
128
+ try { fs.closeSync(fd); } catch { /* ignore */ }
129
+ }
130
+ try { fs.unlinkSync(tmp); } catch { /* already renamed or never created */ }
131
+ }
132
+ }
133
+
134
+ function quarantineMalformedHistory() {
135
+ const file = historyPath();
136
+ if (!fs.existsSync(file) || readHistoryFile(file)) return;
137
+ const quarantine = `${file}.corrupt-${Date.now()}-${process.pid}`;
138
+ try { fs.renameSync(file, quarantine); } catch { /* keep fail-closed recovery from backup */ }
139
+ }
140
+
141
+ // readline keeps history with index 0 = most recent. Only the current working
142
+ // folder's entries are returned; legacy unscoped strings are never displayed.
143
+ function loadHistory(cwd = process.cwd()) {
144
+ const scope = historyScope(cwd);
145
+ return readHistoryDocument().entries
146
+ .filter((entry) => entry && entry.cwd === scope && typeof entry.text === "string")
147
+ .sort((a, b) => Number(b.ts || 0) - Number(a.ts || 0))
148
+ .map((entry) => entry.text)
149
+ .slice(0, HISTORY_MAX);
150
+ }
151
+
152
+ function saveHistory(list, cwd = process.cwd()) {
39
153
  try {
40
154
  fs.mkdirSync(userDataDir(), { recursive: true });
41
155
  const clean = (list || []).filter((x) => typeof x === "string" && x.trim()).slice(0, HISTORY_MAX);
42
- fs.writeFileSync(historyPath(), JSON.stringify(clean), "utf8");
43
- return true;
156
+ const scope = historyScope(cwd);
157
+ return withHistoryLock(() => {
158
+ const currentFileWasValid = Boolean(readHistoryFile(historyPath()));
159
+ const document = readHistoryDocument();
160
+ quarantineMalformedHistory();
161
+ const existingScope = document.entries
162
+ .filter((entry) => entry && entry.cwd === scope && typeof entry.text === "string")
163
+ .sort((a, b) => Number(b.ts || 0) - Number(a.ts || 0));
164
+ const mergedTexts = [...clean];
165
+ for (const entry of existingScope) {
166
+ if (!mergedTexts.includes(entry.text)) mergedTexts.push(entry.text);
167
+ }
168
+ const other = document.entries.filter((entry) => entry && entry.cwd !== scope && typeof entry.text === "string");
169
+ const now = Date.now();
170
+ const current = mergedTexts.slice(0, HISTORY_MAX).map((text, index) => ({ text, cwd: scope, ts: now - index }));
171
+ const next = {
172
+ version: HISTORY_SCHEMA_VERSION,
173
+ entries: [...current, ...other]
174
+ .sort((a, b) => Number(b.ts || 0) - Number(a.ts || 0))
175
+ .slice(0, HISTORY_MAX),
176
+ legacyUnscoped: document.legacyUnscoped,
177
+ };
178
+ if (currentFileWasValid) atomicWriteHistoryFile(historyBackupPath(), document);
179
+ atomicWriteHistoryFile(historyPath(), next);
180
+ return true;
181
+ });
44
182
  } catch {
45
183
  return false;
46
184
  }
47
185
  }
48
186
  // Seed an interactive readline with saved history (no-op on non-TTY).
49
- function attachHistory(rl) {
187
+ function attachHistory(rl, cwd = process.cwd()) {
50
188
  try {
51
- if (rl && rl.terminal && Array.isArray(rl.history)) rl.history = loadHistory();
189
+ if (rl && rl.terminal && Array.isArray(rl.history)) rl.history = loadHistory(cwd);
52
190
  } catch {
53
191
  /* ignore */
54
192
  }
55
193
  }
56
- function persistHistory(rl) {
194
+ function persistHistory(rl, cwd = process.cwd()) {
57
195
  try {
58
- if (rl && Array.isArray(rl.history)) saveHistory(rl.history);
196
+ if (rl && Array.isArray(rl.history)) saveHistory(rl.history, cwd);
59
197
  } catch {
60
198
  /* ignore */
61
199
  }
@@ -71,6 +209,55 @@ function stripContinuation(line) {
71
209
  return (line || "").replace(/\\$/, "");
72
210
  }
73
211
 
212
+ function tokenizeCommandLine(value) {
213
+ const text = String(value || "");
214
+ const tokens = [];
215
+ let token = "";
216
+ let tokenStarted = false;
217
+ let quote = null;
218
+ let escaped = false;
219
+ const push = () => {
220
+ if (!tokenStarted) return;
221
+ tokens.push(token);
222
+ token = "";
223
+ tokenStarted = false;
224
+ };
225
+ for (const char of text) {
226
+ if (escaped) {
227
+ token += char;
228
+ tokenStarted = true;
229
+ escaped = false;
230
+ continue;
231
+ }
232
+ if (char === "\\" && quote !== "'") {
233
+ escaped = true;
234
+ tokenStarted = true;
235
+ continue;
236
+ }
237
+ if (quote) {
238
+ if (char === quote) quote = null;
239
+ else token += char;
240
+ tokenStarted = true;
241
+ continue;
242
+ }
243
+ if (char === "'" || char === '"') {
244
+ quote = char;
245
+ tokenStarted = true;
246
+ continue;
247
+ }
248
+ if (/\s/u.test(char)) {
249
+ push();
250
+ continue;
251
+ }
252
+ token += char;
253
+ tokenStarted = true;
254
+ }
255
+ if (escaped) token += "\\";
256
+ if (quote) throw new Error("unclosed quote");
257
+ push();
258
+ return tokens;
259
+ }
260
+
74
261
  // ── completion ────────────────────────────────────────────
75
262
  const SLASH_COMMAND_META = [
76
263
  { command: "/help", description: "Show Agentlas terminal commands", category: "Help", usage: "/help", detail: "Open the command reference, shortcuts, and common flows." },
@@ -89,6 +276,7 @@ const SLASH_COMMAND_META = [
89
276
  { command: "/permission", description: "Set read/write/full permission", category: "Settings", usage: "/permission full", detail: "No argument shows what read, write, and full mean.", aliases: ["/perm"] },
90
277
  { command: "/permissions", description: "Show or set current permission", category: "Settings", usage: "/permissions", detail: "Codex-style permission screen for Agentlas read/write/full." },
91
278
  { command: "/setup", description: "Run first-time setup again", category: "Settings", usage: "/setup", detail: "Re-run language, runtime, and default permission setup in-place." },
279
+ { command: "/project", description: "Inspect or explicitly initialize local project state", category: "Files", usage: "/project status|init", detail: "Ordinary turns never seed project files. `init` explicitly creates private .agentlas state and updates local ignore/templates." },
92
280
  { command: "/config", description: "Explicit on/off for Stormbreaker and Workforce Ontology auto engagement", category: "Settings", usage: "/config [storm|network] [on|off]", detail: "Both engines default to off — no automatic activation. Turn one on to let direct-routed real-work prompts engage it; /storm and /network stay available explicitly either way.", examples: ["/config", "/config storm on", "/config network off"], aliases: ["/toggles"] },
93
281
  { command: "/cwd", description: "Show or change the working folder", category: "Files", usage: "/cwd <path>", detail: "Change the folder used for tools, file mentions, and local commands." },
94
282
  { command: "/memory", description: "Show the memory injected into this run", category: "Context", usage: "/memory", detail: "Print the project memory that Agentlas adds to agent turns." },
@@ -139,6 +327,7 @@ const HELP_KEY_BY_COMMAND = {
139
327
  "/permission": "help.permission",
140
328
  "/permissions": "help.permissions",
141
329
  "/setup": "help.setup",
330
+ "/project": "help.project",
142
331
  "/config": "help.config",
143
332
  "/cwd": "help.cwd",
144
333
  "/memory": "help.memory",
@@ -162,6 +351,7 @@ const HELP_KEY_BY_COMMAND = {
162
351
  "/research": "help.research",
163
352
  "/search": "help.search",
164
353
  "/network": "help.network",
354
+ "/legacy-network": "help.legacyNetwork",
165
355
  "/browser": "help.browser",
166
356
  "/connect": "help.connect",
167
357
  "/doctor": "help.doctor",
@@ -177,10 +367,26 @@ function localizeSlashEntry(entry, lang) {
177
367
  const helpKey = HELP_KEY_BY_COMMAND[entry.command];
178
368
  const description = helpKey ? i18n.t(lang, helpKey) : entry.description;
179
369
  const category = entry.category ? i18n.t(lang, `category.${entry.category}`) : entry.category;
370
+ const usage = lang === "ko"
371
+ ? String(entry.usage || entry.command)
372
+ .replaceAll("<goal>", "<목표>")
373
+ .replaceAll("<request>", "<요청>")
374
+ .replaceAll("<task>", "<작업>")
375
+ .replaceAll("<question>", "<질문>")
376
+ .replaceAll("<query>", "<검색어>")
377
+ .replaceAll("<slug>", "<식별자>")
378
+ .replaceAll("<path>", "<경로>")
379
+ .replaceAll("<name>", "<이름>")
380
+ .replaceAll("<what you need>", "<필요한 작업>")
381
+ .replaceAll("<what to build>", "<빌드할 대상>")
382
+ .replaceAll("\"query\"", "\"검색어\"")
383
+ .replaceAll("[sub]", "[하위 명령]")
384
+ : entry.usage;
180
385
  return {
181
386
  ...entry,
182
387
  description,
183
388
  category,
389
+ usage,
184
390
  // English keeps the longer authored detail. Other languages must not fall back to
185
391
  // an English paragraph under an otherwise-localized command palette.
186
392
  detail: lang === "en" ? entry.detail : description,
@@ -226,7 +432,8 @@ function slashCommandSuggestions(line, limit = 12, lang = "en") {
226
432
  !entry.command.toLowerCase().startsWith(q) &&
227
433
  (entry.command.toLowerCase().includes(q.slice(1)) || entry.description.toLowerCase().includes(q.slice(1))),
228
434
  );
229
- return (starts.length ? starts.concat(contains) : entries).slice(0, limit);
435
+ if (q === "/") return entries.slice(0, limit);
436
+ return starts.concat(contains).slice(0, limit);
230
437
  }
231
438
 
232
439
  function padVisible(value, width) {
@@ -280,7 +487,7 @@ function visibleWidthLite(value) {
280
487
 
281
488
  function renderSlashPalette(rows, selectedIndex, opts = {}) {
282
489
  if (!rows.length) return "";
283
- const columns = Math.max(48, Number(opts.columns || 88));
490
+ const columns = Math.max(12, Math.floor(Number(opts.columns || 88)));
284
491
  const fallbackColors = {
285
492
  faint: (s) => String(s),
286
493
  dim: (s) => String(s),
@@ -290,16 +497,24 @@ function renderSlashPalette(rows, selectedIndex, opts = {}) {
290
497
  };
291
498
  const c = { ...fallbackColors, ...(opts.colors || {}) };
292
499
  const lang = opts.lang || "en";
293
- const commandWidth = Math.min(24, Math.max(16, rows.reduce((n, row) => Math.max(n, row.command.length), 0) + 2));
294
- const descWidth = Math.max(12, columns - commandWidth - 8);
295
- const lineWidth = Math.min(columns - 1, commandWidth + descWidth + 5);
500
+ const lineWidth = Math.max(1, columns - 1);
501
+ const desiredCommandWidth = Math.max(
502
+ 8,
503
+ rows.reduce((n, row) => Math.max(n, visibleWidthLite(row.command)), 0) + 2,
504
+ );
505
+ const commandWidth = Math.min(
506
+ 24,
507
+ desiredCommandWidth,
508
+ Math.max(8, Math.floor((lineWidth - 2) * 0.55)),
509
+ );
510
+ const descWidth = Math.max(0, lineWidth - commandWidth - 1);
296
511
  const selected = rows[Math.max(0, Math.min(selectedIndex, rows.length - 1))] || rows[0];
297
512
  const out = [
298
513
  c.faint(truncateVisible(`${i18n.t(lang, "palette.title")} ${i18n.t(lang, "palette.search")}`, lineWidth)),
299
514
  c.faint("─".repeat(lineWidth)),
300
515
  ];
301
516
  rows.forEach((row, index) => {
302
- const command = padVisible(row.command, commandWidth);
517
+ const command = padVisible(truncateVisible(row.command, commandWidth), commandWidth);
303
518
  const desc = truncateVisible(row.description, descWidth);
304
519
  const body = " " + c.blue(command) + c.text(desc);
305
520
  out.push(index === selectedIndex ? c.inverse(padVisible(body, lineWidth)) : body);
@@ -531,6 +746,7 @@ module.exports = {
531
746
  attachSlashPalette,
532
747
  isContinuation,
533
748
  stripContinuation,
749
+ tokenizeCommandLine,
534
750
  isAbsolutePathTask,
535
751
  makeCompleter,
536
752
  completePath,
@@ -31,6 +31,7 @@ const SEMANTIC_DISPOSITIONS = new Set(["retain", "session", "discard", "review"]
31
31
  const CONFIDENCE_LEVELS = new Set(["high", "medium", "low"]);
32
32
 
33
33
  const { SECRET_PATTERNS } = require("./agentlas-secret-patterns.cjs");
34
+ const { runWriteTransaction } = require("./agentlas-sqlite-policy.cjs");
34
35
  const ABSOLUTE_PATH_PATTERNS = [
35
36
  /(?:^|[\s("'`])~\/[A-Za-z0-9._-]/,
36
37
  /(?:^|[\s("'`])\/(?:Users|home|private|var|tmp|opt|etc|Volumes|Applications|System|Library)\//,
@@ -258,7 +259,7 @@ function beginTurn(db, input = {}) {
258
259
  : legacyRequestFingerprint;
259
260
  const now = new Date().toISOString();
260
261
 
261
- const run = db.transaction(() => {
262
+ const result = runWriteTransaction(db, () => {
262
263
  if (explicitTurnId) {
263
264
  const existing = db.prepare("SELECT * FROM terminal_memory_turn_intents WHERE turn_id=?").get(explicitTurnId);
264
265
  // Accept the pre-v1 host fingerprint for an in-flight turn created
@@ -299,8 +300,6 @@ function beginTurn(db, input = {}) {
299
300
  reused: false,
300
301
  };
301
302
  });
302
-
303
- const result = run();
304
303
  let storedPolicy = policy;
305
304
  try { storedPolicy = JSON.parse(result.row.owner_policy_json || "{}"); } catch { /* use current safe policy */ }
306
305
  return {
@@ -852,7 +851,7 @@ async function completeTurn(db, input = {}) {
852
851
  let retainedMemories = [];
853
852
  let decisionBatch = null;
854
853
  let replayedByRace = false;
855
- const run = db.transaction(() => {
854
+ runWriteTransaction(db, () => {
856
855
  const raced = readExistingReceipt(db, turnId);
857
856
  if (raced) {
858
857
  ticket = raced;
@@ -953,7 +952,6 @@ async function completeTurn(db, input = {}) {
953
952
  "UPDATE terminal_memory_turn_intents SET status='completed', completed_at=? WHERE turn_id=?",
954
953
  ).run(timestamp, turnId);
955
954
  });
956
- run();
957
955
 
958
956
  if (normalizePermission(turn.permission) !== "read" && projectContextMatches && ownerContextMatches) {
959
957
  mirrorCoreLogs(boundProjectPath, ticket, decisionBatch, input.coreFiles);
@@ -13,6 +13,7 @@
13
13
  const fs = require("node:fs");
14
14
  const path = require("node:path");
15
15
  const { createHash, randomUUID } = require("node:crypto");
16
+ const { runWriteTransaction } = require("./agentlas-sqlite-policy.cjs");
16
17
 
17
18
  const SOURCE_TOKEN_PREFIX = "mem-import:v1";
18
19
  const MAX_FILES = 400;
@@ -286,7 +287,7 @@ function cmdMemory(ctx) {
286
287
  const insert = db.prepare(
287
288
  "INSERT INTO memory_entries (id,scope,kind,content,project_id,project_path,agent_id,chat_id,confidence,sensitivity,evidence_json,context_json,superseded_at,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,NULL,?)",
288
289
  );
289
- const write = db.transaction((list) => {
290
+ runWriteTransaction(db, (list) => {
290
291
  for (const e of list) {
291
292
  if (e.redacted || existsByToken(db, e.token)) continue;
292
293
  const confidence = /\(20\d\d-\d\d-\d\d\)|Date:\s*20\d\d/.test(e.content) ? "high" : "medium";
@@ -295,8 +296,7 @@ function cmdMemory(ctx) {
295
296
  insert.run(randomUUID(), e.scope, e.kind, e.content, null, null, e.ownerAgentId, null, confidence, "internal", evidence, context, now);
296
297
  imported += 1;
297
298
  }
298
- });
299
- write(entries);
299
+ }, entries);
300
300
 
301
301
  out("");
302
302
  out(`imported ${imported} memory entries into the shared DB. (Embedding runs in the desktop app on next open.)`);