@theokit/sdk 4.32.0 → 4.33.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 (102) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/{agent-ES57DGAT.cjs → agent-2STDGKNI.cjs} +11 -10
  3. package/dist/{agent-ES57DGAT.cjs.map → agent-2STDGKNI.cjs.map} +1 -1
  4. package/dist/{agent-FBFPSBTX.js → agent-LFSVFAUI.js} +10 -9
  5. package/dist/{agent-FBFPSBTX.js.map → agent-LFSVFAUI.js.map} +1 -1
  6. package/dist/{chunk-DQSILMWU.cjs → chunk-2DYI7MIF.cjs} +2 -2
  7. package/dist/chunk-2DYI7MIF.cjs.map +1 -0
  8. package/dist/{chunk-OIYQBUW4.cjs → chunk-6M6A6WWW.cjs} +2 -2
  9. package/dist/{chunk-OIYQBUW4.cjs.map → chunk-6M6A6WWW.cjs.map} +1 -1
  10. package/dist/chunk-B3HUL2OQ.js +78 -0
  11. package/dist/chunk-B3HUL2OQ.js.map +1 -0
  12. package/dist/{chunk-I4QDIQ45.cjs → chunk-E33A7YIR.cjs} +28 -6
  13. package/dist/chunk-E33A7YIR.cjs.map +1 -0
  14. package/dist/chunk-FER7UMIC.cjs +75 -0
  15. package/dist/chunk-FER7UMIC.cjs.map +1 -0
  16. package/dist/{chunk-CVSNCUBU.cjs → chunk-FTRNHGAD.cjs} +11 -11
  17. package/dist/{chunk-CVSNCUBU.cjs.map → chunk-FTRNHGAD.cjs.map} +1 -1
  18. package/dist/{chunk-EVADEGM7.cjs → chunk-FX2DAR5R.cjs} +7 -5
  19. package/dist/chunk-FX2DAR5R.cjs.map +1 -0
  20. package/dist/{chunk-WA3FO4R7.js → chunk-GBCQ5742.js} +7 -6
  21. package/dist/chunk-GBCQ5742.js.map +1 -0
  22. package/dist/{chunk-JPG7NMNH.cjs → chunk-HTO4C32Q.cjs} +54 -48
  23. package/dist/chunk-HTO4C32Q.cjs.map +1 -0
  24. package/dist/{chunk-SQVQH4EK.js → chunk-KT6SWUY6.js} +4 -4
  25. package/dist/{chunk-SQVQH4EK.js.map → chunk-KT6SWUY6.js.map} +1 -1
  26. package/dist/chunk-LDDUIXWS.cjs +81 -0
  27. package/dist/chunk-LDDUIXWS.cjs.map +1 -0
  28. package/dist/chunk-OMDRRHRF.js +73 -0
  29. package/dist/chunk-OMDRRHRF.js.map +1 -0
  30. package/dist/{chunk-OQX2DG36.cjs → chunk-PZ4Q5K7O.cjs} +5 -5
  31. package/dist/{chunk-OQX2DG36.cjs.map → chunk-PZ4Q5K7O.cjs.map} +1 -1
  32. package/dist/{chunk-WPBTPSKQ.js → chunk-Q56IYTBZ.js} +2 -2
  33. package/dist/chunk-Q56IYTBZ.js.map +1 -0
  34. package/dist/{chunk-334WDVFZ.js → chunk-SDYJESYZ.js} +27 -6
  35. package/dist/chunk-SDYJESYZ.js.map +1 -0
  36. package/dist/{chunk-NI4PAMJQ.js → chunk-TKE4RTYA.js} +18 -12
  37. package/dist/chunk-TKE4RTYA.js.map +1 -0
  38. package/dist/{chunk-HBTEK5LK.js → chunk-WBGCDZDV.js} +3 -3
  39. package/dist/{chunk-HBTEK5LK.js.map → chunk-WBGCDZDV.js.map} +1 -1
  40. package/dist/{chunk-DGRZNMTU.js → chunk-XV2RBL6D.js} +2 -2
  41. package/dist/{chunk-DGRZNMTU.js.map → chunk-XV2RBL6D.js.map} +1 -1
  42. package/dist/{compact-session-OKXLKJIS.cjs → compact-session-24PK2G4M.cjs} +13 -13
  43. package/dist/{compact-session-OKXLKJIS.cjs.map → compact-session-24PK2G4M.cjs.map} +1 -1
  44. package/dist/{compact-session-UHGMWPMW.js → compact-session-J7VNCICZ.js} +5 -5
  45. package/dist/{compact-session-UHGMWPMW.js.map → compact-session-J7VNCICZ.js.map} +1 -1
  46. package/dist/compaction.cjs +17 -13
  47. package/dist/compaction.d.cts +8 -0
  48. package/dist/compaction.d.ts +8 -0
  49. package/dist/compaction.js +1 -1
  50. package/dist/cron.cjs +10 -9
  51. package/dist/cron.js +9 -8
  52. package/dist/eval.cjs +9 -8
  53. package/dist/eval.cjs.map +1 -1
  54. package/dist/eval.js +8 -7
  55. package/dist/eval.js.map +1 -1
  56. package/dist/fs-session-store-MZ4GD2C6.js +10 -0
  57. package/dist/{fs-session-store-2J5JOXC2.js.map → fs-session-store-MZ4GD2C6.js.map} +1 -1
  58. package/dist/fs-session-store-YMBSJ5Y7.cjs +19 -0
  59. package/dist/{fs-session-store-DNAJ6RQG.cjs.map → fs-session-store-YMBSJ5Y7.cjs.map} +1 -1
  60. package/dist/index.cjs +35 -35
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.js +15 -15
  63. package/dist/index.js.map +1 -1
  64. package/dist/{inject-session-24FJ65LP.cjs → inject-session-343WIDCM.cjs} +6 -6
  65. package/dist/{inject-session-24FJ65LP.cjs.map → inject-session-343WIDCM.cjs.map} +1 -1
  66. package/dist/{inject-session-JXQO33T3.js → inject-session-RZEDWO53.js} +4 -4
  67. package/dist/{inject-session-JXQO33T3.js.map → inject-session-RZEDWO53.js.map} +1 -1
  68. package/dist/internal/local-agent/local-agent.d.ts +93 -1
  69. package/dist/internal/persistence/fs-session-store.d.cts +16 -0
  70. package/dist/internal/persistence/fs-session-store.d.ts +16 -0
  71. package/dist/internal/persistence/index.cjs +7 -7
  72. package/dist/internal/persistence/index.js +1 -1
  73. package/dist/internal/persistence/paths.d.cts +11 -1
  74. package/dist/internal/persistence/paths.d.ts +11 -1
  75. package/dist/internal/persistence/session-writer.d.cts +8 -0
  76. package/dist/internal/persistence/session-writer.d.ts +8 -0
  77. package/dist/internal/session/agent-session.d.ts +22 -0
  78. package/dist/persistence.cjs +13 -39
  79. package/dist/persistence.cjs.map +1 -1
  80. package/dist/persistence.d.cts +1 -1
  81. package/dist/persistence.d.ts +1 -1
  82. package/dist/persistence.js +4 -36
  83. package/dist/persistence.js.map +1 -1
  84. package/dist/{session-transcript-QS6CXHHD.js → session-transcript-F2ANTA2M.js} +3 -3
  85. package/dist/{session-transcript-QS6CXHHD.js.map → session-transcript-F2ANTA2M.js.map} +1 -1
  86. package/dist/{session-transcript-F5OXXO6G.cjs → session-transcript-JXFMPLOT.cjs} +13 -13
  87. package/dist/{session-transcript-F5OXXO6G.cjs.map → session-transcript-JXFMPLOT.cjs.map} +1 -1
  88. package/package.json +1 -1
  89. package/dist/chunk-334WDVFZ.js.map +0 -1
  90. package/dist/chunk-DQSILMWU.cjs.map +0 -1
  91. package/dist/chunk-EIPSZ4GA.js +0 -28
  92. package/dist/chunk-EIPSZ4GA.js.map +0 -1
  93. package/dist/chunk-EVADEGM7.cjs.map +0 -1
  94. package/dist/chunk-HCXDDR6P.cjs +0 -30
  95. package/dist/chunk-HCXDDR6P.cjs.map +0 -1
  96. package/dist/chunk-I4QDIQ45.cjs.map +0 -1
  97. package/dist/chunk-JPG7NMNH.cjs.map +0 -1
  98. package/dist/chunk-NI4PAMJQ.js.map +0 -1
  99. package/dist/chunk-WA3FO4R7.js.map +0 -1
  100. package/dist/chunk-WPBTPSKQ.js.map +0 -1
  101. package/dist/fs-session-store-2J5JOXC2.js +0 -8
  102. package/dist/fs-session-store-DNAJ6RQG.cjs +0 -17
@@ -1,16 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var chunkI4QDIQ45_cjs = require('./chunk-I4QDIQ45.cjs');
3
+ var chunkE33A7YIR_cjs = require('./chunk-E33A7YIR.cjs');
4
4
  var chunkIT3PWBW4_cjs = require('./chunk-IT3PWBW4.cjs');
5
- var chunkDQSILMWU_cjs = require('./chunk-DQSILMWU.cjs');
5
+ var chunk2DYI7MIF_cjs = require('./chunk-2DYI7MIF.cjs');
6
6
  require('./chunk-TIZ4TS5V.cjs');
7
7
  require('./chunk-BEURCLDV.cjs');
8
8
 
9
9
  // src/internal/session/inject-session.ts
10
10
  async function injectSessionTurn(opts) {
11
- await chunkI4QDIQ45_cjs.enqueueSessionWrite(opts.loc.cwd, opts.loc.agentId, async () => {
11
+ await chunkE33A7YIR_cjs.enqueueSessionWrite(opts.loc.cwd, opts.loc.agentId, async () => {
12
12
  const prior = await opts.store.readRecords(opts.loc.agentId);
13
- const transcript = chunkDQSILMWU_cjs.SessionTranscript.fromRecords(prior, {
13
+ const transcript = chunk2DYI7MIF_cjs.SessionTranscript.fromRecords(prior, {
14
14
  cwd: opts.loc.cwd,
15
15
  sessionId: opts.sessionId,
16
16
  model: opts.loc.model ?? "unknown"
@@ -23,5 +23,5 @@ async function injectSessionTurn(opts) {
23
23
  }
24
24
 
25
25
  exports.injectSessionTurn = injectSessionTurn;
26
- //# sourceMappingURL=inject-session-24FJ65LP.cjs.map
27
- //# sourceMappingURL=inject-session-24FJ65LP.cjs.map
26
+ //# sourceMappingURL=inject-session-343WIDCM.cjs.map
27
+ //# sourceMappingURL=inject-session-343WIDCM.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/session/inject-session.ts"],"names":["enqueueSessionWrite","SessionTranscript","invalidateSessionCache"],"mappings":";;;;;;;;;AAWA,eAAsB,kBAAkB,IAAA,EAMtB;AAChB,EAAA,MAAMA,sCAAoB,IAAA,CAAK,GAAA,CAAI,KAAK,IAAA,CAAK,GAAA,CAAI,SAAS,YAAY;AACpE,IAAA,MAAM,QAAQ,MAAM,IAAA,CAAK,MAAM,WAAA,CAAY,IAAA,CAAK,IAAI,OAAO,CAAA;AAC3D,IAAA,MAAM,UAAA,GAAaC,mCAAA,CAAkB,WAAA,CAAY,KAAA,EAAO;AAAA,MACtD,GAAA,EAAK,KAAK,GAAA,CAAI,GAAA;AAAA,MACd,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,KAAA,IAAS;AAAA,KAC1B,CAAA;AACD,IAAA,UAAA,CAAW,cAAA,CAAe,KAAK,QAAQ,CAAA;AACvC,IAAA,UAAA,CAAW,mBAAA,CAAoB,EAAE,IAAA,EAAM,IAAA,CAAK,eAAe,CAAA;AAC3D,IAAA,MAAM,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,UAAA,CAAW,OAAA,EAAQ,CAAE,KAAA,CAAM,KAAA,CAAM,MAAM,CAAC,CAAA;AACzF,IAAAC,wCAAA,CAAuB,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,EACvD,CAAC,CAAA;AACH","file":"inject-session-24FJ65LP.cjs","sourcesContent":["/**\n * M51 (agent-builder) — inject a SYNTHETIC user+assistant pair into a session's persisted transcript\n * WITHOUT running an LLM turn. This is the Codex review-exit mechanism (`exit_success.xml` appended to\n * the parent thread history so follow-ups like \"fix finding 2\" work): the pair chains onto the DAG\n * leaf (M50 pattern) and the in-memory cache is invalidated so the NEXT send re-hydrates with it.\n */\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { SessionTranscript } from \"../persistence/session-transcript.js\";\nimport { enqueueSessionWrite, invalidateSessionCache } from \"./agent-session.js\";\nimport type { CompactLocation } from \"./compact-session.js\";\n\nexport async function injectSessionTurn(opts: {\n store: SessionStore;\n loc: CompactLocation;\n sessionId: string;\n userText: string;\n assistantText: string;\n}): Promise<void> {\n await enqueueSessionWrite(opts.loc.cwd, opts.loc.agentId, async () => {\n const prior = await opts.store.readRecords(opts.loc.agentId);\n const transcript = SessionTranscript.fromRecords(prior, {\n cwd: opts.loc.cwd,\n sessionId: opts.sessionId,\n model: opts.loc.model ?? \"unknown\",\n });\n transcript.appendUserTurn(opts.userText);\n transcript.appendAssistantTurn({ text: opts.assistantText });\n await opts.store.appendRecords(opts.loc.agentId, transcript.records().slice(prior.length));\n invalidateSessionCache(opts.loc.cwd, opts.loc.agentId);\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/session/inject-session.ts"],"names":["enqueueSessionWrite","SessionTranscript","invalidateSessionCache"],"mappings":";;;;;;;;;AAWA,eAAsB,kBAAkB,IAAA,EAMtB;AAChB,EAAA,MAAMA,sCAAoB,IAAA,CAAK,GAAA,CAAI,KAAK,IAAA,CAAK,GAAA,CAAI,SAAS,YAAY;AACpE,IAAA,MAAM,QAAQ,MAAM,IAAA,CAAK,MAAM,WAAA,CAAY,IAAA,CAAK,IAAI,OAAO,CAAA;AAC3D,IAAA,MAAM,UAAA,GAAaC,mCAAA,CAAkB,WAAA,CAAY,KAAA,EAAO;AAAA,MACtD,GAAA,EAAK,KAAK,GAAA,CAAI,GAAA;AAAA,MACd,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,KAAA,IAAS;AAAA,KAC1B,CAAA;AACD,IAAA,UAAA,CAAW,cAAA,CAAe,KAAK,QAAQ,CAAA;AACvC,IAAA,UAAA,CAAW,mBAAA,CAAoB,EAAE,IAAA,EAAM,IAAA,CAAK,eAAe,CAAA;AAC3D,IAAA,MAAM,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,UAAA,CAAW,OAAA,EAAQ,CAAE,KAAA,CAAM,KAAA,CAAM,MAAM,CAAC,CAAA;AACzF,IAAAC,wCAAA,CAAuB,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,EACvD,CAAC,CAAA;AACH","file":"inject-session-343WIDCM.cjs","sourcesContent":["/**\n * M51 (agent-builder) — inject a SYNTHETIC user+assistant pair into a session's persisted transcript\n * WITHOUT running an LLM turn. This is the Codex review-exit mechanism (`exit_success.xml` appended to\n * the parent thread history so follow-ups like \"fix finding 2\" work): the pair chains onto the DAG\n * leaf (M50 pattern) and the in-memory cache is invalidated so the NEXT send re-hydrates with it.\n */\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { SessionTranscript } from \"../persistence/session-transcript.js\";\nimport { enqueueSessionWrite, invalidateSessionCache } from \"./agent-session.js\";\nimport type { CompactLocation } from \"./compact-session.js\";\n\nexport async function injectSessionTurn(opts: {\n store: SessionStore;\n loc: CompactLocation;\n sessionId: string;\n userText: string;\n assistantText: string;\n}): Promise<void> {\n await enqueueSessionWrite(opts.loc.cwd, opts.loc.agentId, async () => {\n const prior = await opts.store.readRecords(opts.loc.agentId);\n const transcript = SessionTranscript.fromRecords(prior, {\n cwd: opts.loc.cwd,\n sessionId: opts.sessionId,\n model: opts.loc.model ?? \"unknown\",\n });\n transcript.appendUserTurn(opts.userText);\n transcript.appendAssistantTurn({ text: opts.assistantText });\n await opts.store.appendRecords(opts.loc.agentId, transcript.records().slice(prior.length));\n invalidateSessionCache(opts.loc.cwd, opts.loc.agentId);\n });\n}\n"]}
@@ -1,6 +1,6 @@
1
- import { enqueueSessionWrite } from './chunk-334WDVFZ.js';
1
+ import { enqueueSessionWrite } from './chunk-SDYJESYZ.js';
2
2
  import { invalidateSessionCache } from './chunk-EEZ7CFMM.js';
3
- import { SessionTranscript } from './chunk-WPBTPSKQ.js';
3
+ import { SessionTranscript } from './chunk-Q56IYTBZ.js';
4
4
  import './chunk-S2BBRVQX.js';
5
5
  import './chunk-JTFYYKDT.js';
6
6
 
@@ -21,5 +21,5 @@ async function injectSessionTurn(opts) {
21
21
  }
22
22
 
23
23
  export { injectSessionTurn };
24
- //# sourceMappingURL=inject-session-JXQO33T3.js.map
25
- //# sourceMappingURL=inject-session-JXQO33T3.js.map
24
+ //# sourceMappingURL=inject-session-RZEDWO53.js.map
25
+ //# sourceMappingURL=inject-session-RZEDWO53.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/session/inject-session.ts"],"names":[],"mappings":";;;;;;;AAWA,eAAsB,kBAAkB,IAAA,EAMtB;AAChB,EAAA,MAAM,oBAAoB,IAAA,CAAK,GAAA,CAAI,KAAK,IAAA,CAAK,GAAA,CAAI,SAAS,YAAY;AACpE,IAAA,MAAM,QAAQ,MAAM,IAAA,CAAK,MAAM,WAAA,CAAY,IAAA,CAAK,IAAI,OAAO,CAAA;AAC3D,IAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,WAAA,CAAY,KAAA,EAAO;AAAA,MACtD,GAAA,EAAK,KAAK,GAAA,CAAI,GAAA;AAAA,MACd,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,KAAA,IAAS;AAAA,KAC1B,CAAA;AACD,IAAA,UAAA,CAAW,cAAA,CAAe,KAAK,QAAQ,CAAA;AACvC,IAAA,UAAA,CAAW,mBAAA,CAAoB,EAAE,IAAA,EAAM,IAAA,CAAK,eAAe,CAAA;AAC3D,IAAA,MAAM,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,UAAA,CAAW,OAAA,EAAQ,CAAE,KAAA,CAAM,KAAA,CAAM,MAAM,CAAC,CAAA;AACzF,IAAA,sBAAA,CAAuB,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,EACvD,CAAC,CAAA;AACH","file":"inject-session-JXQO33T3.js","sourcesContent":["/**\n * M51 (agent-builder) — inject a SYNTHETIC user+assistant pair into a session's persisted transcript\n * WITHOUT running an LLM turn. This is the Codex review-exit mechanism (`exit_success.xml` appended to\n * the parent thread history so follow-ups like \"fix finding 2\" work): the pair chains onto the DAG\n * leaf (M50 pattern) and the in-memory cache is invalidated so the NEXT send re-hydrates with it.\n */\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { SessionTranscript } from \"../persistence/session-transcript.js\";\nimport { enqueueSessionWrite, invalidateSessionCache } from \"./agent-session.js\";\nimport type { CompactLocation } from \"./compact-session.js\";\n\nexport async function injectSessionTurn(opts: {\n store: SessionStore;\n loc: CompactLocation;\n sessionId: string;\n userText: string;\n assistantText: string;\n}): Promise<void> {\n await enqueueSessionWrite(opts.loc.cwd, opts.loc.agentId, async () => {\n const prior = await opts.store.readRecords(opts.loc.agentId);\n const transcript = SessionTranscript.fromRecords(prior, {\n cwd: opts.loc.cwd,\n sessionId: opts.sessionId,\n model: opts.loc.model ?? \"unknown\",\n });\n transcript.appendUserTurn(opts.userText);\n transcript.appendAssistantTurn({ text: opts.assistantText });\n await opts.store.appendRecords(opts.loc.agentId, transcript.records().slice(prior.length));\n invalidateSessionCache(opts.loc.cwd, opts.loc.agentId);\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/session/inject-session.ts"],"names":[],"mappings":";;;;;;;AAWA,eAAsB,kBAAkB,IAAA,EAMtB;AAChB,EAAA,MAAM,oBAAoB,IAAA,CAAK,GAAA,CAAI,KAAK,IAAA,CAAK,GAAA,CAAI,SAAS,YAAY;AACpE,IAAA,MAAM,QAAQ,MAAM,IAAA,CAAK,MAAM,WAAA,CAAY,IAAA,CAAK,IAAI,OAAO,CAAA;AAC3D,IAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,WAAA,CAAY,KAAA,EAAO;AAAA,MACtD,GAAA,EAAK,KAAK,GAAA,CAAI,GAAA;AAAA,MACd,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,KAAA,IAAS;AAAA,KAC1B,CAAA;AACD,IAAA,UAAA,CAAW,cAAA,CAAe,KAAK,QAAQ,CAAA;AACvC,IAAA,UAAA,CAAW,mBAAA,CAAoB,EAAE,IAAA,EAAM,IAAA,CAAK,eAAe,CAAA;AAC3D,IAAA,MAAM,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,UAAA,CAAW,OAAA,EAAQ,CAAE,KAAA,CAAM,KAAA,CAAM,MAAM,CAAC,CAAA;AACzF,IAAA,sBAAA,CAAuB,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,EACvD,CAAC,CAAA;AACH","file":"inject-session-RZEDWO53.js","sourcesContent":["/**\n * M51 (agent-builder) — inject a SYNTHETIC user+assistant pair into a session's persisted transcript\n * WITHOUT running an LLM turn. This is the Codex review-exit mechanism (`exit_success.xml` appended to\n * the parent thread history so follow-ups like \"fix finding 2\" work): the pair chains onto the DAG\n * leaf (M50 pattern) and the in-memory cache is invalidated so the NEXT send re-hydrates with it.\n */\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { SessionTranscript } from \"../persistence/session-transcript.js\";\nimport { enqueueSessionWrite, invalidateSessionCache } from \"./agent-session.js\";\nimport type { CompactLocation } from \"./compact-session.js\";\n\nexport async function injectSessionTurn(opts: {\n store: SessionStore;\n loc: CompactLocation;\n sessionId: string;\n userText: string;\n assistantText: string;\n}): Promise<void> {\n await enqueueSessionWrite(opts.loc.cwd, opts.loc.agentId, async () => {\n const prior = await opts.store.readRecords(opts.loc.agentId);\n const transcript = SessionTranscript.fromRecords(prior, {\n cwd: opts.loc.cwd,\n sessionId: opts.sessionId,\n model: opts.loc.model ?? \"unknown\",\n });\n transcript.appendUserTurn(opts.userText);\n transcript.appendAssistantTurn({ text: opts.assistantText });\n await opts.store.appendRecords(opts.loc.agentId, transcript.records().slice(prior.length));\n invalidateSessionCache(opts.loc.cwd, opts.loc.agentId);\n });\n}\n"]}
@@ -1 +1,93 @@
1
- export {};
1
+ import { type GenerateOptions, type GenerateRunResult } from "../../agent-generate.js";
2
+ import type { AgentOptions, ModelSelection, SDKAgent, SDKArtifact } from "../../types/agent.js";
3
+ import type { Run, SDKUserMessage, SendOptions } from "../../types/run.js";
4
+ import type { PersonalityPreset } from "../personality/types.js";
5
+ import type { ProvidersManagerImpl } from "../runtime/config/providers-manager.js";
6
+ import type { FileContextManager } from "../runtime/context/context-manager.js";
7
+ import { HooksExecutor } from "../runtime/hooks/hooks-executor.js";
8
+ import type { PluginMetadata } from "../runtime/plugins/plugins-manager.js";
9
+ import type { SkillsHandle } from "../runtime/skills/skills-manager.js";
10
+ export declare class LocalAgent implements SDKAgent {
11
+ #private;
12
+ readonly agentId: string;
13
+ model: ModelSelection | undefined;
14
+ context?: FileContextManager;
15
+ providers?: ProvidersManagerImpl;
16
+ skills?: SkillsHandle;
17
+ plugins?: {
18
+ list: () => Promise<PluginMetadata[]>;
19
+ };
20
+ memory?: import("../../types/memory-adapter.js").AgentMemory;
21
+ private readonly options;
22
+ private readonly workspaceCwd;
23
+ /**
24
+ * SE40 — base dir for the native Claude-shaped session transcript. Default
25
+ * `~/.theokit`; set `local.baseDir: "~/.claude"` for Claude Code CLI interop.
26
+ */
27
+ private readonly transcriptBaseDir;
28
+ /**
29
+ * SE41 — the session record store. Defaults to the FS transcript store (byte-
30
+ * identical to SE40); `local.sessionStore` injects an external store (Postgres /
31
+ * Redis / KV) so resume works on serverless (ephemeral FS) and multi-host.
32
+ */
33
+ private readonly sessionStore;
34
+ /**
35
+ * D319: lifecycle AbortController fired on `dispose()`. Composed with the
36
+ * caller's `SendOptions.signal` via `anySignal` so the LLM `fetch()`
37
+ * aborts on either signal (user cancel OR dispose).
38
+ */
39
+ private readonly lifecycleAbortController;
40
+ private readonly settingSourcesIncludeProject;
41
+ private readonly settingSourcesIncludePlugins;
42
+ private resolvedSubagents;
43
+ private disposed;
44
+ private invalidationPending;
45
+ private readonly skillsManager;
46
+ private readonly pluginsManager;
47
+ private readonly hooksExecutor;
48
+ private readonly systemPromptPipeline;
49
+ private readonly memoryGlue;
50
+ private readonly personalityStore;
51
+ /** T0.1 — telemetry handle shared with sendLocked + memory recall path. */
52
+ private readonly _telemetry;
53
+ constructor(options: AgentOptions);
54
+ initialize(): Promise<void>;
55
+ /** Expose the hooks executor so the agent loop can fire PreToolUse/etc. */
56
+ hooks(): HooksExecutor;
57
+ generate<T extends import("zod").ZodType>(message: string | SDKUserMessage, options: GenerateOptions<T>): Promise<GenerateRunResult<import("zod").z.infer<T>>>;
58
+ send(message: string | SDKUserMessage, options?: SendOptions): Promise<Run>;
59
+ private runLockedSendCycle;
60
+ private sendLocked;
61
+ private assemblyInputs;
62
+ private resolveSystemPrompt;
63
+ private applyModelOverride;
64
+ private runPreHook;
65
+ private dispatchRun;
66
+ close(): void;
67
+ reload(): Promise<void>;
68
+ dispose(): Promise<void>;
69
+ [Symbol.asyncDispose](): Promise<void>;
70
+ /**
71
+ * Activate a personality preset (Hermes #26, ADRs D160-D164).
72
+ *
73
+ * Reserved names `none`, `default`, `neutral` clear the active preset.
74
+ *
75
+ * - `opts.save: true` → persist across process restarts (delete on clear,
76
+ * never write null — EC-B).
77
+ * - `opts.reset: true` → also clear session history (preserves by default).
78
+ *
79
+ * Always invalidates the prompt cache (D94 deferred default).
80
+ *
81
+ * @public
82
+ */
83
+ usePersonality(name: string, opts?: {
84
+ save?: boolean;
85
+ reset?: boolean;
86
+ }): Promise<PersonalityPreset | null>;
87
+ listArtifacts(): Promise<SDKArtifact[]>;
88
+ downloadArtifact(_path: string): Promise<Buffer>;
89
+ runUntil(goal?: string, options?: import("../../types/goal-events.js").GoalOptions): import("../../types/goal-events.js").RunUntilIterator;
90
+ fork(options: import("../runtime/lifecycle/fork-agent.js").ForkOptions): Promise<import("../runtime/lifecycle/fork-agent.js").ForkResult>;
91
+ runToCompletion(message: string, options?: import("../../types/run.js").RunToCompletionOptions): Promise<import("../../types/run.js").RunToCompletionResult>;
92
+ streamToCompletion(message: string, options?: import("../../types/run.js").RunToCompletionOptions): AsyncGenerator<import("../../types/messages.js").SDKMessage, import("../../types/run.js").StreamToCompletionResult>;
93
+ }
@@ -17,6 +17,8 @@
17
17
  *
18
18
  * @internal
19
19
  */
20
+ import type { SessionStore } from "../../types/session-store.js";
21
+ import { type SessionRecord } from "./session-transcript.js";
20
22
  /** Options identifying the on-disk transcript location for the FS default store. */
21
23
  export interface FsSessionStoreOptions {
22
24
  /** Transcript base dir (`~/.theokit` default, `~/.claude` for CLI interop) — already `~`-expanded. */
@@ -24,3 +26,17 @@ export interface FsSessionStoreOptions {
24
26
  /** The workspace cwd whose encoded form is the transcript project dir. */
25
27
  cwd: string;
26
28
  }
29
+ export declare class FsSessionStore implements SessionStore {
30
+ #private;
31
+ constructor(options: FsSessionStoreOptions);
32
+ readRecords(agentId: string): Promise<SessionRecord[]>;
33
+ appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void>;
34
+ /**
35
+ * Solta todo lease que este store detém.
36
+ *
37
+ * Sem isto o `.writer.lock` sobrevive ao processo e a próxima abertura teria de esperar a janela
38
+ * de heartbeat — recuperável, mas 30 s de espera para um encerramento LIMPO seria um defeito
39
+ * evitável. Idempotente: chamar duas vezes não é erro.
40
+ */
41
+ dispose(): Promise<void>;
42
+ }
@@ -17,6 +17,8 @@
17
17
  *
18
18
  * @internal
19
19
  */
20
+ import type { SessionStore } from "../../types/session-store.js";
21
+ import { type SessionRecord } from "./session-transcript.js";
20
22
  /** Options identifying the on-disk transcript location for the FS default store. */
21
23
  export interface FsSessionStoreOptions {
22
24
  /** Transcript base dir (`~/.theokit` default, `~/.claude` for CLI interop) — already `~`-expanded. */
@@ -24,3 +26,17 @@ export interface FsSessionStoreOptions {
24
26
  /** The workspace cwd whose encoded form is the transcript project dir. */
25
27
  cwd: string;
26
28
  }
29
+ export declare class FsSessionStore implements SessionStore {
30
+ #private;
31
+ constructor(options: FsSessionStoreOptions);
32
+ readRecords(agentId: string): Promise<SessionRecord[]>;
33
+ appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void>;
34
+ /**
35
+ * Solta todo lease que este store detém.
36
+ *
37
+ * Sem isto o `.writer.lock` sobrevive ao processo e a próxima abertura teria de esperar a janela
38
+ * de heartbeat — recuperável, mas 30 s de espera para um encerramento LIMPO seria um defeito
39
+ * evitável. Idempotente: chamar duas vezes não é erro.
40
+ */
41
+ dispose(): Promise<void>;
42
+ }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkPNVDQL5Y_cjs = require('../../chunk-PNVDQL5Y.cjs');
4
- var chunkOIYQBUW4_cjs = require('../../chunk-OIYQBUW4.cjs');
4
+ var chunk6M6A6WWW_cjs = require('../../chunk-6M6A6WWW.cjs');
5
5
  var chunkCJVMDRQB_cjs = require('../../chunk-CJVMDRQB.cjs');
6
6
  var chunkJD74LN7R_cjs = require('../../chunk-JD74LN7R.cjs');
7
7
  var chunkYLQQX5W2_cjs = require('../../chunk-YLQQX5W2.cjs');
@@ -41,27 +41,27 @@ Object.defineProperty(exports, "PersistenceSchema", {
41
41
  });
42
42
  Object.defineProperty(exports, "displayTheokitHome", {
43
43
  enumerable: true,
44
- get: function () { return chunkOIYQBUW4_cjs.displayTheokitHome; }
44
+ get: function () { return chunk6M6A6WWW_cjs.displayTheokitHome; }
45
45
  });
46
46
  Object.defineProperty(exports, "getProfilesRoot", {
47
47
  enumerable: true,
48
- get: function () { return chunkOIYQBUW4_cjs.getProfilesRoot; }
48
+ get: function () { return chunk6M6A6WWW_cjs.getProfilesRoot; }
49
49
  });
50
50
  Object.defineProperty(exports, "getTheokitHome", {
51
51
  enumerable: true,
52
- get: function () { return chunkOIYQBUW4_cjs.getTheokitHome; }
52
+ get: function () { return chunk6M6A6WWW_cjs.getTheokitHome; }
53
53
  });
54
54
  Object.defineProperty(exports, "migrateSchema", {
55
55
  enumerable: true,
56
- get: function () { return chunkOIYQBUW4_cjs.migrateSchema; }
56
+ get: function () { return chunk6M6A6WWW_cjs.migrateSchema; }
57
57
  });
58
58
  Object.defineProperty(exports, "readVersionedJson", {
59
59
  enumerable: true,
60
- get: function () { return chunkOIYQBUW4_cjs.readVersionedJson; }
60
+ get: function () { return chunk6M6A6WWW_cjs.readVersionedJson; }
61
61
  });
62
62
  Object.defineProperty(exports, "writeVersionedJson", {
63
63
  enumerable: true,
64
- get: function () { return chunkOIYQBUW4_cjs.writeVersionedJson; }
64
+ get: function () { return chunk6M6A6WWW_cjs.writeVersionedJson; }
65
65
  });
66
66
  Object.defineProperty(exports, "JsonlParseError", {
67
67
  enumerable: true,
@@ -1,5 +1,5 @@
1
1
  export { PersistenceSchema } from '../../chunk-CAH3G4IS.js';
2
- export { displayTheokitHome, getProfilesRoot, getTheokitHome, migrateSchema, readVersionedJson, writeVersionedJson } from '../../chunk-DGRZNMTU.js';
2
+ export { displayTheokitHome, getProfilesRoot, getTheokitHome, migrateSchema, readVersionedJson, writeVersionedJson } from '../../chunk-XV2RBL6D.js';
3
3
  export { JsonlParseError, appendJsonl, loadJsonl, readJsonlIds, withFileLock } from '../../chunk-UAYYNWYJ.js';
4
4
  export { applyWalWithFallback, containsCjk, isCorruptionError, openSqliteResilient, sanitizeFts5Query } from '../../chunk-77FOZCOT.js';
5
5
  export { withCwdMutex } from '../../chunk-2XLKLVVR.js';
@@ -6,9 +6,19 @@
6
6
  * test isolation, profile switching, and multi-tenant deployments.
7
7
  *
8
8
  * Rules:
9
- * - `getTheokitHome(cwd)` is the ONLY canonical resolver. Never hardcode
9
+ * - `getTheokitHome(cwd)` is the canonical resolver **for cwd-anchored state**. Never hardcode
10
10
  * `path.join(cwd, ".theokit")` in callers — use this function so tests
11
11
  * and overrides stay consistent.
12
+ *
13
+ * M94 — este comentário dizia "the ONLY canonical resolver", e deixou de ser verdade: o
14
+ * transcript ganhou `transcriptRoot()`, que é **home-ancorado** (`~/.theokit`) com o mesmo
15
+ * override por `THEOKIT_HOME`. Os dois defaults diferem de propósito — unificar moveria o
16
+ * transcript de todo mundo que NÃO define a variável, que é uma migração de dados e não um
17
+ * re-export.
18
+ *
19
+ * Consequência que merece estar escrita: **sem `THEOKIT_HOME` o estado segue partido em dois**
20
+ * — registry em `<cwd>/.theokit`, transcript em `~/.theokit`. O M94 unifica só para quem define
21
+ * a variável. Unificar os dois defaults é trabalho de outro milestone.
12
22
  * - `getProfilesRoot()` is intentionally home-anchored (not affected by
13
23
  * `THEOKIT_HOME`) so `theokit profile list` discovers all profiles
14
24
  * regardless of which is active.
@@ -6,9 +6,19 @@
6
6
  * test isolation, profile switching, and multi-tenant deployments.
7
7
  *
8
8
  * Rules:
9
- * - `getTheokitHome(cwd)` is the ONLY canonical resolver. Never hardcode
9
+ * - `getTheokitHome(cwd)` is the canonical resolver **for cwd-anchored state**. Never hardcode
10
10
  * `path.join(cwd, ".theokit")` in callers — use this function so tests
11
11
  * and overrides stay consistent.
12
+ *
13
+ * M94 — este comentário dizia "the ONLY canonical resolver", e deixou de ser verdade: o
14
+ * transcript ganhou `transcriptRoot()`, que é **home-ancorado** (`~/.theokit`) com o mesmo
15
+ * override por `THEOKIT_HOME`. Os dois defaults diferem de propósito — unificar moveria o
16
+ * transcript de todo mundo que NÃO define a variável, que é uma migração de dados e não um
17
+ * re-export.
18
+ *
19
+ * Consequência que merece estar escrita: **sem `THEOKIT_HOME` o estado segue partido em dois**
20
+ * — registry em `<cwd>/.theokit`, transcript em `~/.theokit`. O M94 unifica só para quem define
21
+ * a variável. Unificar os dois defaults é trabalho de outro milestone.
12
22
  * - `getProfilesRoot()` is intentionally home-anchored (not affected by
13
23
  * `THEOKIT_HOME`) so `theokit profile list` discovers all profiles
14
24
  * regardless of which is active.
@@ -53,4 +53,12 @@ export interface SessionWriterLease {
53
53
  * SDK's `withFileLock` builds on. Exclusivity comes from the filesystem, so it holds across
54
54
  * processes, not just across async tasks in one process.
55
55
  */
56
+ /**
57
+ * Janela de heartbeat: um lock mais velho que isto é considerado obsoleto.
58
+ *
59
+ * 30 s. Curta o bastante para não trancar o usuário por minutos depois de um crash; longa o
60
+ * bastante para não reclamar o lock de um processo vivo que só está lento em I/O. O dono toca o
61
+ * arquivo a cada aquisição, então um processo ativo nunca cruza a janela.
62
+ */
63
+ export declare const JANELA_DE_HEARTBEAT_MS = 30000;
56
64
  export declare function acquireSessionWriter(sessionPath: string): Promise<SessionWriterLease>;
@@ -53,4 +53,12 @@ export interface SessionWriterLease {
53
53
  * SDK's `withFileLock` builds on. Exclusivity comes from the filesystem, so it holds across
54
54
  * processes, not just across async tasks in one process.
55
55
  */
56
+ /**
57
+ * Janela de heartbeat: um lock mais velho que isto é considerado obsoleto.
58
+ *
59
+ * 30 s. Curta o bastante para não trancar o usuário por minutos depois de um crash; longa o
60
+ * bastante para não reclamar o lock de um processo vivo que só está lento em I/O. O dono toca o
61
+ * arquivo a cada aquisição, então um processo ativo nunca cruza a janela.
62
+ */
63
+ export declare const JANELA_DE_HEARTBEAT_MS = 30000;
56
64
  export declare function acquireSessionWriter(sessionPath: string): Promise<SessionWriterLease>;
@@ -1,5 +1,27 @@
1
1
  import type { SessionMessage } from "./session-types.js";
2
2
  export { hydratedKeys, invalidateSessionCache, sessions, transcriptKey, } from "./session-cache.js";
3
+ /**
4
+ * Teto de sessões mantidas em memória.
5
+ *
6
+ * O runtime só lê a sessão **ativa**; o resto é cache puro, reconstruível do transcript em disco.
7
+ * 32 é folgado de propósito — o caminho primário de remoção é o `descartarSessao()` explícito no
8
+ * fim da vida do agente, e este teto é rede de segurança contra um processo de vida longa que roda
9
+ * centenas de sessões (risco #2 do plano: um teto apertado poderia evictar sessão ainda referenciada
10
+ * por um fluxo assíncrono em voo).
11
+ */
12
+ export declare const TETO_DE_SESSOES_EM_CACHE = 32;
13
+ /**
14
+ * Apaga a escrituração de módulo do agente e devolve quantas entradas saíram.
15
+ *
16
+ * M95 — `invalidateSessionCache` limpava **dois** dos quatro mapas (`sessions`, `hydratedKeys`);
17
+ * `pendingWrites` e `recordCounts` nunca eram tocados por id, então cresciam pela vida do processo.
18
+ * Nenhum dos dois é grande por entrada — o vazamento é de contagem, não de volume — mas cache sem
19
+ * dono da remoção é cache que só cresce.
20
+ *
21
+ * Devolve a contagem para que o chamador possa provar a remoção; um segundo descarte devolve 0,
22
+ * que é o que torna o teste de idempotência possível sem expor os mapas.
23
+ */
24
+ export declare function descartarSessao(cwd: string, agentId: string): number;
3
25
  export declare function getSessionMessages(agentId: string): SessionMessage[];
4
26
  export declare function clearSession(agentId: string): void;
5
27
  /**
@@ -1,48 +1,16 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkPNVDQL5Y_cjs = require('./chunk-PNVDQL5Y.cjs');
4
+ var chunkLDDUIXWS_cjs = require('./chunk-LDDUIXWS.cjs');
4
5
  var chunkCJVMDRQB_cjs = require('./chunk-CJVMDRQB.cjs');
5
6
  var chunkJD74LN7R_cjs = require('./chunk-JD74LN7R.cjs');
6
7
  var chunkYLQQX5W2_cjs = require('./chunk-YLQQX5W2.cjs');
7
- var chunkDQSILMWU_cjs = require('./chunk-DQSILMWU.cjs');
8
+ var chunk2DYI7MIF_cjs = require('./chunk-2DYI7MIF.cjs');
8
9
  var chunkTIZ4TS5V_cjs = require('./chunk-TIZ4TS5V.cjs');
9
10
  var chunkA44COQJJ_cjs = require('./chunk-A44COQJJ.cjs');
10
11
  require('./chunk-BEURCLDV.cjs');
11
12
  var fs = require('fs');
12
13
 
13
- var SessionBusyError = class extends chunkA44COQJJ_cjs.TheokitAgentError {
14
- constructor(sessionPath) {
15
- super(
16
- `another process is already writing this session: ${sessionPath}. Fork it to a new id instead of appending \u2014 two writers interleave lines into a sequence that parses but is not what either process wrote.`,
17
- { code: "session_busy", isRetryable: false }
18
- );
19
- this.sessionPath = sessionPath;
20
- }
21
- sessionPath;
22
- name = "SessionBusyError";
23
- };
24
- async function acquireSessionWriter(sessionPath) {
25
- const lockPath = `${sessionPath}.lock`;
26
- let fd;
27
- try {
28
- fd = fs.openSync(lockPath, "wx");
29
- } catch (err) {
30
- if (err.code === "EEXIST") {
31
- throw new SessionBusyError(sessionPath);
32
- }
33
- throw err;
34
- }
35
- fs.closeSync(fd);
36
- let released = false;
37
- return {
38
- sessionPath,
39
- release: async () => {
40
- if (released) return;
41
- released = true;
42
- fs.rmSync(lockPath, { force: true });
43
- }
44
- };
45
- }
46
14
  var LiveSessionError = class extends chunkA44COQJJ_cjs.TheokitAgentError {
47
15
  constructor(path) {
48
16
  super(
@@ -113,6 +81,14 @@ Object.defineProperty(exports, "PersistenceSchema", {
113
81
  enumerable: true,
114
82
  get: function () { return chunkPNVDQL5Y_cjs.PersistenceSchema; }
115
83
  });
84
+ Object.defineProperty(exports, "SessionBusyError", {
85
+ enumerable: true,
86
+ get: function () { return chunkLDDUIXWS_cjs.SessionBusyError; }
87
+ });
88
+ Object.defineProperty(exports, "acquireSessionWriter", {
89
+ enumerable: true,
90
+ get: function () { return chunkLDDUIXWS_cjs.acquireSessionWriter; }
91
+ });
116
92
  Object.defineProperty(exports, "JsonlParseError", {
117
93
  enumerable: true,
118
94
  get: function () { return chunkCJVMDRQB_cjs.JsonlParseError; }
@@ -155,15 +131,15 @@ Object.defineProperty(exports, "withCwdMutex", {
155
131
  });
156
132
  Object.defineProperty(exports, "encodeProjectDir", {
157
133
  enumerable: true,
158
- get: function () { return chunkDQSILMWU_cjs.encodeProjectDir; }
134
+ get: function () { return chunk2DYI7MIF_cjs.encodeProjectDir; }
159
135
  });
160
136
  Object.defineProperty(exports, "transcriptPath", {
161
137
  enumerable: true,
162
- get: function () { return chunkDQSILMWU_cjs.transcriptPath; }
138
+ get: function () { return chunk2DYI7MIF_cjs.transcriptPath; }
163
139
  });
164
140
  Object.defineProperty(exports, "transcriptRoot", {
165
141
  enumerable: true,
166
- get: function () { return chunkDQSILMWU_cjs.transcriptRoot; }
142
+ get: function () { return chunk2DYI7MIF_cjs.transcriptRoot; }
167
143
  });
168
144
  Object.defineProperty(exports, "atomicWriteJson", {
169
145
  enumerable: true,
@@ -178,8 +154,6 @@ Object.defineProperty(exports, "replaceFileAtomic", {
178
154
  get: function () { return chunkTIZ4TS5V_cjs.replaceFileAtomic; }
179
155
  });
180
156
  exports.LiveSessionError = LiveSessionError;
181
- exports.SessionBusyError = SessionBusyError;
182
- exports.acquireSessionWriter = acquireSessionWriter;
183
157
  exports.forkTranscript = forkTranscript;
184
158
  exports.readJsonlTail = readJsonlTail;
185
159
  //# sourceMappingURL=persistence.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/persistence/session-writer.ts","../src/internal/persistence/transcript-ops.ts"],"names":["TheokitAgentError","openSync","closeSync","rmSync","readFileSync","writeSync","statSync","readSync"],"mappings":"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","file":"persistence.cjs","sourcesContent":["/**\n * M81 — single-writer lease for a session transcript.\n *\n * ## The problem\n *\n * Nothing stops two processes appending to the same JSONL transcript. The concrete case: `exec\n * resume --last` can write into the TUI's live session. Two interleaved appends to an append-only\n * file produce lines that are each individually valid and whose SEQUENCE is fiction — and nothing\n * reports it, because every line parses.\n *\n * ## Why an exclusive lockfile rather than `withFileLock`\n *\n * The plan's ADR D2 said to compose `withFileLock`, and that was the right instinct — do not build a\n * second lock mechanism. It turned out not to fit the SHAPE: `withFileLock(path, fn)` is\n * scope-based — it holds the lock for the duration of a callback. A session lease is **held across\n * turns**, for as long as the process owns the session, with an explicit `release()`. Wrapping the\n * whole session lifetime in a callback would invert control of the entire agent loop.\n *\n * So this uses the same underlying primitive `withFileLock` uses (an exclusive-create lockfile,\n * `wx`) with lease semantics on top. That keeps the mechanism single — the file-existence lock —\n * while giving it the lifetime the caller needs. The deviation from D2 is recorded here because the\n * plan's rationale (no second mechanism) still holds; only its shape assumption did not.\n *\n * ## Fail fast, never wait\n *\n * A second writer that WAITED would block `exec` behind a TUI session that can last hours. The typed\n * error lets the caller choose: fork to a new id, or give up with a real diagnosis.\n *\n * @internal\n */\n\nimport { closeSync, openSync, rmSync } from \"node:fs\";\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * M81 — another process already holds the writer lease for this session.\n *\n * Carries `sessionPath` because knowing WHICH session is busy is what lets the caller decide between\n * forking and waiting for the user to close the TUI (`rules/error-handling.md § 2` — context enough\n * to act on).\n */\nexport class SessionBusyError extends TheokitAgentError {\n override readonly name = \"SessionBusyError\";\n\n constructor(readonly sessionPath: string) {\n super(\n `another process is already writing this session: ${sessionPath}. ` +\n \"Fork it to a new id instead of appending — two writers interleave lines into a sequence \" +\n \"that parses but is not what either process wrote.\",\n { code: \"session_busy\", isRetryable: false },\n );\n }\n}\n\n/** A held writer lease. `release()` is idempotent. */\nexport interface SessionWriterLease {\n readonly sessionPath: string;\n release(): Promise<void>;\n}\n\n/**\n * Take the exclusive writer lease for `sessionPath`, or reject with {@link SessionBusyError}.\n *\n * The lock is a sibling `.lock` file created with `wx` — the same file-existence primitive the\n * SDK's `withFileLock` builds on. Exclusivity comes from the filesystem, so it holds across\n * processes, not just across async tasks in one process.\n */\nexport async function acquireSessionWriter(sessionPath: string): Promise<SessionWriterLease> {\n const lockPath = `${sessionPath}.lock`;\n let fd: number;\n try {\n fd = openSync(lockPath, \"wx\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new SessionBusyError(sessionPath);\n }\n throw err;\n }\n closeSync(fd);\n\n let released = false;\n return {\n sessionPath,\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n rmSync(lockPath, { force: true });\n },\n };\n}\n","/**\n * M81 — transcript operations the consumer was doing by hand INSIDE the framework's own store.\n *\n * ## What this replaces\n *\n * `agents/lib/session/backtrack.ts:188` (agent-builder) wrote straight into the session store with a\n * bare `writeFileSync` — no atomicity, no lock, no API. 243 lines re-implementing parse, cut and\n * write for a format the framework owns. The consumer is not at fault: nothing here was reachable.\n *\n * ## The rule that travels WITH the operation\n *\n * `rules/audit-trail-rotation.md § Session transcripts (M60)` defines a NEVER-delete list — the live\n * pointer, the most recent transcript, and any active registry entry. That rule lived in the\n * CONSUMER. Moving the operation here without moving the rule would ship an API able to destroy\n * exactly what the rule protects — the same shape of defect as M80's `reconcileUpdateGoalStatus`:\n * critical knowledge outside the module that needs it, applied by convention.\n *\n * So `forkTranscript` takes `liveSessionPaths` and refuses, with a TYPED error, to write over any of\n * them. The caller supplies the list because only the caller knows which session is live; the\n * enforcement lives here because that is where the write happens.\n *\n * @internal\n */\n\nimport { closeSync, openSync, readFileSync, readSync, statSync, writeSync } from \"node:fs\";\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * M81 — the target is a protected session (live pointer / most-recent transcript / active entry).\n *\n * Typed rather than a bare `Error` because the caller must distinguish \"this session is protected\"\n * from \"the disk is full\": the first is a correct refusal, the second is an incident.\n */\nexport class LiveSessionError extends TheokitAgentError {\n override readonly name = \"LiveSessionError\";\n\n constructor(readonly path: string) {\n super(\n `refusing to write over a live session transcript: ${path}. ` +\n \"Fork to a new id instead — the live pointer, the most recent transcript and any active \" +\n \"registry entry are never overwritten (audit-trail rotation, M60).\",\n { code: \"live_session_protected\", isRetryable: false },\n );\n }\n}\n\n/** Options for {@link forkTranscript}. */\nexport interface ForkTranscriptOptions {\n /** Keep records `[0, beforeRecordIndex)`. Omit to copy the whole transcript. */\n readonly beforeRecordIndex?: number;\n /**\n * Paths that must never be written over — the live pointer, the most recent transcript, any active\n * registry entry. The caller supplies them because only the caller knows which session is live.\n */\n readonly liveSessionPaths?: readonly string[];\n}\n\n/**\n * Copy `src` into `dst`, keeping the first `beforeRecordIndex` records. The SOURCE is never touched.\n *\n * Atomicity comes from `wx` (exclusive create): two concurrent forks to the same destination cannot\n * both succeed — the loser gets `EEXIST` rather than writing over a half-written file. That is also\n * why an existing destination is a refusal, not a silent overwrite: losing a transcript without an\n * error is the worst failure mode for an operation that touches user sessions.\n */\nexport function forkTranscript(\n src: string,\n dst: string,\n options: ForkTranscriptOptions = {},\n): void {\n for (const live of options.liveSessionPaths ?? []) {\n if (live === dst) throw new LiveSessionError(dst);\n }\n\n const lines = readFileSync(src, \"utf8\")\n .split(\"\\n\")\n .filter((l) => l.trim().length > 0);\n const kept =\n options.beforeRecordIndex === undefined ? lines : lines.slice(0, options.beforeRecordIndex);\n const body = kept.length > 0 ? `${kept.join(\"\\n\")}\\n` : \"\";\n\n // `wx` — fails with EEXIST instead of truncating. The exclusivity IS the concurrency guarantee.\n const fd = openSync(dst, \"wx\");\n try {\n writeSync(fd, body);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Options for {@link readJsonlTail}. */\nexport interface ReadJsonlTailOptions {\n /** Maximum records to return, counted from the END. */\n readonly maxRecords?: number;\n /** Stop once a line contains this marker (exclusive). */\n readonly sinceMarker?: string;\n /** Test-only: also report how many bytes were read, to prove the read is not whole-file. */\n readonly _stats?: boolean;\n}\n\nconst TAIL_CHUNK = 64 * 1024;\n\n/**\n * Lê chunks de trás para frente até acumular linhas completas suficientes.\n *\n * Extraído de `readJsonlTail` porque o laço de leitura e a seleção de registros são duas\n * responsabilidades — e juntas passavam do teto de complexidade. A primeira linha do buffer pode\n * estar cortada ao meio quando a leitura parou antes do início do arquivo; por isso ela é descartada.\n */\nfunction lerCaudaBruta(path: string, want: number): { linhas: string[]; bytesRead: number } {\n const size = statSync(path).size;\n const fd = openSync(path, \"r\");\n let bytesRead = 0;\n let tail = \"\";\n let pos = size;\n try {\n while (pos > 0) {\n const len = Math.min(TAIL_CHUNK, pos);\n pos -= len;\n const buf = Buffer.alloc(len);\n readSync(fd, buf, 0, len, pos);\n bytesRead += len;\n tail = buf.toString(\"utf8\") + tail;\n if (naoVazias(tail).length > want) break;\n }\n } finally {\n closeSync(fd);\n }\n const linhas = naoVazias(tail);\n return { linhas: pos > 0 ? linhas.slice(1) : linhas, bytesRead };\n}\n\n/** Linhas com conteúdo, na ordem do arquivo. */\nfunction naoVazias(texto: string): string[] {\n return texto.split(\"\\n\").filter((l) => l.trim().length > 0);\n}\n\n/**\n * Read the LAST records of a JSONL file without loading the whole thing.\n *\n * Reads fixed-size chunks backwards from EOF until enough newlines have been seen. A session\n * transcript grows without bound; loading megabytes to show the last three turns is the cost this\n * exists to avoid — and a `slice` over a full read would be that same cost with a better name.\n */\nexport function readJsonlTail<T = Record<string, unknown>>(\n path: string,\n options: ReadJsonlTailOptions = {},\n): T[] {\n const want = options.maxRecords ?? Number.POSITIVE_INFINITY;\n const { linhas, bytesRead } = lerCaudaBruta(path, want);\n\n let sel = linhas;\n if (options.sinceMarker !== undefined) {\n const marcador = options.sinceMarker;\n const idx = sel.findLastIndex((l) => l.includes(marcador));\n if (idx >= 0) sel = sel.slice(idx + 1);\n }\n if (Number.isFinite(want)) sel = sel.slice(-want);\n\n const out = sel.map((l) => JSON.parse(l) as T);\n return options._stats === true ? (Object.assign(out, { bytesRead }) as T[]) : out;\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/persistence/transcript-ops.ts"],"names":["TheokitAgentError","readFileSync","openSync","writeSync","closeSync","statSync","readSync"],"mappings":"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","file":"persistence.cjs","sourcesContent":["/**\n * M81 — transcript operations the consumer was doing by hand INSIDE the framework's own store.\n *\n * ## What this replaces\n *\n * `agents/lib/session/backtrack.ts:188` (agent-builder) wrote straight into the session store with a\n * bare `writeFileSync` — no atomicity, no lock, no API. 243 lines re-implementing parse, cut and\n * write for a format the framework owns. The consumer is not at fault: nothing here was reachable.\n *\n * ## The rule that travels WITH the operation\n *\n * `rules/audit-trail-rotation.md § Session transcripts (M60)` defines a NEVER-delete list — the live\n * pointer, the most recent transcript, and any active registry entry. That rule lived in the\n * CONSUMER. Moving the operation here without moving the rule would ship an API able to destroy\n * exactly what the rule protects — the same shape of defect as M80's `reconcileUpdateGoalStatus`:\n * critical knowledge outside the module that needs it, applied by convention.\n *\n * So `forkTranscript` takes `liveSessionPaths` and refuses, with a TYPED error, to write over any of\n * them. The caller supplies the list because only the caller knows which session is live; the\n * enforcement lives here because that is where the write happens.\n *\n * @internal\n */\n\nimport { closeSync, openSync, readFileSync, readSync, statSync, writeSync } from \"node:fs\";\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * M81 — the target is a protected session (live pointer / most-recent transcript / active entry).\n *\n * Typed rather than a bare `Error` because the caller must distinguish \"this session is protected\"\n * from \"the disk is full\": the first is a correct refusal, the second is an incident.\n */\nexport class LiveSessionError extends TheokitAgentError {\n override readonly name = \"LiveSessionError\";\n\n constructor(readonly path: string) {\n super(\n `refusing to write over a live session transcript: ${path}. ` +\n \"Fork to a new id instead — the live pointer, the most recent transcript and any active \" +\n \"registry entry are never overwritten (audit-trail rotation, M60).\",\n { code: \"live_session_protected\", isRetryable: false },\n );\n }\n}\n\n/** Options for {@link forkTranscript}. */\nexport interface ForkTranscriptOptions {\n /** Keep records `[0, beforeRecordIndex)`. Omit to copy the whole transcript. */\n readonly beforeRecordIndex?: number;\n /**\n * Paths that must never be written over — the live pointer, the most recent transcript, any active\n * registry entry. The caller supplies them because only the caller knows which session is live.\n */\n readonly liveSessionPaths?: readonly string[];\n}\n\n/**\n * Copy `src` into `dst`, keeping the first `beforeRecordIndex` records. The SOURCE is never touched.\n *\n * Atomicity comes from `wx` (exclusive create): two concurrent forks to the same destination cannot\n * both succeed — the loser gets `EEXIST` rather than writing over a half-written file. That is also\n * why an existing destination is a refusal, not a silent overwrite: losing a transcript without an\n * error is the worst failure mode for an operation that touches user sessions.\n */\nexport function forkTranscript(\n src: string,\n dst: string,\n options: ForkTranscriptOptions = {},\n): void {\n for (const live of options.liveSessionPaths ?? []) {\n if (live === dst) throw new LiveSessionError(dst);\n }\n\n const lines = readFileSync(src, \"utf8\")\n .split(\"\\n\")\n .filter((l) => l.trim().length > 0);\n const kept =\n options.beforeRecordIndex === undefined ? lines : lines.slice(0, options.beforeRecordIndex);\n const body = kept.length > 0 ? `${kept.join(\"\\n\")}\\n` : \"\";\n\n // `wx` — fails with EEXIST instead of truncating. The exclusivity IS the concurrency guarantee.\n const fd = openSync(dst, \"wx\");\n try {\n writeSync(fd, body);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Options for {@link readJsonlTail}. */\nexport interface ReadJsonlTailOptions {\n /** Maximum records to return, counted from the END. */\n readonly maxRecords?: number;\n /** Stop once a line contains this marker (exclusive). */\n readonly sinceMarker?: string;\n /** Test-only: also report how many bytes were read, to prove the read is not whole-file. */\n readonly _stats?: boolean;\n}\n\nconst TAIL_CHUNK = 64 * 1024;\n\n/**\n * Lê chunks de trás para frente até acumular linhas completas suficientes.\n *\n * Extraído de `readJsonlTail` porque o laço de leitura e a seleção de registros são duas\n * responsabilidades — e juntas passavam do teto de complexidade. A primeira linha do buffer pode\n * estar cortada ao meio quando a leitura parou antes do início do arquivo; por isso ela é descartada.\n */\nfunction lerCaudaBruta(path: string, want: number): { linhas: string[]; bytesRead: number } {\n const size = statSync(path).size;\n const fd = openSync(path, \"r\");\n let bytesRead = 0;\n let tail = \"\";\n let pos = size;\n try {\n while (pos > 0) {\n const len = Math.min(TAIL_CHUNK, pos);\n pos -= len;\n const buf = Buffer.alloc(len);\n readSync(fd, buf, 0, len, pos);\n bytesRead += len;\n tail = buf.toString(\"utf8\") + tail;\n if (naoVazias(tail).length > want) break;\n }\n } finally {\n closeSync(fd);\n }\n const linhas = naoVazias(tail);\n return { linhas: pos > 0 ? linhas.slice(1) : linhas, bytesRead };\n}\n\n/** Linhas com conteúdo, na ordem do arquivo. */\nfunction naoVazias(texto: string): string[] {\n return texto.split(\"\\n\").filter((l) => l.trim().length > 0);\n}\n\n/**\n * Read the LAST records of a JSONL file without loading the whole thing.\n *\n * Reads fixed-size chunks backwards from EOF until enough newlines have been seen. A session\n * transcript grows without bound; loading megabytes to show the last three turns is the cost this\n * exists to avoid — and a `slice` over a full read would be that same cost with a better name.\n */\nexport function readJsonlTail<T = Record<string, unknown>>(\n path: string,\n options: ReadJsonlTailOptions = {},\n): T[] {\n const want = options.maxRecords ?? Number.POSITIVE_INFINITY;\n const { linhas, bytesRead } = lerCaudaBruta(path, want);\n\n let sel = linhas;\n if (options.sinceMarker !== undefined) {\n const marcador = options.sinceMarker;\n const idx = sel.findLastIndex((l) => l.includes(marcador));\n if (idx >= 0) sel = sel.slice(idx + 1);\n }\n if (Number.isFinite(want)) sel = sel.slice(-want);\n\n const out = sel.map((l) => JSON.parse(l) as T);\n return options._stats === true ? (Object.assign(out, { bytesRead }) as T[]) : out;\n}\n"]}
@@ -22,10 +22,10 @@ export { sanitizeFts5Query } from "./internal/persistence/fts5-sanitize.js";
22
22
  export { appendJsonl, JsonlParseError, loadJsonl, readJsonlIds, } from "./internal/persistence/jsonl.js";
23
23
  export { PersistenceSchema } from "./internal/persistence/persistence-schema.js";
24
24
  export { encodeProjectDir, transcriptPath, transcriptRoot, } from "./internal/persistence/session-transcript.js";
25
- export type { TranscriptBlock, TranscriptMessage } from "./types/session-record.js";
26
25
  export { acquireSessionWriter, SessionBusyError, type SessionWriterLease, } from "./internal/persistence/session-writer.js";
27
26
  export type { OpenSqliteResilientOptions, ResilientSqliteDb, } from "./internal/persistence/sqlite-open.js";
28
27
  export { isCorruptionError, openSqliteResilient } from "./internal/persistence/sqlite-open.js";
29
28
  export type { WalApplyResult } from "./internal/persistence/sqlite-wal.js";
30
29
  export { applyWalWithFallback } from "./internal/persistence/sqlite-wal.js";
31
30
  export { type ForkTranscriptOptions, forkTranscript, LiveSessionError, type ReadJsonlTailOptions, readJsonlTail, } from "./internal/persistence/transcript-ops.js";
31
+ export type { TranscriptBlock, TranscriptMessage } from "./types/session-record.js";
@@ -22,10 +22,10 @@ export { sanitizeFts5Query } from "./internal/persistence/fts5-sanitize.js";
22
22
  export { appendJsonl, JsonlParseError, loadJsonl, readJsonlIds, } from "./internal/persistence/jsonl.js";
23
23
  export { PersistenceSchema } from "./internal/persistence/persistence-schema.js";
24
24
  export { encodeProjectDir, transcriptPath, transcriptRoot, } from "./internal/persistence/session-transcript.js";
25
- export type { TranscriptBlock, TranscriptMessage } from "./types/session-record.js";
26
25
  export { acquireSessionWriter, SessionBusyError, type SessionWriterLease, } from "./internal/persistence/session-writer.js";
27
26
  export type { OpenSqliteResilientOptions, ResilientSqliteDb, } from "./internal/persistence/sqlite-open.js";
28
27
  export { isCorruptionError, openSqliteResilient } from "./internal/persistence/sqlite-open.js";
29
28
  export type { WalApplyResult } from "./internal/persistence/sqlite-wal.js";
30
29
  export { applyWalWithFallback } from "./internal/persistence/sqlite-wal.js";
31
30
  export { type ForkTranscriptOptions, forkTranscript, LiveSessionError, type ReadJsonlTailOptions, readJsonlTail, } from "./internal/persistence/transcript-ops.js";
31
+ export type { TranscriptBlock, TranscriptMessage } from "./types/session-record.js";
@@ -1,46 +1,14 @@
1
1
  export { PersistenceSchema } from './chunk-CAH3G4IS.js';
2
+ export { SessionBusyError, acquireSessionWriter } from './chunk-B3HUL2OQ.js';
2
3
  export { JsonlParseError, appendJsonl, loadJsonl, readJsonlIds, withFileLock } from './chunk-UAYYNWYJ.js';
3
4
  export { applyWalWithFallback, isCorruptionError, openSqliteResilient, sanitizeFts5Query } from './chunk-77FOZCOT.js';
4
5
  export { withCwdMutex } from './chunk-2XLKLVVR.js';
5
- export { encodeProjectDir, transcriptPath, transcriptRoot } from './chunk-WPBTPSKQ.js';
6
+ export { encodeProjectDir, transcriptPath, transcriptRoot } from './chunk-Q56IYTBZ.js';
6
7
  export { atomicWriteJson, atomicWriteText, replaceFileAtomic } from './chunk-S2BBRVQX.js';
7
8
  import { TheokitAgentError } from './chunk-WLKFWQSJ.js';
8
9
  import './chunk-JTFYYKDT.js';
9
- import { openSync, closeSync, rmSync, readFileSync, writeSync, statSync, readSync } from 'fs';
10
+ import { readFileSync, openSync, writeSync, closeSync, statSync, readSync } from 'fs';
10
11
 
11
- var SessionBusyError = class extends TheokitAgentError {
12
- constructor(sessionPath) {
13
- super(
14
- `another process is already writing this session: ${sessionPath}. Fork it to a new id instead of appending \u2014 two writers interleave lines into a sequence that parses but is not what either process wrote.`,
15
- { code: "session_busy", isRetryable: false }
16
- );
17
- this.sessionPath = sessionPath;
18
- }
19
- sessionPath;
20
- name = "SessionBusyError";
21
- };
22
- async function acquireSessionWriter(sessionPath) {
23
- const lockPath = `${sessionPath}.lock`;
24
- let fd;
25
- try {
26
- fd = openSync(lockPath, "wx");
27
- } catch (err) {
28
- if (err.code === "EEXIST") {
29
- throw new SessionBusyError(sessionPath);
30
- }
31
- throw err;
32
- }
33
- closeSync(fd);
34
- let released = false;
35
- return {
36
- sessionPath,
37
- release: async () => {
38
- if (released) return;
39
- released = true;
40
- rmSync(lockPath, { force: true });
41
- }
42
- };
43
- }
44
12
  var LiveSessionError = class extends TheokitAgentError {
45
13
  constructor(path) {
46
14
  super(
@@ -107,6 +75,6 @@ function readJsonlTail(path, options = {}) {
107
75
  return options._stats === true ? Object.assign(out, { bytesRead }) : out;
108
76
  }
109
77
 
110
- export { LiveSessionError, SessionBusyError, acquireSessionWriter, forkTranscript, readJsonlTail };
78
+ export { LiveSessionError, forkTranscript, readJsonlTail };
111
79
  //# sourceMappingURL=persistence.js.map
112
80
  //# sourceMappingURL=persistence.js.map