@oscharko-dev/keiko-server 0.2.8 → 0.2.9

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 (281) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/chat-handlers.d.ts +18 -2
  3. package/dist/chat-handlers.d.ts.map +1 -1
  4. package/dist/chat-handlers.js +185 -3
  5. package/dist/command-runner-errors.d.ts +17 -0
  6. package/dist/command-runner-errors.d.ts.map +1 -0
  7. package/dist/command-runner-errors.js +37 -0
  8. package/dist/command-runner-evidence.d.ts +23 -0
  9. package/dist/command-runner-evidence.d.ts.map +1 -0
  10. package/dist/command-runner-evidence.js +69 -0
  11. package/dist/command-runner-routes.d.ts +7 -0
  12. package/dist/command-runner-routes.d.ts.map +1 -0
  13. package/dist/command-runner-routes.js +175 -0
  14. package/dist/command-runner.d.ts +29 -0
  15. package/dist/command-runner.d.ts.map +1 -0
  16. package/dist/command-runner.js +348 -0
  17. package/dist/conversation-prompt.d.ts +2 -2
  18. package/dist/conversation-prompt.d.ts.map +1 -1
  19. package/dist/conversation-prompt.js +17 -1
  20. package/dist/csp.d.ts.map +1 -1
  21. package/dist/csp.js +3 -0
  22. package/dist/deps.d.ts +27 -1
  23. package/dist/deps.d.ts.map +1 -1
  24. package/dist/deps.js +288 -13
  25. package/dist/discussion-prompt.d.ts +4 -0
  26. package/dist/discussion-prompt.d.ts.map +1 -0
  27. package/dist/discussion-prompt.js +19 -0
  28. package/dist/editor/agentActionAudit.d.ts +18 -0
  29. package/dist/editor/agentActionAudit.d.ts.map +1 -0
  30. package/dist/editor/agentActionAudit.js +80 -0
  31. package/dist/editor/agentRoutes.d.ts +1 -0
  32. package/dist/editor/agentRoutes.d.ts.map +1 -1
  33. package/dist/editor/agentRoutes.js +292 -55
  34. package/dist/editor/agentSessionRegistry.d.ts +35 -0
  35. package/dist/editor/agentSessionRegistry.d.ts.map +1 -0
  36. package/dist/editor/agentSessionRegistry.js +243 -0
  37. package/dist/editor/completionRoutes.d.ts.map +1 -1
  38. package/dist/editor/completionRoutes.js +5 -10
  39. package/dist/editor/languageRoutes.d.ts +12 -1
  40. package/dist/editor/languageRoutes.d.ts.map +1 -1
  41. package/dist/editor/languageRoutes.js +71 -8
  42. package/dist/editor/languageService.d.ts +3 -2
  43. package/dist/editor/languageService.d.ts.map +1 -1
  44. package/dist/editor/languageService.js +41 -3
  45. package/dist/editor/languageServiceHost.d.ts.map +1 -1
  46. package/dist/editor/languageServiceHost.js +2 -2
  47. package/dist/editor/lsp/hostLanguageOperation.d.ts +17 -0
  48. package/dist/editor/lsp/hostLanguageOperation.d.ts.map +1 -0
  49. package/dist/editor/lsp/hostLanguageOperation.js +436 -0
  50. package/dist/editor/lsp/hostLanguageProviders.d.ts +26 -0
  51. package/dist/editor/lsp/hostLanguageProviders.d.ts.map +1 -0
  52. package/dist/editor/lsp/hostLanguageProviders.js +161 -0
  53. package/dist/editor/lsp/lspFrameCodec.d.ts +13 -0
  54. package/dist/editor/lsp/lspFrameCodec.d.ts.map +1 -0
  55. package/dist/editor/lsp/lspFrameCodec.js +164 -0
  56. package/dist/editor/lsp/lspJsonRpcClient.d.ts +34 -0
  57. package/dist/editor/lsp/lspJsonRpcClient.d.ts.map +1 -0
  58. package/dist/editor/lsp/lspJsonRpcClient.js +173 -0
  59. package/dist/editor/lsp/lspLanguageProvider.d.ts +7 -0
  60. package/dist/editor/lsp/lspLanguageProvider.d.ts.map +1 -0
  61. package/dist/editor/lsp/lspLanguageProvider.js +29 -0
  62. package/dist/editor/lsp/lspLifecycleLedger.d.ts +5 -0
  63. package/dist/editor/lsp/lspLifecycleLedger.d.ts.map +1 -0
  64. package/dist/editor/lsp/lspLifecycleLedger.js +37 -0
  65. package/dist/editor/lsp/lspNodeAdapter.d.ts +31 -0
  66. package/dist/editor/lsp/lspNodeAdapter.d.ts.map +1 -0
  67. package/dist/editor/lsp/lspNodeAdapter.js +230 -0
  68. package/dist/editor/lsp/lspProcessManager.d.ts +24 -0
  69. package/dist/editor/lsp/lspProcessManager.d.ts.map +1 -0
  70. package/dist/editor/lsp/lspProcessManager.js +255 -0
  71. package/dist/editor/lsp/lspRestartThrottle.d.ts +6 -0
  72. package/dist/editor/lsp/lspRestartThrottle.d.ts.map +1 -0
  73. package/dist/editor/lsp/lspRestartThrottle.js +24 -0
  74. package/dist/editor/lsp/lspStatusRoute.d.ts +8 -0
  75. package/dist/editor/lsp/lspStatusRoute.d.ts.map +1 -0
  76. package/dist/editor/lsp/lspStatusRoute.js +22 -0
  77. package/dist/editor/lsp/lspTransport.d.ts +19 -0
  78. package/dist/editor/lsp/lspTransport.d.ts.map +1 -0
  79. package/dist/editor/lsp/lspTransport.js +55 -0
  80. package/dist/editor/lsp/testing/fakeLspProcess.d.ts +23 -0
  81. package/dist/editor/lsp/testing/fakeLspProcess.d.ts.map +1 -0
  82. package/dist/editor/lsp/testing/fakeLspProcess.js +132 -0
  83. package/dist/files.d.ts +45 -0
  84. package/dist/files.d.ts.map +1 -1
  85. package/dist/files.js +631 -7
  86. package/dist/gateway-readiness.js +3 -3
  87. package/dist/gateway-setup.d.ts +2 -0
  88. package/dist/gateway-setup.d.ts.map +1 -1
  89. package/dist/gateway-setup.js +275 -11
  90. package/dist/gitDelivery/actionSheetProjection.d.ts +30 -0
  91. package/dist/gitDelivery/actionSheetProjection.d.ts.map +1 -0
  92. package/dist/gitDelivery/actionSheetProjection.js +206 -0
  93. package/dist/gitDelivery/actionSheetRoutes.d.ts +29 -0
  94. package/dist/gitDelivery/actionSheetRoutes.d.ts.map +1 -0
  95. package/dist/gitDelivery/actionSheetRoutes.js +293 -0
  96. package/dist/gitDelivery/agentOperationsRoutes.d.ts +33 -0
  97. package/dist/gitDelivery/agentOperationsRoutes.d.ts.map +1 -0
  98. package/dist/gitDelivery/agentOperationsRoutes.js +405 -0
  99. package/dist/gitDelivery/commitRoutes.d.ts +23 -0
  100. package/dist/gitDelivery/commitRoutes.d.ts.map +1 -0
  101. package/dist/gitDelivery/commitRoutes.js +204 -0
  102. package/dist/gitDelivery/evidenceRoutes.d.ts +9 -0
  103. package/dist/gitDelivery/evidenceRoutes.d.ts.map +1 -0
  104. package/dist/gitDelivery/evidenceRoutes.js +101 -0
  105. package/dist/gitDelivery/execution.d.ts +38 -0
  106. package/dist/gitDelivery/execution.d.ts.map +1 -0
  107. package/dist/gitDelivery/execution.js +117 -0
  108. package/dist/gitDelivery/localMutationRoutes.d.ts +30 -0
  109. package/dist/gitDelivery/localMutationRoutes.d.ts.map +1 -0
  110. package/dist/gitDelivery/localMutationRoutes.js +165 -0
  111. package/dist/gitDelivery/mergeExecution.d.ts +63 -0
  112. package/dist/gitDelivery/mergeExecution.d.ts.map +1 -0
  113. package/dist/gitDelivery/mergeExecution.js +168 -0
  114. package/dist/gitDelivery/mergeRoutes.d.ts +12 -0
  115. package/dist/gitDelivery/mergeRoutes.d.ts.map +1 -0
  116. package/dist/gitDelivery/mergeRoutes.js +218 -0
  117. package/dist/gitDelivery/mutationEvidenceLedger.d.ts +23 -0
  118. package/dist/gitDelivery/mutationEvidenceLedger.d.ts.map +1 -0
  119. package/dist/gitDelivery/mutationEvidenceLedger.js +87 -0
  120. package/dist/gitDelivery/prExecution.d.ts +54 -0
  121. package/dist/gitDelivery/prExecution.d.ts.map +1 -0
  122. package/dist/gitDelivery/prExecution.js +192 -0
  123. package/dist/gitDelivery/prRoutes.d.ts +12 -0
  124. package/dist/gitDelivery/prRoutes.d.ts.map +1 -0
  125. package/dist/gitDelivery/prRoutes.js +256 -0
  126. package/dist/gitDelivery/pushExecution.d.ts +43 -0
  127. package/dist/gitDelivery/pushExecution.d.ts.map +1 -0
  128. package/dist/gitDelivery/pushExecution.js +124 -0
  129. package/dist/gitDelivery/pushRoutes.d.ts +12 -0
  130. package/dist/gitDelivery/pushRoutes.d.ts.map +1 -0
  131. package/dist/gitDelivery/pushRoutes.js +200 -0
  132. package/dist/gitDelivery/requestGuards.d.ts +15 -0
  133. package/dist/gitDelivery/requestGuards.d.ts.map +1 -0
  134. package/dist/gitDelivery/requestGuards.js +97 -0
  135. package/dist/gitDelivery/syncEvidence.d.ts +37 -0
  136. package/dist/gitDelivery/syncEvidence.d.ts.map +1 -0
  137. package/dist/gitDelivery/syncEvidence.js +85 -0
  138. package/dist/gitDelivery/syncExecution.d.ts +30 -0
  139. package/dist/gitDelivery/syncExecution.d.ts.map +1 -0
  140. package/dist/gitDelivery/syncExecution.js +266 -0
  141. package/dist/gitDelivery/syncRoutes.d.ts +13 -0
  142. package/dist/gitDelivery/syncRoutes.d.ts.map +1 -0
  143. package/dist/gitDelivery/syncRoutes.js +200 -0
  144. package/dist/gitPorcelainStatus.d.ts +15 -0
  145. package/dist/gitPorcelainStatus.d.ts.map +1 -0
  146. package/dist/gitPorcelainStatus.js +104 -0
  147. package/dist/gitRepositoryReads.d.ts +10 -0
  148. package/dist/gitRepositoryReads.d.ts.map +1 -0
  149. package/dist/gitRepositoryReads.js +314 -0
  150. package/dist/gitRepositoryRoutes.d.ts +7 -0
  151. package/dist/gitRepositoryRoutes.d.ts.map +1 -0
  152. package/dist/gitRepositoryRoutes.js +221 -0
  153. package/dist/gitRoutes.d.ts +66 -0
  154. package/dist/gitRoutes.d.ts.map +1 -0
  155. package/dist/gitRoutes.js +543 -0
  156. package/dist/governed-workflow.d.ts +2 -0
  157. package/dist/governed-workflow.d.ts.map +1 -1
  158. package/dist/governed-workflow.js +4 -0
  159. package/dist/grounded-qa.d.ts +11 -0
  160. package/dist/grounded-qa.d.ts.map +1 -1
  161. package/dist/grounded-qa.js +13 -4
  162. package/dist/headers.d.ts +4 -1
  163. package/dist/headers.d.ts.map +1 -1
  164. package/dist/headers.js +11 -4
  165. package/dist/index.d.ts +8 -1
  166. package/dist/index.d.ts.map +1 -1
  167. package/dist/index.js +9 -1
  168. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +1 -1
  169. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
  170. package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1 -1
  171. package/dist/read-handlers.d.ts +5 -0
  172. package/dist/read-handlers.d.ts.map +1 -1
  173. package/dist/read-handlers.js +57 -1
  174. package/dist/routes.d.ts.map +1 -1
  175. package/dist/routes.js +259 -6
  176. package/dist/run-engine.d.ts.map +1 -1
  177. package/dist/run-engine.js +3 -0
  178. package/dist/run-handlers.d.ts.map +1 -1
  179. package/dist/run-handlers.js +74 -4
  180. package/dist/run-request.d.ts +11 -0
  181. package/dist/run-request.d.ts.map +1 -1
  182. package/dist/run-request.js +158 -10
  183. package/dist/runtime/capabilityDetector.d.ts +38 -0
  184. package/dist/runtime/capabilityDetector.d.ts.map +1 -0
  185. package/dist/runtime/capabilityDetector.js +443 -0
  186. package/dist/runtime/capabilityRoutes.d.ts +9 -0
  187. package/dist/runtime/capabilityRoutes.d.ts.map +1 -0
  188. package/dist/runtime/capabilityRoutes.js +45 -0
  189. package/dist/runtime/containerEngineDetector.d.ts +17 -0
  190. package/dist/runtime/containerEngineDetector.d.ts.map +1 -0
  191. package/dist/runtime/containerEngineDetector.js +222 -0
  192. package/dist/runtime/containerRoutes.d.ts +8 -0
  193. package/dist/runtime/containerRoutes.d.ts.map +1 -0
  194. package/dist/runtime/containerRoutes.js +207 -0
  195. package/dist/runtime/containerRunner-errors.d.ts +18 -0
  196. package/dist/runtime/containerRunner-errors.d.ts.map +1 -0
  197. package/dist/runtime/containerRunner-errors.js +42 -0
  198. package/dist/runtime/containerRunner-evidence.d.ts +24 -0
  199. package/dist/runtime/containerRunner-evidence.d.ts.map +1 -0
  200. package/dist/runtime/containerRunner-evidence.js +74 -0
  201. package/dist/runtime/containerRunner.d.ts +37 -0
  202. package/dist/runtime/containerRunner.d.ts.map +1 -0
  203. package/dist/runtime/containerRunner.js +443 -0
  204. package/dist/server.d.ts.map +1 -1
  205. package/dist/server.js +24 -4
  206. package/dist/store/schema.d.ts +1 -1
  207. package/dist/store/schema.d.ts.map +1 -1
  208. package/dist/store/schema.js +62 -1
  209. package/dist/task-workspace/active-store.d.ts +21 -0
  210. package/dist/task-workspace/active-store.d.ts.map +1 -0
  211. package/dist/task-workspace/active-store.js +55 -0
  212. package/dist/task-workspace/authorization.d.ts +7 -0
  213. package/dist/task-workspace/authorization.d.ts.map +1 -0
  214. package/dist/task-workspace/authorization.js +54 -0
  215. package/dist/task-workspace/binding.d.ts +3 -0
  216. package/dist/task-workspace/binding.d.ts.map +1 -0
  217. package/dist/task-workspace/binding.js +22 -0
  218. package/dist/task-workspace/cleanup.d.ts +4 -0
  219. package/dist/task-workspace/cleanup.d.ts.map +1 -0
  220. package/dist/task-workspace/cleanup.js +428 -0
  221. package/dist/task-workspace/errors.d.ts +14 -0
  222. package/dist/task-workspace/errors.d.ts.map +1 -0
  223. package/dist/task-workspace/errors.js +81 -0
  224. package/dist/task-workspace/evidence.d.ts +32 -0
  225. package/dist/task-workspace/evidence.d.ts.map +1 -0
  226. package/dist/task-workspace/evidence.js +52 -0
  227. package/dist/task-workspace/field-safety.d.ts +3 -0
  228. package/dist/task-workspace/field-safety.d.ts.map +1 -0
  229. package/dist/task-workspace/field-safety.js +42 -0
  230. package/dist/task-workspace/health.d.ts +4 -0
  231. package/dist/task-workspace/health.d.ts.map +1 -0
  232. package/dist/task-workspace/health.js +163 -0
  233. package/dist/task-workspace/lifecycle.d.ts +3 -0
  234. package/dist/task-workspace/lifecycle.d.ts.map +1 -0
  235. package/dist/task-workspace/lifecycle.js +248 -0
  236. package/dist/task-workspace/locks.d.ts +13 -0
  237. package/dist/task-workspace/locks.d.ts.map +1 -0
  238. package/dist/task-workspace/locks.js +44 -0
  239. package/dist/task-workspace/managed-root.d.ts +7 -0
  240. package/dist/task-workspace/managed-root.d.ts.map +1 -0
  241. package/dist/task-workspace/managed-root.js +98 -0
  242. package/dist/task-workspace/mutex.d.ts +8 -0
  243. package/dist/task-workspace/mutex.d.ts.map +1 -0
  244. package/dist/task-workspace/mutex.js +82 -0
  245. package/dist/task-workspace/naming.d.ts +15 -0
  246. package/dist/task-workspace/naming.d.ts.map +1 -0
  247. package/dist/task-workspace/naming.js +0 -0
  248. package/dist/task-workspace/provisioning.d.ts +3 -0
  249. package/dist/task-workspace/provisioning.d.ts.map +1 -0
  250. package/dist/task-workspace/provisioning.js +528 -0
  251. package/dist/task-workspace/reconciliation.d.ts +15 -0
  252. package/dist/task-workspace/reconciliation.d.ts.map +1 -0
  253. package/dist/task-workspace/reconciliation.js +274 -0
  254. package/dist/task-workspace/repair.d.ts +3 -0
  255. package/dist/task-workspace/repair.d.ts.map +1 -0
  256. package/dist/task-workspace/repair.js +286 -0
  257. package/dist/task-workspace/routes.d.ts +19 -0
  258. package/dist/task-workspace/routes.d.ts.map +1 -0
  259. package/dist/task-workspace/routes.js +481 -0
  260. package/dist/task-workspace/store.d.ts +12 -0
  261. package/dist/task-workspace/store.d.ts.map +1 -0
  262. package/dist/task-workspace/store.js +128 -0
  263. package/dist/task-workspace/types.d.ts +170 -0
  264. package/dist/task-workspace/types.d.ts.map +1 -0
  265. package/dist/task-workspace/types.js +5 -0
  266. package/dist/voice-action-governance.d.ts +23 -0
  267. package/dist/voice-action-governance.d.ts.map +1 -0
  268. package/dist/voice-action-governance.js +126 -0
  269. package/dist/voice-handlers.d.ts +6 -0
  270. package/dist/voice-handlers.d.ts.map +1 -0
  271. package/dist/voice-handlers.js +570 -0
  272. package/dist/voice-realtime-grounded-tool.d.ts +31 -0
  273. package/dist/voice-realtime-grounded-tool.d.ts.map +1 -0
  274. package/dist/voice-realtime-grounded-tool.js +322 -0
  275. package/dist/voice-realtime.d.ts +69 -0
  276. package/dist/voice-realtime.d.ts.map +1 -0
  277. package/dist/voice-realtime.js +787 -0
  278. package/dist/workspace-state-handlers.d.ts +5 -0
  279. package/dist/workspace-state-handlers.d.ts.map +1 -0
  280. package/dist/workspace-state-handlers.js +106 -0
  281. package/package.json +20 -19
@@ -0,0 +1,13 @@
1
+ import type { WorkspaceLock, WorkspaceLockReason } from "@oscharko-dev/keiko-contracts";
2
+ export declare const DEFAULT_LOCK_TTL_MS: number;
3
+ export declare function lockIsLive(lock: WorkspaceLock | null, nowMs: number, ttlMs: number): boolean;
4
+ export interface MakeWorkspaceLockArgs {
5
+ readonly newId: () => string;
6
+ readonly owner: string;
7
+ readonly reason: WorkspaceLockReason;
8
+ readonly nowMs: number;
9
+ readonly ttlMs: number;
10
+ }
11
+ export declare function makeWorkspaceLock(args: MakeWorkspaceLockArgs): WorkspaceLock;
12
+ export declare function resolveLockTtl(lockTtlMs?: number): number;
13
+ //# sourceMappingURL=locks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/task-workspace/locks.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAIxF,eAAO,MAAM,mBAAmB,QAAa,CAAC;AAI9C,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAQ5F;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAKD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,aAAa,CAQ5E;AAID,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,44 @@
1
+ // Consolidated advisory-lock model for managed task workspaces (Issue #449, Epic #443, ADR-0093 D2).
2
+ //
3
+ // Before #449 the lock-liveness predicate, the default TTL, and the lock builder were copied verbatim
4
+ // into provisioning.ts, lifecycle.ts, reconciliation.ts, cleanup.ts, and repair.ts — five places a TTL
5
+ // or expiry-parsing fix could silently diverge. This module is the single home: the behaviour is
6
+ // byte-for-byte the previous copies (an explicit `expiresAt` wins; otherwise the TTL since
7
+ // `acquiredAt`; a non-finite timestamp fails closed → treated as not live), now defined once.
8
+ //
9
+ // The advisory `WorkspaceLock` is the ACROSS-RESTART / ACROSS-ACTOR coordination record persisted on the
10
+ // instance row; it is checked optimistically and is NOT an intra-process serializer (that is the
11
+ // in-process `WorkspaceMutexRegistry`, ADR-0093 D1). On a process crash a held advisory lock TTL-expires
12
+ // here and #447 reconciliation/repair clears it.
13
+ // The default span a provisioning/activation/mutation/repair/cleanup lock stays valid before it is
14
+ // treated as stale. Mirrors the value the five services each previously declared.
15
+ export const DEFAULT_LOCK_TTL_MS = 5 * 60_000;
16
+ // Whether an advisory lock is still live at `nowMs`. An explicit expiry wins; otherwise the TTL since
17
+ // acquisition; a non-finite timestamp fails closed (treated as not live). `null` is never live.
18
+ export function lockIsLive(lock, nowMs, ttlMs) {
19
+ if (lock === null)
20
+ return false;
21
+ if (lock.expiresAt !== undefined) {
22
+ const expiry = Date.parse(lock.expiresAt);
23
+ return Number.isFinite(expiry) ? nowMs < expiry : false;
24
+ }
25
+ const acquired = Date.parse(lock.acquiredAt);
26
+ return Number.isFinite(acquired) ? nowMs - acquired < ttlMs : false;
27
+ }
28
+ // Builds an advisory lock with a content-free id, the requesting actor as owner, the reason, and an
29
+ // explicit expiry `ttlMs` after acquisition. Generalizes the previous per-service `makeLock` /
30
+ // `makeRepairLock` / `cleanupLock` builders over the lock reason.
31
+ export function makeWorkspaceLock(args) {
32
+ return {
33
+ lockId: args.newId(),
34
+ owner: args.owner,
35
+ reason: args.reason,
36
+ acquiredAt: new Date(args.nowMs).toISOString(),
37
+ expiresAt: new Date(args.nowMs + args.ttlMs).toISOString(),
38
+ };
39
+ }
40
+ // Applies the single default-TTL fallback the five services each inlined as `deps.lockTtlMs ??
41
+ // DEFAULT_LOCK_TTL_MS`, so the default lives in exactly one place.
42
+ export function resolveLockTtl(lockTtlMs) {
43
+ return lockTtlMs ?? DEFAULT_LOCK_TTL_MS;
44
+ }
@@ -0,0 +1,7 @@
1
+ export declare function assertManagedRootOwned(managedRoot: string): void;
2
+ export declare function assertManagedTargetContained(managedRoot: string, worktreePath: string): void;
3
+ export declare function ensureManagedWorktreeParent(worktreePath: string): void;
4
+ export declare function managedTargetExists(worktreePath: string): boolean;
5
+ export declare function isManagedRootOwned(managedRoot: string): boolean;
6
+ export declare function isManagedTargetContained(managedRoot: string, target: string): boolean;
7
+ //# sourceMappingURL=managed-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"managed-root.d.ts","sourceRoot":"","sources":["../../src/task-workspace/managed-root.ts"],"names":[],"mappings":"AAkCA,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAmBhE;AAKD,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAU5F;AAID,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAEtE;AAID,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAEjE;AAMD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAO/D;AAMD,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAOrF"}
@@ -0,0 +1,98 @@
1
+ // Keiko-owned managed-worktree root: ownership proof + realpath containment (Issue #445, Epic #443).
2
+ //
3
+ // Before any worktree is written, the server must PROVE it owns the managed root (SC2) and that the
4
+ // derived worktree path stays inside it even after symlink resolution (AC2). Ownership is proven by a
5
+ // marker file Keiko creates with restrictive permissions; containment delegates entirely to
6
+ // @oscharko-dev/keiko-workspace (no second containment engine, ADR-0088 D5): a lexical check followed
7
+ // by a realpath check that walks the existing parent chain, so a symlinked ancestor that escapes the
8
+ // root is rejected even though the leaf worktree directory does not yet exist.
9
+ import { chmodSync, existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
10
+ import { dirname, join } from "node:path";
11
+ import { assertContainedRealPath, PathEscapeError, resolveWithinWorkspace, } from "@oscharko-dev/keiko-workspace";
12
+ import { nodeWorkspaceFs } from "@oscharko-dev/keiko-workspace/internal/fs";
13
+ import { MANAGED_ROOT_MARKER_FILENAME } from "./naming.js";
14
+ import { TaskWorkspaceError } from "./errors.js";
15
+ const MARKER_CONTENT = JSON.stringify({ keikoManagedRoot: true, schemaVersion: "1" });
16
+ function chmodBestEffort(target, mode) {
17
+ if (process.platform === "win32")
18
+ return;
19
+ try {
20
+ chmodSync(target, mode);
21
+ }
22
+ catch {
23
+ // best-effort permission hardening; ownership is proven by the marker, not by perms alone.
24
+ }
25
+ }
26
+ // Creates (if absent) and proves ownership of the managed-worktree root. The marker file is the
27
+ // ownership proof: provisioning refuses to write under a root Keiko cannot establish and mark as its
28
+ // own (SC2). Throws a content-free UNSAFE_PATH error when ownership cannot be proven.
29
+ export function assertManagedRootOwned(managedRoot) {
30
+ try {
31
+ mkdirSync(managedRoot, { recursive: true, mode: 0o700 });
32
+ chmodBestEffort(managedRoot, 0o700);
33
+ const markerPath = join(managedRoot, MANAGED_ROOT_MARKER_FILENAME);
34
+ if (!existsSync(markerPath)) {
35
+ writeFileSync(markerPath, MARKER_CONTENT, { mode: 0o600 });
36
+ }
37
+ chmodBestEffort(markerPath, 0o600);
38
+ if (!statSync(managedRoot).isDirectory() || !existsSync(markerPath)) {
39
+ throw new Error("marker absent after creation");
40
+ }
41
+ }
42
+ catch (error) {
43
+ if (error instanceof TaskWorkspaceError)
44
+ throw error;
45
+ throw new TaskWorkspaceError("UNSAFE_PATH", "managed worktree root ownership could not be established");
46
+ }
47
+ }
48
+ // Asserts the derived worktree path is contained inside the managed root lexically AND after realpath
49
+ // resolution. Delegates to keiko-workspace; any escape (parent traversal, NUL, symlinked ancestor,
50
+ // absolute outside-root target) becomes a content-free UNSAFE_PATH error.
51
+ export function assertManagedTargetContained(managedRoot, worktreePath) {
52
+ try {
53
+ resolveWithinWorkspace(managedRoot, worktreePath);
54
+ assertContainedRealPath(nodeWorkspaceFs, managedRoot, worktreePath, "managed worktree path");
55
+ }
56
+ catch (error) {
57
+ if (error instanceof PathEscapeError) {
58
+ throw new TaskWorkspaceError("UNSAFE_PATH", "worktree path escapes the managed root");
59
+ }
60
+ throw new TaskWorkspaceError("UNSAFE_PATH", "worktree path failed containment validation");
61
+ }
62
+ }
63
+ // Ensures the worktree's PARENT directory (`<managedRoot>/<repositoryId>`) exists before `git worktree
64
+ // add`, which creates only the leaf directory. Must run AFTER containment is asserted.
65
+ export function ensureManagedWorktreeParent(worktreePath) {
66
+ mkdirSync(dirname(worktreePath), { recursive: true, mode: 0o700 });
67
+ }
68
+ // Whether the managed worktree directory currently exists on disk. Combined with the store lookup by
69
+ // the service to classify a target as managed (resume) vs. unmanaged (reject).
70
+ export function managedTargetExists(worktreePath) {
71
+ return existsSync(worktreePath);
72
+ }
73
+ // Non-throwing, read-only ownership check (#448): the managed root is a directory AND Keiko's marker
74
+ // file is present. Unlike assertManagedRootOwned it never creates the root or the marker, so it is the
75
+ // correct gate for read-only health evaluation and for cleanup (which must REFUSE when ownership cannot
76
+ // be proven rather than establish it). Any IO error fails closed (not owned).
77
+ export function isManagedRootOwned(managedRoot) {
78
+ try {
79
+ const markerPath = join(managedRoot, MANAGED_ROOT_MARKER_FILENAME);
80
+ return statSync(managedRoot).isDirectory() && existsSync(markerPath);
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ // Non-throwing containment check (#448): true iff `target` resolves inside the managed root lexically
87
+ // AND after realpath resolution. Delegates to the same keiko-workspace engine as the throwing assert
88
+ // (no second containment engine); any escape — parent traversal, NUL, symlinked ancestor, out-of-root
89
+ // absolute path — or an unverifiable parent chain fails closed (not contained).
90
+ export function isManagedTargetContained(managedRoot, target) {
91
+ try {
92
+ assertManagedTargetContained(managedRoot, target);
93
+ return true;
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
@@ -0,0 +1,8 @@
1
+ export interface WorkspaceMutexRegistry {
2
+ readonly runExclusive: <T>(keys: readonly string[], fn: () => Promise<T> | T) => Promise<T>;
3
+ }
4
+ export declare function activePointerKey(repositoryId: string): string;
5
+ export declare function workspaceKey(workspaceId: string): string;
6
+ export declare function provisionKey(repositoryId: string, taskId: string): string;
7
+ export declare function createWorkspaceMutexRegistry(): WorkspaceMutexRegistry;
8
+ //# sourceMappingURL=mutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../src/task-workspace/mutex.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,sBAAsB;IAKrC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7F;AAMD,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzE;AAeD,wBAAgB,4BAA4B,IAAI,sBAAsB,CAoCrE"}
@@ -0,0 +1,82 @@
1
+ // In-process keyed async mutex for managed task-workspace mutations (Issue #449, Epic #443, ADR-0093 D1).
2
+ //
3
+ // Every mutating workspace flow is optimistic check-then-write: it reads the instance, evaluates the
4
+ // advisory lock, `await`s a Git-adapter spawn, then `store.upsert`s. The window between the live check
5
+ // and the persisted write is a TOCTOU gap — two concurrent `provision()` calls for the same (repo, task)
6
+ // both pass the gates and both race `git worktree add`. This registry closes that window by SERIALIZING
7
+ // the whole critical section within the single server process: `runExclusive(keys, fn)` queues a flow
8
+ // behind any in-flight holder of an overlapping key and runs it only once it has exclusive access.
9
+ //
10
+ // It composes with — never replaces — the persisted advisory `WorkspaceLock`. The mutex grants TURN
11
+ // ORDER (same-process serialization); the advisory lock grants OWNERSHIP (across-restart / across-actor).
12
+ // The existing cross-actor `LOCK_CONTENTION` rejection therefore stays INSIDE the wrapped section: two
13
+ // requests from the same process queue and run one at a time, while a request from a different actor
14
+ // still hits the advisory check after the queue drains and is still rejected. The mutex is pure
15
+ // in-process JavaScript: no spawn, no filesystem, no new adapter verb, no allowlist entry (SC3). On a
16
+ // process crash it simply vanishes — nothing it protected survives either, and the durable record is the
17
+ // persisted advisory lock + visible lifecycle state (#447 reconciliation/repair resolve any stale lock).
18
+ // ─── key scheme ────────────────────────────────────────────────────────────────────────────────────
19
+ // Three contended resources, three key prefixes. The canonical acquisition order is the tier order
20
+ // below (active → ws → prov), then lexicographic, so a multi-key flow never deadlocks against another.
21
+ export function activePointerKey(repositoryId) {
22
+ return `active:${repositoryId}`;
23
+ }
24
+ export function workspaceKey(workspaceId) {
25
+ return `ws:${workspaceId}`;
26
+ }
27
+ export function provisionKey(repositoryId, taskId) {
28
+ return `prov:${repositoryId}:${taskId}`;
29
+ }
30
+ function keyTier(key) {
31
+ if (key.startsWith("active:"))
32
+ return 0;
33
+ if (key.startsWith("ws:"))
34
+ return 1;
35
+ if (key.startsWith("prov:"))
36
+ return 2;
37
+ return 3;
38
+ }
39
+ function compareKeys(a, b) {
40
+ const tierDelta = keyTier(a) - keyTier(b);
41
+ if (tierDelta !== 0)
42
+ return tierDelta;
43
+ return a < b ? -1 : a > b ? 1 : 0;
44
+ }
45
+ export function createWorkspaceMutexRegistry() {
46
+ // One tail promise per held key. The tail resolves when the current holder of that key releases. The
47
+ // map only holds keys with an active or queued holder; a key's entry is deleted once its chain drains,
48
+ // so the map can never grow unbounded.
49
+ const tails = new Map();
50
+ const runExclusive = (keys, fn) => {
51
+ const ordered = [...new Set(keys)].sort(compareKeys);
52
+ // Synchronous critical region: capture the predecessor tails of every requested key and install our
53
+ // release gate as their new tail in ONE uninterrupted step (no `await` here), so two concurrent
54
+ // callers cannot interleave their capture/install and both think they acquired the same key. The
55
+ // Promise executor runs synchronously, so `releaseGate` is assigned before any use (definite-assign).
56
+ let releaseGate;
57
+ const gate = new Promise((resolve) => {
58
+ releaseGate = resolve;
59
+ });
60
+ const predecessors = [];
61
+ for (const key of ordered) {
62
+ const prev = tails.get(key);
63
+ if (prev !== undefined)
64
+ predecessors.push(prev);
65
+ tails.set(key, gate);
66
+ }
67
+ return (async () => {
68
+ try {
69
+ await Promise.all(predecessors);
70
+ return await fn();
71
+ }
72
+ finally {
73
+ releaseGate();
74
+ for (const key of ordered) {
75
+ if (tails.get(key) === gate)
76
+ tails.delete(key);
77
+ }
78
+ }
79
+ })();
80
+ };
81
+ return { runExclusive };
82
+ }
@@ -0,0 +1,15 @@
1
+ export declare const MANAGED_ROOT_MARKER_FILENAME = ".keiko-managed-root";
2
+ export declare function deriveRepositoryId(repositoryRoot: string): string;
3
+ export declare function deriveWorkspaceId(input: {
4
+ readonly repositoryId: string;
5
+ readonly taskId: string;
6
+ }): string;
7
+ export declare function deriveTaskBranchName(input: {
8
+ readonly taskId: string;
9
+ }): string;
10
+ export declare function deriveManagedWorktreePath(input: {
11
+ readonly managedRoot: string;
12
+ readonly repositoryId: string;
13
+ readonly workspaceId: string;
14
+ }): string;
15
+ //# sourceMappingURL=naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/task-workspace/naming.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,4BAA4B,wBAAwB,CAAC;AAQlE,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAEjE;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAKT;AAmBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAG/E;AAKD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GAAG,MAAM,CAET"}
Binary file
@@ -0,0 +1,3 @@
1
+ import type { WorkspaceProvisioningService, WorkspaceProvisioningServiceDeps } from "./types.js";
2
+ export declare function createWorkspaceProvisioningService(deps: WorkspaceProvisioningServiceDeps): WorkspaceProvisioningService;
3
+ //# sourceMappingURL=provisioning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioning.d.ts","sourceRoot":"","sources":["../../src/task-workspace/provisioning.ts"],"names":[],"mappings":"AAuDA,OAAO,KAAK,EAGV,4BAA4B,EAC5B,gCAAgC,EAGjC,MAAM,YAAY,CAAC;AA2qBpB,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,gCAAgC,GACrC,4BAA4B,CAU9B"}