@bryti/agent 0.0.1 → 0.1.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 (228) hide show
  1. package/Dockerfile +27 -0
  2. package/README.md +77 -50
  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 +673 -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 +94 -0
  119. package/dist/projection/reflection.d.ts.map +1 -0
  120. package/dist/projection/reflection.js +334 -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,84 @@
1
+ /**
2
+ * Agent session management.
3
+ *
4
+ * Wraps pi's createAgentSession() with bryti-specific config: persistent
5
+ * per-user sessions, transcript repair before every prompt, core memory
6
+ * injection into the system prompt, and custom tools.
7
+ *
8
+ * The pi SDK handles the agent loop, model routing, session persistence
9
+ * (append-only JSONL), auto-compaction, streaming, and retry logic.
10
+ *
11
+ * Sessions persist across messages in data/sessions/<userId>/. The model
12
+ * sees its actual prior tool calls and results in context; JSONL history
13
+ * files are kept as an audit log for conversation search.
14
+ */
15
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
16
+ import { ModelRegistry, type AgentSession } from "@mariozechner/pi-coding-agent";
17
+ import type { Config } from "./config.js";
18
+ import type { CoreMemory } from "./memory/core-memory.js";
19
+ import { SILENT_REPLY_TOKEN } from "./system-prompt.js";
20
+ export { SILENT_REPLY_TOKEN };
21
+ /**
22
+ * A loaded, persistent agent session for a single user.
23
+ */
24
+ export interface UserSession {
25
+ /** The underlying pi AgentSession. */
26
+ session: AgentSession;
27
+ /** Model registry, used by promptWithFallback() to resolve fallback models. */
28
+ modelRegistry: ModelRegistry;
29
+ /** User this session belongs to. */
30
+ userId: string;
31
+ /** Path to the per-user session directory on disk. */
32
+ sessionDir: string;
33
+ /** Timestamp of last user-initiated message (not scheduler). */
34
+ lastUserMessageAt: number;
35
+ /** Clean up event listeners. Does NOT delete the session file. */
36
+ dispose(): void;
37
+ }
38
+ /**
39
+ * Load (or create) a persistent agent session for a user.
40
+ *
41
+ * Opens an existing session file and loads its history, or creates a new
42
+ * one if none exists. Transcript repair runs on load to fix any corrupted
43
+ * tool-call/result pairings from a previous run.
44
+ *
45
+ * Call dispose() to clean up event listeners. The session file itself
46
+ * survives; it's reused on the next message.
47
+ */
48
+ export declare function loadUserSession(config: Config, coreMemory: CoreMemory, userId: string, customTools: AgentTool[]): Promise<UserSession>;
49
+ /**
50
+ * Run transcript repair on the session's messages before prompting.
51
+ * Catches pairing issues from the previous turn (partial writes, races).
52
+ */
53
+ export declare function repairSessionTranscript(session: AgentSession, userId: string): void;
54
+ /**
55
+ * Result of a prompt attempt in the fallback chain.
56
+ */
57
+ interface FallbackResult {
58
+ /** The model string that ultimately succeeded. */
59
+ modelUsed: string;
60
+ /** Number of models tried before success (0 = primary succeeded). */
61
+ fallbacksUsed: number;
62
+ }
63
+ /**
64
+ * Send a prompt, trying the primary model first then each fallback in order.
65
+ *
66
+ * On failure the session's model is switched via setModel() so the persistent
67
+ * session file stays intact. Throws the last error if all candidates fail.
68
+ */
69
+ export declare function promptWithFallback(session: AgentSession, text: string, config: Config, modelRegistry: ModelRegistry, userId: string, images?: Array<{
70
+ data: string;
71
+ mimeType: string;
72
+ }>): Promise<FallbackResult>;
73
+ /**
74
+ * Reload the system prompt so it picks up any core memory or projection
75
+ * changes the agent made during the previous turn.
76
+ *
77
+ * TODO: session.reload() reloads the full resource set, which includes any
78
+ * skill files on disk. If skill loading becomes slow (many/large skill files),
79
+ * consider caching the parsed skill content and only re-reading core memory
80
+ * and projections on each turn.
81
+ */
82
+ export declare function refreshSystemPrompt(session: AgentSession): Promise<void>;
83
+ export type { AgentSession };
84
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAGL,aAAa,EAGb,KAAK,YAAY,EAElB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAK1D,OAAO,EAAuC,kBAAkB,EAAoB,MAAM,oBAAoB,CAAC;AAG/G,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,OAAO,EAAE,YAAY,CAAC;IACtB,+EAA+E;IAC/E,aAAa,EAAE,aAAa,CAAC;IAC7B,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB;AAuED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,SAAS,EAAE,GACvB,OAAO,CAAC,WAAW,CAAC,CAkMtB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgBnF;AAID;;GAEG;AACH,UAAU,cAAc;IACtB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;CACvB;AAwCD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GACjD,OAAO,CAAC,cAAc,CAAC,CA6DzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9E;AAGD,YAAY,EAAE,YAAY,EAAE,CAAC"}
package/dist/agent.js ADDED
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Agent session management.
3
+ *
4
+ * Wraps pi's createAgentSession() with bryti-specific config: persistent
5
+ * per-user sessions, transcript repair before every prompt, core memory
6
+ * injection into the system prompt, and custom tools.
7
+ *
8
+ * The pi SDK handles the agent loop, model routing, session persistence
9
+ * (append-only JSONL), auto-compaction, streaming, and retry logic.
10
+ *
11
+ * Sessions persist across messages in data/sessions/<userId>/. The model
12
+ * sees its actual prior tool calls and results in context; JSONL history
13
+ * files are kept as an audit log for conversation search.
14
+ */
15
+ import fs from "node:fs";
16
+ import path from "node:path";
17
+ import { createAgentSession, DefaultResourceLoader, SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent";
18
+ import { repairToolUseResultPairing } from "./compaction/transcript-repair.js";
19
+ import { createProjectionStore, formatProjectionsForPrompt } from "./projection/index.js";
20
+ import { registerToolCapabilities, getToolCapabilities } from "./trust/index.js";
21
+ import { createModelInfra, resolveModel } from "./model-infra.js";
22
+ import { buildSystemPrompt, SILENT_REPLY_TOKEN } from "./system-prompt.js";
23
+ // Re-export for backward compatibility with index.ts
24
+ export { SILENT_REPLY_TOKEN };
25
+ /**
26
+ * Short human-readable summary of tool arguments for the audit log.
27
+ * Gives the /log command enough context without storing raw LLM args.
28
+ *
29
+ * This output is for human audit trail consumption only (/log command).
30
+ * It is never fed back to the model — it exists solely so operators can
31
+ * read what the agent did without wading through raw JSON blobs.
32
+ */
33
+ function buildArgsSummary(toolName, args) {
34
+ if (!args || typeof args !== "object") {
35
+ return "";
36
+ }
37
+ const a = args;
38
+ switch (toolName) {
39
+ case "memory_archival_search":
40
+ return String(a.query ?? "");
41
+ case "memory_archival_insert":
42
+ return truncate(String(a.content ?? ""), 80);
43
+ case "memory_core_append":
44
+ return `${a.section}: ${truncate(String(a.content ?? ""), 60)}`;
45
+ case "memory_core_replace":
46
+ return String(a.section ?? "");
47
+ case "memory_conversation_search":
48
+ return String(a.query ?? "");
49
+ case "projection_create":
50
+ return truncate(String(a.summary ?? ""), 80);
51
+ case "projection_resolve":
52
+ return String(a.id ?? "");
53
+ case "projection_list":
54
+ return "";
55
+ case "projection_link":
56
+ return String(a.projection_id ?? "");
57
+ case "worker_dispatch":
58
+ return truncate(String(a.task ?? ""), 80);
59
+ case "worker_check":
60
+ return String(a.worker_id ?? "");
61
+ case "worker_interrupt":
62
+ return String(a.worker_id ?? "");
63
+ case "worker_steer":
64
+ return String(a.worker_id ?? "");
65
+ case "file_read":
66
+ return String(a.path ?? "");
67
+ case "file_write":
68
+ return String(a.path ?? "");
69
+ case "file_list":
70
+ return String(a.directory ?? "");
71
+ default:
72
+ return "";
73
+ }
74
+ }
75
+ function truncate(text, maxLen) {
76
+ if (text.length <= maxLen)
77
+ return text;
78
+ return text.slice(0, maxLen - 1) + "…";
79
+ }
80
+ /**
81
+ * Per-user session directory. Each user gets their own so continueRecent()
82
+ * picks up the right session.
83
+ */
84
+ function userSessionDir(config, userId) {
85
+ const dir = path.join(config.data_dir, "sessions", userId);
86
+ fs.mkdirSync(dir, { recursive: true });
87
+ return dir;
88
+ }
89
+ /**
90
+ * Load (or create) a persistent agent session for a user.
91
+ *
92
+ * Opens an existing session file and loads its history, or creates a new
93
+ * one if none exists. Transcript repair runs on load to fix any corrupted
94
+ * tool-call/result pairings from a previous run.
95
+ *
96
+ * Call dispose() to clean up event listeners. The session file itself
97
+ * survives; it's reused on the next message.
98
+ */
99
+ export async function loadUserSession(config, coreMemory, userId, customTools) {
100
+ const { authStorage, modelRegistry, agentDir } = createModelInfra(config);
101
+ // --- 1. Model resolution ---
102
+ // Resolve the configured model string to a registry entry. Throws if the
103
+ // model is unknown so we fail fast before touching the session file.
104
+ const model = resolveModel(config.agent.model, modelRegistry);
105
+ if (!model) {
106
+ throw new Error(`Model not found: ${config.agent.model}. Available: ${modelRegistry.getAvailable().map((m) => m.id).join(", ")}`);
107
+ }
108
+ console.log(`Using model: ${model.id} (${model.provider})`);
109
+ // Session manager: continue most recent session for this user, or create new.
110
+ // Each user gets their own session directory so continueRecent finds the right file.
111
+ const sessDir = userSessionDir(config, userId);
112
+ const sessionManager = SessionManager.continueRecent(config.data_dir, sessDir);
113
+ const promptTools = customTools.map((tool) => ({
114
+ name: tool.name,
115
+ description: tool.description,
116
+ }));
117
+ const extensionToolNames = new Set();
118
+ // --- 2. Resource loader setup with system prompt override closure ---
119
+ // The override closure captures core memory and the projection store so it
120
+ // can read both at call time. session.reload() triggers the closure, which
121
+ // means every prompt sees up-to-date memory and projections without restarting.
122
+ //
123
+ // Bryti has its own skills directory in the data dir, separate from the global
124
+ // pi CLI skills. Skills are curated for bryti independently of the CLI.
125
+ // Projection store for this user. Opened once per session, closed on dispose.
126
+ const projectionStore = createProjectionStore(userId, config.data_dir);
127
+ const brytiSkillsDir = path.join(config.data_dir, "skills");
128
+ const additionalSkillPaths = fs.existsSync(brytiSkillsDir) ? [brytiSkillsDir] : [];
129
+ const loader = new DefaultResourceLoader({
130
+ cwd: config.data_dir,
131
+ agentDir,
132
+ additionalSkillPaths,
133
+ settingsManager: SettingsManager.create(config.data_dir, agentDir),
134
+ systemPromptOverride: () => {
135
+ // Expire projections older than 24 hours before injecting them into the
136
+ // system prompt. Stale projections must be cleared first so the agent
137
+ // never reasons about items that have clearly passed — seeing expired
138
+ // events would cause it to act on outdated information.
139
+ projectionStore.autoExpire(24);
140
+ const upcoming = projectionStore.getUpcoming(7);
141
+ const projectionText = formatProjectionsForPrompt(upcoming);
142
+ return buildSystemPrompt(config, coreMemory.read(), promptTools, extensionToolNames, projectionText);
143
+ },
144
+ });
145
+ await loader.reload();
146
+ const settingsManager = SettingsManager.create(config.data_dir, agentDir);
147
+ // --- 3. Session creation + extension loading ---
148
+ const { session, extensionsResult } = await createAgentSession({
149
+ cwd: config.data_dir,
150
+ agentDir,
151
+ authStorage,
152
+ modelRegistry,
153
+ model,
154
+ thinkingLevel: "off",
155
+ tools: [],
156
+ customTools,
157
+ resourceLoader: loader,
158
+ sessionManager,
159
+ settingsManager,
160
+ });
161
+ // Log extension loading results
162
+ if (extensionsResult.extensions.length > 0) {
163
+ for (const extension of extensionsResult.extensions) {
164
+ const toolNames = [...extension.tools.keys()];
165
+ console.log(`[extensions] Loaded: ${extension.path} (tools: ${toolNames.join(", ") || "none"})`);
166
+ for (const toolName of toolNames) {
167
+ extensionToolNames.add(toolName);
168
+ // Register extension tools as elevated by default (they can do anything).
169
+ // Skip if already registered with specific capabilities (e.g., shell_exec).
170
+ const existing = getToolCapabilities(toolName);
171
+ if (existing.level === "safe") {
172
+ registerToolCapabilities(toolName, {
173
+ level: "elevated",
174
+ capabilities: ["network", "filesystem", "shell"],
175
+ reason: "Extension tool with unrestricted access.",
176
+ });
177
+ }
178
+ }
179
+ }
180
+ console.log(`[extensions] ${extensionsResult.extensions.length} extension(s) loaded, ${extensionToolNames.size} tool(s) registered`);
181
+ }
182
+ if (extensionsResult.errors.length > 0) {
183
+ for (const err of extensionsResult.errors) {
184
+ console.error(`[extensions] Failed to load ${err.path}: ${err.error}`);
185
+ }
186
+ }
187
+ // --- 4. Tool registration ---
188
+ // Rebuild promptTools from the fully resolved tool list (custom tools +
189
+ // extension tools) so the system prompt lists every tool the model can call.
190
+ promptTools.splice(0, promptTools.length, ...session.getAllTools().map((tool) => ({
191
+ name: tool.name,
192
+ description: tool.description,
193
+ })));
194
+ await session.reload();
195
+ // --- 5. Transcript repair on load ---
196
+ // Fix any tool-call/result pairing issues that could have been written into
197
+ // the session file from a previous run (partial writes, races, crashes).
198
+ const currentMessages = session.messages;
199
+ if (currentMessages.length > 0) {
200
+ const report = repairToolUseResultPairing(currentMessages);
201
+ if (report.changed) {
202
+ session.agent.replaceMessages(report.messages);
203
+ console.log(`Transcript repair on load for user ${userId}: ` +
204
+ `added=${report.added.length} ` +
205
+ `droppedDuplicates=${report.droppedDuplicateCount} ` +
206
+ `droppedOrphans=${report.droppedOrphanCount}`);
207
+ }
208
+ }
209
+ // --- 6. Event subscription setup ---
210
+ // Subscribe to session events for compaction telemetry and the tool-call
211
+ // audit log. The unsubscribe handle is returned via dispose() so callers
212
+ // can clean up without touching the session file.
213
+ const logsDir = path.join(config.data_dir, "logs");
214
+ fs.mkdirSync(logsDir, { recursive: true });
215
+ const toolCallLogPath = path.join(logsDir, "tool-calls.jsonl");
216
+ // Log compaction and tool call events
217
+ const toolCallCounts = new Map();
218
+ const unsubscribe = session.subscribe((event) => {
219
+ if (event.type === "auto_compaction_start") {
220
+ console.log(`[compaction] starting (reason: ${event.reason}) for user ${userId}`);
221
+ }
222
+ else if (event.type === "auto_compaction_end") {
223
+ if (event.result) {
224
+ const summary = event.result.summary;
225
+ console.log(`[compaction] done for user ${userId}: ` +
226
+ `tokensBefore=${event.result.tokensBefore} ` +
227
+ `summaryLength=${summary.length}`);
228
+ }
229
+ else if (event.errorMessage) {
230
+ console.error(`[compaction] failed for user ${userId}: ${event.errorMessage}`);
231
+ }
232
+ }
233
+ else if (event.type === "tool_execution_start") {
234
+ const name = event.toolName ?? "unknown";
235
+ toolCallCounts.set(name, (toolCallCounts.get(name) ?? 0) + 1);
236
+ console.log(`[tool] ${name} called (total this session: ${toolCallCounts.get(name)})`);
237
+ // Append a structured entry to the audit log. Best-effort: never crash the
238
+ // subscriber if the write fails.
239
+ try {
240
+ const args = event.args;
241
+ const argsSummary = buildArgsSummary(name, args);
242
+ const entry = JSON.stringify({
243
+ timestamp: new Date().toISOString(),
244
+ userId,
245
+ toolName: name,
246
+ args_summary: argsSummary,
247
+ });
248
+ fs.appendFileSync(toolCallLogPath, entry + "\n", "utf-8");
249
+ }
250
+ catch {
251
+ // Best-effort — never let a log write crash the agent loop
252
+ }
253
+ }
254
+ });
255
+ return {
256
+ session,
257
+ modelRegistry,
258
+ userId,
259
+ sessionDir: sessDir,
260
+ lastUserMessageAt: Date.now(),
261
+ dispose() {
262
+ unsubscribe();
263
+ session.dispose();
264
+ projectionStore.close();
265
+ },
266
+ };
267
+ }
268
+ /**
269
+ * Run transcript repair on the session's messages before prompting.
270
+ * Catches pairing issues from the previous turn (partial writes, races).
271
+ */
272
+ export function repairSessionTranscript(session, userId) {
273
+ const messages = session.messages;
274
+ if (messages.length === 0) {
275
+ return;
276
+ }
277
+ const report = repairToolUseResultPairing(messages);
278
+ if (report.changed) {
279
+ session.agent.replaceMessages(report.messages);
280
+ console.log(`Transcript repair pre-prompt for user ${userId}: ` +
281
+ `added=${report.added.length} ` +
282
+ `droppedDuplicates=${report.droppedDuplicateCount} ` +
283
+ `droppedOrphans=${report.droppedOrphanCount}`);
284
+ }
285
+ }
286
+ /**
287
+ * Detect whether pi gave up on a prompt.
288
+ *
289
+ * Two failure modes exist:
290
+ *
291
+ * 1. Thrown error — the SDK exhausted its retry budget and threw. This
292
+ * covers network failures, timeouts, and provider outages.
293
+ *
294
+ * 2. Last assistant message has stopReason "error" — the model returned a
295
+ * response the SDK classified as a hard failure (for example a
296
+ * content-filter block or an internal model error). No exception is
297
+ * thrown in this case; the bad message is simply appended to the transcript.
298
+ *
299
+ * Both cases mean "try the next model" in the fallback chain.
300
+ */
301
+ function didPromptFail(session, thrownError) {
302
+ if (thrownError) {
303
+ const msg = thrownError instanceof Error ? thrownError.message : String(thrownError);
304
+ return { failed: true, reason: msg };
305
+ }
306
+ const lastAssistant = session.messages
307
+ .filter((m) => m.role === "assistant")
308
+ .pop();
309
+ if (lastAssistant?.stopReason === "error") {
310
+ return {
311
+ failed: true,
312
+ reason: String(lastAssistant.errorMessage ?? "model error"),
313
+ };
314
+ }
315
+ return { failed: false, reason: "" };
316
+ }
317
+ /**
318
+ * Send a prompt, trying the primary model first then each fallback in order.
319
+ *
320
+ * On failure the session's model is switched via setModel() so the persistent
321
+ * session file stays intact. Throws the last error if all candidates fail.
322
+ */
323
+ export async function promptWithFallback(session, text, config, modelRegistry, userId, images) {
324
+ const candidates = [config.agent.model, ...(config.agent.fallback_models ?? [])];
325
+ let lastError;
326
+ let lastReason = "";
327
+ // Convert to SDK ImageContent format
328
+ const imageContent = images?.map((img) => ({
329
+ type: "image",
330
+ data: img.data,
331
+ mimeType: img.mimeType,
332
+ }));
333
+ if (imageContent && imageContent.length > 0) {
334
+ console.log(`[images] Sending ${imageContent.length} image(s) to model for user ${userId}: ` +
335
+ imageContent.map((img) => `${img.mimeType} (${Math.round(img.data.length * 0.75 / 1024)}KB base64)`).join(", "));
336
+ }
337
+ for (let i = 0; i < candidates.length; i++) {
338
+ const modelString = candidates[i];
339
+ // Switch the session to this model if it's not already using it
340
+ if (i > 0) {
341
+ const model = resolveModel(modelString, modelRegistry);
342
+ if (!model) {
343
+ console.warn(`Fallback model not found in registry, skipping: ${modelString}`);
344
+ continue;
345
+ }
346
+ console.log(`[fallback] Switching to model ${modelString} for user ${userId} ` +
347
+ `(previous error: ${lastReason})`);
348
+ await session.setModel(model);
349
+ }
350
+ let thrownError = null;
351
+ try {
352
+ await session.prompt(text, imageContent ? { images: imageContent } : undefined);
353
+ }
354
+ catch (err) {
355
+ thrownError = err;
356
+ }
357
+ const { failed, reason } = didPromptFail(session, thrownError);
358
+ if (!failed) {
359
+ if (i > 0) {
360
+ console.log(`[fallback] Succeeded with model ${modelString} for user ${userId}`);
361
+ }
362
+ return { modelUsed: modelString, fallbacksUsed: i };
363
+ }
364
+ lastError = thrownError ?? new Error(reason);
365
+ lastReason = reason;
366
+ console.warn(`[fallback] Model ${modelString} failed for user ${userId}: ${reason}` +
367
+ (i < candidates.length - 1 ? ", trying next..." : ", all models exhausted"));
368
+ }
369
+ throw lastError ?? new Error("All models in fallback chain failed");
370
+ }
371
+ /**
372
+ * Reload the system prompt so it picks up any core memory or projection
373
+ * changes the agent made during the previous turn.
374
+ *
375
+ * TODO: session.reload() reloads the full resource set, which includes any
376
+ * skill files on disk. If skill loading becomes slow (many/large skill files),
377
+ * consider caching the parsed skill content and only re-reading core memory
378
+ * and projections on each turn.
379
+ */
380
+ export async function refreshSystemPrompt(session) {
381
+ await session.reload();
382
+ }
383
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * How to render markdown tables in Telegram output.
3
+ *
4
+ * - `off`: tables are disabled; the raw markdown is passed through as-is.
5
+ * - `bullets`: each row is converted to a bullet-list entry with "Header: value"
6
+ * pairs. First column is used as a section label when there are multiple
7
+ * columns. This is the most readable option for most tables.
8
+ * - `code`: the table is typeset as a fixed-width ASCII grid and wrapped in a
9
+ * code block. Preserves structure but loses all inline formatting.
10
+ */
11
+ export type MarkdownTableMode = "off" | "bullets" | "code";
12
+ export type MarkdownStyle = "bold" | "italic" | "strikethrough" | "code" | "code_block" | "spoiler";
13
+ export type MarkdownStyleSpan = {
14
+ start: number;
15
+ end: number;
16
+ style: MarkdownStyle;
17
+ };
18
+ export type MarkdownLinkSpan = {
19
+ start: number;
20
+ end: number;
21
+ href: string;
22
+ };
23
+ /**
24
+ * The canonical IR produced by markdownToIR.
25
+ *
26
+ * - `text`: the plain-text content with all markdown syntax stripped. This is
27
+ * the string that gets chunked and sent to the platform.
28
+ * - `styles`: character-range spans (start inclusive, end exclusive) that carry
29
+ * bold/italic/code/etc. annotations over substrings of `text`. Multiple spans
30
+ * can overlap, which is legal (e.g. bold-italic).
31
+ * - `links`: separate from `styles` because links carry an `href` payload in
32
+ * addition to the character range. Keeping them in their own list avoids
33
+ * polluting the style union type with optional metadata fields.
34
+ *
35
+ * All three fields are required to fully reconstruct formatted output; losing
36
+ * any one of them produces incorrect rendering.
37
+ */
38
+ export type MarkdownIR = {
39
+ text: string;
40
+ styles: MarkdownStyleSpan[];
41
+ links: MarkdownLinkSpan[];
42
+ };
43
+ export type MarkdownParseOptions = {
44
+ /**
45
+ * Post-process plain-text URLs that are not already inside a markdown link.
46
+ * When true, bare `https://...` strings in prose are wrapped in a link span
47
+ * so renderers can emit them as tappable links. Powered by markdown-it's
48
+ * built-in linkify pass. Default: true.
49
+ */
50
+ linkify?: boolean;
51
+ enableSpoilers?: boolean;
52
+ headingStyle?: "none" | "bold";
53
+ blockquotePrefix?: string;
54
+ autolink?: boolean;
55
+ /** How to render tables (off|bullets|code). Default: off. */
56
+ tableMode?: MarkdownTableMode;
57
+ };
58
+ export declare function markdownToIR(markdown: string, options?: MarkdownParseOptions): MarkdownIR;
59
+ export declare function markdownToIRWithMeta(markdown: string, options?: MarkdownParseOptions): {
60
+ ir: MarkdownIR;
61
+ hasTables: boolean;
62
+ };
63
+ /**
64
+ * Split an IR into chunks whose `text` length does not exceed `limit`.
65
+ *
66
+ * Chunking strategy (applied in priority order):
67
+ * 1. Paragraph boundary (`\n\n`): preferred split point; keeps semantic units
68
+ * together and produces the most natural message breaks.
69
+ * 2. Line break (`\n`): used when no paragraph boundary falls in the latter
70
+ * 70% of the window.
71
+ * 3. Word boundary (space): last resort for very long lines with no newlines.
72
+ * 4. Hard cut at `limit`: only when no whitespace is available at all.
73
+ *
74
+ * Key invariant: a chunk boundary is never placed inside a style span. The
75
+ * sliceStyleSpans / sliceLinkSpans helpers remap span coordinates to each
76
+ * chunk's local origin, so every output IR is self-consistent.
77
+ */
78
+ export declare function chunkMarkdownIR(ir: MarkdownIR, limit: number): MarkdownIR[];
79
+ //# sourceMappingURL=ir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../../../src/channels/markdown/ir.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAmE3D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;AAEpG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B,CAAC;AAuCF,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAitBF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,UAAU,CAE7F;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oBAAyB,GACjC;IAAE,EAAE,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAmDxC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAgC3E"}