talon-agent 4.6.0 → 5.0.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.
Files changed (225) hide show
  1. package/README.md +3 -3
  2. package/package.json +3 -2
  3. package/prompts/README.md +2 -2
  4. package/prompts/system/memory-recall.md +16 -0
  5. package/src/app.ts +19 -4
  6. package/src/backend/claude-sdk/handler.ts +28 -7
  7. package/src/backend/claude-sdk/one-shot.ts +1 -1
  8. package/src/backend/claude-sdk/options.ts +7 -6
  9. package/src/backend/claude-sdk/stream.ts +30 -3
  10. package/src/backend/claude-sdk/warm.ts +1 -1
  11. package/src/backend/codex/constants.ts +1 -1
  12. package/src/backend/codex/factory.ts +2 -2
  13. package/src/backend/codex/handler/events.ts +1 -1
  14. package/src/backend/codex/handler/message.ts +26 -12
  15. package/src/backend/codex/handler/rollout-accounting.ts +1 -1
  16. package/src/backend/codex/init.ts +1 -1
  17. package/src/backend/codex/mcp-config.ts +1 -1
  18. package/src/backend/codex/one-shot.ts +1 -1
  19. package/src/backend/kilo/handler/message.ts +4 -1
  20. package/src/backend/openai-agents/constants.ts +1 -1
  21. package/src/backend/openai-agents/factory.ts +2 -2
  22. package/src/backend/openai-agents/handler/events.ts +1 -1
  23. package/src/backend/openai-agents/handler/message.ts +8 -4
  24. package/src/backend/openai-agents/init.ts +1 -1
  25. package/src/backend/openai-agents/mcp-pool.ts +1 -1
  26. package/src/backend/opencode/handler/message.ts +4 -1
  27. package/src/backend/remote-server/chat-turn.ts +31 -24
  28. package/src/backend/remote-server/events.ts +3 -3
  29. package/src/backend/remote-server/factory.ts +6 -3
  30. package/src/backend/remote-server/index.ts +1 -1
  31. package/src/backend/remote-server/mcp.ts +1 -1
  32. package/src/backend/remote-server/one-shot.ts +1 -1
  33. package/src/backend/remote-server/server-bindings.ts +1 -1
  34. package/src/backend/remote-server/turn.ts +1 -1
  35. package/src/backend/runtime/cache/cache-metrics.ts +126 -0
  36. package/src/backend/{shared → runtime/cache}/cache-telemetry.ts +22 -2
  37. package/src/backend/{shared → runtime}/index.ts +32 -23
  38. package/src/backend/{shared → runtime/prompt}/delivery-contract.ts +1 -1
  39. package/src/backend/{shared → runtime/prompt}/prompt-format.ts +46 -2
  40. package/src/backend/{shared → runtime/prompt}/system-prompt.ts +4 -4
  41. package/src/backend/{shared → runtime/turn}/delivered-text.ts +1 -1
  42. package/src/backend/{shared → runtime/turn}/delivery.ts +2 -2
  43. package/src/backend/{shared → runtime/turn}/handle-retry.ts +5 -5
  44. package/src/backend/{shared → runtime/turn}/handler-to-events.ts +24 -10
  45. package/src/backend/{shared → runtime/turn}/handler-types.ts +7 -1
  46. package/src/backend/{shared → runtime/turn}/model-retry.ts +2 -2
  47. package/src/backend/{shared → runtime/turn}/result-events.ts +2 -2
  48. package/src/backend/{shared → runtime/turn}/stream-state.ts +2 -2
  49. package/src/backend/{shared → runtime/turn}/turn-interrupt.ts +2 -2
  50. package/src/backend/{shared → runtime/turn}/turn-phases.ts +6 -6
  51. package/src/bootstrap.ts +4 -19
  52. package/src/cli.ts +1 -1
  53. package/src/core/agent-runtime/capabilities.ts +11 -0
  54. package/src/core/agent-runtime/contract-tests.ts +92 -1
  55. package/src/core/agent-runtime/events.ts +1 -1
  56. package/src/core/background/{isolated-agent.ts → cron/isolated-agent.ts} +3 -3
  57. package/src/core/background/{job-health.ts → cron/job-health.ts} +1 -1
  58. package/src/core/background/{job-oneshot.ts → cron/job-oneshot.ts} +5 -5
  59. package/src/core/background/{job-prompt.ts → cron/job-prompt.ts} +1 -1
  60. package/src/core/background/{cron.ts → cron/scheduler.ts} +6 -6
  61. package/src/core/background/{cron-spec.ts → cron/spec.ts} +1 -1
  62. package/src/core/background/{dream.ts → dream/index.ts} +10 -15
  63. package/src/core/background/{plan-alerts.ts → pulse/plan-alerts.ts} +3 -3
  64. package/src/core/background/{pulse.ts → pulse/pulse.ts} +8 -5
  65. package/src/core/background/triggers/command.ts +1 -1
  66. package/src/core/config/index.ts +15 -11
  67. package/src/core/daemon/resource-sampler.ts +121 -0
  68. package/src/core/engine/dispatcher.ts +16 -0
  69. package/src/core/engine/gateway-actions/cron.ts +2 -2
  70. package/src/core/engine/gateway-actions/index.ts +3 -0
  71. package/src/core/engine/gateway-actions/memory.ts +335 -0
  72. package/src/core/engine/gateway.ts +0 -6
  73. package/src/core/memory/import.ts +3 -2
  74. package/src/core/memory/taps.ts +199 -0
  75. package/src/core/memory/turn-retrieval.ts +222 -0
  76. package/src/core/prompt/assemble.ts +2 -12
  77. package/src/core/prompt/index.ts +2 -2
  78. package/src/core/prompt/invalidation.ts +1 -1
  79. package/src/core/tasks/index.ts +1 -1
  80. package/src/core/tools/index.ts +2 -0
  81. package/src/core/tools/memory.ts +128 -0
  82. package/src/core/tools/types.ts +1 -0
  83. package/src/core/weaver/turn-cpu.ts +32 -0
  84. package/src/core/weaver/weaver.ts +36 -0
  85. package/src/frontend/discord/admin.ts +1 -1
  86. package/src/frontend/discord/callbacks/components/backend-select.ts +1 -1
  87. package/src/frontend/discord/callbacks/components/pulse.ts +1 -1
  88. package/src/frontend/discord/callbacks/components/settings.ts +1 -1
  89. package/src/frontend/discord/callbacks/modals.ts +4 -1
  90. package/src/frontend/discord/commands/admin.ts +1 -35
  91. package/src/frontend/discord/commands/definitions.ts +0 -11
  92. package/src/frontend/discord/commands/router.ts +0 -3
  93. package/src/frontend/discord/commands/session.ts +6 -0
  94. package/src/frontend/discord/commands/settings.ts +1 -1
  95. package/src/frontend/discord/middleware.ts +1 -25
  96. package/src/frontend/discord/runtime.ts +1 -2
  97. package/src/frontend/native/{auth.ts → bridge/auth.ts} +2 -2
  98. package/src/frontend/native/{discovery.ts → bridge/discovery.ts} +3 -3
  99. package/src/frontend/native/{routes → bridge/routes}/chats.ts +1 -1
  100. package/src/frontend/native/{routes → bridge/routes}/daemon.ts +1 -1
  101. package/src/frontend/native/{routes → bridge/routes}/host.ts +6 -3
  102. package/src/frontend/native/{routes → bridge/routes}/pre-auth.ts +1 -1
  103. package/src/frontend/native/{server.ts → bridge/server.ts} +3 -3
  104. package/src/frontend/native/{tls.ts → bridge/tls.ts} +2 -2
  105. package/src/frontend/native/{chat-lifecycle.ts → chats/chat-lifecycle.ts} +3 -3
  106. package/src/frontend/native/{chat-wire.ts → chats/chat-wire.ts} +4 -4
  107. package/src/frontend/native/{chats.ts → chats/chats.ts} +4 -4
  108. package/src/frontend/native/{empty-chat-sweep.ts → chats/empty-chat-sweep.ts} +4 -4
  109. package/src/frontend/native/{history.ts → chats/history.ts} +7 -7
  110. package/src/frontend/native/{reset.ts → chats/reset.ts} +7 -7
  111. package/src/frontend/native/index.ts +13 -10
  112. package/src/frontend/native/{media.ts → media/media.ts} +3 -3
  113. package/src/frontend/native/runtime.ts +1 -1
  114. package/src/frontend/native/{control.ts → surface/control.ts} +4 -4
  115. package/src/frontend/native/{extensions.ts → surface/extensions.ts} +12 -9
  116. package/src/frontend/native/{handlers.ts → surface/handlers.ts} +19 -14
  117. package/src/frontend/native/{logs.ts → surface/logs.ts} +2 -2
  118. package/src/frontend/native/{memory.ts → surface/memory.ts} +2 -2
  119. package/src/frontend/native/{models.ts → surface/models.ts} +17 -10
  120. package/src/frontend/native/{settings.ts → surface/settings.ts} +9 -9
  121. package/src/frontend/native/{status.ts → surface/status.ts} +3 -3
  122. package/src/frontend/native/{actions.ts → turn/actions.ts} +7 -4
  123. package/src/frontend/native/{context.ts → turn/context.ts} +8 -8
  124. package/src/frontend/native/{emit.ts → turn/emit.ts} +7 -7
  125. package/src/frontend/native/{queue.ts → turn/queue.ts} +3 -3
  126. package/src/frontend/native/{turn-meta.ts → turn/turn-meta.ts} +2 -2
  127. package/src/frontend/native/{turn.ts → turn/turn.ts} +9 -9
  128. package/src/frontend/shared/model-commands.ts +1 -1
  129. package/src/frontend/shared/session-status.ts +35 -8
  130. package/src/frontend/shared/status-context.ts +101 -2
  131. package/src/frontend/telegram/admin/background.ts +1 -1
  132. package/src/frontend/telegram/callbacks/model/backend.ts +1 -1
  133. package/src/frontend/telegram/callbacks/pulse.ts +1 -1
  134. package/src/frontend/telegram/callbacks/settings.ts +1 -1
  135. package/src/frontend/telegram/commands/admin.ts +2 -37
  136. package/src/frontend/telegram/commands/index.ts +1 -1
  137. package/src/frontend/telegram/commands/session.ts +6 -0
  138. package/src/frontend/telegram/commands/settings.ts +1 -1
  139. package/src/frontend/telegram/handlers/messages.ts +0 -10
  140. package/src/frontend/telegram/index.ts +1 -5
  141. package/src/frontend/telegram/middleware.ts +1 -15
  142. package/src/frontend/whatsapp/access.ts +2 -2
  143. package/src/frontend/whatsapp/actions/history.ts +2 -2
  144. package/src/frontend/whatsapp/actions/messaging.ts +2 -2
  145. package/src/frontend/whatsapp/actions/moderation.ts +1 -1
  146. package/src/frontend/whatsapp/actions/send.ts +1 -1
  147. package/src/frontend/whatsapp/commands.ts +8 -2
  148. package/src/frontend/whatsapp/{connection.ts → connection/connection.ts} +5 -5
  149. package/src/frontend/whatsapp/{pairing-service.ts → connection/pairing-service.ts} +3 -3
  150. package/src/frontend/whatsapp/{wa-logger.ts → connection/wa-logger.ts} +2 -2
  151. package/src/frontend/whatsapp/index.ts +4 -4
  152. package/src/frontend/whatsapp/{inbound.ts → messages/inbound.ts} +15 -15
  153. package/src/frontend/whatsapp/{media-store.ts → messages/media-store.ts} +4 -4
  154. package/src/frontend/whatsapp/{message-store.ts → messages/message-store.ts} +1 -1
  155. package/src/frontend/whatsapp/{turn-recovery.ts → messages/turn-recovery.ts} +4 -4
  156. package/src/frontend/whatsapp/registry.ts +1 -1
  157. package/src/frontend/whatsapp/runtime.ts +1 -1
  158. package/src/index.ts +1 -1
  159. package/src/storage/db.ts +6 -1
  160. package/src/storage/memory.ts +59 -8
  161. package/src/storage/metrics.ts +38 -0
  162. package/src/storage/repositories/goals-repo.ts +2 -1
  163. package/src/storage/repositories/sessions-repo.ts +6 -0
  164. package/src/storage/session-record.ts +13 -2
  165. package/src/storage/sessions.ts +12 -0
  166. package/src/storage/sql/db.sql +5 -0
  167. package/src/storage/sql/schema.sql +4 -0
  168. package/src/storage/sql/sessions.sql +3 -3
  169. package/src/storage/sql/statements.generated.ts +10 -3
  170. package/src/storage/sql/turn-meta.sql +1 -1
  171. package/src/util/boot-timer.ts +15 -1
  172. package/src/util/chat-id.ts +30 -0
  173. package/src/util/concurrency.ts +1 -1
  174. package/src/util/log.ts +1 -1
  175. package/src/util/paths.ts +0 -2
  176. package/src/core/soul/README.md +0 -110
  177. package/src/core/soul/RESEARCH.md +0 -98
  178. package/src/core/soul/associative.ts +0 -98
  179. package/src/core/soul/centrality.ts +0 -98
  180. package/src/core/soul/cluster.ts +0 -83
  181. package/src/core/soul/compiler.ts +0 -207
  182. package/src/core/soul/consolidate.ts +0 -179
  183. package/src/core/soul/critic.ts +0 -162
  184. package/src/core/soul/dag.ts +0 -265
  185. package/src/core/soul/delta.ts +0 -123
  186. package/src/core/soul/drift.ts +0 -99
  187. package/src/core/soul/embedder.ts +0 -129
  188. package/src/core/soul/emergent-critic.ts +0 -96
  189. package/src/core/soul/forgetting.ts +0 -131
  190. package/src/core/soul/governance.ts +0 -93
  191. package/src/core/soul/hash.ts +0 -97
  192. package/src/core/soul/hdc.ts +0 -154
  193. package/src/core/soul/kernel.ts +0 -540
  194. package/src/core/soul/lattice.ts +0 -103
  195. package/src/core/soul/lens.ts +0 -110
  196. package/src/core/soul/projector.ts +0 -240
  197. package/src/core/soul/reflect.ts +0 -170
  198. package/src/core/soul/reflex.ts +0 -164
  199. package/src/core/soul/retrieve.ts +0 -146
  200. package/src/core/soul/salience.ts +0 -146
  201. package/src/core/soul/service.ts +0 -204
  202. package/src/core/soul/settings.ts +0 -47
  203. package/src/core/soul/signals.ts +0 -117
  204. package/src/core/soul/talon-embedder.ts +0 -80
  205. package/src/core/soul/taps.ts +0 -199
  206. package/src/core/soul/types.ts +0 -298
  207. package/src/core/soul/valence.ts +0 -83
  208. /package/src/backend/{shared → runtime}/frontends.ts +0 -0
  209. /package/src/backend/{shared → runtime}/metrics.ts +0 -0
  210. /package/src/backend/{shared → runtime}/sleep.ts +0 -0
  211. /package/src/backend/{shared → runtime/turn}/flow-violation.ts +0 -0
  212. /package/src/backend/{shared → runtime}/usage.ts +0 -0
  213. /package/src/core/{scripting/lua-runner.ts → scripts/lua.ts} +0 -0
  214. /package/src/frontend/native/{routes → bridge/routes}/index.ts +0 -0
  215. /package/src/frontend/native/{routes → bridge/routes}/memory.ts +0 -0
  216. /package/src/frontend/native/{routes → bridge/routes}/mesh.ts +0 -0
  217. /package/src/frontend/native/{routes → bridge/routes}/models.ts +0 -0
  218. /package/src/frontend/native/{routes → bridge/routes}/params.ts +0 -0
  219. /package/src/frontend/native/{routes → bridge/routes}/table.ts +0 -0
  220. /package/src/frontend/native/{tool-result.ts → turn/tool-result.ts} +0 -0
  221. /package/src/frontend/whatsapp/{auth-state.ts → connection/auth-state.ts} +0 -0
  222. /package/src/frontend/whatsapp/{identity.ts → connection/identity.ts} +0 -0
  223. /package/src/frontend/whatsapp/{pairing-lock.ts → connection/pairing-lock.ts} +0 -0
  224. /package/src/frontend/whatsapp/{pairing.ts → connection/pairing.ts} +0 -0
  225. /package/src/frontend/whatsapp/{pins.ts → messages/pins.ts} +0 -0
@@ -24,12 +24,15 @@ import type { ContextManager, ExecuteParams, ExecuteResult } from "../types.js";
24
24
  import { bus } from "../bus/index.js";
25
25
  import { taskTable, type TaskHandle } from "../tasks/index.js";
26
26
  import { log, logDebug, logWarn } from "../../util/log.js";
27
+ import { recordHistogram } from "../../storage/metrics.js";
27
28
  import { recordSessionTurnPhases } from "../../storage/sessions.js";
28
29
  import type { TurnPhase } from "../../storage/session-record.js";
30
+ import { retrieveForTurn, type TurnMemory } from "../memory/turn-retrieval.js";
29
31
  import { TalonError } from "../errors.js";
30
32
  import { Loom } from "./loom.js";
31
33
  import { carryTurnEvents, startShuttleTiming } from "./shuttle.js";
32
34
  import type { Thread, ThreadSnapshot } from "./thread.js";
35
+ import { startTurnCpu } from "./turn-cpu.js";
33
36
  import { startTypingLoop } from "./typing-loop.js";
34
37
  import { resolveWarp } from "./warp-resolver.js";
35
38
 
@@ -244,6 +247,7 @@ export class Weaver {
244
247
  );
245
248
  }
246
249
 
250
+ const memory = resolveTurnMemory(params, phases);
247
251
  const stream = backend.chat.runChatTurn({
248
252
  chatId: params.chatId,
249
253
  model: warp.ref,
@@ -252,11 +256,17 @@ export class Weaver {
252
256
  senderHandle: params.senderHandle,
253
257
  isGroup: params.isGroup,
254
258
  messageId: params.messageId,
259
+ retrievedMemory: memory?.text,
255
260
  });
256
261
  const timing = startShuttleTiming();
262
+ // CPU and wall clock over the same bracket, so `turn.cpu_ms` can be
263
+ // divided by `turn.stream_ms` (see turn-cpu.ts). Recorded on the
264
+ // same path `phases.stream` is, so both cover one population.
265
+ const stopCpu = startTurnCpu();
257
266
  const streamStartedAt = Date.now();
258
267
  const agentResult = await carryTurnEvents(stream, params.onEvent, timing);
259
268
  phases.stream = Date.now() - streamStartedAt;
269
+ stopCpu();
260
270
  phases.delivery = timing.deliveryMs;
261
271
  if (timing.firstEventAt !== undefined) {
262
272
  phases.firstToken = timing.firstEventAt - streamStartedAt;
@@ -321,11 +331,37 @@ export class Weaver {
321
331
  }
322
332
  }
323
333
 
334
+ /**
335
+ * This turn's retrieved memory (`core/memory/turn-retrieval.ts`) — off
336
+ * unless `TALON_MEMORY_STORE=1`, fail-closed, and a pure read: nothing
337
+ * here may invalidate the frozen system prompt (plan §3.6), because the
338
+ * block travels in the USER turn, after all cached history.
339
+ *
340
+ * The size goes out as `turn.memory_chars` on every turn, zero
341
+ * included, so the flag's before/after populations are comparable next
342
+ * to `prompt.memory_chars` (the static core-view tier from #943).
343
+ */
344
+ function resolveTurnMemory(
345
+ params: ExecuteParams,
346
+ phases: Partial<Record<TurnPhase, number>>,
347
+ ): TurnMemory | undefined {
348
+ const startedAt = Date.now();
349
+ const memory = retrieveForTurn({
350
+ chatId: params.chatId,
351
+ text: params.prompt,
352
+ isGroup: params.isGroup ?? false,
353
+ });
354
+ phases.memory = Date.now() - startedAt;
355
+ recordHistogram("turn.memory_chars", memory?.chars ?? 0);
356
+ return memory;
357
+ }
358
+
324
359
  /** `queue=12ms warp=3ms ttft=1840ms stream=6200ms delivery=310ms` */
325
360
  function formatPhases(phases: Partial<Record<TurnPhase, number>>): string {
326
361
  const labels: Record<TurnPhase, string> = {
327
362
  queueWait: "queue",
328
363
  warpResolve: "warp",
364
+ memory: "memory",
329
365
  firstToken: "ttft",
330
366
  stream: "stream",
331
367
  delivery: "delivery",
@@ -21,7 +21,7 @@ import {
21
21
  nextRunAt,
22
22
  } from "../../storage/cron.js";
23
23
  import { getActiveCount } from "../../core/engine/dispatcher.js";
24
- import { getPulseStatus } from "../../core/background/pulse.js";
24
+ import { getPulseStatus } from "../../core/background/pulse/pulse.js";
25
25
  import { getHealthStatus, getRecentErrors } from "../../util/watchdog.js";
26
26
  import { formatDuration, formatModelLabel } from "./render.js";
27
27
  import {
@@ -21,7 +21,7 @@ import {
21
21
  } from "../../../../core/engine/backend-controller/index.js";
22
22
  import { resetSession } from "../../../../storage/sessions.js";
23
23
  import { clearHistory } from "../../../../storage/history.js";
24
- import { resetPulseCheckpoint } from "../../../../core/background/pulse.js";
24
+ import { resetPulseCheckpoint } from "../../../../core/background/pulse/pulse.js";
25
25
  import { resolveActiveModelForChat } from "../../../../core/models/active-model.js";
26
26
  import { logError } from "../../../../util/log.js";
27
27
  import { safeSlice } from "../../formatting.js";
@@ -19,7 +19,7 @@ import {
19
19
  disablePulse,
20
20
  enablePulse,
21
21
  isPulseEnabled,
22
- } from "../../../../core/background/pulse.js";
22
+ } from "../../../../core/background/pulse/pulse.js";
23
23
  import type { ComponentContext, ComponentInteraction } from "./types.js";
24
24
 
25
25
  // ── /pulse "Set interval…" button → opens modal ───────────────────────
@@ -30,7 +30,7 @@ import {
30
30
  disablePulse,
31
31
  enablePulse,
32
32
  isPulseEnabled,
33
- } from "../../../../core/background/pulse.js";
33
+ } from "../../../../core/background/pulse/pulse.js";
34
34
  import { renderSettingsText, EFFORT_DESCRIPTIONS } from "../../render.js";
35
35
  import {
36
36
  getBackendIdForChat,
@@ -6,7 +6,10 @@ import { type ModalSubmitInteraction, MessageFlags } from "discord.js";
6
6
  import type { TalonConfig } from "../../../core/config/index.js";
7
7
  import type { Gateway } from "../../../core/engine/gateway.js";
8
8
  import { setChatPulseInterval } from "../../../storage/chat-settings.js";
9
- import { registerChat, enablePulse } from "../../../core/background/pulse.js";
9
+ import {
10
+ registerChat,
11
+ enablePulse,
12
+ } from "../../../core/background/pulse/pulse.js";
10
13
  import { isInteractionAllowed } from "../handlers/index.js";
11
14
  import { parseInterval, formatDuration } from "../render.js";
12
15
  import { logError } from "../../../util/log.js";
@@ -13,14 +13,13 @@ import {
13
13
  import type { TalonConfig } from "../../../core/config/index.js";
14
14
  import type { Gateway } from "../../../core/engine/gateway.js";
15
15
  import { respawnSelf } from "../../../util/respawn.js";
16
- import { forceDream } from "../../../core/background/dream.js";
16
+ import { forceDream } from "../../../core/background/dream/index.js";
17
17
  import {
18
18
  formatDuration,
19
19
  renderMetricsMessages,
20
20
  renderDoctorMessages,
21
21
  } from "../render.js";
22
22
  import { collectDoctorReport } from "../../../core/doctor/index.js";
23
- import { getSoul } from "../../../core/soul/service.js";
24
23
  import { getMetrics, getTodayMetrics } from "../../../storage/metrics.js";
25
24
  import { handleAdminSubcommand } from "../admin.js";
26
25
  import { isAdmin } from "../handlers/index.js";
@@ -130,39 +129,6 @@ export async function handleDoctor(
130
129
  }
131
130
  }
132
131
 
133
- /**
134
- * /soul — read-only introspection of the compiled identity. `action:dream`
135
- * runs the organic maintenance pass and is admin-only. Inert while the soul
136
- * is disabled, so it is safe to ship dormant.
137
- */
138
- export async function handleSoul(
139
- i: ChatInputCommandInteraction,
140
- ): Promise<void> {
141
- const soul = getSoul();
142
- if (!soul.enabled) {
143
- await reply(
144
- i,
145
- "Soul is disabled (set TALON_SOUL_ENABLED to enable).",
146
- true,
147
- );
148
- return;
149
- }
150
- if (i.options.getString("action") === "dream") {
151
- if (!isAdmin(i.user.id)) {
152
- await reply(i, "Not authorized.", true);
153
- return;
154
- }
155
- await i.deferReply({ flags: MessageFlags.Ephemeral });
156
- await i.editReply("🧠 Soul dreaming...");
157
- soul
158
- .dream()
159
- .then(() => i.editReply("🧠 Soul dream complete."))
160
- .catch(() => undefined);
161
- return;
162
- }
163
- await reply(i, suppressMentions(soul.introspect()), true);
164
- }
165
-
166
132
  export async function handleDream(
167
133
  i: ChatInputCommandInteraction,
168
134
  ): Promise<void> {
@@ -114,17 +114,6 @@ function buildCommandDefinitions(devBuild = false): unknown[] {
114
114
  .setName("mesh")
115
115
  .setDescription("Ping and list mesh devices")
116
116
  .toJSON(),
117
- new SlashCommandBuilder()
118
- .setName("soul")
119
- .setDescription("Inspect the compiled identity")
120
- .addStringOption((o) =>
121
- o
122
- .setName("action")
123
- .setDescription("Leave empty to introspect")
124
- .setRequired(false)
125
- .addChoices({ name: "dream", value: "dream" }),
126
- )
127
- .toJSON(),
128
117
  // /update only exists on developer builds running from a git checkout —
129
118
  // a packaged binary has no source tree to pull into, so the command is
130
119
  // never registered there (same gate as the Telegram handler).
@@ -43,7 +43,6 @@ import {
43
43
  handleDream,
44
44
  handleAdmin,
45
45
  handleDoctor,
46
- handleSoul,
47
46
  handleUpdate,
48
47
  } from "./admin.js";
49
48
 
@@ -159,8 +158,6 @@ async function routeSlashCommand(
159
158
  return handleDoctor(interaction, config);
160
159
  case "mesh":
161
160
  return handleMesh(interaction);
162
- case "soul":
163
- return handleSoul(interaction);
164
161
  case "update":
165
162
  return handleUpdate(interaction, config);
166
163
  case "admin":
@@ -23,6 +23,10 @@ import {
23
23
  performSessionReset,
24
24
  collectSessionStatus,
25
25
  } from "../../shared/session-status.js";
26
+ import {
27
+ formatCacheTempLine,
28
+ formatDaemonLine,
29
+ } from "../../shared/status-context.js";
26
30
  import { stopCurrentTurn } from "../../../core/engine/dispatcher.js";
27
31
  import { reply } from "./interaction.js";
28
32
 
@@ -92,6 +96,7 @@ export async function handleStatus(
92
96
  ` Read ${formatTokenCount(s.cache.read)}${s.cache.showsWrite ? ` Write ${formatTokenCount(s.cache.write)}` : ""}`,
93
97
  ]
94
98
  : []),
99
+ ...(s.cacheTemp ? [` ${formatCacheTempLine(s.cacheTemp)}`] : []),
95
100
  ` Input ${formatTokenCount(s.inputTokens)} Output ${formatTokenCount(s.outputTokens)}${s.costUsd > 0 ? ` Cost ${formatUsd(s.costUsd)}` : ""}`,
96
101
  "",
97
102
  ...(s.plan
@@ -109,6 +114,7 @@ export async function handleStatus(
109
114
  `**Session** ${s.sessionName ? `"${s.sessionName}" ` : ""}${s.sessionId ? "`" + s.sessionId.slice(0, 8) + "...`" : "_(new)_"} · ${s.sessionAge} old`,
110
115
  `**Uptime** ${s.uptime} · ${s.activeSessionCount} active session${s.activeSessionCount === 1 ? "" : "s"}`,
111
116
  `**Runtime** ${s.runtime} · ${formatBytes(s.rssBytes)} RSS`,
117
+ formatDaemonLine(s.daemon),
112
118
  ];
113
119
  await i.editReply(lines.join("\n"));
114
120
  }
@@ -26,7 +26,7 @@ import {
26
26
  disablePulse,
27
27
  enablePulse,
28
28
  isPulseEnabled,
29
- } from "../../../core/background/pulse.js";
29
+ } from "../../../core/background/pulse/pulse.js";
30
30
  import {
31
31
  formatModelLabel,
32
32
  formatDuration,
@@ -18,10 +18,9 @@ import type { Client, Message } from "discord.js";
18
18
  import { ChannelType } from "discord.js";
19
19
  import type { TalonConfig } from "../../core/config/index.js";
20
20
  import { pushMessage } from "../../storage/history.js";
21
- import { registerChat } from "../../core/background/pulse.js";
21
+ import { registerChat } from "../../core/background/pulse/pulse.js";
22
22
  import { deriveNumericChatId } from "../../util/chat-id.js";
23
23
  import { handleMessage, getSenderName } from "./handlers/index.js";
24
- import { recordReactionToBot } from "../../core/soul/taps.js";
25
24
 
26
25
  export function registerMiddleware(client: Client, config: TalonConfig): void {
27
26
  client.on("messageCreate", (msg: Message) => {
@@ -86,27 +85,4 @@ export function registerMiddleware(client: Client, config: TalonConfig): void {
86
85
  /* logged inside */
87
86
  });
88
87
  });
89
-
90
- // ── Reaction tap — feed reactions on Talon's own messages to the soul ────
91
- // The gateway records outgoing message ids as `Number(snowflake)`, so the
92
- // lookup here must use the same conversion to match. Inert unless the soul
93
- // is enabled.
94
- client.on("messageReactionAdd", async (reaction, user) => {
95
- if (user.id === client.user?.id) return;
96
- try {
97
- // A partial arrives when the message predates the cache; the chat id
98
- // can't be built without the channel/guild it belongs to.
99
- if (reaction.partial) await reaction.fetch();
100
- } catch {
101
- return;
102
- }
103
- const msg = reaction.message;
104
- const emoji = reaction.emoji.name;
105
- if (!emoji) return;
106
- const chatId =
107
- msg.channel.type === ChannelType.DM
108
- ? `discord_dm_${msg.author?.id ?? user.id}`
109
- : `discord_guild_${msg.guildId}_${msg.channelId}`;
110
- recordReactionToBot(chatId, Number(msg.id), [emoji]);
111
- });
112
88
  }
@@ -34,8 +34,7 @@ function createClient(): Client {
34
34
  GatewayIntentBits.DirectMessageReactions,
35
35
  ],
36
36
  // Partials.Reaction is what makes reactions on messages that predate the
37
- // current cache arrive at all — without it the soul's reaction tap only
38
- // ever sees freshly-cached messages.
37
+ // current cache arrive at all, rather than being dropped silently.
39
38
  partials: [
40
39
  Partials.Channel,
41
40
  Partials.Message,
@@ -15,8 +15,8 @@
15
15
  import { randomBytes } from "node:crypto";
16
16
  import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
17
  import { resolve } from "node:path";
18
- import { dirs } from "../../util/paths.js";
19
- import { log } from "../../util/log.js";
18
+ import { dirs } from "../../../util/paths.js";
19
+ import { log } from "../../../util/log.js";
20
20
 
21
21
  const TOKEN_FILE = "bridge-token";
22
22
  /** 32 random bytes → 43 base64url chars; comfortably beyond brute force. */
@@ -1,9 +1,9 @@
1
1
  import { mkdir, rename, rm, writeFile, chmod } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { randomUUID } from "node:crypto";
4
- import { log, logError } from "../../util/log.js";
5
- import { files } from "../../util/paths.js";
6
- import { BRIDGE_PROTOCOL_VERSION } from "./protocol.js";
4
+ import { log, logError } from "../../../util/log.js";
5
+ import { files } from "../../../util/paths.js";
6
+ import { BRIDGE_PROTOCOL_VERSION } from "../protocol.js";
7
7
 
8
8
  export type BridgeDiscoveryInfo = {
9
9
  port: number;
@@ -6,7 +6,7 @@ import {
6
6
  asString,
7
7
  deviceIdParam,
8
8
  } from "./params.js";
9
- import { log, logWarn } from "../../../util/log.js";
9
+ import { log, logWarn } from "../../../../util/log.js";
10
10
 
11
11
  export function chatRoutes(
12
12
  host: RouteHost,
@@ -1,6 +1,6 @@
1
1
  import type { RouteHost } from "./host.js";
2
2
  import type { BridgeRoutes } from "./table.js";
3
- import { isLogLevel } from "../protocol.js";
3
+ import { isLogLevel } from "../../protocol.js";
4
4
  import { asPositiveInt, asString } from "./params.js";
5
5
 
6
6
  export function daemonRoutes(
@@ -18,9 +18,12 @@ import type {
18
18
  SearchResult,
19
19
  SkillItem,
20
20
  ToggleResult,
21
- } from "../protocol.js";
22
- import type { MemoryListQuery, MemoryListResult } from "../memory.js";
23
- import type { ConfigSnapshot } from "../settings.js";
21
+ } from "../../protocol.js";
22
+ import type {
23
+ MemoryListQuery,
24
+ MemoryListResult,
25
+ } from "../../surface/memory.js";
26
+ import type { ConfigSnapshot } from "../../surface/settings.js";
24
27
 
25
28
  /** Optional attachment references carried alongside a sent message. */
26
29
  export type SendOptions = {
@@ -1,6 +1,6 @@
1
1
  import type { RouteHost } from "./host.js";
2
2
  import type { BridgeRoutes } from "./table.js";
3
- import { BRIDGE_PROTOCOL_VERSION } from "../protocol.js";
3
+ import { BRIDGE_PROTOCOL_VERSION } from "../../protocol.js";
4
4
 
5
5
  export function preAuthRoutes(
6
6
  host: RouteHost,
@@ -24,10 +24,10 @@ import { createServer as createTlsServer } from "node:https";
24
24
  import { createHash, timingSafeEqual } from "node:crypto";
25
25
  import { createReadStream } from "node:fs";
26
26
  import { stat } from "node:fs/promises";
27
- import { log, logError, logDebug, logWarn } from "../../util/log.js";
27
+ import { log, logError, logDebug, logWarn } from "../../../util/log.js";
28
28
  import { formatFingerprint, type BridgeTlsIdentity } from "./tls.js";
29
- import { contentTypeFor } from "./media.js";
30
- import { type BridgeEvent } from "./protocol.js";
29
+ import { contentTypeFor } from "../media/media.js";
30
+ import { type BridgeEvent } from "../protocol.js";
31
31
  import { buildRoutes } from "./routes/index.js";
32
32
  import type { BridgeServerHandlers, RouteHost } from "./routes/host.js";
33
33
  import {
@@ -28,8 +28,8 @@ import {
28
28
  } from "node:crypto";
29
29
  import { mkdir, readFile, writeFile, chmod } from "node:fs/promises";
30
30
  import { resolve } from "node:path";
31
- import { dirs } from "../../util/paths.js";
32
- import { log, logWarn } from "../../util/log.js";
31
+ import { dirs } from "../../../util/paths.js";
32
+ import { log, logWarn } from "../../../util/log.js";
33
33
 
34
34
  export interface BridgeTlsIdentity {
35
35
  /** PKCS#8 private key, PEM. */
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import { toClientChat } from "./chat-wire.js";
7
- import type { ClientChat } from "./protocol.js";
8
- import type { NativeRuntime } from "./runtime.js";
9
- import { clearTurnMeta } from "./turn-meta.js";
7
+ import type { ClientChat } from "../protocol.js";
8
+ import type { NativeRuntime } from "../runtime.js";
9
+ import { clearTurnMeta } from "../turn/turn-meta.js";
10
10
 
11
11
  export function createChat(runtime: NativeRuntime, title?: string): ClientChat {
12
12
  const entry = runtime.chats.create(title);
@@ -8,11 +8,11 @@
8
8
  import {
9
9
  getChatSettings,
10
10
  getChatModelForBackend,
11
- } from "../../storage/chat-settings.js";
12
- import { getBackendIdForChat } from "../../core/engine/backend-controller/index.js";
11
+ } from "../../../storage/chat-settings.js";
12
+ import { getBackendIdForChat } from "../../../core/engine/backend-controller/index.js";
13
13
  import type { ChatEntry } from "./chats.js";
14
- import type { ClientChat, QueuedMessage } from "./protocol.js";
15
- import type { NativeRuntime } from "./runtime.js";
14
+ import type { ClientChat, QueuedMessage } from "../protocol.js";
15
+ import type { NativeRuntime } from "../runtime.js";
16
16
 
17
17
  /** Project the stored queue entry to its wire shape (text + attachments). */
18
18
  function toQueued(
@@ -11,14 +11,14 @@ import {
11
11
  generateNativeChatId,
12
12
  deriveNumericChatId,
13
13
  isNativeChatId,
14
- } from "../../util/chat-id.js";
14
+ } from "../../../util/chat-id.js";
15
15
  import {
16
16
  getAllSessions,
17
17
  setSessionName,
18
18
  deleteSession,
19
- } from "../../storage/sessions.js";
20
- import { getRecentHistory, clearHistory } from "../../storage/history.js";
21
- import { previewOf } from "./protocol.js";
19
+ } from "../../../storage/sessions.js";
20
+ import { getRecentHistory, clearHistory } from "../../../storage/history.js";
21
+ import { previewOf } from "../protocol.js";
22
22
 
23
23
  export type ChatEntry = {
24
24
  id: string;
@@ -15,11 +15,11 @@
15
15
  * means the removal is broadcast, so open lists update in place.
16
16
  */
17
17
 
18
- import { log, logError } from "../../util/log.js";
19
- import { getRecentHistory } from "../../storage/history.js";
18
+ import { log, logError } from "../../../util/log.js";
19
+ import { getRecentHistory } from "../../../storage/history.js";
20
20
  import { deleteChat } from "./chat-lifecycle.js";
21
- import type { NativeRuntime } from "./runtime.js";
22
- import { isBusy } from "./turn.js";
21
+ import type { NativeRuntime } from "../runtime.js";
22
+ import { isBusy } from "../turn/turn.js";
23
23
 
24
24
  const EMPTY_CHAT_MIN_AGE_MS = 60 * 60_000;
25
25
  const EMPTY_CHAT_SWEEP_INTERVAL_MS = 30 * 60_000;
@@ -10,19 +10,19 @@ import {
10
10
  searchHistoryMessages,
11
11
  type HistoryMessage,
12
12
  type MessageAttachment,
13
- } from "../../storage/history.js";
14
- import { isDeliveryTool } from "../../core/tools/index.js";
13
+ } from "../../../storage/history.js";
14
+ import { isDeliveryTool } from "../../../core/tools/index.js";
15
15
  import type { ChatEntry } from "./chats.js";
16
- import { refreshContext } from "./context.js";
17
- import { contentTypeFor, rehydrateAttachment } from "./media.js";
16
+ import { refreshContext } from "../turn/context.js";
17
+ import { contentTypeFor, rehydrateAttachment } from "../media/media.js";
18
18
  import {
19
19
  historyToClientMessage,
20
20
  type ClientAttachment,
21
21
  type ClientMessage,
22
22
  type SearchResult,
23
- } from "./protocol.js";
24
- import type { NativeRuntime } from "./runtime.js";
25
- import { getTurnMeta } from "./turn-meta.js";
23
+ } from "../protocol.js";
24
+ import type { NativeRuntime } from "../runtime.js";
25
+ import { getTurnMeta } from "../turn/turn-meta.js";
26
26
 
27
27
  export type HistoryPageOptions = { before?: number; limit?: number };
28
28
 
@@ -3,14 +3,14 @@
3
3
  * cached readouts, pulse checkpoint) while keeping the chat itself.
4
4
  */
5
5
 
6
- import { resetSession } from "../../storage/sessions.js";
7
- import { clearHistory } from "../../storage/history.js";
8
- import { resetPulseCheckpoint } from "../../core/background/pulse.js";
9
- import { getBackendForChat } from "../../core/engine/backend-controller/index.js";
6
+ import { resetSession } from "../../../storage/sessions.js";
7
+ import { clearHistory } from "../../../storage/history.js";
8
+ import { resetPulseCheckpoint } from "../../../core/background/pulse/pulse.js";
9
+ import { getBackendForChat } from "../../../core/engine/backend-controller/index.js";
10
10
  import { broadcastChatUpdated } from "./chat-wire.js";
11
- import { emitSystem } from "./emit.js";
12
- import type { NativeRuntime } from "./runtime.js";
13
- import { clearTurnMeta } from "./turn-meta.js";
11
+ import { emitSystem } from "../turn/emit.js";
12
+ import type { NativeRuntime } from "../runtime.js";
13
+ import { clearTurnMeta } from "../turn/turn-meta.js";
14
14
 
15
15
  /**
16
16
  * Forget everything the conversation accumulated. Shared by the explicit
@@ -18,18 +18,21 @@ import type { TalonConfig } from "../../core/config/index.js";
18
18
  import type { ContextManager } from "../../core/types.js";
19
19
  import type { Gateway } from "../../core/engine/gateway.js";
20
20
  import { log, logError } from "../../util/log.js";
21
- import { createNativeActionHandler } from "./actions.js";
22
- import { loadOrCreateBridgeToken } from "./auth.js";
23
- import { warmContextCache } from "./context.js";
24
- import { removeBridgeDiscovery, writeBridgeDiscovery } from "./discovery.js";
25
- import { emitAssistant, emitPhoto } from "./emit.js";
26
- import { startEmptyChatSweep } from "./empty-chat-sweep.js";
27
- import { buildBridgeHandlers } from "./handlers.js";
21
+ import { createNativeActionHandler } from "./turn/actions.js";
22
+ import { loadOrCreateBridgeToken } from "./bridge/auth.js";
23
+ import { warmContextCache } from "./turn/context.js";
24
+ import {
25
+ removeBridgeDiscovery,
26
+ writeBridgeDiscovery,
27
+ } from "./bridge/discovery.js";
28
+ import { emitAssistant, emitPhoto } from "./turn/emit.js";
29
+ import { startEmptyChatSweep } from "./chats/empty-chat-sweep.js";
30
+ import { buildBridgeHandlers } from "./surface/handlers.js";
28
31
  import { createNativeRuntime, type NativeRuntime } from "./runtime.js";
29
- import { BridgeServer } from "./server.js";
30
- import { isLoopbackHost, loadOrCreateBridgeTlsIdentity } from "./tls.js";
32
+ import { BridgeServer } from "./bridge/server.js";
33
+ import { isLoopbackHost, loadOrCreateBridgeTlsIdentity } from "./bridge/tls.js";
31
34
 
32
- export { summarizeToolResult } from "./tool-result.js";
35
+ export { summarizeToolResult } from "./turn/tool-result.js";
33
36
 
34
37
  export type NativeFrontend = {
35
38
  name: "native";
@@ -9,9 +9,9 @@ import { mkdir, unlink } from "node:fs/promises";
9
9
  import { basename, extname, join } from "node:path";
10
10
  import { pipeline } from "node:stream/promises";
11
11
  import type { Readable } from "node:stream";
12
- import { dirs } from "../../util/paths.js";
13
- import type { ClientAttachment } from "./protocol.js";
14
- import type { NativeRuntime } from "./runtime.js";
12
+ import { dirs } from "../../../util/paths.js";
13
+ import type { ClientAttachment } from "../protocol.js";
14
+ import type { NativeRuntime } from "../runtime.js";
15
15
 
16
16
  /**
17
17
  * Upload ceiling. Bodies stream straight to disk, so this bounds what a
@@ -11,7 +11,7 @@
11
11
  import type { TalonConfig } from "../../core/config/index.js";
12
12
  import type { Gateway } from "../../core/engine/gateway.js";
13
13
  import { getMeshService, type MeshService } from "../../core/mesh/index.js";
14
- import { NativeChats } from "./chats.js";
14
+ import { NativeChats } from "./chats/chats.js";
15
15
  import type {
16
16
  BridgeEvent,
17
17
  ClientAttachment,
@@ -4,10 +4,10 @@
4
4
 
5
5
  import { dirname, resolve } from "node:path";
6
6
  import { spawn } from "node:child_process";
7
- import { isBunRuntime } from "../../util/runtime.js";
8
- import { logError } from "../../util/log.js";
9
- import { PKG_ROOT } from "../../cli/context.js";
10
- import { forceDream } from "../../core/background/dream.js";
7
+ import { isBunRuntime } from "../../../util/runtime.js";
8
+ import { logError } from "../../../util/log.js";
9
+ import { PKG_ROOT } from "../../../cli/context.js";
10
+ import { forceDream } from "../../../core/background/dream/index.js";
11
11
 
12
12
  /**
13
13
  * Restart the daemon by spawning a detached `talon restart` — the same