talon-agent 5.1.0 → 5.2.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 (100) hide show
  1. package/README.md +3 -1
  2. package/bin/talon.js +35 -0
  3. package/package.json +2 -2
  4. package/prompts/identity.md +10 -2
  5. package/prompts/system/agent-brief.md +43 -0
  6. package/src/app.ts +60 -58
  7. package/src/backend/builtins.ts +26 -7
  8. package/src/backend/claude-sdk/one-shot.ts +13 -5
  9. package/src/backend/remote-server/index.ts +6 -4
  10. package/src/backend/remote-server/model-catalog/index.ts +4 -10
  11. package/src/backend/remote-server/model-catalog/provider.ts +3 -3
  12. package/src/backend/remote-server/profiles/bind.ts +225 -0
  13. package/src/backend/remote-server/profiles/index.ts +10 -0
  14. package/src/backend/remote-server/profiles/kilo.ts +82 -0
  15. package/src/backend/remote-server/profiles/opencode.ts +61 -0
  16. package/src/backend/remote-server/server-bindings.ts +3 -4
  17. package/src/bootstrap.ts +15 -1
  18. package/src/cli/chat.ts +5 -0
  19. package/src/cli/events.ts +9 -0
  20. package/src/core/agents/context.ts +48 -0
  21. package/src/core/agents/delivery.ts +167 -0
  22. package/src/core/agents/index.ts +37 -0
  23. package/src/core/agents/prompt.ts +116 -0
  24. package/src/core/agents/registry.ts +426 -0
  25. package/src/core/agents/runner.ts +448 -0
  26. package/src/core/agents/types.ts +124 -0
  27. package/src/core/background/cron/job-oneshot.ts +7 -12
  28. package/src/core/background/cron/job-prompt.ts +1 -1
  29. package/src/core/background/{cron/isolated-agent.ts → isolated-agent.ts} +45 -24
  30. package/src/core/background/run-log.ts +33 -0
  31. package/src/core/bus/events.ts +49 -2
  32. package/src/core/config/index.ts +25 -0
  33. package/src/core/daemon/crash.ts +82 -0
  34. package/src/core/engine/gateway-actions/agents/control.ts +299 -0
  35. package/src/core/engine/gateway-actions/agents/index.ts +31 -0
  36. package/src/core/engine/gateway-actions/agents/report.ts +107 -0
  37. package/src/core/engine/gateway-actions/index.ts +30 -0
  38. package/src/core/engine/gateway-actions/native/exec-remote.ts +1 -1
  39. package/src/core/engine/gateway-actions/native/exec.ts +1 -1
  40. package/src/core/engine/gateway-actions/native/read.ts +1 -1
  41. package/src/core/engine/gateway-actions/native/search.ts +1 -1
  42. package/src/core/engine/gateway-actions/native/teleport.ts +1 -1
  43. package/src/core/engine/gateway-actions/native/write.ts +1 -1
  44. package/src/core/engine/gateway-routes.ts +12 -0
  45. package/src/core/engine/gateway.ts +96 -25
  46. package/src/core/frontend-runtime/capabilities.ts +18 -0
  47. package/src/core/frontend-runtime/index.ts +4 -0
  48. package/src/core/frontend-runtime/lifecycle.ts +33 -0
  49. package/src/core/frontend-runtime/registry.ts +3 -3
  50. package/src/core/frontend-runtime/run-loop.ts +59 -0
  51. package/src/core/mesh/{registry.ts → devices/registry.ts} +4 -4
  52. package/src/core/mesh/{service.ts → devices/service.ts} +13 -10
  53. package/src/core/mesh/{teleport.ts → devices/teleport.ts} +2 -2
  54. package/src/core/mesh/index.ts +6 -2
  55. package/src/core/mesh/{bridge-links.ts → links/bridge-links.ts} +1 -1
  56. package/src/core/mesh/{companion-pairing.ts → links/companion-pairing.ts} +1 -1
  57. package/src/core/mesh/{node-binaries.ts → links/node-binaries.ts} +5 -5
  58. package/src/core/mesh/{node-provision.ts → links/node-provision.ts} +1 -1
  59. package/src/core/mesh/{common.ts → tool-surface.ts} +7 -2
  60. package/src/core/mesh/{device-files.ts → transfers/device-files.ts} +5 -5
  61. package/src/core/prompt/embedded-prompts.ts +38 -36
  62. package/src/core/tasks/types.ts +2 -2
  63. package/src/core/tools/index.ts +5 -0
  64. package/src/core/tools/ops/agents.ts +195 -0
  65. package/src/core/tools/ops/bridge.ts +4 -0
  66. package/src/core/tools/types.ts +1 -0
  67. package/src/core/types.ts +1 -1
  68. package/src/frontend/discord/commands/info.ts +1 -1
  69. package/src/frontend/discord/render.ts +1 -1
  70. package/src/frontend/native/bridge/routes/mesh.ts +1 -1
  71. package/src/frontend/native/index.ts +2 -0
  72. package/src/frontend/presentation/reports.ts +1 -1
  73. package/src/frontend/teams/index.ts +4 -3
  74. package/src/frontend/telegram/commands/info.ts +61 -20
  75. package/src/frontend/telegram/index.ts +27 -4
  76. package/src/frontend/telegram/render/reports.ts +1 -1
  77. package/src/frontend/terminal/index.ts +6 -2
  78. package/src/frontend/whatsapp/connection/connection.ts +62 -11
  79. package/src/frontend/whatsapp/index.ts +25 -1
  80. package/src/frontend/whatsapp/runtime.ts +7 -0
  81. package/src/util/log.ts +264 -18
  82. package/src/backend/kilo/factory.ts +0 -53
  83. package/src/backend/kilo/handler/index.ts +0 -2
  84. package/src/backend/kilo/handler/message.ts +0 -44
  85. package/src/backend/kilo/index.ts +0 -61
  86. package/src/backend/kilo/model-provider.ts +0 -36
  87. package/src/backend/kilo/models/index.ts +0 -55
  88. package/src/backend/kilo/one-shot.ts +0 -42
  89. package/src/backend/kilo/server.ts +0 -98
  90. package/src/backend/kilo/sessions.ts +0 -37
  91. package/src/backend/opencode/factory.ts +0 -53
  92. package/src/backend/opencode/handler/index.ts +0 -2
  93. package/src/backend/opencode/handler/message.ts +0 -44
  94. package/src/backend/opencode/index.ts +0 -42
  95. package/src/backend/opencode/model-provider.ts +0 -36
  96. package/src/backend/opencode/models/index.ts +0 -54
  97. package/src/backend/opencode/one-shot.ts +0 -42
  98. package/src/backend/opencode/server.ts +0 -80
  99. package/src/backend/opencode/sessions.ts +0 -35
  100. /package/src/core/mesh/{transfers.ts → transfers/transfers.ts} +0 -0
package/src/util/log.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  unlinkSync,
19
19
  createWriteStream,
20
20
  } from "node:fs";
21
+ import { Writable } from "node:stream";
21
22
  import { dirs, files } from "./paths.js";
22
23
 
23
24
  export type LogComponent =
@@ -30,6 +31,7 @@ export type LogComponent =
30
31
  | "media"
31
32
  | "notify"
32
33
  | "agent"
34
+ | "agents"
33
35
  | "pulse"
34
36
  | "userbot"
35
37
  | "users"
@@ -126,23 +128,249 @@ const streams: pino.StreamEntry[] = [];
126
128
 
127
129
  // Console output (disabled in quiet mode), pretty-printed.
128
130
  if (!quiet) {
129
- streams.push({
130
- level: "trace",
131
- stream: prettyStream({
132
- colorize: true,
133
- ignore: "pid,hostname",
134
- translateTime: "HH:MM:ss",
135
- }),
131
+ const consoleStream = prettyStream({
132
+ colorize: true,
133
+ ignore: "pid,hostname",
134
+ translateTime: "HH:MM:ss",
136
135
  });
136
+ // pino-pretty writes through a SonicBoom on fd 1 whose own error
137
+ // handler removes itself after the first non-EPIPE failure
138
+ // (pino-pretty/lib/utils/build-safe-sonic-boom.js). With stdout
139
+ // redirected to a file on a full disk that leaves the pipeline's next
140
+ // error unhandled — i.e. an uncaught exception raised from inside a
141
+ // log call. One permanent listener closes that door; a dead console
142
+ // is not worth a dead daemon.
143
+ consoleStream.on("error", () => {});
144
+ streams.push({ level: "trace", stream: consoleStream });
145
+ }
146
+
147
+ /** How long to wait before the first attempt to reopen a failed log file. */
148
+ const SINK_RETRY_MS = 30_000;
149
+ /** Ceiling for the doubling backoff between reopen attempts. */
150
+ const SINK_MAX_RETRY_MS = 5 * 60_000;
151
+
152
+ export type ResilientFileSinkOptions = {
153
+ /** Opens the underlying file stream. Injection seam for tests. */
154
+ open?: (path: string) => Writable;
155
+ /** Where the pause/resume notices go. Defaults to the console sink. */
156
+ notify?: (level: "warn" | "info", message: string) => void;
157
+ /** First backoff step (default 30s). */
158
+ retryMs?: number;
159
+ /** Backoff ceiling (default 5 min). */
160
+ maxRetryMs?: number;
161
+ };
162
+
163
+ /**
164
+ * A log file destination that cannot take the process down.
165
+ *
166
+ * A bare `createWriteStream` handed to `pino.multistream` is a loaded
167
+ * gun: when the disk fills (ENOSPC), or the file is unlinked under a
168
+ * rotation (EBADF), or the fd goes bad (EIO), the stream emits `error`.
169
+ * With no listener that is an uncaught exception — and it fires from
170
+ * inside a log call, so the crash handler's own `logError` runs on a
171
+ * logger that is already broken. That is how a full disk killed the
172
+ * daemon on 2026-09-18: the process aborted mid-`/update` with no
173
+ * shutdown, no pidfile cleanup, and no successor.
174
+ *
175
+ * This sink owns the file stream instead of exposing it. pino only ever
176
+ * sees this object, which never emits `error` and never blocks:
177
+ * - write failures pause file logging and destroy the broken stream,
178
+ * - lines written while paused are DROPPED and counted (never
179
+ * buffered — the failure mode here is "no space", so growing a
180
+ * buffer is the last thing to do),
181
+ * - an unref'd timer retries the open on a 30s → 5min backoff,
182
+ * - the first write to land again resumes logging and reports how
183
+ * many lines were lost.
184
+ * The console sink keeps working throughout, and carries the two
185
+ * notices.
186
+ */
187
+ export class ResilientFileSink extends Writable {
188
+ private readonly path: string;
189
+ private readonly openStream: (path: string) => Writable;
190
+ private readonly notify: (level: "warn" | "info", message: string) => void;
191
+ private readonly baseRetryMs: number;
192
+ private readonly maxRetryMs: number;
193
+ private inner: Writable | null = null;
194
+ private retryMs: number;
195
+ private retryTimer: ReturnType<typeof setTimeout> | null = null;
196
+ private droppedWhileDown = 0;
197
+ private down = false;
198
+
199
+ constructor(path: string, opts: ResilientFileSinkOptions = {}) {
200
+ // decodeStrings:false keeps pino's serialized lines as strings;
201
+ // autoDestroy:false means a downstream failure can never tear this
202
+ // object down — it is the only thing standing between a broken file
203
+ // and the process.
204
+ super({ decodeStrings: false, autoDestroy: false });
205
+ this.path = path;
206
+ this.openStream = opts.open ?? openLogFile;
207
+ this.notify = opts.notify ?? notifyViaConsoleSink;
208
+ this.baseRetryMs = opts.retryMs ?? SINK_RETRY_MS;
209
+ this.maxRetryMs = opts.maxRetryMs ?? SINK_MAX_RETRY_MS;
210
+ this.retryMs = this.baseRetryMs;
211
+ this.openInner();
212
+ }
213
+
214
+ /** Lines discarded since the sink went down; cleared on recovery. */
215
+ get dropped(): number {
216
+ return this.droppedWhileDown;
217
+ }
218
+
219
+ /** True while file logging is paused (the console sink still runs). */
220
+ get isDown(): boolean {
221
+ return this.down;
222
+ }
223
+
224
+ override _write(
225
+ chunk: unknown,
226
+ _encoding: BufferEncoding,
227
+ callback: (error?: Error | null) => void,
228
+ ): void {
229
+ const inner = this.inner;
230
+ if (inner === null) {
231
+ this.droppedWhileDown++;
232
+ } else {
233
+ try {
234
+ inner.write(chunk as string, (err) => {
235
+ if (!err) this.markHealthy();
236
+ });
237
+ } catch (err) {
238
+ // Synchronous throw (write-after-destroy on a stream we have not
239
+ // been told about yet) — same handling as an `error` event.
240
+ this.fail(err);
241
+ }
242
+ }
243
+ // Always report success: pino must never see this sink fail, and
244
+ // backpressure here would stall whoever called log().
245
+ callback();
246
+ }
247
+
248
+ override _final(callback: (error?: Error | null) => void): void {
249
+ this.clearRetry();
250
+ try {
251
+ this.inner?.end();
252
+ } catch {
253
+ /* going away anyway */
254
+ }
255
+ callback();
256
+ }
257
+
258
+ override _destroy(
259
+ _err: Error | null,
260
+ callback: (error?: Error | null) => void,
261
+ ): void {
262
+ this.clearRetry();
263
+ this.detachInner();
264
+ callback(null);
265
+ }
266
+
267
+ private openInner(): void {
268
+ try {
269
+ const inner = this.openStream(this.path);
270
+ inner.on("error", (err: Error) => {
271
+ // Ignore errors from a stream we have already given up on.
272
+ if (this.inner === inner) this.fail(err);
273
+ });
274
+ this.inner = inner;
275
+ } catch (err) {
276
+ this.fail(err);
277
+ }
278
+ }
279
+
280
+ /** Give up on the current stream and arm a reopen. Never throws. */
281
+ private fail(err: unknown): void {
282
+ const firstFailure = !this.down;
283
+ this.detachInner();
284
+ this.down = true;
285
+ const delay = this.retryMs;
286
+ this.retryMs = Math.min(this.retryMs * 2, this.maxRetryMs);
287
+ this.scheduleRetry(delay);
288
+ if (!firstFailure) return; // a failed reopen is not news
289
+ const code =
290
+ (err as NodeJS.ErrnoException | undefined)?.code ??
291
+ (err instanceof Error ? err.message : String(err));
292
+ this.emitNotice(
293
+ "warn",
294
+ `Log file sink failed (${code}) — file logging paused, ` +
295
+ `retrying in ${Math.round(delay / 1000)}s`,
296
+ );
297
+ }
298
+
299
+ /** A write landed: the file is usable again. */
300
+ private markHealthy(): void {
301
+ if (!this.down) return;
302
+ this.down = false;
303
+ this.retryMs = this.baseRetryMs;
304
+ const dropped = this.droppedWhileDown;
305
+ this.droppedWhileDown = 0;
306
+ this.emitNotice(
307
+ "info",
308
+ `Log file sink recovered — file logging resumed ` +
309
+ `(${dropped} line(s) dropped while it was down)`,
310
+ );
311
+ }
312
+
313
+ private detachInner(): void {
314
+ const inner = this.inner;
315
+ this.inner = null;
316
+ if (inner === null) return;
317
+ try {
318
+ inner.removeAllListeners("error");
319
+ // destroy() can surface one last error — swallow it here rather
320
+ // than let it reach process-level uncaughtException.
321
+ inner.on("error", () => {});
322
+ inner.destroy();
323
+ } catch {
324
+ /* best effort */
325
+ }
326
+ }
327
+
328
+ private scheduleRetry(delay: number): void {
329
+ this.clearRetry();
330
+ const timer = setTimeout(() => {
331
+ this.retryTimer = null;
332
+ this.openInner();
333
+ }, delay);
334
+ // A paused log sink must not hold the event loop open.
335
+ timer.unref();
336
+ this.retryTimer = timer;
337
+ }
338
+
339
+ private clearRetry(): void {
340
+ if (this.retryTimer === null) return;
341
+ clearTimeout(this.retryTimer);
342
+ this.retryTimer = null;
343
+ }
344
+
345
+ /**
346
+ * Notices are deferred: `fail()` can run inside pino's multistream
347
+ * write loop, and logging re-entrantly from there would scramble the
348
+ * metadata pino hangs off the stream for the rest of that loop.
349
+ */
350
+ private emitNotice(level: "warn" | "info", message: string): void {
351
+ queueMicrotask(() => {
352
+ try {
353
+ this.notify(level, message);
354
+ } catch {
355
+ /* the notice is the least important thing here */
356
+ }
357
+ });
358
+ }
359
+ }
360
+
361
+ function openLogFile(path: string): Writable {
362
+ // 0600: turns and tool output land here — same sensitivity as history.
363
+ return createWriteStream(path, { flags: "a", mode: 0o600 });
364
+ }
365
+
366
+ function notifyViaConsoleSink(level: "warn" | "info", message: string): void {
367
+ if (level === "warn") logWarn("file", message);
368
+ else log("file", message);
137
369
  }
138
370
 
139
371
  // JSON file output (always active outside test runs).
140
372
  if (!IS_VITEST) {
141
- streams.push({
142
- level: "trace",
143
- // 0600: turns and tool output land here — same sensitivity as history.
144
- stream: createWriteStream(LOG_FILE, { flags: "a", mode: 0o600 }),
145
- });
373
+ streams.push({ level: "trace", stream: new ResilientFileSink(LOG_FILE) });
146
374
  }
147
375
 
148
376
  const logger =
@@ -150,8 +378,24 @@ const logger =
150
378
  ? pino({ level: "trace" }, pino.multistream(streams))
151
379
  : pino({ level: "silent" });
152
380
 
381
+ /**
382
+ * Emit one record. A logger that throws is worse than a silent one: the
383
+ * throw lands on whoever called log(), which at shutdown is a signal
384
+ * handler or a crash handler, and a throw there takes the daemon down —
385
+ * pino-pretty's SonicBoom, for one, throws "SonicBoom destroyed"
386
+ * synchronously on every write once a failure has destroyed it. Nothing
387
+ * a sink does may escape this module.
388
+ */
389
+ function emit(write: () => void): void {
390
+ try {
391
+ write();
392
+ } catch {
393
+ /* a broken logger must never become a broken daemon */
394
+ }
395
+ }
396
+
153
397
  export function log(component: LogComponent, message: string): void {
154
- logger.info({ component }, message);
398
+ emit(() => logger.info({ component }, message));
155
399
  }
156
400
 
157
401
  export function logError(
@@ -163,20 +407,22 @@ export function logError(
163
407
  // Capture both the concise message (for log consumers that look at `err`)
164
408
  // and the full stack (for diagnostics). pino-pretty renders the `stack`
165
409
  // field on its own line; JSON consumers can read either field.
166
- logger.error({ component, err: err.message, stack: err.stack }, message);
410
+ emit(() =>
411
+ logger.error({ component, err: err.message, stack: err.stack }, message),
412
+ );
167
413
  } else if (err !== undefined) {
168
- logger.error({ component, err: String(err) }, message);
414
+ emit(() => logger.error({ component, err: String(err) }, message));
169
415
  } else {
170
- logger.error({ component }, message);
416
+ emit(() => logger.error({ component }, message));
171
417
  }
172
418
  }
173
419
 
174
420
  export function logWarn(component: LogComponent, message: string): void {
175
- logger.warn({ component }, message);
421
+ emit(() => logger.warn({ component }, message));
176
422
  }
177
423
 
178
424
  export function logDebug(component: LogComponent, message: string): void {
179
- logger.debug({ component }, message);
425
+ emit(() => logger.debug({ component }, message));
180
426
  }
181
427
 
182
428
  // Expose logger to plugins running in the same process
@@ -1,53 +0,0 @@
1
- /**
2
- * Kilo backend factory — wires Kilo into the registry.
3
- *
4
- * Side-effect import (`import "./factory.js"` from `bootstrap.ts`) calls
5
- * `registerBackend(...)` at module load, making Kilo available under
6
- * `config.backend === "kilo"`. The capability wiring is the shared
7
- * remote-server composition; only the bound functions are Kilo's.
8
- */
9
-
10
- import { registerBackend } from "../../core/agent-runtime/backend-registry.js";
11
- import { createRemoteBackendFactory } from "../remote-server/factory.js";
12
- import {
13
- initKiloAgent,
14
- stopKiloServer,
15
- handleMessage,
16
- runOneShotAgent,
17
- getKiloSessionSnapshot,
18
- resolveModel,
19
- getModelInfo,
20
- getSettingsPresentation,
21
- getProviders,
22
- getProviderModels,
23
- formatModelError,
24
- listModels,
25
- refreshPluginMcpServers,
26
- updateSystemPrompt,
27
- warmSession,
28
- } from "./index.js";
29
-
30
- registerBackend(
31
- createRemoteBackendFactory({
32
- id: "kilo",
33
- label: "Kilo",
34
- sdkPackage: "@kilocode/sdk",
35
- init: initKiloAgent,
36
- stop: stopKiloServer,
37
- handleMessage,
38
- runOneShotAgent,
39
- getSessionSnapshot: getKiloSessionSnapshot,
40
- models: {
41
- resolveModel,
42
- getModelInfo,
43
- getSettingsPresentation,
44
- getProviders,
45
- getProviderModels,
46
- formatModelError,
47
- listModels,
48
- },
49
- refreshPluginMcpServers,
50
- warmSession,
51
- updateSystemPrompt,
52
- }),
53
- );
@@ -1,2 +0,0 @@
1
- /** Kilo message handler barrel. */
2
- export { handleMessage } from "./message.js";
@@ -1,44 +0,0 @@
1
- /**
2
- * Kilo main message handler — the shared remote-server chat turn bound to
3
- * Kilo's server bindings.
4
- */
5
-
6
- import type {
7
- QueryParams,
8
- QueryResult,
9
- } from "../../runtime/turn/handler-types.js";
10
- import { runRemoteChatTurn } from "../../remote-server/chat-turn.js";
11
- import {
12
- ensureServer,
13
- trackActiveTurn,
14
- ensureSession,
15
- ensureChatMcpServer,
16
- ensurePluginMcpServers,
17
- buildToolOverrides,
18
- resolveProviderID,
19
- parseStoredKiloModelSelection,
20
- getConfig,
21
- kiloSystemPromptSuffix,
22
- } from "../server.js";
23
-
24
- export function handleMessage(params: QueryParams): Promise<QueryResult> {
25
- // Assembled per call, not at module load: tests mock `../server.js`
26
- // with a partial surface and only touch the members they need.
27
- return runRemoteChatTurn(
28
- {
29
- id: "kilo",
30
- label: "Kilo",
31
- getConfig,
32
- ensureServer,
33
- trackActiveTurn,
34
- parseModelSelection: parseStoredKiloModelSelection,
35
- resolveProviderID,
36
- ensureSession,
37
- ensureChatMcpServer,
38
- ensurePluginMcpServers,
39
- buildToolOverrides,
40
- systemPromptSuffix: kiloSystemPromptSuffix,
41
- },
42
- params,
43
- );
44
- }
@@ -1,61 +0,0 @@
1
- /**
2
- * Kilo backend — barrel re-export.
3
- *
4
- * Uses the `@kilocode/sdk` (a fork of `@opencode-ai/sdk`) and exposes a
5
- * Backend-compatible API. The implementation is split across
6
- * focused modules for readability:
7
- *
8
- * - `models.ts` — model catalog, search, resolution, presentation
9
- * - `sessions.ts` — message parsing, usage summaries, snapshots
10
- * - `server.ts` — server lifecycle, MCP, session management
11
- * - `events.ts` — Kilo-side configurator over shared SSE events
12
- * - `handler.ts` — main message handler (streaming, end_turn,
13
- * delivery routing)
14
- * - `one-shot.ts` — heartbeat / dream one-shot runner
15
- * - `model-provider.ts` — adapts the catalog to the Backend interface
16
- *
17
- * Note: internal `OpenCode*` type names in `models.ts` (e.g.
18
- * `OpenCodeModelCatalogEntry`) are retained on purpose — Kilo's
19
- * provider-bucket API is forked from OpenCode's wire shape, so the
20
- * names match what the upstream actually emits.
21
- */
22
-
23
- // ── Models ─────────────────────────────────────────────────────────────────
24
- export {
25
- getOpenCodeModelSelectionValue,
26
- resolveOpenCodeModelInput,
27
- getOpenCodeQuickPickModels,
28
- formatOpenCodeSelectionError,
29
- } from "./models/index.js";
30
-
31
- // ── Sessions ───────────────────────────────────────────────────────────────
32
- export {
33
- summarizeKiloAssistantMessages,
34
- getKiloSessionSnapshot,
35
- } from "./sessions.js";
36
-
37
- // ── Server / lifecycle ─────────────────────────────────────────────────────
38
- export {
39
- initKiloAgent,
40
- stopKiloServer,
41
- refreshPluginMcpServers,
42
- updateSystemPrompt,
43
- warmSession,
44
- } from "./server.js";
45
-
46
- // ── Handler ────────────────────────────────────────────────────────────────
47
- export { handleMessage } from "./handler/index.js";
48
-
49
- // ── Model provider (Backend adapter) ──────────────────────────────────
50
- export {
51
- resolveModel,
52
- getModelInfo,
53
- getSettingsPresentation,
54
- getProviders,
55
- getProviderModels,
56
- listModels,
57
- formatModelError,
58
- } from "./model-provider.js";
59
-
60
- // ── One-shot agent runner ──────────────────────────────────────────────────
61
- export { runOneShotAgent } from "./one-shot.js";
@@ -1,36 +0,0 @@
1
- /**
2
- * Kilo model provider — adapts the internal catalog to the Backend model
3
- * interface via the shared remote-server factory. Each export matches a
4
- * method on `Backend.models`.
5
- */
6
-
7
- import { createRemoteModelProvider } from "../remote-server/model-catalog/index.js";
8
- import {
9
- getOpenCodeModelCatalog,
10
- getOpenCodeModelInfo,
11
- getOpenCodeModelSelectionValue,
12
- resolveOpenCodeModelInput,
13
- getOpenCodeSettingsPresentation,
14
- formatOpenCodeUnavailableModel,
15
- } from "./models/index.js";
16
-
17
- const provider = createRemoteModelProvider({
18
- label: "Kilo",
19
- getCatalog: (forceRefresh) => getOpenCodeModelCatalog(forceRefresh),
20
- getModelInfo: (id) => getOpenCodeModelInfo(id),
21
- resolveModelInput: (query, catalog) =>
22
- resolveOpenCodeModelInput(query, catalog),
23
- getSelectionValue: (model, catalog) =>
24
- getOpenCodeModelSelectionValue(model, catalog),
25
- formatUnavailableModel: (model) => formatOpenCodeUnavailableModel(model),
26
- getSettingsPresentation: (activeModel, callbackPrefix) =>
27
- getOpenCodeSettingsPresentation(activeModel, callbackPrefix),
28
- });
29
-
30
- export const resolveModel = provider.resolveModel;
31
- export const getModelInfo = provider.getModelInfo;
32
- export const getSettingsPresentation = provider.getSettingsPresentation;
33
- export const getProviders = provider.getProviders;
34
- export const getProviderModels = provider.getProviderModels;
35
- export const listModels = provider.listModels;
36
- export const formatModelError = provider.formatModelError;
@@ -1,55 +0,0 @@
1
- /**
2
- * Kilo model catalog — binder over the shared remote-server model-catalog
3
- * module (see `backend/remote-server/model-catalog/`).
4
- *
5
- * Kilo's provider-bucket API is forked from OpenCode's, so the catalog,
6
- * resolution, and presentation logic is the shared implementation; this file
7
- * only binds the Kilo SDK client + presentation knobs and re-exports the
8
- * result under the historical `OpenCode*`-prefixed names (retained on
9
- * purpose — the names match the wire shape the upstream actually emits).
10
- *
11
- * Knobs: Kilo's model picker renders through Discord StringSelectMenus,
12
- * which allow 25 options and values up to 100 chars with any characters —
13
- * Kilo ids routinely contain "/" and ":" (e.g. "inclusionai/ling-2.6-1t:free").
14
- */
15
-
16
- import { ensureServer, onServerStop } from "../server.js";
17
- import { createRemoteModelCatalogModule } from "../../remote-server/model-catalog/index.js";
18
-
19
- export type {
20
- RemoteModelCatalogEntry as OpenCodeModelCatalogEntry,
21
- RemoteModelCatalog as OpenCodeModelCatalog,
22
- RemoteModelResolution as OpenCodeModelResolution,
23
- ModelButton,
24
- } from "../../remote-server/model-catalog/index.js";
25
- export {
26
- sortCatalogModels,
27
- getRemoteModelSelectionValue as getOpenCodeModelSelectionValue,
28
- resolveRemoteModelInput as resolveOpenCodeModelInput,
29
- guessProviderID,
30
- getBucketPriority,
31
- normalizeModelLookup,
32
- parseRemoteModelQuery as parseOpenCodeModelQuery,
33
- formatRemoteUnavailableModel as formatOpenCodeUnavailableModel,
34
- } from "../../remote-server/model-catalog/index.js";
35
-
36
- const kiloModels = createRemoteModelCatalogModule({
37
- label: "Kilo",
38
- getClient: () => ensureServer(),
39
- maxCallbackIdLength: 90,
40
- allowCallbackSeparators: true,
41
- quickPickLimit: 24,
42
- });
43
-
44
- // A stopped server invalidates the catalog it served.
45
- onServerStop(kiloModels.clearCache);
46
-
47
- export const getOpenCodeModelCatalog = kiloModels.getCatalog;
48
- export const clearModelCatalogCache = kiloModels.clearCache;
49
- export const getOpenCodeModelInfo = kiloModels.getModelInfo;
50
- export const getOpenCodeQuickPickModels = kiloModels.getQuickPickModels;
51
- export const getOpenCodeSettingsPresentation =
52
- kiloModels.getSettingsPresentation;
53
- export const renderOpenCodeModelSummary = kiloModels.renderModelSummary;
54
- export const renderOpenCodeModelList = kiloModels.renderModelList;
55
- export const formatOpenCodeSelectionError = kiloModels.formatSelectionError;
@@ -1,42 +0,0 @@
1
- /**
2
- * Kilo one-shot agent runner — used by heartbeat & dream.
3
- *
4
- * Thin binding over the shared remote-server runner (see
5
- * `backend/remote-server/one-shot.ts` for the design notes): Kilo
6
- * supplies its server bootstrap, model-selection parser, and delivery
7
- * suffix; everything else is the shared lifecycle.
8
- */
9
-
10
- import type { OneShotAgentParams, OneShotUsage } from "../../core/types.js";
11
- import { runRemoteOneShotAgent } from "../remote-server/one-shot.js";
12
- import {
13
- ensureServer,
14
- ensureChatMcpServer,
15
- ensurePluginMcpServers,
16
- buildToolOverrides,
17
- disconnectChatMcpServer,
18
- resolveProviderID,
19
- parseStoredKiloModelSelection,
20
- KILO_SYSTEM_PROMPT_SUFFIX,
21
- errMsg,
22
- } from "./server.js";
23
-
24
- export function runOneShotAgent(
25
- params: OneShotAgentParams,
26
- ): Promise<OneShotUsage | void> {
27
- return runRemoteOneShotAgent(
28
- {
29
- label: "Kilo",
30
- systemPromptSuffix: KILO_SYSTEM_PROMPT_SUFFIX,
31
- ensureServer,
32
- parseModelSelection: parseStoredKiloModelSelection,
33
- resolveProviderID,
34
- ensureChatMcpServer,
35
- ensurePluginMcpServers,
36
- buildToolOverrides,
37
- disconnectChatMcpServer,
38
- errMsg,
39
- },
40
- params,
41
- );
42
- }