paperclip-github-plugin 0.8.4 → 0.8.6

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
@@ -170,7 +170,7 @@ Additional behavior:
170
170
  - If the Paperclip host initially creates that imported maintainer issue in `backlog`, GitHub Sync promotes it to `todo` without replacing the configured default assignee with the executor handoff assignee, so triage ownership stays intact.
171
171
  - When Paperclip board access is connected for a company, the advanced assignee dropdowns list both company agents and `Me` for the connected board user.
172
172
  - 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.
173
- - For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and unresolved review threads as executor work, while merge-ready states such as `CLEAN` and `HAS_HOOKS` can move work into `in_review` when CI is green and review threads are resolved. A stale aggregate `CHANGES_REQUESTED` review decision alone does not move that maintainer wait back to active execution.
173
+ - For linked pull requests, GitHub Sync treats merge-conflict, behind-branch, blocked, draft, unstable merge states, and unresolved review threads as executor work, while merge-ready states such as `CLEAN` and `HAS_HOOKS` can move work into `in_review` when CI is green and review threads are resolved. A stale aggregate `CHANGES_REQUESTED` review decision alone does not move that maintainer wait back to active execution. Transient `UNKNOWN` mergeability also does not move an already `in_review` maintainer wait back to active execution when CI is green and review threads are resolved.
174
174
  - Imported issues that are already `blocked` stay `blocked` while any first-class `blockedBy` issue is still non-terminal, even if the linked GitHub pull request is otherwise green and review-ready.
175
175
  - When sync moves work into `in_review`, GitHub Sync first follows the Paperclip issue execution policy's current reviewer or approver when that stage is visible on the issue. If Paperclip exposes an internal review or approval stage but not yet the participant, the plugin falls back to the configured reviewer or approver handoff assignee. If the transition is only a healthy linked-PR wait with no visible internal review or approval stage, GitHub Sync leaves the issue unassigned so it can wait on normal maintainer review without waking an internal owner.
176
176
  - When sync moves work back into active execution, GitHub Sync first follows the Paperclip issue execution policy `returnAssignee` when it is available. Otherwise it falls back to the configured executor handoff assignee and then to the default imported assignee.
package/dist/manifest.js CHANGED
@@ -535,7 +535,7 @@ var COMPANY_METRIC_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/a
535
535
  var require2 = createRequire(import.meta.url);
536
536
  var packageJson = require2("../package.json");
537
537
  var SCHEDULE_TICK_CRON = "* * * * *";
538
- var MANIFEST_VERSION = "0.8.4"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
538
+ var MANIFEST_VERSION = "0.8.6"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
539
539
  var manifest = {
540
540
  id: GITHUB_SYNC_PLUGIN_ID,
541
541
  apiVersion: 1,
package/dist/worker.js CHANGED
@@ -1447,7 +1447,16 @@ function formatGitHubIssueCountLabel(count) {
1447
1447
  return `${normalizedCount} GitHub ${normalizedCount === 1 ? "issue" : "issues"}`;
1448
1448
  }
1449
1449
  function getErrorMessage(error) {
1450
- return error instanceof Error ? error.message : String(error);
1450
+ if (error instanceof Error) {
1451
+ return error.message;
1452
+ }
1453
+ if (error && typeof error === "object" && "message" in error) {
1454
+ const message = error.message;
1455
+ if (typeof message === "string") {
1456
+ return message;
1457
+ }
1458
+ }
1459
+ return String(error);
1451
1460
  }
1452
1461
  function isPaperclipLabelSyncError(error) {
1453
1462
  return error instanceof PaperclipLabelSyncError;
@@ -1489,6 +1498,88 @@ function getErrorResponseDataMessage(error) {
1489
1498
  const message = data.message;
1490
1499
  return typeof message === "string" && message.trim() ? message.trim() : void 0;
1491
1500
  }
1501
+ function getGitHubRequestId(error) {
1502
+ const requestId = getErrorResponseHeaders(error)["x-github-request-id"]?.trim();
1503
+ return requestId || void 0;
1504
+ }
1505
+ function buildGitHubOctokitLogMetadata(context) {
1506
+ const metadata = {};
1507
+ const companyId = normalizeCompanyId(context.companyId);
1508
+ const operation = normalizeOptionalString2(context.operation);
1509
+ const repositoryUrl = normalizeOptionalString2(context.repositoryUrl);
1510
+ const toolName = normalizeOptionalString2(context.toolName);
1511
+ if (companyId) {
1512
+ metadata.companyId = companyId;
1513
+ }
1514
+ if (operation) {
1515
+ metadata.operation = operation;
1516
+ }
1517
+ if (repositoryUrl) {
1518
+ metadata.repositoryUrl = repositoryUrl;
1519
+ }
1520
+ if (context.syncTrigger) {
1521
+ metadata.syncTrigger = context.syncTrigger;
1522
+ }
1523
+ if (toolName) {
1524
+ metadata.toolName = toolName;
1525
+ }
1526
+ return metadata;
1527
+ }
1528
+ function getGitHubOctokitRequestPath(url, baseUrl) {
1529
+ if (typeof url !== "string" || !url.trim()) {
1530
+ return void 0;
1531
+ }
1532
+ const trimmedUrl = url.trim();
1533
+ if (typeof baseUrl === "string" && baseUrl.trim() && trimmedUrl.startsWith(baseUrl.trim())) {
1534
+ return trimmedUrl.slice(baseUrl.trim().length) || "/";
1535
+ }
1536
+ try {
1537
+ const parsed = new URL(trimmedUrl);
1538
+ return `${parsed.pathname}${parsed.search}`;
1539
+ } catch {
1540
+ return trimmedUrl;
1541
+ }
1542
+ }
1543
+ function createGitHubOctokit(ctx, token, context = {}) {
1544
+ const octokit = new Octokit({
1545
+ auth: token,
1546
+ log: {
1547
+ debug: () => void 0,
1548
+ info: () => void 0,
1549
+ warn: (message) => {
1550
+ ctx.logger.warn("GitHub Octokit warning.", {
1551
+ ...buildGitHubOctokitLogMetadata(context),
1552
+ message
1553
+ });
1554
+ },
1555
+ error: () => void 0
1556
+ }
1557
+ });
1558
+ if (context.logFailures === false) {
1559
+ return octokit;
1560
+ }
1561
+ octokit.hook.wrap("request", async (request, options) => {
1562
+ const start = Date.now();
1563
+ const requestOptions = octokit.request.endpoint.parse(options);
1564
+ try {
1565
+ return await request(options);
1566
+ } catch (error) {
1567
+ const responseMessage = getErrorResponseDataMessage(error);
1568
+ ctx.logger.warn("GitHub API request failed.", {
1569
+ ...buildGitHubOctokitLogMetadata(context),
1570
+ method: requestOptions.method,
1571
+ path: getGitHubOctokitRequestPath(requestOptions.url, options.baseUrl),
1572
+ status: getErrorStatus(error) ?? null,
1573
+ requestId: getGitHubRequestId(error) ?? null,
1574
+ durationMs: Date.now() - start,
1575
+ error: getErrorMessage(error),
1576
+ ...responseMessage ? { responseMessage } : {}
1577
+ });
1578
+ throw error;
1579
+ }
1580
+ });
1581
+ return octokit;
1582
+ }
1492
1583
  function getErrorResponseDataErrors(error) {
1493
1584
  if (!error || typeof error !== "object" || !("response" in error)) {
1494
1585
  return [];
@@ -4889,7 +4980,9 @@ function resolvePaperclipStatusFromLinkedPullRequests(linkedPullRequests, option
4889
4980
  )) {
4890
4981
  return options?.preferInProgress ? "in_progress" : "todo";
4891
4982
  }
4892
- if (linkedPullRequests.length > 0 && linkedPullRequests.every((pullRequest) => isGitHubPullRequestReviewReadyForSync(pullRequest))) {
4983
+ if (linkedPullRequests.length > 0 && linkedPullRequests.every(
4984
+ (pullRequest) => isGitHubPullRequestReviewReadyForSync(pullRequest) || options?.preserveTransientUnknownMergeabilityWait === true && isGitHubPullRequestTransientUnknownMergeabilityWait(pullRequest)
4985
+ )) {
4893
4986
  return "in_review";
4894
4987
  }
4895
4988
  return "in_progress";
@@ -5033,6 +5126,10 @@ function hasUnresolvedPaperclipIssueBlockerSummary(blockers) {
5033
5126
  return status !== "done" && status !== "cancelled";
5034
5127
  });
5035
5128
  }
5129
+ function isMissingIssueRelationsReadCapabilityError(error) {
5130
+ const message = getErrorMessage(error);
5131
+ return /missing required capability/i.test(message) && /issue\.relations\.read/i.test(message) && /issues\.relations\.get/i.test(message);
5132
+ }
5036
5133
  async function hasUnresolvedPaperclipIssueBlocker(ctx, issue, companyId) {
5037
5134
  const record = issue;
5038
5135
  if (hasUnresolvedPaperclipIssueBlockerSummary(record.blockedBy)) {
@@ -5041,8 +5138,15 @@ async function hasUnresolvedPaperclipIssueBlocker(ctx, issue, companyId) {
5041
5138
  if (typeof ctx.issues.relations?.get !== "function") {
5042
5139
  return false;
5043
5140
  }
5044
- const relations = await ctx.issues.relations.get(issue.id, companyId);
5045
- return hasUnresolvedPaperclipIssueBlockerSummary(relations.blockedBy);
5141
+ try {
5142
+ const relations = await ctx.issues.relations.get(issue.id, companyId);
5143
+ return hasUnresolvedPaperclipIssueBlockerSummary(relations.blockedBy);
5144
+ } catch (error) {
5145
+ if (isMissingIssueRelationsReadCapabilityError(error)) {
5146
+ return false;
5147
+ }
5148
+ throw error;
5149
+ }
5046
5150
  }
5047
5151
  function isSamePaperclipIssueAssigneePrincipal(left, right) {
5048
5152
  if (!left || !right) {
@@ -5415,7 +5519,8 @@ function resolvePaperclipIssueStatus(params) {
5415
5519
  }
5416
5520
  if (snapshot.linkedPullRequests.length > 0) {
5417
5521
  return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests, {
5418
- preferInProgress: hasExecutorHandoffTarget
5522
+ preferInProgress: hasExecutorHandoffTarget,
5523
+ preserveTransientUnknownMergeabilityWait: currentStatus === "done" || currentStatus === "in_review"
5419
5524
  });
5420
5525
  }
5421
5526
  if (wasImportedThisRun) {
@@ -5432,7 +5537,8 @@ function resolvePaperclipPullRequestIssueStatus(params) {
5432
5537
  return currentStatus;
5433
5538
  }
5434
5539
  return resolvePaperclipStatusFromLinkedPullRequests([pullRequest], {
5435
- preferInProgress: hasExecutorHandoffTarget
5540
+ preferInProgress: hasExecutorHandoffTarget,
5541
+ preserveTransientUnknownMergeabilityWait: currentStatus === "in_review"
5436
5542
  });
5437
5543
  }
5438
5544
  async function listLinkedPullRequestsForIssue(octokit, repository, issueNumber) {
@@ -5581,6 +5687,9 @@ function normalizeGitHubPullRequestReviewDecision(value) {
5581
5687
  function isGitHubPullRequestActionRequiredForSync(pullRequest) {
5582
5688
  return pullRequest.mergeability === "conflicting" || ACTION_REQUIRED_GITHUB_PULL_REQUEST_MERGE_STATE_STATUSES.has(pullRequest.mergeStateStatus);
5583
5689
  }
5690
+ function isGitHubPullRequestTransientUnknownMergeabilityWait(pullRequest) {
5691
+ return pullRequest.ciState === "green" && !pullRequest.hasUnresolvedReviewThreads && pullRequest.mergeability !== "conflicting" && pullRequest.mergeStateStatus === "unknown";
5692
+ }
5584
5693
  function isGitHubPullRequestReviewReadyForSync(pullRequest) {
5585
5694
  if (pullRequest.ciState !== "green" || pullRequest.hasUnresolvedReviewThreads) {
5586
5695
  return false;
@@ -9751,12 +9860,16 @@ async function handleCompanyMetricApiRoute(ctx, input) {
9751
9860
  }
9752
9861
  };
9753
9862
  }
9754
- async function createGitHubToolOctokit(ctx, companyId) {
9863
+ async function createGitHubToolOctokit(ctx, companyId, context = {}) {
9755
9864
  const token = (await resolveGithubToken(ctx, { companyId })).trim();
9756
9865
  if (!token) {
9757
9866
  throw new Error(MISSING_GITHUB_TOKEN_SYNC_MESSAGE);
9758
9867
  }
9759
- return new Octokit({ auth: token });
9868
+ return createGitHubOctokit(ctx, token, {
9869
+ companyId,
9870
+ operation: "github-api",
9871
+ ...context
9872
+ });
9760
9873
  }
9761
9874
  async function listGitHubRepositoryOpenPullRequestNumbers(octokit, repository) {
9762
9875
  const response = await octokit.rest.pulls.list({
@@ -12657,8 +12770,11 @@ function mergeNamedValues(currentValues, params) {
12657
12770
  }
12658
12771
  return [...values.values()];
12659
12772
  }
12660
- async function validateGithubToken(token) {
12661
- const octokit = new Octokit({ auth: token.trim() });
12773
+ async function validateGithubToken(ctx, token) {
12774
+ const octokit = createGitHubOctokit(ctx, token.trim(), {
12775
+ logFailures: false,
12776
+ operation: "settings.validateToken"
12777
+ });
12662
12778
  try {
12663
12779
  const response = await octokit.rest.users.getAuthenticated();
12664
12780
  return {
@@ -12770,7 +12886,12 @@ async function performSync(ctx, trigger, options = {}) {
12770
12886
  return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
12771
12887
  }
12772
12888
  activePaperclipApiAuthTokensByCompanyId = await resolvePaperclipApiAuthTokens(ctx, settings, config, mappings);
12773
- const octokit = new Octokit({ auth: token });
12889
+ const octokitLogContext = {
12890
+ companyId: targetCompanyId,
12891
+ operation: "sync.github-issues",
12892
+ syncTrigger: trigger
12893
+ };
12894
+ const octokit = createGitHubOctokit(ctx, token, octokitLogContext);
12774
12895
  let syncedIssuesCount = 0;
12775
12896
  let createdIssuesCount = 0;
12776
12897
  let skippedIssuesCount = 0;
@@ -12920,6 +13041,7 @@ async function performSync(ctx, trigger, options = {}) {
12920
13041
  await throwIfSyncCancelled();
12921
13042
  try {
12922
13043
  const repository = requireRepositoryReference(mapping.repositoryUrl);
13044
+ octokitLogContext.repositoryUrl = repository.url;
12923
13045
  const importedIssueRecords = nextRegistry.filter((entry) => doesImportedIssueRecordMatchMapping(entry, mapping)).filter((entry) => doesImportedIssueMatchTarget(entry, options.target));
12924
13046
  const shouldLoadClosedIssues = options.target?.kind === "issue" || importedIssueRecords.length > 0;
12925
13047
  currentProgress = {
@@ -13041,6 +13163,7 @@ async function performSync(ctx, trigger, options = {}) {
13041
13163
  await throwIfSyncCancelled();
13042
13164
  try {
13043
13165
  const { mapping, advancedSettings, repository, repositoryIndex, allIssuesById, issues, pullRequestLinks } = plan;
13166
+ octokitLogContext.repositoryUrl = repository.url;
13044
13167
  const companyId = mapping.companyId;
13045
13168
  let availableLabels = companyId ? companyLabelDirectoryCache.get(companyId) : void 0;
13046
13169
  if (!availableLabels) {
@@ -13482,13 +13605,19 @@ async function startSync(ctx, trigger, options = {}) {
13482
13605
  }
13483
13606
  }
13484
13607
  function registerGitHubAgentTools(ctx) {
13608
+ async function createAgentToolOctokit(runCtx, toolName, repository) {
13609
+ return createGitHubToolOctokit(ctx, runCtx.companyId, {
13610
+ toolName,
13611
+ ...repository ? { repositoryUrl: repository.url } : {}
13612
+ });
13613
+ }
13485
13614
  ctx.tools.register(
13486
13615
  "search_repository_items",
13487
13616
  getGitHubAgentToolDeclaration("search_repository_items"),
13488
13617
  async (params, runCtx) => executeGitHubTool(async () => {
13489
13618
  const input = getToolInputRecord(params);
13490
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13491
13619
  const repository = await resolveGitHubToolRepository(ctx, runCtx, input);
13620
+ const octokit = await createAgentToolOctokit(runCtx, "search_repository_items", repository);
13492
13621
  const rawQuery = normalizeOptionalToolString(input.query);
13493
13622
  if (!rawQuery) {
13494
13623
  throw new Error("query is required.");
@@ -13556,7 +13685,7 @@ function registerGitHubAgentTools(ctx) {
13556
13685
  async (params, runCtx) => executeGitHubTool(async () => {
13557
13686
  const input = getToolInputRecord(params);
13558
13687
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13559
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13688
+ const octokit = await createAgentToolOctokit(runCtx, "get_issue", target.repository);
13560
13689
  const response = await octokit.rest.issues.get({
13561
13690
  owner: target.repository.owner,
13562
13691
  repo: target.repository.repo,
@@ -13603,7 +13732,7 @@ function registerGitHubAgentTools(ctx) {
13603
13732
  async (params, runCtx) => executeGitHubTool(async () => {
13604
13733
  const input = getToolInputRecord(params);
13605
13734
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13606
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13735
+ const octokit = await createAgentToolOctokit(runCtx, "list_issue_comments", target.repository);
13607
13736
  const comments = await listAllGitHubIssueComments(octokit, target.repository, target.issueNumber);
13608
13737
  return buildToolSuccessResult(
13609
13738
  `Loaded ${comments.length} GitHub ${comments.length === 1 ? "comment" : "comments"} from issue #${target.issueNumber}.`,
@@ -13621,7 +13750,7 @@ function registerGitHubAgentTools(ctx) {
13621
13750
  async (params, runCtx) => executeGitHubTool(async () => {
13622
13751
  const input = getToolInputRecord(params);
13623
13752
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13624
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13753
+ const octokit = await createAgentToolOctokit(runCtx, "update_issue", target.repository);
13625
13754
  const currentResponse = await octokit.rest.issues.get({
13626
13755
  owner: target.repository.owner,
13627
13756
  repo: target.repository.repo,
@@ -13694,7 +13823,7 @@ function registerGitHubAgentTools(ctx) {
13694
13823
  async (params, runCtx) => executeGitHubTool(async () => {
13695
13824
  const input = getToolInputRecord(params);
13696
13825
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13697
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13826
+ const octokit = await createAgentToolOctokit(runCtx, "assign_to_current_user", target.repository);
13698
13827
  const [currentResponse, authenticatedUserResponse] = await Promise.all([
13699
13828
  octokit.rest.issues.get({
13700
13829
  owner: target.repository.owner,
@@ -13754,7 +13883,7 @@ function registerGitHubAgentTools(ctx) {
13754
13883
  async (params, runCtx) => executeGitHubTool(async () => {
13755
13884
  const input = getToolInputRecord(params);
13756
13885
  const target = await resolveGitHubIssueToolTarget(ctx, runCtx, input);
13757
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13886
+ const octokit = await createAgentToolOctokit(runCtx, "add_issue_comment", target.repository);
13758
13887
  const body = appendAiAuthorshipFooter(String(input.body ?? ""), "comment", normalizeOptionalToolString(input.llmModel));
13759
13888
  const response = await octokit.rest.issues.createComment({
13760
13889
  owner: target.repository.owner,
@@ -13804,7 +13933,7 @@ function registerGitHubAgentTools(ctx) {
13804
13933
  "pull request description",
13805
13934
  normalizeOptionalToolString(input.llmModel)
13806
13935
  ) : void 0;
13807
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
13936
+ const octokit = await createAgentToolOctokit(runCtx, "create_pull_request", repository);
13808
13937
  const response = await octokit.rest.pulls.create({
13809
13938
  owner: repository.owner,
13810
13939
  repo: repository.repo,
@@ -13880,7 +14009,7 @@ function registerGitHubAgentTools(ctx) {
13880
14009
  async (params, runCtx) => executeGitHubTool(async () => {
13881
14010
  const input = getToolInputRecord(params);
13882
14011
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
13883
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14012
+ const octokit = await createAgentToolOctokit(runCtx, "get_pull_request", target.repository);
13884
14013
  const response = await octokit.rest.pulls.get({
13885
14014
  owner: target.repository.owner,
13886
14015
  repo: target.repository.repo,
@@ -13928,7 +14057,7 @@ function registerGitHubAgentTools(ctx) {
13928
14057
  async (params, runCtx) => executeGitHubTool(async () => {
13929
14058
  const input = getToolInputRecord(params);
13930
14059
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
13931
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14060
+ const octokit = await createAgentToolOctokit(runCtx, "update_pull_request", target.repository);
13932
14061
  let currentResponse = await octokit.rest.pulls.get({
13933
14062
  owner: target.repository.owner,
13934
14063
  repo: target.repository.repo,
@@ -13994,7 +14123,7 @@ function registerGitHubAgentTools(ctx) {
13994
14123
  async (params, runCtx) => executeGitHubTool(async () => {
13995
14124
  const input = getToolInputRecord(params);
13996
14125
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
13997
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14126
+ const octokit = await createAgentToolOctokit(runCtx, "list_pull_request_files", target.repository);
13998
14127
  const files = await listAllPullRequestFiles(octokit, target.repository, target.pullRequestNumber);
13999
14128
  return buildToolSuccessResult(
14000
14129
  `Loaded ${files.length} changed ${files.length === 1 ? "file" : "files"} from pull request #${target.pullRequestNumber}.`,
@@ -14012,7 +14141,7 @@ function registerGitHubAgentTools(ctx) {
14012
14141
  async (params, runCtx) => executeGitHubTool(async () => {
14013
14142
  const input = getToolInputRecord(params);
14014
14143
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14015
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14144
+ const octokit = await createAgentToolOctokit(runCtx, "get_pull_request_checks", target.repository);
14016
14145
  const pullRequestResponse = await octokit.rest.pulls.get({
14017
14146
  owner: target.repository.owner,
14018
14147
  repo: target.repository.repo,
@@ -14111,7 +14240,7 @@ function registerGitHubAgentTools(ctx) {
14111
14240
  async (params, runCtx) => executeGitHubTool(async () => {
14112
14241
  const input = getToolInputRecord(params);
14113
14242
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14114
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14243
+ const octokit = await createAgentToolOctokit(runCtx, "list_pull_request_review_threads", target.repository);
14115
14244
  const threads = await listDetailedPullRequestReviewThreads(octokit, target.repository, target.pullRequestNumber);
14116
14245
  return buildToolSuccessResult(
14117
14246
  `Loaded ${threads.length} review ${threads.length === 1 ? "thread" : "threads"} from pull request #${target.pullRequestNumber}.`,
@@ -14133,7 +14262,7 @@ function registerGitHubAgentTools(ctx) {
14133
14262
  throw new Error("threadId is required.");
14134
14263
  }
14135
14264
  const body = appendAiAuthorshipFooter(String(input.body ?? ""), "comment", normalizeOptionalToolString(input.llmModel));
14136
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14265
+ const octokit = await createAgentToolOctokit(runCtx, "reply_to_review_thread");
14137
14266
  const response = await octokit.graphql(
14138
14267
  GITHUB_ADD_PULL_REQUEST_REVIEW_THREAD_REPLY_MUTATION,
14139
14268
  {
@@ -14168,7 +14297,7 @@ function registerGitHubAgentTools(ctx) {
14168
14297
  if (!threadId) {
14169
14298
  throw new Error("threadId is required.");
14170
14299
  }
14171
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14300
+ const octokit = await createAgentToolOctokit(runCtx, "resolve_review_thread");
14172
14301
  const response = await octokit.graphql(
14173
14302
  GITHUB_RESOLVE_REVIEW_THREAD_MUTATION,
14174
14303
  {
@@ -14199,7 +14328,7 @@ function registerGitHubAgentTools(ctx) {
14199
14328
  if (!threadId) {
14200
14329
  throw new Error("threadId is required.");
14201
14330
  }
14202
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14331
+ const octokit = await createAgentToolOctokit(runCtx, "unresolve_review_thread");
14203
14332
  const response = await octokit.graphql(
14204
14333
  GITHUB_UNRESOLVE_REVIEW_THREAD_MUTATION,
14205
14334
  {
@@ -14232,7 +14361,7 @@ function registerGitHubAgentTools(ctx) {
14232
14361
  if (userReviewers.length === 0 && teamReviewers.length === 0) {
14233
14362
  throw new Error("Provide at least one user reviewer or team reviewer.");
14234
14363
  }
14235
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14364
+ const octokit = await createAgentToolOctokit(runCtx, "request_pull_request_reviewers", target.repository);
14236
14365
  const response = await octokit.rest.pulls.requestReviewers({
14237
14366
  owner: target.repository.owner,
14238
14367
  repo: target.repository.repo,
@@ -14263,7 +14392,7 @@ function registerGitHubAgentTools(ctx) {
14263
14392
  if (!organization) {
14264
14393
  throw new Error("organization is required.");
14265
14394
  }
14266
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14395
+ const octokit = await createAgentToolOctokit(runCtx, "list_organization_projects");
14267
14396
  const projects = await listGitHubOrganizationProjects(octokit, organization, {
14268
14397
  includeClosed: input.includeClosed === true,
14269
14398
  query: normalizeOptionalToolString(input.query),
@@ -14284,7 +14413,7 @@ function registerGitHubAgentTools(ctx) {
14284
14413
  async (params, runCtx) => executeGitHubTool(async () => {
14285
14414
  const input = getToolInputRecord(params);
14286
14415
  const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
14287
- const octokit = await createGitHubToolOctokit(ctx, runCtx.companyId);
14416
+ const octokit = await createAgentToolOctokit(runCtx, "add_pull_request_to_project", target.repository);
14288
14417
  const projectTarget = await resolveGitHubProjectToolTarget(octokit, input);
14289
14418
  const pullRequest = await getGitHubPullRequestProjectItems(
14290
14419
  octokit,
@@ -14353,6 +14482,8 @@ function shouldStartWorkerHost(moduleUrl, entry = process.argv[1]) {
14353
14482
  }
14354
14483
  var __testing = {
14355
14484
  buildSyncFallbackExecutionStatePatch,
14485
+ createGitHubToolOctokit,
14486
+ hasUnresolvedPaperclipIssueBlocker,
14356
14487
  isHealthyMaintainerWaitTransition,
14357
14488
  resolveSyncTransitionAssignee
14358
14489
  };
@@ -14653,7 +14784,7 @@ var plugin = definePlugin({
14653
14784
  if (!trimmedToken) {
14654
14785
  throw new Error("Enter a GitHub token.");
14655
14786
  }
14656
- return validateGithubToken(trimmedToken);
14787
+ return validateGithubToken(ctx, trimmedToken);
14657
14788
  });
14658
14789
  ctx.actions.register("project.pullRequests.createIssue", async (input) => {
14659
14790
  const record = input && typeof input === "object" ? input : {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-github-plugin",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",