paperclip-github-plugin 0.4.9 → 0.5.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/worker.js CHANGED
@@ -636,6 +636,7 @@ var MISSING_MAPPING_SYNC_ACTION = "Open settings, add a repository mapping, let
636
636
  var MISSING_BOARD_ACCESS_SYNC_MESSAGE = "Connect Paperclip board access before running sync on this authenticated deployment.";
637
637
  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.";
638
638
  var IMPORTED_ISSUE_WAKE_REASON = "GitHub Sync imported an assigned issue that is ready for work.";
639
+ var STATUS_TRANSITION_WAKE_REASON = "GitHub Sync moved an assigned issue into its next active state.";
639
640
  var ISSUE_LINK_ENTITY_TYPE = "paperclip-github-plugin.issue-link";
640
641
  var PULL_REQUEST_LINK_ENTITY_TYPE = "paperclip-github-plugin.pull-request-link";
641
642
  var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation";
@@ -2668,6 +2669,7 @@ function getPublicSettings(settings) {
2668
2669
  githubTokenRef: _githubTokenRef,
2669
2670
  paperclipBoardApiTokenRefs: _paperclipBoardApiTokenRefs,
2670
2671
  paperclipBoardAccessIdentityByCompanyId: _paperclipBoardAccessIdentityByCompanyId,
2672
+ paperclipBoardAccessUserIdByCompanyId: _paperclipBoardAccessUserIdByCompanyId,
2671
2673
  companyAdvancedSettingsByCompanyId: _companyAdvancedSettingsByCompanyId,
2672
2674
  syncStateByCompanyId: _syncStateByCompanyId,
2673
2675
  scheduleFrequencyMinutesByCompanyId: _scheduleFrequencyMinutesByCompanyId,
@@ -2690,6 +2692,13 @@ function getPaperclipBoardAccessIdentity(settings, companyId) {
2690
2692
  }
2691
2693
  return normalizeOptionalString2(settings.paperclipBoardAccessIdentityByCompanyId?.[normalizedCompanyId]);
2692
2694
  }
2695
+ function getPaperclipBoardAccessUserId(settings, companyId) {
2696
+ const normalizedCompanyId = normalizeCompanyId(companyId);
2697
+ if (!normalizedCompanyId) {
2698
+ return void 0;
2699
+ }
2700
+ return normalizeOptionalString2(settings.paperclipBoardAccessUserIdByCompanyId?.[normalizedCompanyId]);
2701
+ }
2693
2702
  function getPublicSettingsForScope(settings, companyId) {
2694
2703
  const publicSettings = getPublicSettings(settings);
2695
2704
  const githubTokenLogin = getGitHubTokenLogin(settings, companyId);
@@ -2702,24 +2711,54 @@ function getPublicSettingsForScope(settings, companyId) {
2702
2711
  ...paperclipBoardAccessIdentity ? { paperclipBoardAccessIdentity } : {}
2703
2712
  };
2704
2713
  }
2705
- async function listAvailableAssignees(ctx, companyId) {
2714
+ function compareGitHubSyncAssigneeOptions(left, right) {
2715
+ if (left.kind !== right.kind) {
2716
+ return left.kind === "user" ? -1 : 1;
2717
+ }
2718
+ return left.name.localeCompare(right.name);
2719
+ }
2720
+ function sortGitHubSyncAssigneeOptions(options) {
2721
+ return [...options].sort(compareGitHubSyncAssigneeOptions);
2722
+ }
2723
+ function getConnectedBoardUserAssigneeOption(settings, companyId) {
2724
+ const userId = getPaperclipBoardAccessUserId(settings, companyId);
2725
+ if (!userId) {
2726
+ return null;
2727
+ }
2728
+ const identityLabel = getPaperclipBoardAccessIdentity(settings, companyId);
2729
+ return {
2730
+ kind: "user",
2731
+ id: userId,
2732
+ name: "Me",
2733
+ ...identityLabel ? { title: identityLabel } : {}
2734
+ };
2735
+ }
2736
+ async function listAvailableAssignees(ctx, companyId, settings) {
2737
+ const connectedBoardUserOption = settings ? getConnectedBoardUserAssigneeOption(settings, companyId) : null;
2706
2738
  try {
2707
2739
  const agents = await ctx.agents.list({
2708
2740
  companyId,
2709
2741
  limit: 500
2710
2742
  });
2711
- return agents.filter((agent) => agent.status !== "terminated").map((agent) => ({
2743
+ const assignees = agents.filter((agent) => agent.status !== "terminated").map((agent) => ({
2744
+ kind: "agent",
2712
2745
  id: agent.id,
2713
2746
  name: agent.name,
2714
2747
  ...agent.title?.trim() ? { title: agent.title.trim() } : {},
2715
2748
  ...agent.status ? { status: agent.status } : {}
2716
- })).sort((left, right) => left.name.localeCompare(right.name));
2749
+ }));
2750
+ if (connectedBoardUserOption) {
2751
+ assignees.push(connectedBoardUserOption);
2752
+ }
2753
+ return sortGitHubSyncAssigneeOptions(
2754
+ [...new Map(assignees.map((assignee) => [`${assignee.kind}:${assignee.id}`, assignee])).values()]
2755
+ );
2717
2756
  } catch (error) {
2718
2757
  ctx.logger.warn("Unable to list company agents for GitHub Sync advanced settings.", {
2719
2758
  companyId,
2720
2759
  error: getErrorMessage(error)
2721
2760
  });
2722
- return [];
2761
+ return connectedBoardUserOption ? [connectedBoardUserOption] : [];
2723
2762
  }
2724
2763
  }
2725
2764
  async function resolvePaperclipIssueDrawerAgents(ctx, companyId, agentIds) {
@@ -3218,6 +3257,20 @@ function normalizePaperclipBoardAccessIdentityByCompanyId(value) {
3218
3257
  }
3219
3258
  return Object.fromEntries(entries);
3220
3259
  }
3260
+ function normalizePaperclipBoardAccessUserIdByCompanyId(value) {
3261
+ if (!value || typeof value !== "object") {
3262
+ return void 0;
3263
+ }
3264
+ const entries = Object.entries(value).map(([companyId, userId]) => {
3265
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3266
+ const normalizedUserId = normalizeOptionalString2(userId);
3267
+ return normalizedCompanyId && normalizedUserId ? [normalizedCompanyId, normalizedUserId] : null;
3268
+ }).filter((entry) => entry !== null);
3269
+ if (entries.length === 0) {
3270
+ return void 0;
3271
+ }
3272
+ return Object.fromEntries(entries);
3273
+ }
3221
3274
  function normalizeSyncCancellationRequest(value) {
3222
3275
  if (!value || typeof value !== "object") {
3223
3276
  return null;
@@ -3308,17 +3361,46 @@ function normalizeAgentIds(value) {
3308
3361
  const entries = Array.isArray(value) ? value.map((entry) => normalizeOptionalString2(entry)).filter((entry) => Boolean(entry)) : [];
3309
3362
  return [...new Set(entries)].sort((left, right) => left.localeCompare(right));
3310
3363
  }
3364
+ function normalizeAdvancedSettingsAssigneeOverride(record, keys) {
3365
+ const userId = normalizeOptionalString2(record[keys.userId]);
3366
+ if (userId) {
3367
+ return {
3368
+ [keys.userId]: userId
3369
+ };
3370
+ }
3371
+ const agentId = normalizeOptionalString2(record[keys.agentId]);
3372
+ if (agentId) {
3373
+ return {
3374
+ [keys.agentId]: agentId
3375
+ };
3376
+ }
3377
+ return {};
3378
+ }
3311
3379
  function normalizeAdvancedSettings(value) {
3312
3380
  if (!value || typeof value !== "object") {
3313
3381
  return DEFAULT_ADVANCED_SETTINGS;
3314
3382
  }
3315
3383
  const record = value;
3316
- const defaultAssigneeAgentId = normalizeOptionalString2(record.defaultAssigneeAgentId);
3317
3384
  const defaultStatus = "defaultStatus" in record ? coercePaperclipIssueStatus(record.defaultStatus) : DEFAULT_ADVANCED_SETTINGS.defaultStatus;
3318
3385
  const ignoredIssueAuthorUsernames = "ignoredIssueAuthorUsernames" in record ? normalizeIgnoredIssueAuthorUsernames(record.ignoredIssueAuthorUsernames) : DEFAULT_ADVANCED_SETTINGS.ignoredIssueAuthorUsernames;
3319
3386
  const githubTokenPropagationAgentIds = "githubTokenPropagationAgentIds" in record ? normalizeAgentIds(record.githubTokenPropagationAgentIds) : [];
3320
3387
  return {
3321
- ...defaultAssigneeAgentId ? { defaultAssigneeAgentId } : {},
3388
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
3389
+ agentId: "defaultAssigneeAgentId",
3390
+ userId: "defaultAssigneeUserId"
3391
+ }),
3392
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
3393
+ agentId: "executorAssigneeAgentId",
3394
+ userId: "executorAssigneeUserId"
3395
+ }),
3396
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
3397
+ agentId: "reviewerAssigneeAgentId",
3398
+ userId: "reviewerAssigneeUserId"
3399
+ }),
3400
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
3401
+ agentId: "approverAssigneeAgentId",
3402
+ userId: "approverAssigneeUserId"
3403
+ }),
3322
3404
  defaultStatus,
3323
3405
  ignoredIssueAuthorUsernames,
3324
3406
  ...githubTokenPropagationAgentIds.length > 0 ? { githubTokenPropagationAgentIds } : {}
@@ -3476,6 +3558,9 @@ function normalizeSettings(value) {
3476
3558
  const paperclipBoardAccessIdentityByCompanyId = normalizePaperclipBoardAccessIdentityByCompanyId(
3477
3559
  record.paperclipBoardAccessIdentityByCompanyId
3478
3560
  );
3561
+ const paperclipBoardAccessUserIdByCompanyId = normalizePaperclipBoardAccessUserIdByCompanyId(
3562
+ record.paperclipBoardAccessUserIdByCompanyId
3563
+ );
3479
3564
  const companyAdvancedSettingsByCompanyId = normalizeCompanyAdvancedSettingsByCompanyId(record.companyAdvancedSettingsByCompanyId);
3480
3565
  return {
3481
3566
  mappings: normalizeMappings(record.mappings),
@@ -3491,6 +3576,7 @@ function normalizeSettings(value) {
3491
3576
  ...githubTokenLogin ? { githubTokenLogin } : {},
3492
3577
  ...paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs } : {},
3493
3578
  ...paperclipBoardAccessIdentityByCompanyId ? { paperclipBoardAccessIdentityByCompanyId } : {},
3579
+ ...paperclipBoardAccessUserIdByCompanyId ? { paperclipBoardAccessUserIdByCompanyId } : {},
3494
3580
  ...companyAdvancedSettingsByCompanyId ? { companyAdvancedSettingsByCompanyId } : {},
3495
3581
  updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : void 0
3496
3582
  };
@@ -4107,9 +4193,9 @@ function classifyGitHubPullRequestCiState(contexts) {
4107
4193
  }
4108
4194
  return hasPendingContext ? "unfinished" : "green";
4109
4195
  }
4110
- function resolvePaperclipStatusFromLinkedPullRequests(linkedPullRequests) {
4196
+ function resolvePaperclipStatusFromLinkedPullRequests(linkedPullRequests, options) {
4111
4197
  if (linkedPullRequests.some((pullRequest) => pullRequest.hasUnresolvedReviewThreads || pullRequest.ciState === "red")) {
4112
- return "todo";
4198
+ return options?.preferInProgress ? "in_progress" : "todo";
4113
4199
  }
4114
4200
  if (linkedPullRequests.length > 0 && linkedPullRequests.every(
4115
4201
  (pullRequest) => pullRequest.ciState === "green" && pullRequest.hasUnresolvedReviewThreads === false
@@ -4141,6 +4227,354 @@ function formatPaperclipIssueStatus(status) {
4141
4227
  function normalizePaperclipIssueStatus(value) {
4142
4228
  return PAPERCLIP_ISSUE_STATUSES.includes(value) ? value : void 0;
4143
4229
  }
4230
+ function normalizePaperclipIssueExecutionStageType(value) {
4231
+ return value === "review" || value === "approval" ? value : void 0;
4232
+ }
4233
+ function normalizePaperclipIssueAssigneePrincipal(value) {
4234
+ if (!value || typeof value !== "object") {
4235
+ return null;
4236
+ }
4237
+ const record = value;
4238
+ const principalType = normalizeOptionalString2(record.type);
4239
+ const agentId = normalizeOptionalString2(record.agentId);
4240
+ const userId = normalizeOptionalString2(record.userId);
4241
+ if ((principalType === void 0 || principalType === "agent") && agentId) {
4242
+ return {
4243
+ kind: "agent",
4244
+ id: agentId
4245
+ };
4246
+ }
4247
+ if ((principalType === void 0 || principalType === "user") && userId) {
4248
+ return {
4249
+ kind: "user",
4250
+ id: userId
4251
+ };
4252
+ }
4253
+ return null;
4254
+ }
4255
+ function normalizePaperclipIssueExecutionStage(value) {
4256
+ if (!value || typeof value !== "object") {
4257
+ return null;
4258
+ }
4259
+ const record = value;
4260
+ const type = normalizePaperclipIssueExecutionStageType(record.type);
4261
+ const participants = Array.isArray(record.participants) ? record.participants.map((participant) => normalizePaperclipIssueAssigneePrincipal(participant)).filter((participant) => participant !== null) : [];
4262
+ if (!type || participants.length === 0) {
4263
+ return null;
4264
+ }
4265
+ const id = normalizeOptionalString2(record.id);
4266
+ return {
4267
+ ...id ? { id } : {},
4268
+ type,
4269
+ participants
4270
+ };
4271
+ }
4272
+ function normalizePaperclipIssueExecutionPolicy(value) {
4273
+ if (!value || typeof value !== "object") {
4274
+ return null;
4275
+ }
4276
+ const rawStages = value.stages;
4277
+ const stages = Array.isArray(rawStages) ? rawStages.map((stage) => normalizePaperclipIssueExecutionStage(stage)).filter((stage) => stage !== null) : [];
4278
+ return stages.length > 0 ? {
4279
+ stages
4280
+ } : null;
4281
+ }
4282
+ function normalizePaperclipIssueExecutionState(value) {
4283
+ if (!value || typeof value !== "object") {
4284
+ return null;
4285
+ }
4286
+ const record = value;
4287
+ const status = normalizeOptionalString2(record.status);
4288
+ const currentStageId = normalizeOptionalString2(record.currentStageId) ?? null;
4289
+ const currentStageIndex = typeof record.currentStageIndex === "number" && Number.isInteger(record.currentStageIndex) ? record.currentStageIndex : null;
4290
+ const currentStageType = normalizePaperclipIssueExecutionStageType(record.currentStageType) ?? null;
4291
+ const currentParticipant = normalizePaperclipIssueAssigneePrincipal(record.currentParticipant);
4292
+ const returnAssignee = normalizePaperclipIssueAssigneePrincipal(record.returnAssignee);
4293
+ const lastDecisionId = normalizeOptionalString2(record.lastDecisionId) ?? null;
4294
+ const lastDecisionOutcome = normalizeOptionalString2(record.lastDecisionOutcome) ?? null;
4295
+ const completedStageIds = Array.isArray(record.completedStageIds) ? record.completedStageIds.map((stageId) => normalizeOptionalString2(stageId)).filter((stageId) => Boolean(stageId)) : [];
4296
+ if (!status && currentStageId === null && currentStageIndex === null && currentStageType === null && currentParticipant === null && returnAssignee === null && completedStageIds.length === 0 && lastDecisionId === null && lastDecisionOutcome === null) {
4297
+ return null;
4298
+ }
4299
+ return {
4300
+ ...status ? { status } : {},
4301
+ currentStageId,
4302
+ currentStageIndex,
4303
+ currentStageType,
4304
+ currentParticipant,
4305
+ returnAssignee,
4306
+ completedStageIds,
4307
+ ...lastDecisionId !== null ? { lastDecisionId } : {},
4308
+ ...lastDecisionOutcome !== null ? { lastDecisionOutcome } : {}
4309
+ };
4310
+ }
4311
+ function getPaperclipIssueAssigneePrincipal(issue) {
4312
+ const record = issue;
4313
+ const assigneeAgentId = normalizeOptionalString2(record.assigneeAgentId);
4314
+ if (assigneeAgentId) {
4315
+ return {
4316
+ kind: "agent",
4317
+ id: assigneeAgentId
4318
+ };
4319
+ }
4320
+ const assigneeUserId = normalizeOptionalString2(record.assigneeUserId);
4321
+ return assigneeUserId ? {
4322
+ kind: "user",
4323
+ id: assigneeUserId
4324
+ } : null;
4325
+ }
4326
+ function getPaperclipIssueSyncContext(issue) {
4327
+ const record = issue;
4328
+ return {
4329
+ assignee: getPaperclipIssueAssigneePrincipal(issue),
4330
+ executionPolicy: normalizePaperclipIssueExecutionPolicy(record.executionPolicy),
4331
+ executionState: normalizePaperclipIssueExecutionState(record.executionState)
4332
+ };
4333
+ }
4334
+ function isSamePaperclipIssueAssigneePrincipal(left, right) {
4335
+ if (!left || !right) {
4336
+ return !left && !right;
4337
+ }
4338
+ return left.kind === right.kind && left.id === right.id;
4339
+ }
4340
+ function serializePaperclipIssueAssigneePrincipal(principal) {
4341
+ if (!principal) {
4342
+ return null;
4343
+ }
4344
+ return principal.kind === "agent" ? {
4345
+ type: "agent",
4346
+ agentId: principal.id,
4347
+ userId: null
4348
+ } : {
4349
+ type: "user",
4350
+ agentId: null,
4351
+ userId: principal.id
4352
+ };
4353
+ }
4354
+ function selectPaperclipIssueExecutionStageParticipant(stage, options = {}) {
4355
+ const participants = stage.participants.filter((participant) => !isSamePaperclipIssueAssigneePrincipal(
4356
+ participant,
4357
+ options.exclude
4358
+ ));
4359
+ if (participants.length === 0) {
4360
+ return null;
4361
+ }
4362
+ if (options.preferred) {
4363
+ const preferredParticipant = participants.find((participant) => isSamePaperclipIssueAssigneePrincipal(
4364
+ participant,
4365
+ options.preferred
4366
+ ));
4367
+ if (preferredParticipant) {
4368
+ return preferredParticipant;
4369
+ }
4370
+ }
4371
+ return participants[0] ?? null;
4372
+ }
4373
+ function findPaperclipIssueExecutionStageMatch(executionPolicy, executionState) {
4374
+ const stages = executionPolicy?.stages ?? [];
4375
+ if (stages.length === 0) {
4376
+ return null;
4377
+ }
4378
+ const currentStageId = executionState?.currentStageId;
4379
+ if (currentStageId) {
4380
+ const matchedStageIndex = stages.findIndex((stage) => stage.id === currentStageId);
4381
+ if (matchedStageIndex >= 0) {
4382
+ const matchedStage = stages[matchedStageIndex];
4383
+ if (matchedStage) {
4384
+ return {
4385
+ stage: matchedStage,
4386
+ index: matchedStageIndex
4387
+ };
4388
+ }
4389
+ }
4390
+ }
4391
+ const currentStageIndex = executionState?.currentStageIndex;
4392
+ if (typeof currentStageIndex === "number" && currentStageIndex >= 0 && currentStageIndex < stages.length) {
4393
+ const matchedStage = stages[currentStageIndex];
4394
+ if (matchedStage) {
4395
+ return {
4396
+ stage: matchedStage,
4397
+ index: currentStageIndex
4398
+ };
4399
+ }
4400
+ }
4401
+ const completedStageIds = new Set(executionState?.completedStageIds ?? []);
4402
+ for (const [stageIndex, stage] of stages.entries()) {
4403
+ if (!stage.id || !completedStageIds.has(stage.id)) {
4404
+ return {
4405
+ stage,
4406
+ index: stageIndex
4407
+ };
4408
+ }
4409
+ }
4410
+ const firstStage = stages[0];
4411
+ return firstStage ? {
4412
+ stage: firstStage,
4413
+ index: 0
4414
+ } : null;
4415
+ }
4416
+ function getConfiguredAdvancedAssigneePrincipal(advancedSettings, role) {
4417
+ switch (role) {
4418
+ case "default":
4419
+ return advancedSettings.defaultAssigneeUserId ? { kind: "user", id: advancedSettings.defaultAssigneeUserId } : advancedSettings.defaultAssigneeAgentId ? { kind: "agent", id: advancedSettings.defaultAssigneeAgentId } : null;
4420
+ case "executor":
4421
+ return advancedSettings.executorAssigneeUserId ? { kind: "user", id: advancedSettings.executorAssigneeUserId } : advancedSettings.executorAssigneeAgentId ? { kind: "agent", id: advancedSettings.executorAssigneeAgentId } : null;
4422
+ case "reviewer":
4423
+ return advancedSettings.reviewerAssigneeUserId ? { kind: "user", id: advancedSettings.reviewerAssigneeUserId } : advancedSettings.reviewerAssigneeAgentId ? { kind: "agent", id: advancedSettings.reviewerAssigneeAgentId } : null;
4424
+ case "approver":
4425
+ return advancedSettings.approverAssigneeUserId ? { kind: "user", id: advancedSettings.approverAssigneeUserId } : advancedSettings.approverAssigneeAgentId ? { kind: "agent", id: advancedSettings.approverAssigneeAgentId } : null;
4426
+ }
4427
+ }
4428
+ function resolvePaperclipIssueReviewAssignee(syncContext, advancedSettings) {
4429
+ const currentParticipant = syncContext.executionState?.currentParticipant;
4430
+ const currentStageType = syncContext.executionState?.currentStageType;
4431
+ if (currentParticipant && currentStageType) {
4432
+ return {
4433
+ principal: currentParticipant,
4434
+ role: currentStageType === "approval" ? "approver" : "reviewer"
4435
+ };
4436
+ }
4437
+ const nextStageMatch = findPaperclipIssueExecutionStageMatch(syncContext.executionPolicy, syncContext.executionState);
4438
+ const nextStage = nextStageMatch?.stage ?? null;
4439
+ const nextStageParticipant = nextStage ? selectPaperclipIssueExecutionStageParticipant(nextStage, {
4440
+ exclude: syncContext.executionState?.returnAssignee ?? syncContext.assignee
4441
+ }) : null;
4442
+ if (nextStage && nextStageParticipant) {
4443
+ return {
4444
+ principal: nextStageParticipant,
4445
+ role: nextStage.type === "approval" ? "approver" : "reviewer"
4446
+ };
4447
+ }
4448
+ const approverOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "approver");
4449
+ if (nextStage?.type === "approval" && approverOverride) {
4450
+ return {
4451
+ principal: approverOverride,
4452
+ role: "approver"
4453
+ };
4454
+ }
4455
+ const reviewerOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "reviewer");
4456
+ if (reviewerOverride) {
4457
+ return {
4458
+ principal: reviewerOverride,
4459
+ role: "reviewer"
4460
+ };
4461
+ }
4462
+ if (approverOverride) {
4463
+ return {
4464
+ principal: approverOverride,
4465
+ role: "approver"
4466
+ };
4467
+ }
4468
+ return null;
4469
+ }
4470
+ function resolvePaperclipIssueExecutorAssignee(syncContext, advancedSettings) {
4471
+ const returnAssignee = syncContext.executionState?.returnAssignee;
4472
+ if (returnAssignee) {
4473
+ return {
4474
+ principal: returnAssignee,
4475
+ role: "executor"
4476
+ };
4477
+ }
4478
+ const executorOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "executor");
4479
+ if (executorOverride) {
4480
+ return {
4481
+ principal: executorOverride,
4482
+ role: "executor"
4483
+ };
4484
+ }
4485
+ const defaultOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "default");
4486
+ if (defaultOverride) {
4487
+ return {
4488
+ principal: defaultOverride,
4489
+ role: "executor"
4490
+ };
4491
+ }
4492
+ return null;
4493
+ }
4494
+ function resolveSyncTransitionAssignee(params) {
4495
+ const { nextStatus, syncContext, advancedSettings } = params;
4496
+ if (nextStatus === "in_review") {
4497
+ return resolvePaperclipIssueReviewAssignee(syncContext, advancedSettings);
4498
+ }
4499
+ if (nextStatus === "todo" || nextStatus === "in_progress") {
4500
+ return resolvePaperclipIssueExecutorAssignee(syncContext, advancedSettings);
4501
+ }
4502
+ return null;
4503
+ }
4504
+ function doesPaperclipIssueAssigneeMatch(currentAssignee, nextAssignee) {
4505
+ return isSamePaperclipIssueAssigneePrincipal(currentAssignee, nextAssignee);
4506
+ }
4507
+ function isActionablePaperclipIssueStatus(status) {
4508
+ return status === "todo" || status === "in_progress" || status === "in_review";
4509
+ }
4510
+ function buildPaperclipPendingExecutionState(params) {
4511
+ const { previousState, stage, stageIndex, participant, returnAssignee } = params;
4512
+ return {
4513
+ status: "pending",
4514
+ currentStageId: stage.id ?? null,
4515
+ currentStageIndex: stageIndex,
4516
+ currentStageType: stage.type,
4517
+ currentParticipant: serializePaperclipIssueAssigneePrincipal(participant),
4518
+ returnAssignee: serializePaperclipIssueAssigneePrincipal(returnAssignee),
4519
+ completedStageIds: [...previousState?.completedStageIds ?? []],
4520
+ lastDecisionId: previousState?.lastDecisionId ?? null,
4521
+ lastDecisionOutcome: previousState?.lastDecisionOutcome ?? null
4522
+ };
4523
+ }
4524
+ function buildPaperclipChangesRequestedExecutionState(params) {
4525
+ const { previousState, stage, stageIndex } = params;
4526
+ return {
4527
+ status: "changes_requested",
4528
+ currentStageId: stage.id ?? previousState.currentStageId ?? null,
4529
+ currentStageIndex: stageIndex,
4530
+ currentStageType: stage.type,
4531
+ currentParticipant: serializePaperclipIssueAssigneePrincipal(previousState.currentParticipant),
4532
+ returnAssignee: serializePaperclipIssueAssigneePrincipal(previousState.returnAssignee),
4533
+ completedStageIds: [...previousState.completedStageIds],
4534
+ lastDecisionId: previousState.lastDecisionId ?? null,
4535
+ lastDecisionOutcome: "changes_requested"
4536
+ };
4537
+ }
4538
+ function buildSyncFallbackExecutionStatePatch(params) {
4539
+ const { currentStatus, nextStatus, syncContext, nextAssignee } = params;
4540
+ const previousState = syncContext.executionState;
4541
+ if ((currentStatus === "done" || currentStatus === "cancelled") && nextStatus !== "done" && nextStatus !== "cancelled") {
4542
+ return previousState ? null : void 0;
4543
+ }
4544
+ if (nextStatus === "in_review" && syncContext.executionPolicy) {
4545
+ const nextStageMatch = findPaperclipIssueExecutionStageMatch(syncContext.executionPolicy, previousState);
4546
+ if (!nextStageMatch) {
4547
+ return previousState ? null : void 0;
4548
+ }
4549
+ const returnAssignee = previousState?.returnAssignee ?? syncContext.assignee;
4550
+ const participant = selectPaperclipIssueExecutionStageParticipant(nextStageMatch.stage, {
4551
+ preferred: previousState?.status === "changes_requested" ? nextAssignee ?? previousState.currentParticipant : nextAssignee ?? previousState?.currentParticipant,
4552
+ exclude: returnAssignee
4553
+ });
4554
+ if (!participant) {
4555
+ return previousState ? null : void 0;
4556
+ }
4557
+ return buildPaperclipPendingExecutionState({
4558
+ previousState,
4559
+ stage: nextStageMatch.stage,
4560
+ stageIndex: nextStageMatch.index,
4561
+ participant,
4562
+ returnAssignee
4563
+ });
4564
+ }
4565
+ if ((nextStatus === "todo" || nextStatus === "in_progress") && currentStatus === "in_review") {
4566
+ const currentStageMatch = findPaperclipIssueExecutionStageMatch(syncContext.executionPolicy, previousState);
4567
+ if (previousState?.status === "pending" && previousState.returnAssignee && currentStageMatch) {
4568
+ return buildPaperclipChangesRequestedExecutionState({
4569
+ previousState,
4570
+ stage: currentStageMatch.stage,
4571
+ stageIndex: currentStageMatch.index
4572
+ });
4573
+ }
4574
+ return previousState ? null : void 0;
4575
+ }
4576
+ return void 0;
4577
+ }
4144
4578
  function describeGitHubStatusTransitionReason(params) {
4145
4579
  const { snapshot, previousCommentCount, hasTrustedNewComment, maintainerAuthoredImportedIssue } = params;
4146
4580
  if (snapshot.state === "closed") {
@@ -4233,7 +4667,8 @@ function resolvePaperclipIssueStatus(params) {
4233
4667
  hasTrustedNewComment,
4234
4668
  wasImportedThisRun,
4235
4669
  defaultImportedStatus,
4236
- maintainerAuthoredImportedIssue
4670
+ maintainerAuthoredImportedIssue,
4671
+ hasExecutorHandoffTarget
4237
4672
  } = params;
4238
4673
  if (snapshot.state === "closed") {
4239
4674
  return snapshot.stateReason === "duplicate" || snapshot.stateReason === "not_planned" ? "cancelled" : "done";
@@ -4243,10 +4678,12 @@ function resolvePaperclipIssueStatus(params) {
4243
4678
  }
4244
4679
  const baselineCommentCount = previousCommentCount ?? snapshot.commentCount;
4245
4680
  if (snapshot.commentCount > baselineCommentCount && hasTrustedNewComment) {
4246
- return "todo";
4681
+ return hasExecutorHandoffTarget ? "in_progress" : "todo";
4247
4682
  }
4248
4683
  if (snapshot.linkedPullRequests.length > 0) {
4249
- return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests);
4684
+ return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests, {
4685
+ preferInProgress: hasExecutorHandoffTarget
4686
+ });
4250
4687
  }
4251
4688
  if (wasImportedThisRun) {
4252
4689
  return maintainerAuthoredImportedIssue ? "todo" : defaultImportedStatus;
@@ -5398,7 +5835,7 @@ async function detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl) {
5398
5835
  return false;
5399
5836
  }
5400
5837
  }
5401
- async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
5838
+ async function wakePaperclipIssueAssignee(ctx, params) {
5402
5839
  if (!params.assigneeAgentId || !params.paperclipApiBaseUrl) {
5403
5840
  return;
5404
5841
  }
@@ -5414,10 +5851,12 @@ async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
5414
5851
  body: JSON.stringify({
5415
5852
  source: "assignment",
5416
5853
  triggerDetail: "system",
5417
- reason: IMPORTED_ISSUE_WAKE_REASON,
5854
+ reason: params.reason,
5418
5855
  payload: {
5419
5856
  issueId: params.paperclipIssueId,
5420
- mutation: "import"
5857
+ mutation: params.mutation,
5858
+ ...params.previousStatus ? { previousStatus: params.previousStatus } : {},
5859
+ ...params.nextStatus ? { nextStatus: params.nextStatus } : {}
5421
5860
  }
5422
5861
  })
5423
5862
  },
@@ -5429,11 +5868,12 @@ async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
5429
5868
  throw new Error(`Wakeup request failed with status ${response.status}.`);
5430
5869
  }
5431
5870
  } catch (error) {
5432
- ctx.logger.warn("GitHub sync could not wake the assignee for an imported Paperclip issue.", {
5871
+ ctx.logger.warn("GitHub sync could not wake the assignee for a Paperclip issue.", {
5433
5872
  issueId: params.paperclipIssueId,
5434
5873
  agentId: params.assigneeAgentId,
5435
5874
  companyId: params.companyId,
5436
5875
  paperclipApiBaseUrl: params.paperclipApiBaseUrl,
5876
+ mutation: params.mutation,
5437
5877
  error: error instanceof Error ? error.message : String(error)
5438
5878
  });
5439
5879
  }
@@ -6132,10 +6572,32 @@ function doIssueNumberListsMatch(left, right) {
6132
6572
  }
6133
6573
  return left.every((value, index) => value === right[index]);
6134
6574
  }
6135
- async function updatePaperclipIssueStatus(ctx, params) {
6136
- const { companyId, issueId, nextStatus, transitionComment, transitionCommentAnnotation, paperclipApiBaseUrl } = params;
6575
+ async function updatePaperclipIssueState(ctx, params) {
6576
+ const {
6577
+ companyId,
6578
+ issueId,
6579
+ currentStatus,
6580
+ syncContext,
6581
+ nextStatus,
6582
+ nextAssignee,
6583
+ transitionComment,
6584
+ transitionCommentAnnotation,
6585
+ paperclipApiBaseUrl
6586
+ } = params;
6137
6587
  const trimmedTransitionComment = transitionComment.trim();
6138
- let statusUpdated = false;
6588
+ let issueUpdated = false;
6589
+ const issuePatch = {
6590
+ status: nextStatus
6591
+ };
6592
+ if (nextAssignee) {
6593
+ if (nextAssignee.kind === "agent") {
6594
+ issuePatch.assigneeAgentId = nextAssignee.id;
6595
+ issuePatch.assigneeUserId = null;
6596
+ } else {
6597
+ issuePatch.assigneeAgentId = null;
6598
+ issuePatch.assigneeUserId = nextAssignee.id;
6599
+ }
6600
+ }
6139
6601
  if (paperclipApiBaseUrl) {
6140
6602
  try {
6141
6603
  const response = await fetchPaperclipApi(
@@ -6146,9 +6608,7 @@ async function updatePaperclipIssueStatus(ctx, params) {
6146
6608
  accept: "application/json",
6147
6609
  "content-type": "application/json"
6148
6610
  },
6149
- body: JSON.stringify({
6150
- status: nextStatus
6151
- })
6611
+ body: JSON.stringify(issuePatch)
6152
6612
  },
6153
6613
  {
6154
6614
  companyId
@@ -6159,7 +6619,7 @@ async function updatePaperclipIssueStatus(ctx, params) {
6159
6619
  bodyRequired: false
6160
6620
  });
6161
6621
  if (!payloadResult.failure) {
6162
- statusUpdated = true;
6622
+ issueUpdated = true;
6163
6623
  }
6164
6624
  if (payloadResult.failure && (payloadResult.failure.status ?? response.status) !== 404 && (payloadResult.failure.status ?? response.status) !== 405) {
6165
6625
  logPaperclipIssueStatusUpdateFailure(ctx, {
@@ -6181,8 +6641,29 @@ async function updatePaperclipIssueStatus(ctx, params) {
6181
6641
  });
6182
6642
  }
6183
6643
  }
6184
- if (!statusUpdated) {
6185
- await ctx.issues.update(issueId, { status: nextStatus }, companyId);
6644
+ if (!issueUpdated) {
6645
+ const fallbackExecutionStatePatch = buildSyncFallbackExecutionStatePatch({
6646
+ currentStatus,
6647
+ nextStatus,
6648
+ syncContext,
6649
+ nextAssignee
6650
+ });
6651
+ const preserveExistingUserAssigneeWithoutLocalApi = nextAssignee?.kind === "user" && !paperclipApiBaseUrl;
6652
+ const sdkIssuePatch = {
6653
+ ...issuePatch,
6654
+ ...fallbackExecutionStatePatch !== void 0 ? { executionState: fallbackExecutionStatePatch } : {}
6655
+ };
6656
+ if (preserveExistingUserAssigneeWithoutLocalApi) {
6657
+ delete sdkIssuePatch.assigneeAgentId;
6658
+ delete sdkIssuePatch.assigneeUserId;
6659
+ ctx.logger.warn("GitHub sync could not reassign a Paperclip issue to a user without the local Paperclip API. Preserving the existing assignee.", {
6660
+ companyId,
6661
+ issueId,
6662
+ nextStatus,
6663
+ assigneeUserId: nextAssignee.id
6664
+ });
6665
+ }
6666
+ await ctx.issues.update(issueId, sdkIssuePatch, companyId);
6186
6667
  }
6187
6668
  if (trimmedTransitionComment && typeof ctx.issues.createComment === "function") {
6188
6669
  const createdComment = await ctx.issues.createComment(issueId, trimmedTransitionComment, companyId);
@@ -6242,22 +6723,76 @@ function shouldIgnoreGitHubIssue(issue, advancedSettings) {
6242
6723
  (ignoredUsername) => buildGitHubUsernameAliases(ignoredUsername).some((alias) => issueAuthorAliases.has(alias))
6243
6724
  );
6244
6725
  }
6726
+ async function assignImportedPaperclipIssueToUser(ctx, params) {
6727
+ if (!params.paperclipApiBaseUrl) {
6728
+ ctx.logger.warn("GitHub sync could not assign a newly imported Paperclip issue to a user without the local Paperclip API. Preserving the imported issue assignment.", {
6729
+ companyId: params.companyId,
6730
+ issueId: params.issueId,
6731
+ assigneeUserId: params.assigneeUserId
6732
+ });
6733
+ return;
6734
+ }
6735
+ try {
6736
+ const response = await fetchPaperclipApi(
6737
+ getPaperclipIssueEndpoint(params.paperclipApiBaseUrl, params.issueId),
6738
+ {
6739
+ method: "PATCH",
6740
+ headers: {
6741
+ accept: "application/json",
6742
+ "content-type": "application/json"
6743
+ },
6744
+ body: JSON.stringify({
6745
+ assigneeAgentId: null,
6746
+ assigneeUserId: params.assigneeUserId
6747
+ })
6748
+ },
6749
+ {
6750
+ companyId: params.companyId
6751
+ }
6752
+ );
6753
+ const payloadResult = await readPaperclipApiJsonResponse(response, {
6754
+ operationLabel: "issue update",
6755
+ bodyRequired: false
6756
+ });
6757
+ if (!payloadResult.failure) {
6758
+ return;
6759
+ }
6760
+ throw new Error(payloadResult.failure.errorMessage ?? `Issue update failed with status ${payloadResult.failure.status ?? response.status}.`);
6761
+ } catch (error) {
6762
+ ctx.logger.warn("GitHub sync could not assign a newly imported Paperclip issue to a Paperclip user.", {
6763
+ companyId: params.companyId,
6764
+ issueId: params.issueId,
6765
+ assigneeUserId: params.assigneeUserId,
6766
+ paperclipApiBaseUrl: params.paperclipApiBaseUrl,
6767
+ error: getErrorMessage(error)
6768
+ });
6769
+ }
6770
+ }
6245
6771
  async function createPaperclipIssue(ctx, mapping, advancedSettings, issue, availableLabels, paperclipApiBaseUrl, syncFailureContext) {
6246
6772
  if (!mapping.companyId || !mapping.paperclipProjectId) {
6247
6773
  throw new Error(`Mapping ${mapping.id} is missing resolved Paperclip project identifiers.`);
6248
6774
  }
6249
6775
  const title = issue.title;
6250
6776
  const description = buildPaperclipIssueDescription(issue);
6777
+ const defaultAssignee = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "default");
6251
6778
  const createdIssue = await ctx.issues.create({
6252
6779
  companyId: mapping.companyId,
6253
6780
  projectId: mapping.paperclipProjectId,
6254
6781
  title,
6255
6782
  ...description ? { description } : {},
6256
- ...advancedSettings.defaultAssigneeAgentId ? { assigneeAgentId: advancedSettings.defaultAssigneeAgentId } : {}
6783
+ ...defaultAssignee?.kind === "agent" ? { assigneeAgentId: defaultAssignee.id } : defaultAssignee?.kind === "user" ? { assigneeUserId: defaultAssignee.id } : {}
6257
6784
  });
6258
6785
  const ensuredCreatedIssueId = createdIssue.id;
6259
6786
  const normalizedCreatedIssueDescription = createdIssue.description ?? void 0;
6260
6787
  const createPath = "sdk";
6788
+ if (defaultAssignee?.kind === "user") {
6789
+ await assignImportedPaperclipIssueToUser(ctx, {
6790
+ companyId: mapping.companyId,
6791
+ issueId: ensuredCreatedIssueId,
6792
+ assigneeUserId: defaultAssignee.id,
6793
+ paperclipApiBaseUrl
6794
+ });
6795
+ }
6261
6796
  if (normalizeIssueDescriptionValue(normalizedCreatedIssueDescription) !== description) {
6262
6797
  logIssueDescriptionDiagnostic(
6263
6798
  ctx,
@@ -6395,7 +6930,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6395
6930
  let updatedDescriptionsCount = 0;
6396
6931
  let completedIssueCount = 0;
6397
6932
  const totalIssueCount = importedIssues.length;
6398
- const queuedImportedIssueWakeups = [];
6933
+ const queuedIssueWakeups = [];
6399
6934
  for (const importedIssue of importedIssues) {
6400
6935
  if (assertNotCancelled) {
6401
6936
  await assertNotCancelled();
@@ -6518,6 +7053,11 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6518
7053
  githubIssue,
6519
7054
  maintainerCache: repositoryMaintainerCache
6520
7055
  }) : false;
7056
+ const paperclipIssueSyncContext = getPaperclipIssueSyncContext(paperclipIssue);
7057
+ const executorTransitionAssignee = resolvePaperclipIssueExecutorAssignee(
7058
+ paperclipIssueSyncContext,
7059
+ advancedSettings
7060
+ );
6521
7061
  const nextStatus = resolvePaperclipIssueStatus({
6522
7062
  currentStatus: paperclipIssue.status,
6523
7063
  snapshot,
@@ -6525,16 +7065,26 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6525
7065
  hasTrustedNewComment,
6526
7066
  wasImportedThisRun,
6527
7067
  defaultImportedStatus: advancedSettings.defaultStatus,
6528
- maintainerAuthoredImportedIssue
7068
+ maintainerAuthoredImportedIssue,
7069
+ hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
6529
7070
  });
6530
- const shouldWakeImportedAssignee = wasImportedThisRun && nextStatus === "todo" && Boolean(paperclipIssue.assigneeAgentId);
7071
+ const nextTransitionAssignee = resolveSyncTransitionAssignee({
7072
+ nextStatus,
7073
+ syncContext: paperclipIssueSyncContext,
7074
+ advancedSettings
7075
+ });
7076
+ const nextAssigneeChanged = nextTransitionAssignee ? !doesPaperclipIssueAssigneeMatch(paperclipIssueSyncContext.assignee, nextTransitionAssignee.principal) : false;
7077
+ const shouldWakeImportedAssignee = wasImportedThisRun && paperclipIssue.status === nextStatus && nextStatus === "todo" && paperclipIssueSyncContext.assignee?.kind === "agent";
7078
+ const shouldWakeTransitionAssignee = paperclipIssue.status !== nextStatus && nextTransitionAssignee?.principal.kind === "agent" && isActionablePaperclipIssueStatus(nextStatus) && (nextAssigneeChanged || paperclipIssue.status !== nextStatus);
6531
7079
  importedIssue.githubIssueNumber = githubIssue.number;
6532
7080
  importedIssue.lastSeenCommentCount = snapshot.commentCount;
6533
7081
  if (paperclipIssue.status === nextStatus) {
6534
7082
  if (shouldWakeImportedAssignee) {
6535
- queuedImportedIssueWakeups.push({
6536
- assigneeAgentId: paperclipIssue.assigneeAgentId,
6537
- paperclipIssueId: importedIssue.paperclipIssueId
7083
+ queuedIssueWakeups.push({
7084
+ assigneeAgentId: paperclipIssueSyncContext.assignee?.kind === "agent" ? paperclipIssueSyncContext.assignee.id : null,
7085
+ paperclipIssueId: importedIssue.paperclipIssueId,
7086
+ reason: IMPORTED_ISSUE_WAKE_REASON,
7087
+ mutation: "import"
6538
7088
  });
6539
7089
  }
6540
7090
  continue;
@@ -6553,19 +7103,26 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6553
7103
  repositoryUrl: repository.url,
6554
7104
  githubIssueNumber: githubIssue.number
6555
7105
  });
6556
- await updatePaperclipIssueStatus(ctx, {
7106
+ await updatePaperclipIssueState(ctx, {
6557
7107
  companyId: mapping.companyId,
6558
7108
  issueId: importedIssue.paperclipIssueId,
7109
+ currentStatus: paperclipIssue.status,
7110
+ syncContext: paperclipIssueSyncContext,
6559
7111
  nextStatus,
7112
+ ...nextTransitionAssignee ? { nextAssignee: nextTransitionAssignee.principal } : {},
6560
7113
  transitionComment: transitionComment.body,
6561
7114
  transitionCommentAnnotation: transitionComment.annotation,
6562
7115
  paperclipApiBaseUrl
6563
7116
  });
6564
7117
  updatedStatusesCount += 1;
6565
- if (shouldWakeImportedAssignee) {
6566
- queuedImportedIssueWakeups.push({
6567
- assigneeAgentId: paperclipIssue.assigneeAgentId,
6568
- paperclipIssueId: importedIssue.paperclipIssueId
7118
+ if (shouldWakeTransitionAssignee && nextTransitionAssignee?.principal.kind === "agent") {
7119
+ queuedIssueWakeups.push({
7120
+ assigneeAgentId: nextTransitionAssignee.principal.id,
7121
+ paperclipIssueId: importedIssue.paperclipIssueId,
7122
+ reason: STATUS_TRANSITION_WAKE_REASON,
7123
+ mutation: "status_transition",
7124
+ previousStatus: paperclipIssue.status,
7125
+ nextStatus
6569
7126
  });
6570
7127
  }
6571
7128
  } catch (error) {
@@ -6587,13 +7144,17 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
6587
7144
  }
6588
7145
  }
6589
7146
  await mapWithConcurrency(
6590
- queuedImportedIssueWakeups,
7147
+ queuedIssueWakeups,
6591
7148
  IMPORTED_ISSUE_WAKEUP_CONCURRENCY,
6592
- async (queuedWakeup) => wakePaperclipAssigneeForImportedIssue(ctx, {
7149
+ async (queuedWakeup) => wakePaperclipIssueAssignee(ctx, {
6593
7150
  assigneeAgentId: queuedWakeup.assigneeAgentId,
6594
7151
  paperclipIssueId: queuedWakeup.paperclipIssueId,
6595
7152
  companyId: mapping.companyId,
6596
- paperclipApiBaseUrl
7153
+ paperclipApiBaseUrl,
7154
+ reason: queuedWakeup.reason,
7155
+ mutation: queuedWakeup.mutation,
7156
+ previousStatus: queuedWakeup.previousStatus,
7157
+ nextStatus: queuedWakeup.nextStatus
6597
7158
  })
6598
7159
  );
6599
7160
  return {
@@ -11026,7 +11587,7 @@ var plugin = definePlugin({
11026
11587
  await saveSettingsSyncState(ctx, normalizedSettings, settingsForResponse.syncState, requestedCompanyId);
11027
11588
  }
11028
11589
  const scopedMappings = filterMappingsByCompany(settingsForResponse.mappings, requestedCompanyId);
11029
- const availableAssignees = includeAssignees && requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId) : [];
11590
+ const availableAssignees = includeAssignees && requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId, settingsForResponse) : [];
11030
11591
  return {
11031
11592
  ...getPublicSettingsForScope(settingsForResponse, requestedCompanyId),
11032
11593
  ...includeAssignees ? { availableAssignees } : {},
@@ -11137,6 +11698,7 @@ var plugin = definePlugin({
11137
11698
  ...githubTokenLogin ? { githubTokenLogin } : {},
11138
11699
  paperclipBoardApiTokenRefs: previous.paperclipBoardApiTokenRefs,
11139
11700
  paperclipBoardAccessIdentityByCompanyId: previous.paperclipBoardAccessIdentityByCompanyId,
11701
+ paperclipBoardAccessUserIdByCompanyId: previous.paperclipBoardAccessUserIdByCompanyId,
11140
11702
  ...Object.keys(nextCompanyAdvancedSettingsByCompanyId).length > 0 ? { companyAdvancedSettingsByCompanyId: nextCompanyAdvancedSettingsByCompanyId } : {},
11141
11703
  ...githubTokenRef ? { githubTokenRef } : {}
11142
11704
  });
@@ -11163,6 +11725,7 @@ var plugin = definePlugin({
11163
11725
  ...current.githubTokenLogin ? { githubTokenLogin: current.githubTokenLogin } : {},
11164
11726
  ...current.paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs: current.paperclipBoardApiTokenRefs } : {},
11165
11727
  ...current.paperclipBoardAccessIdentityByCompanyId ? { paperclipBoardAccessIdentityByCompanyId: current.paperclipBoardAccessIdentityByCompanyId } : {},
11728
+ ...current.paperclipBoardAccessUserIdByCompanyId ? { paperclipBoardAccessUserIdByCompanyId: current.paperclipBoardAccessUserIdByCompanyId } : {},
11166
11729
  ...current.companyAdvancedSettingsByCompanyId ? { companyAdvancedSettingsByCompanyId: current.companyAdvancedSettingsByCompanyId } : {},
11167
11730
  ...githubTokenRef ? { githubTokenRef } : {},
11168
11731
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -11177,7 +11740,7 @@ var plugin = definePlugin({
11177
11740
  return {
11178
11741
  ...getPublicSettingsForScope(next, requestedCompanyId),
11179
11742
  ...scopedGitHubTokenLogin ? { githubTokenLogin: scopedGitHubTokenLogin } : {},
11180
- availableAssignees: requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId) : []
11743
+ availableAssignees: requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId, next) : []
11181
11744
  };
11182
11745
  });
11183
11746
  ctx.actions.register("settings.updateBoardAccess", async (input) => {
@@ -11195,11 +11758,15 @@ var plugin = definePlugin({
11195
11758
  const nextPaperclipBoardAccessIdentityByCompanyId = {
11196
11759
  ...previous.paperclipBoardAccessIdentityByCompanyId ?? {}
11197
11760
  };
11761
+ const nextPaperclipBoardAccessUserIdByCompanyId = {
11762
+ ...previous.paperclipBoardAccessUserIdByCompanyId ?? {}
11763
+ };
11198
11764
  if (nextSecretRef) {
11199
11765
  nextPaperclipBoardApiTokenRefs[companyId] = nextSecretRef;
11200
11766
  } else {
11201
11767
  delete nextPaperclipBoardApiTokenRefs[companyId];
11202
11768
  delete nextPaperclipBoardAccessIdentityByCompanyId[companyId];
11769
+ delete nextPaperclipBoardAccessUserIdByCompanyId[companyId];
11203
11770
  }
11204
11771
  if ("paperclipBoardAccessIdentity" in record) {
11205
11772
  const nextIdentityLabel = normalizeOptionalString2(record.paperclipBoardAccessIdentity);
@@ -11209,15 +11776,25 @@ var plugin = definePlugin({
11209
11776
  delete nextPaperclipBoardAccessIdentityByCompanyId[companyId];
11210
11777
  }
11211
11778
  }
11779
+ if ("paperclipBoardAccessUserId" in record) {
11780
+ const nextUserId = normalizeOptionalString2(record.paperclipBoardAccessUserId);
11781
+ if (nextUserId) {
11782
+ nextPaperclipBoardAccessUserIdByCompanyId[companyId] = nextUserId;
11783
+ } else {
11784
+ delete nextPaperclipBoardAccessUserIdByCompanyId[companyId];
11785
+ }
11786
+ }
11212
11787
  const {
11213
11788
  paperclipBoardApiTokenRefs: _previousPaperclipBoardApiTokenRefs,
11214
11789
  paperclipBoardAccessIdentityByCompanyId: _previousPaperclipBoardAccessIdentityByCompanyId,
11790
+ paperclipBoardAccessUserIdByCompanyId: _previousPaperclipBoardAccessUserIdByCompanyId,
11215
11791
  ...previousWithoutBoardAccess
11216
11792
  } = previous;
11217
11793
  const nextBase = sanitizeSettingsForCurrentSetup({
11218
11794
  ...previousWithoutBoardAccess,
11219
11795
  ...Object.keys(nextPaperclipBoardApiTokenRefs).length > 0 ? { paperclipBoardApiTokenRefs: nextPaperclipBoardApiTokenRefs } : {},
11220
11796
  ...Object.keys(nextPaperclipBoardAccessIdentityByCompanyId).length > 0 ? { paperclipBoardAccessIdentityByCompanyId: nextPaperclipBoardAccessIdentityByCompanyId } : {},
11797
+ ...Object.keys(nextPaperclipBoardAccessUserIdByCompanyId).length > 0 ? { paperclipBoardAccessUserIdByCompanyId: nextPaperclipBoardAccessUserIdByCompanyId } : {},
11221
11798
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
11222
11799
  }, {
11223
11800
  hasToken: hasConfiguredGithubToken(previous, config, companyId),