@a5c-ai/adapters-gateway 5.1.1-staging.52898ebfc24f

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 (202) hide show
  1. package/README.md +20 -0
  2. package/dist/auth/bootstrap.d.ts +89 -0
  3. package/dist/auth/bootstrap.d.ts.map +1 -0
  4. package/dist/auth/bootstrap.js +222 -0
  5. package/dist/auth/bootstrap.js.map +1 -0
  6. package/dist/auth/hashing.d.ts +4 -0
  7. package/dist/auth/hashing.d.ts.map +1 -0
  8. package/dist/auth/hashing.js +27 -0
  9. package/dist/auth/hashing.js.map +1 -0
  10. package/dist/auth/middleware.d.ts +3 -0
  11. package/dist/auth/middleware.d.ts.map +1 -0
  12. package/dist/auth/middleware.js +17 -0
  13. package/dist/auth/middleware.js.map +1 -0
  14. package/dist/auth/tokens.d.ts +45 -0
  15. package/dist/auth/tokens.d.ts.map +1 -0
  16. package/dist/auth/tokens.js +186 -0
  17. package/dist/auth/tokens.js.map +1 -0
  18. package/dist/builtin-adapters.d.ts +17 -0
  19. package/dist/builtin-adapters.d.ts.map +1 -0
  20. package/dist/builtin-adapters.js +119 -0
  21. package/dist/builtin-adapters.js.map +1 -0
  22. package/dist/config.d.ts +37 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +97 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/fanout/client-conn.d.ts +20 -0
  27. package/dist/fanout/client-conn.d.ts.map +1 -0
  28. package/dist/fanout/client-conn.js +53 -0
  29. package/dist/fanout/client-conn.js.map +1 -0
  30. package/dist/fanout/subscriber.d.ts +12 -0
  31. package/dist/fanout/subscriber.d.ts.map +1 -0
  32. package/dist/fanout/subscriber.js +40 -0
  33. package/dist/fanout/subscriber.js.map +1 -0
  34. package/dist/index.d.ts +30 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +52 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/kanban/lib/config-loader.d.ts +29 -0
  39. package/dist/kanban/lib/config-loader.d.ts.map +1 -0
  40. package/dist/kanban/lib/config-loader.js +166 -0
  41. package/dist/kanban/lib/config-loader.js.map +1 -0
  42. package/dist/kanban/lib/config.d.ts +3 -0
  43. package/dist/kanban/lib/config.d.ts.map +1 -0
  44. package/dist/kanban/lib/config.js +6 -0
  45. package/dist/kanban/lib/config.js.map +1 -0
  46. package/dist/kanban/lib/create-global-registry.d.ts +28 -0
  47. package/dist/kanban/lib/create-global-registry.d.ts.map +1 -0
  48. package/dist/kanban/lib/create-global-registry.js +53 -0
  49. package/dist/kanban/lib/create-global-registry.js.map +1 -0
  50. package/dist/kanban/lib/dispatch-context-audit.d.ts +12 -0
  51. package/dist/kanban/lib/dispatch-context-audit.d.ts.map +1 -0
  52. package/dist/kanban/lib/dispatch-context-audit.js +44 -0
  53. package/dist/kanban/lib/dispatch-context-audit.js.map +1 -0
  54. package/dist/kanban/lib/error-handler.d.ts +28 -0
  55. package/dist/kanban/lib/error-handler.d.ts.map +1 -0
  56. package/dist/kanban/lib/error-handler.js +61 -0
  57. package/dist/kanban/lib/error-handler.js.map +1 -0
  58. package/dist/kanban/lib/global-registry.d.ts +49 -0
  59. package/dist/kanban/lib/global-registry.d.ts.map +1 -0
  60. package/dist/kanban/lib/global-registry.js +18 -0
  61. package/dist/kanban/lib/global-registry.js.map +1 -0
  62. package/dist/kanban/lib/parser.d.ts +36 -0
  63. package/dist/kanban/lib/parser.d.ts.map +1 -0
  64. package/dist/kanban/lib/parser.js +585 -0
  65. package/dist/kanban/lib/parser.js.map +1 -0
  66. package/dist/kanban/lib/path-resolver.d.ts +2 -0
  67. package/dist/kanban/lib/path-resolver.d.ts.map +1 -0
  68. package/dist/kanban/lib/path-resolver.js +16 -0
  69. package/dist/kanban/lib/path-resolver.js.map +1 -0
  70. package/dist/kanban/lib/review-service.d.ts +63 -0
  71. package/dist/kanban/lib/review-service.d.ts.map +1 -0
  72. package/dist/kanban/lib/review-service.js +571 -0
  73. package/dist/kanban/lib/review-service.js.map +1 -0
  74. package/dist/kanban/lib/run-cache.d.ts +36 -0
  75. package/dist/kanban/lib/run-cache.d.ts.map +1 -0
  76. package/dist/kanban/lib/run-cache.js +313 -0
  77. package/dist/kanban/lib/run-cache.js.map +1 -0
  78. package/dist/kanban/lib/server-init.d.ts +26 -0
  79. package/dist/kanban/lib/server-init.d.ts.map +1 -0
  80. package/dist/kanban/lib/server-init.js +179 -0
  81. package/dist/kanban/lib/server-init.js.map +1 -0
  82. package/dist/kanban/lib/services/automation-rule-service.d.ts +97 -0
  83. package/dist/kanban/lib/services/automation-rule-service.d.ts.map +1 -0
  84. package/dist/kanban/lib/services/automation-rule-service.js +806 -0
  85. package/dist/kanban/lib/services/automation-rule-service.js.map +1 -0
  86. package/dist/kanban/lib/services/automation-webhook-service.d.ts +44 -0
  87. package/dist/kanban/lib/services/automation-webhook-service.d.ts.map +1 -0
  88. package/dist/kanban/lib/services/automation-webhook-service.js +405 -0
  89. package/dist/kanban/lib/services/automation-webhook-service.js.map +1 -0
  90. package/dist/kanban/lib/services/backlog-query-service.d.ts +130 -0
  91. package/dist/kanban/lib/services/backlog-query-service.d.ts.map +1 -0
  92. package/dist/kanban/lib/services/backlog-query-service.js +1972 -0
  93. package/dist/kanban/lib/services/backlog-query-service.js.map +1 -0
  94. package/dist/kanban/lib/services/dispatch-context-label-service.d.ts +39 -0
  95. package/dist/kanban/lib/services/dispatch-context-label-service.d.ts.map +1 -0
  96. package/dist/kanban/lib/services/dispatch-context-label-service.js +160 -0
  97. package/dist/kanban/lib/services/dispatch-context-label-service.js.map +1 -0
  98. package/dist/kanban/lib/services/kanban-storage.d.ts +36 -0
  99. package/dist/kanban/lib/services/kanban-storage.d.ts.map +1 -0
  100. package/dist/kanban/lib/services/kanban-storage.js +26 -0
  101. package/dist/kanban/lib/services/kanban-storage.js.map +1 -0
  102. package/dist/kanban/lib/services/run-query-service.d.ts +79 -0
  103. package/dist/kanban/lib/services/run-query-service.d.ts.map +1 -0
  104. package/dist/kanban/lib/services/run-query-service.js +202 -0
  105. package/dist/kanban/lib/services/run-query-service.js.map +1 -0
  106. package/dist/kanban/lib/services/task-tag-service.d.ts +39 -0
  107. package/dist/kanban/lib/services/task-tag-service.d.ts.map +1 -0
  108. package/dist/kanban/lib/services/task-tag-service.js +145 -0
  109. package/dist/kanban/lib/services/task-tag-service.js.map +1 -0
  110. package/dist/kanban/lib/settings-section-storage.d.ts +13 -0
  111. package/dist/kanban/lib/settings-section-storage.d.ts.map +1 -0
  112. package/dist/kanban/lib/settings-section-storage.js +38 -0
  113. package/dist/kanban/lib/settings-section-storage.js.map +1 -0
  114. package/dist/kanban/lib/source-discovery.d.ts +10 -0
  115. package/dist/kanban/lib/source-discovery.d.ts.map +1 -0
  116. package/dist/kanban/lib/source-discovery.js +201 -0
  117. package/dist/kanban/lib/source-discovery.js.map +1 -0
  118. package/dist/kanban/lib/utils.d.ts +8 -0
  119. package/dist/kanban/lib/utils.d.ts.map +1 -0
  120. package/dist/kanban/lib/utils.js +116 -0
  121. package/dist/kanban/lib/utils.js.map +1 -0
  122. package/dist/kanban/lib/watcher.d.ts +14 -0
  123. package/dist/kanban/lib/watcher.d.ts.map +1 -0
  124. package/dist/kanban/lib/watcher.js +221 -0
  125. package/dist/kanban/lib/watcher.js.map +1 -0
  126. package/dist/kanban/lib/workspace-lifecycle.d.ts +68 -0
  127. package/dist/kanban/lib/workspace-lifecycle.d.ts.map +1 -0
  128. package/dist/kanban/lib/workspace-lifecycle.js +1085 -0
  129. package/dist/kanban/lib/workspace-lifecycle.js.map +1 -0
  130. package/dist/kanban/routes.d.ts +2 -0
  131. package/dist/kanban/routes.d.ts.map +1 -0
  132. package/dist/kanban/routes.js +1358 -0
  133. package/dist/kanban/routes.js.map +1 -0
  134. package/dist/kanban/types/breakpoint.d.ts +13 -0
  135. package/dist/kanban/types/breakpoint.d.ts.map +1 -0
  136. package/dist/kanban/types/breakpoint.js +3 -0
  137. package/dist/kanban/types/breakpoint.js.map +1 -0
  138. package/dist/kanban/types/index.d.ts +173 -0
  139. package/dist/kanban/types/index.d.ts.map +1 -0
  140. package/dist/kanban/types/index.js +3 -0
  141. package/dist/kanban/types/index.js.map +1 -0
  142. package/dist/logging.d.ts +7 -0
  143. package/dist/logging.d.ts.map +1 -0
  144. package/dist/logging.js +22 -0
  145. package/dist/logging.js.map +1 -0
  146. package/dist/notifications/types.d.ts +18 -0
  147. package/dist/notifications/types.d.ts.map +1 -0
  148. package/dist/notifications/types.js +2 -0
  149. package/dist/notifications/types.js.map +1 -0
  150. package/dist/notifications/webhook-out.d.ts +3 -0
  151. package/dist/notifications/webhook-out.d.ts.map +1 -0
  152. package/dist/notifications/webhook-out.js +55 -0
  153. package/dist/notifications/webhook-out.js.map +1 -0
  154. package/dist/pairing/short-code.d.ts +20 -0
  155. package/dist/pairing/short-code.d.ts.map +1 -0
  156. package/dist/pairing/short-code.js +50 -0
  157. package/dist/pairing/short-code.js.map +1 -0
  158. package/dist/protocol/errors.d.ts +10 -0
  159. package/dist/protocol/errors.d.ts.map +1 -0
  160. package/dist/protocol/errors.js +15 -0
  161. package/dist/protocol/errors.js.map +1 -0
  162. package/dist/protocol/frames.d.ts +107 -0
  163. package/dist/protocol/frames.d.ts.map +1 -0
  164. package/dist/protocol/frames.js +146 -0
  165. package/dist/protocol/frames.js.map +1 -0
  166. package/dist/protocol/v1.d.ts +111 -0
  167. package/dist/protocol/v1.d.ts.map +1 -0
  168. package/dist/protocol/v1.js +2 -0
  169. package/dist/protocol/v1.js.map +1 -0
  170. package/dist/runs/event-log-index.d.ts +29 -0
  171. package/dist/runs/event-log-index.d.ts.map +1 -0
  172. package/dist/runs/event-log-index.js +210 -0
  173. package/dist/runs/event-log-index.js.map +1 -0
  174. package/dist/runs/event-log.d.ts +25 -0
  175. package/dist/runs/event-log.d.ts.map +1 -0
  176. package/dist/runs/event-log.js +104 -0
  177. package/dist/runs/event-log.js.map +1 -0
  178. package/dist/runs/hook-broker.d.ts +18 -0
  179. package/dist/runs/hook-broker.d.ts.map +1 -0
  180. package/dist/runs/hook-broker.js +110 -0
  181. package/dist/runs/hook-broker.js.map +1 -0
  182. package/dist/runs/manager.d.ts +57 -0
  183. package/dist/runs/manager.d.ts.map +1 -0
  184. package/dist/runs/manager.js +757 -0
  185. package/dist/runs/manager.js.map +1 -0
  186. package/dist/runs/session-runtime.d.ts +8 -0
  187. package/dist/runs/session-runtime.d.ts.map +1 -0
  188. package/dist/runs/session-runtime.js +291 -0
  189. package/dist/runs/session-runtime.js.map +1 -0
  190. package/dist/runs/types.d.ts +55 -0
  191. package/dist/runs/types.d.ts.map +1 -0
  192. package/dist/runs/types.js +2 -0
  193. package/dist/runs/types.js.map +1 -0
  194. package/dist/server.d.ts +15 -0
  195. package/dist/server.d.ts.map +1 -0
  196. package/dist/server.js +702 -0
  197. package/dist/server.js.map +1 -0
  198. package/dist/static/webui-server.d.ts +2 -0
  199. package/dist/static/webui-server.d.ts.map +1 -0
  200. package/dist/static/webui-server.js +97 -0
  201. package/dist/static/webui-server.js.map +1 -0
  202. package/package.json +68 -0
@@ -0,0 +1,63 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { type KanbanCiGate, type KanbanIntegrationProvider, type KanbanMergeStatus, type KanbanPublishStatus, type KanbanPullRequestStatus, type KanbanReviewArtifact, type KanbanReviewCommentAnchor, type KanbanReviewFeedbackSource, type KanbanReviewDecision, type KanbanReviewStatus, type KanbanReviewSnapshot } from "@a5c-ai/comm-adapter/kanban";
3
+ export interface ReviewServiceDeps {
4
+ readFile: typeof fs.readFile;
5
+ writeFile: typeof fs.writeFile;
6
+ mkdir: typeof fs.mkdir;
7
+ reviewFilePath: string;
8
+ now: () => string;
9
+ cwd: () => string;
10
+ }
11
+ export type ReviewActionInput = {
12
+ action: "submit-review";
13
+ artifactId: string;
14
+ decision: KanbanReviewDecision;
15
+ summary?: string;
16
+ executionTargetId?: string;
17
+ } | {
18
+ action: "approve";
19
+ artifactId: string;
20
+ } | {
21
+ action: "request-changes";
22
+ artifactId: string;
23
+ } | {
24
+ action: "add-comment";
25
+ artifactId: string;
26
+ body: string;
27
+ anchor: KanbanReviewCommentAnchor;
28
+ authorName?: string;
29
+ feedbackSource?: KanbanReviewFeedbackSource;
30
+ } | {
31
+ action: "create-pull-request";
32
+ artifactId: string;
33
+ provider?: KanbanIntegrationProvider;
34
+ title: string;
35
+ reviewers?: string;
36
+ branchName?: string;
37
+ baseBranch?: string;
38
+ url?: string;
39
+ } | {
40
+ action: "link-pull-request";
41
+ artifactId: string;
42
+ provider?: KanbanIntegrationProvider;
43
+ number: number;
44
+ title: string;
45
+ status?: KanbanPullRequestStatus;
46
+ reviewStatus?: KanbanReviewStatus;
47
+ mergeStatus?: KanbanMergeStatus;
48
+ publishStatus?: KanbanPublishStatus;
49
+ ciGates?: readonly KanbanCiGate[];
50
+ branchName?: string;
51
+ baseBranch?: string;
52
+ url?: string;
53
+ };
54
+ export declare class ReviewService {
55
+ private readonly deps;
56
+ constructor(overrides?: Partial<ReviewServiceDeps>);
57
+ private readArtifacts;
58
+ listReviews(filter?: {
59
+ targetType?: KanbanReviewArtifact["targetType"];
60
+ targetId?: string;
61
+ }): Promise<KanbanReviewSnapshot>;
62
+ applyAction(input: ReviewActionInput): Promise<KanbanReviewSnapshot>;
63
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-service.d.ts","sourceRoot":"","sources":["../../../src/kanban/lib/review-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAIzC,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAEzB,KAAK,yBAAyB,EAE9B,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAC;AASrC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC;IAC7B,SAAS,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC;IAC/B,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,iBAAiB,GACzB;IACE,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GACD;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,GACD;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,yBAAyB,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,0BAA0B,CAAC;CAC7C,GACD;IACE,MAAM,EAAE,qBAAqB,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GACD;IACE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAuZN,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;gBAE7B,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM;YAIxC,aAAa;IAqBrB,WAAW,CAAC,MAAM,GAAE;QACxB,UAAU,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;QAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAKhC,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAuL3E"}
@@ -0,0 +1,571 @@
1
+ import { promises as fs } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { summarizeKanbanReviewArtifact, } from "@a5c-ai/comm-adapter/kanban";
5
+ const REVIEW_FILE_PATH = process.env.KANBAN_REVIEW_FILE ?? path.join(os.homedir(), ".a5c", "kanban-review-artifacts.json");
6
+ const defaultDeps = {
7
+ readFile: fs.readFile,
8
+ writeFile: fs.writeFile,
9
+ mkdir: fs.mkdir,
10
+ reviewFilePath: REVIEW_FILE_PATH,
11
+ now: () => new Date().toISOString(),
12
+ cwd: () => process.cwd(),
13
+ };
14
+ function parseReviewerList(value) {
15
+ return value
16
+ .split(",")
17
+ .map((entry) => entry.trim())
18
+ .filter(Boolean);
19
+ }
20
+ function nextPullRequestNumber(artifacts) {
21
+ return (Math.max(0, ...artifacts.map((artifact) => artifact.linkedPullRequest?.number ?? 0)) + 1);
22
+ }
23
+ function defaultBranchName(artifact) {
24
+ return artifact.branch?.trim() || `review/${artifact.targetLabel.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
25
+ }
26
+ function cloneCiGates(gates) {
27
+ return (gates ?? []).map((gate) => ({ ...gate }));
28
+ }
29
+ function cloneExecutionTargets(targets) {
30
+ return (targets ?? []).map((target) => ({ ...target }));
31
+ }
32
+ function defaultCiGates(provider, status) {
33
+ return [
34
+ {
35
+ id: `ci-${provider.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-build`,
36
+ name: "Build",
37
+ provider,
38
+ required: true,
39
+ status,
40
+ summary: status === "passing" ? "Build completed successfully." : "Build is waiting for the next run.",
41
+ },
42
+ {
43
+ id: `ci-${provider.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-tests`,
44
+ name: "Tests",
45
+ provider,
46
+ required: true,
47
+ status,
48
+ summary: status === "passing" ? "Tests completed successfully." : "Tests are waiting for the next run.",
49
+ },
50
+ ];
51
+ }
52
+ function linkStateForArtifact(artifact) {
53
+ return artifact.integration?.status === "connected" ? "linked" : "partially-linked";
54
+ }
55
+ function integrationStatusForArtifact(artifact) {
56
+ return artifact.integration?.status ?? artifact.linkedPullRequest?.integrationStatus ?? "connected";
57
+ }
58
+ function defaultPresentationForArtifact(targetType) {
59
+ return targetType === "workspace" ? "split" : "unified";
60
+ }
61
+ function buildDefaultArtifacts(workspacePath) {
62
+ return [
63
+ {
64
+ id: "review-issue-kanban-gap-004",
65
+ targetType: "issue",
66
+ targetId: "KANBAN-GAP-004",
67
+ targetLabel: "KANBAN-GAP-004",
68
+ title: "Review diff workflow primitives",
69
+ summary: "Shared review artifacts now cover issue diffs, workspace diffs, inline comments, and approval state.",
70
+ branch: "vk/kanban-gap-004",
71
+ decision: "pending",
72
+ queueState: "queued",
73
+ preferredPresentation: defaultPresentationForArtifact("issue"),
74
+ updatedAt: "2026-04-24T12:00:00.000Z",
75
+ executionTargets: [
76
+ {
77
+ id: "issue-run-follow-up",
78
+ kind: "run",
79
+ label: "Open linked run task",
80
+ href: "/runs/run-review-issue?effectId=eff-review-issue",
81
+ description: "Inspect the originating run task and continue the implementation loop from the review context.",
82
+ actionLabel: "Open run task",
83
+ },
84
+ {
85
+ id: "issue-session-follow-up",
86
+ kind: "session",
87
+ label: "Resume linked session",
88
+ href: "/sessions/session-review-issue",
89
+ description: "Return to the session that produced this review artifact and post the follow-up there.",
90
+ actionLabel: "Open session",
91
+ },
92
+ ],
93
+ diff: [
94
+ {
95
+ id: "issue-diff-core",
96
+ path: "packages/adapters/core/src/kanban.ts",
97
+ additions: 18,
98
+ deletions: 0,
99
+ hunks: [
100
+ {
101
+ id: "issue-diff-core-h1",
102
+ header: "@@ review primitives @@",
103
+ lines: [
104
+ { kind: "context", content: " export type KanbanDecompositionKind =" },
105
+ { kind: "add", content: "+export type KanbanReviewDecision = 'pending' | 'changes-requested' | 'approved';", newLineNumber: 24 },
106
+ { kind: "add", content: "+export interface KanbanReviewArtifact {", newLineNumber: 25 },
107
+ { kind: "add", content: "+ readonly diff: readonly KanbanDiffFile[];", newLineNumber: 26 },
108
+ { kind: "add", content: "+ readonly comments: readonly KanbanReviewComment[];", newLineNumber: 27 },
109
+ { kind: "add", content: "+}", newLineNumber: 28 },
110
+ ],
111
+ },
112
+ ],
113
+ },
114
+ {
115
+ id: "issue-diff-api",
116
+ path: "packages/adapters/gateway/src/kanban/routes.ts",
117
+ additions: 22,
118
+ deletions: 0,
119
+ hunks: [
120
+ {
121
+ id: "issue-diff-api-h1",
122
+ header: "@@ api route @@",
123
+ lines: [
124
+ { kind: "add", content: "+export async function GET() {", newLineNumber: 1 },
125
+ { kind: "add", content: "+ return NextResponse.json(await service.listReviews());", newLineNumber: 2 },
126
+ { kind: "add", content: "+}", newLineNumber: 3 },
127
+ { kind: "add", content: "+export async function POST(request: Request) {", newLineNumber: 4 },
128
+ { kind: "add", content: "+ return NextResponse.json(await service.applyAction(await request.json()));", newLineNumber: 5 },
129
+ { kind: "add", content: "+}", newLineNumber: 6 },
130
+ ],
131
+ },
132
+ ],
133
+ },
134
+ ],
135
+ comments: [
136
+ {
137
+ id: "review-issue-comment-1",
138
+ author: { kind: "agent", name: "codex-reviewer" },
139
+ body: "Keep the review model shared so issue and workspace UIs do not drift again.",
140
+ createdAt: "2026-04-24T12:02:00.000Z",
141
+ status: "open",
142
+ anchor: {
143
+ fileId: "issue-diff-core",
144
+ filePath: "packages/adapters/core/src/kanban.ts",
145
+ hunkId: "issue-diff-core-h1",
146
+ side: "head",
147
+ line: 25,
148
+ },
149
+ feedbackSource: {
150
+ kind: "agent-feedback",
151
+ label: "Mapped from codex review feedback",
152
+ sessionId: "session-review-issue",
153
+ runId: "run-review-issue",
154
+ effectId: "eff-review-issue",
155
+ messageId: "msg-review-issue-1",
156
+ },
157
+ },
158
+ ],
159
+ },
160
+ {
161
+ id: "review-workspace-kanban-gap-004",
162
+ targetType: "workspace",
163
+ targetId: workspacePath,
164
+ targetLabel: path.basename(workspacePath),
165
+ title: "Workspace review handoff",
166
+ summary: "Workspace-level review keeps the worktree diff, review queue state, and approval handoff visible in one place.",
167
+ branch: path.basename(workspacePath),
168
+ decision: "changes-requested",
169
+ queueState: "in-review",
170
+ preferredPresentation: defaultPresentationForArtifact("workspace"),
171
+ updatedAt: "2026-04-24T12:05:00.000Z",
172
+ executionTargets: [
173
+ {
174
+ id: "workspace-open",
175
+ kind: "workspace",
176
+ label: "Open workspace",
177
+ href: `/workspaces?workspace=${encodeURIComponent(workspacePath)}`,
178
+ description: "Jump back into the workspace detail view with preview, runtime, and editor affordances.",
179
+ actionLabel: "Open workspace",
180
+ },
181
+ {
182
+ id: "workspace-session-follow-up",
183
+ kind: "session",
184
+ label: "Continue linked session",
185
+ href: "/sessions/session-review-workspace",
186
+ description: "Resume the session that owns this worktree and apply the requested changes from review.",
187
+ actionLabel: "Open session",
188
+ },
189
+ {
190
+ id: "workspace-run-follow-up",
191
+ kind: "run",
192
+ label: "Inspect linked run",
193
+ href: "/runs/run-review-workspace?effectId=eff-review-workspace",
194
+ description: "Open the run/effect that produced the current review handoff for direct follow-through.",
195
+ actionLabel: "Open run",
196
+ },
197
+ ],
198
+ integration: {
199
+ provider: "github",
200
+ status: "connected",
201
+ linkState: "linked",
202
+ guidance: "Linked PR state stays synchronized with the shared review artifact for this workspace.",
203
+ prerequisites: [],
204
+ actions: {
205
+ canCreatePullRequest: true,
206
+ canManagePullRequest: true,
207
+ canApproveFromReview: true,
208
+ },
209
+ },
210
+ linkedPullRequest: {
211
+ provider: "github",
212
+ status: "changes-requested",
213
+ linkState: "linked",
214
+ title: "Workspace review handoff",
215
+ number: 604,
216
+ branchName: path.basename(workspacePath),
217
+ baseBranch: "main",
218
+ reviewStatus: "changes-requested",
219
+ mergeStatus: "blocked",
220
+ publishStatus: "not-ready",
221
+ ciGates: defaultCiGates("GitHub Actions", "pending"),
222
+ integrationStatus: "connected",
223
+ guidance: "Resolve the open review note, re-run CI, and then continue through merge readiness.",
224
+ },
225
+ diff: [
226
+ {
227
+ id: "workspace-diff-page",
228
+ path: "packages/adapters/webui/src/kanban/components/workspaces/workspaces-page.tsx",
229
+ additions: 16,
230
+ deletions: 2,
231
+ hunks: [
232
+ {
233
+ id: "workspace-diff-page-h1",
234
+ header: "@@ workspace review summary @@",
235
+ lines: [
236
+ { kind: "context", content: " function WorkspaceColumn(props: {" },
237
+ { kind: "delete", content: "- title: string;", oldLineNumber: 210 },
238
+ { kind: "add", content: "+ title: string;", newLineNumber: 210 },
239
+ { kind: "add", content: "+ reviewState?: string;", newLineNumber: 211 },
240
+ { kind: "add", content: "+ reviewOpenComments?: number;", newLineNumber: 212 },
241
+ { kind: "add", content: "+ reviewUpdatedAt?: string;", newLineNumber: 213 },
242
+ ],
243
+ },
244
+ ],
245
+ },
246
+ ],
247
+ comments: [
248
+ {
249
+ id: "review-workspace-comment-1",
250
+ author: { kind: "agent", name: "workspace-reviewer" },
251
+ body: "The workspace cards should surface approval state before someone clicks into the full diff.",
252
+ createdAt: "2026-04-24T12:06:00.000Z",
253
+ status: "open",
254
+ anchor: {
255
+ fileId: "workspace-diff-page",
256
+ filePath: "packages/adapters/webui/src/kanban/components/workspaces/workspaces-page.tsx",
257
+ hunkId: "workspace-diff-page-h1",
258
+ side: "head",
259
+ line: 211,
260
+ },
261
+ feedbackSource: {
262
+ kind: "agent-feedback",
263
+ label: "Mapped from workspace reviewer feedback",
264
+ sessionId: "session-review-workspace",
265
+ runId: "run-review-workspace",
266
+ effectId: "eff-review-workspace",
267
+ messageId: "msg-review-workspace-1",
268
+ },
269
+ },
270
+ ],
271
+ },
272
+ ];
273
+ }
274
+ function sortArtifacts(artifacts) {
275
+ const decisionRank = (decision) => {
276
+ if (decision === "changes-requested")
277
+ return 0;
278
+ if (decision === "pending")
279
+ return 1;
280
+ return 2;
281
+ };
282
+ return [...artifacts].sort((left, right) => {
283
+ const queueStateOrder = (value) => {
284
+ if (value === "queued")
285
+ return 0;
286
+ if (value === "in-review")
287
+ return 1;
288
+ return 2;
289
+ };
290
+ const stateDiff = queueStateOrder(left.queueState) - queueStateOrder(right.queueState);
291
+ if (stateDiff !== 0) {
292
+ return stateDiff;
293
+ }
294
+ const decisionDiff = decisionRank(left.decision) - decisionRank(right.decision);
295
+ if (decisionDiff !== 0) {
296
+ return decisionDiff;
297
+ }
298
+ return right.updatedAt.localeCompare(left.updatedAt);
299
+ });
300
+ }
301
+ async function readStore(deps) {
302
+ try {
303
+ const raw = await deps.readFile(deps.reviewFilePath, "utf8");
304
+ return JSON.parse(raw);
305
+ }
306
+ catch (error) {
307
+ const errno = error;
308
+ if (errno.code === "ENOENT") {
309
+ return null;
310
+ }
311
+ throw error;
312
+ }
313
+ }
314
+ async function writeStore(deps, artifacts) {
315
+ await deps.mkdir(path.dirname(deps.reviewFilePath), { recursive: true });
316
+ await deps.writeFile(deps.reviewFilePath, `${JSON.stringify({ artifacts }, null, 2)}\n`, "utf8");
317
+ }
318
+ function buildSnapshot(artifacts, generatedAt, filter = {}) {
319
+ const filteredArtifacts = sortArtifacts(artifacts.filter((artifact) => {
320
+ if (filter.targetType && artifact.targetType !== filter.targetType) {
321
+ return false;
322
+ }
323
+ if (filter.targetId && artifact.targetId !== filter.targetId) {
324
+ return false;
325
+ }
326
+ return true;
327
+ }));
328
+ const queue = filteredArtifacts.map((artifact) => {
329
+ const summary = summarizeKanbanReviewArtifact(artifact);
330
+ return {
331
+ artifactId: artifact.id,
332
+ targetType: artifact.targetType,
333
+ targetId: artifact.targetId,
334
+ targetLabel: artifact.targetLabel,
335
+ title: artifact.title,
336
+ decision: artifact.decision,
337
+ queueState: artifact.queueState,
338
+ commentCount: summary.commentCount,
339
+ openCommentCount: summary.openCommentCount,
340
+ updatedAt: artifact.updatedAt,
341
+ };
342
+ });
343
+ return {
344
+ generatedAt,
345
+ artifacts: filteredArtifacts,
346
+ queue,
347
+ summary: {
348
+ total: filteredArtifacts.length,
349
+ issueCount: filteredArtifacts.filter((artifact) => artifact.targetType === "issue").length,
350
+ workspaceCount: filteredArtifacts.filter((artifact) => artifact.targetType === "workspace").length,
351
+ pendingCount: filteredArtifacts.filter((artifact) => artifact.decision === "pending").length,
352
+ changesRequestedCount: filteredArtifacts.filter((artifact) => artifact.decision === "changes-requested").length,
353
+ approvedCount: filteredArtifacts.filter((artifact) => artifact.decision === "approved").length,
354
+ openCommentCount: filteredArtifacts.reduce((count, artifact) => count + artifact.comments.filter((comment) => comment.status === "open").length, 0),
355
+ },
356
+ };
357
+ }
358
+ function createComment(input) {
359
+ return {
360
+ id: `review-comment-${Math.random().toString(36).slice(2, 10)}`,
361
+ author: {
362
+ kind: "human",
363
+ name: input.authorName?.trim() || "Reviewer",
364
+ },
365
+ body: input.body,
366
+ createdAt: input.createdAt,
367
+ status: "open",
368
+ anchor: input.anchor,
369
+ feedbackSource: input.feedbackSource,
370
+ };
371
+ }
372
+ export class ReviewService {
373
+ deps;
374
+ constructor(overrides = {}) {
375
+ this.deps = { ...defaultDeps, ...overrides };
376
+ }
377
+ async readArtifacts() {
378
+ const store = await readStore(this.deps);
379
+ if (store?.artifacts?.length) {
380
+ return store.artifacts.map((artifact) => ({
381
+ ...artifact,
382
+ comments: [...artifact.comments],
383
+ diff: [...artifact.diff],
384
+ executionTargets: cloneExecutionTargets(artifact.executionTargets),
385
+ latestSubmission: artifact.latestSubmission ? { ...artifact.latestSubmission } : undefined,
386
+ linkedPullRequest: artifact.linkedPullRequest
387
+ ? {
388
+ ...artifact.linkedPullRequest,
389
+ ciGates: cloneCiGates(artifact.linkedPullRequest.ciGates),
390
+ }
391
+ : undefined,
392
+ }));
393
+ }
394
+ return buildDefaultArtifacts(this.deps.cwd());
395
+ }
396
+ async listReviews(filter = {}) {
397
+ const artifacts = await this.readArtifacts();
398
+ return buildSnapshot(artifacts, this.deps.now(), filter);
399
+ }
400
+ async applyAction(input) {
401
+ const artifacts = await this.readArtifacts();
402
+ const index = artifacts.findIndex((artifact) => artifact.id === input.artifactId);
403
+ if (index < 0) {
404
+ throw new Error(`Review artifact not found: ${input.artifactId}`);
405
+ }
406
+ const now = this.deps.now();
407
+ const artifact = artifacts[index];
408
+ let updatedArtifact;
409
+ if (input.action === "submit-review") {
410
+ const target = artifact.executionTargets?.find((candidate) => candidate.id === input.executionTargetId);
411
+ const summary = input.summary?.trim();
412
+ updatedArtifact = {
413
+ ...artifact,
414
+ decision: input.decision,
415
+ queueState: input.decision === "approved" ? "completed" : "in-review",
416
+ updatedAt: now,
417
+ latestSubmission: {
418
+ decision: input.decision,
419
+ summary: summary || undefined,
420
+ submittedAt: now,
421
+ executionTargetId: target?.id,
422
+ executionTargetLabel: target?.label,
423
+ },
424
+ };
425
+ }
426
+ else if (input.action === "approve") {
427
+ updatedArtifact = {
428
+ ...artifact,
429
+ decision: "approved",
430
+ queueState: "completed",
431
+ updatedAt: now,
432
+ latestSubmission: {
433
+ decision: "approved",
434
+ submittedAt: now,
435
+ },
436
+ };
437
+ }
438
+ else if (input.action === "request-changes") {
439
+ updatedArtifact = {
440
+ ...artifact,
441
+ decision: "changes-requested",
442
+ queueState: "in-review",
443
+ updatedAt: now,
444
+ latestSubmission: {
445
+ decision: "changes-requested",
446
+ submittedAt: now,
447
+ },
448
+ };
449
+ }
450
+ else if (input.action === "add-comment") {
451
+ const body = input.body.trim();
452
+ if (!body) {
453
+ throw new Error("Comment body is required.");
454
+ }
455
+ updatedArtifact = {
456
+ ...artifact,
457
+ decision: artifact.decision === "approved" ? "pending" : artifact.decision,
458
+ queueState: artifact.queueState === "completed" ? "in-review" : artifact.queueState,
459
+ updatedAt: now,
460
+ comments: [
461
+ ...artifact.comments,
462
+ createComment({
463
+ body,
464
+ anchor: input.anchor,
465
+ createdAt: now,
466
+ authorName: input.authorName,
467
+ feedbackSource: input.feedbackSource,
468
+ }),
469
+ ],
470
+ };
471
+ }
472
+ else {
473
+ const provider = input.provider ?? artifact.integration?.provider ?? artifact.linkedPullRequest?.provider ?? "github";
474
+ const integrationStatus = integrationStatusForArtifact(artifact);
475
+ const linkState = linkStateForArtifact(artifact);
476
+ const baseBranch = input.baseBranch?.trim() || artifact.linkedPullRequest?.baseBranch || "main";
477
+ const branchName = input.branchName?.trim() || artifact.linkedPullRequest?.branchName || artifact.branch || defaultBranchName(artifact);
478
+ if (input.action === "create-pull-request") {
479
+ if (artifact.integration && !artifact.integration.actions.canCreatePullRequest) {
480
+ throw new Error(artifact.integration.actions.reason ?? artifact.integration.guidance);
481
+ }
482
+ const title = input.title.trim();
483
+ if (!title) {
484
+ throw new Error("PR title is required.");
485
+ }
486
+ const reviewers = parseReviewerList(input.reviewers ?? "");
487
+ updatedArtifact = {
488
+ ...artifact,
489
+ decision: artifact.decision === "approved" ? "pending" : artifact.decision,
490
+ queueState: reviewers.length > 0 ? "in-review" : artifact.queueState === "completed" ? "queued" : artifact.queueState,
491
+ updatedAt: now,
492
+ linkedPullRequest: {
493
+ provider,
494
+ status: reviewers.length > 0 ? "in-review" : "open",
495
+ linkState,
496
+ title,
497
+ number: nextPullRequestNumber(artifacts),
498
+ url: input.url?.trim() || artifact.linkedPullRequest?.url,
499
+ branchName,
500
+ baseBranch,
501
+ reviewStatus: reviewers.length > 0 ? "pending" : "unlinked",
502
+ mergeStatus: "blocked",
503
+ publishStatus: "not-ready",
504
+ ciGates: defaultCiGates(provider === "azure-repos" ? "Azure Pipelines" : "GitHub Actions", "pending"),
505
+ integrationStatus,
506
+ guidance: artifact.integration?.guidance ??
507
+ "Linked PR created. Keep review comments and CI state synchronized from the shared review surface.",
508
+ },
509
+ };
510
+ }
511
+ else {
512
+ if (artifact.integration && !artifact.integration.actions.canManagePullRequest) {
513
+ throw new Error(artifact.integration.actions.reason ?? artifact.integration.guidance);
514
+ }
515
+ const title = input.title.trim();
516
+ if (!title) {
517
+ throw new Error("Linked PR title is required.");
518
+ }
519
+ if (!Number.isFinite(input.number) || input.number <= 0) {
520
+ throw new Error("Linked PR number must be a positive integer.");
521
+ }
522
+ const status = input.status ?? "in-review";
523
+ const reviewStatus = input.reviewStatus ??
524
+ (status === "approved"
525
+ ? "approved"
526
+ : status === "changes-requested"
527
+ ? "changes-requested"
528
+ : status === "open" || status === "draft"
529
+ ? "unlinked"
530
+ : "pending");
531
+ const mergeStatus = input.mergeStatus ?? (status === "merged" ? "merged" : "blocked");
532
+ const publishStatus = input.publishStatus ?? (status === "merged" ? "ready" : "not-ready");
533
+ updatedArtifact = {
534
+ ...artifact,
535
+ decision: reviewStatus === "approved"
536
+ ? "approved"
537
+ : reviewStatus === "changes-requested"
538
+ ? "changes-requested"
539
+ : "pending",
540
+ queueState: reviewStatus === "approved" || status === "merged" ? "completed" : "in-review",
541
+ updatedAt: now,
542
+ linkedPullRequest: {
543
+ provider,
544
+ status,
545
+ linkState,
546
+ title,
547
+ number: Math.floor(input.number),
548
+ url: input.url?.trim() || artifact.linkedPullRequest?.url,
549
+ branchName,
550
+ baseBranch,
551
+ reviewStatus,
552
+ mergeStatus,
553
+ publishStatus,
554
+ ciGates: input.ciGates && input.ciGates.length > 0
555
+ ? cloneCiGates(input.ciGates)
556
+ : artifact.linkedPullRequest?.ciGates && artifact.linkedPullRequest.ciGates.length > 0
557
+ ? cloneCiGates(artifact.linkedPullRequest.ciGates)
558
+ : defaultCiGates(provider === "azure-repos" ? "Azure Pipelines" : "GitHub Actions", "passing"),
559
+ integrationStatus,
560
+ guidance: artifact.integration?.guidance ??
561
+ "Linked PR imported into the shared review surface. Review notes now map back to the active work item.",
562
+ },
563
+ };
564
+ }
565
+ }
566
+ const nextArtifacts = artifacts.map((candidate, candidateIndex) => candidateIndex === index ? updatedArtifact : candidate);
567
+ await writeStore(this.deps, nextArtifacts);
568
+ return buildSnapshot(nextArtifacts, now);
569
+ }
570
+ }
571
+ //# sourceMappingURL=review-service.js.map