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/ui/index.js CHANGED
@@ -22411,6 +22411,7 @@ function normalizeCompanyAssigneeOptionsResponse(response) {
22411
22411
  return null;
22412
22412
  }
22413
22413
  const record = entry;
22414
+ const kind = record.kind === "user" ? "user" : "agent";
22414
22415
  const id = typeof record.id === "string" ? record.id.trim() : "";
22415
22416
  const name2 = typeof record.name === "string" ? record.name.trim() : "";
22416
22417
  const status = typeof record.status === "string" ? record.status.trim() : "";
@@ -22419,12 +22420,18 @@ function normalizeCompanyAssigneeOptionsResponse(response) {
22419
22420
  return null;
22420
22421
  }
22421
22422
  return {
22423
+ kind,
22422
22424
  id,
22423
22425
  name: name2,
22424
22426
  ...title ? { title } : {},
22425
22427
  ...status ? { status } : {}
22426
22428
  };
22427
- }).filter((entry) => entry !== null).sort((left, right) => left.name.localeCompare(right.name));
22429
+ }).filter((entry) => entry !== null).sort((left, right) => {
22430
+ if (left.kind !== right.kind) {
22431
+ return left.kind === "user" ? -1 : 1;
22432
+ }
22433
+ return left.name.localeCompare(right.name);
22434
+ });
22428
22435
  }
22429
22436
 
22430
22437
  // src/ui/http.ts
@@ -23927,9 +23934,15 @@ var PAGE_STYLES = `
23927
23934
 
23928
23935
  .ghsync__picker {
23929
23936
  position: relative;
23937
+ min-width: 0;
23938
+ }
23939
+
23940
+ .ghsync__picker--full {
23941
+ width: 100%;
23930
23942
  }
23931
23943
 
23932
23944
  .ghsync__picker-trigger {
23945
+ box-sizing: border-box;
23933
23946
  width: fit-content;
23934
23947
  max-width: 100%;
23935
23948
  min-height: 0;
@@ -23962,6 +23975,10 @@ var PAGE_STYLES = `
23962
23975
  background: var(--ghsync-surfaceRaised);
23963
23976
  }
23964
23977
 
23978
+ .ghsync__picker--full .ghsync__picker-trigger {
23979
+ width: 100%;
23980
+ }
23981
+
23965
23982
  .ghsync__picker-trigger--assignee {
23966
23983
  min-width: 10rem;
23967
23984
  font-size: 14px;
@@ -24012,6 +24029,7 @@ var PAGE_STYLES = `
24012
24029
  }
24013
24030
 
24014
24031
  .ghsync__picker-panel {
24032
+ box-sizing: border-box;
24015
24033
  position: absolute;
24016
24034
  top: calc(100% + 8px);
24017
24035
  left: 0;
@@ -24024,7 +24042,8 @@ var PAGE_STYLES = `
24024
24042
  }
24025
24043
 
24026
24044
  .ghsync__picker-panel--assignee {
24027
- width: min(20rem, calc(100vw - 2rem));
24045
+ width: min(20rem, 100%);
24046
+ max-width: calc(100vw - 2rem);
24028
24047
  }
24029
24048
 
24030
24049
  .ghsync__picker-panel--status {
@@ -24976,6 +24995,7 @@ var PROJECT_PULL_REQUESTS_PAGE_STYLES = `
24976
24995
  font-weight: 700;
24977
24996
  letter-spacing: 0.02em;
24978
24997
  flex: 0 0 auto;
24998
+ overflow: hidden;
24979
24999
  }
24980
25000
 
24981
25001
  .ghsync-prs-avatar img,
@@ -26630,14 +26650,45 @@ function normalizeAgentIds(value) {
26630
26650
  rawEntries.map((entry) => typeof entry === "string" && entry.trim() ? entry.trim() : null).filter((entry) => Boolean(entry))
26631
26651
  )].sort((left, right) => left.localeCompare(right));
26632
26652
  }
26653
+ function normalizeAdvancedSettingsAssigneeOverride(record, keys2) {
26654
+ const rawUserId = record[keys2.userId];
26655
+ const userId = typeof rawUserId === "string" && rawUserId.trim() ? rawUserId.trim() : void 0;
26656
+ if (userId) {
26657
+ return {
26658
+ [keys2.userId]: userId
26659
+ };
26660
+ }
26661
+ const rawAgentId = record[keys2.agentId];
26662
+ const agentId = typeof rawAgentId === "string" && rawAgentId.trim() ? rawAgentId.trim() : void 0;
26663
+ if (agentId) {
26664
+ return {
26665
+ [keys2.agentId]: agentId
26666
+ };
26667
+ }
26668
+ return {};
26669
+ }
26633
26670
  function normalizeAdvancedSettings(value) {
26634
26671
  if (!value || typeof value !== "object") {
26635
26672
  return DEFAULT_ADVANCED_SETTINGS;
26636
26673
  }
26637
26674
  const record = value;
26638
- const defaultAssigneeAgentId = typeof record.defaultAssigneeAgentId === "string" && record.defaultAssigneeAgentId.trim() ? record.defaultAssigneeAgentId.trim() : void 0;
26639
26675
  return {
26640
- ...defaultAssigneeAgentId ? { defaultAssigneeAgentId } : {},
26676
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26677
+ agentId: "defaultAssigneeAgentId",
26678
+ userId: "defaultAssigneeUserId"
26679
+ }),
26680
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26681
+ agentId: "executorAssigneeAgentId",
26682
+ userId: "executorAssigneeUserId"
26683
+ }),
26684
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26685
+ agentId: "reviewerAssigneeAgentId",
26686
+ userId: "reviewerAssigneeUserId"
26687
+ }),
26688
+ ...normalizeAdvancedSettingsAssigneeOverride(record, {
26689
+ agentId: "approverAssigneeAgentId",
26690
+ userId: "approverAssigneeUserId"
26691
+ }),
26641
26692
  defaultStatus: normalizePaperclipIssueStatus(record.defaultStatus),
26642
26693
  ignoredIssueAuthorUsernames: "ignoredIssueAuthorUsernames" in record ? normalizeIgnoredIssueAuthorUsernames(record.ignoredIssueAuthorUsernames) : DEFAULT_ADVANCED_SETTINGS.ignoredIssueAuthorUsernames,
26643
26694
  ...normalizeAgentIds(record.githubTokenPropagationAgentIds).length > 0 ? { githubTokenPropagationAgentIds: normalizeAgentIds(record.githubTokenPropagationAgentIds) } : {}
@@ -26656,6 +26707,13 @@ function getComparableAdvancedSettings(value) {
26656
26707
  const settings = normalizeAdvancedSettings(value);
26657
26708
  return {
26658
26709
  ...settings.defaultAssigneeAgentId ? { defaultAssigneeAgentId: settings.defaultAssigneeAgentId } : {},
26710
+ ...settings.defaultAssigneeUserId ? { defaultAssigneeUserId: settings.defaultAssigneeUserId } : {},
26711
+ ...settings.executorAssigneeAgentId ? { executorAssigneeAgentId: settings.executorAssigneeAgentId } : {},
26712
+ ...settings.executorAssigneeUserId ? { executorAssigneeUserId: settings.executorAssigneeUserId } : {},
26713
+ ...settings.reviewerAssigneeAgentId ? { reviewerAssigneeAgentId: settings.reviewerAssigneeAgentId } : {},
26714
+ ...settings.reviewerAssigneeUserId ? { reviewerAssigneeUserId: settings.reviewerAssigneeUserId } : {},
26715
+ ...settings.approverAssigneeAgentId ? { approverAssigneeAgentId: settings.approverAssigneeAgentId } : {},
26716
+ ...settings.approverAssigneeUserId ? { approverAssigneeUserId: settings.approverAssigneeUserId } : {},
26659
26717
  defaultStatus: settings.defaultStatus,
26660
26718
  ignoredIssueAuthorUsernames: [...settings.ignoredIssueAuthorUsernames].sort((left, right) => left.localeCompare(right)),
26661
26719
  ...settings.githubTokenPropagationAgentIds?.length ? { githubTokenPropagationAgentIds: [...settings.githubTokenPropagationAgentIds].sort((left, right) => left.localeCompare(right)) } : {}
@@ -26664,43 +26722,127 @@ function getComparableAdvancedSettings(value) {
26664
26722
  function formatAssigneeOptionLabel(option) {
26665
26723
  return option.title?.trim() ? `${option.name} (${option.title.trim()})` : option.name;
26666
26724
  }
26667
- function getAvailableAssigneeOptions(options, selectedAgentId) {
26725
+ function getAssigneeOptionKey(option) {
26726
+ return `${option.kind}:${option.id}`;
26727
+ }
26728
+ function getAssigneeOptionValue(option) {
26729
+ return getAssigneeOptionKey(option);
26730
+ }
26731
+ function parseAssigneeOptionValue(value) {
26732
+ const trimmedValue = typeof value === "string" ? value.trim() : "";
26733
+ if (!trimmedValue) {
26734
+ return null;
26735
+ }
26736
+ if (trimmedValue.startsWith("agent:")) {
26737
+ const id = trimmedValue.slice("agent:".length).trim();
26738
+ return id ? { kind: "agent", id } : null;
26739
+ }
26740
+ if (trimmedValue.startsWith("user:")) {
26741
+ const id = trimmedValue.slice("user:".length).trim();
26742
+ return id ? { kind: "user", id } : null;
26743
+ }
26744
+ return { kind: "agent", id: trimmedValue };
26745
+ }
26746
+ function getAdvancedSettingsAssigneePrincipal(advancedSettings, role) {
26747
+ switch (role) {
26748
+ case "default":
26749
+ return advancedSettings.defaultAssigneeUserId ? { kind: "user", id: advancedSettings.defaultAssigneeUserId } : advancedSettings.defaultAssigneeAgentId ? { kind: "agent", id: advancedSettings.defaultAssigneeAgentId } : null;
26750
+ case "executor":
26751
+ return advancedSettings.executorAssigneeUserId ? { kind: "user", id: advancedSettings.executorAssigneeUserId } : advancedSettings.executorAssigneeAgentId ? { kind: "agent", id: advancedSettings.executorAssigneeAgentId } : null;
26752
+ case "reviewer":
26753
+ return advancedSettings.reviewerAssigneeUserId ? { kind: "user", id: advancedSettings.reviewerAssigneeUserId } : advancedSettings.reviewerAssigneeAgentId ? { kind: "agent", id: advancedSettings.reviewerAssigneeAgentId } : null;
26754
+ case "approver":
26755
+ return advancedSettings.approverAssigneeUserId ? { kind: "user", id: advancedSettings.approverAssigneeUserId } : advancedSettings.approverAssigneeAgentId ? { kind: "agent", id: advancedSettings.approverAssigneeAgentId } : null;
26756
+ }
26757
+ }
26758
+ function setAdvancedSettingsAssigneePrincipal(advancedSettings, role, principal) {
26759
+ switch (role) {
26760
+ case "default":
26761
+ return {
26762
+ ...advancedSettings,
26763
+ defaultAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26764
+ defaultAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26765
+ };
26766
+ case "executor":
26767
+ return {
26768
+ ...advancedSettings,
26769
+ executorAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26770
+ executorAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26771
+ };
26772
+ case "reviewer":
26773
+ return {
26774
+ ...advancedSettings,
26775
+ reviewerAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26776
+ reviewerAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26777
+ };
26778
+ case "approver":
26779
+ return {
26780
+ ...advancedSettings,
26781
+ approverAssigneeAgentId: principal?.kind === "agent" ? principal.id : void 0,
26782
+ approverAssigneeUserId: principal?.kind === "user" ? principal.id : void 0
26783
+ };
26784
+ }
26785
+ }
26786
+ function getSelectedAssigneeOptionValue(advancedSettings, role) {
26787
+ const principal = getAdvancedSettingsAssigneePrincipal(advancedSettings, role);
26788
+ return principal ? getAssigneeOptionValue(principal) : "";
26789
+ }
26790
+ function compareAssigneeOptions(left, right) {
26791
+ if (left.kind !== right.kind) {
26792
+ return left.kind === "user" ? -1 : 1;
26793
+ }
26794
+ return left.name.localeCompare(right.name);
26795
+ }
26796
+ function getAvailableAssigneeOptions(options, selectedAssignees) {
26668
26797
  const normalizedOptions = [...options ?? []];
26669
- if (selectedAgentId && !normalizedOptions.some((option) => option.id === selectedAgentId)) {
26670
- normalizedOptions.push({
26671
- id: selectedAgentId,
26672
- name: "Unavailable agent"
26673
- });
26798
+ const selectedPrincipals = Array.isArray(selectedAssignees) ? selectedAssignees.filter((selectedAssignee) => Boolean(selectedAssignee)) : selectedAssignees ? [selectedAssignees] : [];
26799
+ for (const selectedPrincipal of selectedPrincipals) {
26800
+ if (!normalizedOptions.some((option) => getAssigneeOptionKey(option) === getAssigneeOptionKey(selectedPrincipal))) {
26801
+ normalizedOptions.push({
26802
+ kind: selectedPrincipal.kind,
26803
+ id: selectedPrincipal.id,
26804
+ name: selectedPrincipal.kind === "user" ? "Unavailable user" : "Unavailable agent"
26805
+ });
26806
+ }
26674
26807
  }
26675
- return normalizedOptions;
26808
+ return normalizedOptions.sort(compareAssigneeOptions);
26676
26809
  }
26677
26810
  function getAvailablePropagationAgentOptions(options, selectedAgentIds) {
26678
- const normalizedOptions = [...options ?? []];
26811
+ const normalizedOptions = [...(options ?? []).filter((option) => option.kind === "agent")];
26679
26812
  const selectedIds = normalizeAgentIds(selectedAgentIds);
26680
26813
  for (const selectedAgentId of selectedIds) {
26681
26814
  if (!normalizedOptions.some((option) => option.id === selectedAgentId)) {
26682
26815
  normalizedOptions.push({
26816
+ kind: "agent",
26683
26817
  id: selectedAgentId,
26684
26818
  name: "Unavailable agent"
26685
26819
  });
26686
26820
  }
26687
26821
  }
26688
- return normalizedOptions.sort((left, right) => left.name.localeCompare(right.name));
26822
+ return normalizedOptions.sort(compareAssigneeOptions);
26689
26823
  }
26690
26824
  function formatAdvancedSettingsSummary(advancedSettings, availableAssignees, options) {
26691
- const assigneeLabel = advancedSettings.defaultAssigneeAgentId ? formatAssigneeOptionLabel(
26692
- availableAssignees.find((option) => option.id === advancedSettings.defaultAssigneeAgentId) ?? {
26693
- id: advancedSettings.defaultAssigneeAgentId,
26694
- name: "Unavailable agent"
26695
- }
26696
- ) : "Unassigned";
26825
+ const resolveAssigneeLabel = (principal, fallbackLabel = "None") => {
26826
+ return principal ? formatAssigneeOptionLabel(
26827
+ availableAssignees.find((option) => getAssigneeOptionKey(option) === getAssigneeOptionKey(principal)) ?? {
26828
+ kind: principal.kind,
26829
+ id: principal.id,
26830
+ name: principal.kind === "user" ? "Unavailable user" : "Unavailable agent"
26831
+ }
26832
+ ) : fallbackLabel;
26833
+ };
26834
+ const assigneeLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "default"), "Unassigned");
26835
+ const executorLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "executor"), "Automatic routing");
26836
+ const reviewerLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "reviewer"), "Automatic routing");
26837
+ const approverLabel = resolveAssigneeLabel(getAdvancedSettingsAssigneePrincipal(advancedSettings, "approver"), "Automatic routing");
26697
26838
  const statusLabel = PAPERCLIP_STATUS_OPTIONS.find((option) => option.value === advancedSettings.defaultStatus)?.label ?? "Backlog";
26698
26839
  const ignoredAuthorsLabel = advancedSettings.ignoredIssueAuthorUsernames.length > 0 ? advancedSettings.ignoredIssueAuthorUsernames.join(", ") : "none";
26840
+ const handoffLabel = `Back to work: ${executorLabel} \xB7 Review: ${reviewerLabel} \xB7 Approval: ${approverLabel}`;
26699
26841
  if (options?.includePropagation) {
26700
26842
  const propagatedAgentsLabel = advancedSettings.githubTokenPropagationAgentIds?.length ? `${advancedSettings.githubTokenPropagationAgentIds.length} selected` : "none";
26701
- return `Assignee: ${assigneeLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel} \xB7 Propagate: ${propagatedAgentsLabel}`;
26843
+ return `Import: ${assigneeLabel} \xB7 ${handoffLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel} \xB7 Propagate: ${propagatedAgentsLabel}`;
26702
26844
  }
26703
- return `Assignee: ${assigneeLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel}`;
26845
+ return `Import: ${assigneeLabel} \xB7 ${handoffLabel} \xB7 Status: ${statusLabel} \xB7 Ignore: ${ignoredAuthorsLabel}`;
26704
26846
  }
26705
26847
  function resolveSavedTokenUiState(params) {
26706
26848
  if (params.githubTokenConfigured) {
@@ -26771,6 +26913,29 @@ function AgentIcon() {
26771
26913
  )
26772
26914
  ] });
26773
26915
  }
26916
+ function UserIcon() {
26917
+ return /* @__PURE__ */ jsxs2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
26918
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "5.1", r: "2.3", stroke: "currentColor", strokeWidth: "1.2" }),
26919
+ /* @__PURE__ */ jsx2(
26920
+ "path",
26921
+ {
26922
+ d: "M3.25 12.85C3.78 10.83 5.62 9.4 7.82 9.4H8.18C10.38 9.4 12.22 10.83 12.75 12.85",
26923
+ stroke: "currentColor",
26924
+ strokeWidth: "1.2",
26925
+ strokeLinecap: "round"
26926
+ }
26927
+ )
26928
+ ] });
26929
+ }
26930
+ function SettingsSelectIcon(props) {
26931
+ if (props.icon === "agent") {
26932
+ return /* @__PURE__ */ jsx2(AgentIcon, {});
26933
+ }
26934
+ if (props.icon === "user") {
26935
+ return /* @__PURE__ */ jsx2(UserIcon, {});
26936
+ }
26937
+ return null;
26938
+ }
26774
26939
  function SettingsAssigneePicker(props) {
26775
26940
  const { id, value, options, disabled, onChange } = props;
26776
26941
  const [open, setOpen] = useState2(false);
@@ -26813,7 +26978,7 @@ function SettingsAssigneePicker(props) {
26813
26978
  setOpen(false);
26814
26979
  }
26815
26980
  }, [disabled, open]);
26816
- return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker", ref: rootRef, children: [
26981
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker ghsync__picker--full", ref: rootRef, children: [
26817
26982
  /* @__PURE__ */ jsxs2(
26818
26983
  "button",
26819
26984
  {
@@ -26831,7 +26996,7 @@ function SettingsAssigneePicker(props) {
26831
26996
  },
26832
26997
  children: [
26833
26998
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26834
- selectedOption?.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
26999
+ selectedOption?.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: selectedOption.icon }) }) : null,
26835
27000
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-trigger-label", children: selectedOption?.label ?? "No assignee" })
26836
27001
  ] }),
26837
27002
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-trigger-icon", children: /* @__PURE__ */ jsx2(PickerChevronIcon, {}) })
@@ -26873,7 +27038,7 @@ function SettingsAssigneePicker(props) {
26873
27038
  },
26874
27039
  children: [
26875
27040
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26876
- option.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27041
+ option.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: option.icon }) }) : null,
26877
27042
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-label", children: option.label })
26878
27043
  ] }),
26879
27044
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-check", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx2(PickerCheckIcon, {}) : null })
@@ -26929,7 +27094,7 @@ function SettingsAgentMultiPicker(props) {
26929
27094
  setOpen(false);
26930
27095
  }
26931
27096
  }, [disabled, open]);
26932
- return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker", ref: rootRef, children: [
27097
+ return /* @__PURE__ */ jsxs2("div", { className: "ghsync__picker ghsync__picker--full", ref: rootRef, children: [
26933
27098
  /* @__PURE__ */ jsxs2(
26934
27099
  "button",
26935
27100
  {
@@ -26989,7 +27154,7 @@ function SettingsAgentMultiPicker(props) {
26989
27154
  },
26990
27155
  children: [
26991
27156
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__picker-trigger-main", children: [
26992
- option.icon === "agent" ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(AgentIcon, {}) }) : null,
27157
+ option.icon ? /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-agent-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SettingsSelectIcon, { icon: option.icon }) }) : null,
26993
27158
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-label", children: option.label })
26994
27159
  ] }),
26995
27160
  /* @__PURE__ */ jsx2("span", { className: "ghsync__picker-option-check", "aria-hidden": "true", children: selected ? /* @__PURE__ */ jsx2(PickerCheckIcon, {}) : null })
@@ -28092,6 +28257,20 @@ function getCliAuthIdentityLabel(identity) {
28092
28257
  }
28093
28258
  return null;
28094
28259
  }
28260
+ function getCliAuthIdentityUserId(identity) {
28261
+ const candidates = [
28262
+ identity.user?.id,
28263
+ identity.user?.userId,
28264
+ identity.id,
28265
+ identity.userId
28266
+ ];
28267
+ for (const candidate of candidates) {
28268
+ if (typeof candidate === "string" && candidate.trim()) {
28269
+ return candidate.trim();
28270
+ }
28271
+ }
28272
+ return null;
28273
+ }
28095
28274
  function waitForDuration(durationMs) {
28096
28275
  return new Promise((resolve) => {
28097
28276
  globalThis.setTimeout(resolve, durationMs);
@@ -28146,7 +28325,10 @@ async function fetchBoardAccessIdentity(boardApiToken) {
28146
28325
  authorization: `Bearer ${boardApiToken.trim()}`
28147
28326
  }
28148
28327
  });
28149
- return getCliAuthIdentityLabel(identity);
28328
+ return {
28329
+ label: getCliAuthIdentityLabel(identity),
28330
+ userId: getCliAuthIdentityUserId(identity)
28331
+ };
28150
28332
  }
28151
28333
  function getSyncStatus(syncState, runningSync, syncUnlocked) {
28152
28334
  if (!syncUnlocked) {
@@ -28782,24 +28964,43 @@ function SyncDiagnosticsPanel(props) {
28782
28964
  function PreviewAvatar(props) {
28783
28965
  const backgroundColor = getPreviewAvatarColor(props.person.handle);
28784
28966
  const className = props.stacked ? "ghsync-prs-avatar-stack__item" : "ghsync-prs-avatar";
28785
- const avatarSizePx = props.size === "sm" ? 20 : 28;
28786
- const fontSizePx = props.size === "sm" ? 10 : 11;
28787
28967
  const labels = resolvePreviewPersonLabels(props.person);
28788
28968
  const initialsSource = props.person.name.trim() || props.person.handle.replace(/^@/, "").trim();
28789
28969
  const title = labels.secondary ? `${labels.primary} (${labels.secondary})` : labels.primary;
28970
+ const avatarStyle = {
28971
+ backgroundColor
28972
+ };
28973
+ const imageStyle = props.size === "sm" ? {
28974
+ width: "100%",
28975
+ height: "100%",
28976
+ borderRadius: "inherit",
28977
+ objectFit: "cover",
28978
+ display: "block"
28979
+ } : void 0;
28980
+ if (props.size === "sm") {
28981
+ Object.assign(avatarStyle, {
28982
+ width: 20,
28983
+ height: 20,
28984
+ fontSize: 10,
28985
+ display: "inline-flex",
28986
+ alignItems: "center",
28987
+ justifyContent: "center",
28988
+ borderRadius: 999,
28989
+ color: "white",
28990
+ fontWeight: 700,
28991
+ letterSpacing: "0.02em",
28992
+ flex: "0 0 auto",
28993
+ overflow: "hidden"
28994
+ });
28995
+ }
28790
28996
  return /* @__PURE__ */ jsx2(
28791
28997
  "span",
28792
28998
  {
28793
28999
  className,
28794
- style: {
28795
- backgroundColor,
28796
- width: avatarSizePx,
28797
- height: avatarSizePx,
28798
- fontSize: fontSizePx
28799
- },
29000
+ style: avatarStyle,
28800
29001
  title,
28801
29002
  "aria-hidden": "true",
28802
- children: props.person.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: props.person.avatarUrl, alt: "", loading: "lazy" }) : getInitials(initialsSource)
29003
+ children: props.person.avatarUrl ? /* @__PURE__ */ jsx2("img", { src: props.person.avatarUrl, alt: "", loading: "lazy", style: imageStyle }) : getInitials(initialsSource)
28803
29004
  }
28804
29005
  );
28805
29006
  }
@@ -31419,7 +31620,12 @@ function GitHubSyncSettingsPage() {
31419
31620
  const repositoriesUnlocked = tokenStatus === "valid";
31420
31621
  const availableAssignees = getAvailableAssigneeOptions(
31421
31622
  (currentSettings?.availableAssignees?.length ? currentSettings.availableAssignees : null) ?? (form.availableAssignees?.length ? form.availableAssignees : null) ?? browserAvailableAssignees,
31422
- form.advancedSettings.defaultAssigneeAgentId
31623
+ [
31624
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "default"),
31625
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "executor"),
31626
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "reviewer"),
31627
+ getAdvancedSettingsAssigneePrincipal(form.advancedSettings, "approver")
31628
+ ]
31423
31629
  );
31424
31630
  const propagationAgents = getAvailablePropagationAgentOptions(
31425
31631
  (currentSettings?.availableAssignees?.length ? currentSettings.availableAssignees : null) ?? (form.availableAssignees?.length ? form.availableAssignees : null) ?? browserAvailableAssignees,
@@ -31498,9 +31704,17 @@ function GitHubSyncSettingsPage() {
31498
31704
  const assigneeSelectOptions = [
31499
31705
  { value: "", label: "Unassigned" },
31500
31706
  ...availableAssignees.map((option) => ({
31501
- value: option.id,
31707
+ value: getAssigneeOptionValue(option),
31502
31708
  label: formatAssigneeOptionLabel(option),
31503
- icon: "agent"
31709
+ icon: option.kind
31710
+ }))
31711
+ ];
31712
+ const transitionAssigneeSelectOptions = [
31713
+ { value: "", label: "Automatic routing" },
31714
+ ...availableAssignees.map((option) => ({
31715
+ value: getAssigneeOptionValue(option),
31716
+ label: formatAssigneeOptionLabel(option),
31717
+ icon: option.kind
31504
31718
  }))
31505
31719
  ];
31506
31720
  const propagationAgentOptions = propagationAgents.map((option) => ({
@@ -31800,7 +32014,7 @@ function GitHubSyncSettingsPage() {
31800
32014
  throw new Error("Allow pop-ups for Paperclip, then try connecting board access again.");
31801
32015
  }
31802
32016
  const boardApiToken = await waitForBoardAccessApproval(challenge);
31803
- const identity = await fetchBoardAccessIdentity(boardApiToken);
32017
+ const boardIdentity = await fetchBoardAccessIdentity(boardApiToken);
31804
32018
  const secretName = `paperclip_board_api_${companyId.replace(/[^a-z0-9]+/gi, "_").toLowerCase()}`;
31805
32019
  const secret = await resolveOrCreateCompanySecret(companyId, secretName, boardApiToken);
31806
32020
  await patchPluginConfig(pluginId, {
@@ -31811,15 +32025,16 @@ function GitHubSyncSettingsPage() {
31811
32025
  await updateBoardAccess({
31812
32026
  companyId,
31813
32027
  paperclipBoardApiTokenRef: secret.id,
31814
- paperclipBoardAccessIdentity: identity ?? ""
32028
+ paperclipBoardAccessIdentity: boardIdentity.label ?? "",
32029
+ paperclipBoardAccessUserId: boardIdentity.userId ?? ""
31815
32030
  });
31816
- setBoardAccessIdentity(identity);
32031
+ setBoardAccessIdentity(boardIdentity.label);
31817
32032
  setForm((current) => ({
31818
32033
  ...current,
31819
32034
  paperclipBoardAccessConfigured: true
31820
32035
  }));
31821
32036
  toast({
31822
- title: identity ? `Paperclip board access connected as ${identity}` : "Paperclip board access connected",
32037
+ title: boardIdentity.label ? `Paperclip board access connected as ${boardIdentity.label}` : "Paperclip board access connected",
31823
32038
  body: "Direct Paperclip REST calls can now authenticate in authenticated deployments.",
31824
32039
  tone: "success"
31825
32040
  });
@@ -32374,16 +32589,17 @@ function GitHubSyncSettingsPage() {
32374
32589
  SettingsAssigneePicker,
32375
32590
  {
32376
32591
  id: "advanced-default-assignee",
32377
- value: form.advancedSettings.defaultAssigneeAgentId ?? "",
32592
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "default"),
32378
32593
  options: assigneeSelectOptions,
32379
32594
  disabled: settingsMutationsLocked,
32380
32595
  onChange: (nextValue) => {
32381
32596
  setForm((current) => ({
32382
32597
  ...current,
32383
- advancedSettings: {
32384
- ...current.advancedSettings,
32385
- ...nextValue ? { defaultAssigneeAgentId: nextValue } : { defaultAssigneeAgentId: void 0 }
32386
- }
32598
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32599
+ current.advancedSettings,
32600
+ "default",
32601
+ parseAssigneeOptionValue(nextValue)
32602
+ )
32387
32603
  }));
32388
32604
  }
32389
32605
  }
@@ -32409,8 +32625,78 @@ function GitHubSyncSettingsPage() {
32409
32625
  }
32410
32626
  }
32411
32627
  )
32628
+ ] }),
32629
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32630
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-executor-assignee", children: "Executor handoff" }),
32631
+ /* @__PURE__ */ jsx2(
32632
+ SettingsAssigneePicker,
32633
+ {
32634
+ id: "advanced-executor-assignee",
32635
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "executor"),
32636
+ options: transitionAssigneeSelectOptions,
32637
+ disabled: settingsMutationsLocked,
32638
+ onChange: (nextValue) => {
32639
+ setForm((current) => ({
32640
+ ...current,
32641
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32642
+ current.advancedSettings,
32643
+ "executor",
32644
+ parseAssigneeOptionValue(nextValue)
32645
+ )
32646
+ }));
32647
+ }
32648
+ }
32649
+ ),
32650
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "The assignee that resumes work when GitHub Sync sends an issue back to active execution, such as failing CI, unresolved review threads, or a trusted new GitHub comment." })
32651
+ ] }),
32652
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32653
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-reviewer-assignee", children: "Reviewer handoff" }),
32654
+ /* @__PURE__ */ jsx2(
32655
+ SettingsAssigneePicker,
32656
+ {
32657
+ id: "advanced-reviewer-assignee",
32658
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "reviewer"),
32659
+ options: transitionAssigneeSelectOptions,
32660
+ disabled: settingsMutationsLocked,
32661
+ onChange: (nextValue) => {
32662
+ setForm((current) => ({
32663
+ ...current,
32664
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32665
+ current.advancedSettings,
32666
+ "reviewer",
32667
+ parseAssigneeOptionValue(nextValue)
32668
+ )
32669
+ }));
32670
+ }
32671
+ }
32672
+ ),
32673
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "The assignee that reviews work when GitHub Sync moves an issue into `in_review` because linked pull requests are green and all review threads are resolved." })
32674
+ ] }),
32675
+ /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32676
+ /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-approver-assignee", children: "Approver handoff" }),
32677
+ /* @__PURE__ */ jsx2(
32678
+ SettingsAssigneePicker,
32679
+ {
32680
+ id: "advanced-approver-assignee",
32681
+ value: getSelectedAssigneeOptionValue(form.advancedSettings, "approver"),
32682
+ options: transitionAssigneeSelectOptions,
32683
+ disabled: settingsMutationsLocked,
32684
+ onChange: (nextValue) => {
32685
+ setForm((current) => ({
32686
+ ...current,
32687
+ advancedSettings: setAdvancedSettingsAssigneePrincipal(
32688
+ current.advancedSettings,
32689
+ "approver",
32690
+ parseAssigneeOptionValue(nextValue)
32691
+ )
32692
+ }));
32693
+ }
32694
+ }
32695
+ ),
32696
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "The assignee that approves work for the same `in_review` handoff when Paperclip's execution policy says the current stage is approval instead of review." })
32412
32697
  ] })
32413
32698
  ] }),
32699
+ /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: `Choose "Automatic routing" to let GitHub Sync follow the issue's Paperclip execution policy and built-in fallback behavior. Pick a specific assignee only when you want a company-wide fallback for missing reviewer, approver, or return-assignee data. When Paperclip board access is connected for this company, each assignee picker also includes "Me" so the connected board user can take the handoff instead of an agent.` }),
32414
32700
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
32415
32701
  /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-ignored-authors", children: "Ignore issues from GitHub usernames" }),
32416
32702
  /* @__PURE__ */ jsx2(