@rudderhq/server 0.2.0-canary.9 → 0.2.1
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.
- package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
- package/dist/bootstrap/register-api-routes.js +2 -0
- package/dist/bootstrap/register-api-routes.js.map +1 -1
- package/dist/bundled-plugins/plugin-linear/dist/ui/index.js +8 -1
- package/dist/bundled-plugins/plugin-linear/dist/ui/index.js.map +2 -2
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +20 -5
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
- package/dist/home-paths.d.ts +2 -0
- package/dist/home-paths.d.ts.map +1 -1
- package/dist/home-paths.js +6 -1
- package/dist/home-paths.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -2
- package/dist/index.js.map +1 -1
- package/dist/langfuse-transcript.d.ts.map +1 -1
- package/dist/langfuse-transcript.js +16 -2
- package/dist/langfuse-transcript.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +54 -1
- package/dist/middleware/auth.js.map +1 -1
- package/dist/onboarding-assets/ceo/HEARTBEAT.md +8 -4
- package/dist/onboarding-assets/default/HEARTBEAT.md +7 -4
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +79 -4
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +47 -2
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +300 -92
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/costs.d.ts.map +1 -1
- package/dist/routes/costs.js +20 -0
- package/dist/routes/costs.js.map +1 -1
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +236 -22
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/onboarding.d.ts +3 -0
- package/dist/routes/onboarding.d.ts.map +1 -0
- package/dist/routes/onboarding.js +545 -0
- package/dist/routes/onboarding.js.map +1 -0
- package/dist/routes/orgs.d.ts.map +1 -1
- package/dist/routes/orgs.js +22 -0
- package/dist/routes/orgs.js.map +1 -1
- package/dist/services/activity.d.ts.map +1 -1
- package/dist/services/activity.js +32 -1
- package/dist/services/activity.js.map +1 -1
- package/dist/services/agent-run-context.d.ts +1 -0
- package/dist/services/agent-run-context.d.ts.map +1 -1
- package/dist/services/agent-run-context.js +1 -0
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/agents.d.ts +13 -13
- package/dist/services/automations.d.ts +2 -2
- package/dist/services/calendar.d.ts +4 -4
- package/dist/services/chat-assistant.d.ts +11 -2
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.js +143 -8
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chats.d.ts +112 -13
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.js +218 -38
- package/dist/services/chats.js.map +1 -1
- package/dist/services/costs.d.ts +21 -0
- package/dist/services/costs.d.ts.map +1 -1
- package/dist/services/costs.js +102 -2
- package/dist/services/costs.js.map +1 -1
- package/dist/services/finance.d.ts +2 -2
- package/dist/services/goals.d.ts +12 -12
- package/dist/services/instance-settings.d.ts.map +1 -1
- package/dist/services/instance-settings.js +27 -16
- package/dist/services/instance-settings.js.map +1 -1
- package/dist/services/issue-approvals.d.ts +16 -2
- package/dist/services/issue-approvals.d.ts.map +1 -1
- package/dist/services/issue-approvals.js +27 -4
- package/dist/services/issue-approvals.js.map +1 -1
- package/dist/services/issue-review-wakeup.d.ts +49 -1
- package/dist/services/issue-review-wakeup.d.ts.map +1 -1
- package/dist/services/issue-review-wakeup.js +39 -2
- package/dist/services/issue-review-wakeup.js.map +1 -1
- package/dist/services/issues.d.ts +2 -1
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +126 -5
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.d.ts +1 -0
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +3 -2
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/messenger.d.ts +5 -0
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +154 -15
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/organization-workspace-browser.d.ts.map +1 -1
- package/dist/services/organization-workspace-browser.js +64 -9
- package/dist/services/organization-workspace-browser.js.map +1 -1
- package/dist/services/orgs.d.ts +1 -1
- package/dist/services/plugin-registry.d.ts +4 -4
- package/dist/services/projects.d.ts +1 -1
- package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.js +571 -31
- package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
- package/dist/services/secrets.d.ts +5 -5
- package/dist/services/workspace-backups.d.ts.map +1 -1
- package/dist/services/workspace-backups.js +6 -0
- package/dist/services/workspace-backups.js.map +1 -1
- package/dist/services/workspace-runtime.d.ts.map +1 -1
- package/dist/services/workspace-runtime.js +2 -0
- package/dist/services/workspace-runtime.js.map +1 -1
- package/package.json +13 -13
- package/resources/bundled-skills/rudder/SKILL.md +72 -7
- package/resources/bundled-skills/rudder/references/cli-reference.md +34 -9
- package/resources/bundled-skills/rudder/references/organization-skills.md +12 -7
- package/resources/bundled-skills/rudder-create-agent/references/cli-reference.md +1 -0
- package/skills/rudder/SKILL.md +72 -7
- package/skills/rudder/references/cli-reference.md +34 -9
- package/skills/rudder/references/organization-skills.md +12 -7
- package/skills/rudder-create-agent/references/cli-reference.md +1 -0
- package/ui-dist/assets/{_basePickBy-aX2f6dVl.js → _basePickBy-EvWeCTRb.js} +1 -1
- package/ui-dist/assets/{_baseUniq-BYwL7heN.js → _baseUniq-C_DXAETg.js} +1 -1
- package/ui-dist/assets/{arc-BG9f5pwY.js → arc-BWTkVM-u.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BFFQoJJ1.js → architectureDiagram-2XIMDMQ5-yyX54Dgl.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-Bvx1IB1z.js → blockDiagram-WCTKOSBZ-DleWvS8P.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-DJbCE4sh.js → c4Diagram-IC4MRINW-CltWqWC_.js} +1 -1
- package/ui-dist/assets/channel-Gdzxe2a1.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-BOVbLFsN.js → chunk-4BX2VUAB-CA6RvGN7.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-D5pKj6S9.js → chunk-55IACEB6-D_EpF39w.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-OY5xuJeR.js → chunk-FMBD7UC4-CYMkBnLy.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-C5X67KZg.js → chunk-JSJVCQXG-CIY2Cb1T.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-C-4PZ9Q_.js → chunk-KX2RTZJC-BUyGoIKj.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-XysPlqPj.js → chunk-NQ4KR5QH-DkntSLtY.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-B5wEbFHo.js → chunk-QZHKN3VN-DeEs3yL0.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-BanwYFa2.js → chunk-WL4C6EOR-Va8TkdTb.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-BN6WyuN3.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-BN6WyuN3.js +1 -0
- package/ui-dist/assets/clone-DL9OCUyP.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-Cd4q2swD.js → cose-bilkent-S5V4N54A-Bb6NLaVm.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-B_VyOhf3.js → dagre-KLK3FWXG-DpqLnZ3A.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-BRoG4Mz6.js → diagram-E7M64L7V-D7J8NbEW.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-CRU_A9p9.js → diagram-IFDJBPK2-Ds2u81Zi.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-BYSQDbfb.js → diagram-P4PSJMXO-BwBplO7L.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-v5j1kyWr.js → erDiagram-INFDFZHY-Ba-Ynr8U.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-C06ZQgTj.js → flowDiagram-PKNHOUZH-FnOXpXb_.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-Dw9p5nQ1.js → ganttDiagram-A5KZAMGK-B8-MpUjy.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-CrpXRIaP.js → gitGraphDiagram-K3NZZRJ6-DvyBGQTF.js} +1 -1
- package/ui-dist/assets/{graph-ClTUmULf.js → graph-BdpIVR-I.js} +1 -1
- package/ui-dist/assets/{index-DK13xhRv.js → index-3CPMGfu4.js} +1 -1
- package/ui-dist/assets/index-44A3IjSd.css +1 -0
- package/ui-dist/assets/{index-L6M3nVxh.js → index-B4seykMn.js} +1 -1
- package/ui-dist/assets/{index-Bpc2gRVo.js → index-B5Lq7qho.js} +1 -1
- package/ui-dist/assets/{index-DkDkjZ-D.js → index-BKWZYXO6.js} +1 -1
- package/ui-dist/assets/{index-DxzAgTWd.js → index-BO-P9C91.js} +1 -1
- package/ui-dist/assets/{index-BvGogi9q.js → index-BO9KiNr0.js} +1 -1
- package/ui-dist/assets/{index-Btwy7Cp-.js → index-Bd_GitJ7.js} +1 -1
- package/ui-dist/assets/{index-DNlWBtHa.js → index-BeyQP4jc.js} +1 -1
- package/ui-dist/assets/{index-4uxadHo5.js → index-Bp3rYm9R.js} +1 -1
- package/ui-dist/assets/{index-DWFMs9uk.js → index-CBAKsDOH.js} +1 -1
- package/ui-dist/assets/{index-T81awgzh.js → index-CWPEuLky.js} +1 -1
- package/ui-dist/assets/{index-DAhPD1Ss.js → index-Ce0xbQ5p.js} +1 -1
- package/ui-dist/assets/{index-_x9smX4T.js → index-ChyWxMPa.js} +1 -1
- package/ui-dist/assets/{index-CIr7H9OI.js → index-CkEo4bIl.js} +1 -1
- package/ui-dist/assets/{index-sLGLHxIu.js → index-CvzsgQH3.js} +1 -1
- package/ui-dist/assets/{index-D-6z8wxx.js → index-DF0X3XZi.js} +1 -1
- package/ui-dist/assets/{index-BVfM9ax8.js → index-DNFqhIup.js} +1 -1
- package/ui-dist/assets/index-Dfi8PbGx.js +1484 -0
- package/ui-dist/assets/{index-C_BTFRTX.js → index-Dys_qAzR.js} +1 -1
- package/ui-dist/assets/{index-Cr7n11UG.js → index-DzKALBsQ.js} +1 -1
- package/ui-dist/assets/{index-CqYInp-c.js → index-Qe9bMaYk.js} +1 -1
- package/ui-dist/assets/{index-CQWmziMF.js → index-baeevrWz.js} +1 -1
- package/ui-dist/assets/{index-BYC_xlrx.js → index-bs5pLhnN.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-BA3VxOIU.js → infoDiagram-LFFYTUFH-51Iz4iFI.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-DGrizi0S.js → ishikawaDiagram-PHBUUO56-XMkPw0tW.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-6ey34a7e.js → journeyDiagram-4ABVD52K-DAX0bTCG.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-CwNnmsam.js → kanban-definition-K7BYSVSG-DndcgBkd.js} +1 -1
- package/ui-dist/assets/{layout-buNxvllr.js → layout-DE8DhR5g.js} +1 -1
- package/ui-dist/assets/{linear-BPWhxaRl.js → linear-B6lAW9Wb.js} +1 -1
- package/ui-dist/assets/{mermaid.core-Cajx0s-z.js → mermaid.core-BG--kYhA.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-Bf5InEp-.js → mindmap-definition-YRQLILUH-DkjV0oE3.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-CZFz7NWC.js → pieDiagram-SKSYHLDU-D03TjqYu.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-XBmKVoc9.js → quadrantDiagram-337W2JSQ-C0oqv-xU.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-BkgdDv0H.js → requirementDiagram-Z7DCOOCP-okIS8feM.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-CASFR28i.js → sankeyDiagram-WA2Y5GQK-WOnxUdkO.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-BzY7LMRv.js → sequenceDiagram-2WXFIKYE-RVCXfMRR.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-C9UMSk36.js → stateDiagram-RAJIS63D-CZFHvVtT.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DgYYudAJ.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-D6m4R4xe.js → timeline-definition-YZTLITO2-S0uy5mlJ.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-7V9mnT8T.js → treemap-KZPCXAKY-Bhyg_yHs.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-Ci-sfAyq.js → vennDiagram-LZ73GAT5-EnVupOQz.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-BayXhRSu.js → xychartDiagram-JWTSCODW-BYpdJxGK.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/channel-ClX7n84B.js +0 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-DvWbsnVz.js +0 -1
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-DvWbsnVz.js +0 -1
- package/ui-dist/assets/clone-Dla3A8ZA.js +0 -1
- package/ui-dist/assets/index-CSANx6ee.css +0 -1
- package/ui-dist/assets/index-DCa9-Sy-.js +0 -1439
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DWVhbAdj.js +0 -1
package/dist/routes/issues.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Router } from "express";
|
|
2
2
|
import multer from "multer";
|
|
3
3
|
import { buildIssueDocumentsPrompt } from "@rudderhq/agent-runtime-utils/server-utils";
|
|
4
|
-
import { addIssueCommentSchema, createIssueAttachmentMetadataSchema, createIssueWorkspaceAttachmentSchema, createIssueWorkProductSchema, createIssueLabelSchema, checkoutIssueSchema, createIssueSchema, linkIssueApprovalSchema, issueDocumentKeySchema, reorderIssueSchema, updateIssueLabelSchema, updateIssueWorkProductSchema, upsertIssueDocumentSchema, updateIssueSchema, } from "@rudderhq/shared";
|
|
4
|
+
import { addIssueCommentSchema, createIssueAttachmentMetadataSchema, createIssueWorkspaceAttachmentSchema, createIssueWorkProductSchema, createIssueLabelSchema, checkoutIssueSchema, createIssueSchema, linkIssueApprovalSchema, reportIssueCommitSchema, issueDocumentKeySchema, reorderIssueSchema, updateIssueLabelSchema, updateIssueWorkProductSchema, upsertIssueDocumentSchema, updateIssueSchema, isUuidLike, } from "@rudderhq/shared";
|
|
5
5
|
import { validate } from "../middleware/validate.js";
|
|
6
6
|
import { accessService, agentService, executionWorkspaceService, goalService, heartbeatService, issueApprovalService, issueService, documentService, logActivity, projectService, automationService, workProductService, } from "../services/index.js";
|
|
7
7
|
import { organizationWorkspaceBrowserService } from "../services/organization-workspace-browser.js";
|
|
8
8
|
import { logger } from "../middleware/logger.js";
|
|
9
|
-
import { forbidden, HttpError, unauthorized } from "../errors.js";
|
|
9
|
+
import { forbidden, HttpError, unauthorized, unprocessable } from "../errors.js";
|
|
10
10
|
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
|
|
11
11
|
import { shouldWakeAssigneeOnCheckout } from "./issues-checkout-wakeup.js";
|
|
12
12
|
import { isAllowedContentType, MAX_ATTACHMENT_BYTES } from "../attachment-types.js";
|
|
@@ -139,6 +139,41 @@ export function issueRoutes(db, storage) {
|
|
|
139
139
|
}
|
|
140
140
|
return true;
|
|
141
141
|
}
|
|
142
|
+
function readIssueIdFromRunContext(contextSnapshot) {
|
|
143
|
+
if (!contextSnapshot || typeof contextSnapshot !== "object")
|
|
144
|
+
return null;
|
|
145
|
+
const issueId = contextSnapshot.issueId;
|
|
146
|
+
return typeof issueId === "string" && issueId.trim() ? issueId.trim() : null;
|
|
147
|
+
}
|
|
148
|
+
async function resolveAgentCommitRunId(req, res, issue) {
|
|
149
|
+
if (req.actor.type !== "agent")
|
|
150
|
+
return { ok: true, runId: null };
|
|
151
|
+
const actorAgentId = req.actor.agentId;
|
|
152
|
+
if (!actorAgentId) {
|
|
153
|
+
res.status(403).json({ error: "Agent authentication required" });
|
|
154
|
+
return { ok: false };
|
|
155
|
+
}
|
|
156
|
+
const runId = req.actor.runId?.trim();
|
|
157
|
+
if (!runId)
|
|
158
|
+
return { ok: true, runId: null };
|
|
159
|
+
if (!isUuidLike(runId)) {
|
|
160
|
+
res.status(403).json({ error: "Run context is not valid for this issue" });
|
|
161
|
+
return { ok: false };
|
|
162
|
+
}
|
|
163
|
+
const run = await heartbeat.getRun(runId);
|
|
164
|
+
const runIssueId = readIssueIdFromRunContext(run?.contextSnapshot);
|
|
165
|
+
const runBoundToIssue = issue.checkoutRunId === runId ||
|
|
166
|
+
issue.executionRunId === runId ||
|
|
167
|
+
runIssueId === issue.id;
|
|
168
|
+
if (!run ||
|
|
169
|
+
run.orgId !== issue.orgId ||
|
|
170
|
+
run.agentId !== actorAgentId ||
|
|
171
|
+
!runBoundToIssue) {
|
|
172
|
+
res.status(403).json({ error: "Run context is not valid for this issue" });
|
|
173
|
+
return { ok: false };
|
|
174
|
+
}
|
|
175
|
+
return { ok: true, runId };
|
|
176
|
+
}
|
|
142
177
|
async function normalizeIssueIdentifier(rawId) {
|
|
143
178
|
if (/^[A-Z]+-\d+$/i.test(rawId)) {
|
|
144
179
|
const issue = await svc.getByIdentifier(rawId);
|
|
@@ -158,6 +193,29 @@ export function issueRoutes(db, storage) {
|
|
|
158
193
|
function isReviewerAgentForIssue(actor, issue) {
|
|
159
194
|
return actor.actorType === "agent" && Boolean(actor.agentId) && actor.agentId === issue.reviewerAgentId;
|
|
160
195
|
}
|
|
196
|
+
function statusForReviewDecision(decision) {
|
|
197
|
+
switch (decision) {
|
|
198
|
+
case "approve":
|
|
199
|
+
return "done";
|
|
200
|
+
case "request_changes":
|
|
201
|
+
return "in_progress";
|
|
202
|
+
case "blocked":
|
|
203
|
+
return "blocked";
|
|
204
|
+
case "needs_followup":
|
|
205
|
+
return null;
|
|
206
|
+
default:
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function statusAcceptsReviewerDecision(status) {
|
|
211
|
+
return status === "in_review" || status === "blocked";
|
|
212
|
+
}
|
|
213
|
+
function reviewerDecisionRequiresHumanHandoff(decision) {
|
|
214
|
+
return decision === "blocked";
|
|
215
|
+
}
|
|
216
|
+
function commitSubject(message) {
|
|
217
|
+
return message.split(/\r?\n/, 1)[0]?.trim() || message.trim();
|
|
218
|
+
}
|
|
161
219
|
// Resolve issue identifiers (e.g. "PAP-39") to UUIDs for all /issues/:id routes
|
|
162
220
|
router.param("id", async (req, res, next, rawId) => {
|
|
163
221
|
try {
|
|
@@ -816,7 +874,7 @@ export function issueRoutes(db, storage) {
|
|
|
816
874
|
if (!(await assertCanManageIssueApprovalLinks(req, res, issue.orgId)))
|
|
817
875
|
return;
|
|
818
876
|
const actor = getActorInfo(req);
|
|
819
|
-
await issueApprovalsSvc.link(id, req.body.approvalId, {
|
|
877
|
+
const link = await issueApprovalsSvc.link(id, req.body.approvalId, {
|
|
820
878
|
agentId: actor.agentId,
|
|
821
879
|
userId: actor.actorType === "user" ? actor.actorId : null,
|
|
822
880
|
});
|
|
@@ -829,7 +887,10 @@ export function issueRoutes(db, storage) {
|
|
|
829
887
|
action: "issue.approval_linked",
|
|
830
888
|
entityType: "issue",
|
|
831
889
|
entityId: issue.id,
|
|
832
|
-
details: {
|
|
890
|
+
details: {
|
|
891
|
+
approvalId: req.body.approvalId,
|
|
892
|
+
...(link?.createdAt ? { linkCreatedAt: link.createdAt.toISOString() } : {}),
|
|
893
|
+
},
|
|
833
894
|
});
|
|
834
895
|
const approvals = await issueApprovalsSvc.listApprovalsForIssue(id);
|
|
835
896
|
res.status(201).json(approvals);
|
|
@@ -866,10 +927,18 @@ export function issueRoutes(db, storage) {
|
|
|
866
927
|
await assertCanAssignTasks(req, orgId);
|
|
867
928
|
}
|
|
868
929
|
const actor = getActorInfo(req);
|
|
869
|
-
const
|
|
930
|
+
const createInput = {
|
|
870
931
|
...req.body,
|
|
871
932
|
createdByAgentId: actor.agentId,
|
|
872
933
|
createdByUserId: actor.actorType === "user" ? actor.actorId : null,
|
|
934
|
+
};
|
|
935
|
+
const hasExplicitAssignee = Object.prototype.hasOwnProperty.call(req.body, "assigneeAgentId") ||
|
|
936
|
+
Object.prototype.hasOwnProperty.call(req.body, "assigneeUserId");
|
|
937
|
+
if (actor.actorType === "agent" && actor.agentId && !hasExplicitAssignee) {
|
|
938
|
+
createInput.assigneeAgentId = actor.agentId;
|
|
939
|
+
}
|
|
940
|
+
const issue = await svc.create(orgId, {
|
|
941
|
+
...createInput,
|
|
873
942
|
});
|
|
874
943
|
await logActivity(db, {
|
|
875
944
|
orgId,
|
|
@@ -933,18 +1002,36 @@ export function issueRoutes(db, storage) {
|
|
|
933
1002
|
return;
|
|
934
1003
|
const actor = getActorInfo(req);
|
|
935
1004
|
const isClosed = existing.status === "done" || existing.status === "cancelled";
|
|
936
|
-
const { comment: commentBody, reopen: reopenRequested, hiddenAt: hiddenAtRaw, ...updateFields } = req.body;
|
|
1005
|
+
const { comment: commentBody, reopen: reopenRequested, hiddenAt: hiddenAtRaw, reviewDecision, ...updateFields } = req.body;
|
|
937
1006
|
if (hiddenAtRaw !== undefined) {
|
|
938
1007
|
updateFields.hiddenAt = hiddenAtRaw ? new Date(hiddenAtRaw) : null;
|
|
939
1008
|
}
|
|
940
1009
|
if (commentBody && reopenRequested === true && isClosed && updateFields.status === undefined) {
|
|
941
1010
|
updateFields.status = "todo";
|
|
942
1011
|
}
|
|
1012
|
+
if (reviewDecision !== undefined) {
|
|
1013
|
+
if (!commentBody) {
|
|
1014
|
+
throw unprocessable("Reviewer decisions require a comment");
|
|
1015
|
+
}
|
|
1016
|
+
if (!statusAcceptsReviewerDecision(existing.status)) {
|
|
1017
|
+
throw unprocessable("Reviewer decisions can only be recorded while the issue is in_review or blocked");
|
|
1018
|
+
}
|
|
1019
|
+
if (actor.actorType === "agent" && !isReviewerAgentForIssue(actor, existing)) {
|
|
1020
|
+
throw forbidden("Only the reviewer agent can record a reviewer decision");
|
|
1021
|
+
}
|
|
1022
|
+
const decisionStatus = statusForReviewDecision(reviewDecision);
|
|
1023
|
+
if (decisionStatus) {
|
|
1024
|
+
updateFields.status = decisionStatus;
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
delete updateFields.status;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
943
1030
|
let reviewedCompletionNormalized = false;
|
|
944
1031
|
if (updateFields.status === "done" &&
|
|
945
1032
|
issueHasReviewer(existing) &&
|
|
946
1033
|
actor.actorType === "agent" &&
|
|
947
|
-
!(existing.status
|
|
1034
|
+
!(statusAcceptsReviewerDecision(existing.status) && isReviewerAgentForIssue(actor, existing))) {
|
|
948
1035
|
updateFields.status = "in_review";
|
|
949
1036
|
reviewedCompletionNormalized = true;
|
|
950
1037
|
}
|
|
@@ -1045,20 +1132,66 @@ export function issueRoutes(db, storage) {
|
|
|
1045
1132
|
},
|
|
1046
1133
|
});
|
|
1047
1134
|
}
|
|
1135
|
+
if (reviewDecision !== undefined) {
|
|
1136
|
+
const reviewOutcome = reviewDecision === "blocked" ? "human_handoff" : "review_closed";
|
|
1137
|
+
await logActivity(db, {
|
|
1138
|
+
orgId: issue.orgId,
|
|
1139
|
+
actorType: actor.actorType,
|
|
1140
|
+
actorId: actor.actorId,
|
|
1141
|
+
agentId: actor.agentId,
|
|
1142
|
+
runId: actor.runId,
|
|
1143
|
+
action: "issue.review_decision_recorded",
|
|
1144
|
+
entityType: "issue",
|
|
1145
|
+
entityId: issue.id,
|
|
1146
|
+
details: {
|
|
1147
|
+
decision: reviewDecision,
|
|
1148
|
+
outcome: reviewOutcome,
|
|
1149
|
+
operatorActionRequired: reviewOutcome === "human_handoff",
|
|
1150
|
+
status: issue.status,
|
|
1151
|
+
identifier: issue.identifier,
|
|
1152
|
+
commentId: comment?.id ?? null,
|
|
1153
|
+
},
|
|
1154
|
+
});
|
|
1155
|
+
if (reviewerDecisionRequiresHumanHandoff(reviewDecision)) {
|
|
1156
|
+
await logActivity(db, {
|
|
1157
|
+
orgId: issue.orgId,
|
|
1158
|
+
actorType: actor.actorType,
|
|
1159
|
+
actorId: actor.actorId,
|
|
1160
|
+
agentId: actor.agentId,
|
|
1161
|
+
runId: actor.runId,
|
|
1162
|
+
action: "issue.human_intervention_required",
|
|
1163
|
+
entityType: "issue",
|
|
1164
|
+
entityId: issue.id,
|
|
1165
|
+
details: {
|
|
1166
|
+
decision: reviewDecision,
|
|
1167
|
+
status: issue.status,
|
|
1168
|
+
identifier: issue.identifier,
|
|
1169
|
+
issueTitle: issue.title,
|
|
1170
|
+
commentId: comment?.id ?? null,
|
|
1171
|
+
previousReviewerAgentId: existing.reviewerAgentId,
|
|
1172
|
+
previousReviewerUserId: existing.reviewerUserId,
|
|
1173
|
+
nextAction: "Human/operator intervention is required before agent review can continue.",
|
|
1174
|
+
},
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1048
1178
|
const assigneeChanged = assigneeWillChange;
|
|
1049
1179
|
const reviewerChanged = reviewerWillChange;
|
|
1050
1180
|
const statusChangedFromBacklog = existing.status === "backlog" &&
|
|
1051
1181
|
issue.status !== "backlog" &&
|
|
1052
|
-
|
|
1182
|
+
updateFields.status !== undefined;
|
|
1053
1183
|
const statusChangedToInReview = existing.status !== "in_review" &&
|
|
1054
1184
|
issue.status === "in_review" &&
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
issue.status === "
|
|
1058
|
-
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1185
|
+
updateFields.status !== undefined;
|
|
1186
|
+
const statusChangedToBlocked = existing.status !== "blocked" &&
|
|
1187
|
+
issue.status === "blocked" &&
|
|
1188
|
+
updateFields.status !== undefined;
|
|
1189
|
+
const statusReturnedFromReviewToAssignee = statusAcceptsReviewerDecision(existing.status) &&
|
|
1190
|
+
(issue.status === "in_progress" || issue.status === "todo") &&
|
|
1191
|
+
updateFields.status !== undefined;
|
|
1192
|
+
const reviewerChangedInReviewableStatus = reviewerChanged &&
|
|
1193
|
+
statusAcceptsReviewerDecision(existing.status) &&
|
|
1194
|
+
statusAcceptsReviewerDecision(issue.status);
|
|
1062
1195
|
// Merge all wakeups from this update into one enqueue per agent to avoid duplicate runs.
|
|
1063
1196
|
void (async () => {
|
|
1064
1197
|
const wakeups = new Map();
|
|
@@ -1108,16 +1241,33 @@ export function issueRoutes(db, storage) {
|
|
|
1108
1241
|
},
|
|
1109
1242
|
});
|
|
1110
1243
|
}
|
|
1111
|
-
if (!assigneeChanged &&
|
|
1244
|
+
if (!assigneeChanged && statusReturnedFromReviewToAssignee && issue.assigneeAgentId) {
|
|
1245
|
+
const commentContext = comment
|
|
1246
|
+
? {
|
|
1247
|
+
commentId: comment.id,
|
|
1248
|
+
wakeCommentId: comment.id,
|
|
1249
|
+
comment: {
|
|
1250
|
+
id: comment.id,
|
|
1251
|
+
body: comment.body,
|
|
1252
|
+
authorAgentId: comment.authorAgentId,
|
|
1253
|
+
authorUserId: comment.authorUserId,
|
|
1254
|
+
},
|
|
1255
|
+
}
|
|
1256
|
+
: {};
|
|
1112
1257
|
wakeups.set(issue.assigneeAgentId, {
|
|
1113
1258
|
source: "assignment",
|
|
1114
1259
|
triggerDetail: "system",
|
|
1115
1260
|
reason: "issue_changes_requested",
|
|
1116
|
-
payload: {
|
|
1261
|
+
payload: {
|
|
1262
|
+
issueId: issue.id,
|
|
1263
|
+
mutation: "review_changes_requested",
|
|
1264
|
+
...(comment ? { commentId: comment.id } : {}),
|
|
1265
|
+
},
|
|
1117
1266
|
requestedByActorType: actor.actorType,
|
|
1118
1267
|
requestedByActorId: actor.actorId,
|
|
1119
1268
|
contextSnapshot: {
|
|
1120
1269
|
issueId: issue.id,
|
|
1270
|
+
taskId: issue.id,
|
|
1121
1271
|
source: "issue.review_changes_requested",
|
|
1122
1272
|
wakeSource: "assignment",
|
|
1123
1273
|
wakeReason: "issue_changes_requested",
|
|
@@ -1128,19 +1278,26 @@ export function issueRoutes(db, storage) {
|
|
|
1128
1278
|
status: issue.status,
|
|
1129
1279
|
priority: issue.priority,
|
|
1130
1280
|
},
|
|
1281
|
+
...commentContext,
|
|
1131
1282
|
},
|
|
1132
1283
|
});
|
|
1133
1284
|
}
|
|
1134
|
-
if ((statusChangedToInReview ||
|
|
1135
|
-
const mutation = statusChangedToInReview
|
|
1285
|
+
if ((statusChangedToInReview || statusChangedToBlocked || reviewerChangedInReviewableStatus) && issue.reviewerAgentId) {
|
|
1286
|
+
const mutation = statusChangedToInReview
|
|
1287
|
+
? "status_to_in_review"
|
|
1288
|
+
: statusChangedToBlocked
|
|
1289
|
+
? "status_to_blocked"
|
|
1290
|
+
: issue.status === "blocked"
|
|
1291
|
+
? "reviewer_changed_blocked"
|
|
1292
|
+
: "reviewer_changed_in_review";
|
|
1136
1293
|
const actorIsReviewerAgent = actor.actorType === "agent" && actor.actorId === issue.reviewerAgentId;
|
|
1137
1294
|
const actorIsAssigneeAgent = actor.actorType === "agent" && actor.actorId === issue.assigneeAgentId;
|
|
1138
|
-
const assigneeHandoffToReview = statusChangedToInReview && actorIsAssigneeAgent;
|
|
1295
|
+
const assigneeHandoffToReview = (statusChangedToInReview || statusChangedToBlocked) && actorIsAssigneeAgent;
|
|
1139
1296
|
if (!actorIsReviewerAgent || assigneeHandoffToReview) {
|
|
1140
1297
|
wakeups.set(issue.reviewerAgentId, buildIssueReviewWakeupOptions({
|
|
1141
1298
|
issue,
|
|
1142
1299
|
mutation,
|
|
1143
|
-
contextSource: statusChangedToInReview ? "issue.status_change" : "issue.reviewer_change",
|
|
1300
|
+
contextSource: statusChangedToInReview || statusChangedToBlocked ? "issue.status_change" : "issue.reviewer_change",
|
|
1144
1301
|
requestedByActorType: actor.actorType,
|
|
1145
1302
|
requestedByActorId: actor.actorId,
|
|
1146
1303
|
}));
|
|
@@ -1324,6 +1481,62 @@ export function issueRoutes(db, storage) {
|
|
|
1324
1481
|
});
|
|
1325
1482
|
res.json(released);
|
|
1326
1483
|
});
|
|
1484
|
+
router.post("/issues/:id/commit", validate(reportIssueCommitSchema), async (req, res) => {
|
|
1485
|
+
const id = req.params.id;
|
|
1486
|
+
const issue = await svc.getById(id);
|
|
1487
|
+
if (!issue) {
|
|
1488
|
+
res.status(404).json({ error: "Issue not found" });
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
assertCompanyAccess(req, issue.orgId);
|
|
1492
|
+
if (req.actor.type !== "agent") {
|
|
1493
|
+
res.status(403).json({ error: "Agent authentication required" });
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
if (!(await assertAgentRunCheckoutOwnership(req, res, issue)))
|
|
1497
|
+
return;
|
|
1498
|
+
const actor = getActorInfo(req);
|
|
1499
|
+
const commitRun = await resolveAgentCommitRunId(req, res, issue);
|
|
1500
|
+
if (!commitRun.ok)
|
|
1501
|
+
return;
|
|
1502
|
+
const sha = req.body.sha.trim().toLowerCase();
|
|
1503
|
+
const subject = commitSubject(req.body.message);
|
|
1504
|
+
const shortSha = sha.slice(0, 7);
|
|
1505
|
+
if (commitRun.runId) {
|
|
1506
|
+
await heartbeat.reportRunActivity(commitRun.runId).catch((err) => logger.warn({ err, runId: commitRun.runId }, "failed to clear detached run warning after issue commit activity"));
|
|
1507
|
+
}
|
|
1508
|
+
await logActivity(db, {
|
|
1509
|
+
orgId: issue.orgId,
|
|
1510
|
+
actorType: actor.actorType,
|
|
1511
|
+
actorId: actor.actorId,
|
|
1512
|
+
agentId: actor.agentId,
|
|
1513
|
+
runId: commitRun.runId,
|
|
1514
|
+
action: "issue.code_committed",
|
|
1515
|
+
entityType: "issue",
|
|
1516
|
+
entityId: issue.id,
|
|
1517
|
+
details: {
|
|
1518
|
+
sha,
|
|
1519
|
+
shortSha,
|
|
1520
|
+
message: req.body.message,
|
|
1521
|
+
subject,
|
|
1522
|
+
identifier: issue.identifier,
|
|
1523
|
+
issueTitle: issue.title,
|
|
1524
|
+
branch: req.body.branch ?? null,
|
|
1525
|
+
repoPath: req.body.repoPath ?? null,
|
|
1526
|
+
workspacePath: req.body.workspacePath ?? null,
|
|
1527
|
+
commitCount: req.body.commitCount ?? 1,
|
|
1528
|
+
},
|
|
1529
|
+
});
|
|
1530
|
+
res.status(201).json({
|
|
1531
|
+
ok: true,
|
|
1532
|
+
issueId: issue.id,
|
|
1533
|
+
sha,
|
|
1534
|
+
shortSha,
|
|
1535
|
+
message: req.body.message,
|
|
1536
|
+
subject,
|
|
1537
|
+
runId: commitRun.runId,
|
|
1538
|
+
});
|
|
1539
|
+
});
|
|
1327
1540
|
router.get("/issues/:id/comments", async (req, res) => {
|
|
1328
1541
|
const id = req.params.id;
|
|
1329
1542
|
const issue = await svc.getById(id);
|
|
@@ -1484,7 +1697,8 @@ export function issueRoutes(db, storage) {
|
|
|
1484
1697
|
const assigneeId = currentIssue.assigneeAgentId;
|
|
1485
1698
|
const actorIsAgent = actor.actorType === "agent";
|
|
1486
1699
|
const selfComment = actorIsAgent && actor.actorId === assigneeId;
|
|
1487
|
-
const
|
|
1700
|
+
const backlogComment = currentIssue.status === "backlog";
|
|
1701
|
+
const skipWake = selfComment || isClosed || backlogComment;
|
|
1488
1702
|
if (assigneeId && (reopened || !skipWake)) {
|
|
1489
1703
|
if (reopened) {
|
|
1490
1704
|
wakeups.set(assigneeId, {
|