@tickernelz/paperclip-pro-server 2026.929.3 → 2026.929.5

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 (26) hide show
  1. package/dist/build-info.json +1 -1
  2. package/dist/routes/issues.d.ts.map +1 -1
  3. package/dist/routes/issues.js +138 -1
  4. package/dist/routes/issues.js.map +1 -1
  5. package/dist/services/issues.d.ts.map +1 -1
  6. package/dist/services/issues.js +15 -1
  7. package/dist/services/issues.js.map +1 -1
  8. package/dist/services/recovery/issue-graph-liveness.d.ts +2 -1
  9. package/dist/services/recovery/issue-graph-liveness.d.ts.map +1 -1
  10. package/dist/services/recovery/issue-graph-liveness.js +20 -0
  11. package/dist/services/recovery/issue-graph-liveness.js.map +1 -1
  12. package/package.json +21 -21
  13. package/skills/paperclip/SKILL.md +10 -11
  14. package/skills/paperclip/references/api-reference.md +9 -7
  15. package/ui-dist/assets/{Agents.production-DgL_dkS-.js → Agents.production-B1rYmw_F.js} +2 -2
  16. package/ui-dist/assets/{CompanyActivity.production-B3SxLtX3.js → CompanyActivity.production-8_P9Hbf6.js} +1 -1
  17. package/ui-dist/assets/{CompanyExport-CvtRA1t4.js → CompanyExport-DjCKz1zk.js} +1 -1
  18. package/ui-dist/assets/{CompanySkills.production-BDujOe3I.js → CompanySkills.production-BHl_qJmr.js} +1 -1
  19. package/ui-dist/assets/{Costs.production-YraSSt5l.js → Costs.production-UXGZUFPU.js} +1 -1
  20. package/ui-dist/assets/{OrgChart.production-Ob1Blnw5.js → OrgChart.production-CDnUnIIE.js} +1 -1
  21. package/ui-dist/assets/{RoutineDetail.production-DUASKX-C.js → RoutineDetail.production-Bl23zT09.js} +1 -1
  22. package/ui-dist/assets/{Routines.production-D67kgR0m.js → Routines.production-BO7eZ9iR.js} +1 -1
  23. package/ui-dist/assets/{definition-Bm9_obJ1.js → definition-fKtTXfBN.js} +1 -1
  24. package/ui-dist/assets/{index-un1DvcsQ.js → index--UKi-wFh.js} +125 -125
  25. package/ui-dist/index.html +1 -1
  26. package/ui-dist/sw.js +1 -1
@@ -1,3 +1,3 @@
1
1
  {
2
- "commit": "0feafec5a0e82d4af1a7e4ac3fb0e22b0fe8c403"
2
+ "commit": "6d94a1bd4d8c0a15b332cbca3e377eafb06a7eb2"
3
3
  }
@@ -1 +1 @@
1
- {"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../src/routes/issues.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../src/routes/issues.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ import { createHash, randomUUID } from "node:crypto";
13
13
  import { Router } from "express";
14
14
  import multer from "multer";
15
15
  import { z } from "zod";
16
- import { and, asc, desc, eq, inArray, isNull, notInArray, sql, } from "drizzle-orm";
16
+ import { and, asc, desc, eq, inArray, isNull, or, notInArray, sql, } from "drizzle-orm";
17
17
  import { activityLog, agentWakeupRequests, agents, approvals, chatConversations, chatEndpoints, chatPublications, companyMemberships, documents, executionWorkspaces, heartbeatRuns, issueApprovals, issueComments, issueDocuments, issueExecutionDecisions, issueRelations, issueRecoveryActions, issueThreadInteractions, issues as issueRows, issueWorkProducts, pipelineCaseIssueLinks, pipelineCases, pipelineStages, pipelines, projectWorkspaces, } from "@tickernelz/paperclip-pro-db";
18
18
  import { addIssueCommentSchema, acceptIssueThreadInteractionSchema, attachmentArtifactWorkProductMetadataSchema, cancelIssueThreadInteractionSchema, skipIssueThreadInteractionSchema, withdrawIssueThreadInteractionSchema, companySearchExtractQuerySchema, companySearchQuerySchema, createIssueAttachmentMetadataSchema, createIssueThreadInteractionSchema, createIssueWorkProductSchema, createIssueLabelSchema, createAcceptedPlanDecompositionSchema, checkoutIssueSchema, createDocumentAnnotationCommentSchema, createDocumentAnnotationThreadSchema, createChildIssueSchema, createIssueSchema, resolveCreateIssueStatusDefault, resolveIssueRecoveryActionSchema, runnerGoalActionRequestSchema, issueRunModelOverrideUpdateSchema, feedbackTargetTypeSchema, feedbackTraceStatusSchema, feedbackVoteValueSchema, upsertIssueFeedbackVoteSchema, upsertIssueWatchdogSchema, linkIssueApprovalSchema, issueDocumentKeySchema, ISSUE_CONTINUATION_SUMMARY_DOCUMENT_KEY, ISSUE_WATCHDOG_DISCOVERY_KINDS, TASK_WATCHDOG_PRODUCT_BUG_ORIGIN_KIND, ONBOARDING_FIRST_TASK_ORIGIN_KIND, rejectIssueThreadInteractionSchema, restoreIssueDocumentRevisionSchema, respondIssueThreadInteractionSchema, stalledReviewDecisionSchema, submitIssueThreadInteractionVerdictsSchema, updateIssueWorkProductSchema, updateDocumentAnnotationThreadSchema, upsertIssueDocumentSchema, updateIssueSchema, isClosedIsolatedExecutionWorkspace, isMarkdownArtifactWorkProduct, isMarkdownAttachmentContent, isUuidLike, normalizeIssueIdentifier as normalizeIssueReferenceIdentifier, issueWriteDenialCodeForResponsibleUserDenial, issueWriteDenialResponse, } from "@tickernelz/paperclip-pro-shared";
19
19
  import { trackAgentTaskCompleted } from "@tickernelz/paperclip-pro-shared/telemetry";
@@ -1135,7 +1135,15 @@ const INVALID_AGENT_IN_REVIEW_DISPOSITION_MESSAGE = "invalid_issue_disposition:
1135
1135
  "This request would leave the issue in_review without anyone or anything owning the next action. " +
1136
1136
  "Keep working instead of moving to review, create a request_confirmation or ask_user_questions interaction, " +
1137
1137
  "link or request a pending approval, assign a human reviewer with assigneeUserId, set a typed executionState.currentParticipant through an execution policy, " +
1138
+ "create a review subtask with paperclipCreateChildIssue assigned to the reviewer and list it in blockedByIssueIds, " +
1138
1139
  "or schedule an issue monitor for an external review/check. After creating one of those review paths, retry the status update.";
1140
+ const AGENT_REVIEW_SUBTASK_INSTRUCTION = "Create a review subtask with paperclipCreateChildIssue assigned to the reviewer with a self-contained description " +
1141
+ "(full instructions, acceptance criteria, and the material to review), add that subtask id to blockedByIssueIds on this issue, " +
1142
+ "then set status to in_review. You stay the assignee and are woken when the review subtask is done.";
1143
+ const AGENT_REVIEW_HANDOFF_REQUIRES_SUBTASK_MESSAGE = "agent_review_handoff_requires_subtask: Agents cannot hand this task to a reviewer through review or approval stages they authored themselves. " +
1144
+ AGENT_REVIEW_SUBTASK_INSTRUCTION;
1145
+ const AGENT_REASSIGN_REQUIRES_SUBTASK_MESSAGE = "agent_reassign_requires_subtask: Agents cannot move the assignee of the task they are working on to someone else. " +
1146
+ AGENT_REVIEW_SUBTASK_INSTRUCTION;
1139
1147
  function executionPrincipalsEqual(left, right) {
1140
1148
  if (!left || !right || left.type !== right.type)
1141
1149
  return false;
@@ -1293,6 +1301,16 @@ function summarizeExecutionParticipants(policy, stageType) {
1293
1301
  userId: participant.userId ?? null,
1294
1302
  })) ?? []);
1295
1303
  }
1304
+ function executionStageShape(policy) {
1305
+ return JSON.stringify((policy?.stages ?? []).map((stage) => [
1306
+ stage.type,
1307
+ stage.participants
1308
+ .map((participant) => participant.type === "agent"
1309
+ ? `agent:${participant.agentId ?? ""}`
1310
+ : `user:${participant.userId ?? ""}`)
1311
+ .sort(),
1312
+ ]));
1313
+ }
1296
1314
  function isClosedIssueStatus(status) {
1297
1315
  return status === "done" || status === "cancelled";
1298
1316
  }
@@ -2974,6 +2992,51 @@ export function issueRoutes(db, storage, opts = {}) {
2974
2992
  return;
2975
2993
  await assertEnvironmentSelectionForCompany(environmentsSvc, companyId, environmentId, { allowedDrivers: ["local", "ssh", "sandbox"] });
2976
2994
  }
2995
+ async function executionStageParticipantsAuthoredByAgent(input) {
2996
+ const latestParticipantChange = await db
2997
+ .select({ actorType: activityLog.actorType })
2998
+ .from(activityLog)
2999
+ .where(and(eq(activityLog.companyId, input.companyId), eq(activityLog.entityType, "issue"), eq(activityLog.entityId, input.issueId), inArray(activityLog.action, [
3000
+ "issue.reviewers_updated",
3001
+ "issue.approvers_updated",
3002
+ ])))
3003
+ .orderBy(desc(activityLog.createdAt))
3004
+ .limit(1)
3005
+ .then((rows) => rows[0] ?? null);
3006
+ if (latestParticipantChange) {
3007
+ return latestParticipantChange.actorType === "agent";
3008
+ }
3009
+ return Boolean(input.createdByAgentId) && (input.policy?.stages.length ?? 0) > 0;
3010
+ }
3011
+ async function agentReleasedIssue(input) {
3012
+ const latestAssignmentEvent = await db
3013
+ .select({ action: activityLog.action, agentId: activityLog.agentId })
3014
+ .from(activityLog)
3015
+ .where(and(eq(activityLog.companyId, input.companyId), eq(activityLog.entityType, "issue"), eq(activityLog.entityId, input.issueId), or(inArray(activityLog.action, [
3016
+ "issue.released",
3017
+ "issue.checked_out",
3018
+ "issue.admin_force_release",
3019
+ ]), and(eq(activityLog.action, "issue.updated"), sql `(${activityLog.details} ? 'assigneeAgentId' or ${activityLog.details} ? 'assigneeUserId')`))))
3020
+ .orderBy(desc(activityLog.createdAt))
3021
+ .limit(1)
3022
+ .then((rows) => rows[0] ?? null);
3023
+ return (latestAssignmentEvent?.action === "issue.released" &&
3024
+ latestAssignmentEvent.agentId === input.agentId);
3025
+ }
3026
+ async function hasOpenBlockingIssue(input) {
3027
+ if (input.requestedBlockerIssueIds) {
3028
+ if (input.requestedBlockerIssueIds.length === 0)
3029
+ return false;
3030
+ return await db
3031
+ .select({ id: issueRows.id })
3032
+ .from(issueRows)
3033
+ .where(and(eq(issueRows.companyId, input.companyId), inArray(issueRows.id, input.requestedBlockerIssueIds), notInArray(issueRows.status, ["done", "cancelled"])))
3034
+ .limit(1)
3035
+ .then((rows) => rows.length > 0);
3036
+ }
3037
+ const relations = await svc.getRelationSummaries(input.issueId);
3038
+ return relations.blockedBy.some((blocker) => blocker.status !== "done" && blocker.status !== "cancelled");
3039
+ }
2977
3040
  async function assertInReviewReviewPath(input) {
2978
3041
  const nextStatus = typeof input.updateFields.status === "string"
2979
3042
  ? input.updateFields.status
@@ -3054,6 +3117,12 @@ export function issueRoutes(db, storage, opts = {}) {
3054
3117
  const approvals = await issueApprovalsSvc.listApprovalsForIssue(input.existing.id);
3055
3118
  if (approvals.some((approval) => ACTIVE_REVIEW_APPROVAL_STATUSES.has(String(approval.status))))
3056
3119
  return null;
3120
+ if (await hasOpenBlockingIssue({
3121
+ companyId: input.existing.companyId,
3122
+ issueId: input.existing.id,
3123
+ requestedBlockerIssueIds: input.requestedBlockerIssueIds ?? null,
3124
+ }))
3125
+ return null;
3057
3126
  throw unprocessable(INVALID_AGENT_IN_REVIEW_DISPOSITION_MESSAGE, {
3058
3127
  code: "invalid_issue_disposition",
3059
3128
  missing: "review_path",
@@ -3063,6 +3132,7 @@ export function issueRoutes(db, storage, opts = {}) {
3063
3132
  "human_assignee_user_id",
3064
3133
  "typed_execution_state_current_participant",
3065
3134
  "scheduled_issue_monitor",
3135
+ "open_blocking_issue",
3066
3136
  ],
3067
3137
  });
3068
3138
  }
@@ -9502,6 +9572,40 @@ export function issueRoutes(db, storage, opts = {}) {
9502
9572
  };
9503
9573
  }
9504
9574
  Object.assign(updateFields, transition.patch);
9575
+ if (actor.actorType === "agent" && transition.workflowControlledAssignment) {
9576
+ const previousExecutionState = parseIssueExecutionState(existing.executionState);
9577
+ const stageWorkflowAlreadyRunning = Boolean(previousExecutionState &&
9578
+ (previousExecutionState.currentStageId ||
9579
+ previousExecutionState.status === "pending" ||
9580
+ previousExecutionState.status === "changes_requested" ||
9581
+ (previousExecutionState.completedStageIds?.length ?? 0) > 0));
9582
+ const handoffAssigneeAgentId = updateFields.assigneeAgentId === undefined
9583
+ ? existing.assigneeAgentId
9584
+ : updateFields.assigneeAgentId;
9585
+ const handoffAssigneeUserId = updateFields.assigneeUserId === undefined
9586
+ ? existing.assigneeUserId
9587
+ : updateFields.assigneeUserId;
9588
+ const stageHandoffChangesAssignee = handoffAssigneeAgentId !== existing.assigneeAgentId ||
9589
+ handoffAssigneeUserId !== existing.assigneeUserId;
9590
+ const stagesAuthoredInRequest = req.body.executionPolicy !== undefined &&
9591
+ executionStageShape(previousExecutionPolicy) !==
9592
+ executionStageShape(nextExecutionPolicy);
9593
+ const agentAuthoredStageHandoff = stageHandoffChangesAssignee &&
9594
+ (stagesAuthoredInRequest ||
9595
+ (!stageWorkflowAlreadyRunning &&
9596
+ (await executionStageParticipantsAuthoredByAgent({
9597
+ companyId: existing.companyId,
9598
+ issueId: existing.id,
9599
+ createdByAgentId: existing.createdByAgentId,
9600
+ policy: nextExecutionPolicy,
9601
+ }))));
9602
+ if (agentAuthoredStageHandoff) {
9603
+ throw unprocessable(AGENT_REVIEW_HANDOFF_REQUIRES_SUBTASK_MESSAGE, {
9604
+ code: "agent_review_handoff_requires_subtask",
9605
+ issueId: existing.id,
9606
+ });
9607
+ }
9608
+ }
9505
9609
  const nextStatus = updateFields.status ?? existing.status;
9506
9610
  if (updateFields.unblockDescriptor && nextStatus !== "blocked") {
9507
9611
  throw unprocessable("unblockDescriptor requires blocked status");
@@ -9605,6 +9709,9 @@ export function issueRoutes(db, storage, opts = {}) {
9605
9709
  actorAgentId: actor.agentId,
9606
9710
  actorRunId: actor.runId,
9607
9711
  reviewInteractionId: requestedReviewInteractionId,
9712
+ requestedBlockerIssueIds: Array.isArray(req.body.blockedByIssueIds)
9713
+ ? [...new Set(req.body.blockedByIssueIds)]
9714
+ : null,
9608
9715
  });
9609
9716
  const enteringReviewRequested = existing.status !== "in_review" && updateFields.status === "in_review";
9610
9717
  const persistReviewActivityTransactionally = enteringReviewRequested || Boolean(reviewInteractionId);
@@ -9698,6 +9805,36 @@ export function issueRoutes(db, storage, opts = {}) {
9698
9805
  code: "interaction_response_queued",
9699
9806
  });
9700
9807
  }
9808
+ const agentActsOnOwnTask = actor.actorType === "agent" &&
9809
+ !!actor.agentId &&
9810
+ !transition.workflowControlledAssignment;
9811
+ const requestedAssigneeAgentIdChanged = normalizedAssigneeAgentId !== undefined &&
9812
+ normalizedAssigneeAgentId !== existing.assigneeAgentId;
9813
+ const requestedAssigneeUserIdChanged = req.body.assigneeUserId !== undefined &&
9814
+ req.body.assigneeUserId !== existing.assigneeUserId;
9815
+ const agentRequestedOwnTaskAssigneeChange = agentActsOnOwnTask &&
9816
+ existing.assigneeAgentId === actor.agentId &&
9817
+ (requestedAssigneeAgentIdChanged || requestedAssigneeUserIdChanged);
9818
+ const agentRequestedHandoffOnIssueItReleased = agentActsOnOwnTask &&
9819
+ !existing.assigneeAgentId &&
9820
+ !existing.assigneeUserId &&
9821
+ ((typeof normalizedAssigneeAgentId === "string" &&
9822
+ normalizedAssigneeAgentId !== actor.agentId) ||
9823
+ (typeof req.body.assigneeUserId === "string" &&
9824
+ req.body.assigneeUserId !== existing.createdByUserId)) &&
9825
+ (await agentReleasedIssue({
9826
+ companyId: existing.companyId,
9827
+ issueId: existing.id,
9828
+ agentId: actor.agentId,
9829
+ }));
9830
+ if ((agentRequestedOwnTaskAssigneeChange ||
9831
+ agentRequestedHandoffOnIssueItReleased) &&
9832
+ !isAgentReturningIssueToCreator) {
9833
+ throw unprocessable(AGENT_REASSIGN_REQUIRES_SUBTASK_MESSAGE, {
9834
+ code: "agent_reassign_requires_subtask",
9835
+ issueId: existing.id,
9836
+ });
9837
+ }
9701
9838
  if (assigneeWillChange && !transition.workflowControlledAssignment) {
9702
9839
  if (!isAgentReturningIssueToCreator) {
9703
9840
  await assertCanAssignTasks(req, existing.companyId, {