@tekmidian/pai 1.0.0 → 1.0.3

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 (219) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +67 -5
  3. package/dist/aibroker-client-BxzeEHPF.mjs +3 -0
  4. package/dist/{aibroker-client-CXEYBzbf.mjs → aibroker-client-CQPGqLxL.mjs} +1 -1
  5. package/dist/atomic-file-BorMa4T3.mjs +21 -0
  6. package/dist/{auto-route-D5SXrMpB.mjs → auto-route-B7Yu6Uam.mjs} +3 -3
  7. package/dist/auto-route-CjjQqU1o.mjs +11 -0
  8. package/dist/budget-Dod6rnA9.mjs +15 -0
  9. package/dist/{caller-identity-CVTuiLSs.mjs → caller-identity-yLFb7CKq.mjs} +6 -5
  10. package/dist/{capacity-DaXrVj3J.mjs → capacity-DEveQdFV.mjs} +3 -3
  11. package/dist/{chain-BcQlIlEd.mjs → chain-7n9Gx56r.mjs} +5 -5
  12. package/dist/cli/index.mjs +1628 -370
  13. package/dist/{cockpit-actions-ODmt8yF9.mjs → cockpit-actions-BBHeyEZI.mjs} +84 -42
  14. package/dist/cockpit-actions-DR9X514g.mjs +20 -0
  15. package/dist/cockpit-memory--dnShXBA.mjs +20 -0
  16. package/dist/{config-BSXKyeJ1.mjs → config-Bu1_vbY8.mjs} +42 -3
  17. package/dist/{config-B955yYmo.mjs → config-Cxi-aBf4.mjs} +2 -2
  18. package/dist/{config-TaWmdTLx.mjs → config-DtENJ6cL.mjs} +1 -1
  19. package/dist/{config-D5chi2LO.mjs → config-XpZWOYOG.mjs} +2 -2
  20. package/dist/{controls-BGkK_Bdi.mjs → controls-CPoeudou.mjs} +37 -22
  21. package/dist/daemon/index.mjs +43 -42
  22. package/dist/daemon-DKdBNKfa.mjs +45 -0
  23. package/dist/{daemon-CorGh6Ub.mjs → daemon-DPAo6WTa.mjs} +103 -55
  24. package/dist/daemon-mcp/index.mjs +40 -39
  25. package/dist/{define-DGFu-sCf.mjs → define-CjJsmq-6.mjs} +1 -1
  26. package/dist/{detect-C1d5cUkT.mjs → detect-9CZNO0yf.mjs} +2 -2
  27. package/dist/detect-D1ziFFfA.mjs +10 -0
  28. package/dist/{embed-pass-CeQD8kRE.mjs → embed-pass-DIWjA68P.mjs} +5 -5
  29. package/dist/{embedding-gate-BHuBsgHD.mjs → embedding-gate-6rZrWRXa.mjs} +5 -5
  30. package/dist/{embedding-gate-Z7CJSOJk.mjs → embedding-gate-Cnk4YCyX.mjs} +3 -3
  31. package/dist/{embeddings-D4t6L-EH.mjs → embeddings-BVq3esBF.mjs} +1 -1
  32. package/dist/{embeddings-OF9Pt4by.mjs → embeddings-CIkJBKd-.mjs} +1 -1
  33. package/dist/{endreason-x1KOSrcO.mjs → endreason-C577xIQz.mjs} +7 -6
  34. package/dist/{env-DArqGq-Z.mjs → env-BE8mrsxr.mjs} +1 -1
  35. package/dist/factory-Bsib-qDG.mjs +9 -0
  36. package/dist/{factory-BtjbQ0np.mjs → factory-DjAAsqr_.mjs} +15 -13
  37. package/dist/{fallback-BIyEpPsQ.mjs → fallback-DFlItw1Z.mjs} +5 -5
  38. package/dist/{fmt-age-BiSHrTKN.mjs → fmt-age-ByDkvkoU.mjs} +1 -1
  39. package/dist/{health-C6jZWvRG.mjs → health-Dnym-qcT.mjs} +4 -4
  40. package/dist/{helpers-DFmYRCOa.mjs → helpers-BD6V-p9w.mjs} +2 -2
  41. package/dist/helpers-CMGjy_0N.mjs +10 -0
  42. package/dist/{helpers-CJpb_MNB.mjs → helpers-CRuBUas3.mjs} +87 -38
  43. package/dist/hooks/capture-all-events.mjs +52 -11
  44. package/dist/hooks/capture-session-summary.mjs +64 -47
  45. package/dist/hooks/capture-tool-output.mjs +49 -8
  46. package/dist/hooks/context-compression-hook.mjs +30 -10
  47. package/dist/hooks/initialize-session.mjs +1 -0
  48. package/dist/hooks/load-core-context.mjs +1 -1
  49. package/dist/hooks/load-project-context.mjs +32 -29
  50. package/dist/hooks/security-validator.mjs +45 -13
  51. package/dist/hooks/stop-hook.mjs +54 -16
  52. package/dist/hooks/supervision-banner.mjs +9 -0
  53. package/dist/hooks/sync-todo-to-md.mjs +147 -113
  54. package/dist/hooks/worker-edit-repair.mjs +2 -1
  55. package/dist/hooks/worker-guard.mjs +40 -16
  56. package/dist/hooks/worker-note.mjs +12 -5
  57. package/dist/hooks/worker-path-repair.mjs +1 -1
  58. package/dist/hooks/worker-pool-host.mjs +329 -231
  59. package/dist/hooks/worker-status-line.mjs +195 -114
  60. package/dist/index.d.mts +18 -0
  61. package/dist/index.mjs +12 -12
  62. package/dist/{indexer-backend-BM2CUSu8.mjs → indexer-backend-Cll7le4P.mjs} +149 -96
  63. package/dist/{ipc-client-BMqWw_gv.mjs → ipc-client-CaiGJnSM.mjs} +1 -1
  64. package/dist/{ipc-client-YYDD6zzU.mjs → ipc-client-ZRmDKd5Z.mjs} +1 -1
  65. package/dist/kill-tree-BG7dRVtp.mjs +10 -0
  66. package/dist/{kill-tree-BjYz5BxK.mjs → kill-tree-EWYaEk5v.mjs} +8 -8
  67. package/dist/{launch-CLp0OJxV.mjs → launch-CXVlZjHt.mjs} +8 -8
  68. package/dist/{launch-DgqQdAjI.mjs → launch-Jh5NVeNe.mjs} +3 -3
  69. package/dist/local-candidate-DLdwQt0s.mjs +112 -0
  70. package/dist/local-instances-DOxSgT_L.mjs +210 -0
  71. package/dist/local-instances-ZeRkkuS5.mjs +6 -0
  72. package/dist/{local-tiers-DPZYqv9m.mjs → local-tiers-BE4jGaMa.mjs} +2 -2
  73. package/dist/{local-tiers-DnBIAkbK.mjs → local-tiers-DNMI5W86.mjs} +3 -3
  74. package/dist/{main-config-ops-C0SflOWk.mjs → main-config-ops-BEMpuhDz.mjs} +3 -3
  75. package/dist/{main-resolver-DVPpSWVy.mjs → main-resolver-3Ns1HPmS.mjs} +18 -17
  76. package/dist/{migrate-BUJHadA8.mjs → migrate-DrA_OwlZ.mjs} +45 -14
  77. package/dist/min-max-DrSRUaWQ.mjs +16 -0
  78. package/dist/{model-inventory-Dasber3S.mjs → model-inventory-BFVSNFT3.mjs} +13 -12
  79. package/dist/{models-add-Bt7aUHDs.mjs → models-add-C1QIc1DA.mjs} +39 -38
  80. package/dist/operator-BTqvTIc8.mjs +7 -0
  81. package/dist/{operator-DZxcP-g8.mjs → operator-CyuB3aVw.mjs} +3 -3
  82. package/dist/{pai-marker-kIYuNt3c.mjs → pai-marker-BcZyJguM.mjs} +7 -6
  83. package/dist/{pane-audit-OGYrOAik.mjs → pane-audit-DKQt71s4.mjs} +7 -7
  84. package/dist/{paths-DoRuFc60.mjs → paths-CFHTloSe.mjs} +3 -3
  85. package/dist/{paths-DoIyRnhx.mjs → paths-CHifubDh.mjs} +3 -3
  86. package/dist/{planner-DLGOYlVb.mjs → planner-BmFACdLa.mjs} +31 -30
  87. package/dist/{pool-CZ5MZaXg.mjs → pool-Dh-q3iJi.mjs} +45 -6
  88. package/dist/{pool-cli-BxNSPzzY.mjs → pool-cli-BggU8GRp.mjs} +5 -5
  89. package/dist/{pool-cli-CbJfl37y.mjs → pool-cli-DQ6OzRy2.mjs} +6 -6
  90. package/dist/postgres-BdBNkL_l.mjs +7 -0
  91. package/dist/{postgres-CDnd_k5f.mjs → postgres-C6ti4haA.mjs} +55 -34
  92. package/dist/{project-setup-DM4npnL3.mjs → project-setup-jvfONJTQ.mjs} +131 -74
  93. package/dist/providers-BeDiVEb-.mjs +16 -0
  94. package/dist/{providers-UsKtLcUR.mjs → providers-CblTFrhz.mjs} +6 -6
  95. package/dist/{query-feedback-I-txSnld.mjs → query-feedback-BRfPtqki.mjs} +2 -2
  96. package/dist/registrable-BzH4TObg.mjs +9 -0
  97. package/dist/{registrable-LrRIVYQc.mjs → registrable-DpyTtajg.mjs} +4 -4
  98. package/dist/{render-BDbb4CF-.mjs → render-BYmQkuLd.mjs} +8 -8
  99. package/dist/render-CoMvwMPs.mjs +13 -0
  100. package/dist/{reranker-B4r4vykM.mjs → reranker-8g6xMwP7.mjs} +1 -1
  101. package/dist/{reranker-DxxMCtA0.mjs → reranker-CqCbpGnX.mjs} +1 -1
  102. package/dist/root-FtJsxYnm.mjs +36 -0
  103. package/dist/{root-BMeJuHOb.mjs → root-Xglu0OPD.mjs} +11 -11
  104. package/dist/route-table-B7zpoypi.mjs +15 -0
  105. package/dist/{router-L8Nu-OHF.mjs → router-CQSc3lk3.mjs} +17 -7
  106. package/dist/{router-D6Ec6Fid.mjs → router-DwHJRgG9.mjs} +1 -1
  107. package/dist/{routing-CUyiglfu.mjs → routing-BAAST9vT.mjs} +27 -308
  108. package/dist/{routing-ZuGlc0ge.mjs → routing-CENt4HH1.mjs} +13 -12
  109. package/dist/{run-ulAi5dFD.mjs → run-CSC7mgPf.mjs} +35 -33
  110. package/dist/{scan-B7I0gPe-.mjs → scan-C_Y5W9py.mjs} +6 -6
  111. package/dist/{local-candidate-jOjTNlOL.mjs → scheduler-CtUyWJ3O.mjs} +7 -109
  112. package/dist/scheduler-dmHXoRvm.mjs +3 -0
  113. package/dist/{scope-D5VSQxe4.mjs → scope-D-h6F6DN.mjs} +4 -4
  114. package/dist/{search-CPoE0830.mjs → search-RSyeQqj1.mjs} +18 -18
  115. package/dist/{session-CNXF6DLP.mjs → session-8iqQDkqb.mjs} +55 -31
  116. package/dist/session-BeqAbNPd.mjs +35 -0
  117. package/dist/session-scan-BJI_ZO8M.mjs +9 -0
  118. package/dist/{session-scan-BDLEc4w1.mjs → session-scan-W-gbwJHM.mjs} +6 -6
  119. package/dist/{sources-CjhUKKtO.mjs → sources-ESUkZaWv.mjs} +1 -1
  120. package/dist/{status-DYydKQUI.mjs → status-Dm4dYciN.mjs} +4 -4
  121. package/dist/{status-CvVi9cDf.mjs → status-ggJ7lgVg.mjs} +17 -9
  122. package/dist/{stop-words-SdSL1WKH.mjs → stop-words-DSEYQdNe.mjs} +1 -1
  123. package/dist/{test-isolation-CoDeOVXb.mjs → test-isolation-lqSmYxr0.mjs} +1 -1
  124. package/dist/{timeouts-OwEONmc8.mjs → timeouts-1MLooEBB.mjs} +1 -1
  125. package/dist/{tool-errors-AeRQuG3Y.mjs → tool-errors-7E13StWd.mjs} +4 -3
  126. package/dist/{tool-errors-R5E0fCgf.mjs → tool-errors-CJWODvlo.mjs} +6 -6
  127. package/dist/viewer-CqozWW39.mjs +22 -0
  128. package/dist/{viewer-DtWj5Vti.mjs → viewer-mDXUNHNy.mjs} +15 -15
  129. package/dist/{wait-BMUR-U-U.mjs → wait-ByV0OyMZ.mjs} +3 -3
  130. package/dist/{worker-config-BLqa1yiD.mjs → worker-config-CJhEMk19.mjs} +294 -92
  131. package/dist/worker-config-DESkIjMF.mjs +41 -0
  132. package/dist/{workers-config-DEI9lrV4.mjs → workers-config-f8KrkY4L.mjs} +2 -2
  133. package/dist/{worktree-zmRf556h.mjs → worktree-CRs2gyg0.mjs} +37 -7
  134. package/dist/{zettelkasten-nigq7fqs.mjs → zettelkasten-B_rRRRvD.mjs} +5 -5
  135. package/dist/{zettelkasten-Cr_Xj0mG.mjs → zettelkasten-BzMm8REH.mjs} +29 -19
  136. package/docs/README.md +2 -0
  137. package/docs/cockpit.md +10 -4
  138. package/docs/commands/README.md +4 -4
  139. package/docs/commands/backup.md +2 -0
  140. package/docs/commands/install.md +29 -4
  141. package/docs/commands/providers.md +1 -1
  142. package/docs/commands/restore.md +4 -0
  143. package/docs/commands/setup.md +29 -4
  144. package/docs/commands/worker.md +1 -1
  145. package/docs/config.md +2 -0
  146. package/docs/features.md +299 -0
  147. package/docs/images/architecture.drawio +51 -0
  148. package/docs/images/architecture.png +0 -0
  149. package/docs/images/architecture.svg +3 -0
  150. package/docs/images/cockpit-01-system-tokens.jpg +0 -0
  151. package/docs/images/cockpit-02-totals-cache.jpg +0 -0
  152. package/docs/images/cockpit-03-local-servers-models-pool.jpg +0 -0
  153. package/docs/images/cockpit-04-workers-memory.jpg +0 -0
  154. package/docs/images/cockpit-05-performance.jpg +0 -0
  155. package/docs/images/cockpit-06-runs-speed-routing.jpg +0 -0
  156. package/docs/images/cockpit-07-settings-registry.jpg +0 -0
  157. package/docs/install/index.md +29 -0
  158. package/docs/three-tier-coding.md +1 -1
  159. package/gemini-extension.json +1 -1
  160. package/package.json +1 -5
  161. package/pai-plugin.json +1 -1
  162. package/plugins/context-preservation/plugin.json +1 -1
  163. package/plugins/core/plugin.json +1 -1
  164. package/plugins/core/skills/setup/SKILL.md +41 -9
  165. package/plugins/creative/plugin.json +1 -1
  166. package/plugins/observability/plugin.json +1 -1
  167. package/plugins/productivity/plugin.json +1 -1
  168. package/plugins/semantic-search/plugin.json +1 -1
  169. package/plugins/ui/plugin.json +1 -1
  170. package/plugins/zettelkasten/plugin.json +1 -1
  171. package/src/hooks/session-autosave.sh +8 -1
  172. package/src/hooks/session-stop.sh +8 -1
  173. package/src/hooks/ts/capture-all-events.ts +7 -12
  174. package/src/hooks/ts/lib/context-fill.ts +2 -1
  175. package/src/hooks/ts/lib/path-repair.ts +1 -1
  176. package/src/hooks/ts/lib/project-utils/notify.ts +1 -0
  177. package/src/hooks/ts/lib/project-utils/session-notes.ts +23 -6
  178. package/src/hooks/ts/lib/raw-capture.ts +36 -0
  179. package/src/hooks/ts/lib/session-summary.ts +129 -0
  180. package/src/hooks/ts/lib/todo-sync.ts +120 -0
  181. package/src/hooks/ts/lib/worker-git-guard.ts +1 -1
  182. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +6 -10
  183. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +5 -105
  184. package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
  185. package/src/hooks/ts/pre-tool-use/security-validator.ts +35 -14
  186. package/src/hooks/ts/session-end/capture-session-summary.ts +20 -128
  187. package/src/hooks/ts/session-start/load-core-context.ts +3 -3
  188. package/src/hooks/ts/session-start/load-project-context.ts +18 -22
  189. package/src/hooks/ts/stop/stop-hook.ts +13 -5
  190. package/src/hooks/ztk-auto.sh +7 -2
  191. package/dist/aibroker-client-JjE74jo0.mjs +0 -3
  192. package/dist/auto-route-BRk8NEFv.mjs +0 -11
  193. package/dist/budget-DuhQ2zzG.mjs +0 -14
  194. package/dist/cockpit-actions-DcQ-ucU8.mjs +0 -19
  195. package/dist/cockpit-memory-KV45PPO2.mjs +0 -19
  196. package/dist/daemon-C7yt_ptC.mjs +0 -44
  197. package/dist/detect-DRUkmHnb.mjs +0 -10
  198. package/dist/factory-DAGG9_n8.mjs +0 -9
  199. package/dist/helpers-DPJEHPq6.mjs +0 -10
  200. package/dist/kill-tree-LV0Q7Mte.mjs +0 -10
  201. package/dist/operator-CfL0HiBK.mjs +0 -7
  202. package/dist/postgres-DgqWyqhN.mjs +0 -7
  203. package/dist/providers-CTu2s1z2.mjs +0 -15
  204. package/dist/registrable-D9aNZOTh.mjs +0 -9
  205. package/dist/render-BSfVlYHj.mjs +0 -13
  206. package/dist/root-xI9DqVIe.mjs +0 -35
  207. package/dist/route-table-HjvnLVEU.mjs +0 -14
  208. package/dist/session-BJHzmUg2.mjs +0 -34
  209. package/dist/session-scan-YORKE1NO.mjs +0 -9
  210. package/dist/viewer-B8709jDN.mjs +0 -22
  211. package/dist/worker-config-D8tRuvo7.mjs +0 -40
  212. package/docs/images/cockpit-limits-routing.jpg +0 -0
  213. package/docs/images/cockpit-models.jpg +0 -0
  214. package/docs/images/cockpit-usage.jpg +0 -0
  215. package/docs/images/cockpit-workers-system.jpg +0 -0
  216. package/scripts/build-docs.mjs +0 -41
  217. package/scripts/build-hooks.mjs +0 -311
  218. package/scripts/build-skill-stubs.mjs +0 -216
  219. package/scripts/copy-defaults.mjs +0 -46
@@ -1,12 +1,13 @@
1
1
  import { _ as resolvePaiFile, d as defaultPaiHomeDir, g as paiHomePath } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { A as postgresConnectionString, c as loadConfig, k as DEFAULT_PG_CONNECTION_TIMEOUT_MS } from "./config-BSXKyeJ1.mjs";
3
- import { a as parseSessionTitleChunk, c as yieldToEventLoop, f as fileContentHash, h as sha256File, i as isPathTooBroadForContentScan, n as chunkId, o as walkContentFiles, r as detectTier, s as walkMdFiles, t as INDEX_YIELD_EVERY, u as chunkMarkdown } from "./helpers-CJpb_MNB.mjs";
4
- import { t as STOP_WORDS } from "./stop-words-SdSL1WKH.mjs";
5
- import { n as buildFtsQuery, r as isQuerySyntaxError } from "./search-CPoE0830.mjs";
6
- import { a as entityContentId, t as PostgresBackend } from "./postgres-CDnd_k5f.mjs";
7
- import { p as LEGACY_EMBEDDING_BINDING } from "./embedding-gate-BHuBsgHD.mjs";
8
- import { i as deserializeEmbedding, r as cosineSimilarity } from "./embeddings-D4t6L-EH.mjs";
9
- import { n as routeNotification } from "./router-L8Nu-OHF.mjs";
2
+ import { M as postgresConnectionString, c as loadConfig, k as DEFAULT_PG_CONNECTION_TIMEOUT_MS } from "./config-Bu1_vbY8.mjs";
3
+ import { a as parseSessionTitleChunk, c as yieldToEventLoop, f as fileContentHash, h as sha256File, i as isPathTooBroadForContentScan, n as chunkId, o as walkContentFiles, r as detectTier, s as walkMdFiles, t as INDEX_YIELD_EVERY, u as chunkMarkdown } from "./helpers-CRuBUas3.mjs";
4
+ import { t as STOP_WORDS } from "./stop-words-DSEYQdNe.mjs";
5
+ import { n as buildFtsQuery, r as isQuerySyntaxError } from "./search-RSyeQqj1.mjs";
6
+ import { n as minOf, t as maxOf } from "./min-max-DrSRUaWQ.mjs";
7
+ import { a as entityContentId, t as PostgresBackend } from "./postgres-C6ti4haA.mjs";
8
+ import { p as LEGACY_EMBEDDING_BINDING } from "./embedding-gate-6rZrWRXa.mjs";
9
+ import { i as deserializeEmbedding, r as cosineSimilarity } from "./embeddings-BVq3esBF.mjs";
10
+ import { n as routeNotification } from "./router-CQSc3lk3.mjs";
10
11
  import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
11
12
  import { basename, dirname, join, relative } from "node:path";
12
13
  import pg from "pg";
@@ -478,8 +479,8 @@ async function findTunnelsForSlugMap(db, slugMap, opts) {
478
479
  const totalOccurrences = rows.reduce((s, r) => s + Number(r.cnt), 0);
479
480
  if (totalOccurrences < opts.min_occurrences) continue;
480
481
  const projects = rows.map((r) => slugMap[r.project_id] ?? String(r.project_id)).filter(Boolean);
481
- const firstSeen = Math.min(...rows.map((r) => r.first_seen));
482
- const lastSeen = Math.max(...rows.map((r) => r.last_seen));
482
+ const firstSeen = minOf(rows.map((r) => r.first_seen));
483
+ const lastSeen = maxOf(rows.map((r) => r.last_seen));
483
484
  tunnels.push({
484
485
  concept: term,
485
486
  projects,
@@ -2547,8 +2548,9 @@ var SQLiteRegistryBackend = class {
2547
2548
  `;
2548
2549
  if (where.length) sql += " WHERE " + where.join(" AND ");
2549
2550
  sql += " ORDER BY s.date DESC, s.number DESC";
2550
- const limit = opts.limit ?? 20;
2551
- sql += ` LIMIT ${limit}`;
2551
+ const limit = Math.max(1, Math.trunc(Number(opts.limit ?? 20)) || 20);
2552
+ sql += " LIMIT ?";
2553
+ params.push(limit);
2552
2554
  return this.db.prepare(sql).all(...params);
2553
2555
  }
2554
2556
  async findSessionByFilename(projectId, filename) {
@@ -2913,4 +2915,4 @@ async function closeStorage() {
2913
2915
 
2914
2916
  //#endregion
2915
2917
  export { getStorageBackend as a, MergeError as c, getBackendOutage as d, humanDuration as f, getRegistryBackend as i, federationDbPath as l, createRegistryBackend as n, oldRegistryPath as o, createStorageBackend as r, registryDbPath as s, closeStorage as t, oldFederationPath as u };
2916
- //# sourceMappingURL=factory-BtjbQ0np.mjs.map
2918
+ //# sourceMappingURL=factory-DjAAsqr_.mjs.map
@@ -1,8 +1,8 @@
1
1
  import { l as claudeHomeDir, t as aibrokerHomeDir } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { F as writeWorkersSection, N as resolveModelCapability, P as resolveProviderKey, c as WorkersConfigError, j as readWorkersSection } from "./config-D5chi2LO.mjs";
3
- import { D as readJsonStrict, O as writeJsonAtomic } from "./config-BSXKyeJ1.mjs";
4
- import { u as workersLogDir } from "./paths-DoIyRnhx.mjs";
5
- import { _ as resolveTarget } from "./routing-CUyiglfu.mjs";
2
+ import { F as writeWorkersSection, N as resolveModelCapability, P as resolveProviderKey, c as WorkersConfigError, j as readWorkersSection } from "./config-XpZWOYOG.mjs";
3
+ import { D as readJsonStrict, O as writeJsonAtomic } from "./config-Bu1_vbY8.mjs";
4
+ import { u as workersLogDir } from "./paths-CHifubDh.mjs";
5
+ import { _ as resolveTarget } from "./routing-BAAST9vT.mjs";
6
6
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { execFileSync } from "node:child_process";
@@ -272,4 +272,4 @@ function fallbackStatusText(s) {
272
272
 
273
273
  //#endregion
274
274
  export { fallbackStatusText as i, fallbackOn as n, fallbackStatus as r, fallbackOff as t };
275
- //# sourceMappingURL=fallback-BIyEpPsQ.mjs.map
275
+ //# sourceMappingURL=fallback-DFlItw1Z.mjs.map
@@ -11,4 +11,4 @@ function fmtAge(ms, opts) {
11
11
 
12
12
  //#endregion
13
13
  export { fmtAge as t };
14
- //# sourceMappingURL=fmt-age-BiSHrTKN.mjs.map
14
+ //# sourceMappingURL=fmt-age-ByDkvkoU.mjs.map
@@ -1,7 +1,7 @@
1
1
  import { g as paiHomePath } from "./runtime-paths-WsO9sJFc.mjs";
2
- import { t as ANTHROPIC_NATIVE } from "./config-D5chi2LO.mjs";
3
- import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-CUyiglfu.mjs";
4
- import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-jOjTNlOL.mjs";
2
+ import { t as ANTHROPIC_NATIVE } from "./config-XpZWOYOG.mjs";
3
+ import { A as budgetSkipReason, D as budgetExceeded, E as budgetCache, M as budgetWall } from "./routing-BAAST9vT.mjs";
4
+ import { n as tierProviderNames, t as isLocalCandidate } from "./local-candidate-DLdwQt0s.mjs";
5
5
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
6
6
  import { Chalk } from "chalk";
7
7
 
@@ -311,4 +311,4 @@ function saveBudgetSnapshot(map, now = Date.now()) {
311
311
 
312
312
  //#endregion
313
313
  export { colorEnabled as a, renderCommandHelp as c, renderOverview as d, renderTable as f, saveBudgetSnapshot as i, renderMarkdown as l, usageLine as m, readBudget as n, makeStyle as o, suggest as p, readBudgetSnapshot as r, mdCommand as s, providerHealth as t, renderMore as u };
314
- //# sourceMappingURL=health-C6jZWvRG.mjs.map
314
+ //# sourceMappingURL=health-Dnym-qcT.mjs.map
@@ -1,4 +1,4 @@
1
- import { i as getRegistryBackend } from "./factory-BtjbQ0np.mjs";
1
+ import { i as getRegistryBackend } from "./factory-DjAAsqr_.mjs";
2
2
 
3
3
  //#region src/cli/impl/project/helpers.ts
4
4
  /**
@@ -66,4 +66,4 @@ async function upsertTag(tagName) {
66
66
 
67
67
  //#endregion
68
68
  export { getSessionCount as a, upsertTag as c, getProjectTags as i, getProject as n, requireProject as o, getProjectAliases as r, resolveIdentifier as s, getLastSessionDate as t };
69
- //# sourceMappingURL=helpers-DFmYRCOa.mjs.map
69
+ //# sourceMappingURL=helpers-BD6V-p9w.mjs.map
@@ -0,0 +1,10 @@
1
+ import "./config-Bu1_vbY8.mjs";
2
+ import "./helpers-CRuBUas3.mjs";
3
+ import "./factory-DjAAsqr_.mjs";
4
+ import "./postgres-C6ti4haA.mjs";
5
+ import "./embedding-gate-6rZrWRXa.mjs";
6
+ import "./embeddings-BVq3esBF.mjs";
7
+ import "./router-CQSc3lk3.mjs";
8
+ import { a as getSessionCount, c as upsertTag, i as getProjectTags, n as getProject, o as requireProject, r as getProjectAliases, s as resolveIdentifier, t as getLastSessionDate } from "./helpers-BD6V-p9w.mjs";
9
+
10
+ export { getProject };
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readdirSync } from "node:fs";
2
+ import { createHash } from "node:crypto";
2
3
  import { basename, join, normalize } from "node:path";
3
4
  import { homedir } from "node:os";
4
- import { createHash } from "node:crypto";
5
5
 
6
6
  //#region src/utils/hash.ts
7
7
  /**
@@ -94,8 +94,16 @@ function buildOutline(content) {
94
94
  const headings = parseHeadings(lines);
95
95
  const roots = [];
96
96
  const stack = [];
97
+ const nextSame = new Array(headings.length);
98
+ const pending = [];
99
+ for (let i = headings.length - 1; i >= 0; i--) {
100
+ const h = headings[i];
101
+ while (pending.length > 0 && pending[pending.length - 1].level > h.level) pending.pop();
102
+ nextSame[i] = pending[pending.length - 1];
103
+ pending.push(h);
104
+ }
97
105
  headings.forEach((h, i) => {
98
- const next = headings.slice(i + 1).find((n) => n.level <= h.level);
106
+ const next = nextSame[i];
99
107
  const endLine = Math.max(h.line, next ? next.line - 1 : last);
100
108
  const node = {
101
109
  level: h.level,
@@ -126,7 +134,7 @@ function splitBySections(lines) {
126
134
  const text = current.map((l) => l.text).join("\n");
127
135
  sections.push({
128
136
  lines: current,
129
- tokens: estimateTokens(text),
137
+ tokens: sizeOf(text),
130
138
  headingPath: currentPath
131
139
  });
132
140
  current = [];
@@ -151,7 +159,7 @@ function splitByParagraphs(block) {
151
159
  const text = current.map((l) => l.text).join("\n");
152
160
  paragraphs.push({
153
161
  lines: [...current],
154
- tokens: estimateTokens(text),
162
+ tokens: sizeOf(text),
155
163
  headingPath: block.headingPath
156
164
  });
157
165
  current = [];
@@ -160,47 +168,86 @@ function splitByParagraphs(block) {
160
168
  const text = current.map((l) => l.text).join("\n");
161
169
  paragraphs.push({
162
170
  lines: current,
163
- tokens: estimateTokens(text),
171
+ tokens: sizeOf(text),
164
172
  headingPath: block.headingPath
165
173
  });
166
174
  }
167
175
  return paragraphs.length > 0 ? paragraphs : [block];
168
176
  }
177
+ /** Characters per budget token above which a block counts as oversized (guards CJK / unbroken text). */
178
+ const MAX_CHARS_PER_TOKEN = 6;
179
+ /** Chunk size measure: word-based token estimate, floored by a character-length estimate. */
180
+ function sizeOf(text) {
181
+ return Math.max(estimateTokens(text), Math.ceil(text.length / MAX_CHARS_PER_TOKEN));
182
+ }
183
+ /** Split one over-budget line at sentence ends, then whitespace, then hard by characters. */
184
+ function splitLine(text, lineNo, budget) {
185
+ if (sizeOf(text) <= budget) return [{
186
+ text,
187
+ lineNo,
188
+ join: ""
189
+ }];
190
+ const maxChars = budget * MAX_CHARS_PER_TOKEN;
191
+ const pieces = [];
192
+ const push = (t, join) => {
193
+ if (t) pieces.push({
194
+ text: t,
195
+ lineNo,
196
+ join
197
+ });
198
+ };
199
+ let acc = "";
200
+ const emit = () => {
201
+ push(acc, pieces.length > 0 ? " " : "");
202
+ acc = "";
203
+ };
204
+ const addWord = (word) => {
205
+ if (acc && sizeOf(acc + " " + word) > budget) emit();
206
+ if (sizeOf(word) > budget) {
207
+ if (acc) emit();
208
+ for (let i = 0; i < word.length; i += maxChars) push(word.slice(i, i + maxChars), i === 0 ? pieces.length > 0 ? " " : "" : "");
209
+ return;
210
+ }
211
+ acc = acc ? acc + " " + word : word;
212
+ };
213
+ for (const sentence of text.split(/(?<=[.!?])\s+(?=[A-Z"'])/)) {
214
+ if (acc && sizeOf(acc + " " + sentence) > budget) emit();
215
+ if (sizeOf(sentence) <= budget) acc = acc ? acc + " " + sentence : sentence;
216
+ else for (const word of sentence.split(/\s+/).filter(Boolean)) addWord(word);
217
+ }
218
+ emit();
219
+ return pieces;
220
+ }
169
221
  /**
170
- * Split a LineBlock by sentence boundaries (. ! ?) when even paragraphs are
171
- * too large. Works character-by-character within joined lines.
222
+ * Split an oversized LineBlock at line boundaries; lines that are themselves
223
+ * too large are split by sentence, whitespace, then characters. Original line
224
+ * breaks and real line numbers are kept.
172
225
  */
173
- function splitBySentences(block, maxTokens) {
174
- const sentences = block.lines.map((l) => l.text).join(" ").split(/(?<=[.!?])\s+(?=[A-Z"'])/g);
226
+ function splitOversized(block, budget) {
175
227
  const result = [];
176
- let accText = "";
177
- const startLine = block.lines[0]?.lineNo ?? 1;
178
- const endLine = block.lines[block.lines.length - 1]?.lineNo ?? startLine;
179
- const totalLines = endLine - startLine + 1;
180
- const linesPerSentence = Math.max(1, Math.floor(totalLines / Math.max(1, sentences.length)));
181
- let sentenceIdx = 0;
182
- let approxLine = startLine;
228
+ let cur = [];
229
+ let curText = "";
183
230
  const flush = () => {
184
- if (!accText.trim()) return;
185
- const endApprox = Math.min(approxLine + linesPerSentence - 1, endLine);
186
- result.push({
187
- lines: [{
188
- text: accText.trim(),
189
- lineNo: approxLine
190
- }],
191
- tokens: estimateTokens(accText),
231
+ if (cur.length > 0) result.push({
232
+ lines: cur,
233
+ tokens: sizeOf(curText),
192
234
  headingPath: block.headingPath
193
235
  });
194
- approxLine = endApprox + 1;
195
- accText = "";
236
+ cur = [];
237
+ curText = "";
238
+ };
239
+ const add = (piece, sep) => {
240
+ const last = cur[cur.length - 1];
241
+ if (last && piece.join && last.lineNo === piece.lineNo) last.text += piece.join + piece.text;
242
+ else cur.push({
243
+ text: piece.text,
244
+ lineNo: piece.lineNo
245
+ });
246
+ curText += (curText ? sep : "") + piece.text;
196
247
  };
197
- for (const sentence of sentences) {
198
- sentenceIdx++;
199
- const candidateText = accText ? accText + " " + sentence : sentence;
200
- if (estimateTokens(candidateText) > maxTokens && accText) {
201
- flush();
202
- accText = sentence;
203
- } else accText = candidateText;
248
+ for (const line of block.lines) for (const piece of splitLine(line.text, line.lineNo, budget)) {
249
+ if (cur.length > 0 && sizeOf(curText + "\n" + piece.text) > budget) flush();
250
+ add(piece, piece.join ? piece.join : "\n");
204
251
  }
205
252
  flush();
206
253
  return result.length > 0 ? result : [block];
@@ -239,9 +286,12 @@ function buildOverlapPrefix(lastChunk, overlapTokens) {
239
286
  /**
240
287
  * Strip `<private>...</private>` blocks from content before indexing.
241
288
  * Content within these tags is excluded from memory — never stored or searched.
289
+ * Newlines are preserved so line numbers still match the file; an unclosed
290
+ * tag hides everything after it.
242
291
  */
243
292
  function stripPrivateTags(content) {
244
- return content.replace(/<private>[\s\S]*?<\/private>/gi, "");
293
+ const blank = (m) => m.replace(/[^\n]/g, "");
294
+ return content.replace(/<private>[\s\S]*?<\/private>/gi, blank).replace(/<private>[\s\S]*$/i, blank);
245
295
  }
246
296
  /** One breadcrumb line ("[A > B]\n") for a non-empty heading path, else "". */
247
297
  function breadcrumb(path) {
@@ -258,7 +308,7 @@ function chunkMarkdown(content, opts) {
258
308
  })));
259
309
  const finalBlocks = [];
260
310
  for (const section of sections) {
261
- const budget = Math.max(1, maxTokens - estimateTokens(breadcrumb(section.headingPath)));
311
+ const budget = Math.max(1, maxTokens - sizeOf(breadcrumb(section.headingPath)));
262
312
  if (section.tokens <= budget) {
263
313
  finalBlocks.push(section);
264
314
  continue;
@@ -269,8 +319,7 @@ function chunkMarkdown(content, opts) {
269
319
  finalBlocks.push(para);
270
320
  continue;
271
321
  }
272
- const sentences = splitBySentences(para, budget);
273
- finalBlocks.push(...sentences);
322
+ finalBlocks.push(...splitOversized(para, budget));
274
323
  }
275
324
  }
276
325
  const chunks = [];
@@ -516,4 +565,4 @@ const INDEX_YIELD_EVERY = 10;
516
565
 
517
566
  //#endregion
518
567
  export { parseSessionTitleChunk as a, yieldToEventLoop as c, estimateTokens as d, fileContentHash as f, sha256File as h, isPathTooBroadForContentScan as i, buildOutline as l, sha256 as m, chunkId as n, walkContentFiles as o, parseHeadings as p, detectTier as r, walkMdFiles as s, INDEX_YIELD_EVERY as t, chunkMarkdown as u };
519
- //# sourceMappingURL=helpers-CJpb_MNB.mjs.map
568
+ //# sourceMappingURL=helpers-CRuBUas3.mjs.map
@@ -7365,7 +7365,7 @@ var require_dist = __commonJS({
7365
7365
  });
7366
7366
 
7367
7367
  // src/hooks/ts/capture-all-events.ts
7368
- import { readFileSync as readFileSync4, appendFileSync, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
7368
+ import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
7369
7369
  import { join as join6 } from "path";
7370
7370
 
7371
7371
  // src/hooks/ts/lib/pai-paths.ts
@@ -7718,6 +7718,51 @@ var CONFIG_DIR = dirname3(CONFIG_FILE);
7718
7718
  // src/session/checkpoint-block.ts
7719
7719
  var AUTHORED_GRACE_MS = 60 * 60 * 1e3;
7720
7720
 
7721
+ // src/hooks/ts/lib/raw-capture.ts
7722
+ import { appendFileSync, chmodSync as chmodSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, renameSync as renameSync3, statSync as statSync2 } from "fs";
7723
+
7724
+ // src/workers/ps-table.ts
7725
+ var PS_MAX_BUFFER = 64 * 1024 * 1024;
7726
+
7727
+ // src/hooks/ts/lib/loop-guard.ts
7728
+ var LOOP_MAX_REPEATS = 4;
7729
+ var LOOP_READ_STREAK = Number(process.env.PAI_LOOP_READ_STREAK) > 0 ? Number(process.env.PAI_LOOP_READ_STREAK) : 15;
7730
+ var SELF = "If your part is done, commit and write your R report. If you are stuck, report the blocker in R x= (or use pai worker help if available).";
7731
+ var LOOP_DENY_REASON = `loop guard: you ran this command ${LOOP_MAX_REPEATS} times with the same result and nothing changed. Stop repeating it. ${SELF}`;
7732
+ var WAIT_CAP_MS = 45 * 6e4;
7733
+ var FP_FULL_READ_BYTES = 256 * 1024;
7734
+ var FP_TOTAL_BYTES = 4 * 1024 * 1024;
7735
+ var SECRET_LINE = /pass(?:word|wd)|secret|token|api[_-]?key|credential|authorization|bearer|private key|^\s*(?:export\s+)?[A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASS\w*|CRED\w*|AUTH\w*)\s*=|\b(?:sk|ghp|gho|xox[bap])[-_][A-Za-z0-9_-]{16,}|\b[A-Za-z0-9+]{40,}={0,2}(?![\w/])|\b[a-z][a-z0-9+.-]*:\/\/[^\s\/:@]+:[^\s\/@]+@|\b(?:AKIA|ASIA|AGPA|AIDA|AROA)[A-Z0-9]{16}\b|\bgh[pousr]_[A-Za-z0-9]{20,}/i;
7736
+ var MASKED_LINE = "[line masked: looks like a secret]";
7737
+ function maskSecrets(text) {
7738
+ return text.split("\n").map((l) => SECRET_LINE.test(l) ? MASKED_LINE : l).join("\n");
7739
+ }
7740
+
7741
+ // src/hooks/ts/lib/raw-capture.ts
7742
+ var RAW_CAPTURE_MAX_BYTES = 10 * 1024 * 1024;
7743
+ function redactDeep(value) {
7744
+ if (typeof value === "string") return maskSecrets(value);
7745
+ if (Array.isArray(value)) return value.map(redactDeep);
7746
+ if (value && typeof value === "object") {
7747
+ const out = {};
7748
+ for (const [k, v] of Object.entries(value)) out[k] = redactDeep(v);
7749
+ return out;
7750
+ }
7751
+ return value;
7752
+ }
7753
+ function appendRawCapture(file, dir, line, maxBytes = RAW_CAPTURE_MAX_BYTES) {
7754
+ if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true, mode: 448 });
7755
+ try {
7756
+ if (statSync2(file).size + Buffer.byteLength(line) > maxBytes) renameSync3(file, `${file}.1`);
7757
+ } catch {
7758
+ }
7759
+ appendFileSync(file, line, { mode: 384 });
7760
+ try {
7761
+ chmodSync2(file, 384);
7762
+ } catch {
7763
+ }
7764
+ }
7765
+
7721
7766
  // src/hooks/ts/capture-all-events.ts
7722
7767
  function getLocalTimestamp() {
7723
7768
  const date = /* @__PURE__ */ new Date();
@@ -7739,10 +7784,7 @@ function getEventsFilePath() {
7739
7784
  const month = String(localDate.getMonth() + 1).padStart(2, "0");
7740
7785
  const day = String(localDate.getDate()).padStart(2, "0");
7741
7786
  const monthDir = join6(historyDir(), "raw-outputs", `${year}-${month}`);
7742
- if (!existsSync4(monthDir)) {
7743
- mkdirSync3(monthDir, { recursive: true });
7744
- }
7745
- return join6(monthDir, `${year}-${month}-${day}_all-events.jsonl`);
7787
+ return { dir: monthDir, file: join6(monthDir, `${year}-${month}-${day}_all-events.jsonl`) };
7746
7788
  }
7747
7789
  function getSessionMappingFile() {
7748
7790
  return agentSessionsPath();
@@ -7750,7 +7792,7 @@ function getSessionMappingFile() {
7750
7792
  function getAgentForSession(sessionId) {
7751
7793
  try {
7752
7794
  const mappingFile = getSessionMappingFile();
7753
- if (existsSync4(mappingFile)) {
7795
+ if (existsSync5(mappingFile)) {
7754
7796
  const mappings = JSON.parse(readFileSync4(mappingFile, "utf-8"));
7755
7797
  return mappings[sessionId] || "pai";
7756
7798
  }
@@ -7762,7 +7804,7 @@ function setAgentForSession(sessionId, agentName) {
7762
7804
  try {
7763
7805
  const mappingFile = getSessionMappingFile();
7764
7806
  let mappings = {};
7765
- if (existsSync4(mappingFile)) {
7807
+ if (existsSync5(mappingFile)) {
7766
7808
  try {
7767
7809
  mappings = JSON.parse(readFileSync4(mappingFile, "utf-8"));
7768
7810
  } catch {
@@ -7817,7 +7859,7 @@ async function main() {
7817
7859
  source_app: agentName,
7818
7860
  session_id: hookData.session_id || "main",
7819
7861
  hook_event_type: eventType,
7820
- payload: hookData,
7862
+ payload: redactDeep(hookData),
7821
7863
  timestamp: Date.now(),
7822
7864
  timestamp_local: getLocalTimestamp()
7823
7865
  };
@@ -7828,9 +7870,8 @@ async function main() {
7828
7870
  hookData.description
7829
7871
  );
7830
7872
  }
7831
- const eventsFile = getEventsFilePath();
7832
- const jsonLine = JSON.stringify(event) + "\n";
7833
- appendFileSync(eventsFile, jsonLine, "utf-8");
7873
+ const events = getEventsFilePath();
7874
+ appendRawCapture(events.file, events.dir, JSON.stringify(event) + "\n");
7834
7875
  } catch (error) {
7835
7876
  console.error("Event capture error:", error);
7836
7877
  }
@@ -47,8 +47,9 @@ function paiSocketPath() {
47
47
  }
48
48
 
49
49
  // src/hooks/ts/session-end/capture-session-summary.ts
50
- import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
51
- import { join as join3 } from "path";
50
+ import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4, readFileSync as readFileSync4, unlinkSync } from "fs";
51
+ import { tmpdir } from "os";
52
+ import { join as join4 } from "path";
52
53
 
53
54
  // src/hooks/ts/lib/pai-paths.ts
54
55
  import { homedir as homedir2 } from "os";
@@ -139,47 +140,10 @@ function historyDir() {
139
140
  return resolvePaiFile(paiHomePath("History"), [oldHistoryDir()], "pai config migrate --history");
140
141
  }
141
142
 
142
- // src/hooks/ts/session-end/capture-session-summary.ts
143
- async function main() {
144
- try {
145
- const chunks = [];
146
- for await (const chunk of process.stdin) {
147
- chunks.push(chunk);
148
- }
149
- const input = Buffer.concat(chunks).toString("utf-8");
150
- if (!input || input.trim() === "") {
151
- process.exit(0);
152
- }
153
- if (!process.env.__PAI_HOOK_BG) {
154
- const { spawn } = await import("child_process");
155
- const child = spawn(process.execPath, [process.argv[1], "--background"], {
156
- detached: true,
157
- stdio: "ignore",
158
- env: { ...process.env, __PAI_HOOK_BG: "1", __PAI_HOOK_INPUT: input }
159
- });
160
- child.unref();
161
- process.exit(0);
162
- }
163
- const data = JSON.parse(process.env.__PAI_HOOK_INPUT || input);
164
- const now = /* @__PURE__ */ new Date();
165
- const timestamp = now.toISOString().replace(/:/g, "").replace(/\..+/, "").replace("T", "-");
166
- const yearMonth = timestamp.substring(0, 7);
167
- const sessionInfo = await analyzeSession(data.conversation_id, yearMonth);
168
- const filename = `${timestamp}_SESSION_${sessionInfo.focus}.md`;
169
- const sessionDir = join3(historyDir(), "sessions", yearMonth);
170
- if (!existsSync3(sessionDir)) {
171
- mkdirSync2(sessionDir, { recursive: true });
172
- }
173
- const sessionDoc = formatSessionDocument(timestamp, data, sessionInfo);
174
- writeFileSync(join3(sessionDir, filename), sessionDoc);
175
- await storeStructuredSummary(data.conversation_id, sessionInfo);
176
- process.exit(0);
177
- } catch (error) {
178
- console.error(`[UOCS] SessionEnd hook error: ${error}`);
179
- process.exit(0);
180
- }
181
- }
182
- async function analyzeSession(conversationId, yearMonth) {
143
+ // src/hooks/ts/lib/session-summary.ts
144
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
145
+ import { join as join3 } from "path";
146
+ async function analyzeSession(sessionId, yearMonth) {
183
147
  const rawOutputsDir = join3(historyDir(), "raw-outputs", yearMonth);
184
148
  let filesChanged = [];
185
149
  let commandsExecuted = [];
@@ -197,7 +161,7 @@ async function analyzeSession(conversationId, yearMonth) {
197
161
  for (const line of lines) {
198
162
  try {
199
163
  const entry = JSON.parse(line);
200
- if (entry.session === conversationId) {
164
+ if (entry.session === sessionId) {
201
165
  toolsUsed.add(entry.tool);
202
166
  if (entry.tool === "Edit" || entry.tool === "Write") {
203
167
  if (entry.input?.file_path) {
@@ -228,12 +192,13 @@ async function analyzeSession(conversationId, yearMonth) {
228
192
  }
229
193
  function formatSessionDocument(timestamp, data, info) {
230
194
  const date = timestamp.substring(0, 10);
231
- const time = timestamp.substring(11).replace(/-/g, ":");
195
+ const hhmmss = timestamp.substring(11);
196
+ const time = `${hhmmss.slice(0, 2)}:${hhmmss.slice(2, 4)}:${hhmmss.slice(4, 6)}`;
232
197
  const da = process.env.DA || "PAI";
233
198
  return `---
234
199
  capture_type: SESSION
235
200
  timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
236
- session_id: ${data.conversation_id}
201
+ session_id: ${data.session_id}
237
202
  duration_minutes: ${info.duration}
238
203
  executor: ${da}
239
204
  ---
@@ -242,7 +207,7 @@ executor: ${da}
242
207
 
243
208
  **Date:** ${date}
244
209
  **Time:** ${time}
245
- **Session ID:** ${data.conversation_id}
210
+ **Session ID:** ${data.session_id}
246
211
 
247
212
  ---
248
213
 
@@ -285,6 +250,58 @@ For detailed tool outputs, see: \`${join3(historyDir(), "raw-outputs", timestamp
285
250
  **Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
286
251
  `;
287
252
  }
253
+
254
+ // src/hooks/ts/session-end/capture-session-summary.ts
255
+ async function main() {
256
+ try {
257
+ const chunks = [];
258
+ for await (const chunk of process.stdin) {
259
+ chunks.push(chunk);
260
+ }
261
+ const input = Buffer.concat(chunks).toString("utf-8");
262
+ if ((!input || input.trim() === "") && !process.env.__PAI_HOOK_INPUT_FILE) {
263
+ process.exit(0);
264
+ }
265
+ if (!process.env.__PAI_HOOK_BG) {
266
+ const { spawn } = await import("child_process");
267
+ const inputFile2 = join4(tmpdir(), `pai-session-end-${process.pid}-${Date.now()}.json`);
268
+ writeFileSync(inputFile2, input, { mode: 384 });
269
+ const child = spawn(process.execPath, [process.argv[1], "--background"], {
270
+ detached: true,
271
+ stdio: "ignore",
272
+ env: { ...process.env, __PAI_HOOK_BG: "1", __PAI_HOOK_INPUT_FILE: inputFile2 }
273
+ });
274
+ child.unref();
275
+ process.exit(0);
276
+ }
277
+ const inputFile = process.env.__PAI_HOOK_INPUT_FILE;
278
+ let raw = input;
279
+ if (inputFile) {
280
+ raw = readFileSync4(inputFile, "utf-8");
281
+ try {
282
+ unlinkSync(inputFile);
283
+ } catch {
284
+ }
285
+ }
286
+ const data = JSON.parse(raw);
287
+ const now = /* @__PURE__ */ new Date();
288
+ const timestamp = now.toISOString().replace(/:/g, "").replace(/\..+/, "").replace("T", "-");
289
+ const yearMonth = timestamp.substring(0, 7);
290
+ const sessionInfo = await analyzeSession(data.session_id, yearMonth);
291
+ const filename = `${timestamp}_SESSION_${sessionInfo.focus}.md`;
292
+ const sessionDir = join4(historyDir(), "sessions", yearMonth);
293
+ if (!existsSync4(sessionDir)) {
294
+ mkdirSync2(sessionDir, { recursive: true });
295
+ }
296
+ const sessionDoc = formatSessionDocument(timestamp, data, sessionInfo);
297
+ writeFileSync(join4(sessionDir, filename), sessionDoc);
298
+ await storeStructuredSummary(data.session_id, sessionInfo);
299
+ process.exit(0);
300
+ } catch (error) {
301
+ console.error(`[UOCS] SessionEnd hook error: ${error}`);
302
+ process.exit(0);
303
+ }
304
+ }
288
305
  async function storeStructuredSummary(sessionId, info) {
289
306
  try {
290
307
  const cwd = process.cwd();