@lazyingart/agintiflow 0.8.2 → 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 +3 -1
- package/package.json +1 -1
- package/public/styles.css +16 -2
- package/scripts/smoke-cli-chat.js +22 -13
- package/src/cli.js +20 -4
- package/src/interactive-cli.js +91 -3
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.
|
|
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
package/public/styles.css
CHANGED
|
@@ -536,21 +536,35 @@ button.danger {
|
|
|
536
536
|
overflow: auto;
|
|
537
537
|
display: grid;
|
|
538
538
|
gap: 12px;
|
|
539
|
-
padding:
|
|
539
|
+
padding: 14px;
|
|
540
|
+
border: 1px solid rgba(15, 118, 110, 0.16);
|
|
541
|
+
border-radius: 18px;
|
|
542
|
+
background:
|
|
543
|
+
radial-gradient(circle at 14% 8%, rgba(20, 184, 166, 0.18), transparent 24%),
|
|
544
|
+
radial-gradient(circle at 86% 0%, rgba(245, 158, 11, 0.16), transparent 22%),
|
|
545
|
+
linear-gradient(180deg, rgba(255, 253, 250, 0.86), rgba(241, 245, 249, 0.72));
|
|
546
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.68);
|
|
540
547
|
}
|
|
541
548
|
|
|
542
549
|
.chat-item {
|
|
550
|
+
width: fit-content;
|
|
551
|
+
max-width: min(780px, 88%);
|
|
543
552
|
border: 1px solid var(--line);
|
|
544
|
-
border-radius:
|
|
553
|
+
border-radius: 18px;
|
|
545
554
|
padding: 12px 14px;
|
|
555
|
+
box-shadow: 0 12px 28px rgba(62, 49, 26, 0.08);
|
|
546
556
|
}
|
|
547
557
|
|
|
548
558
|
.chat-item.user {
|
|
559
|
+
justify-self: end;
|
|
549
560
|
background: var(--user);
|
|
561
|
+
border-bottom-right-radius: 6px;
|
|
550
562
|
}
|
|
551
563
|
|
|
552
564
|
.chat-item.assistant {
|
|
565
|
+
justify-self: start;
|
|
553
566
|
background: var(--assistant);
|
|
567
|
+
border-bottom-left-radius: 6px;
|
|
554
568
|
}
|
|
555
569
|
|
|
556
570
|
.chat-meta {
|
|
@@ -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
|
-
|
|
16
|
-
[
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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 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,12 +463,21 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
456
463
|
}
|
|
457
464
|
|
|
458
465
|
if (argv[0] === "resume") {
|
|
459
|
-
|
|
466
|
+
let sessionId = argv[1] || "";
|
|
460
467
|
const prompt = argv.slice(2).join(" ").trim();
|
|
461
|
-
|
|
462
|
-
|
|
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
|
}
|
|
474
|
+
if (!prompt) {
|
|
475
|
+
await startInteractiveCli(agentDefaults({ ...parseArgs([]), resume: sessionId }), {
|
|
476
|
+
packageDir,
|
|
477
|
+
packageVersion: packageJson.version,
|
|
478
|
+
});
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
465
481
|
const config = loadConfig(agentDefaults({ ...parseArgs([prompt]), resume: sessionId, goal: prompt }), { packageDir });
|
|
466
482
|
await runAgent(config);
|
|
467
483
|
return;
|
package/src/interactive-cli.js
CHANGED
|
@@ -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(
|
|
@@ -45,6 +47,46 @@ function printStatus(state) {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
function isAbortError(error) {
|
|
51
|
+
return error?.code === "ABORT_ERR" || error?.name === "AbortError";
|
|
52
|
+
}
|
|
53
|
+
|
|
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) {
|
|
70
|
+
const sessionId = state.sessionId || "";
|
|
71
|
+
console.log("");
|
|
72
|
+
if (sessionId) {
|
|
73
|
+
console.log("Interrupted. Session saved.");
|
|
74
|
+
console.log(`Resume: aginti resume ${sessionId}`);
|
|
75
|
+
console.log(`One-shot: aginti resume ${sessionId} "continue"`);
|
|
76
|
+
} else {
|
|
77
|
+
console.log("Interrupted. No active session yet.");
|
|
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
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
48
90
|
function createState(args = {}) {
|
|
49
91
|
return {
|
|
50
92
|
provider: args.provider || "",
|
|
@@ -84,8 +126,15 @@ async function handleCommand(line, state, packageDir) {
|
|
|
84
126
|
return true;
|
|
85
127
|
}
|
|
86
128
|
if (command === "resume") {
|
|
87
|
-
if (!value
|
|
88
|
-
|
|
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 {
|
|
89
138
|
state.sessionId = value;
|
|
90
139
|
console.log(`Resuming ${state.sessionId}`);
|
|
91
140
|
}
|
|
@@ -199,8 +248,39 @@ async function runPrompt(prompt, state, packageDir) {
|
|
|
199
248
|
{ packageDir, baseDir: process.cwd() }
|
|
200
249
|
);
|
|
201
250
|
|
|
202
|
-
|
|
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
|
+
});
|
|
203
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}`);
|
|
204
284
|
}
|
|
205
285
|
|
|
206
286
|
export async function startInteractiveCli(args = {}, { packageDir, packageVersion } = {}) {
|
|
@@ -219,6 +299,10 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
219
299
|
answer = await rl.question("\naginti> ");
|
|
220
300
|
} catch (error) {
|
|
221
301
|
if (error?.code === "ERR_USE_AFTER_CLOSE") break;
|
|
302
|
+
if (isAbortError(error)) {
|
|
303
|
+
await printResumeHint(state);
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
222
306
|
throw error;
|
|
223
307
|
}
|
|
224
308
|
const line = answer.trim();
|
|
@@ -232,6 +316,10 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
232
316
|
try {
|
|
233
317
|
await runPrompt(line, state, packageDir);
|
|
234
318
|
} catch (error) {
|
|
319
|
+
if (isAbortError(error)) {
|
|
320
|
+
await printResumeHint(state);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
235
323
|
console.error(`error: ${error.message}`);
|
|
236
324
|
}
|
|
237
325
|
}
|