agent-from-zero 0.34.2 → 0.35.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 +0 -96
- package/dist/account-status.js +172 -0
- package/dist/agent-progress.js +39 -0
- package/dist/agent.js +159 -34
- package/dist/auto-budget.js +15 -0
- package/dist/auto-context.js +125 -0
- package/dist/auto-debug.js +87 -0
- package/dist/auto-providers.js +154 -0
- package/dist/auto-review.js +113 -0
- package/dist/auto.js +295 -0
- package/dist/background.js +2 -1
- package/dist/cli.js +83 -0
- package/dist/clipboard.js +88 -0
- package/dist/completion.js +65 -0
- package/dist/config.js +8 -0
- package/dist/context.js +9 -3
- package/dist/cost.js +4 -0
- package/dist/effort.js +81 -0
- package/dist/follow-up.js +22 -0
- package/dist/form.js +8 -6
- package/dist/images.js +43 -0
- package/dist/ink/activity.js +62 -0
- package/dist/ink/agents.js +24 -0
- package/dist/ink/app.js +384 -171
- package/dist/ink/chat.js +12 -1
- package/dist/ink/commands.js +16 -3
- package/dist/ink/completion.js +43 -0
- package/dist/ink/setup.js +9 -4
- package/dist/ink/sink.js +16 -6
- package/dist/ink/viewport.js +15 -0
- package/dist/loop.js +251 -106
- package/dist/mcp.js +1 -0
- package/dist/menu.js +50 -8
- package/dist/output.js +1 -1
- package/dist/pasted-text.js +32 -0
- package/dist/permissions.js +467 -12
- package/dist/print.js +16 -0
- package/dist/prompt.js +7 -4
- package/dist/status.js +80 -0
- package/dist/team.js +9 -3
- package/dist/tools.js +13 -1
- package/dist/tui.js +31 -15
- package/dist/ui.js +4 -6
- package/package.json +3 -3
package/dist/ink/chat.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import { AutoReviewBudget } from "../auto-budget.js";
|
|
1
2
|
import { runLoop } from "../loop.js"; // the REAL agent loop — tools, permissions, retries, the lot
|
|
2
3
|
import { CONFIG } from "../config.js"; // model + sub-agent tier
|
|
4
|
+
import { userContent } from "../images.js";
|
|
3
5
|
// Drive one real turn through the loop. The loop mutates `messages` in place
|
|
4
6
|
// (appends the assistant reply + any tool messages), exactly as the readline
|
|
5
7
|
// REPL relies on — so we only push the user turn here and let the loop do the
|
|
6
8
|
// rest. All screen output flows through hooks.output (the Ink sink).
|
|
7
9
|
export function makeRunTurn(client, messages) {
|
|
10
|
+
let reviewBudget = new AutoReviewBudget();
|
|
8
11
|
return async (input, hooks) => {
|
|
9
|
-
|
|
12
|
+
if (input !== null)
|
|
13
|
+
reviewBudget = new AutoReviewBudget();
|
|
14
|
+
if (input !== null)
|
|
15
|
+
messages.push({ role: "user", content: userContent(input, hooks.images ?? []) });
|
|
10
16
|
return runLoop(messages, {
|
|
11
17
|
client,
|
|
12
18
|
model: CONFIG.model,
|
|
@@ -15,6 +21,11 @@ export function makeRunTurn(client, messages) {
|
|
|
15
21
|
confirm: hooks.confirm,
|
|
16
22
|
askUser: hooks.askUser,
|
|
17
23
|
judge: hooks.judge,
|
|
24
|
+
autoMode: hooks.autoMode,
|
|
25
|
+
reviewBudget,
|
|
26
|
+
followUps: hooks.followUps,
|
|
27
|
+
onFollowUp: hooks.onFollowUp,
|
|
28
|
+
canPrompt: !!process.stdin.isTTY,
|
|
18
29
|
subAgentModel: CONFIG.subAgentModel,
|
|
19
30
|
output: hooks.output,
|
|
20
31
|
});
|
package/dist/ink/commands.js
CHANGED
|
@@ -23,9 +23,13 @@ export const SESSION_HELP = `commands:
|
|
|
23
23
|
/model switch model for THIS session: "/model" to pick from a list, "/model <name>" to set; "/model save <name>" to make it your default
|
|
24
24
|
/stats event counts for this session (local telemetry — nothing leaves this machine)
|
|
25
25
|
/memory show the durable facts the agent remembers about this project
|
|
26
|
+
/status session usage and vendor account balance/cost
|
|
26
27
|
/cost tokens, cache hit rate and estimated spend this session (local)
|
|
27
28
|
/mcp list configured MCP servers + status; select one to authenticate / reconnect / disable
|
|
28
29
|
/plan toggle plan mode — research-only; the agent presents a plan you approve before any change
|
|
30
|
+
/effort [level] list/select the current model’s supported reasoning effort
|
|
31
|
+
/auto toggle auto mode — risky or uncertain actions still require approval
|
|
32
|
+
/auto debug [on|off|status] inspect permission reviewer requests and replies
|
|
29
33
|
/todos show the agent's current task plan (it maintains one with todo_write on multi-step work)
|
|
30
34
|
/bg list background tasks this session (run_bash_background) and their status
|
|
31
35
|
/team list the agent team (spawn_teammate): each teammate's role, status, and pending inbox
|
|
@@ -38,9 +42,18 @@ export const SESSION_HELP = `commands:
|
|
|
38
42
|
/skill <name> run a skill yourself (works even for user-only skills)
|
|
39
43
|
exit leave (Ctrl+C does the same)
|
|
40
44
|
|
|
41
|
-
keys (
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
keys (during or after a turn):
|
|
46
|
+
/ show command suggestions below the input (while idle)
|
|
47
|
+
↑↓ / Tab choose / complete a suggestion; Enter selects; Esc closes
|
|
48
|
+
Enter while working, queue a follow-up for the next tool boundary
|
|
49
|
+
Ctrl+Enter interrupt and send now (Esc also sends already-queued messages)
|
|
50
|
+
Ctrl+V paste an image as [Image #1], or paste text
|
|
51
|
+
Tab / ← focus the agent list (← at the input boundary)
|
|
52
|
+
↑ / ↓ select an agent; Enter views it; Esc returns to main
|
|
53
|
+
Ctrl+R toggle thinking details
|
|
54
|
+
Ctrl+T toggle tool arguments and results
|
|
55
|
+
↑ / ↓ page through open details (PgUp/PgDn also work)
|
|
56
|
+
Esc close details first; otherwise interrupt and process queued input`;
|
|
44
57
|
// Colored status word for an MCP server (shared by the Ink + readline /mcp UIs).
|
|
45
58
|
export function mcpStatusText(info) {
|
|
46
59
|
switch (info.status) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { slashCompletions } from "../completion.js";
|
|
5
|
+
// Keep the full list in state, but render only a window around the selection.
|
|
6
|
+
// Long lists must never grow Ink's redraw region beyond the terminal height.
|
|
7
|
+
export function useSlashCompletion(input, enabled, context, maxRows) {
|
|
8
|
+
const [selection, setSelection] = useState({ input: "", index: 0 });
|
|
9
|
+
const [dismissed, setDismissed] = useState(null);
|
|
10
|
+
useEffect(() => { setDismissed(null); }, [input]);
|
|
11
|
+
const menu = enabled && dismissed !== input ? slashCompletions(input, context) : null;
|
|
12
|
+
const items = menu?.items ?? [];
|
|
13
|
+
const selected = Math.min(selection.input === input ? selection.index : 0, Math.max(0, items.length - 1));
|
|
14
|
+
const count = Math.max(1, Math.min(8, maxRows));
|
|
15
|
+
const start = Math.max(0, Math.min(selected - Math.floor(count / 2), items.length - count));
|
|
16
|
+
return {
|
|
17
|
+
visible: menu !== null,
|
|
18
|
+
handleKey: (key, replace) => {
|
|
19
|
+
if (!menu)
|
|
20
|
+
return { handled: false };
|
|
21
|
+
if (key.escape) {
|
|
22
|
+
setDismissed(input);
|
|
23
|
+
return { handled: true };
|
|
24
|
+
}
|
|
25
|
+
if (!items.length)
|
|
26
|
+
return { handled: false };
|
|
27
|
+
if (key.upArrow || key.downArrow) {
|
|
28
|
+
setSelection({ input, index: (selected + (key.upArrow ? -1 : 1) + items.length) % items.length });
|
|
29
|
+
return { handled: true };
|
|
30
|
+
}
|
|
31
|
+
if (key.tab || (key.return && !key.ctrl && !key.meta)) {
|
|
32
|
+
const item = items[selected];
|
|
33
|
+
if (key.tab || item.children)
|
|
34
|
+
replace(item.value + (item.children ? " " : ""));
|
|
35
|
+
else
|
|
36
|
+
return { handled: true, submit: item.value };
|
|
37
|
+
return { handled: true };
|
|
38
|
+
}
|
|
39
|
+
return { handled: false };
|
|
40
|
+
},
|
|
41
|
+
view: menu && (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [items.slice(start, start + count).map((item, i) => (_jsxs(Text, { color: start + i === selected ? "cyan" : undefined, bold: start + i === selected, wrap: "truncate-end", children: [start + i === selected ? "› " : " ", item.value, " ", _jsx(Text, { dimColor: true, children: item.description.replace(/\s+/g, " ") })] }, item.value))), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: items.length ? `↑↓ choose · Tab complete · Enter select · Esc close · ${selected + 1}/${items.length}` : menu.hint || "No matching commands" })] })),
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/ink/setup.js
CHANGED
|
@@ -2,7 +2,7 @@ import OpenAI from "openai";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { CONFIG, requireApiKey } from "../config.js";
|
|
5
|
-
import { buildSystemMessage } from "../prompt.js";
|
|
5
|
+
import { buildSystemMessage, readProjectInstructions } from "../prompt.js";
|
|
6
6
|
import { estimateHistoryTokens } from "../context.js";
|
|
7
7
|
import { initCostMeter, DEFAULT_PRICING } from "../cost.js";
|
|
8
8
|
import { gitBranch } from "../tui.js";
|
|
@@ -11,6 +11,7 @@ import { initTelemetry, emit } from "../telemetry.js";
|
|
|
11
11
|
import { runHooks } from "../hooks.js";
|
|
12
12
|
import { connectMcpServers } from "../mcp.js";
|
|
13
13
|
import { Judge } from "../judge.js";
|
|
14
|
+
import { AutoMode } from "../auto.js";
|
|
14
15
|
import { registerExternalTool } from "../tools.js";
|
|
15
16
|
import { rememberTool, readMemory } from "../memory.js";
|
|
16
17
|
import { loadSkills, buildSkillTool } from "../skills.js";
|
|
@@ -30,7 +31,8 @@ const pkg = createRequire(import.meta.url)("../../package.json");
|
|
|
30
31
|
export async function buildInkSession(opts = {}) {
|
|
31
32
|
requireApiKey();
|
|
32
33
|
const client = new OpenAI({ baseURL: CONFIG.baseURL, apiKey: CONFIG.apiKey, maxRetries: 0 });
|
|
33
|
-
const
|
|
34
|
+
const projectInstructions = readProjectInstructions();
|
|
35
|
+
const systemMessage = buildSystemMessage(projectInstructions); // stable prefix = cache hits every request
|
|
34
36
|
const messages = [{ role: "system", content: systemMessage }];
|
|
35
37
|
const notices = [];
|
|
36
38
|
let sessionId = newSessionId();
|
|
@@ -63,9 +65,11 @@ export async function buildInkSession(opts = {}) {
|
|
|
63
65
|
if (cronCount)
|
|
64
66
|
notices.push(`(cron: ${cronCount} durable job${cronCount !== 1 ? "s" : ""} loaded)`);
|
|
65
67
|
startCronScheduler();
|
|
66
|
-
const judge = CONFIG.judge.enabled ? new Judge(client, CONFIG.judge.model || CONFIG.model) : undefined;
|
|
68
|
+
const judge = CONFIG.judge.enabled && !CONFIG.bypassPermissions ? new Judge(client, CONFIG.judge.model || CONFIG.model) : undefined;
|
|
69
|
+
const autoMode = new AutoMode(client, { projectInstructions });
|
|
70
|
+
notices.push(...autoMode.startupNotices());
|
|
67
71
|
if (judge)
|
|
68
|
-
notices.push(`(permission judge on —
|
|
72
|
+
notices.push(`(permission judge on — ${autoMode.backend})`);
|
|
69
73
|
// The remember tool flows through the same permission gate as any tool.
|
|
70
74
|
registerExternalTool(rememberTool);
|
|
71
75
|
const memCount = readMemory().length;
|
|
@@ -104,6 +108,7 @@ export async function buildInkSession(opts = {}) {
|
|
|
104
108
|
costMeter,
|
|
105
109
|
skills,
|
|
106
110
|
judge,
|
|
111
|
+
autoMode,
|
|
107
112
|
model: CONFIG.model,
|
|
108
113
|
dir,
|
|
109
114
|
branch,
|
package/dist/ink/sink.js
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
// Build a LoopOutput backed by Ink state. Pure glue: every method maps a loop
|
|
2
2
|
// event to a state change.
|
|
3
3
|
export function makeInkSink(api) {
|
|
4
|
+
// Concurrent reads can finish out of order. Removing one handle must not
|
|
5
|
+
// clear another call's animation, and stopped handles cannot revive it.
|
|
6
|
+
const statuses = new Map();
|
|
7
|
+
const refresh = () => api.setStatus([...statuses.values()].at(-1) ?? null);
|
|
4
8
|
return {
|
|
9
|
+
streamingStatus: true,
|
|
5
10
|
// A spinner becomes the single "status" line below the conversation. A fresh
|
|
6
11
|
// handle per model call (the loop makes one per round); `active` mirrors
|
|
7
12
|
// ora's isSpinning so the loop's `spinner?.spinning` checks behave the same.
|
|
8
13
|
spinner(text) {
|
|
9
|
-
|
|
14
|
+
const id = Symbol();
|
|
15
|
+
statuses.set(id, text);
|
|
16
|
+
refresh();
|
|
10
17
|
let active = true;
|
|
11
18
|
return {
|
|
12
19
|
set: (t) => {
|
|
13
|
-
if (active)
|
|
14
|
-
|
|
20
|
+
if (active) {
|
|
21
|
+
statuses.set(id, t);
|
|
22
|
+
refresh();
|
|
23
|
+
}
|
|
15
24
|
},
|
|
16
25
|
stop: () => {
|
|
17
26
|
if (active) {
|
|
18
27
|
active = false;
|
|
19
|
-
|
|
28
|
+
statuses.delete(id);
|
|
29
|
+
refresh();
|
|
20
30
|
}
|
|
21
31
|
},
|
|
22
32
|
get spinning() {
|
|
@@ -24,8 +34,8 @@ export function makeInkSink(api) {
|
|
|
24
34
|
},
|
|
25
35
|
};
|
|
26
36
|
},
|
|
27
|
-
// The
|
|
28
|
-
reasoning: (
|
|
37
|
+
// The trace is already stored for Ctrl+R; no per-round scrollback noise.
|
|
38
|
+
reasoning: () => { },
|
|
29
39
|
// The streamed answer accumulates in the dynamic region as raw text (live,
|
|
30
40
|
// "it's typing"), then commits as ONE markdown-rendered item when it ends —
|
|
31
41
|
// partial markdown mid-stream renders badly, so we format only once it's whole.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useStdout } from "ink";
|
|
3
|
+
// Size the dynamic viewport against the SAME output stream Ink renders to.
|
|
4
|
+
// Resize must trigger React too: Ink's Yoga relayout alone cannot repaginate text.
|
|
5
|
+
export function useTerminalSize() {
|
|
6
|
+
const { stdout } = useStdout();
|
|
7
|
+
const read = () => ({ rows: stdout.rows || 24, columns: stdout.columns || 80 });
|
|
8
|
+
const [size, setSize] = useState(read);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const resize = () => setSize(read());
|
|
11
|
+
stdout.on("resize", resize);
|
|
12
|
+
return () => { stdout.off("resize", resize); };
|
|
13
|
+
}, [stdout]);
|
|
14
|
+
return size;
|
|
15
|
+
}
|