@lazyingart/agintiflow 0.8.3 → 0.8.4

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
@@ -54,7 +54,7 @@ aginti
54
54
  aginti chat
55
55
  ```
56
56
 
57
- Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is now 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 <session-id>` to continue work. Ctrl+C exits cleanly and prints a resume command when a session exists.
57
+ Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is now 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. Ctrl+C exits cleanly, prints the active resume command when a session exists, and lists recent sessions when no new session has started yet.
58
58
 
59
59
  Launch the local web UI from an installed package:
60
60
 
@@ -80,6 +80,8 @@ aginti capabilities
80
80
  aginti doctor --capabilities
81
81
  aginti sessions list
82
82
  aginti sessions show <session-id>
83
+ aginti resume
84
+ aginti resume latest
83
85
  aginti resume <session-id> "continue with a short follow-up"
84
86
  aginti queue <session-id> "extra instruction for the running agent"
85
87
  aginti --profile code "write a small Python CLI app with tests"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
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",
@@ -10,19 +10,19 @@ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-")
10
10
  const binPath = path.join(repoRoot, "bin/aginti-cli.js");
11
11
 
12
12
  function runChat(inputText) {
13
+ return runCli(["chat", "--provider", "mock", "--routing", "manual", "--profile", "code"], inputText);
14
+ }
15
+
16
+ function runCli(args, inputText) {
13
17
  return new Promise((resolve, reject) => {
14
- const child = spawn(
15
- process.execPath,
16
- [binPath, "chat", "--provider", "mock", "--routing", "manual", "--profile", "code"],
17
- {
18
- cwd: tempRoot,
19
- stdio: ["pipe", "pipe", "pipe"],
20
- env: {
21
- ...process.env,
22
- AGINTIFLOW_RUNTIME_DIR: "",
23
- },
24
- }
25
- );
18
+ const child = spawn(process.execPath, [binPath, ...args], {
19
+ cwd: tempRoot,
20
+ stdio: ["pipe", "pipe", "pipe"],
21
+ env: {
22
+ ...process.env,
23
+ AGINTIFLOW_RUNTIME_DIR: "",
24
+ },
25
+ });
26
26
 
27
27
  let stdout = "";
28
28
  let stderr = "";
@@ -60,12 +60,21 @@ try {
60
60
  if (!result.stdout.includes("Interactive agent chat")) {
61
61
  throw new Error("interactive chat did not print its banner");
62
62
  }
63
+ if (!result.stdout.includes("status=running workingOn=") || !result.stdout.includes("status=idle session=")) {
64
+ throw new Error("interactive chat did not print simple run status updates");
65
+ }
66
+
67
+ const latest = await runCli(["resume"], "/exit\n");
68
+ if (!latest.stdout.includes("session=") || !latest.stdout.includes("Interactive agent chat")) {
69
+ throw new Error("bare aginti resume did not open the latest session interactively");
70
+ }
71
+
63
72
  console.log(
64
73
  JSON.stringify(
65
74
  {
66
75
  ok: true,
67
76
  projectRoot: tempRoot,
68
- checks: ["interactive-chat", "mock-file-write"],
77
+ checks: ["interactive-chat", "mock-file-write", "run-status", "resume-latest"],
69
78
  },
70
79
  null,
71
80
  2
package/src/cli.js CHANGED
@@ -219,7 +219,7 @@ export function parseArgs(argv) {
219
219
 
220
220
  function printUsage() {
221
221
  console.log(
222
- 'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti resume <session-id> ["prompt"] OR aginti queue <session-id> "message" OR aginti [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-destructive] [--allow-file-tools|--no-file-tools] [--allow-wrappers --wrapper codex] [--sandbox-status|--sandbox-preflight] "your task"'
222
+ 'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-destructive] [--allow-file-tools|--no-file-tools] [--allow-wrappers --wrapper codex] [--sandbox-status|--sandbox-preflight] "your task"'
223
223
  );
224
224
  }
225
225
 
@@ -356,6 +356,13 @@ async function handleSessionsCommand(argv) {
356
356
  process.exit(1);
357
357
  }
358
358
 
359
+ async function resolveResumeSessionId(sessionId) {
360
+ if (sessionId && sessionId !== "latest") return sessionId;
361
+ const sessions = await listProjectSessions(process.cwd(), 1);
362
+ if (sessions[0]?.sessionId) return sessions[0].sessionId;
363
+ throw new Error("No project-local sessions found. Run `aginti sessions list` to check this folder.");
364
+ }
365
+
359
366
  async function handleQueueCommand(argv) {
360
367
  const sessionId = argv[0] || "";
361
368
  const content = argv.slice(1).join(" ").trim();
@@ -456,23 +463,21 @@ export async function main(argv = process.argv.slice(2)) {
456
463
  }
457
464
 
458
465
  if (argv[0] === "resume") {
459
- const sessionId = argv[1] || "";
466
+ let sessionId = argv[1] || "";
460
467
  const prompt = argv.slice(2).join(" ").trim();
461
- if (!sessionId) {
462
- console.error('Usage: aginti resume <session-id> ["new prompt"]');
468
+ try {
469
+ sessionId = await resolveResumeSessionId(sessionId);
470
+ } catch (error) {
471
+ console.error(error instanceof Error ? error.message : String(error));
463
472
  process.exit(1);
464
473
  }
465
- if (!prompt && process.stdin.isTTY) {
474
+ if (!prompt) {
466
475
  await startInteractiveCli(agentDefaults({ ...parseArgs([]), resume: sessionId }), {
467
476
  packageDir,
468
477
  packageVersion: packageJson.version,
469
478
  });
470
479
  return;
471
480
  }
472
- if (!prompt) {
473
- console.error('Usage: aginti resume <session-id> "new prompt"');
474
- process.exit(1);
475
- }
476
481
  const config = loadConfig(agentDefaults({ ...parseArgs([prompt]), resume: sessionId, goal: prompt }), { packageDir });
477
482
  await runAgent(config);
478
483
  return;
@@ -35,6 +35,8 @@ function printStatus(state) {
35
35
  console.log(`project=${process.cwd()}`);
36
36
  console.log(`cwd=${state.commandCwd || process.cwd()}`);
37
37
  console.log(`session=${state.sessionId || "new"}`);
38
+ console.log(`status=${state.status || "idle"}${state.activeGoal ? ` workingOn=${state.activeGoal}` : ""}`);
39
+ if (state.lastEvent) console.log(`last=${state.lastEvent}`);
38
40
  console.log(`provider=${state.provider || "auto"} routing=${state.routingMode} model=${state.model || "auto"}`);
39
41
  console.log(`profile=${state.taskProfile} maxSteps=${state.maxSteps}`);
40
42
  console.log(
@@ -49,7 +51,22 @@ function isAbortError(error) {
49
51
  return error?.code === "ABORT_ERR" || error?.name === "AbortError";
50
52
  }
51
53
 
52
- function printResumeHint(state) {
54
+ function formatSessionLine(session) {
55
+ const goal = session.goal ? ` ${session.goal.slice(0, 72)}` : "";
56
+ return `${session.sessionId} ${session.provider || "unknown"}/${session.model || "unknown"} ${session.updatedAt || ""}${goal}`.trim();
57
+ }
58
+
59
+ async function latestSession() {
60
+ const sessions = await listProjectSessions(process.cwd(), 1);
61
+ return sessions[0] || null;
62
+ }
63
+
64
+ function printStatusEvent(state, label, details = "") {
65
+ state.lastEvent = details ? `${label}: ${details}` : label;
66
+ console.log(`status=${state.status || "running"} ${state.lastEvent}`);
67
+ }
68
+
69
+ async function printResumeHint(state) {
53
70
  const sessionId = state.sessionId || "";
54
71
  console.log("");
55
72
  if (sessionId) {
@@ -58,7 +75,15 @@ function printResumeHint(state) {
58
75
  console.log(`One-shot: aginti resume ${sessionId} "continue"`);
59
76
  } else {
60
77
  console.log("Interrupted. No active session yet.");
61
- console.log("Restart: aginti");
78
+ const sessions = await listProjectSessions(process.cwd(), 5).catch(() => []);
79
+ if (sessions.length > 0) {
80
+ console.log("Recent sessions:");
81
+ for (const session of sessions) console.log(` ${formatSessionLine(session)}`);
82
+ console.log(`Resume latest: aginti resume ${sessions[0].sessionId}`);
83
+ console.log("List all: aginti sessions list");
84
+ } else {
85
+ console.log("Restart: aginti");
86
+ }
62
87
  }
63
88
  }
64
89
 
@@ -101,8 +126,15 @@ async function handleCommand(line, state, packageDir) {
101
126
  return true;
102
127
  }
103
128
  if (command === "resume") {
104
- if (!value) console.log("Usage: /resume <session-id>");
105
- else {
129
+ if (!value || value === "latest") {
130
+ const latest = await latestSession();
131
+ if (!latest) {
132
+ console.log("No project-local sessions found. Use /new or type a request to start one.");
133
+ } else {
134
+ state.sessionId = latest.sessionId;
135
+ console.log(`Resuming latest ${formatSessionLine(latest)}`);
136
+ }
137
+ } else {
106
138
  state.sessionId = value;
107
139
  console.log(`Resuming ${state.sessionId}`);
108
140
  }
@@ -216,8 +248,39 @@ async function runPrompt(prompt, state, packageDir) {
216
248
  { packageDir, baseDir: process.cwd() }
217
249
  );
218
250
 
219
- const result = await runAgent(config);
251
+ state.sessionId = config.resume || config.sessionId || state.sessionId;
252
+ state.status = "running";
253
+ state.activeGoal = prompt.replace(/\s+/g, " ").slice(0, 120);
254
+ state.lastEvent = "";
255
+ console.log(`session=${state.sessionId}`);
256
+ console.log(`status=running workingOn=${state.activeGoal}`);
257
+
258
+ const result = await runAgent({
259
+ ...config,
260
+ onEvent: (type, data = {}) => {
261
+ if (type === "plan.created") {
262
+ printStatusEvent(state, "planned");
263
+ } else if (type === "tool.started") {
264
+ printStatusEvent(state, "tool", data.toolName || "unknown");
265
+ } else if (type === "tool.completed") {
266
+ printStatusEvent(state, "tool_done", data.toolName || "unknown");
267
+ } else if (type === "tool.blocked") {
268
+ printStatusEvent(state, "tool_blocked", data.toolName || data.reason || "unknown");
269
+ } else if (type === "conversation.queued_input_applied") {
270
+ printStatusEvent(state, "queued_input_applied");
271
+ } else if (type === "session.finished") {
272
+ printStatusEvent(state, "finished");
273
+ } else if (type === "session.stopped") {
274
+ printStatusEvent(state, "stopped", data.reason || "");
275
+ } else if (type === "model.responded") {
276
+ printStatusEvent(state, "model_responded", data.content ? data.content.slice(0, 80).replace(/\s+/g, " ") : "");
277
+ }
278
+ },
279
+ });
220
280
  state.sessionId = result.sessionId || state.sessionId;
281
+ state.status = result.stopped ? "stopped" : "idle";
282
+ state.activeGoal = "";
283
+ console.log(`status=${state.status} session=${state.sessionId}`);
221
284
  }
222
285
 
223
286
  export async function startInteractiveCli(args = {}, { packageDir, packageVersion } = {}) {
@@ -237,7 +300,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
237
300
  } catch (error) {
238
301
  if (error?.code === "ERR_USE_AFTER_CLOSE") break;
239
302
  if (isAbortError(error)) {
240
- printResumeHint(state);
303
+ await printResumeHint(state);
241
304
  break;
242
305
  }
243
306
  throw error;
@@ -254,7 +317,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
254
317
  await runPrompt(line, state, packageDir);
255
318
  } catch (error) {
256
319
  if (isAbortError(error)) {
257
- printResumeHint(state);
320
+ await printResumeHint(state);
258
321
  break;
259
322
  }
260
323
  console.error(`error: ${error.message}`);