@agentuity/coder 1.0.37
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 +57 -0
- package/dist/chain-preview.d.ts +55 -0
- package/dist/chain-preview.d.ts.map +1 -0
- package/dist/chain-preview.js +472 -0
- package/dist/chain-preview.js.map +1 -0
- package/dist/client.d.ts +43 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +402 -0
- package/dist/client.js.map +1 -0
- package/dist/commands.d.ts +22 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +99 -0
- package/dist/commands.js.map +1 -0
- package/dist/footer.d.ts +34 -0
- package/dist/footer.d.ts.map +1 -0
- package/dist/footer.js +249 -0
- package/dist/footer.js.map +1 -0
- package/dist/handlers.d.ts +24 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +83 -0
- package/dist/handlers.js.map +1 -0
- package/dist/hub-overlay.d.ts +107 -0
- package/dist/hub-overlay.d.ts.map +1 -0
- package/dist/hub-overlay.js +1794 -0
- package/dist/hub-overlay.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1585 -0
- package/dist/index.js.map +1 -0
- package/dist/output-viewer.d.ts +49 -0
- package/dist/output-viewer.d.ts.map +1 -0
- package/dist/output-viewer.js +389 -0
- package/dist/output-viewer.js.map +1 -0
- package/dist/overlay.d.ts +40 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +225 -0
- package/dist/overlay.js.map +1 -0
- package/dist/protocol.d.ts +118 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +3 -0
- package/dist/protocol.js.map +1 -0
- package/dist/remote-session.d.ts +113 -0
- package/dist/remote-session.d.ts.map +1 -0
- package/dist/remote-session.js +645 -0
- package/dist/remote-session.js.map +1 -0
- package/dist/remote-tui.d.ts +40 -0
- package/dist/remote-tui.d.ts.map +1 -0
- package/dist/remote-tui.js +606 -0
- package/dist/remote-tui.js.map +1 -0
- package/dist/renderers.d.ts +34 -0
- package/dist/renderers.d.ts.map +1 -0
- package/dist/renderers.js +669 -0
- package/dist/renderers.js.map +1 -0
- package/dist/review.d.ts +15 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +154 -0
- package/dist/review.js.map +1 -0
- package/dist/titlebar.d.ts +3 -0
- package/dist/titlebar.d.ts.map +1 -0
- package/dist/titlebar.js +59 -0
- package/dist/titlebar.js.map +1 -0
- package/dist/todo/index.d.ts +3 -0
- package/dist/todo/index.d.ts.map +1 -0
- package/dist/todo/index.js +3 -0
- package/dist/todo/index.js.map +1 -0
- package/dist/todo/store.d.ts +6 -0
- package/dist/todo/store.d.ts.map +1 -0
- package/dist/todo/store.js +43 -0
- package/dist/todo/store.js.map +1 -0
- package/dist/todo/types.d.ts +13 -0
- package/dist/todo/types.d.ts.map +1 -0
- package/dist/todo/types.js +2 -0
- package/dist/todo/types.js.map +1 -0
- package/package.json +44 -0
- package/src/chain-preview.ts +621 -0
- package/src/client.ts +515 -0
- package/src/commands.ts +132 -0
- package/src/footer.ts +305 -0
- package/src/handlers.ts +113 -0
- package/src/hub-overlay.ts +2324 -0
- package/src/index.ts +1907 -0
- package/src/output-viewer.ts +480 -0
- package/src/overlay.ts +294 -0
- package/src/protocol.ts +157 -0
- package/src/remote-session.ts +800 -0
- package/src/remote-tui.ts +707 -0
- package/src/renderers.ts +740 -0
- package/src/review.ts +201 -0
- package/src/titlebar.ts +63 -0
- package/src/todo/index.ts +2 -0
- package/src/todo/store.ts +49 -0
- package/src/todo/types.ts +14 -0
package/src/footer.ts
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coder footer for the Pi TUI.
|
|
3
|
+
*
|
|
4
|
+
* Uses transparent backgrounds with foreground-only ANSI true-color text.
|
|
5
|
+
* Includes a braille spinner animation when an agent is actively working.
|
|
6
|
+
*
|
|
7
|
+
* Layout:
|
|
8
|
+
* [brand] [branch] > [model/agent] [hub] | [N] label token-stats
|
|
9
|
+
*
|
|
10
|
+
* Observer awareness (ASCII only):
|
|
11
|
+
* [3] SwiftRaven — 3 observers watching, session label "SwiftRaven"
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionContext, ReadonlyFooterDataProvider } from '@mariozechner/pi-coding-agent';
|
|
15
|
+
|
|
16
|
+
const RESET = '\x1b[0m';
|
|
17
|
+
const SEP = '>';
|
|
18
|
+
|
|
19
|
+
// ──────────────────────────────────────────────
|
|
20
|
+
// ANSI true-color helper (foreground only)
|
|
21
|
+
// ──────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
type RGB = [number, number, number];
|
|
24
|
+
|
|
25
|
+
function fg(color: RGB, text: string): string {
|
|
26
|
+
return `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${text}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ──────────────────────────────────────────────
|
|
30
|
+
// Color palette (foreground only, no backgrounds)
|
|
31
|
+
// ──────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
const FG_BRAND: RGB = [100, 200, 255];
|
|
34
|
+
const FG_MODEL: RGB = [215, 135, 175];
|
|
35
|
+
const FG_AGENT: RGB = [130, 200, 130];
|
|
36
|
+
const FG_BRANCH: RGB = [150, 180, 150];
|
|
37
|
+
const FG_HUB_OK: RGB = [80, 200, 120];
|
|
38
|
+
const FG_HUB_WARN: RGB = [245, 179, 66];
|
|
39
|
+
const FG_HUB_ERR: RGB = [220, 80, 80];
|
|
40
|
+
const FG_DIM: RGB = [100, 110, 120];
|
|
41
|
+
|
|
42
|
+
type HubStatus = 'connected' | 'reconnecting' | 'offline';
|
|
43
|
+
|
|
44
|
+
/** Observer state provided by the extension's presence tracking. */
|
|
45
|
+
export interface ObserverState {
|
|
46
|
+
/** Number of observers watching this session (excludes lead + sub-agents). */
|
|
47
|
+
count: number;
|
|
48
|
+
/** Human-readable session label (e.g. "SwiftRaven"). */
|
|
49
|
+
label: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const FG_OBSERVER: RGB = [140, 180, 220];
|
|
53
|
+
|
|
54
|
+
// ──────────────────────────────────────────────
|
|
55
|
+
// Braille spinner
|
|
56
|
+
// ──────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
const SPINNER_FRAMES = [
|
|
59
|
+
'\u280B',
|
|
60
|
+
'\u2819',
|
|
61
|
+
'\u2839',
|
|
62
|
+
'\u2838',
|
|
63
|
+
'\u283C',
|
|
64
|
+
'\u2834',
|
|
65
|
+
'\u2826',
|
|
66
|
+
'\u2827',
|
|
67
|
+
'\u2807',
|
|
68
|
+
'\u280F',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// ──────────────────────────────────────────────
|
|
72
|
+
// Footer builder (transparent bg, foreground only)
|
|
73
|
+
// ──────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/** Strip ANSI escape sequences to get visible character count. */
|
|
76
|
+
function visibleLength(str: string): number {
|
|
77
|
+
// eslint-disable-next-line no-control-regex
|
|
78
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Truncate an ANSI-colored string to a maximum visible width. */
|
|
82
|
+
function truncateAnsi(str: string, maxWidth: number): string {
|
|
83
|
+
let visible = 0;
|
|
84
|
+
let i = 0;
|
|
85
|
+
while (i < str.length && visible < maxWidth) {
|
|
86
|
+
if (str[i] === '\x1b') {
|
|
87
|
+
// Skip entire ANSI escape sequence
|
|
88
|
+
const end = str.indexOf('m', i);
|
|
89
|
+
if (end !== -1) {
|
|
90
|
+
i = end + 1;
|
|
91
|
+
} else {
|
|
92
|
+
i++;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
visible++;
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return str.slice(0, i) + RESET;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function buildFooter(left: string, rightText: string, width: number): string {
|
|
103
|
+
// Safety margin for Unicode characters that may be double-width
|
|
104
|
+
const safeWidth = width - 4;
|
|
105
|
+
const leftLen = visibleLength(left);
|
|
106
|
+
const rightLen = visibleLength(rightText);
|
|
107
|
+
const total = leftLen + 1 + rightLen;
|
|
108
|
+
|
|
109
|
+
if (total > safeWidth) {
|
|
110
|
+
const maxLeft = safeWidth - rightLen - 1;
|
|
111
|
+
if (maxLeft > 0) {
|
|
112
|
+
return truncateAnsi(left, maxLeft) + ' ' + rightText;
|
|
113
|
+
}
|
|
114
|
+
return truncateAnsi(left + ' ' + rightText, safeWidth);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const gap = safeWidth - leftLen - rightLen;
|
|
118
|
+
return left + ' '.repeat(gap) + rightText;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ──────────────────────────────────────────────
|
|
122
|
+
// Minimal component
|
|
123
|
+
// ──────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
class FooterComponent {
|
|
126
|
+
private getText: (width: number) => string;
|
|
127
|
+
private _unsubscribeBranch?: () => void;
|
|
128
|
+
|
|
129
|
+
constructor(
|
|
130
|
+
getText: (width: number) => string,
|
|
131
|
+
footerData: ReadonlyFooterDataProvider,
|
|
132
|
+
cleanupSpinner: () => void
|
|
133
|
+
) {
|
|
134
|
+
this.getText = getText;
|
|
135
|
+
this._cleanupSpinner = cleanupSpinner;
|
|
136
|
+
this._unsubscribeBranch = footerData.onBranchChange(() => {
|
|
137
|
+
// Triggers TUI refresh
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private _cleanupSpinner: () => void;
|
|
142
|
+
|
|
143
|
+
render(width: number): string[] {
|
|
144
|
+
const text = this.getText(width);
|
|
145
|
+
// Final safety: ensure line never exceeds terminal width
|
|
146
|
+
if (visibleLength(text) > width) {
|
|
147
|
+
return [truncateAnsi(text, width)];
|
|
148
|
+
}
|
|
149
|
+
return [text];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
invalidate(): void {
|
|
153
|
+
// no-op
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
dispose(): void {
|
|
157
|
+
this._unsubscribeBranch?.();
|
|
158
|
+
this._cleanupSpinner();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ──────────────────────────────────────────────
|
|
163
|
+
// Token stat formatters
|
|
164
|
+
// ──────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
function formatTokens(n: number): string {
|
|
167
|
+
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
168
|
+
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
|
|
169
|
+
return String(n);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function formatCost(n: number): string {
|
|
173
|
+
if (n === 0) return '$0.00';
|
|
174
|
+
if (n < 0.01) return `$${n.toFixed(4)}`;
|
|
175
|
+
return `$${n.toFixed(2)}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ──────────────────────────────────────────────
|
|
179
|
+
// Public API
|
|
180
|
+
// ──────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Set up the Coder footer (transparent bg, foreground-colored text).
|
|
184
|
+
* Call this once with the extension context to replace Pi's default footer.
|
|
185
|
+
*
|
|
186
|
+
* Includes a braille spinner animation when an agent is actively working.
|
|
187
|
+
*
|
|
188
|
+
* @param ctx Extension context with UI access
|
|
189
|
+
* @param getHubStatus Callback that returns current Hub connection status
|
|
190
|
+
* @param getObserverState Optional callback that returns observer count + session label
|
|
191
|
+
*/
|
|
192
|
+
export function setupCoderFooter(
|
|
193
|
+
ctx: ExtensionContext,
|
|
194
|
+
getHubStatus: () => HubStatus,
|
|
195
|
+
getObserverState?: () => ObserverState
|
|
196
|
+
): void {
|
|
197
|
+
if (!ctx.hasUI) return;
|
|
198
|
+
|
|
199
|
+
ctx.ui.setFooter((tui, _theme, footerData) => {
|
|
200
|
+
// Spinner state
|
|
201
|
+
let spinnerTimer: ReturnType<typeof setInterval> | null = null;
|
|
202
|
+
let spinnerFrame = 0;
|
|
203
|
+
|
|
204
|
+
const getText = (width: number): string => {
|
|
205
|
+
// Detect active agent
|
|
206
|
+
const activeAgent = footerData.getExtensionStatuses().get('active_agent');
|
|
207
|
+
|
|
208
|
+
// Start/stop spinner based on agent activity
|
|
209
|
+
if (activeAgent && !spinnerTimer) {
|
|
210
|
+
spinnerTimer = setInterval(() => {
|
|
211
|
+
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
212
|
+
tui.requestRender();
|
|
213
|
+
}, 80);
|
|
214
|
+
} else if (!activeAgent && spinnerTimer) {
|
|
215
|
+
clearInterval(spinnerTimer);
|
|
216
|
+
spinnerTimer = null;
|
|
217
|
+
spinnerFrame = 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Token stats from session messages
|
|
221
|
+
let inputTokens = 0;
|
|
222
|
+
let outputTokens = 0;
|
|
223
|
+
let totalCost = 0;
|
|
224
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
225
|
+
if (entry.type === 'message') {
|
|
226
|
+
const msg = entry.message as {
|
|
227
|
+
role?: string;
|
|
228
|
+
usage?: { input: number; output: number; cost: { total: number } };
|
|
229
|
+
};
|
|
230
|
+
if (msg.role === 'assistant' && msg.usage) {
|
|
231
|
+
inputTokens += msg.usage.input;
|
|
232
|
+
outputTokens += msg.usage.output;
|
|
233
|
+
totalCost += msg.usage.cost.total;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const tokenStr = `\u2191${formatTokens(inputTokens)} \u2193${formatTokens(outputTokens)} ${formatCost(totalCost)}`;
|
|
238
|
+
|
|
239
|
+
// LEFT side: brand > branch > model/agent
|
|
240
|
+
const leftParts: string[] = [];
|
|
241
|
+
|
|
242
|
+
// Brand (with spinner)
|
|
243
|
+
const brandChar = spinnerTimer ? SPINNER_FRAMES[spinnerFrame]! : '\u2A3A';
|
|
244
|
+
leftParts.push(fg(FG_BRAND, ` ${brandChar}`));
|
|
245
|
+
|
|
246
|
+
// Branch
|
|
247
|
+
const branch = footerData.getGitBranch();
|
|
248
|
+
if (branch) {
|
|
249
|
+
leftParts.push(' ');
|
|
250
|
+
leftParts.push(fg(FG_BRANCH, branch));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Model or active agent
|
|
254
|
+
leftParts.push(fg(FG_DIM, ` ${SEP} `));
|
|
255
|
+
if (activeAgent) {
|
|
256
|
+
leftParts.push(fg(FG_AGENT, activeAgent));
|
|
257
|
+
} else {
|
|
258
|
+
const modelId = ctx.model ? String((ctx.model as { id?: string }).id ?? '?') : '?';
|
|
259
|
+
leftParts.push(fg(FG_MODEL, modelId));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const left = leftParts.join('');
|
|
263
|
+
|
|
264
|
+
// RIGHT side: hub status + observer info + token stats
|
|
265
|
+
const rightParts: string[] = [];
|
|
266
|
+
const hubStatus = getHubStatus();
|
|
267
|
+
if (hubStatus === 'connected') {
|
|
268
|
+
rightParts.push(fg(FG_HUB_OK, 'Hub'));
|
|
269
|
+
} else if (hubStatus === 'reconnecting') {
|
|
270
|
+
rightParts.push(fg(FG_HUB_WARN, 'Hub...'));
|
|
271
|
+
} else {
|
|
272
|
+
rightParts.push(fg(FG_HUB_ERR, 'Hub Off'));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Observer awareness (ASCII only, no emojis)
|
|
276
|
+
if (getObserverState && hubStatus === 'connected') {
|
|
277
|
+
const obs = getObserverState();
|
|
278
|
+
if (obs.count > 0 || obs.label) {
|
|
279
|
+
rightParts.push(fg(FG_DIM, ' | '));
|
|
280
|
+
if (obs.count > 0) {
|
|
281
|
+
rightParts.push(fg(FG_OBSERVER, `[${obs.count}]`));
|
|
282
|
+
if (obs.label) rightParts.push(' ');
|
|
283
|
+
}
|
|
284
|
+
if (obs.label) {
|
|
285
|
+
rightParts.push(fg(FG_OBSERVER, obs.label));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
rightParts.push(fg(FG_DIM, ` ${tokenStr}`) + RESET);
|
|
291
|
+
const rightText = rightParts.join('');
|
|
292
|
+
|
|
293
|
+
return buildFooter(left, rightText, width);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const cleanupSpinner = (): void => {
|
|
297
|
+
if (spinnerTimer) {
|
|
298
|
+
clearInterval(spinnerTimer);
|
|
299
|
+
spinnerTimer = null;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return new FooterComponent(getText, footerData, cleanupSpinner);
|
|
304
|
+
});
|
|
305
|
+
}
|
package/src/handlers.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { HubAction } from './protocol.ts';
|
|
2
|
+
|
|
3
|
+
export interface ActionResult {
|
|
4
|
+
block?: { block: true; reason: string };
|
|
5
|
+
returnValue?: unknown;
|
|
6
|
+
systemPrompt?: string;
|
|
7
|
+
systemPromptMode?: 'replace' | 'prefix' | 'suffix';
|
|
8
|
+
// undefined means ACK (proceed normally)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Minimal UI surface used by action handlers — avoids a hard dep on pi-coding-agent. */
|
|
12
|
+
interface ActionContext {
|
|
13
|
+
ui?: {
|
|
14
|
+
notify(message: string, level?: 'info' | 'warning' | 'error'): void;
|
|
15
|
+
confirm(title: string, message: string): Promise<boolean>;
|
|
16
|
+
setStatus(key: string, text?: string): void;
|
|
17
|
+
};
|
|
18
|
+
sendUserMessage?: (message: string, options?: { deliverAs?: 'followUp' }) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function processActions(
|
|
22
|
+
actions: HubAction[],
|
|
23
|
+
ctx: ActionContext
|
|
24
|
+
): Promise<ActionResult> {
|
|
25
|
+
let result: ActionResult = {};
|
|
26
|
+
|
|
27
|
+
for (const action of actions) {
|
|
28
|
+
switch (action.action) {
|
|
29
|
+
case 'ACK':
|
|
30
|
+
// Terminal: proceed normally
|
|
31
|
+
result = {};
|
|
32
|
+
break;
|
|
33
|
+
|
|
34
|
+
case 'BLOCK':
|
|
35
|
+
// Terminal: block
|
|
36
|
+
result = { block: { block: true, reason: action.reason } };
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case 'RETURN':
|
|
40
|
+
// Terminal: return a specific result
|
|
41
|
+
result = { returnValue: action.result };
|
|
42
|
+
break;
|
|
43
|
+
|
|
44
|
+
case 'NOTIFY':
|
|
45
|
+
// Side effect: show notification, continue
|
|
46
|
+
if (ctx?.ui) {
|
|
47
|
+
ctx.ui.notify(action.message, action.level ?? 'info');
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'STATUS':
|
|
52
|
+
// Side effect: set status, continue
|
|
53
|
+
if (ctx?.ui) {
|
|
54
|
+
ctx.ui.setStatus(action.key, action.text);
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case 'CONFIRM': {
|
|
59
|
+
// Gate: if user denies, stop and block
|
|
60
|
+
if (ctx?.ui) {
|
|
61
|
+
const confirmed = await ctx.ui.confirm(action.title, action.message);
|
|
62
|
+
if (!confirmed) {
|
|
63
|
+
return {
|
|
64
|
+
block: {
|
|
65
|
+
block: true,
|
|
66
|
+
reason: action.deny_reason ?? 'Denied by user',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// No UI available — block by default for safety
|
|
72
|
+
result = {
|
|
73
|
+
block: {
|
|
74
|
+
block: true,
|
|
75
|
+
reason: action.deny_reason ?? 'Confirmation required but no UI available',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case 'SYSTEM_PROMPT':
|
|
83
|
+
// System prompt injection — store for before_agent_start handler
|
|
84
|
+
result = {
|
|
85
|
+
...result,
|
|
86
|
+
systemPrompt: action.systemPrompt,
|
|
87
|
+
systemPromptMode: action.mode,
|
|
88
|
+
};
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case 'INJECT_MESSAGE': {
|
|
92
|
+
const content = action.message?.content?.trim();
|
|
93
|
+
if (!content) break;
|
|
94
|
+
|
|
95
|
+
if (action.message?.role === 'user') {
|
|
96
|
+
if (ctx.sendUserMessage) {
|
|
97
|
+
ctx.sendUserMessage(content, { deliverAs: 'followUp' });
|
|
98
|
+
} else if (ctx.ui) {
|
|
99
|
+
ctx.ui.notify(content, 'info');
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (ctx.ui) {
|
|
105
|
+
ctx.ui.notify(content, 'info');
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return result;
|
|
113
|
+
}
|