@rudderhq/server 0.2.0-canary.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +17 -3
- 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 +62 -3
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +30 -1
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +285 -46
- 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 +229 -19
- 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 +10 -1
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.js +102 -3
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chats.d.ts +87 -12
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.js +185 -10
- 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 +76 -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 +25 -16
- package/dist/services/instance-settings.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 +147 -9
- 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 +567 -29
- 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-3Hg7N37c.js} +1 -1
- package/ui-dist/assets/{_baseUniq-BYwL7heN.js → _baseUniq-Bvy8WJh0.js} +1 -1
- package/ui-dist/assets/{arc-BG9f5pwY.js → arc-DrmvGX4U.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BFFQoJJ1.js → architectureDiagram-2XIMDMQ5-vbevcV-8.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-Bvx1IB1z.js → blockDiagram-WCTKOSBZ-DvupMRN9.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-DJbCE4sh.js → c4Diagram-IC4MRINW-CbsNVA8e.js} +1 -1
- package/ui-dist/assets/channel-DhW0A-FV.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-BOVbLFsN.js → chunk-4BX2VUAB-BL4OUqNV.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-D5pKj6S9.js → chunk-55IACEB6-DFwq2ebc.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-OY5xuJeR.js → chunk-FMBD7UC4-Cyl6kF9G.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-C5X67KZg.js → chunk-JSJVCQXG-v4mfLtsY.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-C-4PZ9Q_.js → chunk-KX2RTZJC-Bfg48g5k.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-XysPlqPj.js → chunk-NQ4KR5QH-BcSdbequ.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-B5wEbFHo.js → chunk-QZHKN3VN-BT8QI712.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-BanwYFa2.js → chunk-WL4C6EOR-CqH2or9g.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-Bw6kzUsz.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Bw6kzUsz.js +1 -0
- package/ui-dist/assets/clone-Luak8Fsn.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-Cd4q2swD.js → cose-bilkent-S5V4N54A-CLH06Lnz.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-B_VyOhf3.js → dagre-KLK3FWXG-DxNQPDBj.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-BRoG4Mz6.js → diagram-E7M64L7V-BOcSeWh0.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-CRU_A9p9.js → diagram-IFDJBPK2-DXyaFKVr.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-BYSQDbfb.js → diagram-P4PSJMXO-DhY_ls3C.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-v5j1kyWr.js → erDiagram-INFDFZHY-QtL5Yt_b.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-C06ZQgTj.js → flowDiagram-PKNHOUZH-BYqyaowc.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-Dw9p5nQ1.js → ganttDiagram-A5KZAMGK-D4xd7J_z.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-CrpXRIaP.js → gitGraphDiagram-K3NZZRJ6-Co9xqKNH.js} +1 -1
- package/ui-dist/assets/{graph-ClTUmULf.js → graph-DEC7S98H.js} +1 -1
- package/ui-dist/assets/{index-sLGLHxIu.js → index-4_gJOU3u.js} +1 -1
- package/ui-dist/assets/{index-Cr7n11UG.js → index-B8QjK4Xd.js} +1 -1
- package/ui-dist/assets/index-BLDnKx7N.js +1478 -0
- package/ui-dist/assets/{index-DxzAgTWd.js → index-BX6QyxsL.js} +1 -1
- package/ui-dist/assets/{index-CIr7H9OI.js → index-BZGiyL9p.js} +1 -1
- package/ui-dist/assets/{index-DK13xhRv.js → index-BelfAyHh.js} +1 -1
- package/ui-dist/assets/index-BisI78wU.css +1 -0
- package/ui-dist/assets/{index-D-6z8wxx.js → index-Bm86s0IY.js} +1 -1
- package/ui-dist/assets/{index-T81awgzh.js → index-Bz0jEwWG.js} +1 -1
- package/ui-dist/assets/{index-CqYInp-c.js → index-CFANc8oH.js} +1 -1
- package/ui-dist/assets/{index-Btwy7Cp-.js → index-CIAMqUzr.js} +1 -1
- package/ui-dist/assets/{index-L6M3nVxh.js → index-ClrueuiI.js} +1 -1
- package/ui-dist/assets/{index-C_BTFRTX.js → index-CpxwEuIg.js} +1 -1
- package/ui-dist/assets/{index-CQWmziMF.js → index-D1ZkASZY.js} +1 -1
- package/ui-dist/assets/{index-DWFMs9uk.js → index-DUP0i_Iv.js} +1 -1
- package/ui-dist/assets/{index-BVfM9ax8.js → index-DawkXomB.js} +1 -1
- package/ui-dist/assets/{index-DNlWBtHa.js → index-DxchV0Z7.js} +1 -1
- package/ui-dist/assets/{index-DkDkjZ-D.js → index-Dzd88G_H.js} +1 -1
- package/ui-dist/assets/{index-BvGogi9q.js → index-SklGX83C.js} +1 -1
- package/ui-dist/assets/{index-Bpc2gRVo.js → index-_xX3B4n0.js} +1 -1
- package/ui-dist/assets/{index-_x9smX4T.js → index-bVqVfFu5.js} +1 -1
- package/ui-dist/assets/{index-DAhPD1Ss.js → index-eIjkqSkc.js} +1 -1
- package/ui-dist/assets/{index-4uxadHo5.js → index-mIrYeZR2.js} +1 -1
- package/ui-dist/assets/{index-BYC_xlrx.js → index-xg2FQeSA.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-BA3VxOIU.js → infoDiagram-LFFYTUFH-BQ0qsBJ6.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-DGrizi0S.js → ishikawaDiagram-PHBUUO56-B1u2RAnY.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-6ey34a7e.js → journeyDiagram-4ABVD52K-Dv5wJGwT.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-CwNnmsam.js → kanban-definition-K7BYSVSG-CJOykCsT.js} +1 -1
- package/ui-dist/assets/{layout-buNxvllr.js → layout-BDcM6t-f.js} +1 -1
- package/ui-dist/assets/{linear-BPWhxaRl.js → linear-B9Sm5Y96.js} +1 -1
- package/ui-dist/assets/{mermaid.core-Cajx0s-z.js → mermaid.core-lZPaf_Ix.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-Bf5InEp-.js → mindmap-definition-YRQLILUH-Cu4HfP8K.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-CZFz7NWC.js → pieDiagram-SKSYHLDU-B_v-Vluc.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-XBmKVoc9.js → quadrantDiagram-337W2JSQ-BU1ZwGcS.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-BkgdDv0H.js → requirementDiagram-Z7DCOOCP-DBOqB50G.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-CASFR28i.js → sankeyDiagram-WA2Y5GQK-CsXDIOlq.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-BzY7LMRv.js → sequenceDiagram-2WXFIKYE-Cmgr7vKy.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-C9UMSk36.js → stateDiagram-RAJIS63D-Bd0uRbWd.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-qGaY7iN1.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-D6m4R4xe.js → timeline-definition-YZTLITO2-B9OfCgYQ.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-7V9mnT8T.js → treemap-KZPCXAKY-FWWMNo03.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-Ci-sfAyq.js → vennDiagram-LZ73GAT5-CGs3T7cn.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-BayXhRSu.js → xychartDiagram-JWTSCODW-BJ6DrP1k.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 {
|
|
@@ -866,10 +924,18 @@ export function issueRoutes(db, storage) {
|
|
|
866
924
|
await assertCanAssignTasks(req, orgId);
|
|
867
925
|
}
|
|
868
926
|
const actor = getActorInfo(req);
|
|
869
|
-
const
|
|
927
|
+
const createInput = {
|
|
870
928
|
...req.body,
|
|
871
929
|
createdByAgentId: actor.agentId,
|
|
872
930
|
createdByUserId: actor.actorType === "user" ? actor.actorId : null,
|
|
931
|
+
};
|
|
932
|
+
const hasExplicitAssignee = Object.prototype.hasOwnProperty.call(req.body, "assigneeAgentId") ||
|
|
933
|
+
Object.prototype.hasOwnProperty.call(req.body, "assigneeUserId");
|
|
934
|
+
if (actor.actorType === "agent" && actor.agentId && !hasExplicitAssignee) {
|
|
935
|
+
createInput.assigneeAgentId = actor.agentId;
|
|
936
|
+
}
|
|
937
|
+
const issue = await svc.create(orgId, {
|
|
938
|
+
...createInput,
|
|
873
939
|
});
|
|
874
940
|
await logActivity(db, {
|
|
875
941
|
orgId,
|
|
@@ -933,18 +999,36 @@ export function issueRoutes(db, storage) {
|
|
|
933
999
|
return;
|
|
934
1000
|
const actor = getActorInfo(req);
|
|
935
1001
|
const isClosed = existing.status === "done" || existing.status === "cancelled";
|
|
936
|
-
const { comment: commentBody, reopen: reopenRequested, hiddenAt: hiddenAtRaw, ...updateFields } = req.body;
|
|
1002
|
+
const { comment: commentBody, reopen: reopenRequested, hiddenAt: hiddenAtRaw, reviewDecision, ...updateFields } = req.body;
|
|
937
1003
|
if (hiddenAtRaw !== undefined) {
|
|
938
1004
|
updateFields.hiddenAt = hiddenAtRaw ? new Date(hiddenAtRaw) : null;
|
|
939
1005
|
}
|
|
940
1006
|
if (commentBody && reopenRequested === true && isClosed && updateFields.status === undefined) {
|
|
941
1007
|
updateFields.status = "todo";
|
|
942
1008
|
}
|
|
1009
|
+
if (reviewDecision !== undefined) {
|
|
1010
|
+
if (!commentBody) {
|
|
1011
|
+
throw unprocessable("Reviewer decisions require a comment");
|
|
1012
|
+
}
|
|
1013
|
+
if (!statusAcceptsReviewerDecision(existing.status)) {
|
|
1014
|
+
throw unprocessable("Reviewer decisions can only be recorded while the issue is in_review or blocked");
|
|
1015
|
+
}
|
|
1016
|
+
if (actor.actorType === "agent" && !isReviewerAgentForIssue(actor, existing)) {
|
|
1017
|
+
throw forbidden("Only the reviewer agent can record a reviewer decision");
|
|
1018
|
+
}
|
|
1019
|
+
const decisionStatus = statusForReviewDecision(reviewDecision);
|
|
1020
|
+
if (decisionStatus) {
|
|
1021
|
+
updateFields.status = decisionStatus;
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
delete updateFields.status;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
943
1027
|
let reviewedCompletionNormalized = false;
|
|
944
1028
|
if (updateFields.status === "done" &&
|
|
945
1029
|
issueHasReviewer(existing) &&
|
|
946
1030
|
actor.actorType === "agent" &&
|
|
947
|
-
!(existing.status
|
|
1031
|
+
!(statusAcceptsReviewerDecision(existing.status) && isReviewerAgentForIssue(actor, existing))) {
|
|
948
1032
|
updateFields.status = "in_review";
|
|
949
1033
|
reviewedCompletionNormalized = true;
|
|
950
1034
|
}
|
|
@@ -1045,20 +1129,66 @@ export function issueRoutes(db, storage) {
|
|
|
1045
1129
|
},
|
|
1046
1130
|
});
|
|
1047
1131
|
}
|
|
1132
|
+
if (reviewDecision !== undefined) {
|
|
1133
|
+
const reviewOutcome = reviewDecision === "blocked" ? "human_handoff" : "review_closed";
|
|
1134
|
+
await logActivity(db, {
|
|
1135
|
+
orgId: issue.orgId,
|
|
1136
|
+
actorType: actor.actorType,
|
|
1137
|
+
actorId: actor.actorId,
|
|
1138
|
+
agentId: actor.agentId,
|
|
1139
|
+
runId: actor.runId,
|
|
1140
|
+
action: "issue.review_decision_recorded",
|
|
1141
|
+
entityType: "issue",
|
|
1142
|
+
entityId: issue.id,
|
|
1143
|
+
details: {
|
|
1144
|
+
decision: reviewDecision,
|
|
1145
|
+
outcome: reviewOutcome,
|
|
1146
|
+
operatorActionRequired: reviewOutcome === "human_handoff",
|
|
1147
|
+
status: issue.status,
|
|
1148
|
+
identifier: issue.identifier,
|
|
1149
|
+
commentId: comment?.id ?? null,
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
if (reviewerDecisionRequiresHumanHandoff(reviewDecision)) {
|
|
1153
|
+
await logActivity(db, {
|
|
1154
|
+
orgId: issue.orgId,
|
|
1155
|
+
actorType: actor.actorType,
|
|
1156
|
+
actorId: actor.actorId,
|
|
1157
|
+
agentId: actor.agentId,
|
|
1158
|
+
runId: actor.runId,
|
|
1159
|
+
action: "issue.human_intervention_required",
|
|
1160
|
+
entityType: "issue",
|
|
1161
|
+
entityId: issue.id,
|
|
1162
|
+
details: {
|
|
1163
|
+
decision: reviewDecision,
|
|
1164
|
+
status: issue.status,
|
|
1165
|
+
identifier: issue.identifier,
|
|
1166
|
+
issueTitle: issue.title,
|
|
1167
|
+
commentId: comment?.id ?? null,
|
|
1168
|
+
previousReviewerAgentId: existing.reviewerAgentId,
|
|
1169
|
+
previousReviewerUserId: existing.reviewerUserId,
|
|
1170
|
+
nextAction: "Human/operator intervention is required before agent review can continue.",
|
|
1171
|
+
},
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1048
1175
|
const assigneeChanged = assigneeWillChange;
|
|
1049
1176
|
const reviewerChanged = reviewerWillChange;
|
|
1050
1177
|
const statusChangedFromBacklog = existing.status === "backlog" &&
|
|
1051
1178
|
issue.status !== "backlog" &&
|
|
1052
|
-
|
|
1179
|
+
updateFields.status !== undefined;
|
|
1053
1180
|
const statusChangedToInReview = existing.status !== "in_review" &&
|
|
1054
1181
|
issue.status === "in_review" &&
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
issue.status === "
|
|
1058
|
-
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1182
|
+
updateFields.status !== undefined;
|
|
1183
|
+
const statusChangedToBlocked = existing.status !== "blocked" &&
|
|
1184
|
+
issue.status === "blocked" &&
|
|
1185
|
+
updateFields.status !== undefined;
|
|
1186
|
+
const statusReturnedFromReviewToAssignee = statusAcceptsReviewerDecision(existing.status) &&
|
|
1187
|
+
(issue.status === "in_progress" || issue.status === "todo") &&
|
|
1188
|
+
updateFields.status !== undefined;
|
|
1189
|
+
const reviewerChangedInReviewableStatus = reviewerChanged &&
|
|
1190
|
+
statusAcceptsReviewerDecision(existing.status) &&
|
|
1191
|
+
statusAcceptsReviewerDecision(issue.status);
|
|
1062
1192
|
// Merge all wakeups from this update into one enqueue per agent to avoid duplicate runs.
|
|
1063
1193
|
void (async () => {
|
|
1064
1194
|
const wakeups = new Map();
|
|
@@ -1108,16 +1238,33 @@ export function issueRoutes(db, storage) {
|
|
|
1108
1238
|
},
|
|
1109
1239
|
});
|
|
1110
1240
|
}
|
|
1111
|
-
if (!assigneeChanged &&
|
|
1241
|
+
if (!assigneeChanged && statusReturnedFromReviewToAssignee && issue.assigneeAgentId) {
|
|
1242
|
+
const commentContext = comment
|
|
1243
|
+
? {
|
|
1244
|
+
commentId: comment.id,
|
|
1245
|
+
wakeCommentId: comment.id,
|
|
1246
|
+
comment: {
|
|
1247
|
+
id: comment.id,
|
|
1248
|
+
body: comment.body,
|
|
1249
|
+
authorAgentId: comment.authorAgentId,
|
|
1250
|
+
authorUserId: comment.authorUserId,
|
|
1251
|
+
},
|
|
1252
|
+
}
|
|
1253
|
+
: {};
|
|
1112
1254
|
wakeups.set(issue.assigneeAgentId, {
|
|
1113
1255
|
source: "assignment",
|
|
1114
1256
|
triggerDetail: "system",
|
|
1115
1257
|
reason: "issue_changes_requested",
|
|
1116
|
-
payload: {
|
|
1258
|
+
payload: {
|
|
1259
|
+
issueId: issue.id,
|
|
1260
|
+
mutation: "review_changes_requested",
|
|
1261
|
+
...(comment ? { commentId: comment.id } : {}),
|
|
1262
|
+
},
|
|
1117
1263
|
requestedByActorType: actor.actorType,
|
|
1118
1264
|
requestedByActorId: actor.actorId,
|
|
1119
1265
|
contextSnapshot: {
|
|
1120
1266
|
issueId: issue.id,
|
|
1267
|
+
taskId: issue.id,
|
|
1121
1268
|
source: "issue.review_changes_requested",
|
|
1122
1269
|
wakeSource: "assignment",
|
|
1123
1270
|
wakeReason: "issue_changes_requested",
|
|
@@ -1128,19 +1275,26 @@ export function issueRoutes(db, storage) {
|
|
|
1128
1275
|
status: issue.status,
|
|
1129
1276
|
priority: issue.priority,
|
|
1130
1277
|
},
|
|
1278
|
+
...commentContext,
|
|
1131
1279
|
},
|
|
1132
1280
|
});
|
|
1133
1281
|
}
|
|
1134
|
-
if ((statusChangedToInReview ||
|
|
1135
|
-
const mutation = statusChangedToInReview
|
|
1282
|
+
if ((statusChangedToInReview || statusChangedToBlocked || reviewerChangedInReviewableStatus) && issue.reviewerAgentId) {
|
|
1283
|
+
const mutation = statusChangedToInReview
|
|
1284
|
+
? "status_to_in_review"
|
|
1285
|
+
: statusChangedToBlocked
|
|
1286
|
+
? "status_to_blocked"
|
|
1287
|
+
: issue.status === "blocked"
|
|
1288
|
+
? "reviewer_changed_blocked"
|
|
1289
|
+
: "reviewer_changed_in_review";
|
|
1136
1290
|
const actorIsReviewerAgent = actor.actorType === "agent" && actor.actorId === issue.reviewerAgentId;
|
|
1137
1291
|
const actorIsAssigneeAgent = actor.actorType === "agent" && actor.actorId === issue.assigneeAgentId;
|
|
1138
|
-
const assigneeHandoffToReview = statusChangedToInReview && actorIsAssigneeAgent;
|
|
1292
|
+
const assigneeHandoffToReview = (statusChangedToInReview || statusChangedToBlocked) && actorIsAssigneeAgent;
|
|
1139
1293
|
if (!actorIsReviewerAgent || assigneeHandoffToReview) {
|
|
1140
1294
|
wakeups.set(issue.reviewerAgentId, buildIssueReviewWakeupOptions({
|
|
1141
1295
|
issue,
|
|
1142
1296
|
mutation,
|
|
1143
|
-
contextSource: statusChangedToInReview ? "issue.status_change" : "issue.reviewer_change",
|
|
1297
|
+
contextSource: statusChangedToInReview || statusChangedToBlocked ? "issue.status_change" : "issue.reviewer_change",
|
|
1144
1298
|
requestedByActorType: actor.actorType,
|
|
1145
1299
|
requestedByActorId: actor.actorId,
|
|
1146
1300
|
}));
|
|
@@ -1324,6 +1478,62 @@ export function issueRoutes(db, storage) {
|
|
|
1324
1478
|
});
|
|
1325
1479
|
res.json(released);
|
|
1326
1480
|
});
|
|
1481
|
+
router.post("/issues/:id/commit", validate(reportIssueCommitSchema), async (req, res) => {
|
|
1482
|
+
const id = req.params.id;
|
|
1483
|
+
const issue = await svc.getById(id);
|
|
1484
|
+
if (!issue) {
|
|
1485
|
+
res.status(404).json({ error: "Issue not found" });
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
assertCompanyAccess(req, issue.orgId);
|
|
1489
|
+
if (req.actor.type !== "agent") {
|
|
1490
|
+
res.status(403).json({ error: "Agent authentication required" });
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (!(await assertAgentRunCheckoutOwnership(req, res, issue)))
|
|
1494
|
+
return;
|
|
1495
|
+
const actor = getActorInfo(req);
|
|
1496
|
+
const commitRun = await resolveAgentCommitRunId(req, res, issue);
|
|
1497
|
+
if (!commitRun.ok)
|
|
1498
|
+
return;
|
|
1499
|
+
const sha = req.body.sha.trim().toLowerCase();
|
|
1500
|
+
const subject = commitSubject(req.body.message);
|
|
1501
|
+
const shortSha = sha.slice(0, 7);
|
|
1502
|
+
if (commitRun.runId) {
|
|
1503
|
+
await heartbeat.reportRunActivity(commitRun.runId).catch((err) => logger.warn({ err, runId: commitRun.runId }, "failed to clear detached run warning after issue commit activity"));
|
|
1504
|
+
}
|
|
1505
|
+
await logActivity(db, {
|
|
1506
|
+
orgId: issue.orgId,
|
|
1507
|
+
actorType: actor.actorType,
|
|
1508
|
+
actorId: actor.actorId,
|
|
1509
|
+
agentId: actor.agentId,
|
|
1510
|
+
runId: commitRun.runId,
|
|
1511
|
+
action: "issue.code_committed",
|
|
1512
|
+
entityType: "issue",
|
|
1513
|
+
entityId: issue.id,
|
|
1514
|
+
details: {
|
|
1515
|
+
sha,
|
|
1516
|
+
shortSha,
|
|
1517
|
+
message: req.body.message,
|
|
1518
|
+
subject,
|
|
1519
|
+
identifier: issue.identifier,
|
|
1520
|
+
issueTitle: issue.title,
|
|
1521
|
+
branch: req.body.branch ?? null,
|
|
1522
|
+
repoPath: req.body.repoPath ?? null,
|
|
1523
|
+
workspacePath: req.body.workspacePath ?? null,
|
|
1524
|
+
commitCount: req.body.commitCount ?? 1,
|
|
1525
|
+
},
|
|
1526
|
+
});
|
|
1527
|
+
res.status(201).json({
|
|
1528
|
+
ok: true,
|
|
1529
|
+
issueId: issue.id,
|
|
1530
|
+
sha,
|
|
1531
|
+
shortSha,
|
|
1532
|
+
message: req.body.message,
|
|
1533
|
+
subject,
|
|
1534
|
+
runId: commitRun.runId,
|
|
1535
|
+
});
|
|
1536
|
+
});
|
|
1327
1537
|
router.get("/issues/:id/comments", async (req, res) => {
|
|
1328
1538
|
const id = req.params.id;
|
|
1329
1539
|
const issue = await svc.getById(id);
|