talon-agent 3.8.1 → 3.9.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/README.md +1 -1
- package/package.json +1 -2
- package/src/backend/claude-sdk/handler.ts +1 -1
- package/src/backend/codex/handler/message.ts +1 -1
- package/src/backend/kilo/handler/index.ts +1 -2
- package/src/backend/kilo/handler/state.ts +3 -8
- package/src/backend/kilo/index.ts +2 -28
- package/src/backend/openai-agents/handler/message.ts +1 -1
- package/src/backend/openai-agents/session.ts +1 -1
- package/src/backend/opencode/handler/index.ts +1 -2
- package/src/backend/opencode/handler/state.ts +3 -8
- package/src/backend/opencode/index.ts +0 -7
- package/src/backend/shared/delivery.ts +1 -1
- package/src/backend/shared/handle-retry.ts +1 -1
- package/src/backend/shared/index.ts +3 -2
- package/src/backend/shared/turn-interrupt.ts +1 -1
- package/src/cli/index.ts +0 -8
- package/src/core/bus/index.ts +1 -9
- package/src/core/mesh/registry.ts +106 -4
- package/src/core/mesh/service.ts +22 -7
- package/src/core/mesh/transfers.ts +33 -4
- package/src/core/plugin/index.ts +0 -9
- package/src/core/prompt/index.ts +1 -16
- package/src/core/vfs/index.ts +2 -21
- package/src/core/weaver/index.ts +5 -9
- package/src/frontend/discord/actions/messaging.ts +73 -13
- package/src/frontend/discord/actions/shared.ts +21 -3
- package/src/frontend/discord/commands/admin.ts +1 -1
- package/src/frontend/discord/errors.ts +16 -5
- package/src/frontend/discord/formatting.ts +19 -4
- package/src/frontend/native/index.ts +22 -8
- package/src/frontend/native/protocol.ts +6 -0
- package/src/frontend/native/server.ts +171 -12
- package/src/frontend/shared/format.ts +6 -0
- package/src/frontend/telegram/actions/messaging.ts +120 -24
- package/src/frontend/telegram/callbacks/metrics.ts +1 -1
- package/src/frontend/telegram/commands/admin.ts +1 -1
- package/src/frontend/telegram/formatting.ts +64 -19
- package/src/frontend/terminal/commands.ts +46 -8
- package/src/frontend/terminal/input.ts +110 -12
- package/src/frontend/terminal/renderer.ts +2 -0
- package/src/{util → storage}/metrics.ts +1 -1
- package/src/storage/scheduled-store.ts +6 -3
- package/src/util/config.ts +8 -0
- package/src/util/log.ts +1 -0
- package/src/backend/codex/index.ts +0 -33
- package/src/backend/openai-agents/index.ts +0 -48
- package/src/cli/plugin-entries.ts +0 -104
- package/src/core/agent-runtime/index.ts +0 -62
- package/src/core/background/index.ts +0 -21
- package/src/core/engine/index.ts +0 -20
- package/src/core/models/index.ts +0 -17
- package/src/core/soul/index.ts +0 -140
- package/src/core/tools/mcp-server.ts +0 -111
- /package/src/{backend/shared → util}/session-name.ts +0 -0
|
@@ -15,7 +15,15 @@ import {
|
|
|
15
15
|
resolveModel as coreResolveModel,
|
|
16
16
|
getModels,
|
|
17
17
|
} from "../../core/models/catalog.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
buildCacheDisplay,
|
|
20
|
+
buildContextDisplay,
|
|
21
|
+
} from "../shared/status-context.js";
|
|
22
|
+
import {
|
|
23
|
+
formatDuration,
|
|
24
|
+
formatTokenCount,
|
|
25
|
+
formatUsd,
|
|
26
|
+
} from "../shared/format.js";
|
|
19
27
|
import {
|
|
20
28
|
getChatSettings,
|
|
21
29
|
setChatModel,
|
|
@@ -282,7 +290,7 @@ export function registerBuiltinCommands(): void {
|
|
|
282
290
|
let displayCacheRead = u.totalCacheRead;
|
|
283
291
|
let displayCacheWrite = u.totalCacheWrite;
|
|
284
292
|
let backendModelLine = "";
|
|
285
|
-
let
|
|
293
|
+
let contextWindow = u.contextWindow;
|
|
286
294
|
ctx.renderer.writeln();
|
|
287
295
|
const nameStr = info.sessionName ? `"${info.sessionName}" · ` : "";
|
|
288
296
|
|
|
@@ -313,25 +321,55 @@ export function registerBuiltinCommands(): void {
|
|
|
313
321
|
if (modelInfo) {
|
|
314
322
|
backendModelLine = ` ${pc.bold(label)} ${modelInfo.displayName} · ${modelInfo.providerName}${modelInfo.free ? " · free" : ""}`;
|
|
315
323
|
if (modelInfo.contextWindow) {
|
|
316
|
-
|
|
317
|
-
? ` ${pc.dim(`context window ${modelInfo.contextWindow.toLocaleString()} · cache ${cache.read.toLocaleString()} / ${cache.write.toLocaleString()}`)}`
|
|
318
|
-
: ` ${pc.dim(`context window ${modelInfo.contextWindow.toLocaleString()}`)}`;
|
|
324
|
+
contextWindow ||= modelInfo.contextWindow;
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
327
|
}
|
|
322
328
|
|
|
329
|
+
const context = buildContextDisplay({
|
|
330
|
+
contextTokens: u.contextTokens,
|
|
331
|
+
lastPromptTokens: u.lastPromptTokens,
|
|
332
|
+
contextWindow,
|
|
333
|
+
});
|
|
334
|
+
const contextUsed = context.known
|
|
335
|
+
? formatTokenCount(context.used)
|
|
336
|
+
: "unknown";
|
|
337
|
+
const contextMax = context.max
|
|
338
|
+
? formatTokenCount(context.max)
|
|
339
|
+
: "unknown";
|
|
340
|
+
const avgResponseMs =
|
|
341
|
+
info.turns > 0 && u.totalResponseMs
|
|
342
|
+
? Math.round(u.totalResponseMs / info.turns)
|
|
343
|
+
: 0;
|
|
344
|
+
const fastestResponseMs =
|
|
345
|
+
Number.isFinite(u.fastestResponseMs) && u.fastestResponseMs > 0
|
|
346
|
+
? u.fastestResponseMs
|
|
347
|
+
: 0;
|
|
348
|
+
|
|
323
349
|
ctx.renderer.writeln(
|
|
324
350
|
` ${pc.bold("Session")} ${nameStr}turns ${info.turns}${cache ? ` · ${cache.hitPct}% cache` : ""}`,
|
|
325
351
|
);
|
|
326
352
|
ctx.renderer.writeln(
|
|
327
353
|
` ${pc.dim(`in ${displayInputTokens.toLocaleString()} · out ${displayOutputTokens.toLocaleString()} tokens`)}`,
|
|
328
354
|
);
|
|
355
|
+
ctx.renderer.writeln();
|
|
356
|
+
ctx.renderer.writeln(
|
|
357
|
+
` ${pc.bold("Context")} ${contextUsed} / ${contextMax} (${context.known ? `${context.pct}%` : "unknown"})${context.warn ? pc.yellow(" nearing limit") : ""}`,
|
|
358
|
+
);
|
|
359
|
+
ctx.renderer.writeln(
|
|
360
|
+
` ${context.warn ? pc.yellow(context.bar) : pc.dim(context.bar)}`,
|
|
361
|
+
);
|
|
362
|
+
ctx.renderer.writeln(
|
|
363
|
+
` ${pc.dim(`response last ${u.lastResponseMs ? formatDuration(u.lastResponseMs) : "—"} · avg ${avgResponseMs ? formatDuration(avgResponseMs) : "—"} · best ${fastestResponseMs ? formatDuration(fastestResponseMs) : "—"}`)}`,
|
|
364
|
+
);
|
|
365
|
+
if (u.estimatedCostUsd > 0) {
|
|
366
|
+
ctx.renderer.writeln(
|
|
367
|
+
` ${pc.dim(`estimated session cost ${formatUsd(u.estimatedCostUsd)}`)}`,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
329
370
|
if (backendModelLine) {
|
|
330
371
|
ctx.renderer.writeln();
|
|
331
372
|
ctx.renderer.writeln(backendModelLine);
|
|
332
|
-
if (backendContextLine) {
|
|
333
|
-
ctx.renderer.writeln(backendContextLine);
|
|
334
|
-
}
|
|
335
373
|
}
|
|
336
374
|
|
|
337
375
|
const plugins = getLoadedPlugins();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Input is a list of parts: text segments and collapsed paste blocks.
|
|
5
5
|
* You can type, paste, type more, paste again. Backspace removes from the end.
|
|
6
|
-
* Enter submits everything. Ctrl+U clears all.
|
|
6
|
+
* Enter submits everything. Ctrl+U clears all. Up/Down walk prompt history.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import pc from "picocolors";
|
|
@@ -19,9 +19,10 @@ export type InputHandler = {
|
|
|
19
19
|
resume(): void;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
type
|
|
23
|
-
type
|
|
24
|
-
type
|
|
22
|
+
export type InputPart =
|
|
23
|
+
{ type: "text"; content: string } | { type: "paste"; content: string };
|
|
24
|
+
type TextPart = Extract<InputPart, { type: "text" }>;
|
|
25
|
+
type PastePart = Extract<InputPart, { type: "paste" }>;
|
|
25
26
|
|
|
26
27
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
27
28
|
|
|
@@ -29,6 +30,79 @@ const PASTE_COLLAPSE_LINES = 3;
|
|
|
29
30
|
const PASTE_COLLAPSE_CHARS = 150;
|
|
30
31
|
const PASTE_START = "\x1b[200~";
|
|
31
32
|
const PASTE_END = "\x1b[201~";
|
|
33
|
+
const MAX_HISTORY_ITEMS = 100;
|
|
34
|
+
|
|
35
|
+
function cloneParts(parts: readonly InputPart[]): InputPart[] {
|
|
36
|
+
return parts.map((part) => ({ ...part }));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function partsText(parts: readonly InputPart[]): string {
|
|
40
|
+
return parts
|
|
41
|
+
.map((part) => part.content)
|
|
42
|
+
.join("\n")
|
|
43
|
+
.trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* In-process prompt history. Keeping this out of the persistent stores avoids
|
|
48
|
+
* creating a second plaintext transcript while still giving terminal users
|
|
49
|
+
* the expected Up/Down workflow.
|
|
50
|
+
*/
|
|
51
|
+
export class PromptHistory {
|
|
52
|
+
private readonly entries: InputPart[][] = [];
|
|
53
|
+
private cursor = 0;
|
|
54
|
+
private draft: InputPart[] | null = null;
|
|
55
|
+
|
|
56
|
+
constructor(private readonly maxItems = MAX_HISTORY_ITEMS) {}
|
|
57
|
+
|
|
58
|
+
add(parts: readonly InputPart[]): void {
|
|
59
|
+
const text = partsText(parts);
|
|
60
|
+
if (!text) return;
|
|
61
|
+
|
|
62
|
+
const latest = this.entries.at(-1);
|
|
63
|
+
if (!latest || partsText(latest) !== text) {
|
|
64
|
+
this.entries.push(cloneParts(parts));
|
|
65
|
+
if (this.entries.length > this.maxItems) this.entries.shift();
|
|
66
|
+
}
|
|
67
|
+
this.resetNavigation();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
move(
|
|
71
|
+
direction: "previous" | "next",
|
|
72
|
+
current: readonly InputPart[],
|
|
73
|
+
): InputPart[] | undefined {
|
|
74
|
+
if (this.entries.length === 0) return undefined;
|
|
75
|
+
|
|
76
|
+
if (direction === "previous") {
|
|
77
|
+
if (this.cursor === this.entries.length) {
|
|
78
|
+
this.draft = cloneParts(current);
|
|
79
|
+
}
|
|
80
|
+
if (this.cursor > 0) this.cursor--;
|
|
81
|
+
return cloneParts(this.entries[this.cursor]!);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this.cursor === this.entries.length) return undefined;
|
|
85
|
+
if (this.cursor < this.entries.length - 1) {
|
|
86
|
+
this.cursor++;
|
|
87
|
+
return cloneParts(this.entries[this.cursor]!);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.cursor = this.entries.length;
|
|
91
|
+
return cloneParts(this.draft ?? [{ type: "text", content: "" }]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Preserve an edited recalled prompt as the current draft. */
|
|
95
|
+
detach(parts: readonly InputPart[]): void {
|
|
96
|
+
if (this.cursor === this.entries.length) return;
|
|
97
|
+
this.cursor = this.entries.length;
|
|
98
|
+
this.draft = cloneParts(parts);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
resetNavigation(): void {
|
|
102
|
+
this.cursor = this.entries.length;
|
|
103
|
+
this.draft = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
32
106
|
|
|
33
107
|
// ── Factory ──────────────────────────────────────────────────────────────────
|
|
34
108
|
|
|
@@ -38,7 +112,8 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
38
112
|
let paused = false;
|
|
39
113
|
|
|
40
114
|
// Input is an ordered list of parts
|
|
41
|
-
let parts:
|
|
115
|
+
let parts: InputPart[] = [{ type: "text", content: "" }];
|
|
116
|
+
const history = new PromptHistory();
|
|
42
117
|
|
|
43
118
|
// Bracketed paste accumulation
|
|
44
119
|
let inPaste = false;
|
|
@@ -46,7 +121,7 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
46
121
|
|
|
47
122
|
// ── Helpers ──
|
|
48
123
|
|
|
49
|
-
function lastPart():
|
|
124
|
+
function lastPart(): InputPart {
|
|
50
125
|
return parts[parts.length - 1]!;
|
|
51
126
|
}
|
|
52
127
|
|
|
@@ -99,18 +174,17 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
99
174
|
}
|
|
100
175
|
|
|
101
176
|
function getFullText(): string {
|
|
102
|
-
return parts
|
|
103
|
-
.map((p) => p.content)
|
|
104
|
-
.join("\n")
|
|
105
|
-
.trim();
|
|
177
|
+
return partsText(parts);
|
|
106
178
|
}
|
|
107
179
|
|
|
108
180
|
function clear(): void {
|
|
109
181
|
parts = [{ type: "text", content: "" }];
|
|
182
|
+
history.resetNavigation();
|
|
110
183
|
}
|
|
111
184
|
|
|
112
185
|
function submit(): void {
|
|
113
186
|
const text = getFullText();
|
|
187
|
+
if (!pendingResolve) history.add(parts);
|
|
114
188
|
clear();
|
|
115
189
|
process.stdout.write("\n");
|
|
116
190
|
|
|
@@ -135,6 +209,7 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
135
209
|
// Short paste — inline into current text part
|
|
136
210
|
ensureTrailingText().content += text.replace(/\n/g, " ");
|
|
137
211
|
}
|
|
212
|
+
history.detach(parts);
|
|
138
213
|
redraw();
|
|
139
214
|
}
|
|
140
215
|
|
|
@@ -160,6 +235,14 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
160
235
|
if (parts.length === 0) parts.push({ type: "text", content: "" });
|
|
161
236
|
ensureTrailingText();
|
|
162
237
|
}
|
|
238
|
+
history.detach(parts);
|
|
239
|
+
redraw();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function navigateHistory(direction: "previous" | "next"): void {
|
|
243
|
+
const recalled = history.move(direction, parts);
|
|
244
|
+
if (!recalled) return;
|
|
245
|
+
parts = recalled;
|
|
163
246
|
redraw();
|
|
164
247
|
}
|
|
165
248
|
|
|
@@ -236,9 +319,22 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
236
319
|
|
|
237
320
|
if (code === 0x1b) {
|
|
238
321
|
if (i + 1 < chunk.length && chunk[i + 1] === "[") {
|
|
239
|
-
//
|
|
322
|
+
// CSI escape sequence. Up/Down navigate history; the rest are
|
|
323
|
+
// ignored until cursor editing is implemented.
|
|
324
|
+
let end = i + 2;
|
|
325
|
+
while (end < chunk.length && chunk.charCodeAt(end) < 0x40) end++;
|
|
326
|
+
const final = chunk[end];
|
|
327
|
+
if (final === "A") navigateHistory("previous");
|
|
328
|
+
if (final === "B") navigateHistory("next");
|
|
329
|
+
i = end;
|
|
330
|
+
} else if (
|
|
331
|
+
i + 2 < chunk.length &&
|
|
332
|
+
chunk[i + 1] === "O" &&
|
|
333
|
+
(chunk[i + 2] === "A" || chunk[i + 2] === "B")
|
|
334
|
+
) {
|
|
335
|
+
// Some terminals use application-cursor sequences (ESC O A/B).
|
|
336
|
+
navigateHistory(chunk[i + 2] === "A" ? "previous" : "next");
|
|
240
337
|
i += 2;
|
|
241
|
-
while (i < chunk.length && chunk.charCodeAt(i) < 0x40) i++;
|
|
242
338
|
} else if (pendingResolve) {
|
|
243
339
|
// Bare Escape during waitForInput — cancel
|
|
244
340
|
const resolve = pendingResolve;
|
|
@@ -253,6 +349,7 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
253
349
|
if (code === 0x09) {
|
|
254
350
|
// Tab
|
|
255
351
|
ensureTrailingText().content += " ";
|
|
352
|
+
history.detach(parts);
|
|
256
353
|
redraw();
|
|
257
354
|
continue;
|
|
258
355
|
}
|
|
@@ -261,6 +358,7 @@ export function createInput(promptStr: string): InputHandler {
|
|
|
261
358
|
|
|
262
359
|
// Printable char
|
|
263
360
|
ensureTrailingText().content += ch;
|
|
361
|
+
history.detach(parts);
|
|
264
362
|
redraw();
|
|
265
363
|
}
|
|
266
364
|
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import pc from "picocolors";
|
|
10
|
+
import { formatUsd } from "../shared/format.js";
|
|
10
11
|
|
|
11
12
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
12
13
|
|
|
@@ -193,6 +194,7 @@ export function createRenderer(cols?: number, displayName = "Talon"): Renderer {
|
|
|
193
194
|
if (typeof info.cacheHitPct === "number") {
|
|
194
195
|
p.push(`${info.cacheHitPct}% cache`);
|
|
195
196
|
}
|
|
197
|
+
if (info.costUsd > 0) p.push(formatUsd(info.costUsd));
|
|
196
198
|
if (tools > 0) p.push(`${tools} tool${tools > 1 ? "s" : ""}`);
|
|
197
199
|
writeln();
|
|
198
200
|
writeln(` ${pc.dim(p.join(" · "))}`);
|
|
@@ -40,9 +40,12 @@ export type ScheduledMessage = {
|
|
|
40
40
|
/** Epoch ms when the message should be sent. */
|
|
41
41
|
fireAt: number;
|
|
42
42
|
createdAt: number;
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Message id to reply to, in the owning frontend's native shape:
|
|
45
|
+
* numeric for Telegram, a snowflake string for Discord.
|
|
46
|
+
*/
|
|
47
|
+
replyTo?: number | string;
|
|
48
|
+
/** Inline keyboard / button rows to replay at fire time. */
|
|
46
49
|
rows?: ScheduledButtonRow[];
|
|
47
50
|
};
|
|
48
51
|
|
package/src/util/config.ts
CHANGED
|
@@ -173,6 +173,14 @@ const nativeConfigSchema = z
|
|
|
173
173
|
* (~/.talon/keys/bridge-token) — the network never gets an open bridge.
|
|
174
174
|
*/
|
|
175
175
|
token: z.string().optional(),
|
|
176
|
+
/**
|
|
177
|
+
* Origins allowed to call the bridge from a BROWSER. Native clients
|
|
178
|
+
* (Electron main, Flutter, curl, talon-node) send no Origin header and
|
|
179
|
+
* never need an entry here. Anything listed gets a matching
|
|
180
|
+
* Access-Control-Allow-Origin; everything else is refused 403, so a
|
|
181
|
+
* random web page cannot drive the agent API on the user's machine.
|
|
182
|
+
*/
|
|
183
|
+
allowedOrigins: z.array(z.string().min(1)).optional(),
|
|
176
184
|
/**
|
|
177
185
|
* Serve the bridge over TLS with a persistent self-signed certificate
|
|
178
186
|
* (~/.talon/keys/); clients pin its SHA-256 fingerprint on first
|
package/src/util/log.ts
CHANGED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex backend — barrel re-export.
|
|
3
|
-
*
|
|
4
|
-
* Wraps `@openai/codex-sdk` (which in turn spawns the `codex` CLI from
|
|
5
|
-
* `@openai/codex`) and exposes a Backend-compatible API. The
|
|
6
|
-
* implementation is split across focused modules:
|
|
7
|
-
*
|
|
8
|
-
* - `constants.ts` — system-prompt suffix, default model.
|
|
9
|
-
* - `state.ts` — per-process state container.
|
|
10
|
-
* - `init.ts` — backend init + lazy Codex-instance construction.
|
|
11
|
-
* - `mcp-config.ts` — Talon MCP plugins → Codex `--config` TOML.
|
|
12
|
-
* - `handler.ts` — main message handler (streaming, end_turn).
|
|
13
|
-
* - `factory.ts` — registry registration.
|
|
14
|
-
*
|
|
15
|
-
* MCP integration: Codex configures MCP servers at thread-creation
|
|
16
|
-
* time via TOML overrides. Talon rebuilds the `Codex` instance when
|
|
17
|
-
* the active chat id changes so each chat sees its own MCP server
|
|
18
|
-
* environment (frontend-tools + plugin servers).
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
export {
|
|
22
|
-
CODEX_SYSTEM_PROMPT_SUFFIX,
|
|
23
|
-
CODEX_DEFAULT_MODEL,
|
|
24
|
-
CODEX_DEFAULT_WORKING_DIRECTORY,
|
|
25
|
-
} from "./constants.js";
|
|
26
|
-
|
|
27
|
-
export { initCodexAgent, ensureCodex } from "./init.js";
|
|
28
|
-
|
|
29
|
-
export { handleMessage, getActiveAbort } from "./handler/index.js";
|
|
30
|
-
|
|
31
|
-
export { runOneShotAgent } from "./one-shot.js";
|
|
32
|
-
|
|
33
|
-
export { buildCodexMcpServers, type CodexMcpServer } from "./mcp-config.js";
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenAI Agents backend — barrel.
|
|
3
|
-
*
|
|
4
|
-
* Importing this module registers the backend with the registry via
|
|
5
|
-
* `factory.ts`'s side-effect import. Most callers should NOT need to
|
|
6
|
-
* reach in here — the bootstrap layer picks the active backend by id.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import "./factory.js";
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
initOpenAIAgentsAgent,
|
|
13
|
-
getOpenAIApiKey,
|
|
14
|
-
getOpenAIBaseUrl,
|
|
15
|
-
triggerDiscoveryRefresh,
|
|
16
|
-
} from "./init.js";
|
|
17
|
-
export {
|
|
18
|
-
awaitDiscovery,
|
|
19
|
-
startDiscovery,
|
|
20
|
-
refreshDiscovery,
|
|
21
|
-
hasDiscoveredCatalog,
|
|
22
|
-
fetchEndpointModels,
|
|
23
|
-
extractCapabilities,
|
|
24
|
-
} from "./discovery.js";
|
|
25
|
-
export { handleMessage, getActiveAbort } from "./handler/index.js";
|
|
26
|
-
export { resetState, getState } from "./state.js";
|
|
27
|
-
export {
|
|
28
|
-
resolveModel,
|
|
29
|
-
getModelInfo,
|
|
30
|
-
getSettingsPresentation,
|
|
31
|
-
getProviders,
|
|
32
|
-
getProviderModels,
|
|
33
|
-
formatModelError,
|
|
34
|
-
listModels,
|
|
35
|
-
} from "./models.js";
|
|
36
|
-
export {
|
|
37
|
-
OPENAI_AGENTS_SYSTEM_PROMPT_SUFFIX,
|
|
38
|
-
OPENAI_AGENTS_DEFAULT_MODEL,
|
|
39
|
-
OPENAI_AGENTS_MAX_TURNS,
|
|
40
|
-
OPENAI_AGENTS_AGENT_NAME,
|
|
41
|
-
} from "./constants.js";
|
|
42
|
-
export {
|
|
43
|
-
getOrCreateBundle,
|
|
44
|
-
releaseBundle,
|
|
45
|
-
releaseAllBundles,
|
|
46
|
-
getActiveBundleIds,
|
|
47
|
-
} from "./mcp-pool.js";
|
|
48
|
-
export type { OpenAIAgentsMcpBundle, BundleInputs } from "./mcp-pool.js";
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers over the config `plugins` array for the `talon plugin`
|
|
3
|
-
* command group. The CLI edits config.json as JSON — entries here are the
|
|
4
|
-
* on-disk shape (`core/plugin/types.ts` PluginEntry), not loaded plugins.
|
|
5
|
-
* No filesystem or process access — everything is testable data-in/data-out.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** On-disk plugin entry — path-based module or standalone MCP server. */
|
|
9
|
-
export type PluginEntryJson = {
|
|
10
|
-
path?: string;
|
|
11
|
-
config?: Record<string, unknown>;
|
|
12
|
-
name?: string;
|
|
13
|
-
command?: string;
|
|
14
|
-
args?: string[];
|
|
15
|
-
env?: Record<string, string>;
|
|
16
|
-
enabled?: boolean;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Built-in plugins toggled by their own config sections (`github.enabled`,
|
|
21
|
-
* …) rather than `plugins[]` entries — see core/plugin/builtins.ts.
|
|
22
|
-
*/
|
|
23
|
-
export const BUILTIN_PLUGINS = [
|
|
24
|
-
"github",
|
|
25
|
-
"mempalace",
|
|
26
|
-
"mem0",
|
|
27
|
-
"playwright",
|
|
28
|
-
] as const;
|
|
29
|
-
|
|
30
|
-
export function isBuiltinPlugin(name: string): boolean {
|
|
31
|
-
return (BUILTIN_PLUGINS as readonly string[]).includes(name);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Split a config path on either separator — Windows configs carry `\`. */
|
|
35
|
-
function pathSegments(path: string): string[] {
|
|
36
|
-
return path.split(/[\\/]+/).filter(Boolean);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Display name for an entry: the MCP name, or for path entries the package
|
|
41
|
-
* name — segments after the last `node_modules` (preserving `@scope/pkg`),
|
|
42
|
-
* else the folder basename.
|
|
43
|
-
*/
|
|
44
|
-
export function entryDisplayName(entry: PluginEntryJson): string {
|
|
45
|
-
if (entry.name) return entry.name;
|
|
46
|
-
if (!entry.path) return "(invalid entry)";
|
|
47
|
-
const segments = pathSegments(entry.path);
|
|
48
|
-
const nm = segments.lastIndexOf("node_modules");
|
|
49
|
-
if (nm >= 0 && nm < segments.length - 1) {
|
|
50
|
-
return segments.slice(nm + 1).join("/");
|
|
51
|
-
}
|
|
52
|
-
return segments[segments.length - 1] ?? entry.path;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Does `token` identify this entry? Display name, basename, or full path. */
|
|
56
|
-
export function entryMatches(entry: PluginEntryJson, token: string): boolean {
|
|
57
|
-
if (entryDisplayName(entry) === token) return true;
|
|
58
|
-
if (!entry.path) return false;
|
|
59
|
-
const segments = pathSegments(entry.path);
|
|
60
|
-
return entry.path === token || segments[segments.length - 1] === token;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function findEntryIndex(
|
|
64
|
-
entries: PluginEntryJson[],
|
|
65
|
-
token: string,
|
|
66
|
-
): number {
|
|
67
|
-
return entries.findIndex((entry) => entryMatches(entry, token));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Index of an entry with the same identity as `candidate` (same module
|
|
72
|
-
* path or same MCP name) — the duplicate an install must replace.
|
|
73
|
-
*/
|
|
74
|
-
export function findConflictIndex(
|
|
75
|
-
entries: PluginEntryJson[],
|
|
76
|
-
candidate: PluginEntryJson,
|
|
77
|
-
): number {
|
|
78
|
-
return entries.findIndex((entry) =>
|
|
79
|
-
candidate.path !== undefined
|
|
80
|
-
? entry.path === candidate.path
|
|
81
|
-
: entry.name !== undefined && entry.name === candidate.name,
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Copy of `entry` with the enabled state applied. Enabled is the default,
|
|
87
|
-
* so enabling removes the key rather than writing `enabled: true`.
|
|
88
|
-
*/
|
|
89
|
-
export function withEnabled(
|
|
90
|
-
entry: PluginEntryJson,
|
|
91
|
-
enabled: boolean,
|
|
92
|
-
): PluginEntryJson {
|
|
93
|
-
const { enabled: _drop, ...rest } = entry;
|
|
94
|
-
return enabled ? rest : { ...rest, enabled: false };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Package name of an npm spec, version stripped: `pkg@1.2` → `pkg`,
|
|
99
|
-
* `@scope/pkg@^1` → `@scope/pkg`. A leading `@` is the scope, not a version.
|
|
100
|
-
*/
|
|
101
|
-
export function npmSpecName(spec: string): string {
|
|
102
|
-
const at = spec.lastIndexOf("@");
|
|
103
|
-
return at > 0 ? spec.slice(0, at) : spec;
|
|
104
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent runtime — barrel for the primitives every backend, frontend,
|
|
3
|
-
* and dispatcher consumer reads through. See `agent-runtime/README.md`
|
|
4
|
-
* for the module map.
|
|
5
|
-
*
|
|
6
|
-
* - `AgentEvent` + helpers (events.ts)
|
|
7
|
-
* - `ModelRef` + helpers (model-ref.ts)
|
|
8
|
-
* - `Backend` + capability interfaces + `composeBackend` (capabilities.ts)
|
|
9
|
-
* - Backend contract assertions (contract-tests.ts)
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
type AgentEvent,
|
|
14
|
-
type AgentError,
|
|
15
|
-
type AgentErrorKind,
|
|
16
|
-
type AgentResult,
|
|
17
|
-
type UsageSnapshot,
|
|
18
|
-
addUsage,
|
|
19
|
-
AgentRunError,
|
|
20
|
-
classifiedToAgentError,
|
|
21
|
-
emptyUsage,
|
|
22
|
-
isAgentEventOf,
|
|
23
|
-
isAgentRunTerminator,
|
|
24
|
-
toolInputToRecord,
|
|
25
|
-
} from "./events.js";
|
|
26
|
-
|
|
27
|
-
export {
|
|
28
|
-
type BackendId,
|
|
29
|
-
type CacheSupport,
|
|
30
|
-
type ModelRef,
|
|
31
|
-
type ModelSource,
|
|
32
|
-
BACKEND_IDS,
|
|
33
|
-
isBackendId,
|
|
34
|
-
makeBareModelRef,
|
|
35
|
-
sameModelRef,
|
|
36
|
-
} from "./model-ref.js";
|
|
37
|
-
|
|
38
|
-
export {
|
|
39
|
-
type Backend,
|
|
40
|
-
type BackgroundRunner,
|
|
41
|
-
type ChatBackend,
|
|
42
|
-
type ChatRunParams,
|
|
43
|
-
type ModelCatalog,
|
|
44
|
-
type SessionBackend,
|
|
45
|
-
type SystemControl,
|
|
46
|
-
type ToolRefreshResult,
|
|
47
|
-
type ToolRuntime,
|
|
48
|
-
type UsageTelemetry,
|
|
49
|
-
composeBackend,
|
|
50
|
-
} from "./capabilities.js";
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
assertBackendContract,
|
|
54
|
-
assertBackendIdentity,
|
|
55
|
-
assertBackgroundRunnerLifecycle,
|
|
56
|
-
assertChatBackendEmitsRunStarted,
|
|
57
|
-
assertChatBackendEmitsSingleUsage,
|
|
58
|
-
assertChatBackendTerminates,
|
|
59
|
-
assertCompletedUsageMatchesUsageEvent,
|
|
60
|
-
assertModelCatalogDefaultShape,
|
|
61
|
-
assertUsageTelemetryShape,
|
|
62
|
-
} from "./contract-tests.js";
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Background agents — everything that runs without a user message.
|
|
3
|
-
*
|
|
4
|
-
* - `heartbeat` — periodic autonomous agent pass ("anything need
|
|
5
|
-
* doing?"), prompted by prompts/heartbeat.md.
|
|
6
|
-
* - `dream` — memory-consolidation agent (daily notes → memory,
|
|
7
|
-
* mempalace mining), prompted by prompts/dream.md.
|
|
8
|
-
* - `pulse` — lightweight liveness/typing-indicator ticker.
|
|
9
|
-
* - `cron` — persistent user-scheduled jobs (message / query).
|
|
10
|
-
* - `triggers` — user-authored watcher scripts that fire wake-ups.
|
|
11
|
-
*
|
|
12
|
-
* All of these drive the backend through the same `BackgroundRunner`
|
|
13
|
-
* surface (core/agent-runtime/capabilities.ts) — they never talk to a
|
|
14
|
-
* concrete backend directly.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export * from "./heartbeat/index.js";
|
|
18
|
-
export * from "./dream.js";
|
|
19
|
-
export * from "./pulse.js";
|
|
20
|
-
export * from "./cron.js";
|
|
21
|
-
export * from "./triggers/index.js";
|
package/src/core/engine/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Engine — the message-flow core.
|
|
3
|
-
*
|
|
4
|
-
* - `dispatcher` — per-chat serial, cross-chat parallel turn
|
|
5
|
-
* execution; the single entry point for "run this user message".
|
|
6
|
-
* - `gateway` — loopback HTTP bridge the MCP tool
|
|
7
|
-
* subprocesses call back into.
|
|
8
|
-
* - `gateway-actions` — the action handlers the bridge dispatches
|
|
9
|
-
* to (send/edit/react/cron/trigger/admin …).
|
|
10
|
-
* - `backend-controller` — backend lifecycle: init, hot-swap,
|
|
11
|
-
* cleanup, registry lookup.
|
|
12
|
-
*
|
|
13
|
-
* Everything here is frontend- and backend-agnostic: frontends feed
|
|
14
|
-
* the dispatcher, backends are reached through the agent-runtime
|
|
15
|
-
* capability slots.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
export * from "./dispatcher.js";
|
|
19
|
-
export * from "./gateway.js";
|
|
20
|
-
export * from "./backend-controller/index.js";
|
package/src/core/models/index.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model layer — everything about "which model is the chat using".
|
|
3
|
-
*
|
|
4
|
-
* - `catalog` — model-id resolution, defaults, and the
|
|
5
|
-
* curated cross-backend catalog.
|
|
6
|
-
* - `active-model` — per-chat active-model resolution
|
|
7
|
-
* (chat-settings → config → backend default).
|
|
8
|
-
* - `reasoning-levels` — the reasoning-effort vocabulary shared by
|
|
9
|
-
* every backend (order, labels, normalisation).
|
|
10
|
-
*
|
|
11
|
-
* Presentation helpers (menus, status lines) live in `frontend/` —
|
|
12
|
-
* this layer is pure domain.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export * from "./catalog.js";
|
|
16
|
-
export * from "./active-model.js";
|
|
17
|
-
export * from "./reasoning-levels.js";
|