talon-agent 4.6.1 → 5.0.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 (310) 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 +22 -7
  6. package/src/backend/claude-sdk/factory.ts +21 -41
  7. package/src/backend/claude-sdk/handler.ts +28 -7
  8. package/src/backend/claude-sdk/host/in-process.ts +147 -0
  9. package/src/backend/claude-sdk/one-shot.ts +1 -1
  10. package/src/backend/claude-sdk/options.ts +7 -6
  11. package/src/backend/claude-sdk/stream.ts +30 -3
  12. package/src/backend/claude-sdk/warm.ts +1 -1
  13. package/src/backend/codex/constants.ts +1 -1
  14. package/src/backend/codex/factory.ts +2 -2
  15. package/src/backend/codex/handler/events.ts +1 -1
  16. package/src/backend/codex/handler/message.ts +26 -12
  17. package/src/backend/codex/handler/rollout-accounting.ts +1 -1
  18. package/src/backend/codex/init.ts +1 -1
  19. package/src/backend/codex/mcp-config.ts +2 -2
  20. package/src/backend/codex/one-shot.ts +1 -1
  21. package/src/backend/kilo/handler/message.ts +4 -1
  22. package/src/backend/openai-agents/constants.ts +1 -1
  23. package/src/backend/openai-agents/factory.ts +2 -2
  24. package/src/backend/openai-agents/handler/events.ts +1 -1
  25. package/src/backend/openai-agents/handler/message.ts +8 -4
  26. package/src/backend/openai-agents/init.ts +1 -1
  27. package/src/backend/openai-agents/mcp-pool.ts +2 -2
  28. package/src/backend/opencode/handler/message.ts +4 -1
  29. package/src/backend/remote-server/chat-turn.ts +31 -23
  30. package/src/backend/remote-server/events.ts +3 -3
  31. package/src/backend/remote-server/factory.ts +6 -3
  32. package/src/backend/remote-server/index.ts +1 -1
  33. package/src/backend/remote-server/mcp.ts +1 -1
  34. package/src/backend/remote-server/one-shot.ts +1 -1
  35. package/src/backend/remote-server/server-bindings.ts +1 -1
  36. package/src/backend/remote-server/turn.ts +1 -1
  37. package/src/backend/runtime/cache/cache-metrics.ts +126 -0
  38. package/src/backend/{shared → runtime/cache}/cache-telemetry.ts +22 -2
  39. package/src/backend/{shared → runtime}/index.ts +34 -25
  40. package/src/backend/{shared → runtime/prompt}/delivery-contract.ts +1 -1
  41. package/src/backend/{shared → runtime/prompt}/prompt-format.ts +46 -2
  42. package/src/backend/{shared → runtime/prompt}/system-prompt.ts +4 -4
  43. package/src/backend/{shared → runtime/turn}/delivered-text.ts +1 -1
  44. package/src/backend/{shared → runtime/turn}/delivery.ts +3 -3
  45. package/src/backend/{shared → runtime/turn}/handle-retry.ts +5 -5
  46. package/src/backend/{shared → runtime/turn}/handler-to-events.ts +24 -10
  47. package/src/backend/{shared → runtime/turn}/handler-types.ts +7 -1
  48. package/src/backend/{shared → runtime/turn}/model-retry.ts +2 -2
  49. package/src/backend/{shared → runtime/turn}/result-events.ts +2 -2
  50. package/src/backend/{shared → runtime/turn}/stream-state.ts +2 -2
  51. package/src/backend/{shared → runtime/turn}/turn-interrupt.ts +2 -2
  52. package/src/backend/{shared → runtime/turn}/turn-phases.ts +6 -6
  53. package/src/bootstrap.ts +6 -21
  54. package/src/cli.ts +6 -3
  55. package/src/core/agent-runtime/README.md +15 -0
  56. package/src/core/agent-runtime/agent-host.ts +387 -0
  57. package/src/core/agent-runtime/capabilities.ts +11 -0
  58. package/src/core/agent-runtime/contract-tests.ts +92 -1
  59. package/src/core/agent-runtime/events.ts +1 -1
  60. package/src/core/background/{isolated-agent.ts → cron/isolated-agent.ts} +3 -3
  61. package/src/core/background/{job-health.ts → cron/job-health.ts} +1 -1
  62. package/src/core/background/{job-oneshot.ts → cron/job-oneshot.ts} +5 -5
  63. package/src/core/background/{job-prompt.ts → cron/job-prompt.ts} +1 -1
  64. package/src/core/background/{cron.ts → cron/scheduler.ts} +6 -6
  65. package/src/core/background/{cron-spec.ts → cron/spec.ts} +1 -1
  66. package/src/core/background/{dream.ts → dream/index.ts} +10 -15
  67. package/src/core/background/{plan-alerts.ts → pulse/plan-alerts.ts} +3 -3
  68. package/src/core/background/{pulse.ts → pulse/pulse.ts} +8 -5
  69. package/src/core/background/triggers/command.ts +2 -2
  70. package/src/{util → core/config}/harden.ts +1 -1
  71. package/src/core/config/index.ts +16 -12
  72. package/src/{util → core/daemon}/boot-timer.ts +15 -1
  73. package/src/core/daemon/pidfile.ts +1 -1
  74. package/src/core/daemon/resource-sampler.ts +121 -0
  75. package/src/{util → core/daemon}/respawn.ts +1 -1
  76. package/src/core/doctor/index.ts +1 -1
  77. package/src/core/engine/dispatcher.ts +16 -0
  78. package/src/core/engine/gateway-actions/cron.ts +2 -2
  79. package/src/core/engine/gateway-actions/fetch-url.ts +1 -1
  80. package/src/core/engine/gateway-actions/index.ts +4 -1
  81. package/src/core/engine/gateway-actions/memory.ts +335 -0
  82. package/src/core/engine/gateway-actions/native/exec-background.ts +129 -0
  83. package/src/core/engine/gateway-actions/native/exec-remote.ts +85 -0
  84. package/src/core/engine/gateway-actions/native/exec.ts +182 -0
  85. package/src/core/engine/gateway-actions/native/index.ts +46 -0
  86. package/src/core/engine/gateway-actions/native/params.ts +46 -0
  87. package/src/core/engine/gateway-actions/native/read.ts +182 -0
  88. package/src/core/engine/gateway-actions/native/results.ts +34 -0
  89. package/src/core/engine/gateway-actions/native/search.ts +212 -0
  90. package/src/core/engine/gateway-actions/native/shell.ts +39 -0
  91. package/src/core/engine/gateway-actions/native/teleport.ts +55 -0
  92. package/src/core/engine/gateway-actions/native/write.ts +170 -0
  93. package/src/core/engine/gateway.ts +0 -6
  94. package/src/core/frontend-runtime/builtins.ts +2 -2
  95. package/src/{util → core/frontend-runtime}/chat-id.ts +30 -0
  96. package/src/core/mcp-hub/index.ts +1 -1
  97. package/src/core/mcp-hub/talon-server.ts +1 -1
  98. package/src/core/memory/import.ts +3 -2
  99. package/src/core/memory/taps.ts +199 -0
  100. package/src/core/memory/turn-retrieval.ts +222 -0
  101. package/src/core/plugin/mcp.ts +1 -1
  102. package/src/core/prompt/assemble.ts +2 -12
  103. package/src/core/prompt/index.ts +2 -2
  104. package/src/core/prompt/invalidation.ts +1 -1
  105. package/src/core/{scripting/lua-runner.ts → scripts/lua.ts} +1 -1
  106. package/src/core/tasks/index.ts +1 -1
  107. package/src/core/tools/{chat.ts → chat/chat.ts} +1 -1
  108. package/src/core/tools/{cross-send.ts → chat/cross-send.ts} +1 -1
  109. package/src/core/tools/{history.ts → chat/history.ts} +2 -2
  110. package/src/core/tools/{media.ts → chat/media.ts} +1 -1
  111. package/src/core/tools/{members.ts → chat/members.ts} +2 -2
  112. package/src/core/tools/{messaging.ts → chat/messaging.ts} +2 -2
  113. package/src/core/tools/{moderation.ts → chat/moderation.ts} +2 -2
  114. package/src/core/tools/{stickers.ts → chat/stickers.ts} +2 -2
  115. package/src/core/tools/{whatsapp.ts → chat/whatsapp.ts} +1 -1
  116. package/src/core/tools/content/memory.ts +128 -0
  117. package/src/core/tools/{web.ts → content/web.ts} +1 -1
  118. package/src/core/tools/index.ts +21 -19
  119. package/src/core/tools/{admin.ts → ops/admin.ts} +1 -1
  120. package/src/core/tools/{bridge.ts → ops/bridge.ts} +2 -2
  121. package/src/core/tools/{goals.ts → ops/goals.ts} +2 -2
  122. package/src/core/tools/{mesh.ts → ops/mesh.ts} +1 -1
  123. package/src/core/tools/{models.ts → ops/models.ts} +1 -1
  124. package/src/core/tools/{native.ts → ops/native.ts} +1 -1
  125. package/src/core/tools/{scheduling.ts → ops/scheduling.ts} +1 -1
  126. package/src/core/tools/{scripts.ts → ops/scripts.ts} +1 -1
  127. package/src/core/tools/{skills.ts → ops/skills.ts} +1 -1
  128. package/src/core/tools/{triggers.ts → ops/triggers.ts} +1 -1
  129. package/src/core/tools/types.ts +1 -0
  130. package/src/{util → core/vfs}/workspace.ts +2 -2
  131. package/src/core/weaver/turn-cpu.ts +32 -0
  132. package/src/core/weaver/weaver.ts +36 -0
  133. package/src/frontend/discord/admin.ts +1 -1
  134. package/src/frontend/discord/callbacks/components/backend-select.ts +1 -1
  135. package/src/frontend/discord/callbacks/components/effort.ts +1 -1
  136. package/src/frontend/discord/callbacks/components/index.ts +1 -1
  137. package/src/frontend/discord/callbacks/components/pulse.ts +1 -1
  138. package/src/frontend/discord/callbacks/components/settings.ts +2 -2
  139. package/src/frontend/discord/callbacks/modals.ts +4 -1
  140. package/src/frontend/discord/commands/admin.ts +2 -36
  141. package/src/frontend/discord/commands/definitions.ts +0 -11
  142. package/src/frontend/discord/commands/info.ts +1 -1
  143. package/src/frontend/discord/commands/interaction.ts +1 -1
  144. package/src/frontend/discord/commands/router.ts +0 -3
  145. package/src/frontend/discord/commands/session.ts +8 -2
  146. package/src/frontend/discord/commands/settings.ts +2 -2
  147. package/src/frontend/discord/handlers/messages.ts +1 -1
  148. package/src/frontend/discord/handlers/state.ts +1 -1
  149. package/src/frontend/discord/middleware.ts +2 -26
  150. package/src/frontend/discord/ready.ts +1 -1
  151. package/src/frontend/discord/render.ts +41 -259
  152. package/src/frontend/discord/runtime.ts +1 -2
  153. package/src/frontend/native/{auth.ts → bridge/auth.ts} +2 -2
  154. package/src/frontend/native/{discovery.ts → bridge/discovery.ts} +3 -3
  155. package/src/frontend/native/{routes → bridge/routes}/chats.ts +1 -1
  156. package/src/frontend/native/{routes → bridge/routes}/daemon.ts +1 -1
  157. package/src/frontend/native/{routes → bridge/routes}/host.ts +6 -3
  158. package/src/frontend/native/{routes → bridge/routes}/pre-auth.ts +1 -1
  159. package/src/frontend/native/{server.ts → bridge/server.ts} +3 -3
  160. package/src/frontend/native/{tls.ts → bridge/tls.ts} +2 -2
  161. package/src/frontend/native/{chat-lifecycle.ts → chats/chat-lifecycle.ts} +3 -3
  162. package/src/frontend/native/{chat-wire.ts → chats/chat-wire.ts} +4 -4
  163. package/src/frontend/native/{chats.ts → chats/chats.ts} +4 -4
  164. package/src/frontend/native/{empty-chat-sweep.ts → chats/empty-chat-sweep.ts} +4 -4
  165. package/src/frontend/native/{history.ts → chats/history.ts} +7 -7
  166. package/src/frontend/native/{reset.ts → chats/reset.ts} +7 -7
  167. package/src/frontend/native/index.ts +13 -10
  168. package/src/frontend/native/{media.ts → media/media.ts} +3 -3
  169. package/src/frontend/native/runtime.ts +1 -1
  170. package/src/frontend/native/{control.ts → surface/control.ts} +4 -4
  171. package/src/frontend/native/{extensions.ts → surface/extensions.ts} +12 -9
  172. package/src/frontend/native/{handlers.ts → surface/handlers.ts} +19 -14
  173. package/src/frontend/native/{logs.ts → surface/logs.ts} +2 -2
  174. package/src/frontend/native/{memory.ts → surface/memory.ts} +2 -2
  175. package/src/frontend/native/{models.ts → surface/models.ts} +17 -10
  176. package/src/frontend/native/{settings.ts → surface/settings.ts} +9 -9
  177. package/src/frontend/native/{status.ts → surface/status.ts} +3 -3
  178. package/src/frontend/native/{actions.ts → turn/actions.ts} +7 -4
  179. package/src/frontend/native/{context.ts → turn/context.ts} +8 -8
  180. package/src/frontend/native/{emit.ts → turn/emit.ts} +7 -7
  181. package/src/frontend/native/{queue.ts → turn/queue.ts} +3 -3
  182. package/src/frontend/native/{turn-meta.ts → turn/turn-meta.ts} +2 -2
  183. package/src/frontend/native/{turn.ts → turn/turn.ts} +9 -9
  184. package/src/frontend/{shared → presentation}/format.ts +4 -3
  185. package/src/frontend/{shared → presentation}/model-commands.ts +1 -1
  186. package/src/frontend/{shared → presentation}/reasoning-levels.ts +12 -0
  187. package/src/frontend/presentation/reports.ts +493 -0
  188. package/src/frontend/{shared → presentation}/session-status.ts +36 -9
  189. package/src/frontend/{shared → presentation}/status-context.ts +101 -2
  190. package/src/frontend/teams/commands.ts +2 -2
  191. package/src/frontend/teams/turn.ts +1 -1
  192. package/src/frontend/telegram/admin/background.ts +1 -1
  193. package/src/frontend/telegram/admin/health.ts +1 -1
  194. package/src/frontend/telegram/admin/sessions.ts +1 -1
  195. package/src/frontend/telegram/callbacks/effort.ts +2 -2
  196. package/src/frontend/telegram/callbacks/metrics.ts +1 -1
  197. package/src/frontend/telegram/callbacks/model/backend.ts +1 -1
  198. package/src/frontend/telegram/callbacks/model/views.ts +1 -1
  199. package/src/frontend/telegram/callbacks/pulse.ts +1 -1
  200. package/src/frontend/telegram/callbacks/query.ts +1 -1
  201. package/src/frontend/telegram/callbacks/settings.ts +3 -3
  202. package/src/frontend/telegram/commands/admin.ts +6 -41
  203. package/src/frontend/telegram/commands/index.ts +1 -1
  204. package/src/frontend/telegram/commands/info.ts +2 -2
  205. package/src/frontend/telegram/commands/session.ts +9 -3
  206. package/src/frontend/telegram/commands/settings.ts +5 -3
  207. package/src/frontend/telegram/handlers/messages.ts +0 -10
  208. package/src/frontend/telegram/handlers/state.ts +1 -1
  209. package/src/frontend/telegram/index.ts +1 -5
  210. package/src/frontend/telegram/middleware.ts +1 -15
  211. package/src/frontend/telegram/model-menu.ts +1 -1
  212. package/src/frontend/telegram/render/html.ts +30 -0
  213. package/src/frontend/telegram/{helpers → render}/menu.ts +46 -19
  214. package/src/frontend/telegram/render/reports.ts +182 -0
  215. package/src/frontend/terminal/builtins/context.ts +2 -2
  216. package/src/frontend/terminal/builtins/session.ts +1 -1
  217. package/src/frontend/terminal/builtins/status.ts +2 -2
  218. package/src/frontend/terminal/index.ts +2 -2
  219. package/src/frontend/terminal/renderer.ts +1 -1
  220. package/src/frontend/whatsapp/access.ts +2 -2
  221. package/src/frontend/whatsapp/actions/history.ts +2 -2
  222. package/src/frontend/whatsapp/actions/messaging.ts +2 -2
  223. package/src/frontend/whatsapp/actions/moderation.ts +1 -1
  224. package/src/frontend/whatsapp/actions/send.ts +1 -1
  225. package/src/frontend/whatsapp/commands.ts +12 -6
  226. package/src/frontend/whatsapp/{connection.ts → connection/connection.ts} +5 -5
  227. package/src/frontend/whatsapp/{pairing-service.ts → connection/pairing-service.ts} +3 -3
  228. package/src/frontend/whatsapp/{wa-logger.ts → connection/wa-logger.ts} +2 -2
  229. package/src/frontend/whatsapp/index.ts +4 -4
  230. package/src/frontend/whatsapp/{inbound.ts → messages/inbound.ts} +15 -15
  231. package/src/frontend/whatsapp/{media-store.ts → messages/media-store.ts} +4 -4
  232. package/src/frontend/whatsapp/{message-store.ts → messages/message-store.ts} +1 -1
  233. package/src/frontend/whatsapp/{turn-recovery.ts → messages/turn-recovery.ts} +4 -4
  234. package/src/frontend/whatsapp/registry.ts +2 -2
  235. package/src/frontend/whatsapp/runtime.ts +1 -1
  236. package/src/index.ts +6 -3
  237. package/src/storage/db.ts +6 -1
  238. package/src/storage/memory.ts +59 -8
  239. package/src/storage/metrics.ts +38 -0
  240. package/src/storage/repositories/sessions-repo.ts +6 -0
  241. package/src/storage/session-record.ts +13 -2
  242. package/src/storage/sessions.ts +12 -0
  243. package/src/storage/sql/db.sql +5 -0
  244. package/src/storage/sql/schema.sql +4 -0
  245. package/src/storage/sql/sessions.sql +3 -3
  246. package/src/storage/sql/statements.generated.ts +10 -3
  247. package/src/storage/sql/turn-meta.sql +1 -1
  248. package/src/util/log.ts +1 -1
  249. package/src/util/paths.ts +0 -2
  250. package/src/util/runtime.ts +1 -1
  251. package/src/core/engine/gateway-actions/native.ts +0 -1035
  252. package/src/core/soul/README.md +0 -110
  253. package/src/core/soul/RESEARCH.md +0 -98
  254. package/src/core/soul/associative.ts +0 -98
  255. package/src/core/soul/centrality.ts +0 -98
  256. package/src/core/soul/cluster.ts +0 -83
  257. package/src/core/soul/compiler.ts +0 -207
  258. package/src/core/soul/consolidate.ts +0 -179
  259. package/src/core/soul/critic.ts +0 -162
  260. package/src/core/soul/dag.ts +0 -265
  261. package/src/core/soul/delta.ts +0 -123
  262. package/src/core/soul/drift.ts +0 -99
  263. package/src/core/soul/embedder.ts +0 -129
  264. package/src/core/soul/emergent-critic.ts +0 -96
  265. package/src/core/soul/forgetting.ts +0 -131
  266. package/src/core/soul/governance.ts +0 -93
  267. package/src/core/soul/hash.ts +0 -97
  268. package/src/core/soul/hdc.ts +0 -154
  269. package/src/core/soul/kernel.ts +0 -540
  270. package/src/core/soul/lattice.ts +0 -103
  271. package/src/core/soul/lens.ts +0 -110
  272. package/src/core/soul/projector.ts +0 -240
  273. package/src/core/soul/reflect.ts +0 -170
  274. package/src/core/soul/reflex.ts +0 -164
  275. package/src/core/soul/retrieve.ts +0 -146
  276. package/src/core/soul/salience.ts +0 -146
  277. package/src/core/soul/service.ts +0 -204
  278. package/src/core/soul/settings.ts +0 -47
  279. package/src/core/soul/signals.ts +0 -117
  280. package/src/core/soul/talon-embedder.ts +0 -80
  281. package/src/core/soul/taps.ts +0 -199
  282. package/src/core/soul/types.ts +0 -298
  283. package/src/core/soul/valence.ts +0 -83
  284. package/src/frontend/telegram/helpers/diagnostics.ts +0 -428
  285. package/src/frontend/telegram/helpers/format.ts +0 -42
  286. package/src/frontend/telegram/helpers/index.ts +0 -13
  287. package/src/util/cleanup-registry.ts +0 -36
  288. /package/src/backend/{shared → runtime}/frontends.ts +0 -0
  289. /package/src/backend/{shared → runtime}/metrics.ts +0 -0
  290. /package/src/backend/{shared → runtime}/sleep.ts +0 -0
  291. /package/src/backend/{shared → runtime/turn}/flow-violation.ts +0 -0
  292. /package/src/backend/{shared → runtime}/usage.ts +0 -0
  293. /package/src/{util/mcp-launcher.ts → core/mcp-hub/launcher.ts} +0 -0
  294. /package/src/{util → core/tools/content}/web-content.ts +0 -0
  295. /package/src/core/tools/{mcp-env.ts → ops/mcp-env.ts} +0 -0
  296. /package/src/{util → core/weaver}/session-name.ts +0 -0
  297. /package/src/frontend/native/{routes → bridge/routes}/index.ts +0 -0
  298. /package/src/frontend/native/{routes → bridge/routes}/memory.ts +0 -0
  299. /package/src/frontend/native/{routes → bridge/routes}/mesh.ts +0 -0
  300. /package/src/frontend/native/{routes → bridge/routes}/models.ts +0 -0
  301. /package/src/frontend/native/{routes → bridge/routes}/params.ts +0 -0
  302. /package/src/frontend/native/{routes → bridge/routes}/table.ts +0 -0
  303. /package/src/frontend/native/{tool-result.ts → turn/tool-result.ts} +0 -0
  304. /package/src/frontend/{shared → presentation}/access.ts +0 -0
  305. /package/src/frontend/{shared → presentation}/plan-usage-report.ts +0 -0
  306. /package/src/frontend/whatsapp/{auth-state.ts → connection/auth-state.ts} +0 -0
  307. /package/src/frontend/whatsapp/{identity.ts → connection/identity.ts} +0 -0
  308. /package/src/frontend/whatsapp/{pairing-lock.ts → connection/pairing-lock.ts} +0 -0
  309. /package/src/frontend/whatsapp/{pairing.ts → connection/pairing.ts} +0 -0
  310. /package/src/frontend/whatsapp/{pins.ts → messages/pins.ts} +0 -0
@@ -1,179 +0,0 @@
1
- /**
2
- * Soul Kernel — organic consolidation ("the dream").
3
- *
4
- * Crystallization clusters loose evidence once. Consolidation is what makes the
5
- * value graph *alive*: it periodically re-examines the whole lattice and lets it
6
- * reorganize as experience accumulates — values that have drifted together merge
7
- * into one, carrying their learned salience and evidence forward.
8
- *
9
- * Two principles keep it organic rather than over-programmed:
10
- *
11
- * 1. The merge threshold is ADAPTIVE — derived from the current distribution of
12
- * nearest-neighbor distances among values, not a magic constant. The lattice
13
- * decides its own granularity from its own shape, and that shape changes as
14
- * it grows.
15
- *
16
- * 2. Learned state is MIGRATED, never lost. A merge supersedes the old values
17
- * with a new content-addressed node, sums their salience/evidence into it,
18
- * and records `supersedes` edges + a Spine event. Nothing is deleted — the
19
- * old nodes simply fall out of projection (filtered as superseded) and decay.
20
- *
21
- * No model decides what merges. Geometry and accumulated weight do.
22
- */
23
-
24
- import type { SoulDag } from "./dag.js";
25
- import { cosineDistance, medoidIndex, type Embedder } from "./embedder.js";
26
- import { effectiveSalience } from "./salience.js";
27
- import type { Hash, SoulConfig, ValuePayload } from "./types.js";
28
- import { halfLifeForKind } from "./types.js";
29
-
30
- /** A value is dead once something supersedes it. */
31
- export function isSuperseded(dag: SoulDag, value: Hash): boolean {
32
- return dag.allEdges("supersedes").some((e) => e.to === value);
33
- }
34
-
35
- /** Live (non-superseded) value nodes. */
36
- export function liveValues(dag: SoulDag): Hash[] {
37
- return dag
38
- .nodesOfKind("value")
39
- .map((n) => n.hash)
40
- .filter((h) => !isSuperseded(dag, h));
41
- }
42
-
43
- function evidenceText(dag: SoulDag, hash: Hash): string {
44
- const node = dag.getNode(hash);
45
- return node?.payload.kind === "evidence" ? node.payload.text : "";
46
- }
47
-
48
- function valueOf(dag: SoulDag, hash: Hash): ValuePayload {
49
- return dag.getNode(hash)!.payload as ValuePayload;
50
- }
51
-
52
- /** Median of a numeric array (0 for empty). */
53
- function median(xs: number[]): number {
54
- if (xs.length === 0) return 0;
55
- const s = [...xs].sort((a, b) => a - b);
56
- const mid = Math.floor(s.length / 2);
57
- return s.length % 2 ? s[mid]! : (s[mid - 1]! + s[mid]!) / 2;
58
- }
59
-
60
- export interface ConsolidateOptions {
61
- readonly now: number;
62
- /** Override the adaptive threshold (mainly for tests). */
63
- readonly mergeThreshold?: number;
64
- /** Fraction of the median NN distance used as the adaptive threshold. */
65
- readonly adaptiveFactor?: number;
66
- }
67
-
68
- export interface ConsolidateResult {
69
- readonly created: Hash[];
70
- readonly superseded: Hash[];
71
- readonly groups: number;
72
- readonly threshold: number;
73
- }
74
-
75
- /**
76
- * Run one consolidation pass. Returns what merged. Caller commits.
77
- */
78
- export async function consolidate(
79
- dag: SoulDag,
80
- embedder: Embedder,
81
- cfg: SoulConfig,
82
- opts: ConsolidateOptions,
83
- ): Promise<ConsolidateResult> {
84
- const values = liveValues(dag);
85
- if (values.length < 2) {
86
- return { created: [], superseded: [], groups: 0, threshold: 0 };
87
- }
88
-
89
- // Represent each value by its medoid embedding.
90
- const repTexts = values.map((h) => evidenceText(dag, valueOf(dag, h).medoid));
91
- const reps = await embedder.embed(repTexts);
92
-
93
- // Adaptive threshold: how close is "unusually close" for THIS lattice right now.
94
- const nn: number[] = [];
95
- for (let i = 0; i < values.length; i++) {
96
- let best = Infinity;
97
- for (let j = 0; j < values.length; j++) {
98
- if (i === j) continue;
99
- best = Math.min(best, cosineDistance(reps[i]!, reps[j]!));
100
- }
101
- if (Number.isFinite(best)) nn.push(best);
102
- }
103
- const factor = opts.adaptiveFactor ?? 0.6;
104
- const threshold = opts.mergeThreshold ?? median(nn) * factor;
105
-
106
- // Connected components over the "close enough to merge" graph (union-find).
107
- const parent = values.map((_, i) => i);
108
- const find = (x: number): number => {
109
- while (parent[x] !== x) {
110
- parent[x] = parent[parent[x]!]!;
111
- x = parent[x]!;
112
- }
113
- return x;
114
- };
115
- const union = (a: number, b: number): void => {
116
- parent[find(a)] = find(b);
117
- };
118
- for (let i = 0; i < values.length; i++) {
119
- for (let j = i + 1; j < values.length; j++) {
120
- if (cosineDistance(reps[i]!, reps[j]!) <= threshold) union(i, j);
121
- }
122
- }
123
-
124
- // Gather groups of size > 1.
125
- const groups = new Map<number, number[]>();
126
- for (let i = 0; i < values.length; i++) {
127
- const root = find(i);
128
- (groups.get(root) ?? groups.set(root, []).get(root)!).push(i);
129
- }
130
-
131
- const created: Hash[] = [];
132
- const superseded: Hash[] = [];
133
-
134
- for (const members of groups.values()) {
135
- if (members.length < 2) continue;
136
-
137
- const oldValues = members.map((i) => values[i]!);
138
- // Union of all member evidence across the merging values.
139
- const evidenceSet = new Set<Hash>();
140
- for (const v of oldValues) {
141
- const p = valueOf(dag, v);
142
- evidenceSet.add(p.medoid);
143
- for (const m of p.members) evidenceSet.add(m);
144
- }
145
- const evidence = [...evidenceSet].sort();
146
-
147
- // Recompute the medoid over the merged evidence — its new label emerges.
148
- const evTexts = evidence.map((h) => evidenceText(dag, h));
149
- const evVecs = await embedder.embed(evTexts);
150
- const medoid = evidence[medoidIndex(evVecs)]!;
151
-
152
- const merged = dag.addNode(
153
- { kind: "value", members: evidence, medoid },
154
- opts.now,
155
- );
156
- created.push(merged);
157
-
158
- // Migrate learned state forward: sum decayed salience + evidence + activations.
159
- const target = dag.stateOf(merged);
160
- for (const v of oldValues) {
161
- const s = dag.stateOf(v);
162
- target.salience += effectiveSalience(
163
- s,
164
- opts.now,
165
- halfLifeForKind(cfg, "value"),
166
- );
167
- target.evidence += s.evidence;
168
- target.activations += s.activations;
169
- const e = dag.edge(merged, "supersedes", v, opts.now);
170
- e.weight = 1;
171
- e.updatedAt = opts.now;
172
- if (v !== merged) superseded.push(v);
173
- }
174
- target.lastActivatedAt = opts.now;
175
- dag.touch(merged);
176
- }
177
-
178
- return { created, superseded, groups: created.length, threshold };
179
- }
@@ -1,162 +0,0 @@
1
- /**
2
- * Soul Kernel — the Critic. A self-model of failure modes, made mechanical.
3
- *
4
- * In v3 the Critic is not a reasoning model second-guessing the first; it is a
5
- * set of frozen, feature-based classifiers. Each scores a candidate text (a
6
- * drafted reply, or a projection surface) against one of Talon's documented
7
- * failure modes — wall-of-text, sycophancy, emoji-overload — using a linear
8
- * combination of cheap lexical/structural features and a fixed threshold. A
9
- * classifier is a fixed function, not an agent, so this stays strictly within
10
- * the model-free principle.
11
- *
12
- * Embedding-based gates (contradiction against the KG, drift from the core
13
- * centroid) are a later phase; they too are distance comparisons, not model
14
- * calls. The lexical classifiers here are the immediately-useful, fully
15
- * dependency-free core.
16
- */
17
-
18
- import { estimateTokens } from "./projector.js";
19
-
20
- type FailureMode = "wall-of-text" | "sycophancy" | "emoji-overload";
21
-
22
- export interface CritiqueFeatures {
23
- readonly tokens: number;
24
- readonly sentences: number;
25
- /** Hedging phrases per 100 tokens. */
26
- readonly hedgeRate: number;
27
- /** Emoji characters per 100 tokens. */
28
- readonly emojiDensity: number;
29
- /** Count of sycophancy-lexicon hits. */
30
- readonly sycophancyHits: number;
31
- }
32
-
33
- export interface Critique {
34
- readonly mode: FailureMode;
35
- /** Continuous severity; >= 1 means the fixed threshold was crossed. */
36
- readonly score: number;
37
- readonly flagged: boolean;
38
- readonly reason: string;
39
- }
40
-
41
- const HEDGES = [
42
- "i think",
43
- "i guess",
44
- "maybe",
45
- "perhaps",
46
- "sort of",
47
- "kind of",
48
- "i suppose",
49
- "probably",
50
- "it seems",
51
- ];
52
-
53
- const SYCOPHANCY = [
54
- "great question",
55
- "excellent question",
56
- "you're absolutely right",
57
- "you are absolutely right",
58
- "i'd be happy to",
59
- "i would be happy to",
60
- "absolutely!",
61
- "certainly!",
62
- "of course!",
63
- "happy to help",
64
- "great point",
65
- ];
66
-
67
- // Emoji detection without a heavy dependency: pictographic + symbol ranges.
68
- const EMOJI_RE =
69
- // Variation selectors are combining marks — alternated separately so the
70
- // character class holds only standalone pictographic ranges
71
- // (no-misleading-character-class).
72
- /[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2190}-\u{21FF}\u{2B00}-\u{2BFF}\u{1F000}-\u{1F0FF}]|[\u{FE00}-\u{FE0F}]/gu;
73
-
74
- function countOccurrences(
75
- haystack: string,
76
- needles: readonly string[],
77
- ): number {
78
- let n = 0;
79
- for (const needle of needles) {
80
- let from = 0;
81
- for (;;) {
82
- const i = haystack.indexOf(needle, from);
83
- if (i === -1) break;
84
- n++;
85
- from = i + needle.length;
86
- }
87
- }
88
- return n;
89
- }
90
-
91
- /** Extract the cheap features the classifiers run over. Pure + deterministic. */
92
- export function extractFeatures(text: string): CritiqueFeatures {
93
- const lower = text.toLowerCase();
94
- const tokens = Math.max(1, estimateTokens(text));
95
- const sentences = Math.max(1, (text.match(/[.!?](\s|$)/g) ?? []).length);
96
- const emoji = (text.match(EMOJI_RE) ?? []).length;
97
- const hedges = countOccurrences(lower, HEDGES);
98
- return {
99
- tokens,
100
- sentences,
101
- hedgeRate: (hedges / tokens) * 100,
102
- emojiDensity: (emoji / tokens) * 100,
103
- sycophancyHits: countOccurrences(lower, SYCOPHANCY),
104
- };
105
- }
106
-
107
- export interface CritiqueThresholds {
108
- /** Token count above which prose is "wall-of-text". */
109
- readonly maxTokens: number;
110
- /** Sycophancy score (hits + hedge contribution) threshold. */
111
- readonly maxSycophancy: number;
112
- /** Emoji-per-100-tokens threshold. */
113
- readonly maxEmojiDensity: number;
114
- }
115
-
116
- export const DEFAULT_THRESHOLDS: CritiqueThresholds = {
117
- maxTokens: 320,
118
- maxSycophancy: 1,
119
- maxEmojiDensity: 6,
120
- };
121
-
122
- /**
123
- * Run the frozen classifiers over a candidate text. Returns one critique per
124
- * failure mode; `flagged` is true when its normalized score reaches 1.
125
- */
126
- export function critique(
127
- text: string,
128
- thresholds: CritiqueThresholds = DEFAULT_THRESHOLDS,
129
- ): Critique[] {
130
- const f = extractFeatures(text);
131
-
132
- const sycophancyScore = f.sycophancyHits + f.hedgeRate / 5;
133
- const wall = f.tokens / thresholds.maxTokens;
134
- const syco = sycophancyScore / Math.max(0.0001, thresholds.maxSycophancy);
135
- const emoji = f.emojiDensity / Math.max(0.0001, thresholds.maxEmojiDensity);
136
-
137
- return [
138
- {
139
- mode: "wall-of-text",
140
- score: wall,
141
- flagged: wall >= 1,
142
- reason: `${f.tokens} tokens vs budget ${thresholds.maxTokens}`,
143
- },
144
- {
145
- mode: "sycophancy",
146
- score: syco,
147
- flagged: syco >= 1,
148
- reason: `${f.sycophancyHits} sycophancy hits, hedge ${f.hedgeRate.toFixed(1)}/100tok`,
149
- },
150
- {
151
- mode: "emoji-overload",
152
- score: emoji,
153
- flagged: emoji >= 1,
154
- reason: `${f.emojiDensity.toFixed(1)} emoji/100tok vs ${thresholds.maxEmojiDensity}`,
155
- },
156
- ];
157
- }
158
-
159
- /** True if any failure mode tripped its threshold. */
160
- export function isFlagged(critiques: readonly Critique[]): boolean {
161
- return critiques.some((c) => c.flagged);
162
- }
@@ -1,265 +0,0 @@
1
- /**
2
- * Soul Kernel — the content-addressed DAG store.
3
- *
4
- * Holds three things, kept rigorously separate:
5
- *
6
- * 1. Nodes — immutable, content-addressed identity content (the Merkle DAG).
7
- * 2. State — mutable per-node "weather" (salience, evidence weight).
8
- * 3. Edges — mutable associative links (Hebbian co-activation, tension).
9
- *
10
- * Referential integrity is enforced on insert: a node may only reference child
11
- * hashes that already exist, so the DAG can never contain a dangling pointer and
12
- * the Merkle root is always sound.
13
- *
14
- * Dirty-tracking is structural and underpins "partial recompilation": only the
15
- * nodes added or whose state mutated since the last commit are reported dirty, so
16
- * downstream consumers (projector, critic) reprocess a handful of nodes, not the
17
- * whole self.
18
- */
19
-
20
- import { hashPayload, merkleRoot } from "./hash.js";
21
- import type {
22
- ActivationState,
23
- AssocEdge,
24
- EdgeKind,
25
- Hash,
26
- NodePayload,
27
- SoulNode,
28
- } from "./types.js";
29
-
30
- /** The structural (hashed) children a payload references, by kind. */
31
- export function structuralChildren(payload: NodePayload): Hash[] {
32
- switch (payload.kind) {
33
- case "evidence":
34
- case "reflex":
35
- return [];
36
- case "value":
37
- return dedupe([payload.medoid, ...payload.members]);
38
- case "theme":
39
- return dedupe([payload.medoid, ...payload.values]);
40
- case "spine":
41
- return dedupe([
42
- ...payload.affects,
43
- ...(payload.prev ? [payload.prev] : []),
44
- ]);
45
- case "lens":
46
- return dedupe([
47
- ...payload.amplify.map((a) => a.node),
48
- ...payload.evidence,
49
- ]);
50
- }
51
- }
52
-
53
- function dedupe(hashes: Hash[]): Hash[] {
54
- return [...new Set(hashes)];
55
- }
56
-
57
- function freshState(now: number): ActivationState {
58
- return { salience: 0, evidence: 0, activations: 0, lastActivatedAt: now };
59
- }
60
-
61
- /** Edge map key — one edge per (from, kind, to) triple. */
62
- function edgeKey(from: Hash, kind: EdgeKind, to: Hash): string {
63
- return `${from}|${kind}|${to}`;
64
- }
65
-
66
- export interface DagSnapshot {
67
- readonly nodes: readonly SoulNode[];
68
- readonly state: readonly (readonly [Hash, ActivationState])[];
69
- readonly edges: readonly AssocEdge[];
70
- }
71
-
72
- export class SoulDag {
73
- private readonly nodes = new Map<Hash, SoulNode>();
74
- private readonly state = new Map<Hash, ActivationState>();
75
- private readonly edges = new Map<string, AssocEdge>();
76
- /** Reverse adjacency for structural children → parents, for dirty propagation. */
77
- private readonly parents = new Map<Hash, Set<Hash>>();
78
- private readonly dirty = new Set<Hash>();
79
-
80
- // ── Nodes ──────────────────────────────────────────────────────────────────
81
-
82
- /**
83
- * Insert a node by content. Idempotent: identical content returns the same
84
- * hash and does not re-dirty. Throws if any structural child is absent.
85
- */
86
- addNode(payload: NodePayload, now = Date.now()): Hash {
87
- const hash = hashPayload(payload);
88
- if (this.nodes.has(hash)) return hash;
89
-
90
- const children = structuralChildren(payload);
91
- for (const child of children) {
92
- if (!this.nodes.has(child)) {
93
- throw new Error(
94
- `SoulDag.addNode: dangling reference ${child} from ${payload.kind} node`,
95
- );
96
- }
97
- }
98
-
99
- this.nodes.set(hash, { hash, payload });
100
- this.state.set(hash, freshState(now));
101
- for (const child of children) {
102
- let set = this.parents.get(child);
103
- if (!set) {
104
- set = new Set();
105
- this.parents.set(child, set);
106
- }
107
- set.add(hash);
108
- }
109
- this.dirty.add(hash);
110
- return hash;
111
- }
112
-
113
- getNode(hash: Hash): SoulNode | undefined {
114
- return this.nodes.get(hash);
115
- }
116
-
117
- hasNode(hash: Hash): boolean {
118
- return this.nodes.has(hash);
119
- }
120
-
121
- /** All node hashes of a given kind. */
122
- nodesOfKind(kind: NodePayload["kind"]): SoulNode[] {
123
- const out: SoulNode[] = [];
124
- for (const node of this.nodes.values()) {
125
- if (node.payload.kind === kind) out.push(node);
126
- }
127
- return out;
128
- }
129
-
130
- get size(): number {
131
- return this.nodes.size;
132
- }
133
-
134
- // ── State ──────────────────────────────────────────────────────────────────
135
-
136
- /** Mutable activation state for a node (lazily created). */
137
- stateOf(hash: Hash): ActivationState {
138
- let s = this.state.get(hash);
139
- if (!s) {
140
- s = freshState(Date.now());
141
- this.state.set(hash, s);
142
- }
143
- return s;
144
- }
145
-
146
- /** Mark a node (and its structural ancestors) dirty after a state change. */
147
- touch(hash: Hash): void {
148
- if (!this.nodes.has(hash)) return;
149
- this.dirty.add(hash);
150
- }
151
-
152
- // ── Edges ──────────────────────────────────────────────────────────────────
153
-
154
- /** Create or fetch the edge for a triple; weight starts at 0. */
155
- edge(from: Hash, kind: EdgeKind, to: Hash, now = Date.now()): AssocEdge {
156
- const key = edgeKey(from, kind, to);
157
- let e = this.edges.get(key);
158
- if (!e) {
159
- e = { from, to, kind, weight: 0, updatedAt: now };
160
- this.edges.set(key, e);
161
- }
162
- return e;
163
- }
164
-
165
- getEdge(from: Hash, kind: EdgeKind, to: Hash): AssocEdge | undefined {
166
- return this.edges.get(edgeKey(from, kind, to));
167
- }
168
-
169
- /** All edges originating at `from`, optionally filtered by kind. */
170
- edgesFrom(from: Hash, kind?: EdgeKind): AssocEdge[] {
171
- const out: AssocEdge[] = [];
172
- for (const e of this.edges.values()) {
173
- if (e.from === from && (!kind || e.kind === kind)) out.push(e);
174
- }
175
- return out;
176
- }
177
-
178
- /** Every edge in the graph, optionally filtered by kind. */
179
- allEdges(kind?: EdgeKind): AssocEdge[] {
180
- const out: AssocEdge[] = [];
181
- for (const e of this.edges.values()) {
182
- if (!kind || e.kind === kind) out.push(e);
183
- }
184
- return out;
185
- }
186
-
187
- // ── Structure ────────────────────────────────────────────────────────────────
188
-
189
- childrenOf(hash: Hash): Hash[] {
190
- const node = this.nodes.get(hash);
191
- return node ? structuralChildren(node.payload) : [];
192
- }
193
-
194
- parentsOf(hash: Hash): Hash[] {
195
- return [...(this.parents.get(hash) ?? [])];
196
- }
197
-
198
- /** Merkle root over the full set of node content present. The version id. */
199
- root(): Hash {
200
- return merkleRoot(this.nodes.keys());
201
- }
202
-
203
- // ── Dirty-tracking ───────────────────────────────────────────────────────────
204
-
205
- /** Nodes added or touched since the last clear, plus their ancestors. */
206
- dirtySet(): Set<Hash> {
207
- const out = new Set<Hash>();
208
- const stack = [...this.dirty];
209
- while (stack.length) {
210
- const h = stack.pop()!;
211
- if (out.has(h)) continue;
212
- out.add(h);
213
- for (const parent of this.parents.get(h) ?? []) stack.push(parent);
214
- }
215
- return out;
216
- }
217
-
218
- clearDirty(): void {
219
- this.dirty.clear();
220
- }
221
-
222
- // ── Serialization ────────────────────────────────────────────────────────────
223
-
224
- snapshot(): DagSnapshot {
225
- return {
226
- nodes: [...this.nodes.values()],
227
- state: [...this.state.entries()].map(([h, s]) => [h, { ...s }]),
228
- edges: [...this.edges.values()].map((e) => ({ ...e })),
229
- };
230
- }
231
-
232
- /**
233
- * Rebuild a DAG from a snapshot. Nodes are re-inserted in dependency order so
234
- * referential integrity is re-verified on load (a corrupt snapshot throws
235
- * rather than silently producing a broken graph).
236
- */
237
- static restore(snap: DagSnapshot): SoulDag {
238
- const dag = new SoulDag();
239
- const byHash = new Map(snap.nodes.map((n) => [n.hash, n]));
240
- const inserted = new Set<Hash>();
241
-
242
- const insert = (node: SoulNode): void => {
243
- if (inserted.has(node.hash)) return;
244
- for (const child of structuralChildren(node.payload)) {
245
- const childNode = byHash.get(child);
246
- if (!childNode) {
247
- throw new Error(`SoulDag.restore: snapshot missing child ${child}`);
248
- }
249
- insert(childNode);
250
- }
251
- dag.addNode(node.payload);
252
- inserted.add(node.hash);
253
- };
254
- for (const node of snap.nodes) insert(node);
255
-
256
- for (const [hash, s] of snap.state) {
257
- if (dag.nodes.has(hash)) dag.state.set(hash, { ...s });
258
- }
259
- for (const e of snap.edges) {
260
- dag.edges.set(edgeKey(e.from, e.kind, e.to), { ...e });
261
- }
262
- dag.clearDirty();
263
- return dag;
264
- }
265
- }