@tacuchi/agent-workflow-cli 22.4.2 → 24.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/README.md +39 -3
  2. package/dist/adapters/git-cli.js +59 -17
  3. package/dist/adapters/git-cli.js.map +1 -1
  4. package/dist/adapters/node-file-system.js +55 -1
  5. package/dist/adapters/node-file-system.js.map +1 -1
  6. package/dist/adapters/postgres-readonly-tools.js +277 -0
  7. package/dist/adapters/postgres-readonly-tools.js.map +1 -0
  8. package/dist/application/artifacts-service.js +2 -2
  9. package/dist/application/artifacts-service.js.map +1 -1
  10. package/dist/application/capability/dispatcher.js +2 -2
  11. package/dist/application/capability/dispatcher.js.map +1 -1
  12. package/dist/application/capability/installed-inventory.js +2 -2
  13. package/dist/application/capability/installed-inventory.js.map +1 -1
  14. package/dist/application/capability/readiness.js +1 -1
  15. package/dist/application/capability/readiness.js.map +1 -1
  16. package/dist/application/check-branch-service.js +2 -2
  17. package/dist/application/check-branch-service.js.map +1 -1
  18. package/dist/application/checkpoint/files-touched.js +271 -0
  19. package/dist/application/checkpoint/files-touched.js.map +1 -0
  20. package/dist/application/checkpoint/markdown.js +61 -8
  21. package/dist/application/checkpoint/markdown.js.map +1 -1
  22. package/dist/application/checkpoint/state-reader.js +8 -2
  23. package/dist/application/checkpoint/state-reader.js.map +1 -1
  24. package/dist/application/checkpoint-write-service.js +16 -5
  25. package/dist/application/checkpoint-write-service.js.map +1 -1
  26. package/dist/application/claims-ledger.js +330 -0
  27. package/dist/application/claims-ledger.js.map +1 -0
  28. package/dist/application/claims-recovery.js +338 -0
  29. package/dist/application/claims-recovery.js.map +1 -0
  30. package/dist/application/database-mcp-server.js +168 -0
  31. package/dist/application/database-mcp-server.js.map +1 -0
  32. package/dist/application/database-mcp-stdio.js +137 -0
  33. package/dist/application/database-mcp-stdio.js.map +1 -0
  34. package/dist/application/database-tool-catalog.js +226 -0
  35. package/dist/application/database-tool-catalog.js.map +1 -0
  36. package/dist/application/dev-only-services.js +154 -44
  37. package/dist/application/dev-only-services.js.map +1 -1
  38. package/dist/application/dsn-reader-service.js +1 -1
  39. package/dist/application/elicitation-server.js +382 -0
  40. package/dist/application/elicitation-server.js.map +1 -0
  41. package/dist/application/elicitation-stdio.js +48 -0
  42. package/dist/application/elicitation-stdio.js.map +1 -0
  43. package/dist/application/flow/advance.js +86 -5
  44. package/dist/application/flow/advance.js.map +1 -1
  45. package/dist/application/flow/checkout-observation.js +152 -0
  46. package/dist/application/flow/checkout-observation.js.map +1 -0
  47. package/dist/application/flow/flow-service.js +72 -13
  48. package/dist/application/flow/flow-service.js.map +1 -1
  49. package/dist/application/flow/internal-actions.js +460 -1
  50. package/dist/application/flow/internal-actions.js.map +1 -1
  51. package/dist/application/flow/internal-drive.js +22 -9
  52. package/dist/application/flow/internal-drive.js.map +1 -1
  53. package/dist/application/flow/prove.js +205 -0
  54. package/dist/application/flow/prove.js.map +1 -0
  55. package/dist/application/flow/run-journey.js +15 -0
  56. package/dist/application/flow/run-journey.js.map +1 -0
  57. package/dist/application/flow/run-projection.js +15 -3
  58. package/dist/application/flow/run-projection.js.map +1 -1
  59. package/dist/application/flow/run-state-service.js +19 -4
  60. package/dist/application/flow/run-state-service.js.map +1 -1
  61. package/dist/application/flow/submit.js +250 -72
  62. package/dist/application/flow/submit.js.map +1 -1
  63. package/dist/application/generate-launch-service.js +4 -4
  64. package/dist/application/generate-launch-service.js.map +1 -1
  65. package/dist/application/git-flow-service.js +9 -0
  66. package/dist/application/git-flow-service.js.map +1 -1
  67. package/dist/application/local-proposal.js +9 -0
  68. package/dist/application/local-proposal.js.map +1 -1
  69. package/dist/application/lock-service.js +96 -28
  70. package/dist/application/lock-service.js.map +1 -1
  71. package/dist/application/logging/log-events.js +19 -3
  72. package/dist/application/logging/log-events.js.map +1 -1
  73. package/dist/application/logging/logger.js +2 -2
  74. package/dist/application/logging/logger.js.map +1 -1
  75. package/dist/application/mcp-connections-service.js +115 -41
  76. package/dist/application/mcp-connections-service.js.map +1 -1
  77. package/dist/application/mcp-doctor-service.js +98 -54
  78. package/dist/application/mcp-doctor-service.js.map +1 -1
  79. package/dist/application/mcp-entry-classification.js +60 -0
  80. package/dist/application/mcp-entry-classification.js.map +1 -0
  81. package/dist/application/mcp-host-reader.js +114 -81
  82. package/dist/application/mcp-host-reader.js.map +1 -1
  83. package/dist/application/mcp-host-receipt-service.js +399 -0
  84. package/dist/application/mcp-host-receipt-service.js.map +1 -0
  85. package/dist/application/mcp-host-receipt-store.js +122 -0
  86. package/dist/application/mcp-host-receipt-store.js.map +1 -0
  87. package/dist/application/mcp-host-receipts.js +28 -0
  88. package/dist/application/mcp-host-receipts.js.map +1 -0
  89. package/dist/application/mcp-host-writer.js +321 -126
  90. package/dist/application/mcp-host-writer.js.map +1 -1
  91. package/dist/application/mcp-launch-probe-service.js +142 -0
  92. package/dist/application/mcp-launch-probe-service.js.map +1 -0
  93. package/dist/application/mcp-migration-service.js +351 -0
  94. package/dist/application/mcp-migration-service.js.map +1 -0
  95. package/dist/application/mcp-native-host-check-service.js +154 -0
  96. package/dist/application/mcp-native-host-check-service.js.map +1 -0
  97. package/dist/application/mcp-receipt-registration-service.js +105 -0
  98. package/dist/application/mcp-receipt-registration-service.js.map +1 -0
  99. package/dist/application/mcp-receipt-removal-service.js +62 -0
  100. package/dist/application/mcp-receipt-removal-service.js.map +1 -0
  101. package/dist/application/mcp-remove-service.js +84 -21
  102. package/dist/application/mcp-remove-service.js.map +1 -1
  103. package/dist/application/mcp-scope-common.js +2 -4
  104. package/dist/application/mcp-scope-common.js.map +1 -1
  105. package/dist/application/mcp-setup-service.js +60 -21
  106. package/dist/application/mcp-setup-service.js.map +1 -1
  107. package/dist/application/mcp-stdio-probe.js +526 -0
  108. package/dist/application/mcp-stdio-probe.js.map +1 -0
  109. package/dist/application/mcp-test-connection-service.js +23 -50
  110. package/dist/application/mcp-test-connection-service.js.map +1 -1
  111. package/dist/application/merge-state-service.js +10 -16
  112. package/dist/application/merge-state-service.js.map +1 -1
  113. package/dist/application/multiroot/claude.js +34 -15
  114. package/dist/application/multiroot/claude.js.map +1 -1
  115. package/dist/application/multiroot/codex.js +9 -5
  116. package/dist/application/multiroot/codex.js.map +1 -1
  117. package/dist/application/multiroot-service.js +20 -11
  118. package/dist/application/multiroot-service.js.map +1 -1
  119. package/dist/application/parsers/project-block.js +25 -2
  120. package/dist/application/parsers/project-block.js.map +1 -1
  121. package/dist/application/paths-service.js +30 -31
  122. package/dist/application/paths-service.js.map +1 -1
  123. package/dist/application/plan-exec-batch-service.js +549 -0
  124. package/dist/application/plan-exec-batch-service.js.map +1 -0
  125. package/dist/application/plan-exec-decision-service.js +273 -0
  126. package/dist/application/plan-exec-decision-service.js.map +1 -0
  127. package/dist/application/project-md-service.js +2 -2
  128. package/dist/application/project-md-service.js.map +1 -1
  129. package/dist/application/project-md-upsert-service.js +4 -4
  130. package/dist/application/project-md-upsert-service.js.map +1 -1
  131. package/dist/application/project-tab-data.js +12 -7
  132. package/dist/application/project-tab-data.js.map +1 -1
  133. package/dist/application/release-data-service.js +2 -2
  134. package/dist/application/release-data-service.js.map +1 -1
  135. package/dist/application/resume-service.js +32 -11
  136. package/dist/application/resume-service.js.map +1 -1
  137. package/dist/application/retirement/apply.js +84 -0
  138. package/dist/application/retirement/apply.js.map +1 -1
  139. package/dist/application/retirement/attribution.js +31 -1
  140. package/dist/application/retirement/attribution.js.map +1 -1
  141. package/dist/application/retirement/graph.js +19 -1
  142. package/dist/application/retirement/graph.js.map +1 -1
  143. package/dist/application/retirement/journal.js +11 -0
  144. package/dist/application/retirement/journal.js.map +1 -1
  145. package/dist/application/retirement/prepare.js +89 -3
  146. package/dist/application/retirement/prepare.js.map +1 -1
  147. package/dist/application/retirement/preview.js +16 -0
  148. package/dist/application/retirement/preview.js.map +1 -1
  149. package/dist/application/retirement/resolve.js +65 -0
  150. package/dist/application/retirement/resolve.js.map +1 -1
  151. package/dist/application/self/host-states.js +2 -0
  152. package/dist/application/self/host-states.js.map +1 -1
  153. package/dist/application/self/install-skill.js +21 -17
  154. package/dist/application/self/install-skill.js.map +1 -1
  155. package/dist/application/self/mcp-config.js +392 -84
  156. package/dist/application/self/mcp-config.js.map +1 -1
  157. package/dist/application/self/mcp-via-state.js +62 -0
  158. package/dist/application/self/mcp-via-state.js.map +1 -0
  159. package/dist/application/self/uninstall.js +15 -5
  160. package/dist/application/self/uninstall.js.map +1 -1
  161. package/dist/application/session-close-service.js +24 -1
  162. package/dist/application/session-close-service.js.map +1 -1
  163. package/dist/application/session-create-service.js +7 -0
  164. package/dist/application/session-create-service.js.map +1 -1
  165. package/dist/application/session-resume-service.js +2 -2
  166. package/dist/application/session-resume-service.js.map +1 -1
  167. package/dist/application/sessions-service.js +2 -4
  168. package/dist/application/sessions-service.js.map +1 -1
  169. package/dist/application/skills-resolver-service.js +4 -4
  170. package/dist/application/skills-resolver-service.js.map +1 -1
  171. package/dist/application/source-boundary-policy.js +18 -7
  172. package/dist/application/source-boundary-policy.js.map +1 -1
  173. package/dist/application/source-remove-service.js +12 -1
  174. package/dist/application/source-remove-service.js.map +1 -1
  175. package/dist/application/sources-service.js +2 -2
  176. package/dist/application/sources-service.js.map +1 -1
  177. package/dist/application/status-service.js +4 -0
  178. package/dist/application/status-service.js.map +1 -1
  179. package/dist/application/visibility-doctor-service.js +1 -1
  180. package/dist/application/visibility-doctor-service.js.map +1 -1
  181. package/dist/application/workline-index-service.js +182 -27
  182. package/dist/application/workline-index-service.js.map +1 -1
  183. package/dist/application/workspace-init-service.js +89 -271
  184. package/dist/application/workspace-init-service.js.map +1 -1
  185. package/dist/application/workspace-materialization-service.js +300 -0
  186. package/dist/application/workspace-materialization-service.js.map +1 -0
  187. package/dist/application/worktree-service.js +32 -3
  188. package/dist/application/worktree-service.js.map +1 -1
  189. package/dist/cli/commands/capability.js +4 -7
  190. package/dist/cli/commands/capability.js.map +1 -1
  191. package/dist/cli/commands/claims.js +72 -0
  192. package/dist/cli/commands/claims.js.map +1 -0
  193. package/dist/cli/commands/dev-only.js +73 -25
  194. package/dist/cli/commands/dev-only.js.map +1 -1
  195. package/dist/cli/commands/flow.js +145 -42
  196. package/dist/cli/commands/flow.js.map +1 -1
  197. package/dist/cli/commands/index.js +4 -0
  198. package/dist/cli/commands/index.js.map +1 -1
  199. package/dist/cli/commands/mcp.js +752 -76
  200. package/dist/cli/commands/mcp.js.map +1 -1
  201. package/dist/cli/commands/multiroot.js +40 -7
  202. package/dist/cli/commands/multiroot.js.map +1 -1
  203. package/dist/cli/commands/resume.js +9 -3
  204. package/dist/cli/commands/resume.js.map +1 -1
  205. package/dist/cli/commands/retirement.js +9 -0
  206. package/dist/cli/commands/retirement.js.map +1 -1
  207. package/dist/cli/commands/session-create.js +1 -1
  208. package/dist/cli/commands/session-create.js.map +1 -1
  209. package/dist/cli/commands/skills.js +1 -1
  210. package/dist/cli/commands/skills.js.map +1 -1
  211. package/dist/cli/commands/status.js +72 -24
  212. package/dist/cli/commands/status.js.map +1 -1
  213. package/dist/cli/commands/tool.js +141 -0
  214. package/dist/cli/commands/tool.js.map +1 -0
  215. package/dist/cli/commands/workspace-init.js +5 -5
  216. package/dist/cli/commands/workspace-init.js.map +1 -1
  217. package/dist/cli/help-groups.js +6 -1
  218. package/dist/cli/help-groups.js.map +1 -1
  219. package/dist/cli/main.js +272 -67
  220. package/dist/cli/main.js.map +1 -1
  221. package/dist/cli/parser.js +8 -1
  222. package/dist/cli/parser.js.map +1 -1
  223. package/dist/cli/registry.js.map +1 -1
  224. package/dist/cli/render.js +15 -2
  225. package/dist/cli/render.js.map +1 -1
  226. package/dist/cli/tui/app.js +3 -2
  227. package/dist/cli/tui/app.js.map +1 -1
  228. package/dist/cli/tui/components/workspace-init-form.js +7 -7
  229. package/dist/cli/tui/components/workspace-init-form.js.map +1 -1
  230. package/dist/cli/tui/data/workflow-content.js +2 -2
  231. package/dist/cli/tui/data/workflow-content.js.map +1 -1
  232. package/dist/cli/tui/tabs/config-tab.js +4 -3
  233. package/dist/cli/tui/tabs/config-tab.js.map +1 -1
  234. package/dist/cli/tui/tabs/mcp-tab-helpers.js +72 -1
  235. package/dist/cli/tui/tabs/mcp-tab-helpers.js.map +1 -1
  236. package/dist/cli/tui/tabs/mcp-tab.js +101 -42
  237. package/dist/cli/tui/tabs/mcp-tab.js.map +1 -1
  238. package/dist/cli/tui/tabs/project-tab.js +18 -21
  239. package/dist/cli/tui/tabs/project-tab.js.map +1 -1
  240. package/dist/cli/tui/workspace-root.js +13 -0
  241. package/dist/cli/tui/workspace-root.js.map +1 -0
  242. package/dist/domain/capability/protocol.js +2 -2
  243. package/dist/domain/capability/protocol.js.map +1 -1
  244. package/dist/domain/database-tools.js +445 -0
  245. package/dist/domain/database-tools.js.map +1 -0
  246. package/dist/domain/degradation-notice.js +60 -0
  247. package/dist/domain/degradation-notice.js.map +1 -0
  248. package/dist/domain/elicitation.js +152 -0
  249. package/dist/domain/elicitation.js.map +1 -0
  250. package/dist/domain/flow/answer.js +173 -42
  251. package/dist/domain/flow/answer.js.map +1 -1
  252. package/dist/domain/flow/authority.js +184 -127
  253. package/dist/domain/flow/authority.js.map +1 -1
  254. package/dist/domain/flow/directive.js +47 -1
  255. package/dist/domain/flow/directive.js.map +1 -1
  256. package/dist/domain/flow/rules.js +7 -4
  257. package/dist/domain/flow/rules.js.map +1 -1
  258. package/dist/domain/flow/run-state.js +636 -31
  259. package/dist/domain/flow/run-state.js.map +1 -1
  260. package/dist/domain/harnesses.js +16 -0
  261. package/dist/domain/harnesses.js.map +1 -1
  262. package/dist/domain/mcp-entry.js +164 -28
  263. package/dist/domain/mcp-entry.js.map +1 -1
  264. package/dist/domain/postgres-object-search.js +320 -0
  265. package/dist/domain/postgres-object-search.js.map +1 -0
  266. package/dist/domain/redaction.js +47 -0
  267. package/dist/domain/redaction.js.map +1 -0
  268. package/dist/domain/retirement/proposal.js +1 -0
  269. package/dist/domain/retirement/proposal.js.map +1 -1
  270. package/dist/domain/structured-choice-stamp.js +32 -4
  271. package/dist/domain/structured-choice-stamp.js.map +1 -1
  272. package/dist/domain/workline-mcp-entry.js +34 -0
  273. package/dist/domain/workline-mcp-entry.js.map +1 -0
  274. package/dist/ports/postgres-tools.js +10 -0
  275. package/dist/ports/postgres-tools.js.map +1 -0
  276. package/dist/runtime/namespace-resolver.js +104 -18
  277. package/dist/runtime/namespace-resolver.js.map +1 -1
  278. package/package.json +5 -1
  279. package/skills/w/README.md +2 -2
  280. package/skills/w/SKILL.md +9 -10
  281. package/skills/w/artifacts/README.md +1 -1
  282. package/skills/w/commands/README.md +3 -3
  283. package/skills/w/commands/persist.md +3 -1
  284. package/skills/w/commands/resume.md +1 -1
  285. package/skills/w/commands/spec-new.md +2 -2
  286. package/skills/w/commands/status.md +1 -1
  287. package/skills/w/commands/workspace-init.md +9 -10
  288. package/skills/w/context/MANIFEST.json +1 -4
  289. package/skills/w/harness/HARNESS.md +1 -1
  290. package/skills/w/loops/quick-loop/LOOP.md +1 -1
  291. package/skills/w/modules/PROMPT-CONTINUITY.md +1 -1
  292. package/skills/w/modules/WORKSPACE-SCAFFOLD.md +17 -20
  293. package/skills/w/schemas/capability-descriptor.schema.json +1 -1
  294. package/dist/application/mcp-dbhub-launcher.js +0 -216
  295. package/dist/application/mcp-dbhub-launcher.js.map +0 -1
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Recovering a reservation whose owner never came back.
3
+ *
4
+ * The normal cycle needs none of this: completing, closing and cancelling all
5
+ * resolve a reservation through its owner. What is left is the run that simply
6
+ * died — no publication, no close, nothing terminal — and left a correlative held
7
+ * by a session that is never going to finish it. There is deliberately NO clock
8
+ * here: a reservation does not expire, because "it has been a while" is not
9
+ * evidence that nobody is coming, and a timer that frees a slot somebody is still
10
+ * writing into would be worse than the stranded number it fixes.
11
+ *
12
+ * So the release is explicit, authorized, and ORDERED. The order is the whole
13
+ * safety argument and it runs one way only:
14
+ *
15
+ * 1. the reservation is still exactly its own intact marker — otherwise the
16
+ * bytes are somebody's work and this is not a recovery;
17
+ * 2. a revocation is SEALED, durably and irrevocably, scoped to that one claim;
18
+ * 3. and only then is the slot released.
19
+ *
20
+ * Step 2 before step 3 is not a preference. Once the file is gone the correlative
21
+ * is eligible again, so a late sealed proposal from the dead owner — one that was
22
+ * approved before the recovery and lands after it — would write a document onto a
23
+ * number somebody else may already hold. The fence is what makes that publication
24
+ * fail instead of collide, and a fence written after the release is a window. If
25
+ * the seal cannot be written, the recovery FAILS and releases nothing: a released
26
+ * slot with no fence is the one outcome this must never produce.
27
+ *
28
+ * The revocation belongs to the CLAIM, never to the session. The owner may be
29
+ * alive and working on other things, and revoking its whole session to reclaim
30
+ * one number would destroy work to tidy up a correlative.
31
+ */
32
+ import { join } from "node:path";
33
+ import { leadingCorrelative } from "../domain/correlative.js";
34
+ import { reservationOwnerOf } from "../domain/reservation.js";
35
+ import { appendClaimEvent, claimKey, isRevoked, openOwnerOfSlot, readClaimEvents, wasPublished, } from "./claims-ledger.js";
36
+ import { withCwdLock } from "./lock-service.js";
37
+ import { semanticDigest } from "./semantic-operation/protocol.js";
38
+ import { CLOSED_MARKER, listSessionFolders } from "./session-resolver.js";
39
+ /** The identity a reservation's slot maps to, or `null` for an unattributable one. */
40
+ function claimOfSlot(slot) {
41
+ if (slot.owner === null)
42
+ return null;
43
+ return {
44
+ category: slot.category,
45
+ correlative: slot.correlative,
46
+ name: slot.name,
47
+ owner: slot.owner,
48
+ };
49
+ }
50
+ /**
51
+ * The slot a numbered file is, or `null` when it is a document.
52
+ *
53
+ * The LEDGER decides before the bytes do, and the order is the fix for a real
54
+ * defect: classifying from bytes alone made every empty numbered file an
55
+ * ownerless legacy placeholder — releasable with no fence — including a published
56
+ * document whose content happens to be empty, and including a reservation whose
57
+ * marker somebody emptied. In the first case a recovery deleted a published
58
+ * document; in the second it gave a correlative back while its real owner still
59
+ * held an open claim on it. Both times the record held the answer and only the
60
+ * bytes were consulted.
61
+ */
62
+ function slotOf(category, fileName, text, events, activeOwners) {
63
+ const correlative = leadingCorrelative(fileName);
64
+ if (correlative === null)
65
+ return null;
66
+ const name = fileName.slice(correlative.length + 1);
67
+ const slot = { category, correlative, name };
68
+ // Published is terminal and terminal means "a document lives here": whatever
69
+ // its bytes look like, it is not a slot anybody may free.
70
+ if (wasPublished(events, slot))
71
+ return null;
72
+ const markerOwner = reservationOwnerOf(text);
73
+ const base = { path: `docs/${category}/${fileName}`, category, correlative, name };
74
+ if (markerOwner !== null) {
75
+ return {
76
+ ...base,
77
+ kind: "reservation",
78
+ owner: markerOwner,
79
+ ownerActive: activeOwners.has(markerOwner),
80
+ revoked: false,
81
+ intact: true,
82
+ };
83
+ }
84
+ // Real content and no marker: a document, by its bytes this time.
85
+ if (text.trim().length > 0)
86
+ return null;
87
+ // Empty. If the ledger still holds an open claim on this slot, it is that
88
+ // owner's reservation with damaged bytes — not nobody's placeholder.
89
+ const open = openOwnerOfSlot(events, slot);
90
+ if (open !== null) {
91
+ return {
92
+ ...base,
93
+ kind: "reservation",
94
+ owner: open.owner,
95
+ ownerActive: activeOwners.has(open.owner),
96
+ revoked: false,
97
+ intact: false,
98
+ };
99
+ }
100
+ return {
101
+ ...base,
102
+ kind: "legacy-placeholder",
103
+ owner: null,
104
+ ownerActive: null,
105
+ revoked: false,
106
+ intact: false,
107
+ };
108
+ }
109
+ /**
110
+ * Walk `docs/` into `into`, throwing on the first unreadable thing.
111
+ *
112
+ * The accumulator is the caller's so a failure still reports what it managed to
113
+ * see: an unreadable category is a different fact from an empty one, and folding
114
+ * the two would let the board say "no reservations" about a directory nobody
115
+ * could open.
116
+ */
117
+ async function walkSlots(fs, docs, events, activeOwners, into) {
118
+ for (const category of await fs.list(docs)) {
119
+ if (category.type !== "dir")
120
+ continue;
121
+ for (const entry of await fs.list(category.path)) {
122
+ // The filename decides whether the bytes are worth reading at all. Reading
123
+ // every file in every docs/ subdirectory to then discard most of them on the
124
+ // first line of `slotOf` made a scan that runs on EVERY board projection pay
125
+ // for the whole corpus.
126
+ if (entry.type !== "file" || leadingCorrelative(entry.name) === null)
127
+ continue;
128
+ const slot = slotOf(category.name, entry.name, await fs.readText(entry.path), events, activeOwners);
129
+ if (slot === null)
130
+ continue;
131
+ const claim = claimOfSlot(slot);
132
+ slot.revoked = claim !== null && isRevoked(events, claim);
133
+ into.push(slot);
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * The session folders that are still open.
139
+ *
140
+ * Read once per scan, because the sanctioned action for a slot depends on it: a
141
+ * reservation of a live session is that session's to finish or close, and only a
142
+ * slot nobody is finishing may be recovered.
143
+ */
144
+ async function activeSessionFolders(fs, paths) {
145
+ const active = new Set();
146
+ try {
147
+ for (const folder of await listSessionFolders(fs, paths.cwdSessionsDir())) {
148
+ if (await fs.exists(join(folder.path, CLOSED_MARKER)))
149
+ continue;
150
+ active.add(folder.name);
151
+ }
152
+ }
153
+ catch {
154
+ // An unreadable sessions dir means nobody can be proven alive. The fallback is
155
+ // the conservative one: with no owner known to be active, no slot is offered
156
+ // for recovery on the strength of liveness it could not check.
157
+ }
158
+ return active;
159
+ }
160
+ /**
161
+ * Every reservation and legacy placeholder under `docs/`.
162
+ *
163
+ * Walks each immediate subdirectory rather than a list of categories, for the
164
+ * same reason the close's sweep does: the claim mechanism is category-agnostic,
165
+ * and a hardcoded list is a second place to update the day something else claims
166
+ * a number.
167
+ *
168
+ * A failure comes back WITH its reason and with whatever was already seen. An
169
+ * unreadable `docs/` is not an empty one, and swallowing the difference would let
170
+ * the board answer "no reservations" about a directory nobody could open.
171
+ */
172
+ export async function scanSlots(fs, paths) {
173
+ const docs = join(paths.workspaceDir(), "docs");
174
+ const slots = [];
175
+ const ledger = await readClaimEvents(fs, paths);
176
+ const activeOwners = await activeSessionFolders(fs, paths);
177
+ const sorted = () => slots.sort((a, b) => a.path.localeCompare(b.path));
178
+ if (!(await fs.exists(docs)))
179
+ return { slots };
180
+ try {
181
+ await walkSlots(fs, docs, ledger.events, activeOwners, slots);
182
+ }
183
+ catch (error) {
184
+ return {
185
+ slots: sorted(),
186
+ error: `no se pudo revisar docs/ en busca de reservas: ${error instanceof Error ? error.message : String(error)}`,
187
+ };
188
+ }
189
+ return { slots: sorted() };
190
+ }
191
+ function sealRecovery(body) {
192
+ return { ...body, digest: semanticDigest(body) };
193
+ }
194
+ /** The proposal for one slot, or why there is none. */
195
+ export async function previewRecovery(fs, paths, target) {
196
+ const scan = await scanSlots(fs, paths);
197
+ const slot = scan.slots.find((candidate) => candidate.path === target);
198
+ if (slot === undefined) {
199
+ return {
200
+ error: `'${target}' no es una reserva ni un placeholder legacy de este workspace`,
201
+ action: scan.error ??
202
+ "corré 'aw claims' para ver los correlativos recuperables; un documento publicado no se recupera",
203
+ };
204
+ }
205
+ return {
206
+ proposal: sealRecovery({
207
+ version: 1,
208
+ target: slot.path,
209
+ kind: slot.kind,
210
+ claim: claimOfSlot(slot),
211
+ requires_no_producer_confirmation: !slot.intact,
212
+ resuming: slot.revoked,
213
+ }),
214
+ };
215
+ }
216
+ /**
217
+ * Seal the fence, then release — and never the other way round.
218
+ *
219
+ * The whole thing runs under the workspace lock, which is not decoration: the
220
+ * check that the slot is still what the preview saw, the seal and the removal are
221
+ * three separate awaits, and without the lock a sanctioned publication could pass
222
+ * its own fence, take the lock, write its document and have this function delete
223
+ * it a moment later — leaving the correlative recorded as both spent-forever and
224
+ * eligible-again, with the published document silently gone. Its two siblings hold
225
+ * the lock across exactly this span for exactly this reason.
226
+ *
227
+ * It also re-derives its own proposal inside the lock instead of trusting the
228
+ * digest it was handed: the approval proves a person authorized THIS recovery, not
229
+ * that the world still looks the way it did when they read it.
230
+ */
231
+ export async function applyRecovery(fs, paths, input) {
232
+ const outcome = await withCwdLock(fs, paths, () => recoverUnderLock(fs, paths, input));
233
+ if ("error" in outcome && "action" in outcome)
234
+ return outcome;
235
+ if ("error" in outcome) {
236
+ return {
237
+ error: `no se pudo tomar el candado del workspace: ${outcome.error}`,
238
+ action: "esperá a que otro flujo lo libere y volvé a aplicar la recuperación",
239
+ };
240
+ }
241
+ return outcome;
242
+ }
243
+ async function recoverUnderLock(fs, paths, input) {
244
+ const preview = await previewRecovery(fs, paths, input.target);
245
+ if ("error" in preview)
246
+ return preview;
247
+ const proposal = preview.proposal;
248
+ if (proposal.digest !== input.approval) {
249
+ return {
250
+ error: "la aprobación no corresponde a la recuperación vigente",
251
+ action: `volvé a mirarla con 'aw claims recover ${input.target}' y aprobá el digest que devuelve`,
252
+ };
253
+ }
254
+ if (proposal.requires_no_producer_confirmation && input.noProducerConfirmed !== true) {
255
+ return {
256
+ error: proposal.kind === "legacy-placeholder"
257
+ ? `'${input.target}' es un placeholder legacy ambiguo: sus bytes no prueban que nadie vaya a escribirlo`
258
+ : `'${input.target}' ya no tiene el marcador intacto de su dueño: alguien escribió ahí`,
259
+ action: "confirmá explícitamente que no queda productor capaz de escribir ese destino antes de liberar su correlativo",
260
+ };
261
+ }
262
+ const claim = proposal.claim;
263
+ if (claim !== null && !proposal.resuming) {
264
+ // The fence FIRST. If this throws, nothing was released and the slot is
265
+ // exactly as it was: the recovery failed, which is the correct outcome.
266
+ // Releasing first would leave a window where the correlative is eligible and
267
+ // a late sealed publication can still land on it.
268
+ await appendClaimEvent(fs, paths, {
269
+ at: new Date().toISOString(),
270
+ event: "revoked",
271
+ claim,
272
+ cause: `aw claims recover: recuperación autorizada de ${proposal.target}`,
273
+ });
274
+ }
275
+ // Recorded before the file goes, same reason as the close's release: a slot
276
+ // freed with no line saying so is the state this ledger exists to end. The
277
+ // identity is the SLOT's, so it joins to its own history by claimKey — an
278
+ // ownerless placeholder still records the category, correlative and name it
279
+ // gave back, and only its owner field says nobody held it.
280
+ await appendClaimEvent(fs, paths, {
281
+ at: new Date().toISOString(),
282
+ event: "released",
283
+ claim: claim ?? {
284
+ category: slotIdentityOf(proposal.target).category,
285
+ correlative: slotIdentityOf(proposal.target).correlative,
286
+ name: slotIdentityOf(proposal.target).name,
287
+ owner: LEGACY_OWNERLESS,
288
+ },
289
+ cause: claim === null
290
+ ? "aw claims recover: placeholder legacy liberado con confirmación explícita de que no queda productor"
291
+ : `aw claims recover: liberado tras revocar ${claimKey(claim)}`,
292
+ });
293
+ await fs.remove(join(paths.workspaceDir(), proposal.target));
294
+ return {
295
+ applied: {
296
+ target: proposal.target,
297
+ revoked: claim,
298
+ released: true,
299
+ resumed: proposal.resuming,
300
+ digest: proposal.digest,
301
+ },
302
+ };
303
+ }
304
+ /** The owner field of a slot that never had one. Never a session folder. */
305
+ const LEGACY_OWNERLESS = "(placeholder legacy sin dueño)";
306
+ /** `docs/<cat>/<NNN>-<name>` split the same way every ledger record splits it. */
307
+ function slotIdentityOf(target) {
308
+ const parts = target.split("/");
309
+ const category = parts[1] ?? "";
310
+ const file = parts[2] ?? "";
311
+ const correlative = leadingCorrelative(file) ?? "";
312
+ return {
313
+ category,
314
+ correlative,
315
+ name: correlative.length > 0 ? file.slice(correlative.length + 1) : file,
316
+ };
317
+ }
318
+ /**
319
+ * The one action this slot sanctions, named per slot.
320
+ *
321
+ * A reservation of a live session is resumed or closed by its owner; only a slot
322
+ * nobody is finishing gets recovered. And a slot whose bytes are not its own
323
+ * intact marker carries the confirmation flag in the command itself, so the
324
+ * operator sees the extra assertion being asked of them before they type it.
325
+ */
326
+ export function sanctionedActionFor(slot) {
327
+ // A live owner's reservation is NOT a recovery candidate. Naming the recovery
328
+ // here was destructive: the board handed the running session the one command
329
+ // that revokes its own slot irrevocably, and the flow reads this very field as
330
+ // "the sanctioned next command". Closing the owner releases an intact
331
+ // reservation as part of closing, which is the action that actually resolves it.
332
+ if (slot.ownerActive === true && slot.owner !== null) {
333
+ return `aw session-close --code ${slot.owner}`;
334
+ }
335
+ const confirm = slot.intact ? "" : " --confirm-no-producer";
336
+ return `aw claims recover ${slot.path}${confirm}`;
337
+ }
338
+ //# sourceMappingURL=claims-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims-recovery.js","sourceRoot":"","sources":["../../src/application/claims-recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,168 @@
1
+ import { encodeToolResponse, toolFailure } from "../domain/database-tools.js";
2
+ const PROTOCOL_VERSION = "2025-06-18";
3
+ /** JSON-RPC adapter over the shared catalog. It owns no tool rules. */
4
+ export function createDatabaseMcpServer(deps) {
5
+ let lifecycle = "new";
6
+ const activeCalls = new Map();
7
+ const cancelledBeforeStart = new Set();
8
+ function reply(id, result) {
9
+ if (!isJsonRpcResponseId(id))
10
+ return;
11
+ deps.send({ jsonrpc: "2.0", id, result });
12
+ }
13
+ function replyError(id, code, message) {
14
+ if (!isJsonRpcResponseId(id))
15
+ return;
16
+ deps.send({ jsonrpc: "2.0", id, error: { code, message } });
17
+ }
18
+ function toolResult(response) {
19
+ return {
20
+ content: [{ type: "text", text: encodeToolResponse(response), mimeType: "application/json" }],
21
+ ...(response.success ? {} : { isError: true }),
22
+ };
23
+ }
24
+ async function dispatchToolCall(message) {
25
+ const params = isRecord(message.params) ? message.params : {};
26
+ const name = typeof params.name === "string" ? params.name : "";
27
+ const requestKey = requestIdKey(message.id);
28
+ // tools/call is a request, not a notification. Rejecting an id-less call
29
+ // prevents an untracked database operation that the client cannot cancel.
30
+ if (requestKey === undefined) {
31
+ replyError(message.id, -32600, "tools/call requiere un id de solicitud válido");
32
+ return true;
33
+ }
34
+ if (cancelledBeforeStart.delete(requestKey)) {
35
+ reply(message.id, toolResult(toolFailure("QUERY_CANCELLED", "La consulta fue cancelada.")));
36
+ return true;
37
+ }
38
+ const controller = new AbortController();
39
+ activeCalls.set(requestKey, controller);
40
+ try {
41
+ const outcome = await deps.catalog.call({
42
+ tool: name,
43
+ connection: deps.connection,
44
+ input: params.arguments,
45
+ signal: controller.signal,
46
+ });
47
+ reply(message.id, toolResult(outcome.response));
48
+ }
49
+ catch {
50
+ reply(message.id, toolResult(toolFailure("TOOL_EXECUTION_FAILED", "La tool no pudo completar la solicitud.")));
51
+ }
52
+ finally {
53
+ if (activeCalls.get(requestKey) === controller) {
54
+ activeCalls.delete(requestKey);
55
+ }
56
+ }
57
+ return true;
58
+ }
59
+ async function cancelToolCall(message) {
60
+ const params = isRecord(message.params) ? message.params : {};
61
+ const key = requestIdKey(params.requestId);
62
+ if (key === undefined)
63
+ return true;
64
+ const active = activeCalls.get(key);
65
+ if (active !== undefined) {
66
+ active.abort();
67
+ return true;
68
+ }
69
+ // A stdio transport may receive cancellation while an earlier call keeps a
70
+ // later request queued. Retain bounded intent so that later call never
71
+ // reaches PostgreSQL once it starts.
72
+ cancelledBeforeStart.add(key);
73
+ if (cancelledBeforeStart.size > 1_000) {
74
+ const oldest = cancelledBeforeStart.values().next().value;
75
+ if (oldest !== undefined)
76
+ cancelledBeforeStart.delete(oldest);
77
+ }
78
+ return true;
79
+ }
80
+ async function initialize(message) {
81
+ if (lifecycle !== "new") {
82
+ replyError(message.id, -32600, "initialize sólo puede llamarse una vez por sesión");
83
+ return true;
84
+ }
85
+ lifecycle = "awaiting-initialized";
86
+ reply(message.id, {
87
+ protocolVersion: PROTOCOL_VERSION,
88
+ capabilities: { tools: {} },
89
+ serverInfo: { name: "agent-workflow-db", version: "1" },
90
+ });
91
+ return true;
92
+ }
93
+ async function initialized() {
94
+ if (lifecycle === "awaiting-initialized") {
95
+ lifecycle = "ready";
96
+ await deps.onHostLoadObserved?.();
97
+ }
98
+ return true;
99
+ }
100
+ function readyOrReply(message) {
101
+ if (lifecycle === "ready")
102
+ return true;
103
+ replyError(message.id, -32002, "Server not initialized");
104
+ return false;
105
+ }
106
+ async function listTools(message) {
107
+ if (!readyOrReply(message))
108
+ return true;
109
+ try {
110
+ const list = deps.catalog.list(deps.connection);
111
+ reply(message.id, "tools" in list.payload ? list.payload : toolResult(list.payload));
112
+ }
113
+ catch {
114
+ reply(message.id, toolResult(toolFailure("MCP_CONNECTION_INVALID", "No se pudo resolver la conexión solicitada.")));
115
+ }
116
+ return true;
117
+ }
118
+ async function callTool(message) {
119
+ if (!readyOrReply(message))
120
+ return true;
121
+ return await dispatchToolCall(message);
122
+ }
123
+ const handlers = {
124
+ initialize,
125
+ "notifications/initialized": async () => await initialized(),
126
+ "notifications/cancelled": async (message) => await cancelToolCall(message),
127
+ "tools/list": listTools,
128
+ "tools/call": callTool,
129
+ };
130
+ return {
131
+ async handle(message) {
132
+ if (!isJsonRpcEnvelope(message))
133
+ return false;
134
+ const handler = handlers[message.method];
135
+ if (handler !== undefined)
136
+ return await handler(message);
137
+ replyError(message.id, -32601, "Method not found");
138
+ return true;
139
+ },
140
+ };
141
+ }
142
+ /** Accept only envelopes for which every response remains valid JSON-RPC. */
143
+ function isJsonRpcEnvelope(value) {
144
+ return (isRecord(value) &&
145
+ value.jsonrpc === "2.0" &&
146
+ typeof value.method === "string" &&
147
+ isJsonRpcId(value.id));
148
+ }
149
+ function isJsonRpcId(value) {
150
+ return value === undefined || isJsonRpcResponseId(value);
151
+ }
152
+ function isJsonRpcResponseId(value) {
153
+ return value === null || typeof value === "string" || isFiniteId(value);
154
+ }
155
+ function isFiniteId(value) {
156
+ return typeof value === "number" && Number.isFinite(value);
157
+ }
158
+ function requestIdKey(value) {
159
+ if (typeof value === "string")
160
+ return `string:${value}`;
161
+ if (typeof value === "number" && Number.isFinite(value))
162
+ return `number:${value}`;
163
+ return undefined;
164
+ }
165
+ function isRecord(value) {
166
+ return value !== null && typeof value === "object" && !Array.isArray(value);
167
+ }
168
+ //# sourceMappingURL=database-mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-mcp-server.js","sourceRoot":"","sources":["../../src/application/database-mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import { StringDecoder } from "node:string_decoder";
2
+ import { MAX_TOOL_TRANSPORT_INPUT_BYTES } from "../domain/database-tools.js";
3
+ import { createDatabaseMcpServer } from "./database-mcp-server.js";
4
+ // The RPC envelope is allowed a small fixed overhead beyond the canonical 1
5
+ // MiB tool input. The shared catalog still makes the exact input decision.
6
+ const MAX_MCP_INPUT_FRAME_BYTES = MAX_TOOL_TRANSPORT_INPUT_BYTES;
7
+ /**
8
+ * Line-framed stdio transport. stdout is exclusively JSON-RPC; malformed input
9
+ * is diagnosed on stderr and cannot corrupt a later valid request.
10
+ */
11
+ export function runDatabaseMcpStdio(deps) {
12
+ const server = createDatabaseMcpServer({
13
+ send: (message) => deps.output.write(`${JSON.stringify(message)}\n`),
14
+ catalog: deps.catalog,
15
+ connection: deps.connection,
16
+ ...(deps.onHostLoadObserved === undefined
17
+ ? {}
18
+ : { onHostLoadObserved: deps.onHostLoadObserved }),
19
+ });
20
+ const frames = {
21
+ buffer: "",
22
+ bufferedBytes: 0,
23
+ discardingOversizedLine: false,
24
+ decoder: new StringDecoder("utf8"),
25
+ };
26
+ let queue = Promise.resolve();
27
+ function enqueue(line) {
28
+ let message;
29
+ try {
30
+ message = JSON.parse(line);
31
+ }
32
+ catch {
33
+ deps.diagnostics.write("aw mcp serve-db: línea JSON-RPC inválida descartada\n");
34
+ return;
35
+ }
36
+ // Cancellation must not wait behind a running database call in the serial
37
+ // queue. All other frames retain order, including the lifecycle sequence.
38
+ if (isCancellationNotification(message)) {
39
+ void server.handle(message).catch(() => {
40
+ deps.diagnostics.write("aw mcp serve-db: línea JSON-RPC inválida descartada\n");
41
+ });
42
+ return;
43
+ }
44
+ queue = queue.then(async () => {
45
+ try {
46
+ await server.handle(message);
47
+ }
48
+ catch {
49
+ deps.diagnostics.write("aw mcp serve-db: línea JSON-RPC inválida descartada\n");
50
+ }
51
+ });
52
+ }
53
+ return new Promise((resolve) => {
54
+ let ended = false;
55
+ const finish = () => {
56
+ if (ended)
57
+ return;
58
+ ended = true;
59
+ // An unterminated tail is not a JSON-RPC frame. Do not enqueue it, but
60
+ // flush the decoder so no multibyte bytes remain retained by the stream.
61
+ if (!frames.discardingOversizedLine)
62
+ frames.buffer += frames.decoder.end();
63
+ void queue.then(() => resolve());
64
+ };
65
+ deps.input.on("data", (chunk) => {
66
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
67
+ enqueueConsumption(consumeChunk(frames, bytes), deps, enqueue);
68
+ });
69
+ deps.input.on("end", finish);
70
+ deps.input.on("close", finish);
71
+ });
72
+ }
73
+ function isCancellationNotification(message) {
74
+ return (message !== null &&
75
+ typeof message === "object" &&
76
+ !Array.isArray(message) &&
77
+ message.method === "notifications/cancelled");
78
+ }
79
+ /**
80
+ * Splits raw bytes at newline boundaries before decoding. This matters for an
81
+ * oversized frame already terminated in the same chunk: appending/splitting a
82
+ * decoded 100 MiB string merely to reject it would defeat the input bound.
83
+ */
84
+ function consumeChunk(state, bytes) {
85
+ const frames = [];
86
+ let oversizedFrames = 0;
87
+ let offset = 0;
88
+ while (offset < bytes.length) {
89
+ const newline = bytes.indexOf(0x0a, offset);
90
+ const end = newline < 0 ? bytes.length : newline + 1;
91
+ const segment = bytes.subarray(offset, end);
92
+ if (state.discardingOversizedLine) {
93
+ if (newline >= 0) {
94
+ state.discardingOversizedLine = false;
95
+ state.decoder = new StringDecoder("utf8");
96
+ }
97
+ offset = end;
98
+ continue;
99
+ }
100
+ if (state.bufferedBytes + segment.length > MAX_MCP_INPUT_FRAME_BYTES) {
101
+ state.buffer = "";
102
+ state.bufferedBytes = 0;
103
+ state.decoder = new StringDecoder("utf8");
104
+ state.discardingOversizedLine = newline < 0;
105
+ oversizedFrames += 1;
106
+ offset = end;
107
+ continue;
108
+ }
109
+ state.bufferedBytes += segment.length;
110
+ state.buffer += state.decoder.write(segment);
111
+ if (newline >= 0)
112
+ consumeCompleteLine(state, frames);
113
+ offset = end;
114
+ }
115
+ return { frames, oversizedFrames };
116
+ }
117
+ function consumeCompleteLine(state, frames) {
118
+ const newline = state.buffer.indexOf("\n");
119
+ if (newline < 0)
120
+ return;
121
+ const trimmed = state.buffer.slice(0, newline).replace(/\r$/, "").trim();
122
+ state.buffer = state.buffer.slice(newline + 1);
123
+ state.bufferedBytes = 0;
124
+ if (trimmed.length > 0)
125
+ frames.push(trimmed);
126
+ }
127
+ function reportOversizedFrames(output, count) {
128
+ for (let index = 0; index < count; index += 1) {
129
+ output.write("aw mcp serve-db: frame JSON-RPC demasiado grande descartado\n");
130
+ }
131
+ }
132
+ function enqueueConsumption(consumed, deps, enqueue) {
133
+ reportOversizedFrames(deps.diagnostics, consumed.oversizedFrames);
134
+ for (const frame of consumed.frames)
135
+ enqueue(frame);
136
+ }
137
+ //# sourceMappingURL=database-mcp-stdio.js.map