@bryti/agent 0.0.1 → 0.1.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 (228) hide show
  1. package/Dockerfile +27 -0
  2. package/README.md +91 -51
  3. package/config.example.yml +265 -0
  4. package/dist/active-hours.d.ts +23 -0
  5. package/dist/active-hours.d.ts.map +1 -0
  6. package/dist/active-hours.js +68 -0
  7. package/dist/active-hours.js.map +1 -0
  8. package/dist/agent.d.ts +84 -0
  9. package/dist/agent.d.ts.map +1 -0
  10. package/dist/agent.js +383 -0
  11. package/dist/agent.js.map +1 -0
  12. package/dist/channels/markdown/ir.d.ts +79 -0
  13. package/dist/channels/markdown/ir.d.ts.map +1 -0
  14. package/dist/channels/markdown/ir.js +824 -0
  15. package/dist/channels/markdown/ir.js.map +1 -0
  16. package/dist/channels/markdown/render.d.ts +35 -0
  17. package/dist/channels/markdown/render.d.ts.map +1 -0
  18. package/dist/channels/markdown/render.js +178 -0
  19. package/dist/channels/markdown/render.js.map +1 -0
  20. package/dist/channels/telegram-network-errors.d.ts +27 -0
  21. package/dist/channels/telegram-network-errors.d.ts.map +1 -0
  22. package/dist/channels/telegram-network-errors.js +156 -0
  23. package/dist/channels/telegram-network-errors.js.map +1 -0
  24. package/dist/channels/telegram.d.ts +76 -0
  25. package/dist/channels/telegram.d.ts.map +1 -0
  26. package/dist/channels/telegram.js +814 -0
  27. package/dist/channels/telegram.js.map +1 -0
  28. package/dist/channels/types.d.ts +59 -0
  29. package/dist/channels/types.d.ts.map +1 -0
  30. package/dist/channels/types.js +9 -0
  31. package/dist/channels/types.js.map +1 -0
  32. package/dist/channels/whatsapp.d.ts +45 -0
  33. package/dist/channels/whatsapp.d.ts.map +1 -0
  34. package/dist/channels/whatsapp.js +310 -0
  35. package/dist/channels/whatsapp.js.map +1 -0
  36. package/dist/cli.d.ts +13 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +635 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/commands.d.ts +35 -0
  41. package/dist/commands.d.ts.map +1 -0
  42. package/dist/commands.js +113 -0
  43. package/dist/commands.js.map +1 -0
  44. package/dist/compaction/history.d.ts +17 -0
  45. package/dist/compaction/history.d.ts.map +1 -0
  46. package/dist/compaction/history.js +35 -0
  47. package/dist/compaction/history.js.map +1 -0
  48. package/dist/compaction/index.d.ts +3 -0
  49. package/dist/compaction/index.d.ts.map +1 -0
  50. package/dist/compaction/index.js +3 -0
  51. package/dist/compaction/index.js.map +1 -0
  52. package/dist/compaction/proactive.d.ts +25 -0
  53. package/dist/compaction/proactive.d.ts.map +1 -0
  54. package/dist/compaction/proactive.js +87 -0
  55. package/dist/compaction/proactive.js.map +1 -0
  56. package/dist/compaction/transcript-repair.d.ts +55 -0
  57. package/dist/compaction/transcript-repair.d.ts.map +1 -0
  58. package/dist/compaction/transcript-repair.js +215 -0
  59. package/dist/compaction/transcript-repair.js.map +1 -0
  60. package/dist/config.d.ts +128 -0
  61. package/dist/config.d.ts.map +1 -0
  62. package/dist/config.js +317 -0
  63. package/dist/config.js.map +1 -0
  64. package/dist/crash-recovery.d.ts +23 -0
  65. package/dist/crash-recovery.d.ts.map +1 -0
  66. package/dist/crash-recovery.js +96 -0
  67. package/dist/crash-recovery.js.map +1 -0
  68. package/dist/defaults/extensions/EXTENSIONS.md +158 -0
  69. package/dist/defaults/extensions/documents-hedgedoc.ts +153 -0
  70. package/dist/history.d.ts +31 -0
  71. package/dist/history.d.ts.map +1 -0
  72. package/dist/history.js +49 -0
  73. package/dist/history.js.map +1 -0
  74. package/dist/index.d.ts +19 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +686 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/logger.d.ts +39 -0
  79. package/dist/logger.d.ts.map +1 -0
  80. package/dist/logger.js +143 -0
  81. package/dist/logger.js.map +1 -0
  82. package/dist/memory/conversation-search.d.ts +15 -0
  83. package/dist/memory/conversation-search.d.ts.map +1 -0
  84. package/dist/memory/conversation-search.js +60 -0
  85. package/dist/memory/conversation-search.js.map +1 -0
  86. package/dist/memory/core-memory.d.ts +28 -0
  87. package/dist/memory/core-memory.d.ts.map +1 -0
  88. package/dist/memory/core-memory.js +102 -0
  89. package/dist/memory/core-memory.js.map +1 -0
  90. package/dist/memory/embeddings.d.ts +44 -0
  91. package/dist/memory/embeddings.d.ts.map +1 -0
  92. package/dist/memory/embeddings.js +139 -0
  93. package/dist/memory/embeddings.js.map +1 -0
  94. package/dist/memory/search.d.ts +49 -0
  95. package/dist/memory/search.d.ts.map +1 -0
  96. package/dist/memory/search.js +97 -0
  97. package/dist/memory/search.js.map +1 -0
  98. package/dist/memory/store.d.ts +32 -0
  99. package/dist/memory/store.d.ts.map +1 -0
  100. package/dist/memory/store.js +205 -0
  101. package/dist/memory/store.js.map +1 -0
  102. package/dist/message-queue.d.ts +73 -0
  103. package/dist/message-queue.d.ts.map +1 -0
  104. package/dist/message-queue.js +188 -0
  105. package/dist/message-queue.js.map +1 -0
  106. package/dist/model-infra.d.ts +64 -0
  107. package/dist/model-infra.d.ts.map +1 -0
  108. package/dist/model-infra.js +202 -0
  109. package/dist/model-infra.js.map +1 -0
  110. package/dist/projection/format.d.ts +10 -0
  111. package/dist/projection/format.d.ts.map +1 -0
  112. package/dist/projection/format.js +30 -0
  113. package/dist/projection/format.js.map +1 -0
  114. package/dist/projection/index.d.ts +11 -0
  115. package/dist/projection/index.d.ts.map +1 -0
  116. package/dist/projection/index.js +9 -0
  117. package/dist/projection/index.js.map +1 -0
  118. package/dist/projection/reflection.d.ts +119 -0
  119. package/dist/projection/reflection.d.ts.map +1 -0
  120. package/dist/projection/reflection.js +422 -0
  121. package/dist/projection/reflection.js.map +1 -0
  122. package/dist/projection/store.d.ts +144 -0
  123. package/dist/projection/store.d.ts.map +1 -0
  124. package/dist/projection/store.js +519 -0
  125. package/dist/projection/store.js.map +1 -0
  126. package/dist/projection/tools.d.ts +11 -0
  127. package/dist/projection/tools.d.ts.map +1 -0
  128. package/dist/projection/tools.js +237 -0
  129. package/dist/projection/tools.js.map +1 -0
  130. package/dist/scheduler.d.ts +36 -0
  131. package/dist/scheduler.d.ts.map +1 -0
  132. package/dist/scheduler.js +286 -0
  133. package/dist/scheduler.js.map +1 -0
  134. package/dist/system-prompt.d.ts +41 -0
  135. package/dist/system-prompt.d.ts.map +1 -0
  136. package/dist/system-prompt.js +162 -0
  137. package/dist/system-prompt.js.map +1 -0
  138. package/dist/time.d.ts +52 -0
  139. package/dist/time.d.ts.map +1 -0
  140. package/dist/time.js +138 -0
  141. package/dist/time.js.map +1 -0
  142. package/dist/tools/archival-memory-tool.d.ts +8 -0
  143. package/dist/tools/archival-memory-tool.d.ts.map +1 -0
  144. package/dist/tools/archival-memory-tool.js +68 -0
  145. package/dist/tools/archival-memory-tool.js.map +1 -0
  146. package/dist/tools/conversation-search-tool.d.ts +6 -0
  147. package/dist/tools/conversation-search-tool.d.ts.map +1 -0
  148. package/dist/tools/conversation-search-tool.js +28 -0
  149. package/dist/tools/conversation-search-tool.js.map +1 -0
  150. package/dist/tools/core-memory-tool.d.ts +7 -0
  151. package/dist/tools/core-memory-tool.d.ts.map +1 -0
  152. package/dist/tools/core-memory-tool.js +59 -0
  153. package/dist/tools/core-memory-tool.js.map +1 -0
  154. package/dist/tools/fetch-url.d.ts +15 -0
  155. package/dist/tools/fetch-url.d.ts.map +1 -0
  156. package/dist/tools/fetch-url.js +76 -0
  157. package/dist/tools/fetch-url.js.map +1 -0
  158. package/dist/tools/files.d.ts +10 -0
  159. package/dist/tools/files.d.ts.map +1 -0
  160. package/dist/tools/files.js +127 -0
  161. package/dist/tools/files.js.map +1 -0
  162. package/dist/tools/index.d.ts +17 -0
  163. package/dist/tools/index.d.ts.map +1 -0
  164. package/dist/tools/index.js +118 -0
  165. package/dist/tools/index.js.map +1 -0
  166. package/dist/tools/result.d.ts +21 -0
  167. package/dist/tools/result.d.ts.map +1 -0
  168. package/dist/tools/result.js +36 -0
  169. package/dist/tools/result.js.map +1 -0
  170. package/dist/tools/skill-install.d.ts +17 -0
  171. package/dist/tools/skill-install.d.ts.map +1 -0
  172. package/dist/tools/skill-install.js +148 -0
  173. package/dist/tools/skill-install.js.map +1 -0
  174. package/dist/tools/web-search.d.ts +42 -0
  175. package/dist/tools/web-search.d.ts.map +1 -0
  176. package/dist/tools/web-search.js +237 -0
  177. package/dist/tools/web-search.js.map +1 -0
  178. package/dist/trust/guardrail.d.ts +60 -0
  179. package/dist/trust/guardrail.d.ts.map +1 -0
  180. package/dist/trust/guardrail.js +171 -0
  181. package/dist/trust/guardrail.js.map +1 -0
  182. package/dist/trust/index.d.ts +12 -0
  183. package/dist/trust/index.d.ts.map +1 -0
  184. package/dist/trust/index.js +12 -0
  185. package/dist/trust/index.js.map +1 -0
  186. package/dist/trust/store.d.ts +118 -0
  187. package/dist/trust/store.d.ts.map +1 -0
  188. package/dist/trust/store.js +209 -0
  189. package/dist/trust/store.js.map +1 -0
  190. package/dist/trust/wrapper.d.ts +36 -0
  191. package/dist/trust/wrapper.d.ts.map +1 -0
  192. package/dist/trust/wrapper.js +142 -0
  193. package/dist/trust/wrapper.js.map +1 -0
  194. package/dist/usage.d.ts +53 -0
  195. package/dist/usage.d.ts.map +1 -0
  196. package/dist/usage.js +124 -0
  197. package/dist/usage.js.map +1 -0
  198. package/dist/util/math.d.ts +9 -0
  199. package/dist/util/math.d.ts.map +1 -0
  200. package/dist/util/math.js +22 -0
  201. package/dist/util/math.js.map +1 -0
  202. package/dist/util/ssrf.d.ts +21 -0
  203. package/dist/util/ssrf.d.ts.map +1 -0
  204. package/dist/util/ssrf.js +77 -0
  205. package/dist/util/ssrf.js.map +1 -0
  206. package/dist/workers/index.d.ts +8 -0
  207. package/dist/workers/index.d.ts.map +1 -0
  208. package/dist/workers/index.js +7 -0
  209. package/dist/workers/index.js.map +1 -0
  210. package/dist/workers/registry.d.ts +53 -0
  211. package/dist/workers/registry.d.ts.map +1 -0
  212. package/dist/workers/registry.js +38 -0
  213. package/dist/workers/registry.js.map +1 -0
  214. package/dist/workers/scoped-tools.d.ts +21 -0
  215. package/dist/workers/scoped-tools.d.ts.map +1 -0
  216. package/dist/workers/scoped-tools.js +111 -0
  217. package/dist/workers/scoped-tools.js.map +1 -0
  218. package/dist/workers/spawn.d.ts +62 -0
  219. package/dist/workers/spawn.d.ts.map +1 -0
  220. package/dist/workers/spawn.js +314 -0
  221. package/dist/workers/spawn.js.map +1 -0
  222. package/dist/workers/tools.d.ts +26 -0
  223. package/dist/workers/tools.d.ts.map +1 -0
  224. package/dist/workers/tools.js +380 -0
  225. package/dist/workers/tools.js.map +1 -0
  226. package/docker-compose.yml +72 -0
  227. package/package.json +16 -1
  228. package/run.sh +27 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Projection reflection pass.
3
+ *
4
+ * Lightweight background job that scans recent conversation history for
5
+ * future references the agent missed during live chat.
6
+ *
7
+ * Runs every 30 min via cron. Reads the JSONL audit log, makes a single
8
+ * completeSimple() call with a narrow extraction prompt (no agent loop,
9
+ * no tools), parses the JSON output, and writes projections directly to
10
+ * SQLite. Existing pending projections are included in the prompt so the
11
+ * model won't duplicate them. A per-user timestamp tracks the last run
12
+ * to skip unchanged transcripts.
13
+ *
14
+ * Why completeSimple() instead of a full agent loop?
15
+ * The reflection pass has no side effects and requires no tool calls. It only
16
+ * needs one prompt in and one JSON blob out. Using a full agent loop would add
17
+ * latency, cost, and the risk of unintended tool invocations. completeSimple()
18
+ * is cheaper, faster, and keeps the pass strictly read-only from the model's
19
+ * perspective.
20
+ */
21
+ import type { Config } from "../config.js";
22
+ import type { ProjectionResolution, ProjectionStore } from "./store.js";
23
+ export interface ProjectionCandidate {
24
+ summary: string;
25
+ when?: string;
26
+ resolution?: ProjectionResolution;
27
+ context?: string;
28
+ }
29
+ export interface ArchiveCandidate {
30
+ content: string;
31
+ }
32
+ export interface ReflectionOutput {
33
+ project: ProjectionCandidate[];
34
+ /**
35
+ * TODO: archive candidates are extracted from the LLM output but are not
36
+ * currently written anywhere. This field is a placeholder for a future
37
+ * feature that would auto-insert noteworthy facts into archival memory
38
+ * during the reflection pass.
39
+ */
40
+ archive: ArchiveCandidate[];
41
+ }
42
+ export interface ReflectionResult {
43
+ /** Number of projections written to the store. */
44
+ projectionsAdded: number;
45
+ /** Raw candidates extracted (before dedup). */
46
+ candidates: ProjectionCandidate[];
47
+ /** Whether reflection was skipped (no new messages). */
48
+ skipped: boolean;
49
+ /** Reason for skipping, if applicable. */
50
+ skipReason?: string;
51
+ }
52
+ interface HistoryEntry {
53
+ role: "user" | "assistant" | "system" | "tool";
54
+ content: string;
55
+ timestamp: string;
56
+ }
57
+ /**
58
+ * Read user+assistant messages from the JSONL audit log for the last
59
+ * `windowMinutes`. Returns entries in chronological order, capped at
60
+ * `maxMessages`.
61
+ *
62
+ * Reads from the JSONL audit log written by src/compaction/history.ts, NOT
63
+ * from the pi SDK session file. The audit log is the only way to get
64
+ * structured turn-by-turn history outside of a live session context: the pi
65
+ * session file is append-only and interleaved with tool scaffolding, whereas
66
+ * the audit log contains clean role/content/timestamp records per message.
67
+ */
68
+ export declare function readRecentHistory(historyDir: string, windowMinutes: number, maxMessages?: number): HistoryEntry[];
69
+ /**
70
+ * Keyword patterns that suggest the conversation contains future commitments,
71
+ * plans, reminders, or time-specific references worth sending to the LLM.
72
+ *
73
+ * Organised into three semantic buckets for easy extension:
74
+ * - Time references: explicit temporal expressions
75
+ * - Intent markers: nouns that commonly describe scheduled events
76
+ * - Commitment verbs: phrases that express intent to act in the future
77
+ *
78
+ * Each entry is a plain string matched case-insensitively as a whole-word or
79
+ * phrase boundary pattern. Apostrophes in contractions ("I'll", "let's") are
80
+ * included literally; the regex wraps each keyword with \b so "calling" does
81
+ * not match "call", but multi-word phrases like "next week" match anywhere in
82
+ * the text.
83
+ */
84
+ export declare const FUTURE_INTENT_PATTERNS: RegExp[];
85
+ /**
86
+ * Returns true when at least one turn in the transcript matches a
87
+ * future-intent pattern. Only user and assistant turns are scanned; tool
88
+ * and system messages are ignored.
89
+ *
90
+ * False negatives are acceptable: if a commitment slips through, the next
91
+ * reflection pass (30 min later) will catch it.
92
+ */
93
+ export declare function hasFutureIntentSignals(turns: HistoryEntry[]): boolean;
94
+ interface CompletionMessage {
95
+ role: "system" | "user";
96
+ content: string;
97
+ }
98
+ /**
99
+ * Single chat completion via the pi SDK provider layer. Uses reflection_model
100
+ * if configured, otherwise falls back to the primary model and then the
101
+ * fallback chain. This lets operators use a cheaper model for reflection.
102
+ */
103
+ export declare function sdkComplete(config: Config, messages: CompletionMessage[]): Promise<string>;
104
+ /**
105
+ * Parse the LLM output, tolerating markdown code fences and minor formatting.
106
+ *
107
+ * Even with `temperature: 0` and an explicit "output JSON only" instruction,
108
+ * models occasionally wrap their response in a ```json ... ``` code fence.
109
+ * The stripping step removes those fences before calling JSON.parse(), so
110
+ * both bare JSON and fenced JSON are accepted.
111
+ */
112
+ export declare function parseReflectionOutput(raw: string): ReflectionOutput;
113
+ /**
114
+ * Run one reflection pass for a user. Reads recent conversation, extracts
115
+ * future references via LLM, and writes new projections to the store.
116
+ */
117
+ export declare function runReflection(config: Config, userId: string, windowMinutes?: number, store?: ProjectionStore, completeFn?: typeof sdkComplete): Promise<ReflectionResult>;
118
+ export {};
119
+ //# sourceMappingURL=reflection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflection.d.ts","sourceRoot":"","sources":["../../src/projection/reflection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAUxE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B;;;;;OAKG;IACH,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,gBAAgB,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,wDAAwD;IACxD,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,SAAK,GACf,YAAY,EAAE,CAyChB;AAuCD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAmC1C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CASrE;AAMD,UAAU,iBAAiB;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,CA6CjB;AA+CD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAgBnE;AAMD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,aAAa,SAAK,EAClB,KAAK,CAAC,EAAE,eAAe,EACvB,UAAU,CAAC,EAAE,OAAO,WAAW,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAyI3B"}
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Projection reflection pass.
3
+ *
4
+ * Lightweight background job that scans recent conversation history for
5
+ * future references the agent missed during live chat.
6
+ *
7
+ * Runs every 30 min via cron. Reads the JSONL audit log, makes a single
8
+ * completeSimple() call with a narrow extraction prompt (no agent loop,
9
+ * no tools), parses the JSON output, and writes projections directly to
10
+ * SQLite. Existing pending projections are included in the prompt so the
11
+ * model won't duplicate them. A per-user timestamp tracks the last run
12
+ * to skip unchanged transcripts.
13
+ *
14
+ * Why completeSimple() instead of a full agent loop?
15
+ * The reflection pass has no side effects and requires no tool calls. It only
16
+ * needs one prompt in and one JSON blob out. Using a full agent loop would add
17
+ * latency, cost, and the risk of unintended tool invocations. completeSimple()
18
+ * is cheaper, faster, and keeps the pass strictly read-only from the model's
19
+ * perspective.
20
+ */
21
+ import fs from "node:fs";
22
+ import path from "node:path";
23
+ import Database from "better-sqlite3";
24
+ import { completeSimple } from "@mariozechner/pi-ai";
25
+ import { createProjectionStore } from "./store.js";
26
+ import { formatProjectionsForPrompt } from "./format.js";
27
+ import { toUtc, getUserTimezone } from "../time.js";
28
+ import { createModelInfra, resolveFirstModel } from "../model-infra.js";
29
+ /**
30
+ * Read user+assistant messages from the JSONL audit log for the last
31
+ * `windowMinutes`. Returns entries in chronological order, capped at
32
+ * `maxMessages`.
33
+ *
34
+ * Reads from the JSONL audit log written by src/compaction/history.ts, NOT
35
+ * from the pi SDK session file. The audit log is the only way to get
36
+ * structured turn-by-turn history outside of a live session context: the pi
37
+ * session file is append-only and interleaved with tool scaffolding, whereas
38
+ * the audit log contains clean role/content/timestamp records per message.
39
+ */
40
+ export function readRecentHistory(historyDir, windowMinutes, maxMessages = 40) {
41
+ if (!fs.existsSync(historyDir))
42
+ return [];
43
+ const cutoff = new Date(Date.now() - windowMinutes * 60 * 1000);
44
+ const files = fs.readdirSync(historyDir)
45
+ .filter((f) => f.endsWith(".jsonl"))
46
+ .sort()
47
+ .reverse(); // Most recent first
48
+ const collected = [];
49
+ for (const file of files) {
50
+ // Quick file-level date check: skip files older than cutoff date
51
+ const fileDate = path.basename(file, ".jsonl"); // "YYYY-MM-DD"
52
+ const fileDateObj = new Date(fileDate + "T00:00:00Z");
53
+ // A file from yesterday may still have messages within the window
54
+ if (fileDateObj.getTime() + 86400 * 1000 < cutoff.getTime()) {
55
+ break; // Files are sorted newest first; nothing older will match
56
+ }
57
+ const filePath = path.join(historyDir, file);
58
+ const lines = fs.readFileSync(filePath, "utf-8").split("\n");
59
+ for (const line of lines.reverse()) { // Newest first within file
60
+ if (!line.trim())
61
+ continue;
62
+ try {
63
+ const entry = JSON.parse(line);
64
+ if (entry.role !== "user" && entry.role !== "assistant")
65
+ continue;
66
+ const ts = new Date(entry.timestamp);
67
+ if (ts < cutoff)
68
+ continue;
69
+ collected.push(entry);
70
+ if (collected.length >= maxMessages)
71
+ break;
72
+ }
73
+ catch {
74
+ // Skip malformed lines
75
+ }
76
+ }
77
+ if (collected.length >= maxMessages)
78
+ break;
79
+ }
80
+ // Return chronological order
81
+ return collected.reverse();
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Last-reflection tracking
85
+ // ---------------------------------------------------------------------------
86
+ /**
87
+ * Read/write the last reflection timestamp from a metadata table in memory.db.
88
+ *
89
+ * The timestamp is used to skip reflection when there are no new messages
90
+ * since the last run: if the newest audit-log entry is not newer than the
91
+ * stored timestamp, the pass exits early without calling the LLM. This keeps
92
+ * cron overhead negligible for idle users.
93
+ *
94
+ * The timestamp is stored in the same SQLite database as archival memory
95
+ * (memory.db), so it survives process restarts. A plain text file was
96
+ * considered but SQLite gives atomic writes for free.
97
+ */
98
+ function getLastReflectionTimestamp(db) {
99
+ db.exec(`
100
+ CREATE TABLE IF NOT EXISTS reflection_meta (
101
+ key TEXT PRIMARY KEY,
102
+ value TEXT NOT NULL
103
+ );
104
+ `);
105
+ const row = db.prepare("SELECT value FROM reflection_meta WHERE key = 'last_reflection'").get();
106
+ return row?.value ?? null;
107
+ }
108
+ function setLastReflectionTimestamp(db, ts) {
109
+ db.prepare("INSERT OR REPLACE INTO reflection_meta (key, value) VALUES ('last_reflection', ?)").run(ts);
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Future-intent pre-filter
113
+ // ---------------------------------------------------------------------------
114
+ /**
115
+ * Keyword patterns that suggest the conversation contains future commitments,
116
+ * plans, reminders, or time-specific references worth sending to the LLM.
117
+ *
118
+ * Organised into three semantic buckets for easy extension:
119
+ * - Time references: explicit temporal expressions
120
+ * - Intent markers: nouns that commonly describe scheduled events
121
+ * - Commitment verbs: phrases that express intent to act in the future
122
+ *
123
+ * Each entry is a plain string matched case-insensitively as a whole-word or
124
+ * phrase boundary pattern. Apostrophes in contractions ("I'll", "let's") are
125
+ * included literally; the regex wraps each keyword with \b so "calling" does
126
+ * not match "call", but multi-word phrases like "next week" match anywhere in
127
+ * the text.
128
+ */
129
+ export const FUTURE_INTENT_PATTERNS = [
130
+ // Time references
131
+ /\btomorrow\b/i,
132
+ /\btonight\b/i,
133
+ /\bnext\s+(week|month|year|monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i,
134
+ /\bthis\s+(weekend|week|monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i,
135
+ /\bon\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i,
136
+ /\bby\s+friday\b/i,
137
+ /\bat\s+\d{1,2}(:\d{2})?\s*(am|pm)?\b/i,
138
+ /\bin\s+\d+\s+(day|days|week|weeks|month|months)\b/i,
139
+ /\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{1,2}\b/i,
140
+ /\b\d{1,2}\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\b/i,
141
+ // Intent markers
142
+ /\bremind\s+me\b/i,
143
+ /\bdon'?t\s+forget\b/i,
144
+ /\bappointment\b/i,
145
+ /\bdeadline\b/i,
146
+ /\bmeeting\b/i,
147
+ /\bbirthday\b/i,
148
+ /\bdentist\b/i,
149
+ /\bdoctor\b/i,
150
+ /\binterview\b/i,
151
+ /\bevent\b/i,
152
+ /\bschedule\b/i,
153
+ /\bcalendar\b/i,
154
+ // Commitment verbs / phrases
155
+ /\bI'?ll\b/i,
156
+ /\bI\s+will\b/i,
157
+ /\bI\s+need\s+to\b/i,
158
+ /\bI\s+have\s+(a|an|to)\b/i,
159
+ /\bwe\s+should\b/i,
160
+ /\blet'?s\b/i,
161
+ /\bplan\s+to\b/i,
162
+ /\bgoing\s+to\b/i,
163
+ /\bwant\s+to\b/i,
164
+ ];
165
+ /**
166
+ * Returns true when at least one turn in the transcript matches a
167
+ * future-intent pattern. Only user and assistant turns are scanned; tool
168
+ * and system messages are ignored.
169
+ *
170
+ * False negatives are acceptable: if a commitment slips through, the next
171
+ * reflection pass (30 min later) will catch it.
172
+ */
173
+ export function hasFutureIntentSignals(turns) {
174
+ for (const turn of turns) {
175
+ for (const pattern of FUTURE_INTENT_PATTERNS) {
176
+ if (pattern.test(turn.content)) {
177
+ return true;
178
+ }
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+ /**
184
+ * Single chat completion via the pi SDK provider layer. Uses reflection_model
185
+ * if configured, otherwise falls back to the primary model and then the
186
+ * fallback chain. This lets operators use a cheaper model for reflection.
187
+ */
188
+ export async function sdkComplete(config, messages) {
189
+ const { modelRegistry } = createModelInfra(config);
190
+ // Resolve the model: reflection_model > primary model > first fallback
191
+ const candidates = [
192
+ config.agent.reflection_model,
193
+ config.agent.model,
194
+ ...(config.agent.fallback_models ?? []),
195
+ ].filter(Boolean);
196
+ const model = resolveFirstModel(candidates, modelRegistry);
197
+ if (!model) {
198
+ throw new Error(`Reflection: no usable model found. Tried: ${candidates.join(", ")}`);
199
+ }
200
+ // Separate system prompt from user/assistant messages
201
+ const systemMsg = messages.find((m) => m.role === "system");
202
+ const userMessages = messages.filter((m) => m.role !== "system");
203
+ const context = {
204
+ systemPrompt: systemMsg?.content,
205
+ messages: userMessages.map((m) => ({
206
+ role: m.role,
207
+ content: m.content,
208
+ timestamp: Date.now(),
209
+ })),
210
+ };
211
+ const apiKey = await modelRegistry.getApiKey(model);
212
+ const result = await completeSimple(model, context, {
213
+ maxTokens: 1024,
214
+ temperature: 0,
215
+ apiKey: apiKey ?? undefined,
216
+ });
217
+ if (result.stopReason === "error") {
218
+ throw new Error(`Reflection LLM error: ${result.errorMessage ?? "unknown"}`);
219
+ }
220
+ return result.content
221
+ .filter((c) => c.type === "text")
222
+ .map((c) => c.type === "text" ? c.text : "")
223
+ .join("");
224
+ }
225
+ // ---------------------------------------------------------------------------
226
+ // Reflection prompt
227
+ // ---------------------------------------------------------------------------
228
+ function buildReflectionPrompt(turns, pendingProjections, currentDatetime) {
229
+ const transcript = turns
230
+ .map((t) => `${t.role.toUpperCase()}: ${t.content}`)
231
+ .join("\n\n");
232
+ const systemPrompt = `You are a memory assistant. Your job is to extract future commitments, ` +
233
+ `plans, deadlines, reminders, and events from a conversation transcript.\n\n` +
234
+ `Current datetime: ${currentDatetime}\n\n` +
235
+ `Rules:\n` +
236
+ `- Only extract things that are clearly about the FUTURE (from the perspective of the current datetime).\n` +
237
+ `- Do NOT extract things already listed under "Already stored".\n` +
238
+ `- Resolve time expressions to ISO dates or datetimes where possible.\n` +
239
+ `- For 'when': use "YYYY-MM-DD HH:MM" for exact times, "YYYY-MM-DD" for day-resolution, ` +
240
+ `"YYYY-Www" for week-resolution (e.g. "2026-W09"), "YYYY-MM" for month-resolution, ` +
241
+ `or "someday" for no specific time.\n` +
242
+ `- For 'resolution': use "exact", "day", "week", "month", or "someday".\n` +
243
+ `- Only include items with at least a clear summary. Context is optional.\n` +
244
+ `- If there is nothing new to extract, output: {"project":[],"archive":[]}\n` +
245
+ `- Output valid JSON only. No commentary before or after.\n\n` +
246
+ `Already stored as pending projections:\n${pendingProjections}`;
247
+ const userPrompt = `Here is the recent conversation:\n\n${transcript}\n\n` +
248
+ `What future events, plans, or commitments are mentioned that are NOT already stored?\n` +
249
+ `Output JSON only:`;
250
+ return [
251
+ { role: "system", content: systemPrompt },
252
+ { role: "user", content: userPrompt },
253
+ ];
254
+ }
255
+ // ---------------------------------------------------------------------------
256
+ // JSON parsing
257
+ // ---------------------------------------------------------------------------
258
+ /**
259
+ * Parse the LLM output, tolerating markdown code fences and minor formatting.
260
+ *
261
+ * Even with `temperature: 0` and an explicit "output JSON only" instruction,
262
+ * models occasionally wrap their response in a ```json ... ``` code fence.
263
+ * The stripping step removes those fences before calling JSON.parse(), so
264
+ * both bare JSON and fenced JSON are accepted.
265
+ */
266
+ export function parseReflectionOutput(raw) {
267
+ const stripped = raw
268
+ .replace(/^```(?:json)?\s*/i, "")
269
+ .replace(/\s*```\s*$/, "")
270
+ .trim();
271
+ try {
272
+ const parsed = JSON.parse(stripped);
273
+ return {
274
+ project: Array.isArray(parsed.project) ? parsed.project : [],
275
+ archive: Array.isArray(parsed.archive) ? parsed.archive : [],
276
+ };
277
+ }
278
+ catch {
279
+ // If the whole thing doesn't parse, return empty
280
+ return { project: [], archive: [] };
281
+ }
282
+ }
283
+ // ---------------------------------------------------------------------------
284
+ // Main reflection function
285
+ // ---------------------------------------------------------------------------
286
+ /**
287
+ * Run one reflection pass for a user. Reads recent conversation, extracts
288
+ * future references via LLM, and writes new projections to the store.
289
+ */
290
+ export async function runReflection(config, userId, windowMinutes = 30, store, completeFn) {
291
+ // Step 1: Read history from the JSONL audit log.
292
+ const historyDir = path.join(config.data_dir, "history");
293
+ const turns = readRecentHistory(historyDir, windowMinutes);
294
+ // Open store (or use injected one for tests)
295
+ const ownStore = !store;
296
+ const projStore = store ?? createProjectionStore(userId, config.data_dir);
297
+ // Access the underlying DB for metadata tracking via the store's DB path
298
+ const dbPath = path.join(config.data_dir, "users", userId, "memory.db");
299
+ let metaDb = null;
300
+ try {
301
+ // Step 2: Check for new messages. Skip the LLM call entirely if there is
302
+ // nothing to process: no turns in the window, or no turns newer than the
303
+ // last reflection timestamp.
304
+ if (turns.length === 0) {
305
+ return { projectionsAdded: 0, candidates: [], skipped: true, skipReason: "no recent messages" };
306
+ }
307
+ metaDb = new Database(dbPath);
308
+ const lastReflection = getLastReflectionTimestamp(metaDb);
309
+ if (lastReflection) {
310
+ const lastTs = new Date(lastReflection);
311
+ const newestTurn = turns[turns.length - 1];
312
+ if (new Date(newestTurn.timestamp) <= lastTs) {
313
+ return { projectionsAdded: 0, candidates: [], skipped: true, skipReason: "no new messages since last reflection" };
314
+ }
315
+ }
316
+ // Step 3: Pre-filter — skip the LLM call if the transcript has no signals
317
+ // that suggest future commitments or plans. This avoids paying for an LLM
318
+ // round-trip every 30 minutes when the conversation was purely small talk.
319
+ // False negatives are acceptable: the next reflection pass will catch any
320
+ // commitments this filter misses.
321
+ if (!hasFutureIntentSignals(turns)) {
322
+ // Still update the timestamp so we don't re-scan the same turns next run.
323
+ metaDb = metaDb ?? new Database(dbPath);
324
+ setLastReflectionTimestamp(metaDb, new Date().toISOString());
325
+ return {
326
+ projectionsAdded: 0,
327
+ candidates: [],
328
+ skipped: true,
329
+ skipReason: "no future-intent signals in transcript",
330
+ };
331
+ }
332
+ // Step 4: Load existing pending projections so the model can skip them.
333
+ // A 90-day window is used here (wider than the history window) to give the
334
+ // deduplication step the best chance of catching near-duplicates.
335
+ const upcoming = projStore.getUpcoming(90);
336
+ const pendingText = formatProjectionsForPrompt(upcoming, 30);
337
+ const tz = getUserTimezone(config);
338
+ const now = new Date();
339
+ const currentDatetime = now
340
+ .toLocaleString("sv-SE", { timeZone: tz, hour12: false })
341
+ .slice(0, 16)
342
+ .replace("T", " ") + (tz !== "UTC" ? ` (${tz})` : " UTC");
343
+ const messages = buildReflectionPrompt(turns, pendingText, currentDatetime);
344
+ // Step 5: Call the LLM. One prompt in, one JSON blob out — no tool calls.
345
+ const doComplete = completeFn ?? sdkComplete;
346
+ let raw;
347
+ try {
348
+ raw = await doComplete(config, messages);
349
+ }
350
+ catch (err) {
351
+ console.error("[reflection] LLM call failed:", err.message);
352
+ return { projectionsAdded: 0, candidates: [], skipped: true, skipReason: `LLM error: ${err.message}` };
353
+ }
354
+ // Step 6: Parse JSON from the raw LLM output.
355
+ const output = parseReflectionOutput(raw);
356
+ // Step 7: Deduplicate against existing projections and write survivors.
357
+ // Deduplication is prompt-based: the existing pending projections were
358
+ // included in the system prompt under "Already stored", and the model is
359
+ // instructed not to re-extract them. This is approximate — the model may
360
+ // still emit a candidate whose summary is a paraphrase of an existing one.
361
+ // A secondary code-level check (substring match on summaries) would reduce
362
+ // false duplicates but is not currently implemented.
363
+ const tz2 = getUserTimezone(config);
364
+ let projectionsAdded = 0;
365
+ for (const candidate of output.project) {
366
+ if (!candidate.summary?.trim())
367
+ continue;
368
+ try {
369
+ let resolved_when;
370
+ let raw_when;
371
+ let resolution = candidate.resolution ?? "day";
372
+ if (candidate.when && candidate.when !== "someday") {
373
+ const isoPattern = /^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2})?/;
374
+ const weekPattern = /^\d{4}-W\d{2}$/;
375
+ const monthPattern = /^\d{4}-\d{2}$/;
376
+ if (weekPattern.test(candidate.when)) {
377
+ raw_when = candidate.when;
378
+ resolution = "week";
379
+ }
380
+ else if (monthPattern.test(candidate.when)) {
381
+ raw_when = candidate.when;
382
+ resolution = "month";
383
+ }
384
+ else if (isoPattern.test(candidate.when)) {
385
+ const hasTime = candidate.when.includes("T") || (candidate.when.length > 10 && candidate.when[10] === " ");
386
+ resolved_when = hasTime ? toUtc(candidate.when, tz2) : candidate.when;
387
+ resolution = hasTime ? "exact" : (candidate.resolution ?? "day");
388
+ }
389
+ else {
390
+ raw_when = candidate.when;
391
+ }
392
+ }
393
+ else if (candidate.when === "someday") {
394
+ resolution = "someday";
395
+ }
396
+ projStore.add({
397
+ summary: candidate.summary.trim(),
398
+ raw_when,
399
+ resolved_when,
400
+ resolution,
401
+ context: candidate.context,
402
+ });
403
+ projectionsAdded++;
404
+ }
405
+ catch (err) {
406
+ console.warn("[reflection] Failed to store projection:", err.message, candidate);
407
+ }
408
+ }
409
+ // Update last-reflection timestamp
410
+ const reflectedAt = new Date().toISOString();
411
+ if (metaDb) {
412
+ setLastReflectionTimestamp(metaDb, reflectedAt);
413
+ }
414
+ return { projectionsAdded, candidates: output.project, skipped: false };
415
+ }
416
+ finally {
417
+ metaDb?.close();
418
+ if (ownStore)
419
+ projStore.close();
420
+ }
421
+ }
422
+ //# sourceMappingURL=reflection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflection.js","sourceRoot":"","sources":["../../src/projection/reflection.ts"],"names":[],"mappings":"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"}