atom-agent 1.2.0 → 1.3.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.
- package/CHANGELOG.md +75 -0
- package/README.md +13 -4
- package/atom.example.json +11 -0
- package/dist/App.js +923 -200
- package/dist/adapters.js +82 -13
- package/dist/agent/goal-evaluator.js +69 -0
- package/dist/agent/loop.js +517 -76
- package/dist/cli.js +11 -3
- package/dist/compact.js +41 -15
- package/dist/config.js +43 -7
- package/dist/context-manager.js +16 -198
- package/dist/context-windows.js +4 -2
- package/dist/env-block.js +5 -5
- package/dist/extension-commands.js +196 -0
- package/dist/extension-ui.js +153 -0
- package/dist/extensions.js +1571 -0
- package/dist/goal.js +583 -0
- package/dist/project-trust.js +96 -0
- package/dist/providers.js +6 -6
- package/dist/scheduler.js +74 -36
- package/dist/session.js +23 -5
- package/dist/sessions.js +25 -6
- package/dist/telemetry-dashboard.js +28 -0
- package/dist/telemetry.js +39 -0
- package/dist/tools/compaction-hooks.js +165 -0
- package/dist/tools/custom.js +189 -0
- package/dist/tools/intercept.js +145 -0
- package/dist/tools/overrides.js +105 -0
- package/dist/tools/provider-hooks.js +224 -0
- package/dist/tools/registry.js +246 -17
- package/dist/tools.js +44 -0
- package/dist/ui/palette.js +1 -1
- package/dist/ui/status-bar.js +80 -5
- package/dist/zen.js +305 -75
- package/documentation/architecture.md +114 -0
- package/documentation/cli.md +82 -0
- package/documentation/compaction.md +50 -0
- package/documentation/configuration.md +111 -0
- package/documentation/development.md +62 -0
- package/documentation/extensions.md +160 -0
- package/documentation/getting-started.md +63 -0
- package/documentation/goals.md +41 -0
- package/documentation/index.md +41 -0
- package/documentation/observability.md +70 -0
- package/documentation/permissions.md +66 -0
- package/documentation/providers.md +78 -0
- package/documentation/sessions.md +92 -0
- package/documentation/skills.md +57 -0
- package/documentation/tools.md +94 -0
- package/documentation/troubleshooting.md +54 -0
- package/examples/extensions/01-audit-gate.js +24 -0
- package/examples/extensions/02-notes-tool.js +32 -0
- package/examples/extensions/03-custom-command.js +32 -0
- package/package.json +6 -2
package/dist/ui/status-bar.js
CHANGED
|
@@ -12,6 +12,47 @@ import React from "react";
|
|
|
12
12
|
import { Box, Text } from "ink";
|
|
13
13
|
import { formatTokenSegment } from "../context-windows.js";
|
|
14
14
|
import { theme } from "./theme.js";
|
|
15
|
+
// Default objective budget for the goal segment: compact enough to share the
|
|
16
|
+
// line with the pinned model/token/mode segments at 100 columns.
|
|
17
|
+
export const GOAL_STATUS_OBJECTIVE_CHARS = 32;
|
|
18
|
+
// Truncate an objective to n chars max (`…` tail keeps the start, which
|
|
19
|
+
// carries the verb). n < 4 yields "" (the caller drops the segment instead).
|
|
20
|
+
export function truncateGoalObjective(objective, max = GOAL_STATUS_OBJECTIVE_CHARS) {
|
|
21
|
+
const text = typeof objective === "string" ? objective : "";
|
|
22
|
+
if (text.length <= max)
|
|
23
|
+
return text;
|
|
24
|
+
if (max < 4)
|
|
25
|
+
return "";
|
|
26
|
+
return `${text.slice(0, max - 1)}…`;
|
|
27
|
+
}
|
|
28
|
+
// Full goal segment at the default budget, or null when no goal is live.
|
|
29
|
+
// Paused reads distinct from active (`[paused]` vs `[active]`).
|
|
30
|
+
export function formatGoalSegment(goal, max = GOAL_STATUS_OBJECTIVE_CHARS) {
|
|
31
|
+
if (!goal || typeof goal.objective !== "string" || goal.objective.length === 0)
|
|
32
|
+
return null;
|
|
33
|
+
const state = goal.active === true ? "active" : "paused";
|
|
34
|
+
return `goal: ${truncateGoalObjective(goal.objective, max)} [${state}]`;
|
|
35
|
+
}
|
|
36
|
+
// Fit the goal segment into `room` chars (the width left after every other
|
|
37
|
+
// segment): full text when it fits, a shorter truncation when it almost
|
|
38
|
+
// fits, null (drop the segment) when even a stub would displace the line.
|
|
39
|
+
// Never throws; never returns "".
|
|
40
|
+
export function fitGoalSegment(goal, room) {
|
|
41
|
+
if (!goal || typeof goal.objective !== "string" || goal.objective.length === 0)
|
|
42
|
+
return null;
|
|
43
|
+
if (typeof room !== "number" || !Number.isFinite(room) || room <= 0)
|
|
44
|
+
return null;
|
|
45
|
+
const state = goal.active === true ? "active" : "paused";
|
|
46
|
+
const full = `goal: ${goal.objective} [${state}]`;
|
|
47
|
+
if (full.length <= room)
|
|
48
|
+
return full;
|
|
49
|
+
// Room for at least 4 objective chars plus the fixed framing, else drop.
|
|
50
|
+
const overhead = `goal: [${state}]`.length + 1;
|
|
51
|
+
const allow = Math.floor(room - overhead);
|
|
52
|
+
if (allow < 4)
|
|
53
|
+
return null;
|
|
54
|
+
return `goal: ${truncateGoalObjective(goal.objective, allow)} [${state}]`;
|
|
55
|
+
}
|
|
15
56
|
// ~/… collapse + tail-cut: informative, never a full scroll of nesting.
|
|
16
57
|
// Further shrinking for tight widths goes through shrinkTo below (the bar
|
|
17
58
|
// measures first and only renders what fits).
|
|
@@ -34,9 +75,13 @@ export function shrinkTo(s, n) {
|
|
|
34
75
|
// render (same-props parent churn — token paints, keystrokes, unrelated
|
|
35
76
|
// ticks — must skip it; only changed props repaint).
|
|
36
77
|
export const statusBarRenderProbe = { count: 0 };
|
|
37
|
-
export const StatusBar = React.memo(function StatusBar({ provider, model, usageTotals, contextLoad, reasoningDisplay, mode, trustAll, busy, activity, phaseLabel, elapsedSecs, stalled, approvalPending, cwd, branch, columns = 100, }) {
|
|
78
|
+
export const StatusBar = React.memo(function StatusBar({ provider, model, usageTotals, contextLoad, reasoningDisplay, mode, trustAll, busy, activity, phaseLabel, elapsedSecs, stalled, approvalPending, cwd, branch, columns = 100, extensionStatus, goal, }) {
|
|
38
79
|
statusBarRenderProbe.count += 1;
|
|
39
80
|
const bar = theme.symbol.bar;
|
|
81
|
+
// Extension guest slot (ticket 10): pre-budgeted text renders only when
|
|
82
|
+
// the full line still fits — the fixed-width contract above. The `+ 3`
|
|
83
|
+
// is the ` ${bar} ` separator the segment carries with it.
|
|
84
|
+
const hasExt = typeof extensionStatus === "string" && extensionStatus.length > 0;
|
|
40
85
|
if (!busy) {
|
|
41
86
|
const token = formatTokenSegment(usageTotals, model, contextLoad);
|
|
42
87
|
const trust = trustAll && mode !== "plan" ? "+trust" : "";
|
|
@@ -46,7 +91,8 @@ export const StatusBar = React.memo(function StatusBar({ provider, model, usageT
|
|
|
46
91
|
// order: branch → cwd tail → the whole segment.
|
|
47
92
|
const tail = `reasoning: ${reasoningDisplay} ${bar} mode: ${mode}${trust}`;
|
|
48
93
|
const baseLen = `${provider}/${model} ${bar} ${token} ${bar} ${bar} ${tail}`.length;
|
|
49
|
-
const
|
|
94
|
+
const showExt = hasExt && baseLen + extensionStatus.length + 3 + 2 <= columns;
|
|
95
|
+
const avail = columns - baseLen - (showExt ? extensionStatus.length + 3 : 0);
|
|
50
96
|
let loc = null;
|
|
51
97
|
if (cwd) {
|
|
52
98
|
const branchPart = branch ? ` : ${branch}` : "";
|
|
@@ -61,15 +107,44 @@ export const StatusBar = React.memo(function StatusBar({ provider, model, usageT
|
|
|
61
107
|
loc = shrunk ? shrunk : null;
|
|
62
108
|
}
|
|
63
109
|
}
|
|
64
|
-
|
|
110
|
+
// Goal segment (ticket 09): lowest-priority builtin — it takes only the
|
|
111
|
+
// width left after every other segment and drops whole rather than push
|
|
112
|
+
// the line past `columns`. Hidden entirely with no goal.
|
|
113
|
+
const lineSoFar = baseLen + (showExt ? extensionStatus.length + 3 : 0) + (loc ? loc.length + 3 : 0);
|
|
114
|
+
const goalSeg = fitGoalSegment(goal ?? null, columns - lineSoFar - 2);
|
|
115
|
+
return (_jsx(Box, { marginTop: theme.spacing.statusMarginTop, children: _jsxs(Text, { dimColor: true, children: [provider, "/", model, " ", bar, " ", token, showExt ? (_jsxs(_Fragment, { children: [" ", bar, " ", extensionStatus] })) : null, loc ? (_jsxs(_Fragment, { children: [" ", bar, " ", loc] })) : null, " ", bar, " reasoning: ", reasoningDisplay, " ", bar, " mode: ", mode, trust ? "+trust" : null, goalSeg ? (_jsxs(_Fragment, { children: [" ", bar, " ", goalSeg] })) : null] }) }));
|
|
65
116
|
}
|
|
66
117
|
// Busy layout prioritizes activity + clock + interrupt hint; the mode
|
|
67
118
|
// stays pinned (it used to vanish while working), and the reasoning
|
|
68
119
|
// effort stays visible (it used to vanish while working). The activity text
|
|
69
120
|
// shrinks to fit so `esc stops` never wraps away.
|
|
70
121
|
const busyTrust = trustAll && mode !== "plan" ? "+trust" : "";
|
|
71
|
-
const
|
|
122
|
+
const busyToken = formatTokenSegment(usageTotals, model, contextLoad);
|
|
123
|
+
// Goal segment (ticket 09): a guest in the fixed part — capped at 48
|
|
124
|
+
// chars and rendered only when the FULL activity text still fits beside
|
|
125
|
+
// it. Otherwise the goal drops whole and every existing segment renders
|
|
126
|
+
// exactly as with no goal (the goal never displaces, same precedent as
|
|
127
|
+
// the extension guest above). The clock, token, mode, and esc-hint
|
|
128
|
+
// segments never move for it either way.
|
|
129
|
+
const busyGoalSeg = fitGoalSegment(goal ?? null, 48);
|
|
130
|
+
const busyGoalCandidate = busyGoalSeg ? ` ${bar} ${busyGoalSeg}` : "";
|
|
131
|
+
const activityFull = activity ?? phaseLabel;
|
|
132
|
+
const busyCore = ` ${bar} ${elapsedSecs}s ${bar} ${busyToken} ${bar} reasoning: ${reasoningDisplay} ${bar} mode: ${mode}${busyTrust}`;
|
|
133
|
+
const busyTail = ` ${bar} esc stops`;
|
|
134
|
+
const busyExtCandidate = hasExt && `${busyCore}${busyGoalCandidate}${busyTail}`.length + extensionStatus.length + 3 + 2 <= columns
|
|
135
|
+
? ` ${bar} ${extensionStatus}`
|
|
136
|
+
: "";
|
|
137
|
+
const withGoalFixed = `${busyCore}${busyExtCandidate}${busyGoalCandidate}${busyTail}`;
|
|
138
|
+
// Room check against the unfitted activity text: when it no longer fits
|
|
139
|
+
// whole with the goal aboard, the goal yields (drop whole, recompute).
|
|
140
|
+
const busyGoalPart = busyGoalCandidate !== "" &&
|
|
141
|
+
withGoalFixed.length + activityFull.length + 2 <= columns
|
|
142
|
+
? busyGoalCandidate
|
|
143
|
+
: "";
|
|
144
|
+
const busyNoExt = `${busyCore}${busyGoalPart}${busyTail}`;
|
|
145
|
+
const showBusyExt = hasExt && busyNoExt.length + extensionStatus.length + 3 + 2 <= columns;
|
|
146
|
+
const busyFixed = ` ${bar} ${elapsedSecs}s${showBusyExt ? ` ${bar} ${extensionStatus}` : ""} ${bar} ${busyToken} ${bar} reasoning: ${reasoningDisplay} ${bar} mode: ${mode}${busyTrust}${busyGoalPart} ${bar} esc stops`;
|
|
72
147
|
const busyAvail = columns - busyFixed.length - 2;
|
|
73
|
-
const activityText = shrinkTo(
|
|
148
|
+
const activityText = shrinkTo(activityFull, Math.max(0, busyAvail));
|
|
74
149
|
return (_jsx(Box, { marginTop: theme.spacing.statusMarginTop, children: _jsxs(Text, { dimColor: true, children: [_jsxs(Text, { color: theme.color.activity, children: [theme.symbol.workTool, " ", activityText] }), busyFixed, stalled && !approvalPending ? ` ${bar} waiting${theme.symbol.ellipsis}` : null, approvalPending ? (_jsxs(Text, { color: theme.color.warning, children: [" ", bar, " waiting approval"] })) : null] }) }));
|
|
75
150
|
});
|