atom-agent 1.1.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 +106 -0
- package/README.md +18 -8
- package/atom.example.json +11 -0
- package/dist/App.js +1637 -255
- package/dist/adapters.js +112 -21
- package/dist/agent/gates.js +14 -1
- package/dist/agent/goal-evaluator.js +69 -0
- package/dist/agent/loop-guard.js +11 -13
- package/dist/agent/loop.js +716 -132
- package/dist/agent/normalize.js +9 -2
- package/dist/cli.js +25 -3
- package/dist/compact.js +169 -17
- 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 +46 -8
- 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 +159 -41
- package/dist/session.js +23 -5
- package/dist/sessions.js +543 -0
- package/dist/system.js +89 -13
- 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/dir-cache.js +7 -0
- package/dist/tools/filesystem.js +3 -2
- 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 +247 -17
- package/dist/tools/ripgrep.js +256 -0
- package/dist/tools/search.js +119 -58
- package/dist/tools/shared.js +39 -0
- package/dist/tools/shell.js +7 -5
- package/dist/tools/web.js +6 -6
- package/dist/tools.js +45 -0
- package/dist/ui/diff-view.js +7 -2
- package/dist/ui/live-host.js +18 -0
- package/dist/ui/live-tail.js +9 -3
- package/dist/ui/markdown.js +26 -2
- package/dist/ui/palette.js +3 -1
- package/dist/ui/side-by-side.js +2 -2
- package/dist/ui/status-bar.js +80 -5
- package/dist/ui/status-host.js +22 -0
- package/dist/ui/stream-store.js +48 -0
- package/dist/ui/tool-inspector.js +7 -1
- package/dist/ui/transcript.js +92 -38
- package/dist/zen.js +370 -87
- 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
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Extension UI surface (ticket 10): the dependency-free render model behind
|
|
2
|
+
// ExtensionAPI.setStatusSegment/setWidget/notify/promptUser. No imports, no
|
|
3
|
+
// React — like extension-commands.ts and tools/intercept.ts — so the host,
|
|
4
|
+
// the App, and pure unit tests share it with no cycle: the host validates
|
|
5
|
+
// and stages here, the App renders from here, nobody imports the other.
|
|
6
|
+
//
|
|
7
|
+
// Shapes:
|
|
8
|
+
// - Status: one text slot per extension (upsert by owner name). The bar
|
|
9
|
+
// budget lives with the render (status-bar.tsx states the fixed-width
|
|
10
|
+
// rule); this module only validates shape so a bad segment fails
|
|
11
|
+
// activation loudly instead of corrupting the bar.
|
|
12
|
+
// - Widget: titled text blocks keyed by owner + id (default "main"),
|
|
13
|
+
// rendered by the App in the configured placement. "panel" is the only
|
|
14
|
+
// placement in v1 (the bordered panel above the input zone, beside the
|
|
15
|
+
// todo panel) — unknown placements throw fail-closed so a typo surfaces
|
|
16
|
+
// at activation instead of rendering nowhere.
|
|
17
|
+
// - Notices: transient fire-and-forget strings the App drains into the
|
|
18
|
+
// transcript (one `(owner) message` info line each). Sync and bounded
|
|
19
|
+
// by flow — notify() never blocks, headless or not; the host caps the
|
|
20
|
+
// staged queue drop-oldest (EXT_NOTICE_CAP in extensions.ts).
|
|
21
|
+
// - Dialogs: validated { question, options, allowCustom } specs mirroring
|
|
22
|
+
// the QuestionBox the App fulfills them with (same option caps the modal
|
|
23
|
+
// already assumes). The pending-promise mechanics live in extensions.ts
|
|
24
|
+
// (runtime-local, generation-bound); this module only validates shape.
|
|
25
|
+
export const EXT_STATUS_SEGMENT_MAX = 24;
|
|
26
|
+
export const EXT_STATUS_TOTAL_MAX = 40;
|
|
27
|
+
export const EXT_WIDGET_TITLE_MAX = 48;
|
|
28
|
+
export const EXT_WIDGET_TEXT_MAX = 500;
|
|
29
|
+
export const EXT_NOTIFY_MAX = 200;
|
|
30
|
+
export const EXT_DIALOG_QUESTION_MAX = 200;
|
|
31
|
+
export const EXT_DIALOG_OPTIONS_MAX = 8;
|
|
32
|
+
export const EXT_DIALOG_OPTION_MAX = 80;
|
|
33
|
+
// v1 widget placements. The App renders "panel" above the input zone;
|
|
34
|
+
// anything else is a loud validation error (never a silent nowhere).
|
|
35
|
+
export const EXT_WIDGET_PLACEMENTS = ["panel"];
|
|
36
|
+
const WIDGET_ID_RE = /^[a-z0-9][a-z0-9_-]{0,31}$/;
|
|
37
|
+
function isRecord(value) {
|
|
38
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39
|
+
}
|
|
40
|
+
function errorFor(owner, what) {
|
|
41
|
+
return `extension "${owner}" ${what}`;
|
|
42
|
+
}
|
|
43
|
+
/** Validate a status segment. Throws Error on any problem. Returns the trimmed text. */
|
|
44
|
+
export function validateStatusSegment(owner, text) {
|
|
45
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
46
|
+
throw new Error(errorFor(owner, "status segment needs a non-empty string"));
|
|
47
|
+
}
|
|
48
|
+
if (text.length > EXT_STATUS_SEGMENT_MAX * 4) {
|
|
49
|
+
throw new Error(errorFor(owner, `status segment is too long (${text.length} chars, max ${EXT_STATUS_SEGMENT_MAX * 4})`));
|
|
50
|
+
}
|
|
51
|
+
return text;
|
|
52
|
+
}
|
|
53
|
+
/** Validate a widget definition. Throws Error on any problem. */
|
|
54
|
+
export function validateWidgetDef(owner, def) {
|
|
55
|
+
if (!isRecord(def))
|
|
56
|
+
throw new Error(errorFor(owner, "widget definition must be an object"));
|
|
57
|
+
const id = def.id === undefined ? "main" : def.id;
|
|
58
|
+
if (typeof id !== "string" || !WIDGET_ID_RE.test(id)) {
|
|
59
|
+
throw new Error(errorFor(owner, `widget has an invalid id ${JSON.stringify(def.id)} (want 1-32 char a-z0-9_-, default "main")`));
|
|
60
|
+
}
|
|
61
|
+
if (!EXT_WIDGET_PLACEMENTS.includes(def.placement)) {
|
|
62
|
+
throw new Error(errorFor(owner, `widget "${id}" has an unknown placement ${JSON.stringify(def.placement)} (want one of: ${EXT_WIDGET_PLACEMENTS.join(", ")})`));
|
|
63
|
+
}
|
|
64
|
+
if (typeof def.title !== "string" || def.title.trim().length === 0) {
|
|
65
|
+
throw new Error(errorFor(owner, `widget "${id}" needs a non-empty title`));
|
|
66
|
+
}
|
|
67
|
+
if (def.title.length > EXT_WIDGET_TITLE_MAX) {
|
|
68
|
+
throw new Error(errorFor(owner, `widget "${id}" title is too long (${def.title.length} chars, max ${EXT_WIDGET_TITLE_MAX})`));
|
|
69
|
+
}
|
|
70
|
+
if (typeof def.text !== "string" || def.text.trim().length === 0) {
|
|
71
|
+
throw new Error(errorFor(owner, `widget "${id}" needs a non-empty text body`));
|
|
72
|
+
}
|
|
73
|
+
if (def.text.length > EXT_WIDGET_TEXT_MAX) {
|
|
74
|
+
throw new Error(errorFor(owner, `widget "${id}" text is too long (${def.text.length} chars, max ${EXT_WIDGET_TEXT_MAX})`));
|
|
75
|
+
}
|
|
76
|
+
return { id, placement: def.placement, title: def.title, text: def.text };
|
|
77
|
+
}
|
|
78
|
+
/** Validate a notification message. Throws Error on any problem. Returns the message. */
|
|
79
|
+
export function validateNotifyMessage(owner, message) {
|
|
80
|
+
if (typeof message !== "string" || message.trim().length === 0) {
|
|
81
|
+
throw new Error(errorFor(owner, "notification needs a non-empty string"));
|
|
82
|
+
}
|
|
83
|
+
if (message.length > EXT_NOTIFY_MAX * 4) {
|
|
84
|
+
throw new Error(errorFor(owner, `notification is too long (${message.length} chars, max ${EXT_NOTIFY_MAX * 4})`));
|
|
85
|
+
}
|
|
86
|
+
return message;
|
|
87
|
+
}
|
|
88
|
+
/** Validate a dialog spec. Throws Error on any problem. */
|
|
89
|
+
export function validateDialogDef(owner, def) {
|
|
90
|
+
if (!isRecord(def))
|
|
91
|
+
throw new Error(errorFor(owner, "dialog definition must be an object"));
|
|
92
|
+
if (typeof def.question !== "string" || def.question.trim().length === 0) {
|
|
93
|
+
throw new Error(errorFor(owner, "dialog needs a non-empty question"));
|
|
94
|
+
}
|
|
95
|
+
if (def.question.length > EXT_DIALOG_QUESTION_MAX) {
|
|
96
|
+
throw new Error(errorFor(owner, `dialog question is too long (${def.question.length} chars, max ${EXT_DIALOG_QUESTION_MAX})`));
|
|
97
|
+
}
|
|
98
|
+
const options = def.options === undefined ? [] : def.options;
|
|
99
|
+
if (!Array.isArray(options)) {
|
|
100
|
+
throw new Error(errorFor(owner, "dialog options must be an array of strings"));
|
|
101
|
+
}
|
|
102
|
+
if (options.length > EXT_DIALOG_OPTIONS_MAX) {
|
|
103
|
+
throw new Error(errorFor(owner, `dialog has too many options (${options.length}, max ${EXT_DIALOG_OPTIONS_MAX})`));
|
|
104
|
+
}
|
|
105
|
+
for (const o of options) {
|
|
106
|
+
if (typeof o !== "string" || o.trim().length === 0) {
|
|
107
|
+
throw new Error(errorFor(owner, "dialog options must be non-empty strings"));
|
|
108
|
+
}
|
|
109
|
+
if (o.length > EXT_DIALOG_OPTION_MAX) {
|
|
110
|
+
throw new Error(errorFor(owner, `dialog option is too long (${o.length} chars, max ${EXT_DIALOG_OPTION_MAX})`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!options.length && def.allowCustom !== true) {
|
|
114
|
+
throw new Error(errorFor(owner, "dialog needs options or allowCustom: true (nothing to answer with)"));
|
|
115
|
+
}
|
|
116
|
+
return { question: def.question, options: [...options], allowCustom: def.allowCustom === true };
|
|
117
|
+
}
|
|
118
|
+
// Truncate text to n chars max for tight widths (`…/tail` keeps the
|
|
119
|
+
// meaningful end, the status-bar convention). n < 4 yields "" (the caller
|
|
120
|
+
// drops the segment instead of rendering a stub).
|
|
121
|
+
export function truncateSegment(s, n) {
|
|
122
|
+
if (s.length <= n)
|
|
123
|
+
return s;
|
|
124
|
+
if (n < 4)
|
|
125
|
+
return "";
|
|
126
|
+
return `…/${s.slice(-(n - 3))}`;
|
|
127
|
+
}
|
|
128
|
+
// Pure status-bar text for extension segments (unit-tested; the bar itself
|
|
129
|
+
// only decides fit-or-drop against the terminal width, never the content).
|
|
130
|
+
// Each segment truncates to EXT_STATUS_SEGMENT_MAX, joined with " · "; the
|
|
131
|
+
// total caps at EXT_STATUS_TOTAL_MAX with trailing segments dropped whole
|
|
132
|
+
// (never a mid-segment cut past the per-segment truncation). Null when
|
|
133
|
+
// nothing renderable remains.
|
|
134
|
+
export function formatExtensionStatusText(segments) {
|
|
135
|
+
const parts = [];
|
|
136
|
+
let len = 0;
|
|
137
|
+
for (const raw of segments) {
|
|
138
|
+
if (typeof raw !== "string")
|
|
139
|
+
continue;
|
|
140
|
+
const text = raw.trim();
|
|
141
|
+
if (!text)
|
|
142
|
+
continue;
|
|
143
|
+
const seg = truncateSegment(text, EXT_STATUS_SEGMENT_MAX);
|
|
144
|
+
if (!seg)
|
|
145
|
+
continue;
|
|
146
|
+
const add = (parts.length > 0 ? 3 : 0) + seg.length;
|
|
147
|
+
if (len + add > EXT_STATUS_TOTAL_MAX)
|
|
148
|
+
continue;
|
|
149
|
+
parts.push(seg);
|
|
150
|
+
len += add;
|
|
151
|
+
}
|
|
152
|
+
return parts.length > 0 ? parts.join(" · ") : null;
|
|
153
|
+
}
|