@lazyingart/agintiflow 0.6.0 → 0.8.0

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.
@@ -146,7 +146,7 @@ export async function createPlan(client, config, state) {
146
146
  state.startUrl ? `Suggested start URL: ${state.startUrl}` : "",
147
147
  config.allowedDomains.length > 0 ? `Allowed domains: ${config.allowedDomains.join(", ")}` : "",
148
148
  config.allowShellTool
149
- ? `Shell tool is enabled in ${config.commandCwd}. In Docker, this path is mounted as /workspace. Use relative paths or /workspace paths, not absolute host temp paths. Sandbox mode: ${config.sandboxMode}. Package install policy: ${config.packageInstallPolicy}. For npm/pip/conda/venv setup, explain the need and wait for approval unless policy is allow.`
149
+ ? `Shell tool is enabled in ${config.commandCwd}. In Docker, this path is mounted as /workspace with persistent /aginti-env and /aginti-cache mounts. Use relative paths or /workspace paths, not absolute host temp paths. Sandbox mode: ${config.sandboxMode}. Package install policy: ${config.packageInstallPolicy}. For npm/pip/conda/venv setup, explain the need and wait for approval unless policy is allow.`
150
150
  : "",
151
151
  config.allowFileTools
152
152
  ? `Workspace file tools are enabled in ${config.commandCwd}: list_files, read_file, search_files, write_file, apply_patch. Keep all paths workspace-relative, for example plot_fx.svg or docs/report.tex, and avoid secrets.`
@@ -159,6 +159,8 @@ export async function createPlan(client, config, state) {
159
159
  "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.",
160
160
  "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.",
161
161
  "For environment or system-maintenance work, prefer project-local dry-run plans/scripts unless the configured policy explicitly allows stronger actions.",
162
+ "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.",
163
+ "If the run is close to the max-step limit, finish with the best complete artifact and honest limitations instead of starting a new approach.",
162
164
  "Plan for a complete result, not endless exploration; finish once the request is satisfied and checks have passed or been honestly skipped.",
163
165
  "Return a numbered plan only.",
164
166
  ]
@@ -10,6 +10,7 @@ export class SessionStore {
10
10
  this.statePath = path.join(this.sessionDir, "state.json");
11
11
  this.planPath = path.join(this.sessionDir, "plan.md");
12
12
  this.eventsPath = path.join(this.sessionDir, "events.jsonl");
13
+ this.inboxPath = path.join(this.sessionDir, "inbox.jsonl");
13
14
  this.storageStatePath = path.join(this.sessionDir, "storage-state.json");
14
15
  }
15
16
 
@@ -59,6 +60,34 @@ export class SessionStore {
59
60
  }
60
61
  }
61
62
 
63
+ async appendInbox(content, metadata = {}) {
64
+ await this.ensure();
65
+ const text = String(content || "").trim();
66
+ if (!text) return;
67
+ const line = JSON.stringify({
68
+ timestamp: new Date().toISOString(),
69
+ content: text,
70
+ ...metadata,
71
+ });
72
+ await fs.appendFile(this.inboxPath, `${line}\n`, "utf8");
73
+ }
74
+
75
+ async drainInbox() {
76
+ await this.ensure();
77
+ let raw = "";
78
+ try {
79
+ raw = await fs.readFile(this.inboxPath, "utf8");
80
+ } catch {
81
+ return [];
82
+ }
83
+ await fs.writeFile(this.inboxPath, "", "utf8");
84
+ return raw
85
+ .split("\n")
86
+ .map((line) => line.trim())
87
+ .filter(Boolean)
88
+ .map((line) => JSON.parse(line));
89
+ }
90
+
62
91
  async saveSnapshot(step, snapshot) {
63
92
  await this.ensure();
64
93
  const filename = `step-${String(step).padStart(3, "0")}.snapshot.json`;
@@ -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.",
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.",
70
70
  tools: ["files", "shell", "canvas", "sandbox"],
71
71
  },
72
72
  maintenance: {
package/src/web-db.js CHANGED
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { DatabaseSync } from "node:sqlite";
4
4
  import { getModelPresets } from "./model-routing.js";
5
5
 
6
- const PREFERENCES_SCHEMA_VERSION = 3;
6
+ const PREFERENCES_SCHEMA_VERSION = 4;
7
7
 
8
8
  function defaultPreferences(baseDir) {
9
9
  const presets = getModelPresets();
@@ -13,7 +13,7 @@ function defaultPreferences(baseDir) {
13
13
  provider: "deepseek",
14
14
  model: presets.fast.model,
15
15
  headless: true,
16
- maxSteps: 15,
16
+ maxSteps: 24,
17
17
  startUrl: "",
18
18
  allowedDomains: "",
19
19
  commandCwd: path.resolve(baseDir),
@@ -23,7 +23,7 @@ function defaultPreferences(baseDir) {
23
23
  preferredWrapper: "codex",
24
24
  wrapperTimeoutMs: 120000,
25
25
  sandboxMode: "docker-workspace",
26
- packageInstallPolicy: "prompt",
26
+ packageInstallPolicy: "allow",
27
27
  useDockerSandbox: true,
28
28
  dockerSandboxImage: "agintiflow-sandbox:latest",
29
29
  allowPasswords: false,
@@ -89,8 +89,11 @@ export class WebDatabase {
89
89
  preferences.sandboxMode = "docker-workspace";
90
90
  preferences.useDockerSandbox = true;
91
91
  }
92
- if (parsed.packageInstallPolicy === "block") {
93
- preferences.packageInstallPolicy = "prompt";
92
+ if ((parsed.packageInstallPolicy || "") !== "allow") {
93
+ preferences.packageInstallPolicy = "allow";
94
+ }
95
+ if (!Number.isFinite(Number(parsed.maxSteps)) || Number(parsed.maxSteps) < 24) {
96
+ preferences.maxSteps = 24;
94
97
  }
95
98
  this.savePreferences(preferences);
96
99
  }
package/web.js CHANGED
@@ -130,7 +130,7 @@ function normalizePreferencePayload(body = {}, current = db.getPreferences()) {
130
130
  ? providerDefaults.model
131
131
  : current.model || modelPresets.fast.model || providerDefaults.model,
132
132
  headless: typeof body.headless === "boolean" ? body.headless : Boolean(current.headless),
133
- maxSteps: Number.isFinite(parsedMaxSteps) && parsedMaxSteps > 0 ? parsedMaxSteps : Number(current.maxSteps) || 15,
133
+ maxSteps: Number.isFinite(parsedMaxSteps) && parsedMaxSteps > 0 ? parsedMaxSteps : Number(current.maxSteps) || 24,
134
134
  startUrl: typeof body.startUrl === "string" ? body.startUrl.trim() : current.startUrl || "",
135
135
  allowedDomains:
136
136
  typeof body.allowedDomains === "string" ? body.allowedDomains.trim() : current.allowedDomains || "",
@@ -156,7 +156,7 @@ function normalizePreferencePayload(body = {}, current = db.getPreferences()) {
156
156
  : Boolean(current.useDockerSandbox),
157
157
  sandboxMode,
158
158
  packageInstallPolicy: normalizePackageInstallPolicy(
159
- body.packageInstallPolicy || current.packageInstallPolicy || "prompt"
159
+ body.packageInstallPolicy || current.packageInstallPolicy || (sandboxMode === "host" ? "prompt" : "allow")
160
160
  ),
161
161
  dockerSandboxImage:
162
162
  typeof body.dockerSandboxImage === "string" && body.dockerSandboxImage.trim()
@@ -519,7 +519,7 @@ app.get("/api/config", async (_req, res) => {
519
519
  deepseek: publicProviderDefault("deepseek"),
520
520
  mock: publicProviderDefault("mock"),
521
521
  headless: true,
522
- maxSteps: 15,
522
+ maxSteps: 24,
523
523
  },
524
524
  taskProfiles: listTaskProfiles(),
525
525
  routing: {
@@ -768,6 +768,28 @@ app.post("/api/sessions/:sessionId/messages", async (req, res) => {
768
768
  return;
769
769
  }
770
770
 
771
+ if (!isSafeSessionId(sessionId)) {
772
+ res.status(400).json({ error: "Invalid session id." });
773
+ return;
774
+ }
775
+
776
+ const active = runs.get(sessionId);
777
+ if (active?.status === "running") {
778
+ const store = sessionStore(sessionId);
779
+ await store.appendInbox(content, { source: "web" });
780
+ await store.appendEvent("conversation.queued_input", { prompt: content, source: "web" }).catch(() => {});
781
+ active.logs.push({
782
+ at: new Date().toISOString(),
783
+ kind: "event",
784
+ message: "conversation.queued_input",
785
+ data: { prompt: content, source: "web" },
786
+ });
787
+ active.updatedAt = new Date().toISOString();
788
+ db.upsertSession(active);
789
+ res.json({ sessionId, queued: true });
790
+ return;
791
+ }
792
+
771
793
  try {
772
794
  await ensureNotRunning(sessionId);
773
795
  } catch (error) {