@lazyingart/agintiflow 0.8.7 → 0.8.9

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
@@ -50,6 +50,7 @@ On first interactive use, if no DeepSeek key is detected, `aginti` asks you to p
50
50
 
51
51
  ```bash
52
52
  aginti login deepseek
53
+ # inside chat, use /login or /auth
53
54
  # or non-interactively:
54
55
  printf '%s' "$DEEPSEEK_API_KEY" | aginti keys set deepseek --stdin
55
56
  ```
@@ -62,7 +63,7 @@ aginti
62
63
  aginti chat
63
64
  ```
64
65
 
65
- Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is Docker workspace mode with approved package installs, so coding, plotting, and LaTeX tasks can set up project-local tools without touching the host. Use `/help` for commands, `/latex on` for PDF work, `/docker off` only when you intentionally want host mode, `/sessions` to list project runs, and `/resume latest` or `/resume <session-id>` to continue work. Esc or Ctrl+C stops the active run cleanly and prints the resume command.
66
+ Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is Docker workspace mode with approved package installs, so coding, plotting, and LaTeX tasks can set up project-local tools without touching the host. Use `/help` for commands, `/login` or `/auth` to paste a provider key, `/latex on` for PDF work, `/docker off` only when you intentionally want host mode, `/sessions` to list project runs, and `/resume latest` or `/resume <session-id>` to continue work. Type `/` then Tab for command completion. Esc or Ctrl+C stops the active run cleanly and prints the resume command.
66
67
 
67
68
  Launch the local web UI from an installed package:
68
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.8.7",
3
+ "version": "0.8.9",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a resumable Playwright website-control agent with OpenAI-compatible tool calling.",
6
6
  "license": "Apache-2.0",
@@ -258,6 +258,7 @@ function createInitialState(config, sessionId) {
258
258
  content: [
259
259
  "You are a careful browser and shell agent with a small tool surface.",
260
260
  "Use only the provided tools.",
261
+ "The execution plan is not the final answer. After planning, actively use tools until the requested task is complete or genuinely blocked.",
261
262
  "If the shell tool can satisfy a local task, prefer it before opening a browser.",
262
263
  "Do not open a browser page just because a start URL exists. Treat it as a suggestion only.",
263
264
  "Only reference element ids from the latest browser snapshot.",
@@ -279,6 +280,9 @@ function createInitialState(config, sessionId) {
279
280
  "A frontend canvas/artifacts tunnel exists. Use send_to_canvas when important markdown, diffs, screenshots, images, or workspace files should be highlighted in the UI. It is optional and ordinary final text can still go directly to finish.",
280
281
  "For visual-output requests such as draw, plot, graph, chart, diagram, figure, image, or visualization, proactively publish a canvas artifact even when the user does not mention canvas. If workspace file tools are enabled, prefer creating a small SVG or markdown artifact and call send_to_canvas with selected=true.",
281
282
  "Work like a practical coding agent: inspect when useful, edit with file tools, run safe checks when they add confidence, and keep outputs inside the workspace.",
283
+ "For large projects, decompose into useful files and milestones, implement a coherent minimal version first, then iterate with checks rather than only describing what you would do.",
284
+ "For website/app/code/LaTeX/Python/C/shell tasks, create or edit real workspace files, run available build/compile/test commands, and surface artifacts through the canvas when useful.",
285
+ "For research or web-search tasks, use browser tools or safe shell network tools when the current policy allows; cite or save useful sources in workspace notes when the task needs traceability.",
282
286
  "Use the canvas tunnel for outputs the user would likely want to inspect visually, such as figures, PDFs, screenshots, images, important markdown, or generated files.",
283
287
  "For environment or system-maintenance work, use the configured sandbox and package policy; Docker workspace mode is the preferred place for installs and toolchain setup.",
284
288
  "If the user asks to open a generated local website or file, use open_workspace_file for a file or preview_workspace for a static site. Do not keep retrying the same localhost URL when a preview fails.",
@@ -309,6 +313,7 @@ function createInitialState(config, sessionId) {
309
313
  "Canvas/artifacts tunnel: available through send_to_canvas for optional frontend rendering.",
310
314
  "Visual-output requests should produce a canvas artifact without requiring the user to ask for canvas explicitly.",
311
315
  "Use file, shell, browser, canvas, and wrapper tools when they are useful; choose the workflow from the user's request.",
316
+ "Do not stop at a plan when tools can accomplish the request. Continue through implementation, checks, artifact selection, and finish.",
312
317
  "Use the configured sandbox and package policy for environment or system-maintenance work.",
313
318
  ]
314
319
  .filter(Boolean)
@@ -1078,6 +1083,7 @@ export async function runAgent(config) {
1078
1083
  url: snapshot.url,
1079
1084
  title: snapshot.title,
1080
1085
  screenshotPath: snapshot.screenshotPath,
1086
+ screenshotWarning: snapshot.screenshotWarning || "",
1081
1087
  snapshotPath: snapshot.snapshotPath,
1082
1088
  });
1083
1089
  observers.event("snapshot.captured", {
@@ -1085,6 +1091,7 @@ export async function runAgent(config) {
1085
1091
  url: snapshot.url,
1086
1092
  title: snapshot.title,
1087
1093
  screenshotPath: snapshot.screenshotPath,
1094
+ screenshotWarning: snapshot.screenshotWarning || "",
1088
1095
  });
1089
1096
 
1090
1097
  state.messages.push({
@@ -3,12 +3,12 @@ import { emitKeypressEvents } from "node:readline";
3
3
  import { stdin as input, stdout as output } from "node:process";
4
4
  import { runAgent } from "./agent-runner.js";
5
5
  import { loadConfig } from "./config.js";
6
- import { initProject, listProjectSessions } from "./project.js";
6
+ import { initProject, listProjectSessions, providerKeyStatus, setProviderKey } from "./project.js";
7
7
  import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./command-policy.js";
8
8
  import { normalizeTaskProfile } from "./task-profiles.js";
9
- import { promptAndSaveDeepSeekKey, shouldPromptForDeepSeek } from "./auth-onboarding.js";
9
+ import { promptAndSaveDeepSeekKey, promptHidden, shouldPromptForDeepSeek } from "./auth-onboarding.js";
10
10
 
11
- const useColor = Boolean(input.isTTY && output.isTTY && !process.env.NO_COLOR);
11
+ const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
12
12
  const ansi = {
13
13
  reset: "\x1b[0m",
14
14
  bold: "\x1b[1m",
@@ -17,16 +17,44 @@ const ansi = {
17
17
  green: "\x1b[32m",
18
18
  yellow: "\x1b[33m",
19
19
  red: "\x1b[31m",
20
+ clearLine: "\x1b[2K",
21
+ cursorHide: "\x1b[?25l",
22
+ cursorShow: "\x1b[?25h",
20
23
  userBg: "\x1b[48;5;24m\x1b[38;5;231m",
21
24
  agentBg: "\x1b[48;5;29m\x1b[38;5;231m",
22
25
  systemBg: "\x1b[48;5;236m\x1b[38;5;245m",
23
26
  };
27
+ const brandPalette = ["\x1b[38;5;45m", "\x1b[38;5;81m", "\x1b[38;5;86m", "\x1b[38;5;118m", "\x1b[38;5;226m"];
28
+ const SLASH_COMMANDS = [
29
+ "/help",
30
+ "/status",
31
+ "/login",
32
+ "/auth",
33
+ "/new",
34
+ "/resume",
35
+ "/sessions",
36
+ "/profile",
37
+ "/routing",
38
+ "/provider",
39
+ "/model",
40
+ "/docker",
41
+ "/latex",
42
+ "/installs",
43
+ "/cwd",
44
+ "/init",
45
+ "/web",
46
+ "/exit",
47
+ ];
24
48
 
25
49
  function color(value, ...codes) {
26
50
  if (!useColor || codes.length === 0) return String(value);
27
51
  return `${codes.join("")}${value}${ansi.reset}`;
28
52
  }
29
53
 
54
+ function sleep(ms) {
55
+ return new Promise((resolve) => setTimeout(resolve, ms));
56
+ }
57
+
30
58
  function label(name, bgCode) {
31
59
  return color(` ${name} `, bgCode, ansi.bold);
32
60
  }
@@ -35,6 +63,13 @@ function userPrompt() {
35
63
  return `\n${label("user>", ansi.userBg)} `;
36
64
  }
37
65
 
66
+ function commandCompleter(line = "") {
67
+ const trimmed = String(line || "");
68
+ if (!trimmed.startsWith("/")) return [[], trimmed];
69
+ const hits = SLASH_COMMANDS.filter((command) => command.startsWith(trimmed));
70
+ return [hits.length > 0 ? hits : SLASH_COMMANDS, trimmed];
71
+ }
72
+
38
73
  function stripMarkdown(text) {
39
74
  const lines = String(text || "").split(/\r?\n/);
40
75
  let inFence = false;
@@ -96,12 +131,51 @@ function printHeading(text) {
96
131
  console.log(color(stripMarkdown(text), ansi.bold, ansi.cyan));
97
132
  }
98
133
 
134
+ function shimmerText(text, frame) {
135
+ if (!useColor) return text;
136
+ return [...text]
137
+ .map((char, index) => {
138
+ if (char === " ") return char;
139
+ const code = brandPalette[(index + frame) % brandPalette.length];
140
+ return `${code}${ansi.bold}${char}${ansi.reset}`;
141
+ })
142
+ .join("");
143
+ }
144
+
145
+ async function renderLaunchHeader(packageVersion = "") {
146
+ const title = "AgInTi Flow";
147
+ const subtitle = "web-first agent workspace";
148
+ const version = packageVersion ? `v${packageVersion}` : "";
149
+ const line = "+--------------------------------------------------+";
150
+
151
+ if (!useColor || process.env.AGINTIFLOW_NO_ANIMATION === "1") {
152
+ console.log(` AgInTiFlow ${packageVersion || ""}`.trim());
153
+ return;
154
+ }
155
+
156
+ output.write(ansi.cursorHide);
157
+ for (let frame = 0; frame < 18; frame += 1) {
158
+ output.write(`\r${ansi.clearLine}${shimmerText(title, frame)} ${color("is starting", ansi.dim)}`);
159
+ await sleep(32);
160
+ }
161
+ output.write(`\r${ansi.clearLine}`);
162
+ output.write(ansi.cursorShow);
163
+
164
+ console.log(color(line, "\x1b[38;5;45m"));
165
+ console.log(`${color("|", "\x1b[38;5;45m")} ${shimmerText(title, 2)} ${color(version.padStart(36 - title.length), ansi.dim)} ${color("|", "\x1b[38;5;45m")}`);
166
+ console.log(`${color("|", "\x1b[38;5;45m")} ${color(subtitle.padEnd(48), ansi.dim)} ${color("|", "\x1b[38;5;45m")}`);
167
+ console.log(`${color("|", "\x1b[38;5;45m")} ${color("browser + shell + files + docker + canvas".padEnd(48), ansi.cyan)} ${color("|", "\x1b[38;5;45m")}`);
168
+ console.log(color(line, "\x1b[38;5;45m"));
169
+ }
170
+
99
171
  function printHelp() {
100
172
  printAgentMessage(
101
173
  [
102
174
  "Commands:",
103
175
  " /help Show this help.",
104
176
  " /status Show active route, workspace, sandbox, and session.",
177
+ " /login [deepseek|openai] Paste and save a project-local API key.",
178
+ " /auth [deepseek|openai] Alias for /login.",
105
179
  " /new Start a fresh session on the next message.",
106
180
  " /resume <session-id> Continue a saved session.",
107
181
  " /sessions List recent sessions in this project.",
@@ -117,6 +191,7 @@ function printHelp() {
117
191
  " /exit Quit.",
118
192
  "",
119
193
  "Type a normal request to run the agent. Example: write a Python CLI app with tests",
194
+ "Type / then Tab to autocomplete commands.",
120
195
  "While a run is active, press Esc or Ctrl+C once to stop gracefully and print a resume command.",
121
196
  ].join("\n")
122
197
  );
@@ -247,6 +322,28 @@ async function maybeOnboardDeepSeekKey(state) {
247
322
  printAgentMessage("No key saved. Continuing in local mock mode. Use `/provider deepseek` after running `aginti login deepseek`.");
248
323
  }
249
324
 
325
+ async function promptAndSaveProviderKey(provider = "deepseek", state = null) {
326
+ const normalized = ["openai", "deepseek"].includes(String(provider || "").toLowerCase())
327
+ ? String(provider || "").toLowerCase()
328
+ : "deepseek";
329
+ const labelText = normalized === "openai" ? "OpenAI" : "DeepSeek";
330
+ const key = await promptHidden(`${labelText} API key/token (paste, Enter to save): `);
331
+ if (!key) {
332
+ printAgentMessage("No key saved.");
333
+ return;
334
+ }
335
+
336
+ const result = await setProviderKey(process.cwd(), normalized, key);
337
+ if (state) {
338
+ state.provider = normalized;
339
+ if (state.routingMode === "manual" && state.model === "mock-agent") {
340
+ state.routingMode = "smart";
341
+ state.model = "";
342
+ }
343
+ }
344
+ printAgentMessage(`Saved ${result.keyName} to project-local ignored env. Raw key was not printed.`);
345
+ }
346
+
250
347
  async function handleCommand(line, state, packageDir) {
251
348
  const [command, ...rest] = line.slice(1).trim().split(/\s+/);
252
349
  const value = rest.join(" ").trim();
@@ -258,6 +355,12 @@ async function handleCommand(line, state, packageDir) {
258
355
  if (command === "exit" || command === "quit" || command === "q") return false;
259
356
  if (command === "status") {
260
357
  printStatus(state);
358
+ const keys = providerKeyStatus(process.cwd());
359
+ printSystemLine(`keys deepseek=${keys.deepseek ? "available" : "missing"} openai=${keys.openai ? "available" : "missing"}`);
360
+ return true;
361
+ }
362
+ if (command === "login" || command === "auth") {
363
+ await promptAndSaveProviderKey(value || "deepseek", state);
261
364
  return true;
262
365
  }
263
366
  if (command === "new") {
@@ -365,7 +468,13 @@ async function handleCommand(line, state, packageDir) {
365
468
  return true;
366
469
  }
367
470
 
368
- printAgentMessage(`Unknown command: /${command}. Use /help.`);
471
+ const typed = `/${command}`;
472
+ const suggestions = SLASH_COMMANDS.filter((candidate) => candidate.startsWith(typed));
473
+ printAgentMessage(
474
+ suggestions.length > 0
475
+ ? `Unknown command: /${command}. Did you mean:\n${suggestions.map((item) => ` ${item}`).join("\n")}`
476
+ : `Unknown command: /${command}. Use /help.`
477
+ );
369
478
  return true;
370
479
  }
371
480
 
@@ -455,11 +564,16 @@ async function runPrompt(prompt, state, packageDir) {
455
564
 
456
565
  export async function startInteractiveCli(args = {}, { packageDir, packageVersion } = {}) {
457
566
  const state = createState(args);
458
- await maybeOnboardDeepSeekKey(state);
459
- const rl = readline.createInterface({ input, output, terminal: Boolean(input.isTTY && output.isTTY) });
567
+ const rl = readline.createInterface({
568
+ input,
569
+ output,
570
+ terminal: Boolean(input.isTTY && output.isTTY),
571
+ completer: commandCompleter,
572
+ });
460
573
 
461
- console.log(color(` AgInTiFlow ${packageVersion || ""} `, ansi.agentBg, ansi.bold).trimEnd());
574
+ await renderLaunchHeader(packageVersion);
462
575
  printSystemLine(`Project: ${process.cwd()}`);
576
+ await maybeOnboardDeepSeekKey(state);
463
577
  printAgentMessage("Interactive agent chat. Type /help for commands, /exit to quit.");
464
578
  printStatus(state);
465
579
 
@@ -154,7 +154,7 @@ export async function createPlan(client, config, state) {
154
154
  {
155
155
  role: "system",
156
156
  content:
157
- "You are planning a browser-and-shell automation task. If a local shell command can satisfy the goal, prefer that before browser actions. Treat any suggested start URL as optional. Write a concise execution plan with 3 to 6 steps. Mention risks or blockers when relevant. Keep it short and practical.",
157
+ "You are planning a browser, shell, workspace, and coding-agent task. The plan is only a launchpad: after planning, the runtime will continue with tools until the task is complete or genuinely blocked. Prefer real workspace edits/checks over advice-only answers. If a local shell command can satisfy the goal, prefer that before browser actions. Treat any suggested start URL as optional. Write a concise execution plan with 3 to 6 steps. Mention risks or blockers when relevant. Keep it short and practical.",
158
158
  },
159
159
  {
160
160
  role: "user",
@@ -174,6 +174,8 @@ export async function createPlan(client, config, state) {
174
174
  `Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
175
175
  "A canvas/artifacts tunnel is available through send_to_canvas. Use it when an output should be highlighted visually, such as screenshots, image files, important markdown, diffs, or generated artifact paths. It is optional for ordinary text answers.",
176
176
  "Work like a practical coding agent: inspect when useful, edit with file tools, run safe checks when they add confidence, and keep outputs inside the workspace.",
177
+ "For large apps, websites, LaTeX documents, Python/C/shell projects, or system tasks, plan a coherent minimal implementation, then use tools to create files, run checks, and publish artifacts.",
178
+ "For web search or current information tasks, plan to use browser tools or safe shell network tools when allowed, then preserve useful source notes if the output depends on them.",
177
179
  "Use the canvas tunnel for outputs the user would likely want to inspect visually, such as figures, PDFs, screenshots, images, important markdown, or generated files.",
178
180
  "For environment or system-maintenance work, prefer project-local dry-run plans/scripts unless the configured policy explicitly allows stronger actions.",
179
181
  "Docker language/toolchain installs should prefer /aginti-env or project files so they persist across runs; apt/apk changes are ephemeral unless the image is rebuilt.",
package/src/snapshot.js CHANGED
@@ -1,55 +1,71 @@
1
1
  export async function captureSnapshot(page, store, step) {
2
- const snapshot = await page.evaluate(() => {
3
- const isVisible = (el) => {
4
- const rect = el.getBoundingClientRect();
5
- const style = window.getComputedStyle(el);
6
- return rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden";
7
- };
8
-
9
- document.querySelectorAll("[data-agent-id]").forEach((el) => el.removeAttribute("data-agent-id"));
10
-
11
- const candidates = Array.from(
12
- document.querySelectorAll("a, button, input, textarea, select, [role='button'], [contenteditable='true']")
13
- );
14
-
15
- const elements = [];
16
-
17
- for (const el of candidates) {
18
- if (!isVisible(el)) continue;
19
- if (elements.length >= 50) break;
20
-
21
- const id = String(elements.length + 1);
22
- el.setAttribute("data-agent-id", id);
23
-
24
- const tag = el.tagName.toLowerCase();
25
- elements.push({
26
- id,
27
- tag,
28
- role: el.getAttribute("role") || "",
29
- text: (el.innerText || el.textContent || "").replace(/\s+/g, " ").trim().slice(0, 100),
30
- ariaLabel: (el.getAttribute("aria-label") || "").trim().slice(0, 100),
31
- placeholder: (el.getAttribute("placeholder") || "").trim().slice(0, 100),
32
- href: tag === "a" ? (el.getAttribute("href") || "").trim() : "",
33
- inputType: tag === "input" ? (el.getAttribute("type") || "text").trim() : "",
34
- autocomplete: (el.getAttribute("autocomplete") || "").trim(),
35
- });
36
- }
37
-
38
- return {
39
- title: document.title,
40
- url: window.location.href,
41
- pageText: (document.body?.innerText || "").replace(/\s+/g, " ").trim().slice(0, 2500),
42
- elements,
43
- };
44
- });
2
+ const snapshot = await page
3
+ .evaluate(() => {
4
+ const isVisible = (el) => {
5
+ const rect = el.getBoundingClientRect();
6
+ const style = window.getComputedStyle(el);
7
+ return rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden";
8
+ };
9
+
10
+ document.querySelectorAll("[data-agent-id]").forEach((el) => el.removeAttribute("data-agent-id"));
11
+
12
+ const candidates = Array.from(
13
+ document.querySelectorAll("a, button, input, textarea, select, [role='button'], [contenteditable='true']")
14
+ );
15
+
16
+ const elements = [];
17
+
18
+ for (const el of candidates) {
19
+ if (!isVisible(el)) continue;
20
+ if (elements.length >= 50) break;
21
+
22
+ const id = String(elements.length + 1);
23
+ el.setAttribute("data-agent-id", id);
24
+
25
+ const tag = el.tagName.toLowerCase();
26
+ elements.push({
27
+ id,
28
+ tag,
29
+ role: el.getAttribute("role") || "",
30
+ text: (el.innerText || el.textContent || "").replace(/\s+/g, " ").trim().slice(0, 100),
31
+ ariaLabel: (el.getAttribute("aria-label") || "").trim().slice(0, 100),
32
+ placeholder: (el.getAttribute("placeholder") || "").trim().slice(0, 100),
33
+ href: tag === "a" ? (el.getAttribute("href") || "").trim() : "",
34
+ inputType: tag === "input" ? (el.getAttribute("type") || "text").trim() : "",
35
+ autocomplete: (el.getAttribute("autocomplete") || "").trim(),
36
+ });
37
+ }
38
+
39
+ return {
40
+ title: document.title,
41
+ url: window.location.href,
42
+ pageText: (document.body?.innerText || "").replace(/\s+/g, " ").trim().slice(0, 2500),
43
+ elements,
44
+ };
45
+ })
46
+ .catch((error) => ({
47
+ title: "Browser page open",
48
+ url: page.url(),
49
+ pageText: `DOM snapshot unavailable: ${error instanceof Error ? error.message : String(error)}`.slice(0, 2500),
50
+ elements: [],
51
+ snapshotWarning: "dom-unavailable",
52
+ }));
45
53
 
46
54
  const screenshotPath = store.screenshotPath(step);
47
- await page.screenshot({ path: screenshotPath, fullPage: true });
48
- const snapshotPath = await store.saveSnapshot(step, snapshot);
55
+ let savedScreenshotPath = screenshotPath;
56
+ let screenshotWarning = "";
57
+ try {
58
+ await page.screenshot({ path: screenshotPath, fullPage: true });
59
+ } catch (error) {
60
+ savedScreenshotPath = "";
61
+ screenshotWarning = `Screenshot unavailable: ${error instanceof Error ? error.message : String(error)}`.slice(0, 500);
62
+ }
63
+ const snapshotPath = await store.saveSnapshot(step, { ...snapshot, screenshotWarning });
49
64
 
50
65
  return {
51
66
  ...snapshot,
52
- screenshotPath,
67
+ screenshotPath: savedScreenshotPath,
68
+ screenshotWarning,
53
69
  snapshotPath,
54
70
  };
55
71
  }
@@ -3,14 +3,14 @@ export const TASK_PROFILES = {
3
3
  id: "auto",
4
4
  label: "Auto",
5
5
  prompt:
6
- "Infer the task type from the user request. Prefer the smallest safe tool sequence, preserve workspace files, and summarize what changed.",
6
+ "Infer the task type from the user request. Prefer the smallest safe tool sequence that actually completes the work, preserve workspace files, run useful checks, and summarize what changed.",
7
7
  tools: ["browser", "shell", "files", "canvas"],
8
8
  },
9
9
  code: {
10
10
  id: "code",
11
11
  label: "Code writing",
12
12
  prompt:
13
- "Act like a coding agent: understand the request, edit workspace files, run useful safe checks, and report changed files and residual risks.",
13
+ "Act like a coding agent: understand the request, edit workspace files, run useful safe checks, iterate on failures, and report changed files and residual risks.",
14
14
  tools: ["files", "shell", "sandbox"],
15
15
  },
16
16
  writing: {
@@ -52,7 +52,7 @@ export const TASK_PROFILES = {
52
52
  id: "website",
53
53
  label: "Website testing",
54
54
  prompt:
55
- "For website-testing tasks, create or inspect the site, add a local check when useful, and use the configured sandbox/package policy for dependencies.",
55
+ "For website/app tasks, create or inspect real site files, preview with workspace preview tools, add local checks when useful, and use the configured sandbox/package policy for dependencies.",
56
56
  tools: ["files", "shell", "canvas", "sandbox"],
57
57
  },
58
58
  aaps: {
@@ -66,7 +66,7 @@ export const TASK_PROFILES = {
66
66
  id: "latex",
67
67
  label: "LaTeX",
68
68
  prompt:
69
- "For LaTeX/PDF tasks, create source and figures in a subfolder, compile when a TeX toolchain is available, and send the PDF through the canvas tunnel. In Docker, use /workspace for project outputs and the persistent Python/conda/tool cache under /aginti-env when setup is needed.",
69
+ "For LaTeX/PDF tasks, locate or create source and figures in a subfolder, compile when a TeX toolchain is available, run enough passes for references, and send the PDF through the canvas tunnel. In Docker, use /workspace for project outputs and the persistent Python/conda/tool cache under /aginti-env when setup is needed.",
70
70
  tools: ["files", "shell", "canvas", "sandbox"],
71
71
  },
72
72
  maintenance: {