talon-agent 4.6.1 → 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 (223) hide show
  1. package/README.md +3 -3
  2. package/package.json +2 -1
  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 -23
  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/sessions-repo.ts +6 -0
  163. package/src/storage/session-record.ts +13 -2
  164. package/src/storage/sessions.ts +12 -0
  165. package/src/storage/sql/db.sql +5 -0
  166. package/src/storage/sql/schema.sql +4 -0
  167. package/src/storage/sql/sessions.sql +3 -3
  168. package/src/storage/sql/statements.generated.ts +10 -3
  169. package/src/storage/sql/turn-meta.sql +1 -1
  170. package/src/util/boot-timer.ts +15 -1
  171. package/src/util/chat-id.ts +30 -0
  172. package/src/util/log.ts +1 -1
  173. package/src/util/paths.ts +0 -2
  174. package/src/core/soul/README.md +0 -110
  175. package/src/core/soul/RESEARCH.md +0 -98
  176. package/src/core/soul/associative.ts +0 -98
  177. package/src/core/soul/centrality.ts +0 -98
  178. package/src/core/soul/cluster.ts +0 -83
  179. package/src/core/soul/compiler.ts +0 -207
  180. package/src/core/soul/consolidate.ts +0 -179
  181. package/src/core/soul/critic.ts +0 -162
  182. package/src/core/soul/dag.ts +0 -265
  183. package/src/core/soul/delta.ts +0 -123
  184. package/src/core/soul/drift.ts +0 -99
  185. package/src/core/soul/embedder.ts +0 -129
  186. package/src/core/soul/emergent-critic.ts +0 -96
  187. package/src/core/soul/forgetting.ts +0 -131
  188. package/src/core/soul/governance.ts +0 -93
  189. package/src/core/soul/hash.ts +0 -97
  190. package/src/core/soul/hdc.ts +0 -154
  191. package/src/core/soul/kernel.ts +0 -540
  192. package/src/core/soul/lattice.ts +0 -103
  193. package/src/core/soul/lens.ts +0 -110
  194. package/src/core/soul/projector.ts +0 -240
  195. package/src/core/soul/reflect.ts +0 -170
  196. package/src/core/soul/reflex.ts +0 -164
  197. package/src/core/soul/retrieve.ts +0 -146
  198. package/src/core/soul/salience.ts +0 -146
  199. package/src/core/soul/service.ts +0 -204
  200. package/src/core/soul/settings.ts +0 -47
  201. package/src/core/soul/signals.ts +0 -117
  202. package/src/core/soul/talon-embedder.ts +0 -80
  203. package/src/core/soul/taps.ts +0 -199
  204. package/src/core/soul/types.ts +0 -298
  205. package/src/core/soul/valence.ts +0 -83
  206. /package/src/backend/{shared → runtime}/frontends.ts +0 -0
  207. /package/src/backend/{shared → runtime}/metrics.ts +0 -0
  208. /package/src/backend/{shared → runtime}/sleep.ts +0 -0
  209. /package/src/backend/{shared → runtime/turn}/flow-violation.ts +0 -0
  210. /package/src/backend/{shared → runtime}/usage.ts +0 -0
  211. /package/src/core/{scripting/lua-runner.ts → scripts/lua.ts} +0 -0
  212. /package/src/frontend/native/{routes → bridge/routes}/index.ts +0 -0
  213. /package/src/frontend/native/{routes → bridge/routes}/memory.ts +0 -0
  214. /package/src/frontend/native/{routes → bridge/routes}/mesh.ts +0 -0
  215. /package/src/frontend/native/{routes → bridge/routes}/models.ts +0 -0
  216. /package/src/frontend/native/{routes → bridge/routes}/params.ts +0 -0
  217. /package/src/frontend/native/{routes → bridge/routes}/table.ts +0 -0
  218. /package/src/frontend/native/{tool-result.ts → turn/tool-result.ts} +0 -0
  219. /package/src/frontend/whatsapp/{auth-state.ts → connection/auth-state.ts} +0 -0
  220. /package/src/frontend/whatsapp/{identity.ts → connection/identity.ts} +0 -0
  221. /package/src/frontend/whatsapp/{pairing-lock.ts → connection/pairing-lock.ts} +0 -0
  222. /package/src/frontend/whatsapp/{pairing.ts → connection/pairing.ts} +0 -0
  223. /package/src/frontend/whatsapp/{pins.ts → messages/pins.ts} +0 -0
@@ -19,14 +19,34 @@
19
19
  import {
20
20
  type AgentEvent,
21
21
  classifiedToAgentError,
22
- } from "../../core/agent-runtime/events.js";
23
- import { classify } from "../../core/errors.js";
24
- import type { ChatRunParams } from "../../core/agent-runtime/capabilities.js";
22
+ } from "../../../core/agent-runtime/events.js";
23
+ import { classify } from "../../../core/errors.js";
24
+ import type { ChatRunParams } from "../../../core/agent-runtime/capabilities.js";
25
25
  import type { QueryParams, QueryResult } from "./handler-types.js";
26
26
  import { buildResultEvents } from "./result-events.js";
27
27
 
28
28
  const SENTINEL = Symbol("handler-to-events:sentinel");
29
29
 
30
+ /**
31
+ * The DATA half of `QueryParams`, carried verbatim from the canonical
32
+ * `ChatRunParams` (only `ModelRef` flattens to `model.id`). The
33
+ * callback half is the queue's and is built per run below. Keeping the
34
+ * carry in one place is what stops a field — `retrievedMemory` being
35
+ * the one #639 lost — from reaching some handlers and not others.
36
+ */
37
+ function turnFields(params: ChatRunParams) {
38
+ return {
39
+ chatId: params.chatId,
40
+ model: params.model.id,
41
+ text: params.text,
42
+ senderName: params.senderName,
43
+ senderHandle: params.senderHandle,
44
+ isGroup: params.isGroup,
45
+ messageId: params.messageId,
46
+ retrievedMemory: params.retrievedMemory,
47
+ };
48
+ }
49
+
30
50
  type QueueEvent = AgentEvent | typeof SENTINEL;
31
51
 
32
52
  /**
@@ -79,13 +99,7 @@ export async function* handlerToEvents(
79
99
  let lastAccumulated = "";
80
100
 
81
101
  const handlerParams: QueryParams = {
82
- chatId: params.chatId,
83
- model: params.model.id,
84
- text: params.text,
85
- senderName: params.senderName,
86
- senderHandle: params.senderHandle,
87
- isGroup: params.isGroup,
88
- messageId: params.messageId,
102
+ ...turnFields(params),
89
103
  onStreamDelta: (accumulated) => {
90
104
  if (typeof accumulated !== "string" || accumulated.length === 0) {
91
105
  return;
@@ -10,7 +10,7 @@
10
10
  * stream loop that `handler-to-events.ts` adapts onto the native
11
11
  * `AsyncIterable<AgentEvent>` shape at the factory boundary.
12
12
  *
13
- * Living in `backend/shared/` keeps `core/types.ts` clean of any
13
+ * Living in `backend/runtime/turn/` keeps `core/types.ts` clean of any
14
14
  * implementation-detail shapes, so the dispatcher / cron / triggers /
15
15
  * frontends never accidentally couple to the callback contract.
16
16
  */
@@ -35,6 +35,12 @@ export type QueryParams = {
35
35
  * Provider message ID. Telegram is numeric; Discord snowflakes are strings.
36
36
  */
37
37
  messageId?: number | string;
38
+ /**
39
+ * Per-turn retrieved memory, carried verbatim from `ChatRunParams`.
40
+ * The handler passes it to `formatUserPrompt` and does nothing else
41
+ * with it — see `backend/runtime/prompt/prompt-format.ts`.
42
+ */
43
+ retrievedMemory?: string;
38
44
  onStreamDelta?: (accumulated: string, phase?: "thinking" | "text") => void;
39
45
  onTextBlock?: (text: string) => Promise<void>;
40
46
  /**
@@ -21,8 +21,8 @@
21
21
  * model swap) because those touch backend-specific storage.
22
22
  */
23
23
 
24
- import type { TalonError } from "../../core/errors.js";
25
- import { getFallbackModel } from "../../core/models/catalog.js";
24
+ import type { TalonError } from "../../../core/errors.js";
25
+ import { getFallbackModel } from "../../../core/models/catalog.js";
26
26
 
27
27
  // ── Public API ──────────────────────────────────────────────────────────────
28
28
 
@@ -5,8 +5,8 @@
5
5
  * logging imports so the adapter stays a pure event translator.
6
6
  */
7
7
 
8
- import type { AgentEvent } from "../../core/agent-runtime/events.js";
9
- import type { TokenUsageSnapshot } from "./usage.js";
8
+ import type { AgentEvent } from "../../../core/agent-runtime/events.js";
9
+ import type { TokenUsageSnapshot } from "../usage.js";
10
10
 
11
11
  export function buildResultEvents(inputs: {
12
12
  text: string;
@@ -20,8 +20,8 @@
20
20
  */
21
21
 
22
22
  import { captureDeliveredText } from "./delivered-text.js";
23
- import { isTurnTerminator, stripMcpPrefix } from "../../core/tools/index.js";
24
- import { updateLiveTurn } from "../../storage/sessions.js";
23
+ import { isTurnTerminator, stripMcpPrefix } from "../../../core/tools/index.js";
24
+ import { updateLiveTurn } from "../../../storage/sessions.js";
25
25
 
26
26
  // ── State shape ─────────────────────────────────────────────────────────────
27
27
 
@@ -17,8 +17,8 @@
17
17
  * partial text and real usage — never as an error, never as a retry.
18
18
  */
19
19
 
20
- import { log, logWarn } from "../../util/log.js";
21
- import { incrementCounter } from "../../storage/metrics.js";
20
+ import { log, logWarn } from "../../../util/log.js";
21
+ import { incrementCounter } from "../../../storage/metrics.js";
22
22
 
23
23
  const interrupts = new Map<string, () => void | Promise<void>>();
24
24
 
@@ -23,10 +23,10 @@ import {
23
23
  recordUsage,
24
24
  setSessionId,
25
25
  setSessionName,
26
- } from "../../storage/sessions.js";
27
- import { log } from "../../util/log.js";
28
- import { extractSessionName } from "../../util/session-name.js";
29
- import { traceMessage } from "../../util/trace.js";
26
+ } from "../../../storage/sessions.js";
27
+ import { log } from "../../../util/log.js";
28
+ import { extractSessionName } from "../../../util/session-name.js";
29
+ import { traceMessage } from "../../../util/trace.js";
30
30
  import {
31
31
  FLOW_VIOLATION_MAX_RETRIES,
32
32
  detectFlowViolation,
@@ -37,9 +37,9 @@ import {
37
37
  recordFailedTurnAccounting,
38
38
  recordFlowViolation,
39
39
  recordTurnMetrics,
40
- } from "./metrics.js";
40
+ } from "../metrics.js";
41
41
  import type { StreamState } from "./stream-state.js";
42
- import { summarizeUsage, type TokenUsageSnapshot } from "./usage.js";
42
+ import { summarizeUsage, type TokenUsageSnapshot } from "../usage.js";
43
43
 
44
44
  // ── Usage snapshot ──────────────────────────────────────────────────────────
45
45
 
package/src/bootstrap.ts CHANGED
@@ -24,16 +24,16 @@ import {
24
24
  } from "./core/engine/dispatcher.js";
25
25
  import { bus } from "./core/bus/index.js";
26
26
  import { appendToJournal } from "./storage/journal.js";
27
- import { initPulse, resetPulseTimer } from "./core/background/pulse.js";
28
- import { initCron } from "./core/background/cron.js";
29
- import { initPlanAlerts } from "./core/background/plan-alerts.js";
27
+ import { initPulse, resetPulseTimer } from "./core/background/pulse/pulse.js";
28
+ import { initCron } from "./core/background/cron/scheduler.js";
29
+ import { initPlanAlerts } from "./core/background/pulse/plan-alerts.js";
30
30
  import { setAdminNotifier } from "./core/frontend-runtime/admin-notify.js";
31
31
  import { startAuthExpiryMonitor } from "./core/auth/expiry-monitor.js";
32
32
  import {
33
33
  initTriggers,
34
34
  resumeAfterRestart as resumeTriggersAfterRestart,
35
35
  } from "./core/background/triggers/index.js";
36
- import { initDream, maybeStartDream } from "./core/background/dream.js";
36
+ import { initDream, maybeStartDream } from "./core/background/dream/index.js";
37
37
  import { initHeartbeat } from "./core/background/heartbeat/index.js";
38
38
  import { log, logWarn, logDebug } from "./util/log.js";
39
39
  import { bootPhase } from "./util/boot-timer.js";
@@ -489,21 +489,6 @@ export async function initBackendAndDispatcher(
489
489
  // alerts above.
490
490
  wireAdminNotifier(config, frontends);
491
491
 
492
- // Soul — initialize the identity kernel singleton from config so the prompt
493
- // injection / dream hooks see the right enabled state. Off by default; a
494
- // failure here must never block startup.
495
- try {
496
- const { SoulService, setSoul } = await import("./core/soul/service.js");
497
- setSoul(
498
- SoulService.create({
499
- enabled: config.soul?.enabled ?? false,
500
- ...(config.soul?.path ? { path: config.soul.path } : {}),
501
- }),
502
- );
503
- } catch (err) {
504
- log("soul", `init skipped: ${String(err)}`);
505
- }
506
-
507
492
  // Only enable mempalace dream integration if the plugin actually registered
508
493
  let mempalaceCfg: { pythonPath: string; palacePath: string } | undefined;
509
494
  if (config.mempalace?.enabled) {
package/src/cli.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import { MCP_LAUNCH_SUBCOMMAND, runSupervisor } from "./util/mcp-launcher.js";
20
- import { LUA_RUN_SUBCOMMAND, runLuaMain } from "./core/scripting/lua-runner.js";
20
+ import { LUA_RUN_SUBCOMMAND, runLuaMain } from "./core/scripts/lua.js";
21
21
 
22
22
  // Hidden subcommand dispatch — must run before anything else. Talon
23
23
  // supervises MCP stdio children (`_mcp-launch`) and runs WASM-sandboxed
@@ -56,6 +56,17 @@ export interface ChatRunParams {
56
56
  isGroup?: boolean;
57
57
  /** Provider message ID. Telegram is numeric; Discord snowflakes are strings. */
58
58
  messageId?: number | string;
59
+ /**
60
+ * Memory retrieved for this turn by `core/memory/turn-retrieval.ts`,
61
+ * already ranked, trust-filtered and budgeted. The Weaver resolves it
62
+ * before the call; a backend's only job is to hand it to
63
+ * `formatUserPrompt`, which owns the rendering.
64
+ *
65
+ * Turn-scoped and user-turn-only: it must never reach
66
+ * `prepareSystemPrompt()` or a backend `system` field, or the
67
+ * per-session frozen prompt stops being frozen (plan §3.6).
68
+ */
69
+ retrievedMemory?: string;
59
70
  }
60
71
 
61
72
  // ── Catalog types ───────────────────────────────────────────────────────────
@@ -199,6 +199,80 @@ export async function assertCompletedUsageMatchesUsageEvent(
199
199
  }
200
200
  }
201
201
 
202
+ /**
203
+ * `ChatRunParams.retrievedMemory` must reach the prompt the backend
204
+ * sends, in the USER turn, after the message text.
205
+ *
206
+ * This is the clause #639 was written in the absence of: the old
207
+ * pre-retrieval field was honoured by two backends and dropped in
208
+ * silence by the other four, a divergence that would have become a
209
+ * real bug the day a retriever existed. Now there is one renderer
210
+ * (`backend/runtime/prompt/prompt-format.ts`) that every backend already calls
211
+ * for its time tag and `msg_id` framing — and this assertion is what
212
+ * stops a backend from quietly building its prompt some other way.
213
+ *
214
+ * The caller supplies `capturePrompt`, returning the prompt the
215
+ * backend built for the last run: `core/` cannot import `backend/`, so
216
+ * the rendering itself is asserted by the caller, which can. `marker`
217
+ * is the retrieved text; it must appear after the user's own text, and
218
+ * must not appear at all when the field is absent.
219
+ */
220
+ export async function assertChatBackendCarriesRetrievedMemory(
221
+ backend: Backend,
222
+ capturePrompt: () => string | undefined,
223
+ options: { text?: string; chatId?: string; marker?: string } = {},
224
+ ): Promise<void> {
225
+ const chat = backend.chat;
226
+ if (!chat) return;
227
+ const text = options.text ?? "ping";
228
+ const marker = options.marker ?? "#1 [fact] contract: remembered thing";
229
+ const run = async (retrievedMemory?: string): Promise<string> => {
230
+ await drain(
231
+ chat.runChatTurn({
232
+ chatId: options.chatId ?? "contract-test-chat",
233
+ model: makeBareModelRef(backend.id, "contract-test-model"),
234
+ text,
235
+ senderName: "ContractTest",
236
+ ...(retrievedMemory !== undefined ? { retrievedMemory } : {}),
237
+ }),
238
+ );
239
+ const prompt = capturePrompt();
240
+ if (prompt === undefined) {
241
+ throw new ContractViolation(
242
+ backend.id,
243
+ "ChatBackend.carriesRetrievedMemory",
244
+ "the backend built no prompt for the turn",
245
+ );
246
+ }
247
+ return prompt;
248
+ };
249
+
250
+ const withMemory = await run(marker);
251
+ if (!withMemory.includes(marker)) {
252
+ throw new ContractViolation(
253
+ backend.id,
254
+ "ChatBackend.carriesRetrievedMemory",
255
+ `retrievedMemory never reached the prompt: ${JSON.stringify(withMemory)}`,
256
+ );
257
+ }
258
+ if (withMemory.indexOf(marker) < withMemory.indexOf(text)) {
259
+ throw new ContractViolation(
260
+ backend.id,
261
+ "ChatBackend.carriesRetrievedMemory",
262
+ "the memory block precedes the user text; it must be appended after it",
263
+ );
264
+ }
265
+
266
+ const without = await run();
267
+ if (without.includes(marker)) {
268
+ throw new ContractViolation(
269
+ backend.id,
270
+ "ChatBackend.carriesRetrievedMemory",
271
+ "the memory block appeared in a turn that carried no retrievedMemory",
272
+ );
273
+ }
274
+ }
275
+
202
276
  /**
203
277
  * Backends that expose a `BackgroundRunner` must terminate with
204
278
  * `completed` or `error` and must emit `run_started` first — same
@@ -323,7 +397,16 @@ export async function assertUsageTelemetryShape(
323
397
  */
324
398
  export async function assertBackendContract(
325
399
  backend: Backend,
326
- options: { text?: string; chatId?: string } = {},
400
+ options: {
401
+ text?: string;
402
+ chatId?: string;
403
+ /**
404
+ * The prompt the backend built for the last run, when the fixture
405
+ * can capture it. Supplying it opts the turn-retrieval clause into
406
+ * the suite; omitting it skips that clause only.
407
+ */
408
+ capturePrompt?: () => string | undefined;
409
+ } = {},
327
410
  ): Promise<string[]> {
328
411
  const checked: string[] = [];
329
412
 
@@ -336,6 +419,14 @@ export async function assertBackendContract(
336
419
  checked.push("ChatBackend.singleUsage");
337
420
  await assertCompletedUsageMatchesUsageEvent(backend, options);
338
421
  checked.push("ChatBackend.completedUsageMatches");
422
+ if (options.capturePrompt) {
423
+ await assertChatBackendCarriesRetrievedMemory(
424
+ backend,
425
+ options.capturePrompt,
426
+ options,
427
+ );
428
+ checked.push("ChatBackend.carriesRetrievedMemory");
429
+ }
339
430
  }
340
431
  if (backend.background) {
341
432
  await assertBackgroundRunnerLifecycle(backend);
@@ -9,7 +9,7 @@
9
9
  * longer render markdown logs themselves and core no longer parses
10
10
  * backend-specific output.
11
11
  *
12
- * The shared wrapper `backend/shared/handler-to-events.ts` converts
12
+ * The shared wrapper `backend/runtime/turn/handler-to-events.ts` converts
13
13
  * each backend's existing callback-driven `handleMessage` into the
14
14
  * canonical sequence: `run_started → text_delta* →
15
15
  * assistant_message* → tool_call* → usage → completed`. Backends
@@ -14,9 +14,9 @@
14
14
  * unset to avoid sweeping the other context's subprocesses.
15
15
  */
16
16
 
17
- import type { BackgroundRunner } from "../agent-runtime/capabilities.js";
18
- import type { OneShotAgentParams, OneShotUsage } from "../types.js";
19
- import { logWarn, logError } from "../../util/log.js";
17
+ import type { BackgroundRunner } from "../../agent-runtime/capabilities.js";
18
+ import type { OneShotAgentParams, OneShotUsage } from "../../types.js";
19
+ import { logWarn, logError } from "../../../util/log.js";
20
20
 
21
21
  /** Default bounded grace after an abort before giving up on the backend. */
22
22
  const DEFAULT_ABORT_GRACE_MS = 30 * 1000;
@@ -22,7 +22,7 @@ import {
22
22
  CLOSED_BREAKER,
23
23
  stepBreaker,
24
24
  type Breaker,
25
- } from "../../native/scheduler-core.js";
25
+ } from "../../../native/scheduler-core.js";
26
26
 
27
27
  export type JobHealthOptions = {
28
28
  /** Consecutive failures before the breaker opens. */
@@ -14,14 +14,14 @@
14
14
  */
15
15
 
16
16
  import { mkdir, appendFile } from "node:fs/promises";
17
- import { dirs } from "../../util/paths.js";
18
- import { log, logWarn } from "../../util/log.js";
17
+ import { dirs } from "../../../util/paths.js";
18
+ import { log, logWarn } from "../../../util/log.js";
19
19
  import {
20
20
  acquireBackendInstance,
21
21
  isModelValidForBackend,
22
- } from "../engine/backend-controller/index.js";
23
- import { taskTable } from "../tasks/index.js";
24
- import type { OneShotAgentParams } from "../types.js";
22
+ } from "../../engine/backend-controller/index.js";
23
+ import { taskTable } from "../../tasks/index.js";
24
+ import type { OneShotAgentParams } from "../../types.js";
25
25
  import { runIsolatedAgent } from "./isolated-agent.js";
26
26
  import {
27
27
  buildJobSystemPrompt,
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { resolve } from "node:path";
8
- import { dirs } from "../../util/paths.js";
8
+ import { dirs } from "../../../util/paths.js";
9
9
 
10
10
  export type JobKind = "trigger" | "cron";
11
11
 
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import { Cron } from "croner";
23
- import { getActiveCount } from "../engine/dispatcher.js";
23
+ import { getActiveCount } from "../../engine/dispatcher.js";
24
24
  import {
25
25
  getAllCronJobs,
26
26
  getCronJob,
@@ -31,10 +31,10 @@ import {
31
31
  describeSchedule,
32
32
  type CronJob,
33
33
  type CronRunOutcome,
34
- } from "../../storage/cron.js";
35
- import { appendDailyLog } from "../../storage/daily-log.js";
36
- import { log, logError, logWarn } from "../../util/log.js";
37
- import { numericChatIdFor } from "../../util/chat-id.js";
34
+ } from "../../../storage/cron.js";
35
+ import { appendDailyLog } from "../../../storage/daily-log.js";
36
+ import { log, logError, logWarn } from "../../../util/log.js";
37
+ import { numericChatIdFor } from "../../../util/chat-id.js";
38
38
  import { runJobOneShot } from "./job-oneshot.js";
39
39
  import {
40
40
  jobAllowsRun,
@@ -46,7 +46,7 @@ import {
46
46
  import {
47
47
  catchupRunCount,
48
48
  missedRunCount,
49
- } from "../../native/scheduler-core.js";
49
+ } from "../../../native/scheduler-core.js";
50
50
 
51
51
  // ── Dependencies (injected at startup) ──────────────────────────────────────
52
52
 
@@ -18,7 +18,7 @@ import {
18
18
  type CatchupPolicy,
19
19
  type CronJob,
20
20
  type CronJobType,
21
- } from "../../storage/cron.js";
21
+ } from "../../../storage/cron.js";
22
22
 
23
23
  /** The scheduler ticks once a minute, so sub-minute intervals are meaningless. */
24
24
  const MIN_INTERVAL_SECONDS = 60;
@@ -13,18 +13,17 @@
13
13
 
14
14
  import { existsSync, mkdirSync, appendFileSync } from "node:fs";
15
15
  import { resolve } from "node:path";
16
- import { files as pathFiles, dirs } from "../../util/paths.js";
17
- import { kvGet, kvSet } from "../../storage/kv.js";
18
- import { importLegacyJson } from "../../storage/legacy-import.js";
16
+ import { files as pathFiles, dirs } from "../../../util/paths.js";
17
+ import { kvGet, kvSet } from "../../../storage/kv.js";
18
+ import { importLegacyJson } from "../../../storage/legacy-import.js";
19
19
  import { readPromptAsset } from "#prompt-assets";
20
- import { log, logError, logWarn } from "../../util/log.js";
21
- import { getDefaultModel } from "../models/catalog.js";
22
- import type { OneShotAgentParams, ReasoningEffortLevel } from "../types.js";
23
- import type { Backend } from "../agent-runtime/capabilities.js";
24
- import { getSoul } from "../soul/service.js";
25
- import { taskTable } from "../tasks/index.js";
26
- import { resolveBackgroundEffort } from "./effort.js";
27
- import { FailureBackoff } from "./failure-backoff.js";
20
+ import { log, logError, logWarn } from "../../../util/log.js";
21
+ import { getDefaultModel } from "../../models/catalog.js";
22
+ import type { OneShotAgentParams, ReasoningEffortLevel } from "../../types.js";
23
+ import type { Backend } from "../../agent-runtime/capabilities.js";
24
+ import { taskTable } from "../../tasks/index.js";
25
+ import { resolveBackgroundEffort } from "../effort.js";
26
+ import { FailureBackoff } from "../failure-backoff.js";
28
27
 
29
28
  // ── Types ────────────────────────────────────────────────────────────────────
30
29
 
@@ -160,10 +159,6 @@ async function executeDream(trigger: "auto" | "forced"): Promise<void> {
160
159
  "dream",
161
160
  `Memory consolidation complete (${trigger}), log: ${dreamLogPath}`,
162
161
  );
163
- // The soul's organic maintenance shares the dream cadence. Inert (no-op)
164
- // unless the soul is enabled, and isolated so a soul failure never breaks
165
- // memory consolidation.
166
- await getSoul().dream();
167
162
  } catch (err) {
168
163
  logError("dream", `Memory consolidation failed (${trigger})`, err);
169
164
  writeDreamState({ last_run: state?.last_run ?? 0, status: "idle" });
@@ -11,9 +11,9 @@
11
11
  * also what covers windows the plan reports no reset for).
12
12
  */
13
13
 
14
- import { getPooledBackend } from "../engine/backend-controller/index.js";
15
- import { log, logWarn } from "../../util/log.js";
16
- import { formatSmartTimestamp } from "../../util/time.js";
14
+ import { getPooledBackend } from "../../engine/backend-controller/index.js";
15
+ import { log, logWarn } from "../../../util/log.js";
16
+ import { formatSmartTimestamp } from "../../../util/time.js";
17
17
 
18
18
  const CHECK_INTERVAL_MS = 5 * 60_000;
19
19
 
@@ -8,16 +8,19 @@
8
8
  * Knows nothing about the backend or frontend — uses the dispatcher.
9
9
  */
10
10
 
11
- import { execute, getActiveCount } from "../engine/dispatcher.js";
11
+ import { execute, getActiveCount } from "../../engine/dispatcher.js";
12
12
  import {
13
13
  setChatPulse,
14
14
  getRegisteredPulseChats,
15
15
  getChatSettings,
16
16
  setPulseLastCheckMsgId,
17
- } from "../../storage/chat-settings.js";
18
- import { getRecentHistory, getLatestMessageId } from "../../storage/history.js";
19
- import { log, logError } from "../../util/log.js";
20
- import { formatSmartTimestamp } from "../../util/time.js";
17
+ } from "../../../storage/chat-settings.js";
18
+ import {
19
+ getRecentHistory,
20
+ getLatestMessageId,
21
+ } from "../../../storage/history.js";
22
+ import { log, logError } from "../../../util/log.js";
23
+ import { formatSmartTimestamp } from "../../../util/time.js";
21
24
 
22
25
  // ── State ────────────────────────────────────────────────────────────────────
23
26
 
@@ -7,7 +7,7 @@
7
7
  import { spawnSync } from "node:child_process";
8
8
  import type { Trigger } from "../../../storage/triggers.js";
9
9
  import { selfInvocation } from "../../../util/mcp-launcher.js";
10
- import { LUA_RUN_SUBCOMMAND } from "../../scripting/lua-runner.js";
10
+ import { LUA_RUN_SUBCOMMAND } from "../../scripts/lua.js";
11
11
 
12
12
  export function commandForLanguage(
13
13
  lang: Trigger["language"],
@@ -557,16 +557,11 @@ const configSchema = z.object({
557
557
  // Playwright — headless browser automation via MCP
558
558
  playwright: playwrightConfigSchema.optional(),
559
559
 
560
- // Soul — compiled, self-organizing identity kernel (experimental).
561
- // Off by default; when enabled, the projected identity surface is injected
562
- // into the system prompt and the organic pass runs on the dream cadence.
563
- soul: z
564
- .object({
565
- enabled: z.boolean().default(false),
566
- /** Kernel persistence path (default: ~/.talon/data/soul.json) */
567
- path: z.string().min(1).optional(),
568
- })
569
- .optional(),
560
+ // Soul — the compiled identity kernel, removed in #953. The root schema
561
+ // is `.strict()`, so a key that simply disappeared would make every
562
+ // existing config.json fail to parse; accepted and ignored instead, with
563
+ // one warning at load so the operator knows to drop it.
564
+ soul: z.unknown().optional(),
570
565
 
571
566
  // Discord — discord.js v14-based frontend
572
567
  discord: discordConfigSchema.optional(),
@@ -741,6 +736,15 @@ export function loadConfig(): TalonConfig {
741
736
 
742
737
  const parsed = configSchema.parse(fileConfig);
743
738
 
739
+ // The soul kernel is gone (#953). Its config block still parses so an
740
+ // existing config.json keeps loading, but it no longer does anything —
741
+ // say so once rather than silently ignoring it.
742
+ if (parsed.soul !== undefined) {
743
+ console.warn(
744
+ `Ignoring "soul" in ${CONFIG_FILE}: the soul kernel was removed; its message taps now write to the memory store. Remove the key.`,
745
+ );
746
+ }
747
+
744
748
  // Unified memory section: mirror the selected backend onto the per-plugin
745
749
  // section the loaders consume (builtins.ts reads config.mempalace /
746
750
  // config.mem0). The memory section wins over a legacy section when both
@@ -827,7 +831,7 @@ export function primaryFrontend(config: TalonConfig): string {
827
831
  * the config. Multi-frontend deployments need per-chat prompt
828
832
  * flavours (a native-app chat must get native.md guidance, not the
829
833
  * telegram.md that `frontends[0]` happens to be) — the per-session
830
- * snapshot layer (backend/shared/system-prompt.ts) calls this with the
834
+ * snapshot layer (backend/runtime/prompt/system-prompt.ts) calls this with the
831
835
  * chat's owning frontend and freezes the result per session.
832
836
  */
833
837
  export function buildSystemPromptPartsFor(