@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
@@ -1,4 +1,4 @@
1
- import type { ConversationDocumentContextWire } from "@oscharko-dev/keiko-contracts";
1
+ import { type ConversationDocumentContextWire, type DiscussionMode } from "@oscharko-dev/keiko-contracts";
2
2
  import type { ConversationMemoryActionWire, ConversationMemoryResultWire } from "@oscharko-dev/keiko-contracts/bff-wire";
3
3
  import { type Chat, type ChatMessage } from "./store/index.js";
4
4
  import { type ConversationAttachment } from "./conversation-validation.js";
@@ -26,12 +26,14 @@ export interface SendDesktopChatRequest {
26
26
  readonly documentContext: readonly ConversationDocumentContextWire[];
27
27
  readonly attachments: readonly ConversationAttachment[];
28
28
  readonly memory: ParsedConversationMemoryRequest | undefined;
29
+ readonly discussionMode: DiscussionMode | undefined;
29
30
  }
30
- interface ParsedConversationMemoryRequest {
31
+ export interface ParsedConversationMemoryRequest {
31
32
  readonly enabled: boolean;
32
33
  readonly budgetTokens?: number;
33
34
  readonly context: Record<string, unknown>;
34
35
  }
36
+ export declare function parseMemoryRequest(value: unknown): ParsedConversationMemoryRequest | RouteResult | undefined;
35
37
  export declare function createUserMessage(deps: UiHandlerDeps, request: SendDesktopChatRequest): ChatMessage;
36
38
  export declare function createAssistantMessage(deps: UiHandlerDeps, request: SendDesktopChatRequest, content: string, modelId: string): ChatMessage;
37
39
  export declare function emptyMemoryResult(enabled: boolean): ConversationMemoryResultWire;
@@ -60,5 +62,19 @@ export interface PreparedDesktopChatSend {
60
62
  }
61
63
  export declare function prepareDesktopChatSend(ctx: RouteContext, deps: UiHandlerDeps): Promise<PreparedDesktopChatSend | RouteResult>;
62
64
  export declare function handleSendDesktopChat(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
65
+ type VoiceTurnMessageRole = "user" | "assistant";
66
+ interface VoiceTurnAppendMessage {
67
+ readonly role: VoiceTurnMessageRole;
68
+ readonly content: string;
69
+ readonly timestamp?: number | undefined;
70
+ }
71
+ export interface VoiceTurnAppendRequest {
72
+ readonly chatId: string;
73
+ readonly projectPath: string;
74
+ readonly messages: readonly VoiceTurnAppendMessage[];
75
+ readonly memory: ParsedConversationMemoryRequest | undefined;
76
+ }
77
+ export declare function buildVoiceTurnMemoryResult(deps: UiHandlerDeps, request: VoiceTurnAppendRequest, chat: Chat, memoryContext: ConversationMemoryRuntimeContext | undefined): Promise<ConversationMemoryResultWire | undefined>;
78
+ export declare function handleAppendDesktopVoiceTurn(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
63
79
  export {};
64
80
  //# sourceMappingURL=chat-handlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-handlers.d.ts","sourceRoot":"","sources":["../src/chat-handlers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-handlers.d.ts","sourceRoot":"","sources":["../src/chat-handlers.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { basename } from "node:path";
6
6
  import { GatewayError, findCapability, findConfiguredCapability, listCapabilities, listConfiguredCapabilities, } from "@oscharko-dev/keiko-model-gateway";
7
+ import { isDiscussionMode, stripUnsafeFormatChars, } from "@oscharko-dev/keiko-contracts";
7
8
  import { retrieveMemoryContext } from "@oscharko-dev/keiko-memory-retrieval";
8
9
  import { maybeRunAutoMaintenance, } from "./memory-maintenance-handlers.js";
9
10
  import { buildConversationRetrievalSignals, conversationFusionMode, } from "./memory-retrieval-signals.js";
@@ -264,7 +265,7 @@ function parseMemoryBudget(raw) {
264
265
  body: errorBody("BAD_REQUEST", "memory.budgetTokens must be a non-negative integer."),
265
266
  };
266
267
  }
267
- function parseMemoryRequest(value) {
268
+ export function parseMemoryRequest(value) {
268
269
  if (value === undefined)
269
270
  return undefined;
270
271
  if (!isRecord(value)) {
@@ -429,8 +430,15 @@ function sendRequestFromBody(body) {
429
430
  documentContext: parseDocumentContext(body.documentContext),
430
431
  attachments: parseAttachments(body.attachments),
431
432
  memory,
433
+ discussionMode: parseDiscussionMode(body.discussionMode),
432
434
  };
433
435
  }
436
+ // Issue #502 — accepts a known DiscussionMode, otherwise drops to `undefined`. An unknown or
437
+ // missing value is NOT a request error (the field is optional and turn-local); it simply leaves
438
+ // the turn in the default no-mode behaviour.
439
+ function parseDiscussionMode(value) {
440
+ return isDiscussionMode(value) ? value : undefined;
441
+ }
434
442
  function invalidChatModelResult(modelId, deps) {
435
443
  const capability = chatCapability(deps, modelId);
436
444
  if (capability?.kind === "chat") {
@@ -472,11 +480,14 @@ export function createAssistantMessage(deps, request, content, modelId) {
472
480
  // attached document blocks). Earlier history turns stay verbatim — the document context is a
473
481
  // per-send payload and never replayed across the conversation log.
474
482
  function applyDocumentContextToLatestUserTurn(history, request, memoryText) {
483
+ // Issue #502 — a selected discussion mode also requires composing the latest user turn (to
484
+ // prepend the additive directive block), even with no documents or memory.
475
485
  if (request.documentContext.length === 0 &&
476
- (memoryText === undefined || memoryText.length === 0)) {
486
+ (memoryText === undefined || memoryText.length === 0) &&
487
+ request.discussionMode === undefined) {
477
488
  return Array.from(history);
478
489
  }
479
- const composed = composeConversationPrompt(request.content, request.documentContext, memoryText);
490
+ const composed = composeConversationPrompt(request.content, request.documentContext, memoryText, request.discussionMode);
480
491
  // Replace ONLY the last user turn (the one we just persisted). System and assistant turns
481
492
  // are untouched. Walking from the end avoids rewriting a same-text earlier turn.
482
493
  const out = Array.from(history);
@@ -869,3 +880,174 @@ export async function handleSendDesktopChat(ctx, deps) {
869
880
  const { request, chat, modelId, memoryContext } = prepared;
870
881
  return persistModelChatTurn(deps, request, chat, modelId, memoryContext);
871
882
  }
883
+ const MAX_VOICE_TURN_MESSAGES = 8;
884
+ function parseVoiceTurnAppendMessage(value) {
885
+ if (!isRecord(value))
886
+ return undefined;
887
+ const role = value.role;
888
+ if (role !== "user" && role !== "assistant")
889
+ return undefined;
890
+ const content = typeof value.content === "string" ? value.content.trim() : "";
891
+ if (content.length === 0 || content.length > MAX_CHAT_INPUT_CHARS)
892
+ return undefined;
893
+ const timestamp = value.timestamp;
894
+ if (timestamp !== undefined) {
895
+ if (!Number.isInteger(timestamp) || timestamp < 0)
896
+ return undefined;
897
+ return { role, content, timestamp: timestamp };
898
+ }
899
+ return { role, content };
900
+ }
901
+ function voiceTurnAppendRequestFromBody(body) {
902
+ const chatId = typeof body.chatId === "string" ? body.chatId : "";
903
+ const projectPath = typeof body.projectPath === "string" ? body.projectPath : "";
904
+ if (chatId.length === 0 || projectPath.length === 0) {
905
+ return { status: 400, body: errorBody("BAD_REQUEST", "chatId and projectPath are required.") };
906
+ }
907
+ if (!Array.isArray(body.messages) || body.messages.length === 0) {
908
+ return { status: 400, body: errorBody("BAD_REQUEST", "messages must be a non-empty array.") };
909
+ }
910
+ if (body.messages.length > MAX_VOICE_TURN_MESSAGES) {
911
+ return { status: 400, body: errorBody("BAD_REQUEST", "messages contains too many entries.") };
912
+ }
913
+ const messages = body.messages.map(parseVoiceTurnAppendMessage);
914
+ if (messages.some((message) => message === undefined)) {
915
+ return {
916
+ status: 400,
917
+ body: errorBody("BAD_REQUEST", "messages must contain committed user or assistant text."),
918
+ };
919
+ }
920
+ const memory = parseMemoryRequest(body.memory);
921
+ if (isRouteResult(memory))
922
+ return memory;
923
+ return {
924
+ chatId,
925
+ projectPath,
926
+ messages: messages,
927
+ memory,
928
+ };
929
+ }
930
+ function sanitizeVoiceTurnText(text, deps) {
931
+ return deps.redactor(stripUnsafeFormatChars(text));
932
+ }
933
+ function voiceTurnCombinedText(messages) {
934
+ return messages.map((message) => message.content).join("\n").trim();
935
+ }
936
+ function voiceTurnAsSendRequest(request, content) {
937
+ return {
938
+ chatId: request.chatId,
939
+ projectPath: request.projectPath,
940
+ content,
941
+ modelId: undefined,
942
+ documentContext: [],
943
+ attachments: [],
944
+ memory: request.memory,
945
+ discussionMode: undefined,
946
+ };
947
+ }
948
+ async function collectVoiceTurnMemoryActions(deps, request, context, modelId) {
949
+ if (context === undefined || request.memory?.enabled !== true) {
950
+ return [];
951
+ }
952
+ if (deps.memoryVault === undefined) {
953
+ return [];
954
+ }
955
+ const actions = [];
956
+ for (const message of request.messages) {
957
+ const outcomes = extractCandidatesFromUserText(message.content, buildCaptureContext(context), {
958
+ ...memoryCapturePolicyForDeps(deps, {
959
+ resolver: createMemoryTargetResolver(deps.memoryVault),
960
+ }),
961
+ });
962
+ for (const outcome of outcomes) {
963
+ const action = await captureActionFromOutcome(outcome, deps);
964
+ if (action !== null)
965
+ actions.push(action);
966
+ }
967
+ }
968
+ const userText = request.messages
969
+ .filter((message) => message.role === "user")
970
+ .map((message) => message.content)
971
+ .join("\n")
972
+ .trim();
973
+ const assistantText = request.messages
974
+ .filter((message) => message.role === "assistant")
975
+ .map((message) => message.content)
976
+ .join("\n")
977
+ .trim();
978
+ const salientActions = await captureSalientFromTurn(deps, {
979
+ content: userText.length > 0 ? userText : voiceTurnCombinedText(request.messages),
980
+ memory: request.memory,
981
+ }, context, modelId, assistantText);
982
+ actions.push(...salientActions);
983
+ return actions;
984
+ }
985
+ export async function buildVoiceTurnMemoryResult(deps, request, chat, memoryContext) {
986
+ if (request.memory === undefined) {
987
+ return undefined;
988
+ }
989
+ if (memoryContext === undefined) {
990
+ return emptyMemoryResult(false);
991
+ }
992
+ const content = voiceTurnCombinedText(request.messages);
993
+ const memory = await buildMemoryResult(voiceTurnAsSendRequest(request, content), deps, memoryContext);
994
+ const actions = await collectVoiceTurnMemoryActions(deps, request, memoryContext, chat.selectedModel);
995
+ return { ...memory, actions };
996
+ }
997
+ function persistVoiceTurnMessages(deps, request) {
998
+ const baseNow = Date.now();
999
+ return deps.store.createMessages(request.messages.map((message, index) => ({
1000
+ chatId: request.chatId,
1001
+ role: message.role,
1002
+ content: sanitizeVoiceTurnText(message.content, deps),
1003
+ timestamp: message.timestamp ?? baseNow + index,
1004
+ runId: undefined,
1005
+ workflowId: undefined,
1006
+ workflowStatus: undefined,
1007
+ shortResult: undefined,
1008
+ taskType: undefined,
1009
+ })));
1010
+ }
1011
+ function updateChatAfterVoiceTurn(deps, request, chat, created) {
1012
+ const firstUser = created.find((message) => message.role === "user");
1013
+ const chatPatch = chat.title === DEFAULT_CHAT_TITLE && firstUser !== undefined
1014
+ ? { title: firstUser.content.slice(0, 60) }
1015
+ : {};
1016
+ return deps.store.updateChat(request.chatId, chatPatch);
1017
+ }
1018
+ export async function handleAppendDesktopVoiceTurn(ctx, deps) {
1019
+ const body = await readJsonObject(ctx.req);
1020
+ if (isRouteResult(body))
1021
+ return body;
1022
+ const request = voiceTurnAppendRequestFromBody(body);
1023
+ if (isRouteResult(request))
1024
+ return request;
1025
+ const normalizedProjectPath = normalizeDesktopProjectPath(request.projectPath, deps);
1026
+ if (isRouteResult(normalizedProjectPath))
1027
+ return normalizedProjectPath;
1028
+ const chat = findChat(deps, normalizedProjectPath, request.chatId);
1029
+ if (chat === undefined) {
1030
+ return { status: 404, body: errorBody("NOT_FOUND", "Chat not found.") };
1031
+ }
1032
+ const memoryContext = request.memory === undefined
1033
+ ? undefined
1034
+ : resolveConversationMemoryContext(deps, normalizedProjectPath, request.chatId);
1035
+ if (isRouteResult(memoryContext))
1036
+ return memoryContext;
1037
+ try {
1038
+ const created = persistVoiceTurnMessages(deps, request);
1039
+ const updatedChat = updateChatAfterVoiceTurn(deps, request, chat, created);
1040
+ const memory = await buildVoiceTurnMemoryResult(deps, request, updatedChat, memoryContext);
1041
+ return {
1042
+ status: 200,
1043
+ body: {
1044
+ chat: updatedChat,
1045
+ messages: created,
1046
+ ...(memory === undefined ? {} : { memory }),
1047
+ },
1048
+ };
1049
+ }
1050
+ catch (error) {
1051
+ return desktopChatErrorResult(error, deps);
1052
+ }
1053
+ }
@@ -0,0 +1,17 @@
1
+ export declare const COMMAND_RUNNER_ERROR_CODES: {
2
+ readonly PROJECT_NOT_FOUND: "PROJECT_NOT_FOUND";
3
+ readonly TASK_NOT_FOUND: "TASK_NOT_FOUND";
4
+ readonly RUN_LIMIT_EXCEEDED: "RUN_LIMIT_EXCEEDED";
5
+ readonly RUN_NOT_FOUND: "RUN_NOT_FOUND";
6
+ readonly BAD_REQUEST: "BAD_REQUEST";
7
+ readonly PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE";
8
+ readonly COMMAND_RUNNER_UNAVAILABLE: "COMMAND_RUNNER_UNAVAILABLE";
9
+ readonly INTERNAL: "INTERNAL";
10
+ };
11
+ export type CommandRunnerErrorCode = (typeof COMMAND_RUNNER_ERROR_CODES)[keyof typeof COMMAND_RUNNER_ERROR_CODES];
12
+ export declare class CommandRunnerError extends Error {
13
+ readonly code: CommandRunnerErrorCode;
14
+ readonly status: number;
15
+ constructor(code: CommandRunnerErrorCode, message: string);
16
+ }
17
+ //# sourceMappingURL=command-runner-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-runner-errors.d.ts","sourceRoot":"","sources":["../src/command-runner-errors.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,0BAA0B;;;;;;;;;CAS7B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAChC,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,OAAO,0BAA0B,CAAC,CAAC;AAa/E,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAgB,IAAI,EAAE,sBAAsB,CAAC;IAC7C,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAEZ,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM;CAMjE"}
@@ -0,0 +1,37 @@
1
+ // Issue #1387 — typed failure modes for the controlled command runner. Callers switch on `code`;
2
+ // messages are static strings that never leak filesystem paths or raw Node/OS error text into the
3
+ // HTTP response or SSE event payload. Mirrors the ADR-0018 terminal error model.
4
+ //
5
+ // Only PRE-spawn governance failures surface as a thrown CommandRunnerError (the route maps them to a
6
+ // 4xx/5xx envelope). Every actual EXECUTION outcome — non-zero exit, timeout, cancellation, denied
7
+ // spawn — is reported as a `CommandTaskRunResult` with a `failureReason`, never as an error.
8
+ export const COMMAND_RUNNER_ERROR_CODES = {
9
+ PROJECT_NOT_FOUND: "PROJECT_NOT_FOUND",
10
+ TASK_NOT_FOUND: "TASK_NOT_FOUND",
11
+ RUN_LIMIT_EXCEEDED: "RUN_LIMIT_EXCEEDED",
12
+ RUN_NOT_FOUND: "RUN_NOT_FOUND",
13
+ BAD_REQUEST: "BAD_REQUEST",
14
+ PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
15
+ COMMAND_RUNNER_UNAVAILABLE: "COMMAND_RUNNER_UNAVAILABLE",
16
+ INTERNAL: "INTERNAL",
17
+ };
18
+ const STATUS_MAP = {
19
+ PROJECT_NOT_FOUND: 404,
20
+ TASK_NOT_FOUND: 404,
21
+ RUN_LIMIT_EXCEEDED: 429,
22
+ RUN_NOT_FOUND: 404,
23
+ BAD_REQUEST: 400,
24
+ PAYLOAD_TOO_LARGE: 413,
25
+ COMMAND_RUNNER_UNAVAILABLE: 503,
26
+ INTERNAL: 500,
27
+ };
28
+ export class CommandRunnerError extends Error {
29
+ code;
30
+ status;
31
+ constructor(code, message) {
32
+ super(message);
33
+ this.name = "CommandRunnerError";
34
+ this.code = code;
35
+ this.status = STATUS_MAP[code];
36
+ }
37
+ }
@@ -0,0 +1,23 @@
1
+ import type { EvidenceManifest, EvidenceStore } from "@oscharko-dev/keiko-evidence";
2
+ import type { CommandFailureReason, CommandTaskKind } from "@oscharko-dev/keiko-contracts";
3
+ export declare const COMMAND_RUN_EVIDENCE_KIND: "command-run";
4
+ export type CommandRunEvidenceEntry = EvidenceManifest;
5
+ export interface CommandRunEvidenceInput {
6
+ readonly runId: string;
7
+ readonly projectId: string;
8
+ readonly taskId: string;
9
+ readonly kind: CommandTaskKind;
10
+ readonly executable: string;
11
+ readonly argCount: number;
12
+ readonly exitCode: number | null;
13
+ readonly durationMs: number;
14
+ readonly timedOut: boolean;
15
+ readonly truncated: boolean;
16
+ readonly failureReason: CommandFailureReason;
17
+ readonly stdoutBytes: number;
18
+ readonly stderrBytes: number;
19
+ readonly startedAt: number;
20
+ }
21
+ export declare function buildCommandRunEvidenceEntry(input: CommandRunEvidenceInput): CommandRunEvidenceEntry;
22
+ export declare function appendCommandRunEvidence(store: EvidenceStore, entry: CommandRunEvidenceEntry, redact: (input: string) => string): string;
23
+ //# sourceMappingURL=command-runner-evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-runner-evidence.d.ts","sourceRoot":"","sources":["../src/command-runner-evidence.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAIpF,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAE3F,eAAO,MAAM,yBAAyB,EAAG,aAAsB,CAAC;AAEhE,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAiBD,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,uBAAuB,GAC7B,uBAAuB,CAsCzB;AAKD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,uBAAuB,EAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAChC,MAAM,CAGR"}
@@ -0,0 +1,69 @@
1
+ // Issue #1387 — command-run evidence. Each finished run writes a standard EvidenceManifest via the
2
+ // existing EvidenceStore.put port so the shared evidence list/detail APIs can parse it. The runner
3
+ // data lives in the standard run identity plus one commandExecutions record. It carries COUNTS and
4
+ // ENUMS ONLY — never the task argv, never output bytes, never absolute paths (ADR-0048 content-free
5
+ // invariant). Mirrors terminal-evidence.ts.
6
+ import { deepRedactStrings } from "@oscharko-dev/keiko-evidence";
7
+ import { EVIDENCE_SCHEMA_VERSION } from "@oscharko-dev/keiko-evidence";
8
+ import { HARNESS_VERSION } from "@oscharko-dev/keiko-harness";
9
+ export const COMMAND_RUN_EVIDENCE_KIND = "command-run";
10
+ function commandRunOutcome(input) {
11
+ switch (input.failureReason) {
12
+ case "none":
13
+ return "completed";
14
+ case "timed-out":
15
+ return "limit-exceeded";
16
+ case "cancelled":
17
+ return "cancelled";
18
+ default:
19
+ return "failed";
20
+ }
21
+ }
22
+ // PURE. Builds the on-disk manifest from a finished run. Identifiers only; args and output excluded.
23
+ // The caller supplies runId/startedAt (no clock, no randomness here) so evidence stays deterministic.
24
+ export function buildCommandRunEvidenceEntry(input) {
25
+ const runId = input.runId;
26
+ return {
27
+ evidenceSchemaVersion: EVIDENCE_SCHEMA_VERSION,
28
+ run: {
29
+ runId,
30
+ fingerprint: runId,
31
+ harnessVersion: HARNESS_VERSION,
32
+ taskType: COMMAND_RUN_EVIDENCE_KIND,
33
+ outcome: commandRunOutcome(input),
34
+ startedAt: input.startedAt,
35
+ finishedAt: input.startedAt + input.durationMs,
36
+ durationMs: input.durationMs,
37
+ },
38
+ model: { modelId: "command-runner", costClass: "unknown" },
39
+ usageTotals: { promptTokens: 0, completionTokens: 0, requestCount: 0, totalLatencyMs: 0 },
40
+ context: {
41
+ workspaceRoot: input.projectId,
42
+ totalCandidates: 0,
43
+ usedBytes: 0,
44
+ budgetBytes: 0,
45
+ droppedForBudget: 0,
46
+ entries: [],
47
+ },
48
+ stateTransitions: [],
49
+ toolCalls: [],
50
+ commandExecutions: [
51
+ {
52
+ seq: 1,
53
+ ts: input.startedAt,
54
+ executable: input.executable,
55
+ argCount: input.argCount,
56
+ exitCode: input.exitCode,
57
+ timedOut: input.timedOut,
58
+ durationMs: input.durationMs,
59
+ },
60
+ ],
61
+ };
62
+ }
63
+ // Defense in depth: applies the live redactor to every string leaf before serializing. All known
64
+ // leaves (executable, projectId, runId, taskId) are structurally safe today; a future schema
65
+ // addition inherits the redaction automatically. Mirrors appendTerminalEvidence.
66
+ export function appendCommandRunEvidence(store, entry, redact) {
67
+ const safe = deepRedactStrings(entry, redact);
68
+ return store.put(safe.run.runId, JSON.stringify(safe, null, 2));
69
+ }
@@ -0,0 +1,7 @@
1
+ import type { UiHandlerDeps } from "./deps.js";
2
+ import { type HandlerOutcome, type RouteContext, type RouteResult } from "./routes.js";
3
+ export declare function handleCommandCatalog(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
4
+ export declare function handleCreateCommandRun(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
5
+ export declare function handleDeleteCommandRun(ctx: RouteContext, deps: UiHandlerDeps): RouteResult;
6
+ export declare function handleCommandEvents(ctx: RouteContext, deps: UiHandlerDeps): HandlerOutcome;
7
+ //# sourceMappingURL=command-runner-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-runner-routes.d.ts","sourceRoot":"","sources":["../src/command-runner-routes.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAyFrB,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAUtB;AAKD,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAuBtB;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAQ1F;AAID,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,cAAc,CAQ1F"}
@@ -0,0 +1,175 @@
1
+ // Issue #1387 — four /api/commands/* BFF route handlers for the controlled command runner. CSRF is
2
+ // enforced by the server's state-changing-request gate (POST/DELETE flow through it); GET routes are
3
+ // read-only and exempt. SSE framing mirrors /api/terminal/*/events and /api/browser/*/events.
4
+ //
5
+ // GET /api/commands/catalog?projectId=… discovered task catalog (no runner deps required)
6
+ // POST /api/commands/runs run a catalog task → structured result (redacted)
7
+ // DELETE /api/commands/runs/:runId cancel an in-flight run
8
+ // GET /api/commands/events SSE stream of run lifecycle events
9
+ import { parseCommandTaskRunRequest } from "@oscharko-dev/keiko-contracts";
10
+ import { CommandRunnerError } from "./command-runner-errors.js";
11
+ import { SSE_HEADERS, readyMessage } from "./sse.js";
12
+ import { errorBody, STREAMING, } from "./routes.js";
13
+ const MAX_COMMAND_BODY_BYTES = 16_000;
14
+ class BodyTooLargeError extends Error {
15
+ constructor() {
16
+ super("command runner request body too large");
17
+ this.name = "BodyTooLargeError";
18
+ }
19
+ }
20
+ function noRunnerDeps() {
21
+ return {
22
+ status: 503,
23
+ body: errorBody("COMMAND_RUNNER_UNAVAILABLE", "Command runner is not configured for this BFF."),
24
+ };
25
+ }
26
+ function requireRunner(deps) {
27
+ return deps.commandRunner ?? noRunnerDeps();
28
+ }
29
+ function isRouteResult(value) {
30
+ return typeof value.status === "number";
31
+ }
32
+ function toRouteResult(error) {
33
+ return { status: error.status, body: errorBody(error.code, error.message) };
34
+ }
35
+ function readBody(req) {
36
+ return new Promise((resolve, reject) => {
37
+ const chunks = [];
38
+ let total = 0;
39
+ let capped = false;
40
+ req.on("data", (chunk) => {
41
+ total += chunk.length;
42
+ if (total > MAX_COMMAND_BODY_BYTES) {
43
+ if (!capped) {
44
+ capped = true;
45
+ chunks.length = 0;
46
+ reject(new BodyTooLargeError());
47
+ req.resume();
48
+ }
49
+ return;
50
+ }
51
+ chunks.push(chunk);
52
+ });
53
+ req.on("end", () => {
54
+ if (!capped)
55
+ resolve(Buffer.concat(chunks).toString("utf8"));
56
+ });
57
+ req.on("error", reject);
58
+ });
59
+ }
60
+ async function readJsonObject(req) {
61
+ const raw = await readBody(req);
62
+ if (raw.length === 0)
63
+ return {};
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(raw);
67
+ }
68
+ catch {
69
+ throw new CommandRunnerError("BAD_REQUEST", "Request body is not valid JSON.");
70
+ }
71
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
72
+ throw new CommandRunnerError("BAD_REQUEST", "Request body must be a JSON object.");
73
+ }
74
+ return parsed;
75
+ }
76
+ async function runHandler(work) {
77
+ try {
78
+ return await work();
79
+ }
80
+ catch (error) {
81
+ if (error instanceof BodyTooLargeError) {
82
+ return {
83
+ status: 413,
84
+ body: errorBody("PAYLOAD_TOO_LARGE", "Request body exceeds the size limit."),
85
+ };
86
+ }
87
+ if (error instanceof CommandRunnerError)
88
+ return toRouteResult(error);
89
+ throw error;
90
+ }
91
+ }
92
+ // GET /api/commands/catalog — the discovered, vetted task list for a project. Requires the runner so
93
+ // discovery uses the same workspace containment as execution.
94
+ export async function handleCommandCatalog(ctx, deps) {
95
+ const guard = requireRunner(deps);
96
+ if (isRouteResult(guard))
97
+ return guard;
98
+ return runHandler(() => {
99
+ const projectId = ctx.url.searchParams.get("projectId");
100
+ if (projectId === null || projectId.length === 0) {
101
+ throw new CommandRunnerError("BAD_REQUEST", "Query parameter 'projectId' is required.");
102
+ }
103
+ return { status: 200, body: guard.discover(projectId) };
104
+ });
105
+ }
106
+ // POST /api/commands/runs — run a catalog task. The result carries the structured outcome (exit code,
107
+ // duration, truncation, failure reason). Layer-2 redaction is applied to stdout/stderr before the
108
+ // body reaches the browser; runCommand already applied Layer-1 env-value redaction.
109
+ export async function handleCreateCommandRun(ctx, deps) {
110
+ const guard = requireRunner(deps);
111
+ if (isRouteResult(guard))
112
+ return guard;
113
+ return runHandler(async () => {
114
+ const body = await readJsonObject(ctx.req);
115
+ const parsed = parseCommandTaskRunRequest(body);
116
+ if (!parsed.ok) {
117
+ throw new CommandRunnerError("BAD_REQUEST", parsed.errors.join("; "));
118
+ }
119
+ const input = {
120
+ projectId: parsed.value.projectId,
121
+ taskId: parsed.value.taskId,
122
+ ...(parsed.value.timeoutMs === undefined ? {} : { timeoutMs: parsed.value.timeoutMs }),
123
+ ...(parsed.value.requestId === undefined ? {} : { requestId: parsed.value.requestId }),
124
+ };
125
+ const raw = await guard.execute(input);
126
+ const redactStr = (value) => {
127
+ const redacted = deps.redactor(value);
128
+ return typeof redacted === "string" ? redacted : value;
129
+ };
130
+ const result = { ...raw, stdout: redactStr(raw.stdout), stderr: redactStr(raw.stderr) };
131
+ return { status: 200, body: result };
132
+ });
133
+ }
134
+ export function handleDeleteCommandRun(ctx, deps) {
135
+ const guard = requireRunner(deps);
136
+ if (isRouteResult(guard))
137
+ return guard;
138
+ const runId = ctx.params.runId ?? "";
139
+ if (!guard.abort(runId)) {
140
+ return { status: 404, body: errorBody("RUN_NOT_FOUND", "Command run not found.") };
141
+ }
142
+ return { status: 200, body: { ok: true } };
143
+ }
144
+ // SSE — one runner event becomes one message with `event: command:<kind>` and a JSON payload. A
145
+ // synthetic `ready` is emitted first so the client can transition from connecting to live.
146
+ export function handleCommandEvents(ctx, deps) {
147
+ const guard = requireRunner(deps);
148
+ if (isRouteResult(guard))
149
+ return guard;
150
+ openCommandSseStream(ctx.res, guard, deps.redactor);
151
+ ctx.req.on("close", () => {
152
+ ctx.res.end();
153
+ });
154
+ return STREAMING;
155
+ }
156
+ function openCommandSseStream(res, manager, redactor) {
157
+ res.writeHead(200, SSE_HEADERS);
158
+ let seq = 0;
159
+ const unsubscribe = manager.subscribe((event) => {
160
+ seq += 1;
161
+ writeCommandEvent(res, event, seq, redactor);
162
+ });
163
+ res.write(readyMessage());
164
+ res.on("close", () => {
165
+ unsubscribe();
166
+ });
167
+ }
168
+ function writeCommandEvent(res, event, seq, redactor) {
169
+ const redacted = redactor(event);
170
+ const data = JSON.stringify(redacted);
171
+ const frame = `id: ${String(seq)}\nevent: command:${event.kind}\ndata: ${data}\n\n`;
172
+ if (!res.write(frame)) {
173
+ res.destroy();
174
+ }
175
+ }
@@ -0,0 +1,29 @@
1
+ import { type RunCommandDeps, type SandboxPolicy } from "@oscharko-dev/keiko-tools";
2
+ import { type CommandRunnerEvent, type CommandTaskCatalog, type CommandTaskRunResult } from "@oscharko-dev/keiko-contracts";
3
+ import type { EvidenceStore } from "@oscharko-dev/keiko-evidence";
4
+ import type { UiStore } from "./store/index.js";
5
+ export interface CommandRunInput {
6
+ readonly projectId: string;
7
+ readonly taskId: string;
8
+ readonly timeoutMs?: number | undefined;
9
+ readonly requestId?: string | undefined;
10
+ }
11
+ export type CommandRunnerEventEmitter = (event: CommandRunnerEvent) => void;
12
+ export interface CommandRunnerManager {
13
+ readonly discover: (projectId: string) => CommandTaskCatalog;
14
+ readonly execute: (input: CommandRunInput) => Promise<CommandTaskRunResult>;
15
+ readonly abort: (runId: string) => boolean;
16
+ readonly subscribe: (listener: CommandRunnerEventEmitter) => () => void;
17
+ readonly inFlightCount: () => number;
18
+ }
19
+ export interface CommandRunnerManagerOptions {
20
+ readonly store: UiStore;
21
+ readonly evidenceStore?: EvidenceStore | undefined;
22
+ readonly policy?: SandboxPolicy | undefined;
23
+ readonly processEnv?: NodeJS.ProcessEnv | undefined;
24
+ readonly redactor?: ((input: string) => string) | undefined;
25
+ readonly runDeps?: Partial<RunCommandDeps> | undefined;
26
+ readonly now?: (() => number) | undefined;
27
+ }
28
+ export declare function createCommandRunnerManager(opts: CommandRunnerManagerOptions): CommandRunnerManager;
29
+ //# sourceMappingURL=command-runner.d.ts.map