talon-agent 3.32.1 → 3.33.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.
Files changed (138) hide show
  1. package/package.json +1 -1
  2. package/src/backend/claude-sdk/constants.ts +1 -6
  3. package/src/backend/claude-sdk/index.ts +2 -11
  4. package/src/backend/claude-sdk/models/parsing.ts +2 -2
  5. package/src/backend/claude-sdk/plan-usage.ts +0 -5
  6. package/src/backend/claude-sdk/stream.ts +1 -1
  7. package/src/backend/codex/auth.ts +2 -2
  8. package/src/backend/codex/constants.ts +0 -21
  9. package/src/backend/codex/handler/index.ts +0 -1
  10. package/src/backend/codex/mcp-config.ts +2 -2
  11. package/src/backend/codex/plan-usage.ts +0 -5
  12. package/src/backend/codex/state.ts +1 -1
  13. package/src/backend/codex/token-usage.ts +1 -1
  14. package/src/backend/kilo/factory.ts +39 -119
  15. package/src/backend/kilo/handler/index.ts +1 -11
  16. package/src/backend/kilo/handler/message.ts +20 -317
  17. package/src/backend/kilo/index.ts +0 -6
  18. package/src/backend/kilo/server.ts +55 -292
  19. package/src/backend/kilo/sessions.ts +5 -86
  20. package/src/backend/openai-agents/handler/index.ts +0 -1
  21. package/src/backend/openai-agents/handler/state.ts +0 -5
  22. package/src/backend/openai-agents/init.ts +1 -18
  23. package/src/backend/opencode/factory.ts +41 -118
  24. package/src/backend/opencode/handler/index.ts +1 -11
  25. package/src/backend/opencode/handler/message.ts +20 -321
  26. package/src/backend/opencode/index.ts +0 -4
  27. package/src/backend/opencode/server.ts +56 -225
  28. package/src/backend/opencode/sessions.ts +5 -86
  29. package/src/backend/remote-server/chat-turn.ts +359 -0
  30. package/src/backend/remote-server/client.ts +1 -1
  31. package/src/backend/remote-server/events.ts +1 -1
  32. package/src/backend/remote-server/factory.ts +150 -0
  33. package/src/backend/remote-server/index.ts +15 -43
  34. package/src/backend/remote-server/mcp.ts +1 -4
  35. package/src/backend/remote-server/model-catalog/catalog.ts +1 -1
  36. package/src/backend/remote-server/model-catalog/index.ts +1 -15
  37. package/src/backend/remote-server/model-catalog/provider.ts +0 -1
  38. package/src/backend/remote-server/server-bindings.ts +232 -0
  39. package/src/backend/remote-server/sse-stream.ts +1 -3
  40. package/src/backend/remote-server/turn-timeout.ts +1 -1
  41. package/src/backend/{kilo/handler → remote-server}/turn.ts +103 -67
  42. package/src/backend/shared/index.ts +7 -43
  43. package/src/cli/logs.ts +1 -1
  44. package/src/cli/status.ts +1 -1
  45. package/src/core/agent-runtime/backend-registry.ts +1 -1
  46. package/src/core/agent-runtime/capabilities.ts +1 -1
  47. package/src/core/agent-runtime/events.ts +1 -1
  48. package/src/core/background/heartbeat/agent.ts +1 -1
  49. package/src/core/background/heartbeat/index.ts +1 -2
  50. package/src/core/background/isolated-agent.ts +1 -1
  51. package/src/core/background/triggers/exit.ts +1 -1
  52. package/src/core/background/triggers/index.ts +1 -1
  53. package/src/core/bus/events.ts +2 -2
  54. package/src/core/doctor.ts +1 -1
  55. package/src/core/engine/backend-controller/index.ts +1 -3
  56. package/src/core/engine/backend-controller/rebind.ts +2 -2
  57. package/src/core/engine/dispatcher.ts +1 -15
  58. package/src/core/engine/gateway-actions/index.ts +0 -3
  59. package/src/core/engine/gateway-actions/types.ts +1 -1
  60. package/src/core/frontend-runtime/capabilities.ts +0 -2
  61. package/src/core/frontend-runtime/index.ts +1 -6
  62. package/src/core/frontend-runtime/routing.ts +0 -1
  63. package/src/core/mcp-hub/children.ts +0 -5
  64. package/src/core/mesh/bridge-links.ts +319 -0
  65. package/src/core/mesh/common.ts +30 -0
  66. package/src/core/mesh/device-files.ts +710 -0
  67. package/src/core/mesh/index.ts +1 -18
  68. package/src/core/mesh/service.ts +72 -894
  69. package/src/core/models/active-model.ts +0 -19
  70. package/src/core/plugin/index.ts +0 -1
  71. package/src/core/plugin/registry.ts +1 -1
  72. package/src/core/prompt/templates.ts +0 -5
  73. package/src/core/scripting/lua-runner.ts +1 -1
  74. package/src/core/scripts/runner.ts +1 -1
  75. package/src/core/soul/critic.ts +1 -1
  76. package/src/core/soul/embedder.ts +2 -2
  77. package/src/core/soul/governance.ts +1 -1
  78. package/src/core/soul/hdc.ts +1 -1
  79. package/src/core/soul/reflex.ts +2 -2
  80. package/src/core/soul/retrieve.ts +1 -1
  81. package/src/core/soul/signals.ts +4 -4
  82. package/src/core/soul/types.ts +1 -1
  83. package/src/core/tasks/index.ts +0 -4
  84. package/src/core/tasks/types.ts +1 -1
  85. package/src/core/tools/index.ts +1 -6
  86. package/src/core/tools/mcp-env.ts +3 -33
  87. package/src/core/types.ts +1 -1
  88. package/src/core/update/self-update.ts +1 -1
  89. package/src/core/vfs/fusefs.ts +1 -1
  90. package/src/core/vfs/types.ts +1 -1
  91. package/src/core/weaver/index.ts +1 -1
  92. package/src/core/weaver/typing-loop.ts +1 -1
  93. package/src/frontend/discord/actions/types.ts +1 -1
  94. package/src/frontend/discord/commands/definitions.ts +1 -1
  95. package/src/frontend/discord/handlers/access.ts +7 -61
  96. package/src/frontend/discord/handlers/index.ts +2 -15
  97. package/src/frontend/discord/handlers/registry.ts +0 -6
  98. package/src/frontend/discord/handlers/state.ts +15 -8
  99. package/src/frontend/native/protocol.ts +1 -4
  100. package/src/frontend/native/server.ts +392 -324
  101. package/src/frontend/shared/access.ts +152 -0
  102. package/src/frontend/shared/status-context.ts +1 -1
  103. package/src/frontend/telegram/actions/types.ts +1 -1
  104. package/src/frontend/telegram/commands/index.ts +1 -1
  105. package/src/frontend/telegram/commands/state.ts +1 -1
  106. package/src/frontend/telegram/handlers/access.ts +8 -25
  107. package/src/frontend/telegram/handlers/index.ts +0 -1
  108. package/src/frontend/telegram/handlers/queue.ts +3 -35
  109. package/src/frontend/telegram/handlers/state.ts +15 -8
  110. package/src/frontend/telegram/helpers/menu.ts +1 -1
  111. package/src/frontend/terminal/commands.ts +1 -4
  112. package/src/frontend/terminal/renderer.ts +1 -1
  113. package/src/frontend/whatsapp/actions/chat-info.ts +0 -3
  114. package/src/frontend/whatsapp/actions/messaging.ts +1 -7
  115. package/src/frontend/whatsapp/actions/shared.ts +1 -1
  116. package/src/frontend/whatsapp/actions/types.ts +1 -1
  117. package/src/frontend/whatsapp/formatting.ts +1 -1
  118. package/src/frontend/whatsapp/media-store.ts +1 -1
  119. package/src/frontend/whatsapp/pins.ts +0 -5
  120. package/src/native/fusefs.ts +3 -3
  121. package/src/native/scheduler-core.ts +1 -1
  122. package/src/native/warden.ts +2 -2
  123. package/src/plugins/mempalace/provision.ts +1 -5
  124. package/src/plugins/playwright/provision.ts +1 -1
  125. package/src/storage/db.ts +1 -1
  126. package/src/storage/goal-store.ts +1 -5
  127. package/src/storage/metrics.ts +0 -5
  128. package/src/storage/repositories/scripts-repo.ts +0 -5
  129. package/src/storage/scheduled-store.ts +1 -5
  130. package/src/storage/script-store.ts +5 -13
  131. package/src/storage/sessions.ts +0 -3
  132. package/src/storage/skill-store.ts +3 -3
  133. package/src/util/exec-output.ts +1 -1
  134. package/src/util/mcp-launcher.ts +3 -0
  135. package/src/backend/kilo/events.ts +0 -60
  136. package/src/backend/kilo/handler/state.ts +0 -9
  137. package/src/backend/opencode/handler/state.ts +0 -9
  138. package/src/backend/opencode/handler/turn.ts +0 -314
@@ -1,23 +1,10 @@
1
1
  /**
2
- * Kilo main message handler.
3
- *
4
- * Orchestrates the full turn lifecycle on top of Kilo's HTTP API + SSE event
5
- * stream. Shares the non-SDK-specific primitives via `../../shared/`. The
6
- * Kilo-specific turn driver + SSE subscription live in `turn.ts`.
2
+ * Kilo main message handler — the shared remote-server chat turn bound to
3
+ * Kilo's server bindings.
7
4
  */
8
5
 
9
- import {
10
- getSession,
11
- incrementTurns,
12
- recordUsage,
13
- setSessionId,
14
- setSessionName,
15
- } from "../../../storage/sessions.js";
16
- import { getChatSettings } from "../../../storage/chat-settings.js";
17
- import { log, logError } from "../../../util/log.js";
18
- import { traceMessage } from "../../../util/trace.js";
19
6
  import type { QueryParams, QueryResult } from "../../shared/handler-types.js";
20
-
7
+ import { runRemoteChatTurn } from "../../remote-server/chat-turn.js";
21
8
  import {
22
9
  ensureServer,
23
10
  ensureSession,
@@ -29,308 +16,24 @@ import {
29
16
  getConfig,
30
17
  kiloSystemPromptSuffix,
31
18
  } from "../server.js";
32
- import {
33
- frontendsForChat,
34
- nonTerminalFrontends,
35
- } from "../../shared/frontends.js";
36
- import { getKiloTurnSummary } from "../sessions.js";
37
- import {
38
- createStreamState,
39
- recordTokens,
40
- finalizeResponseText,
41
- formatUserPrompt,
42
- prepareSystemPrompt,
43
- extractSessionName,
44
- summarizeUsage,
45
- routeDelivery,
46
- applyRetryDecision,
47
- recordTurnMetrics,
48
- recordFailedTurnAccounting,
49
- registerTurnInterrupt,
50
- } from "../../shared/index.js";
51
- import { activeSessions } from "./state.js";
52
- import { runKiloTurn } from "./turn.js";
53
-
54
- export async function handleMessage(
55
- params: QueryParams,
56
- _retried = false,
57
- ): Promise<QueryResult> {
58
- const config = getConfig();
59
- if (!config) throw new Error("Kilo agent not initialized");
60
-
61
- const {
62
- chatId,
63
- text,
64
- senderName,
65
- senderHandle,
66
- isGroup,
67
- messageId,
68
- onTextBlock,
69
- onToolUse,
70
- } = params;
71
- const t0 = Date.now();
72
- const session = getSession(chatId);
73
- const previousTurns = session.turns;
74
-
75
- // Resolve active model + provider lookup against Kilo's catalog
76
- const chatSettings = getChatSettings(chatId);
77
- const activeModel = params.model ?? chatSettings.model ?? config.model;
78
- const { providerID: selectedProviderID, modelID } =
79
- parseStoredKiloModelSelection(activeModel);
80
-
81
- const oc = await ensureServer();
82
- const providerID =
83
- selectedProviderID ?? (await resolveProviderID(oc, modelID));
84
- log(
85
- "agent",
86
- `[${chatId}] Kilo model resolved: provider=${providerID} model=${modelID}` +
87
- (selectedProviderID ? "" : " (provider via catalog lookup)"),
88
- );
89
- const sessionId = await ensureSession(oc, chatId);
90
- const chatMcpServerName = await ensureChatMcpServer(oc, chatId);
91
- const pluginMcpServerNames = await ensurePluginMcpServers(oc, chatId);
92
- const toolOverrides = await buildToolOverrides(
93
- oc,
94
- chatMcpServerName,
95
- pluginMcpServerNames,
96
- );
97
-
98
- // Build the prompt (time tag + sender + msg_id reference)
99
- const prompt = formatUserPrompt({
100
- text,
101
- senderName: senderName ?? "user",
102
- senderHandle,
103
- isGroup,
104
- messageId,
105
- });
106
-
107
- // Per-session frozen prompt + Kilo-specific delivery suffix
108
- const { text: systemPrompt } = prepareSystemPrompt({
109
- config,
110
- previousTurns,
111
- backendSuffix: kiloSystemPromptSuffix(
112
- frontendsForChat(chatId, nonTerminalFrontends(config.frontend))[0] ??
113
- "telegram",
114
- ),
115
- chatId,
116
- sessionEpoch: session.createdAt,
117
- });
118
-
119
- log("agent", `[${chatId}] <- (${text.length} chars)`);
120
- traceMessage(chatId, "in", text, { senderName, isGroup });
121
- activeSessions.set(chatId, sessionId);
122
-
123
- // Bind the stream state to the chat so token mutators mirror counts
124
- // into the live-turn overlay — /status updates while the turn runs.
125
- const state = createStreamState(chatId);
126
- state.newSessionId = sessionId;
127
- // A user interrupt is a synthetic turn terminator: marking the flag
128
- // before aborting the session routes the close through the same clean
129
- // path a model-fired end_turn takes (MessageAborted swallowed as the
130
- // expected close, no retry), settling with whatever the turn produced.
131
- const unregisterInterrupt = registerTurnInterrupt(chatId, async () => {
132
- state.turnTerminated = true;
133
- await oc.session.abort({ sessionID: sessionId });
134
- });
135
- const promptStartedAt = Date.now();
136
- const seenQuestionIds = new Set<string>();
137
- const seenPermissionIds = new Set<string>();
138
- const seenToolCallIds = new Set<string>();
139
-
140
- const setupMs = Date.now() - t0;
141
- let promptMs = 0;
142
-
143
- try {
144
- // Drive the Kilo turn: subscribe to SSE events in parallel with
145
- // promptAsync, surface tool calls + terminator into shared state, and
146
- // exit when the turn closes / goes idle. `onStreamDelta` is
147
- // intentionally NOT forwarded — Telegram's contract is "send the final
148
- // reply once", not live edit_message updates. Final delivery happens
149
- // through `end_turn` / `send`.
150
- const turnStart = Date.now();
151
- await runKiloTurn({
152
- oc,
153
- sessionId,
154
- prompt,
155
- systemPrompt,
156
- providerID,
157
- modelID,
158
- state,
159
- chatId,
160
- seenQuestionIds,
161
- seenPermissionIds,
162
- seenToolCallIds,
163
- toolOverrides,
164
- onStreamDelta: undefined,
165
- onTextBlock,
166
- onToolUse,
167
- });
168
- promptMs = Date.now() - turnStart;
169
- } catch (err) {
170
- const outcome = await applyRetryDecision({
171
- err,
172
- chatId,
173
- activeModel,
174
- retried: _retried,
175
- params,
176
- recurseWithRetried: (p) => handleMessage(p, true),
177
- backendLabel: "Kilo",
178
- });
179
- if (outcome.retry) return outcome.retry;
180
19
 
181
- // Terminal failure — account for whatever the turn consumed before
182
- // dying and drop the live overlay (the retry path above did its own
183
- // accounting inside the recursive attempt).
184
- recordFailedTurnAccounting({
185
- backend: "kilo",
186
- chatId,
187
- durationMs: Date.now() - t0,
188
- toolCalls: state.toolCalls,
189
- apiCalls: state.numApiCalls,
190
- model: activeModel,
191
- usage: {
192
- inputTokens: state.sdkInputTokens,
193
- outputTokens: state.sdkOutputTokens,
194
- cacheRead: state.sdkCacheRead,
195
- cacheWrite: state.sdkCacheWrite,
196
- },
197
- contextTokens: state.contextTokens,
198
- contextWindow: state.contextWindow,
199
- });
200
-
201
- logError("agent", `[${chatId}] Kilo error: ${outcome.classified.message}`);
202
- throw outcome.classified;
203
- } finally {
204
- unregisterInterrupt();
205
- if (activeSessions.get(chatId) === sessionId) {
206
- activeSessions.delete(chatId);
207
- }
208
- // Note: we deliberately do NOT disconnect the chat MCP server here.
209
- // The server is named per-chat so it's safe to keep across turns;
210
- // re-spawning the subprocess each turn cost ~800ms per message.
211
- // Per-prompt overrides isolate visibility across concurrent chats.
212
- }
213
-
214
- // ── Post-loop accounting ──────────────────────────────────────────────────
215
-
216
- // If the SSE loop missed any usage info, fall back to the session
217
- // summary endpoint (which always reflects the final server state).
218
- if (
219
- state.sdkInputTokens === 0 &&
220
- state.sdkOutputTokens === 0 &&
221
- state.sdkCacheRead === 0
222
- ) {
223
- try {
224
- const summary = await getKiloTurnSummary(oc, sessionId, promptStartedAt);
225
- if (summary.usage.assistantMessages > 0) {
226
- recordTokens(state, {
227
- inputTokens: summary.usage.inputTokens,
228
- outputTokens: summary.usage.outputTokens,
229
- cacheRead: summary.usage.cacheRead,
230
- cacheWrite: summary.usage.cacheWrite,
231
- });
232
- }
233
- } catch {
234
- // best-effort — Kilo session summaries can race on cancellation
235
- }
236
- }
237
-
238
- const responseText = finalizeResponseText(state);
239
- const durationMs = Date.now() - t0;
240
- recordTurnMetrics({
241
- chatId,
242
- backend: "kilo",
243
- durationMs,
244
- toolCalls: state.toolCalls,
245
- apiCalls: state.numApiCalls,
246
- usage: {
247
- inputTokens: state.sdkInputTokens,
248
- outputTokens: state.sdkOutputTokens,
249
- cacheRead: state.sdkCacheRead,
250
- cacheWrite: state.sdkCacheWrite,
20
+ export function handleMessage(params: QueryParams): Promise<QueryResult> {
21
+ // Assembled per call, not at module load: tests mock `../server.js`
22
+ // with a partial surface and only touch the members they need.
23
+ return runRemoteChatTurn(
24
+ {
25
+ id: "kilo",
26
+ label: "Kilo",
27
+ getConfig,
28
+ ensureServer,
29
+ parseModelSelection: parseStoredKiloModelSelection,
30
+ resolveProviderID,
31
+ ensureSession,
32
+ ensureChatMcpServer,
33
+ ensurePluginMcpServers,
34
+ buildToolOverrides,
35
+ systemPromptSuffix: kiloSystemPromptSuffix,
251
36
  },
252
- });
253
-
254
- if (state.newSessionId) {
255
- // setSessionId tolerates "same id" calls — keep it simple.
256
- const stored = getSession(chatId).sessionId;
257
- if (stored !== state.newSessionId) {
258
- setSessionId(chatId, state.newSessionId);
259
- }
260
- }
261
-
262
- incrementTurns(chatId);
263
- recordUsage(chatId, {
264
- inputTokens: state.sdkInputTokens,
265
- outputTokens: state.sdkOutputTokens,
266
- cacheRead: state.sdkCacheRead,
267
- cacheWrite: state.sdkCacheWrite,
268
- durationMs,
269
- model: activeModel,
270
- });
271
-
272
- // Set a descriptive session name from the user's first message
273
- if (previousTurns === 0) {
274
- const name = extractSessionName(text);
275
- if (name) setSessionName(chatId, name);
276
- }
277
-
278
- // ── Delivery — decision tree shared with the OpenCode backend ──────────────
279
- const delivery = await routeDelivery({
280
- backendLabel: "Kilo",
281
- chatId,
282
- state,
283
- responseText,
284
- onTextBlock,
285
- });
286
-
287
- log(
288
- "agent",
289
- `[${chatId}] delivery: ${delivery.route} (${delivery.chars} chars)`,
290
- );
291
-
292
- log(
293
- "agent",
294
- `[${chatId}] -> (${summarizeUsage(
295
- {
296
- inputTokens: state.sdkInputTokens,
297
- outputTokens: state.sdkOutputTokens,
298
- cacheRead: state.sdkCacheRead,
299
- cacheWrite: state.sdkCacheWrite,
300
- },
301
- { durationMs, toolCalls: state.toolCalls },
302
- )} terminator=${state.turnTerminated ? "yes" : "no"} ` +
303
- `delivered=${state.deliveredTextNorms.length} ` +
304
- `respLen=${responseText.length} ` +
305
- `setup=${setupMs}ms turn=${promptMs}ms ` +
306
- `events=${formatEventCounts(state.eventCounts)})`,
37
+ params,
307
38
  );
308
- traceMessage(chatId, "out", responseText, {
309
- durationMs,
310
- toolCalls: state.toolCalls,
311
- });
312
-
313
- return {
314
- text: responseText,
315
- durationMs,
316
- inputTokens: state.sdkInputTokens,
317
- outputTokens: state.sdkOutputTokens,
318
- cacheRead: state.sdkCacheRead,
319
- cacheWrite: state.sdkCacheWrite,
320
- };
321
- }
322
-
323
- /**
324
- * Compact summary of which SSE event types fired this turn. `none` for a
325
- * silent turn (itself a useful diagnostic — the SSE socket dropped or never
326
- * matched our session id). Otherwise renders as `delta×42,part.updated×1`.
327
- */
328
- function formatEventCounts(counts: Record<string, number>): string {
329
- const entries = Object.entries(counts);
330
- if (entries.length === 0) return "none";
331
- // Trim the noisy `message.` / `session.` prefixes so the line stays
332
- // readable in the live log tail.
333
- return entries
334
- .map(([type, n]) => `${type.replace(/^(message|session)\./, "")}×${n}`)
335
- .join(",");
336
39
  }
@@ -22,10 +22,6 @@
22
22
 
23
23
  // ── Models ─────────────────────────────────────────────────────────────────
24
24
  export {
25
- type OpenCodeModelCatalogEntry,
26
- type OpenCodeModelCatalog,
27
- type OpenCodeModelResolution,
28
- type ModelButton,
29
25
  getOpenCodeModelSelectionValue,
30
26
  resolveOpenCodeModelInput,
31
27
  getOpenCodeQuickPickModels,
@@ -36,8 +32,6 @@ export {
36
32
  export {
37
33
  summarizeKiloAssistantMessages,
38
34
  getKiloSessionSnapshot,
39
- type KiloAssistantInfo,
40
- type KiloSessionSnapshot,
41
35
  } from "./sessions.js";
42
36
 
43
37
  // ── Server / lifecycle ─────────────────────────────────────────────────────