talon-agent 3.33.4 → 3.34.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/package.json +5 -2
- package/src/app.ts +16 -8
- package/src/backend/claude-sdk/handler.ts +428 -415
- package/src/backend/claude-sdk/stream.ts +76 -53
- package/src/backend/codex/handler/message.ts +275 -366
- package/src/backend/codex/handler/rollout-accounting.ts +137 -0
- package/src/backend/openai-agents/handler/message.ts +282 -356
- package/src/backend/remote-server/chat-turn.ts +66 -122
- package/src/backend/remote-server/index.ts +4 -0
- package/src/backend/remote-server/mcp.ts +73 -9
- package/src/backend/remote-server/model-catalog/presentation.ts +269 -228
- package/src/backend/remote-server/sessions.ts +2 -2
- package/src/backend/remote-server/turn.ts +58 -55
- package/src/backend/shared/cache-telemetry.ts +17 -1
- package/src/backend/shared/handler-to-events.ts +11 -16
- package/src/backend/shared/index.ts +14 -15
- package/src/backend/shared/result-events.ts +30 -0
- package/src/backend/shared/turn-phases.ts +277 -0
- package/src/bootstrap.ts +120 -79
- package/src/cli/setup.ts +375 -349
- package/src/core/background/cron-spec.ts +273 -0
- package/src/core/background/heartbeat/agent.ts +167 -104
- package/src/core/background/triggers/exit.ts +19 -2
- package/src/core/background/triggers/resume.ts +14 -7
- package/src/core/background/triggers/state.ts +9 -0
- package/src/core/engine/backend-controller/pool.ts +2 -0
- package/src/core/engine/backend-controller/state.ts +25 -12
- package/src/core/engine/gateway-actions/cron.ts +28 -292
- package/src/core/engine/gateway-routes.ts +239 -0
- package/src/core/engine/gateway.ts +66 -238
- package/src/core/mcp-hub/child-transport.ts +215 -0
- package/src/core/mcp-hub/children.ts +72 -13
- package/src/core/mcp-hub/index.ts +32 -13
- package/src/core/models/active-model.ts +29 -6
- package/src/core/vfs/mounts/files.ts +128 -115
- package/src/core/weaver/shuttle.ts +33 -2
- package/src/core/weaver/weaver.ts +37 -6
- package/src/frontend/discord/callbacks/components/agent-buttons.ts +82 -0
- package/src/frontend/discord/callbacks/components/backend-select.ts +149 -0
- package/src/frontend/discord/callbacks/components/effort.ts +72 -0
- package/src/frontend/discord/callbacks/components/index.ts +120 -0
- package/src/frontend/discord/callbacks/components/metrics.ts +24 -0
- package/src/frontend/discord/callbacks/components/model-nav.ts +93 -0
- package/src/frontend/discord/callbacks/components/model-select.ts +118 -0
- package/src/frontend/discord/callbacks/components/model.ts +33 -0
- package/src/frontend/discord/callbacks/components/pulse.ts +93 -0
- package/src/frontend/discord/callbacks/components/settings.ts +243 -0
- package/src/frontend/discord/callbacks/components/types.ts +34 -0
- package/src/frontend/discord/callbacks/index.ts +4 -4
- package/src/frontend/discord/connection.ts +36 -0
- package/src/frontend/discord/diagnostics.ts +62 -0
- package/src/frontend/discord/guild-policy.ts +89 -0
- package/src/frontend/discord/index.ts +44 -305
- package/src/frontend/discord/outbound.ts +61 -0
- package/src/frontend/discord/ready.ts +73 -0
- package/src/frontend/discord/runtime.ts +55 -0
- package/src/frontend/native/chat-lifecycle.ts +39 -0
- package/src/frontend/native/chat-wire.ts +69 -0
- package/src/frontend/native/context.ts +106 -0
- package/src/frontend/native/control.ts +78 -0
- package/src/frontend/native/emit.ts +167 -0
- package/src/frontend/native/empty-chat-sweep.ts +50 -0
- package/src/frontend/native/handlers.ts +121 -0
- package/src/frontend/native/history.ts +101 -0
- package/src/frontend/native/index.ts +90 -1293
- package/src/frontend/native/media.ts +43 -0
- package/src/frontend/native/models.ts +221 -0
- package/src/frontend/native/queue.ts +47 -0
- package/src/frontend/native/reset.ts +50 -0
- package/src/frontend/native/routes/chats.ts +115 -0
- package/src/frontend/native/routes/daemon.ts +70 -0
- package/src/frontend/native/routes/host.ts +151 -0
- package/src/frontend/native/routes/index.ts +22 -0
- package/src/frontend/native/routes/mesh.ts +72 -0
- package/src/frontend/native/routes/models.ts +54 -0
- package/src/frontend/native/routes/params.ts +29 -0
- package/src/frontend/native/routes/pre-auth.ts +94 -0
- package/src/frontend/native/routes/table.ts +92 -0
- package/src/frontend/native/runtime.ts +109 -0
- package/src/frontend/native/server.ts +30 -555
- package/src/frontend/native/status.ts +26 -0
- package/src/frontend/native/tool-result.ts +48 -0
- package/src/frontend/native/turn.ts +341 -0
- package/src/frontend/telegram/admin.ts +75 -52
- package/src/frontend/whatsapp/access.ts +67 -0
- package/src/frontend/whatsapp/connection.ts +280 -0
- package/src/frontend/whatsapp/inbound.ts +327 -0
- package/src/frontend/whatsapp/index.ts +28 -599
- package/src/frontend/whatsapp/runtime.ts +74 -0
- package/src/storage/metrics.ts +18 -0
- package/src/storage/session-record.ts +29 -0
- package/src/storage/sessions.ts +24 -0
- package/src/storage/trigger-store.ts +8 -0
- package/src/util/boot-timer.ts +31 -0
- package/src/util/concurrency.ts +28 -0
- package/src/util/watchdog.ts +32 -7
- package/src/frontend/discord/callbacks/components.ts +0 -793
- package/src/frontend/discord/callbacks/shared.ts +0 -22
package/src/bootstrap.ts
CHANGED
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
import { initDream, maybeStartDream } from "./core/background/dream.js";
|
|
36
36
|
import { initHeartbeat } from "./core/background/heartbeat/index.js";
|
|
37
37
|
import { log, logWarn, logDebug } from "./util/log.js";
|
|
38
|
+
import { bootPhase } from "./util/boot-timer.js";
|
|
39
|
+
import { mapConcurrent } from "./util/concurrency.js";
|
|
38
40
|
import type { TalonConfig } from "./util/config.js";
|
|
39
41
|
import { resolveFrontendIdAmong } from "./core/frontend-runtime/routing.js";
|
|
40
42
|
import type { Frontend } from "./core/frontend-runtime/index.js";
|
|
@@ -122,11 +124,11 @@ export async function bootstrap(
|
|
|
122
124
|
const frontends =
|
|
123
125
|
options.frontendNames ??
|
|
124
126
|
(Array.isArray(config.frontend) ? config.frontend : [config.frontend]);
|
|
125
|
-
await loadPlugins(config.plugins, frontends);
|
|
127
|
+
await bootPhase("plugins", () => loadPlugins(config.plugins, frontends));
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
// Built-in plugins (GitHub, MemPalace, mem0, Playwright) — shared with hot-reload
|
|
129
|
-
await loadBuiltinPlugins(config);
|
|
131
|
+
await bootPhase("builtin plugins", () => loadBuiltinPlugins(config));
|
|
130
132
|
|
|
131
133
|
rebuildSystemPrompt(config, getPluginPromptAdditions());
|
|
132
134
|
}
|
|
@@ -143,12 +145,14 @@ export async function bootstrap(
|
|
|
143
145
|
});
|
|
144
146
|
|
|
145
147
|
initWorkspace(config.workspace);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
await bootPhase("stores", () => {
|
|
149
|
+
loadSessions();
|
|
150
|
+
loadChatSettings();
|
|
151
|
+
loadCronJobs();
|
|
152
|
+
loadTriggers();
|
|
153
|
+
loadHistory();
|
|
154
|
+
loadMediaIndex();
|
|
155
|
+
});
|
|
152
156
|
cleanupOldLogs();
|
|
153
157
|
|
|
154
158
|
return { config };
|
|
@@ -165,6 +169,104 @@ export async function bootstrap(
|
|
|
165
169
|
* heartbeat all read through `getActiveBackend()` so a runtime swap
|
|
166
170
|
* via `switchBackend(id, config)` propagates without any re-init.
|
|
167
171
|
*/
|
|
172
|
+
/** The backend-controller surface `reconcileChatBindings` needs. */
|
|
173
|
+
type ChatBindingDeps = {
|
|
174
|
+
isBackendAvailable: (id: string, config: TalonConfig) => boolean;
|
|
175
|
+
releaseChat: (chatId: string) => Promise<void>;
|
|
176
|
+
rebindChat: (
|
|
177
|
+
chatId: string,
|
|
178
|
+
backendId: string,
|
|
179
|
+
config: TalonConfig,
|
|
180
|
+
) => Promise<{ ok: boolean; error?: string }>;
|
|
181
|
+
getBackendIdForChat: (chatId: string) => string;
|
|
182
|
+
getBackendForChat: (chatId: string) => Backend;
|
|
183
|
+
isModelValidForBackend: (backend: Backend, model: string) => Promise<boolean>;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const CHAT_BINDING_CONCURRENCY = 8;
|
|
187
|
+
const REBIND_RETRY_DELAY_MS = 1_500;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Re-establish every chat's stored backend/model override against the
|
|
191
|
+
* backends this boot actually has. Chats are independent, so they are
|
|
192
|
+
* reconciled `CHAT_BINDING_CONCURRENCY` at a time; a shared backend that
|
|
193
|
+
* two chats need at once is initialised exactly once by the pool.
|
|
194
|
+
*/
|
|
195
|
+
export async function reconcileChatBindings(
|
|
196
|
+
config: TalonConfig,
|
|
197
|
+
deps: ChatBindingDeps,
|
|
198
|
+
): Promise<void> {
|
|
199
|
+
const { getAllChatSettings } = await import("./storage/chat-settings.js");
|
|
200
|
+
await mapConcurrent(
|
|
201
|
+
Object.entries(getAllChatSettings()),
|
|
202
|
+
CHAT_BINDING_CONCURRENCY,
|
|
203
|
+
([cid, settings]) => reconcileChatBinding(cid, settings, config, deps),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function reconcileChatBinding(
|
|
208
|
+
cid: string,
|
|
209
|
+
settings: { backend?: string; model?: string },
|
|
210
|
+
config: TalonConfig,
|
|
211
|
+
deps: ChatBindingDeps,
|
|
212
|
+
): Promise<void> {
|
|
213
|
+
const { getAllChatSettings, setChatBackend, setChatModel } =
|
|
214
|
+
await import("./storage/chat-settings.js");
|
|
215
|
+
let resetVolatileState = false;
|
|
216
|
+
if (settings.backend) {
|
|
217
|
+
if (!deps.isBackendAvailable(settings.backend, config)) {
|
|
218
|
+
log(
|
|
219
|
+
"bot",
|
|
220
|
+
`Per-chat backend ${settings.backend} for ${cid} is no longer available — resetting chat to default backend`,
|
|
221
|
+
);
|
|
222
|
+
await deps.releaseChat(cid);
|
|
223
|
+
setChatBackend(cid, undefined);
|
|
224
|
+
setChatModel(cid, undefined);
|
|
225
|
+
resetVolatileState = true;
|
|
226
|
+
} else {
|
|
227
|
+
let result = await deps.rebindChat(cid, settings.backend, config);
|
|
228
|
+
if (!result.ok) {
|
|
229
|
+
await new Promise((r) => setTimeout(r, REBIND_RETRY_DELAY_MS));
|
|
230
|
+
result = await deps.rebindChat(cid, settings.backend, config);
|
|
231
|
+
}
|
|
232
|
+
if (!result.ok) {
|
|
233
|
+
log(
|
|
234
|
+
"bot",
|
|
235
|
+
`Per-chat backend rebind failed for ${cid} → ${settings.backend}: ${result.error} — keeping the setting; will serve on the default backend until re-selected`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const bindingMatchesSetting =
|
|
241
|
+
!settings.backend || deps.getBackendIdForChat(cid) === settings.backend;
|
|
242
|
+
const currentModel = getAllChatSettings()[cid]?.model;
|
|
243
|
+
if (currentModel && bindingMatchesSetting) {
|
|
244
|
+
const be = deps.getBackendForChat(cid);
|
|
245
|
+
try {
|
|
246
|
+
const valid = await deps.isModelValidForBackend(be, currentModel);
|
|
247
|
+
if (!valid) {
|
|
248
|
+
log(
|
|
249
|
+
"bot",
|
|
250
|
+
`Per-chat model ${currentModel} for ${cid} is not valid for its backend — resetting model to default`,
|
|
251
|
+
);
|
|
252
|
+
setChatModel(cid, undefined);
|
|
253
|
+
resetVolatileState = true;
|
|
254
|
+
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
log(
|
|
257
|
+
"bot",
|
|
258
|
+
`Per-chat model validation failed for ${cid} (${currentModel}): ${
|
|
259
|
+
err instanceof Error ? err.message : String(err)
|
|
260
|
+
} — keeping stored model`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (resetVolatileState) {
|
|
265
|
+
resetSession(cid);
|
|
266
|
+
clearHistory(cid);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
168
270
|
export async function initBackendAndDispatcher(
|
|
169
271
|
config: TalonConfig,
|
|
170
272
|
frontend: FrontendSelection,
|
|
@@ -258,77 +360,16 @@ export async function initBackendAndDispatcher(
|
|
|
258
360
|
// for the backend that would serve it, clear the override and reset
|
|
259
361
|
// volatile chat state so the next user message starts a fresh default
|
|
260
362
|
// session instead of crashing on an orphaned model id.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
);
|
|
272
|
-
await releaseChat(cid);
|
|
273
|
-
setChatBackend(cid, undefined);
|
|
274
|
-
setChatModel(cid, undefined);
|
|
275
|
-
resetVolatileState = true;
|
|
276
|
-
} else {
|
|
277
|
-
// A boot-time rebind failure is usually TRANSIENT (backend slow to
|
|
278
|
-
// spawn, auth endpoint briefly unreachable) — it must not destroy
|
|
279
|
-
// the user's persisted choice, session, and history. Retry once,
|
|
280
|
-
// then keep the setting and move on: the chat runs on the default
|
|
281
|
-
// backend until a later switch succeeds, and clients keep showing
|
|
282
|
-
// the user's chosen backend from settings (the source of truth).
|
|
283
|
-
let result = await rebindChat(cid, settings.backend, config);
|
|
284
|
-
if (!result.ok) {
|
|
285
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
286
|
-
result = await rebindChat(cid, settings.backend, config);
|
|
287
|
-
}
|
|
288
|
-
if (!result.ok) {
|
|
289
|
-
log(
|
|
290
|
-
"bot",
|
|
291
|
-
`Per-chat backend rebind failed for ${cid} → ${settings.backend}: ${result.error} — keeping the setting; will serve on the default backend until re-selected`,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Only validate the stored model against the backend that will really
|
|
298
|
-
// serve this chat. After a kept-but-unbound override (transient rebind
|
|
299
|
-
// failure above) the chat temporarily runs on the default backend — the
|
|
300
|
-
// stored model belongs to the chosen backend, so validating it against
|
|
301
|
-
// the default would wrongly clear it.
|
|
302
|
-
const bindingMatchesSetting =
|
|
303
|
-
!settings.backend || getBackendIdForChat(cid) === settings.backend;
|
|
304
|
-
const currentModel = getAllChatSettings()[cid]?.model;
|
|
305
|
-
if (currentModel && bindingMatchesSetting) {
|
|
306
|
-
const be = getBackendForChat(cid);
|
|
307
|
-
try {
|
|
308
|
-
const valid = await isModelValidForBackend(be, currentModel);
|
|
309
|
-
if (!valid) {
|
|
310
|
-
log(
|
|
311
|
-
"bot",
|
|
312
|
-
`Per-chat model ${currentModel} for ${cid} is not valid for its backend — resetting model to default`,
|
|
313
|
-
);
|
|
314
|
-
setChatModel(cid, undefined);
|
|
315
|
-
resetVolatileState = true;
|
|
316
|
-
}
|
|
317
|
-
} catch (err) {
|
|
318
|
-
log(
|
|
319
|
-
"bot",
|
|
320
|
-
`Per-chat model validation failed for ${cid} (${currentModel}): ${
|
|
321
|
-
err instanceof Error ? err.message : String(err)
|
|
322
|
-
} — keeping stored model`,
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (resetVolatileState) {
|
|
328
|
-
resetSession(cid);
|
|
329
|
-
clearHistory(cid);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
363
|
+
await bootPhase("chat bindings", () =>
|
|
364
|
+
reconcileChatBindings(config, {
|
|
365
|
+
isBackendAvailable,
|
|
366
|
+
releaseChat,
|
|
367
|
+
rebindChat,
|
|
368
|
+
getBackendIdForChat,
|
|
369
|
+
getBackendForChat,
|
|
370
|
+
isModelValidForBackend,
|
|
371
|
+
}),
|
|
372
|
+
);
|
|
332
373
|
|
|
333
374
|
initDispatcher({
|
|
334
375
|
// Dispatcher reads the backend per query so per-chat overrides
|