@asermax/tachikoma 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/README.md +64 -0
  2. package/dist/agent/adapter.d.ts +8 -0
  3. package/dist/agent/adapter.js +86 -0
  4. package/dist/agent/adapter.js.map +1 -0
  5. package/dist/agent/manager.d.ts +35 -0
  6. package/dist/agent/manager.js +76 -0
  7. package/dist/agent/manager.js.map +1 -0
  8. package/dist/agent/models.d.ts +46 -0
  9. package/dist/agent/models.js +96 -0
  10. package/dist/agent/models.js.map +1 -0
  11. package/dist/agent/side-run.d.ts +42 -0
  12. package/dist/agent/side-run.js +83 -0
  13. package/dist/agent/side-run.js.map +1 -0
  14. package/dist/app.d.ts +5 -0
  15. package/dist/app.js +79 -0
  16. package/dist/app.js.map +1 -0
  17. package/dist/channels/types.d.ts +37 -0
  18. package/dist/channels/types.js +5 -0
  19. package/dist/channels/types.js.map +1 -0
  20. package/dist/config/default-template.d.ts +1 -0
  21. package/dist/config/default-template.js +49 -0
  22. package/dist/config/default-template.js.map +1 -0
  23. package/dist/config/load.d.ts +8 -0
  24. package/dist/config/load.js +28 -0
  25. package/dist/config/load.js.map +1 -0
  26. package/dist/config/parse.d.ts +5 -0
  27. package/dist/config/parse.js +18 -0
  28. package/dist/config/parse.js.map +1 -0
  29. package/dist/config/schema.d.ts +29 -0
  30. package/dist/config/schema.js +35 -0
  31. package/dist/config/schema.js.map +1 -0
  32. package/dist/coordinator.d.ts +54 -0
  33. package/dist/coordinator.js +344 -0
  34. package/dist/coordinator.js.map +1 -0
  35. package/dist/db/core-schema.d.ts +250 -0
  36. package/dist/db/core-schema.js +19 -0
  37. package/dist/db/core-schema.js.map +1 -0
  38. package/dist/db/index.d.ts +8 -0
  39. package/dist/db/index.js +16 -0
  40. package/dist/db/index.js.map +1 -0
  41. package/dist/db/schema.d.ts +4 -0
  42. package/dist/db/schema.js +7 -0
  43. package/dist/db/schema.js.map +1 -0
  44. package/dist/db/state.d.ts +10 -0
  45. package/dist/db/state.js +36 -0
  46. package/dist/db/state.js.map +1 -0
  47. package/dist/domain/agent-events.d.ts +26 -0
  48. package/dist/domain/agent-events.js +2 -0
  49. package/dist/domain/agent-events.js.map +1 -0
  50. package/dist/domain/message.d.ts +25 -0
  51. package/dist/domain/message.js +17 -0
  52. package/dist/domain/message.js.map +1 -0
  53. package/dist/events.d.ts +9 -0
  54. package/dist/events.js +27 -0
  55. package/dist/events.js.map +1 -0
  56. package/dist/extensions/api.d.ts +118 -0
  57. package/dist/extensions/api.js +7 -0
  58. package/dist/extensions/api.js.map +1 -0
  59. package/dist/extensions/boundary/detector.d.ts +20 -0
  60. package/dist/extensions/boundary/detector.js +57 -0
  61. package/dist/extensions/boundary/detector.js.map +1 -0
  62. package/dist/extensions/boundary/idle.d.ts +10 -0
  63. package/dist/extensions/boundary/idle.js +28 -0
  64. package/dist/extensions/boundary/idle.js.map +1 -0
  65. package/dist/extensions/boundary/index.d.ts +12 -0
  66. package/dist/extensions/boundary/index.js +65 -0
  67. package/dist/extensions/boundary/index.js.map +1 -0
  68. package/dist/extensions/boundary/summary.d.ts +5 -0
  69. package/dist/extensions/boundary/summary.js +33 -0
  70. package/dist/extensions/boundary/summary.js.map +1 -0
  71. package/dist/extensions/commands/index.d.ts +7 -0
  72. package/dist/extensions/commands/index.js +21 -0
  73. package/dist/extensions/commands/index.js.map +1 -0
  74. package/dist/extensions/context/index.d.ts +7 -0
  75. package/dist/extensions/context/index.js +65 -0
  76. package/dist/extensions/context/index.js.map +1 -0
  77. package/dist/extensions/context/processor.d.ts +27 -0
  78. package/dist/extensions/context/processor.js +228 -0
  79. package/dist/extensions/context/processor.js.map +1 -0
  80. package/dist/extensions/detached-processes/index.d.ts +12 -0
  81. package/dist/extensions/detached-processes/index.js +51 -0
  82. package/dist/extensions/detached-processes/index.js.map +1 -0
  83. package/dist/extensions/detached-processes/limits.d.ts +27 -0
  84. package/dist/extensions/detached-processes/limits.js +55 -0
  85. package/dist/extensions/detached-processes/limits.js.map +1 -0
  86. package/dist/extensions/detached-processes/output.d.ts +2 -0
  87. package/dist/extensions/detached-processes/output.js +26 -0
  88. package/dist/extensions/detached-processes/output.js.map +1 -0
  89. package/dist/extensions/detached-processes/reconcile.d.ts +31 -0
  90. package/dist/extensions/detached-processes/reconcile.js +71 -0
  91. package/dist/extensions/detached-processes/reconcile.js.map +1 -0
  92. package/dist/extensions/detached-processes/repository.d.ts +33 -0
  93. package/dist/extensions/detached-processes/repository.js +62 -0
  94. package/dist/extensions/detached-processes/repository.js.map +1 -0
  95. package/dist/extensions/detached-processes/schema.d.ts +252 -0
  96. package/dist/extensions/detached-processes/schema.js +23 -0
  97. package/dist/extensions/detached-processes/schema.js.map +1 -0
  98. package/dist/extensions/detached-processes/spawn.d.ts +40 -0
  99. package/dist/extensions/detached-processes/spawn.js +137 -0
  100. package/dist/extensions/detached-processes/spawn.js.map +1 -0
  101. package/dist/extensions/detached-processes/tools.d.ts +41 -0
  102. package/dist/extensions/detached-processes/tools.js +243 -0
  103. package/dist/extensions/detached-processes/tools.js.map +1 -0
  104. package/dist/extensions/detached-processes/watcher.d.ts +7 -0
  105. package/dist/extensions/detached-processes/watcher.js +19 -0
  106. package/dist/extensions/detached-processes/watcher.js.map +1 -0
  107. package/dist/extensions/external/index.d.ts +11 -0
  108. package/dist/extensions/external/index.js +40 -0
  109. package/dist/extensions/external/index.js.map +1 -0
  110. package/dist/extensions/external/installs.d.ts +39 -0
  111. package/dist/extensions/external/installs.js +98 -0
  112. package/dist/extensions/external/installs.js.map +1 -0
  113. package/dist/extensions/external/loader.d.ts +19 -0
  114. package/dist/extensions/external/loader.js +70 -0
  115. package/dist/extensions/external/loader.js.map +1 -0
  116. package/dist/extensions/external/tools.d.ts +17 -0
  117. package/dist/extensions/external/tools.js +112 -0
  118. package/dist/extensions/external/tools.js.map +1 -0
  119. package/dist/extensions/git/commit.d.ts +19 -0
  120. package/dist/extensions/git/commit.js +44 -0
  121. package/dist/extensions/git/commit.js.map +1 -0
  122. package/dist/extensions/git/git.d.ts +11 -0
  123. package/dist/extensions/git/git.js +29 -0
  124. package/dist/extensions/git/git.js.map +1 -0
  125. package/dist/extensions/git/hooks.d.ts +10 -0
  126. package/dist/extensions/git/hooks.js +88 -0
  127. package/dist/extensions/git/hooks.js.map +1 -0
  128. package/dist/extensions/git/index.d.ts +11 -0
  129. package/dist/extensions/git/index.js +28 -0
  130. package/dist/extensions/git/index.js.map +1 -0
  131. package/dist/extensions/git/processor.d.ts +13 -0
  132. package/dist/extensions/git/processor.js +52 -0
  133. package/dist/extensions/git/processor.js.map +1 -0
  134. package/dist/extensions/git/sync.d.ts +44 -0
  135. package/dist/extensions/git/sync.js +189 -0
  136. package/dist/extensions/git/sync.js.map +1 -0
  137. package/dist/extensions/git/tools.d.ts +21 -0
  138. package/dist/extensions/git/tools.js +101 -0
  139. package/dist/extensions/git/tools.js.map +1 -0
  140. package/dist/extensions/host.d.ts +31 -0
  141. package/dist/extensions/host.js +75 -0
  142. package/dist/extensions/host.js.map +1 -0
  143. package/dist/extensions/index.d.ts +3 -0
  144. package/dist/extensions/index.js +32 -0
  145. package/dist/extensions/index.js.map +1 -0
  146. package/dist/extensions/memory/archive.d.ts +8 -0
  147. package/dist/extensions/memory/archive.js +46 -0
  148. package/dist/extensions/memory/archive.js.map +1 -0
  149. package/dist/extensions/memory/dates.d.ts +2 -0
  150. package/dist/extensions/memory/dates.js +7 -0
  151. package/dist/extensions/memory/dates.js.map +1 -0
  152. package/dist/extensions/memory/extraction.d.ts +17 -0
  153. package/dist/extensions/memory/extraction.js +218 -0
  154. package/dist/extensions/memory/extraction.js.map +1 -0
  155. package/dist/extensions/memory/index.d.ts +20 -0
  156. package/dist/extensions/memory/index.js +67 -0
  157. package/dist/extensions/memory/index.js.map +1 -0
  158. package/dist/extensions/memory/indexes.d.ts +14 -0
  159. package/dist/extensions/memory/indexes.js +64 -0
  160. package/dist/extensions/memory/indexes.js.map +1 -0
  161. package/dist/extensions/memory/layout.d.ts +20 -0
  162. package/dist/extensions/memory/layout.js +79 -0
  163. package/dist/extensions/memory/layout.js.map +1 -0
  164. package/dist/extensions/memory/maintenance.d.ts +21 -0
  165. package/dist/extensions/memory/maintenance.js +357 -0
  166. package/dist/extensions/memory/maintenance.js.map +1 -0
  167. package/dist/extensions/memory/prompts.d.ts +8 -0
  168. package/dist/extensions/memory/prompts.js +125 -0
  169. package/dist/extensions/memory/prompts.js.map +1 -0
  170. package/dist/extensions/memory/transcript.d.ts +18 -0
  171. package/dist/extensions/memory/transcript.js +79 -0
  172. package/dist/extensions/memory/transcript.js.map +1 -0
  173. package/dist/extensions/notifications/format.d.ts +5 -0
  174. package/dist/extensions/notifications/format.js +17 -0
  175. package/dist/extensions/notifications/format.js.map +1 -0
  176. package/dist/extensions/notifications/index.d.ts +13 -0
  177. package/dist/extensions/notifications/index.js +29 -0
  178. package/dist/extensions/notifications/index.js.map +1 -0
  179. package/dist/extensions/notifications/payload.d.ts +22 -0
  180. package/dist/extensions/notifications/payload.js +29 -0
  181. package/dist/extensions/notifications/payload.js.map +1 -0
  182. package/dist/extensions/notifications/router.d.ts +29 -0
  183. package/dist/extensions/notifications/router.js +55 -0
  184. package/dist/extensions/notifications/router.js.map +1 -0
  185. package/dist/extensions/notifications/tools.d.ts +12 -0
  186. package/dist/extensions/notifications/tools.js +41 -0
  187. package/dist/extensions/notifications/tools.js.map +1 -0
  188. package/dist/extensions/projects/context-provider.d.ts +9 -0
  189. package/dist/extensions/projects/context-provider.js +37 -0
  190. package/dist/extensions/projects/context-provider.js.map +1 -0
  191. package/dist/extensions/projects/git.d.ts +28 -0
  192. package/dist/extensions/projects/git.js +91 -0
  193. package/dist/extensions/projects/git.js.map +1 -0
  194. package/dist/extensions/projects/hooks.d.ts +7 -0
  195. package/dist/extensions/projects/hooks.js +42 -0
  196. package/dist/extensions/projects/hooks.js.map +1 -0
  197. package/dist/extensions/projects/index.d.ts +11 -0
  198. package/dist/extensions/projects/index.js +30 -0
  199. package/dist/extensions/projects/index.js.map +1 -0
  200. package/dist/extensions/projects/processor.d.ts +13 -0
  201. package/dist/extensions/projects/processor.js +63 -0
  202. package/dist/extensions/projects/processor.js.map +1 -0
  203. package/dist/extensions/projects/tools.d.ts +21 -0
  204. package/dist/extensions/projects/tools.js +118 -0
  205. package/dist/extensions/projects/tools.js.map +1 -0
  206. package/dist/extensions/registrations.d.ts +21 -0
  207. package/dist/extensions/registrations.js +12 -0
  208. package/dist/extensions/registrations.js.map +1 -0
  209. package/dist/extensions/repl/index.d.ts +2 -0
  210. package/dist/extensions/repl/index.js +85 -0
  211. package/dist/extensions/repl/index.js.map +1 -0
  212. package/dist/extensions/skills/agents.d.ts +17 -0
  213. package/dist/extensions/skills/agents.js +77 -0
  214. package/dist/extensions/skills/agents.js.map +1 -0
  215. package/dist/extensions/skills/delegate.d.ts +22 -0
  216. package/dist/extensions/skills/delegate.js +54 -0
  217. package/dist/extensions/skills/delegate.js.map +1 -0
  218. package/dist/extensions/skills/index.d.ts +11 -0
  219. package/dist/extensions/skills/index.js +43 -0
  220. package/dist/extensions/skills/index.js.map +1 -0
  221. package/dist/extensions/skills/reload.d.ts +8 -0
  222. package/dist/extensions/skills/reload.js +38 -0
  223. package/dist/extensions/skills/reload.js.map +1 -0
  224. package/dist/extensions/tasks/executor.d.ts +43 -0
  225. package/dist/extensions/tasks/executor.js +179 -0
  226. package/dist/extensions/tasks/executor.js.map +1 -0
  227. package/dist/extensions/tasks/expiration.d.ts +12 -0
  228. package/dist/extensions/tasks/expiration.js +17 -0
  229. package/dist/extensions/tasks/expiration.js.map +1 -0
  230. package/dist/extensions/tasks/generation.d.ts +14 -0
  231. package/dist/extensions/tasks/generation.js +70 -0
  232. package/dist/extensions/tasks/generation.js.map +1 -0
  233. package/dist/extensions/tasks/index.d.ts +14 -0
  234. package/dist/extensions/tasks/index.js +75 -0
  235. package/dist/extensions/tasks/index.js.map +1 -0
  236. package/dist/extensions/tasks/repository.d.ts +53 -0
  237. package/dist/extensions/tasks/repository.js +147 -0
  238. package/dist/extensions/tasks/repository.js.map +1 -0
  239. package/dist/extensions/tasks/schedule.d.ts +13 -0
  240. package/dist/extensions/tasks/schedule.js +32 -0
  241. package/dist/extensions/tasks/schedule.js.map +1 -0
  242. package/dist/extensions/tasks/schema.d.ts +423 -0
  243. package/dist/extensions/tasks/schema.js +45 -0
  244. package/dist/extensions/tasks/schema.js.map +1 -0
  245. package/dist/extensions/tasks/session-delivery.d.ts +18 -0
  246. package/dist/extensions/tasks/session-delivery.js +39 -0
  247. package/dist/extensions/tasks/session-delivery.js.map +1 -0
  248. package/dist/extensions/tasks/tools.d.ts +38 -0
  249. package/dist/extensions/tasks/tools.js +181 -0
  250. package/dist/extensions/tasks/tools.js.map +1 -0
  251. package/dist/extensions/telegram/buttons.d.ts +14 -0
  252. package/dist/extensions/telegram/buttons.js +49 -0
  253. package/dist/extensions/telegram/buttons.js.map +1 -0
  254. package/dist/extensions/telegram/channel.d.ts +39 -0
  255. package/dist/extensions/telegram/channel.js +201 -0
  256. package/dist/extensions/telegram/channel.js.map +1 -0
  257. package/dist/extensions/telegram/chunking.d.ts +7 -0
  258. package/dist/extensions/telegram/chunking.js +67 -0
  259. package/dist/extensions/telegram/chunking.js.map +1 -0
  260. package/dist/extensions/telegram/inbound.d.ts +7 -0
  261. package/dist/extensions/telegram/inbound.js +29 -0
  262. package/dist/extensions/telegram/inbound.js.map +1 -0
  263. package/dist/extensions/telegram/index.d.ts +13 -0
  264. package/dist/extensions/telegram/index.js +67 -0
  265. package/dist/extensions/telegram/index.js.map +1 -0
  266. package/dist/extensions/telegram/media.d.ts +39 -0
  267. package/dist/extensions/telegram/media.js +223 -0
  268. package/dist/extensions/telegram/media.js.map +1 -0
  269. package/dist/extensions/telegram/mutex.d.ts +9 -0
  270. package/dist/extensions/telegram/mutex.js +14 -0
  271. package/dist/extensions/telegram/mutex.js.map +1 -0
  272. package/dist/extensions/telegram/sending.d.ts +48 -0
  273. package/dist/extensions/telegram/sending.js +119 -0
  274. package/dist/extensions/telegram/sending.js.map +1 -0
  275. package/dist/extensions/telegram/streaming.d.ts +46 -0
  276. package/dist/extensions/telegram/streaming.js +140 -0
  277. package/dist/extensions/telegram/streaming.js.map +1 -0
  278. package/dist/extensions/telegram/tools.d.ts +80 -0
  279. package/dist/extensions/telegram/tools.js +232 -0
  280. package/dist/extensions/telegram/tools.js.map +1 -0
  281. package/dist/extensions/workflows/cleanup.d.ts +10 -0
  282. package/dist/extensions/workflows/cleanup.js +38 -0
  283. package/dist/extensions/workflows/cleanup.js.map +1 -0
  284. package/dist/extensions/workflows/index.d.ts +11 -0
  285. package/dist/extensions/workflows/index.js +42 -0
  286. package/dist/extensions/workflows/index.js.map +1 -0
  287. package/dist/extensions/workflows/loader.d.ts +27 -0
  288. package/dist/extensions/workflows/loader.js +90 -0
  289. package/dist/extensions/workflows/loader.js.map +1 -0
  290. package/dist/extensions/workflows/model.d.ts +19 -0
  291. package/dist/extensions/workflows/model.js +7 -0
  292. package/dist/extensions/workflows/model.js.map +1 -0
  293. package/dist/extensions/workflows/repository.d.ts +24 -0
  294. package/dist/extensions/workflows/repository.js +61 -0
  295. package/dist/extensions/workflows/repository.js.map +1 -0
  296. package/dist/extensions/workflows/schema.d.ts +193 -0
  297. package/dist/extensions/workflows/schema.js +20 -0
  298. package/dist/extensions/workflows/schema.js.map +1 -0
  299. package/dist/extensions/workflows/tools.d.ts +27 -0
  300. package/dist/extensions/workflows/tools.js +285 -0
  301. package/dist/extensions/workflows/tools.js.map +1 -0
  302. package/dist/log.d.ts +8 -0
  303. package/dist/log.js +15 -0
  304. package/dist/log.js.map +1 -0
  305. package/dist/main.d.ts +2 -0
  306. package/dist/main.js +27 -0
  307. package/dist/main.js.map +1 -0
  308. package/dist/migration/ask.d.ts +8 -0
  309. package/dist/migration/ask.js +24 -0
  310. package/dist/migration/ask.js.map +1 -0
  311. package/dist/migration/config.d.ts +10 -0
  312. package/dist/migration/config.js +122 -0
  313. package/dist/migration/config.js.map +1 -0
  314. package/dist/migration/context.d.ts +3 -0
  315. package/dist/migration/context.js +24 -0
  316. package/dist/migration/context.js.map +1 -0
  317. package/dist/migration/database.d.ts +8 -0
  318. package/dist/migration/database.js +51 -0
  319. package/dist/migration/database.js.map +1 -0
  320. package/dist/migration/fs.d.ts +1 -0
  321. package/dist/migration/fs.js +11 -0
  322. package/dist/migration/fs.js.map +1 -0
  323. package/dist/migration/index.d.ts +11 -0
  324. package/dist/migration/index.js +28 -0
  325. package/dist/migration/index.js.map +1 -0
  326. package/dist/migration/skills.d.ts +19 -0
  327. package/dist/migration/skills.js +77 -0
  328. package/dist/migration/skills.js.map +1 -0
  329. package/dist/scheduler.d.ts +17 -0
  330. package/dist/scheduler.js +53 -0
  331. package/dist/scheduler.js.map +1 -0
  332. package/dist/sessions/registry.d.ts +15 -0
  333. package/dist/sessions/registry.js +42 -0
  334. package/dist/sessions/registry.js.map +1 -0
  335. package/dist/workspace.d.ts +13 -0
  336. package/dist/workspace.js +32 -0
  337. package/dist/workspace.js.map +1 -0
  338. package/drizzle/0000_init.sql +19 -0
  339. package/drizzle/0001_extensions.sql +63 -0
  340. package/drizzle/meta/0000_snapshot.json +134 -0
  341. package/drizzle/meta/0001_snapshot.json +526 -0
  342. package/drizzle/meta/_journal.json +20 -0
  343. package/package.json +63 -0
  344. package/skills/skill-authoring/SKILL.md +168 -0
  345. package/skills/workflow-authoring/SKILL.md +251 -0
@@ -0,0 +1,65 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { defineExtension } from "../api.js";
4
+ const SOUL_TEMPLATE = `# Soul
5
+
6
+ You are Tachikoma, a proactive personal assistant. You maintain persistent memory
7
+ across conversations, learn continuously about the person you assist, and handle
8
+ background work during quiet moments.
9
+
10
+ You are curious, direct, and warm. You take initiative when it helps and stay out
11
+ of the way when it doesn't.
12
+ `;
13
+ const USER_TEMPLATE = `# User
14
+
15
+ Nothing is known about the user yet. This file accumulates durable knowledge about
16
+ who they are, extracted from conversations.
17
+ `;
18
+ const BASE_PROMPT = `You are a personal assistant operating inside your own workspace.
19
+ The workspace is a git-versioned directory that holds your memories, context files, and notes.
20
+ Prefer reading and writing workspace files over guessing; keep your knowledge files current.`;
21
+ const readOrCreate = async (path, template) => {
22
+ try {
23
+ return await readFile(path, "utf8");
24
+ }
25
+ catch (error) {
26
+ if (error.code !== "ENOENT")
27
+ throw error;
28
+ await writeFile(path, template, "utf8");
29
+ return template;
30
+ }
31
+ };
32
+ /**
33
+ * Foundational context: SOUL.md (personality) and USER.md (durable user knowledge)
34
+ * compose the system prompt. AGENTS.md is discovered natively by pi from the
35
+ * workspace root, so it needs no handling here.
36
+ */
37
+ export default defineExtension({
38
+ name: "context",
39
+ async setup(app) {
40
+ let soul = "";
41
+ let user = "";
42
+ app.bootstrap("load-context-files", async () => {
43
+ soul = await readOrCreate(app.workspace.resolve("SOUL.md"), SOUL_TEMPLATE);
44
+ user = await readOrCreate(app.workspace.resolve("USER.md"), USER_TEMPLATE);
45
+ });
46
+ // Re-read on every session build so core-context updates from the previous
47
+ // session's post-processing apply without a restart; the bootstrap snapshot
48
+ // is only the fallback when a read fails mid-flight.
49
+ const fresh = (path, fallback) => {
50
+ try {
51
+ return readFileSync(path, "utf8");
52
+ }
53
+ catch {
54
+ return fallback;
55
+ }
56
+ };
57
+ app.agent.systemPrompt(() => [
58
+ BASE_PROMPT,
59
+ fresh(app.workspace.resolve("SOUL.md"), soul),
60
+ fresh(app.workspace.resolve("USER.md"), user),
61
+ `Workspace root: ${app.workspace.root}`,
62
+ ].join("\n\n"));
63
+ },
64
+ });
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,aAAa,GAAG;;;;;;;;CAQrB,CAAC;AAEF,MAAM,aAAa,GAAG;;;;CAIrB,CAAC;AAEF,MAAM,WAAW,GAAG;;6FAEyE,CAAC;AAE9F,MAAM,YAAY,GAAG,KAAK,EAAE,IAAY,EAAE,QAAgB,EAAmB,EAAE;IAC7E,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;QAEpE,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACxC,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,eAAe,eAAe,CAAC;IAC7B,IAAI,EAAE,SAAS;IAEf,KAAK,CAAC,KAAK,CAAC,GAAG;QACb,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,IAAI,GAAG,EAAE,CAAC;QAEd,GAAG,CAAC,SAAS,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;YAC7C,IAAI,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;YAC3E,IAAI,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,2EAA2E;QAC3E,4EAA4E;QAC5E,qDAAqD;QACrD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,QAAgB,EAAU,EAAE;YACvD,IAAI,CAAC;gBACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QAEF,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAC1B;YACE,WAAW;YACX,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;YAC7C,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;YAC7C,mBAAmB,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE;SACxC,CAAC,IAAI,CAAC,MAAM,CAAC,CACf,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { SideRunner } from "../../agent/side-run.ts";
2
+ import type { Logger } from "../../log.ts";
3
+ import type { PostProcessor } from "../api.ts";
4
+ export declare const PENDING_SIGNALS_FILENAME = "pending-signals.md";
5
+ export interface PendingSignal {
6
+ date: string;
7
+ text: string;
8
+ }
9
+ export declare const parsePendingSignals: (content: string) => PendingSignal[];
10
+ /**
11
+ * Drop pending signals older than maxAgeDays. Runs before each context update
12
+ * so the recurrence-detection list never accumulates stale noise.
13
+ */
14
+ export declare const cleanPendingSignals: (dataDir: string, log: Logger, maxAgeDays?: number) => Promise<void>;
15
+ export interface CoreContextDeps {
16
+ side: Pick<SideRunner, "run">;
17
+ workspaceRoot: string;
18
+ /** Internal data directory holding the pending signals file (never committed). */
19
+ dataDir: string;
20
+ maxTranscriptChars?: number;
21
+ }
22
+ /**
23
+ * Conservative updates to the foundational context files (SOUL.md, USER.md,
24
+ * AGENTS.md at the workspace root) after each session, with a file-based
25
+ * pending-signals list for recurrence detection of ambiguous signals.
26
+ */
27
+ export declare const createCoreContextProcessor: ({ side, workspaceRoot, dataDir, maxTranscriptChars, }: CoreContextDeps) => PostProcessor;
@@ -0,0 +1,228 @@
1
+ import { readFile, unlink, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { localIsoDate } from "../memory/dates.js";
4
+ import { loadConversation } from "../memory/transcript.js";
5
+ export const PENDING_SIGNALS_FILENAME = "pending-signals.md";
6
+ const PENDING_SIGNALS_HEADER = "# Pending Signals\n\n";
7
+ const ENTRY_PATTERN = /^- \*\*(\d{4}-\d{2}-\d{2})\*\*:\s*(.+)$/gm;
8
+ const FILE_TOOLS = ["read", "grep", "find", "ls", "edit", "write"];
9
+ export const parsePendingSignals = (content) => [...content.matchAll(ENTRY_PATTERN)].map((match) => ({
10
+ date: match[1],
11
+ text: match[2],
12
+ }));
13
+ const serializePendingSignals = (entries) => `${PENDING_SIGNALS_HEADER}${entries.map((entry) => `- **${entry.date}**: ${entry.text}`).join("\n")}\n`;
14
+ /**
15
+ * Drop pending signals older than maxAgeDays. Runs before each context update
16
+ * so the recurrence-detection list never accumulates stale noise.
17
+ */
18
+ export const cleanPendingSignals = async (dataDir, log, maxAgeDays = 30) => {
19
+ const filePath = join(dataDir, PENDING_SIGNALS_FILENAME);
20
+ let content;
21
+ try {
22
+ content = await readFile(filePath, "utf8");
23
+ }
24
+ catch {
25
+ return;
26
+ }
27
+ if (content.trim() === "")
28
+ return;
29
+ const entries = parsePendingSignals(content);
30
+ if (entries.length === 0) {
31
+ log.warn({ file: filePath }, "pending signals file has content but no parseable entries");
32
+ return;
33
+ }
34
+ const cutoff = new Date();
35
+ cutoff.setDate(cutoff.getDate() - maxAgeDays);
36
+ const cutoffDate = localIsoDate(cutoff);
37
+ // Lexicographic comparison is correct for zero-padded YYYY-MM-DD dates.
38
+ const kept = entries.filter((entry) => entry.date >= cutoffDate);
39
+ if (kept.length === entries.length)
40
+ return;
41
+ if (kept.length === 0) {
42
+ await unlink(filePath);
43
+ return;
44
+ }
45
+ await writeFile(filePath, serializePendingSignals(kept), "utf8");
46
+ };
47
+ const readPendingSignals = async (filePath) => {
48
+ try {
49
+ return parsePendingSignals(await readFile(filePath, "utf8"));
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ };
55
+ const formatPendingSignalsSection = (snapshot) => {
56
+ if (snapshot.length === 0)
57
+ return "No pending signals at this time.";
58
+ return snapshot
59
+ .map((entry, index) => `S${index + 1}: **${entry.date}**: ${entry.text}`)
60
+ .join("\n");
61
+ };
62
+ const SYSTEM_TEMPLATE = `You are a context file update agent. Your task is to analyze the completed conversation and update the foundational context files when appropriate.
63
+
64
+ Today's date is {date}.
65
+
66
+ ## Your Task
67
+
68
+ 1. **Read all three context files:**
69
+ - \`$WORKSPACE/SOUL.md\` — Personality traits, tone, and behavioral guidelines
70
+ - \`$WORKSPACE/USER.md\` — What the assistant knows about the user
71
+ - \`$WORKSPACE/AGENTS.md\` — Operational instructions and workflow preferences
72
+
73
+ 2. **Review pending signals:**
74
+
75
+ {pending_signals_section}
76
+
77
+ The pending signals file lives at \`$SIGNALS_FILE\`. Manage it by editing that file directly:
78
+ - To stage a new signal, append a line in the format \`- **{date}**: signal text\`
79
+ - To remove a promoted or stale signal, delete its line
80
+ - Keep the \`# Pending Signals\` header as the first line; create the file with that header if it does not exist yet
81
+
82
+ 3. **Analyze the conversation** for information that should update these files:
83
+
84
+ **USER.md** — Stable identity and interests. Things that stay true for weeks or months:
85
+ - Name, location, employer, profession
86
+ - Broad interests and hobbies ("learning trumpet", "game development")
87
+ - Active project NAMES with one-line descriptions — not status, specs, or progress
88
+ - Communication preferences, learning style
89
+ DO NOT put in USER.md: project status updates, detailed specs, meeting prep, daily routine logs, game mechanics, implementation details. If a section is being rewritten more than once a week, it's too detailed for USER.md — that content belongs in memory files (facts or preferences).
90
+
91
+ **SOUL.md** — Personality and behavioral guidelines:
92
+ - Tone and communication style feedback ("be more concise", "push back more")
93
+ - Behavioral instructions that shape the assistant's character
94
+
95
+ **AGENTS.md** — Operational instructions and workflow preferences:
96
+ - Tool usage patterns, CLI preferences
97
+ - Workflow conventions, formatting rules
98
+ - System-specific instructions (task scheduling, note creation patterns)
99
+
100
+ **Correction Detection** — Watch for moments where the agent was corrected and extract the lesson as a behavioral instruction:
101
+ - **Explicit user corrections**: The user directly says "no", "don't", "wrong", "actually", or otherwise rejects the agent's approach and provides the right one
102
+ - **Implicit user corrections**: The user restates or rephrases their request after the agent gave a clearly wrong answer, or provides the correct answer themselves after the agent was wrong — only when the agent demonstrably erred, not normal conversational refinement
103
+ - **Agent self-corrections**: The agent acknowledges a mistake ("I was wrong", "let me fix that") and provides the corrected approach
104
+
105
+ When a correction is detected:
106
+ - Extract the lesson as a concise, positive instruction that describes the correct behavior: \`- When [context], [correct behavior].\`
107
+ - Lead with what to do, not what went wrong. The entry should teach the right approach as if explaining to a colleague — natural, direct, and actionable
108
+ - Place the entry under the AGENTS.md section that matches its domain. This keeps related instructions together. If no matching section exists, create one with a descriptive heading.
109
+ - Before adding, read existing entries in that section and skip if a semantically similar entry already covers it — or refine the existing entry if the correction adds new nuance (e.g., a missing condition or clarified boundary)
110
+ - Keep entries to one line each. No explanations, no context, no history
111
+
112
+ **Routing note**: Corrections about task execution, tool usage, or problem-solving go to AGENTS.md under the domain-appropriate section. Corrections about communication style or tone (e.g., "be more casual") go to SOUL.md as personality adjustments — those are not corrections.
113
+
114
+ 4. **Classify each signal** and take action:
115
+
116
+ **Clear & explicit signals** (strong evidence, unambiguous):
117
+ - Update the appropriate context file directly
118
+ - Read the file first, preserve structure, merge changes contextually
119
+ - Replace outdated information when there's clear evidence of change
120
+
121
+ **Ambiguous / one-off signals** (single mention, no clear directive):
122
+ - Check the pending signals list above for semantic recurrence
123
+ - If a recurring pattern is detected → promote it to a context file update AND delete the promoted signal's line from the pending signals file
124
+ - If it's a first occurrence → stage it as a new pending signal for future tracking
125
+
126
+ **Stale or irrelevant signals in the list:**
127
+ - Delete their lines to prevent noise in future sessions
128
+
129
+ **No relevant information** → do nothing (this is perfectly acceptable)
130
+
131
+ 5. **Prune stale content** from context files:
132
+ - While reading context files, actively look for content that is outdated or no longer accurate:
133
+ - USER.md: projects that were completed or abandoned (confirmed by conversation), outdated employer or role info, interests the user has moved away from, resolved bugs or issues the user discussed in the past, completed one-time tasks or work items, past events or completed trips, one-time plans that are now past
134
+ - AGENTS.md: entries about resolved bugs or completed work (the fix is done, the instruction is no longer needed), entries that duplicate another section (keep the better version and remove the other), procedural step-by-step instructions that belong elsewhere, outdated conventions
135
+ - SOUL.md: personality adjustments that the user has contradicted or reversed
136
+ - **Consolidate duplicate sections**: If two sections in the same file cover the same topic with semantically equivalent content, merge them into one section combining the best of both. Only consolidate when sections are truly equivalent — related-but-distinct topics (e.g., "remote work preferences" vs "home office equipment") must remain separate.
137
+ - Remove or update stale sections to keep files current and concise. Do not leave outdated info "just in case" — these files should be a current snapshot, not an archive.
138
+ - **Do NOT prune based on**: vague hints, assumptions, or the age of content alone (age is not staleness — only prune when you have clear evidence)
139
+
140
+ 6. **Important constraints:**
141
+ - **Be conservative**: Only apply changes with clear conversational evidence
142
+ - **Route correctly**: personality→SOUL, user info→USER, instructions→AGENTS
143
+ - **Read-first**: Always read a file before modifying it
144
+ - **Preserve structure**: Keep existing formatting and organization
145
+ - **Watch file size**: USER.md should stay under ~120 lines, AGENTS.md under ~400 lines. When a file exceeds its limit, prune actively:
146
+ - USER.md: summarize, remove stale sections, or omit details that belong in facts/preferences memory
147
+ - AGENTS.md: remove entries about resolved bugs or completed work, and consolidate duplicated entries across sections
148
+ - **Replace, don't append**: When updating a section, rewrite it cleanly rather than appending new paragraphs. Each section should read as a current snapshot, not a changelog.
149
+
150
+ ## Pending Signals Lifecycle
151
+
152
+ The pending signals mechanism tracks ambiguous observations that might become patterns if they recur:
153
+
154
+ 1. **Stage**: When you notice a potential signal but it's ambiguous or one-off, append a dated line to the pending signals file.
155
+
156
+ 2. **Promote**: When you detect a recurring pattern in pending signals, update the appropriate context file AND delete the promoted lines from the pending signals file.
157
+
158
+ 3. **Cleanup**: When you notice stale or irrelevant signals in the list, delete their lines proactively rather than waiting for the 30-day expiry.
159
+
160
+ ## Examples
161
+
162
+ ### Clear Signal → Direct Update
163
+ User: "I just started a new job at Acme Corp"
164
+ Action: Update USER.md with new employer information
165
+
166
+ ### Ambiguous Signal → Stage
167
+ User: "that was too verbose"
168
+ Action: Check pending signals above. If no similar signal, append a dated entry to the pending signals file for recurrence detection.
169
+
170
+ ### Recurring Signal → Promote and Remove
171
+ Pending signals: S1: "User seemed to prefer shorter responses"
172
+ Current message: "your answers are way too long"
173
+ Action: This confirms a pattern → update SOUL.md with preference for concise responses, then delete the S1 line from the pending signals file.
174
+
175
+ ### Stale Signal → Cleanup
176
+ Pending signals: S2: "User mentioned liking dark themes" (from 3 weeks ago, no recurrence in subsequent conversations)
177
+ Action: Delete the S2 line from the pending signals file.
178
+
179
+ ### Stale Content → Prune
180
+ USER.md contains: "- Planning trip to Berlin (March 15-20)"
181
+ Conversation reveals: The trip happened and is now in the past.
182
+ Action: Remove the trip entry — it's time-specific and no longer current.
183
+
184
+ ### Duplicate Sections → Consolidate
185
+ AGENTS.md has both a "Code Review" section and a "PR Conventions" section covering the same review workflow rules.
186
+ Action: Merge into a single "Code Review" section combining the rules from both.
187
+
188
+ ## Workspace Validation
189
+
190
+ Before writing claims about workspace state — file paths, project structure, configuration values — verify each claim directly by reading the relevant file(s) or grepping for the referenced content. Omit claims you cannot verify. Do NOT validate subjective information, preferences, or personal details.
191
+
192
+ ## Scope
193
+
194
+ You can read files anywhere in the workspace (needed for validation). Only modify \`$WORKSPACE/SOUL.md\`, \`$WORKSPACE/USER.md\`, \`$WORKSPACE/AGENTS.md\`, and the pending signals file at \`$SIGNALS_FILE\`.
195
+
196
+ ## Remember
197
+
198
+ These files shape the assistant's identity and behavior across all sessions. Updates should be deliberate and evidence-based. When in doubt, stage the signal for future recurrence detection rather than making premature changes.`;
199
+ /**
200
+ * Conservative updates to the foundational context files (SOUL.md, USER.md,
201
+ * AGENTS.md at the workspace root) after each session, with a file-based
202
+ * pending-signals list for recurrence detection of ambiguous signals.
203
+ */
204
+ export const createCoreContextProcessor = ({ side, workspaceRoot, dataDir, maxTranscriptChars = 24000, }) => ({
205
+ name: "core-context",
206
+ phase: "preFinalize",
207
+ async process({ transcriptPath, log }) {
208
+ if (transcriptPath == null) {
209
+ log.debug("no transcript — skipping core context update");
210
+ return;
211
+ }
212
+ const conversation = await loadConversation(transcriptPath, maxTranscriptChars);
213
+ if (conversation === "") {
214
+ log.debug("empty conversation — skipping core context update");
215
+ return;
216
+ }
217
+ await cleanPendingSignals(dataDir, log);
218
+ const signalsFile = join(dataDir, PENDING_SIGNALS_FILENAME);
219
+ const snapshot = await readPendingSignals(signalsFile);
220
+ const system = SYSTEM_TEMPLATE.replaceAll("{pending_signals_section}", formatPendingSignalsSection(snapshot))
221
+ .replaceAll("{date}", localIsoDate())
222
+ .replaceAll("$WORKSPACE", workspaceRoot)
223
+ .replaceAll("$SIGNALS_FILE", signalsFile);
224
+ const prompt = `The following conversation with the user just ended:\n\n<conversation>\n${conversation}\n</conversation>\n\nFollow your instructions and update the context files accordingly.`;
225
+ await side.run({ tools: FILE_TOOLS, system, prompt, tier: "processor" });
226
+ },
227
+ });
228
+ //# sourceMappingURL=processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processor.js","sourceRoot":"","sources":["../../../src/extensions/context/processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ interface DetachedProcessesConfig {
2
+ /** Default memory limit applied to spawned processes; 0 disables the default. */
3
+ defaultMemoryLimitMb: number;
4
+ watchIntervalSeconds: number;
5
+ }
6
+ /**
7
+ * Detached processes: spawn shell commands that outlive Tachikoma, capture
8
+ * their output to files, watch for exits, and notify on completion. The agent
9
+ * manages them through the process tools.
10
+ */
11
+ declare const _default: import("../api.ts").TachikomaExtension<DetachedProcessesConfig>;
12
+ export default _default;
@@ -0,0 +1,51 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { Type } from "typebox";
4
+ import { defineExtension } from "../api.js";
5
+ import { SystemdRunLimiter } from "./limits.js";
6
+ import { reconcileOnStartup } from "./reconcile.js";
7
+ import { ProcessRepository } from "./repository.js";
8
+ import { createProcessToolsFactory } from "./tools.js";
9
+ import { createWatcherTick } from "./watcher.js";
10
+ /**
11
+ * Detached processes: spawn shell commands that outlive Tachikoma, capture
12
+ * their output to files, watch for exits, and notify on completion. The agent
13
+ * manages them through the process tools.
14
+ */
15
+ export default defineExtension({
16
+ name: "detached-processes",
17
+ configSchema: Type.Object({
18
+ defaultMemoryLimitMb: Type.Number({ default: 1024 }),
19
+ watchIntervalSeconds: Type.Number({ default: 15 }),
20
+ }),
21
+ setup(app) {
22
+ const repository = new ProcessRepository(app.db);
23
+ const limiter = new SystemdRunLimiter(app.log);
24
+ const processesDir = join(app.workspace.dataDir, "processes");
25
+ const reconcile = {
26
+ repository,
27
+ processesDir,
28
+ notify: (notification) => app.events.emit("notify", {
29
+ title: `Process ${notification.processId}`,
30
+ text: notification.message,
31
+ severity: notification.severity === "error" ? "warning" : "info",
32
+ source: notification.source,
33
+ }),
34
+ log: app.log,
35
+ };
36
+ app.bootstrap("reconcile", async () => {
37
+ await mkdir(processesDir, { recursive: true });
38
+ await limiter.detect();
39
+ await reconcileOnStartup(reconcile);
40
+ });
41
+ app.agent.use(createProcessToolsFactory({
42
+ ...reconcile,
43
+ limiter,
44
+ defaultMemoryLimitMb: app.extensionConfig.defaultMemoryLimitMb > 0
45
+ ? app.extensionConfig.defaultMemoryLimitMb
46
+ : null,
47
+ }));
48
+ app.scheduler.every("detached-watch", app.extensionConfig.watchIntervalSeconds, createWatcherTick(reconcile));
49
+ },
50
+ });
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/detached-processes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAgD,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAQjD;;;;GAIG;AACH,eAAe,eAAe,CAA0B;IACtD,IAAI,EAAE,oBAAoB;IAE1B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACpD,oBAAoB,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;KACnD,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAE9D,MAAM,SAAS,GAAkB;YAC/B,UAAU;YACV,YAAY;YACZ,MAAM,EAAE,CAAC,YAAiC,EAAE,EAAE,CAC5C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACxB,KAAK,EAAE,WAAW,YAAY,CAAC,SAAS,EAAE;gBAC1C,IAAI,EAAE,YAAY,CAAC,OAAO;gBAC1B,QAAQ,EAAE,YAAY,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;gBAChE,MAAM,EAAE,YAAY,CAAC,MAAM;aAC5B,CAAC;YACJ,GAAG,EAAE,GAAG,CAAC,GAAG;SACb,CAAC;QAEF,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;YACpC,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,KAAK,CAAC,GAAG,CACX,yBAAyB,CAAC;YACxB,GAAG,SAAS;YACZ,OAAO;YACP,oBAAoB,EAClB,GAAG,CAAC,eAAe,CAAC,oBAAoB,GAAG,CAAC;gBAC1C,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,oBAAoB;gBAC1C,CAAC,CAAC,IAAI;SACX,CAAC,CACH,CAAC;QAEF,GAAG,CAAC,SAAS,CAAC,KAAK,CACjB,gBAAgB,EAChB,GAAG,CAAC,eAAe,CAAC,oBAAoB,EACxC,iBAAiB,CAAC,SAAS,CAAC,CAC7B,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { Logger } from "../../log.ts";
2
+ export interface WrappedCommand {
3
+ file: string;
4
+ args: string[];
5
+ /** Whether a memory limit is actually enforced on the spawned process. */
6
+ limited: boolean;
7
+ }
8
+ /**
9
+ * Seam for resource limiting; a direct cgroup v2 implementation can slot in
10
+ * behind this interface.
11
+ */
12
+ export interface ProcessLimiter {
13
+ wrap(command: string, memoryLimitMb: number | null): WrappedCommand;
14
+ }
15
+ /**
16
+ * Limits via `systemd-run --user --scope -p MemoryMax=…`: systemd places the
17
+ * command in a transient scope (cgroup) and exits with the command's status,
18
+ * so liveness checks and exit codes behave like an unwrapped spawn.
19
+ */
20
+ export declare class SystemdRunLimiter implements ProcessLimiter {
21
+ private available;
22
+ private readonly log;
23
+ constructor(log: Logger);
24
+ /** Probe `systemd-run` once at bootstrap; wrap() degrades gracefully without it. */
25
+ detect(probe?: () => Promise<unknown>): Promise<void>;
26
+ wrap(command: string, memoryLimitMb: number | null): WrappedCommand;
27
+ }
@@ -0,0 +1,55 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const execFileAsync = promisify(execFile);
4
+ const plainShell = (command) => ({
5
+ file: "sh",
6
+ args: ["-c", command],
7
+ limited: false,
8
+ });
9
+ /**
10
+ * Limits via `systemd-run --user --scope -p MemoryMax=…`: systemd places the
11
+ * command in a transient scope (cgroup) and exits with the command's status,
12
+ * so liveness checks and exit codes behave like an unwrapped spawn.
13
+ */
14
+ export class SystemdRunLimiter {
15
+ available = false;
16
+ log;
17
+ constructor(log) {
18
+ this.log = log;
19
+ }
20
+ /** Probe `systemd-run` once at bootstrap; wrap() degrades gracefully without it. */
21
+ async detect(probe = () => execFileAsync("systemd-run", ["--version"])) {
22
+ try {
23
+ await probe();
24
+ this.available = true;
25
+ }
26
+ catch {
27
+ this.available = false;
28
+ this.log.info("systemd-run not available — processes will run without memory limits");
29
+ }
30
+ }
31
+ wrap(command, memoryLimitMb) {
32
+ if (memoryLimitMb == null)
33
+ return plainShell(command);
34
+ if (!this.available) {
35
+ this.log.warn({ memoryLimitMb }, "systemd-run unavailable — spawning without memory limit");
36
+ return plainShell(command);
37
+ }
38
+ return {
39
+ file: "systemd-run",
40
+ args: [
41
+ "--user",
42
+ "--scope",
43
+ "--quiet",
44
+ "-p",
45
+ `MemoryMax=${memoryLimitMb}M`,
46
+ "--",
47
+ "sh",
48
+ "-c",
49
+ command,
50
+ ],
51
+ limited: true,
52
+ };
53
+ }
54
+ }
55
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../../src/extensions/detached-processes/limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAItC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAiB1C,MAAM,UAAU,GAAG,CAAC,OAAe,EAAkB,EAAE,CAAC,CAAC;IACvD,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,KAAK;CACf,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IACpB,SAAS,GAAG,KAAK,CAAC;IACT,GAAG,CAAS;IAE7B,YAAY,GAAW;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,oFAAoF;IACpF,KAAK,CAAC,MAAM,CACV,QAAgC,GAAG,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,CAAC,WAAW,CAAC,CAAC;QAEjF,IAAI,CAAC;YACH,MAAM,KAAK,EAAE,CAAC;YACd,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,aAA4B;QAChD,IAAI,aAAa,IAAI,IAAI;YAAE,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAEtD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,EAAE,yDAAyD,CAAC,CAAC;YAC5F,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAED,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE;gBACJ,QAAQ;gBACR,SAAS;gBACT,SAAS;gBACT,IAAI;gBACJ,aAAa,aAAa,GAAG;gBAC7B,IAAI;gBACJ,IAAI;gBACJ,IAAI;gBACJ,OAAO;aACR;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ /** Read the last chunk of a log file; null when the file does not exist. */
2
+ export declare const readOutputTail: (path: string) => Promise<string | null>;
@@ -0,0 +1,26 @@
1
+ import { open, stat } from "node:fs/promises";
2
+ // Generous raw window — truncateTail trims it to pi's byte/line limits afterwards.
3
+ const TAIL_READ_BYTES = 256 * 1024;
4
+ /** Read the last chunk of a log file; null when the file does not exist. */
5
+ export const readOutputTail = async (path) => {
6
+ let size;
7
+ try {
8
+ size = (await stat(path)).size;
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ if (size === 0)
14
+ return "";
15
+ const start = Math.max(0, size - TAIL_READ_BYTES);
16
+ const handle = await open(path, "r");
17
+ try {
18
+ const buffer = Buffer.alloc(size - start);
19
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, start);
20
+ return buffer.subarray(0, bytesRead).toString("utf-8");
21
+ }
22
+ finally {
23
+ await handle.close();
24
+ }
25
+ };
26
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/extensions/detached-processes/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE9C,mFAAmF;AACnF,MAAM,eAAe,GAAG,GAAG,GAAG,IAAI,CAAC;AAEnC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,IAAY,EAA0B,EAAE;IAC3E,IAAI,IAAY,CAAC;IAEjB,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE1B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,eAAe,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAErC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;QAC1C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAEzE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzD,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { Logger } from "../../log.ts";
2
+ import type { ProcessRepository } from "./repository.ts";
3
+ export interface ProcessNotification {
4
+ source: string;
5
+ processId: string;
6
+ severity: "info" | "error";
7
+ message: string;
8
+ }
9
+ export interface ReconcileDeps {
10
+ repository: ProcessRepository;
11
+ processesDir: string;
12
+ notify: (notification: ProcessNotification) => void;
13
+ log: Logger;
14
+ now?: () => Date;
15
+ }
16
+ /**
17
+ * Transition a running record to exited, optionally dispatching a notification.
18
+ * Idempotent: a conditional UPDATE makes concurrent reconcilers converge to a
19
+ * single winner, and only the winner notifies.
20
+ */
21
+ export declare const reconcileExit: (deps: ReconcileDeps, recordId: string, { dispatchNotification }?: {
22
+ dispatchNotification?: boolean;
23
+ }) => Promise<void>;
24
+ /**
25
+ * Crash recovery: reconcile records whose processes died while the host was
26
+ * down. Notifications are suppressed so the user doesn't get a burst on
27
+ * restart. Records whose pids are still alive simply stay running — without a
28
+ * create-time check a reused pid would keep a record alive until its next
29
+ * natural exit, which the polling watcher then reconciles.
30
+ */
31
+ export declare const reconcileOnStartup: (deps: ReconcileDeps) => Promise<void>;
@@ -0,0 +1,71 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { setTimeout as sleep } from "node:timers/promises";
3
+ import { STOP_REASON_AGENT_STOPPED } from "./schema.js";
4
+ import { exitCodePath, isAlive } from "./spawn.js";
5
+ const SIGKILL_EXIT_CODE = 137;
6
+ const readExitCode = async (path) => {
7
+ // Retry once after 100ms: the watcher's kill(pid, 0) can observe death before
8
+ // the in-process exit listener has written the sidecar.
9
+ for (const delayMs of [0, 100]) {
10
+ if (delayMs > 0)
11
+ await sleep(delayMs);
12
+ try {
13
+ const parsed = Number.parseInt(await readFile(path, "utf-8"), 10);
14
+ return Number.isNaN(parsed) ? null : parsed;
15
+ }
16
+ catch {
17
+ // Missing sidecar — exit happened while the host was down (unknowable code).
18
+ }
19
+ }
20
+ return null;
21
+ };
22
+ /**
23
+ * Transition a running record to exited, optionally dispatching a notification.
24
+ * Idempotent: a conditional UPDATE makes concurrent reconcilers converge to a
25
+ * single winner, and only the winner notifies.
26
+ */
27
+ export const reconcileExit = async (deps, recordId, { dispatchNotification = true } = {}) => {
28
+ const { repository, processesDir, notify, log } = deps;
29
+ const now = deps.now ?? (() => new Date());
30
+ try {
31
+ const record = repository.get(recordId);
32
+ if (record == null || record.status !== "running")
33
+ return;
34
+ const exitCode = await readExitCode(exitCodePath(processesDir, record.id));
35
+ const won = repository.reconcileToExited(record.id, now(), exitCode);
36
+ if (!won || !dispatchNotification)
37
+ return;
38
+ if (record.stopReason === STOP_REASON_AGENT_STOPPED) {
39
+ log.debug({ id: recordId }, "suppressing exit notification for agent-stopped process");
40
+ return;
41
+ }
42
+ const message = exitCode === SIGKILL_EXIT_CODE
43
+ ? `Process '${record.name}' (id: ${record.id}) was killed by signal (SIGKILL).`
44
+ : `Process '${record.name}' (id: ${record.id}) exited with code ${exitCode ?? "unknown"}.`;
45
+ notify({
46
+ source: `Detached process: ${record.name}`,
47
+ processId: record.id,
48
+ severity: exitCode === 0 ? "info" : "error",
49
+ message,
50
+ });
51
+ }
52
+ catch (error) {
53
+ log.error({ id: recordId, err: error }, "error reconciling process");
54
+ }
55
+ };
56
+ /**
57
+ * Crash recovery: reconcile records whose processes died while the host was
58
+ * down. Notifications are suppressed so the user doesn't get a burst on
59
+ * restart. Records whose pids are still alive simply stay running — without a
60
+ * create-time check a reused pid would keep a record alive until its next
61
+ * natural exit, which the polling watcher then reconciles.
62
+ */
63
+ export const reconcileOnStartup = async (deps) => {
64
+ for (const record of deps.repository.listRunning()) {
65
+ if (isAlive(record.pid))
66
+ continue;
67
+ await reconcileExit(deps, record.id, { dispatchNotification: false });
68
+ deps.log.info({ id: record.id }, "crash recovery: marked process as exited");
69
+ }
70
+ };
71
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../src/extensions/detached-processes/reconcile.ts"],"names":[],"mappings":"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"}