paperclip-github-plugin 0.4.2 → 0.4.4

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/README.md CHANGED
@@ -144,6 +144,7 @@ When the local Paperclip API is available, the plugin also syncs labels by name,
144
144
  Additional behavior:
145
145
 
146
146
  - Open issues with no linked pull request that are created by a verified repository maintainer/admin bypass the default imported status and start in `todo`.
147
+ - Newly imported issues that finish sync in `todo` and are assigned to an agent enqueue an assignee wakeup so the agent can pick them up promptly.
147
148
  - Open imported issues that are already in `backlog` stay in `backlog` until someone changes them in Paperclip.
148
149
  - If an imported issue is `done` or `cancelled` and GitHub shows it open again with no linked pull request, sync moves it to `todo` so agents can pick it up again.
149
150
  - Trusted new GitHub comments from the original issue author or a verified maintainer/admin can move an open imported issue back to `todo`.
package/dist/manifest.js CHANGED
@@ -503,7 +503,7 @@ var require2 = createRequire(import.meta.url);
503
503
  var packageJson = require2("../package.json");
504
504
  var DASHBOARD_WIDGET_CAPABILITY = "ui.dashboardWidget.register";
505
505
  var SCHEDULE_TICK_CRON = "* * * * *";
506
- var MANIFEST_VERSION = "0.4.2"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
506
+ var MANIFEST_VERSION = "0.4.4"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
507
507
  var manifest = {
508
508
  id: "paperclip-github-plugin",
509
509
  apiVersion: 1,
package/dist/worker.js CHANGED
@@ -618,12 +618,14 @@ var CANCELLING_SYNC_MESSAGE = "Cancellation requested. GitHub sync will stop aft
618
618
  var SYNC_PROGRESS_PERSIST_INTERVAL_MS = 250;
619
619
  var MAX_SYNC_FAILURE_LOG_ENTRIES = 25;
620
620
  var GITHUB_SECONDARY_RATE_LIMIT_FALLBACK_MS = 6e4;
621
+ var IMPORTED_ISSUE_WAKEUP_CONCURRENCY = 4;
621
622
  var MISSING_GITHUB_TOKEN_SYNC_MESSAGE = "Configure a GitHub token before running sync.";
622
623
  var MISSING_GITHUB_TOKEN_SYNC_ACTION = 'Open settings and save a GitHub token secret, or create $PAPERCLIP_HOME/plugins/github-sync/config.json (or ~/.paperclip/plugins/github-sync/config.json when PAPERCLIP_HOME is unset) with a "githubToken" value, and then run sync again.';
623
624
  var MISSING_MAPPING_SYNC_MESSAGE = "Save at least one mapping with a created Paperclip project before running sync.";
624
625
  var MISSING_MAPPING_SYNC_ACTION = "Open settings, add a repository mapping, let Paperclip create the target project, and then retry sync.";
625
626
  var MISSING_BOARD_ACCESS_SYNC_MESSAGE = "Connect Paperclip board access before running sync on this authenticated deployment.";
626
627
  var MISSING_BOARD_ACCESS_SYNC_ACTION = "Open plugin settings for each mapped company that sync will touch, connect Paperclip board access, approve the flow, and then run sync again.";
628
+ var IMPORTED_ISSUE_WAKE_REASON = "GitHub Sync imported an assigned issue that is ready for work.";
627
629
  var ISSUE_LINK_ENTITY_TYPE = "paperclip-github-plugin.issue-link";
628
630
  var PULL_REQUEST_LINK_ENTITY_TYPE = "paperclip-github-plugin.pull-request-link";
629
631
  var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation";
@@ -4121,7 +4123,7 @@ function resolvePaperclipIssueStatus(params) {
4121
4123
  if (snapshot.state === "closed") {
4122
4124
  return snapshot.stateReason === "duplicate" || snapshot.stateReason === "not_planned" ? "cancelled" : "done";
4123
4125
  }
4124
- if (currentStatus === "backlog") {
4126
+ if (currentStatus === "backlog" && !wasImportedThisRun) {
4125
4127
  return "backlog";
4126
4128
  }
4127
4129
  const baselineCommentCount = previousCommentCount ?? snapshot.commentCount;
@@ -5225,6 +5227,9 @@ function getPaperclipIssueEndpoint(baseUrl, issueId) {
5225
5227
  function getPaperclipHealthEndpoint(baseUrl) {
5226
5228
  return new URL("/api/health", baseUrl).toString();
5227
5229
  }
5230
+ function getPaperclipAgentWakeupEndpoint(baseUrl, agentId) {
5231
+ return new URL(`/api/agents/${agentId}/wakeup`, baseUrl).toString();
5232
+ }
5228
5233
  function getActivePaperclipApiAuthToken(companyId) {
5229
5234
  if (!companyId) {
5230
5235
  return void 0;
@@ -5269,6 +5274,46 @@ async function detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl) {
5269
5274
  return false;
5270
5275
  }
5271
5276
  }
5277
+ async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
5278
+ if (!params.assigneeAgentId || !params.paperclipApiBaseUrl) {
5279
+ return;
5280
+ }
5281
+ try {
5282
+ const response = await fetchPaperclipApi(
5283
+ getPaperclipAgentWakeupEndpoint(params.paperclipApiBaseUrl, params.assigneeAgentId),
5284
+ {
5285
+ method: "POST",
5286
+ headers: {
5287
+ accept: "application/json",
5288
+ "content-type": "application/json"
5289
+ },
5290
+ body: JSON.stringify({
5291
+ source: "assignment",
5292
+ triggerDetail: "system",
5293
+ reason: IMPORTED_ISSUE_WAKE_REASON,
5294
+ payload: {
5295
+ issueId: params.paperclipIssueId,
5296
+ mutation: "import"
5297
+ }
5298
+ })
5299
+ },
5300
+ {
5301
+ companyId: params.companyId
5302
+ }
5303
+ );
5304
+ if (!response.ok) {
5305
+ throw new Error(`Wakeup request failed with status ${response.status}.`);
5306
+ }
5307
+ } catch (error) {
5308
+ ctx.logger.warn("GitHub sync could not wake the assignee for an imported Paperclip issue.", {
5309
+ issueId: params.paperclipIssueId,
5310
+ agentId: params.assigneeAgentId,
5311
+ companyId: params.companyId,
5312
+ paperclipApiBaseUrl: params.paperclipApiBaseUrl,
5313
+ error: error instanceof Error ? error.message : String(error)
5314
+ });
5315
+ }
5316
+ }
5272
5317
  function parsePaperclipIssueDescription(value) {
5273
5318
  if (!value || typeof value !== "object") {
5274
5319
  return void 0;
@@ -6226,6 +6271,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6226
6271
  let updatedDescriptionsCount = 0;
6227
6272
  let completedIssueCount = 0;
6228
6273
  const totalIssueCount = importedIssues.length;
6274
+ const queuedImportedIssueWakeups = [];
6229
6275
  for (const importedIssue of importedIssues) {
6230
6276
  if (assertNotCancelled) {
6231
6277
  await assertNotCancelled();
@@ -6357,9 +6403,16 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6357
6403
  defaultImportedStatus: advancedSettings.defaultStatus,
6358
6404
  maintainerAuthoredImportedIssue
6359
6405
  });
6406
+ const shouldWakeImportedAssignee = wasImportedThisRun && nextStatus === "todo" && Boolean(paperclipIssue.assigneeAgentId);
6360
6407
  importedIssue.githubIssueNumber = githubIssue.number;
6361
6408
  importedIssue.lastSeenCommentCount = snapshot.commentCount;
6362
6409
  if (paperclipIssue.status === nextStatus) {
6410
+ if (shouldWakeImportedAssignee) {
6411
+ queuedImportedIssueWakeups.push({
6412
+ assigneeAgentId: paperclipIssue.assigneeAgentId,
6413
+ paperclipIssueId: importedIssue.paperclipIssueId
6414
+ });
6415
+ }
6363
6416
  continue;
6364
6417
  }
6365
6418
  const transitionComment = buildPaperclipIssueStatusTransitionComment({
@@ -6385,6 +6438,12 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6385
6438
  paperclipApiBaseUrl
6386
6439
  });
6387
6440
  updatedStatusesCount += 1;
6441
+ if (shouldWakeImportedAssignee) {
6442
+ queuedImportedIssueWakeups.push({
6443
+ assigneeAgentId: paperclipIssue.assigneeAgentId,
6444
+ paperclipIssueId: importedIssue.paperclipIssueId
6445
+ });
6446
+ }
6388
6447
  } catch (error) {
6389
6448
  if (isGitHubRateLimitError(error)) {
6390
6449
  throw error;
@@ -6403,6 +6462,16 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6403
6462
  }
6404
6463
  }
6405
6464
  }
6465
+ await mapWithConcurrency(
6466
+ queuedImportedIssueWakeups,
6467
+ IMPORTED_ISSUE_WAKEUP_CONCURRENCY,
6468
+ async (queuedWakeup) => wakePaperclipAssigneeForImportedIssue(ctx, {
6469
+ assigneeAgentId: queuedWakeup.assigneeAgentId,
6470
+ paperclipIssueId: queuedWakeup.paperclipIssueId,
6471
+ companyId: mapping.companyId,
6472
+ paperclipApiBaseUrl
6473
+ })
6474
+ );
6406
6475
  return {
6407
6476
  updatedStatusesCount,
6408
6477
  updatedLabelsCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-github-plugin",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -54,6 +54,6 @@
54
54
  "esbuild": "0.28.0",
55
55
  "playwright": "1.59.1",
56
56
  "tsx": "4.21.0",
57
- "typescript": "6.0.2"
57
+ "typescript": "6.0.3"
58
58
  }
59
59
  }