@lazyingart/agintiflow 0.14.0 → 0.14.1

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
@@ -67,7 +67,7 @@ aginti
67
67
  aginti chat
68
68
  ```
69
69
 
70
- 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, `/instructions` to inspect `AGINTI.md`, `/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. `Ctrl+J` inserts a new line in the colored input panel, Enter sends, arrow keys move through wrapped multiline input, and `Ctrl+A`/`Ctrl+E` jump to the current line start/end. During an active run, Enter sends the draft as an ASAP pipe message (`→`) and Tab queues it for after the run (`↳`); ASAP messages are consumed first, Alt+Up edits the last piped message, and Shift+Left edits the last after-finish queued message. The input panel always shows the current `cwd` footer and a single live status row, so long goals and tool updates are compacted instead of flooding the transcript. Assistant responses start on a fresh line after the `aginti>` header with a colored response gutter and render common Markdown, including headings, inline code, bold text, lists, quotes, code fences, tables, and red/green patch diff lines. Resuming a session prints the full saved chat history with wrapped messages before the prompt. Esc or Ctrl+C stops the active run cleanly and prints the resume command.
70
+ 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, `/instructions` to inspect `AGINTI.md`, `/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. `Ctrl+J` inserts a new line in the colored input panel, Enter sends, arrow keys move through wrapped multiline input, and `Ctrl+A`/`Ctrl+E` jump to the current line start/end. During an active run, Enter sends the draft as an ASAP pipe message (`→`) and Tab queues it for after the run (`↳`); ASAP messages are consumed before after-finish queued prompts, Alt+Up edits the last piped message, and Shift+Left edits the last after-finish queued message. Idle Esc is ignored so it does not disturb the input panel; during a run, Esc waits when `→` messages are pending and otherwise stops the run cleanly. Ctrl+C always stops and prints the resume command. The input panel always shows the current `cwd` footer and a single live status row, so long goals and tool updates are compacted instead of flooding the transcript. Assistant responses start on a fresh line after the `aginti>` header with a colored response gutter and render common Markdown, including headings, inline code, bold text, lists, quotes, code fences, tables, and red/green patch diff lines. Resuming a session prints the full saved chat history with wrapped messages before the prompt.
71
71
 
72
72
  `aginti init` creates `AGINTI.md` at the project root. This is the editable project-instruction file for both CLI and web runs, similar to `AGENTS.md` or project memory in other agents. Keep durable preferences, commands, and constraints there, but never secrets. You can edit it manually or ask in chat, for example: `update AGINTI.md to remember that this project uses pytest and npm run check`.
73
73
 
@@ -9,11 +9,11 @@ AgInTiFlow keeps CLI and web runs equivalent by using the project folder as the
9
9
 
10
10
  When a run is active, the web chat and `aginti queue <session-id> "..."` append messages to the inbox instead of trying to mutate the running process directly. The web API exposes `GET /api/sessions/:id/inbox`, `POST /api/sessions/:id/inbox`, `PATCH /api/sessions/:id/inbox/:itemId`, and `DELETE /api/sessions/:id/inbox/:itemId` so browser users can inspect, edit, or remove pending pipe messages before the runner consumes them. The runner drains the inbox at safe boundaries: before each model step and after tool execution. This mirrors the event-queue style used by mature agent UIs while keeping the backend decoupled from any specific frontend.
11
11
 
12
- The interactive CLI keeps the input panel visible while a run is working. Enter sends the current draft as an ASAP pipe message and displays it as `→`; the runner drains those messages before normal inbox items. Tab stores the draft as an after-finish queue item and displays it as `↳`; those prompts run only after the current run completes. Alt+Up moves the last pending `→` message back into the editor, and Shift+Left moves the last pending `↳` message back into the editor. The current command cwd is rendered below the input panel in both idle and running states.
12
+ The interactive CLI keeps the input panel visible while a run is working. Enter sends the current draft as an ASAP pipe message and displays it as `→`; the runner drains those messages before normal inbox items and before after-finish queued prompts. Tab stores the draft as an after-finish queue item and displays it as `↳`; those prompts run only after the current run completes. Alt+Up moves the last pending `→` message back into the editor, and Shift+Left moves the last pending `↳` message back into the editor. Idle Esc is ignored so it does not redraw the prompt into the transcript. During a run, Esc waits when `→` pipe messages are still pending and stops the run only when no ASAP pipe message is pending; Ctrl+C always stops. The current command cwd is rendered below the input panel in both idle and running states.
13
13
 
14
14
  The web UI uses a related but browser-appropriate pattern. Enter sends and Shift+Enter adds a newline. `Pipe to run` writes an ASAP inbox item shared with CLI. `Queue after finish` keeps a browser-local next prompt and starts it after the current web-owned run finishes. Both lanes render in a pending panel with Edit and Remove buttons instead of terminal-only keybindings.
15
15
 
16
- Runs can be stopped without corrupting session state. The CLI listens for Esc or Ctrl+C during an active run, and the web UI exposes a Stop button plus Esc. Both paths send an abort signal, persist `session.stopped`, and leave the session resumable through `aginti resume <session-id>`.
16
+ Runs can be stopped without corrupting session state. The CLI listens for Ctrl+C during an active run, and Esc stops only when no ASAP pipe message is waiting to be applied. The web UI exposes a Stop button plus Esc. Stop paths send an abort signal, persist `session.stopped`, and leave the session resumable through `aginti resume <session-id>`.
17
17
 
18
18
  Default execution is Docker workspace mode with package installs approved inside the sandbox. The project is mounted at `/workspace`; persistent agent toolchain folders are mounted at `/aginti-home`, `/aginti-cache`, and `/aginti-env` from `~/.agintiflow/docker/`. Python, conda, and other language-level environments should be installed under `/aginti-env` so they survive across runs. Apt/apk package changes are ephemeral unless the Docker image is rebuilt.
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
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",
@@ -4,7 +4,7 @@ import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { buildPromptLayout, stripMarkdown } from "../src/interactive-cli.js";
7
+ import { buildPromptLayout, classifyEscapeAction, stripMarkdown } from "../src/interactive-cli.js";
8
8
 
9
9
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
10
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-"));
@@ -112,6 +112,15 @@ try {
112
112
  if (!queuedText.includes("run running · tool: apply_patch") || !queuedText.includes("→ apply this") || !queuedText.includes("↳ run this") || !queuedText.includes("cwd /tmp/aginti-project")) {
113
113
  throw new Error("terminal prompt layout did not render live input queue and cwd footer");
114
114
  }
115
+ if (classifyEscapeAction({ active: false }) !== "noop") {
116
+ throw new Error("idle Esc should not redraw or clear the prompt");
117
+ }
118
+ if (classifyEscapeAction({ active: true, pendingAsap: [{ content: "apply now" }] }) !== "wait-for-asap") {
119
+ throw new Error("active Esc should wait when ASAP pipe messages are pending");
120
+ }
121
+ if (classifyEscapeAction({ active: true, pendingAsap: [] }) !== "abort") {
122
+ throw new Error("active Esc should abort when no ASAP pipe messages are pending");
123
+ }
115
124
 
116
125
  await runCli(["init"], "");
117
126
  const instructions = await fs.readFile(path.join(tempRoot, "AGINTI.md"), "utf8");
@@ -163,7 +172,7 @@ try {
163
172
  {
164
173
  ok: true,
165
174
  projectRoot: tempRoot,
166
- checks: ["markdown-render", "markdown-table-no-duplicate", "patch-diff-render", "prompt-layout", "live-input-status-layout", "agent-response-gutter", "aginti-md", "instructions-command", "skills-command", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-full"],
175
+ checks: ["markdown-render", "markdown-table-no-duplicate", "patch-diff-render", "prompt-layout", "escape-policy", "live-input-status-layout", "agent-response-gutter", "aginti-md", "instructions-command", "skills-command", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-full"],
167
176
  },
168
177
  null,
169
178
  2
@@ -501,7 +501,7 @@ function printHelp() {
501
501
  "Type / then Tab to autocomplete commands.",
502
502
  "While a run is active, Enter pipes a message into the current run (→), Tab queues it after finish (↳).",
503
503
  "Alt+Up edits the last piped message; Shift+Left edits the last queued message.",
504
- "Esc or Ctrl+C stops gracefully and prints a resume command.",
504
+ "Esc is ignored while idle. During a run, Esc waits for pending → pipe messages or stops if none; Ctrl+C always stops.",
505
505
  ].join("\n")
506
506
  );
507
507
  }
@@ -793,6 +793,11 @@ function createAbortError(message = "Aborted with Ctrl+C") {
793
793
  return error;
794
794
  }
795
795
 
796
+ export function classifyEscapeAction({ active = false, pendingAsap = [] } = {}) {
797
+ if (!active) return "noop";
798
+ return Array.isArray(pendingAsap) && pendingAsap.length > 0 ? "wait-for-asap" : "abort";
799
+ }
800
+
796
801
  function readTtyPrompt(options = {}) {
797
802
  return new Promise((resolve, reject) => {
798
803
  emitKeypressEvents(input);
@@ -966,10 +971,7 @@ function readTtyPrompt(options = {}) {
966
971
  return;
967
972
  }
968
973
  if (key.name === "escape") {
969
- buffer = "";
970
- cursor = 0;
971
- preferredColumn = null;
972
- redraw();
974
+ if (classifyEscapeAction({ active: false }) === "noop") return;
973
975
  return;
974
976
  }
975
977
  if (key.ctrl || key.meta) return;
@@ -1192,6 +1194,13 @@ class LiveRunInput {
1192
1194
  return;
1193
1195
  }
1194
1196
  if (key.name === "escape") {
1197
+ const action = classifyEscapeAction({ active: true, pendingAsap: this.pendingAsap });
1198
+ if (action === "wait-for-asap") {
1199
+ const count = this.pendingAsap.length;
1200
+ this.setStatus(`running · waiting to apply ${count} asap pipe message${count === 1 ? "" : "s"}`);
1201
+ this.redraw();
1202
+ return;
1203
+ }
1195
1204
  this.controller.abort(new Error("Interrupted by escape."));
1196
1205
  return;
1197
1206
  }