@sideboard-ai/core 0.1.203 → 0.1.204

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/{abletime-JFGZBDHH.js → abletime-S3UHQZVW.js} +3 -3
  2. package/dist/{abletime-SISJEPJ3.js → abletime-TONBQP4O.js} +3 -3
  3. package/dist/{abletime-oauth-7TD5Z2QV.js → abletime-oauth-AVCEO2DY.js} +54 -5
  4. package/dist/{abletime-oauth-OSQYFB3E.js → abletime-oauth-SD4SZY5U.js} +14 -2
  5. package/dist/{abletime-rest-QPJTDLKX.js → abletime-rest-6ODSXV7G.js} +2 -2
  6. package/dist/{abletime-rest-ZJGWYALM.js → abletime-rest-J2OBB4VA.js} +2 -2
  7. package/dist/agents/cursor-runner.js +1 -1
  8. package/dist/{agents-ZMWL43SP.js → agents-67K4BQFJ.js} +3 -3
  9. package/dist/{agents-3755WYQM.js → agents-BVOJNHV6.js} +4 -4
  10. package/dist/{chunk-6VEIKW5C.js → chunk-3K257PCS.js} +6 -3
  11. package/dist/{chunk-LCU6GBZP.js → chunk-5SMS6QTO.js} +1 -1
  12. package/dist/{chunk-MHPINQKY.js → chunk-6Q7DFVDQ.js} +54 -5
  13. package/dist/{chunk-3PYH2RLA.js → chunk-CYLDJWRK.js} +6 -3
  14. package/dist/{chunk-FZ6IBUKY.js → chunk-FO2NTJ7L.js} +3 -3
  15. package/dist/{chunk-2DVCCLII.js → chunk-FO45DMJ5.js} +60 -60
  16. package/dist/{chunk-CQ5R3VPP.js → chunk-KZ7PI7FE.js} +18 -0
  17. package/dist/{chunk-FZYYEMET.js → chunk-QAVWH2RQ.js} +3 -3
  18. package/dist/{chunk-QSEF2YVQ.js → chunk-QUW5D62C.js} +33 -19
  19. package/dist/{chunk-PJHFNFXA.js → chunk-SGUVAK66.js} +27 -8
  20. package/dist/{chunk-TZNJKZ2A.js → chunk-ULZLWQBS.js} +58 -58
  21. package/dist/{chunk-TNDUTGDV.js → chunk-VIPA75ZA.js} +1 -1
  22. package/dist/{chunk-BFEBFGCS.js → chunk-W6VM36YO.js} +30 -8
  23. package/dist/{chunk-X2TFUFIS.js → chunk-WF33LOP4.js} +43 -5
  24. package/dist/{chunk-CMGRV36E.js → chunk-WVATAIF4.js} +33 -19
  25. package/dist/{chunk-5IXJTUST.js → chunk-YOSTWSNC.js} +26 -6
  26. package/dist/{coordinator-prompt-U4PEZDAW.js → coordinator-prompt-NPT555BO.js} +2 -2
  27. package/dist/{coordinator-prompt-2B2JA2IE.js → coordinator-prompt-WEPXXLLX.js} +2 -2
  28. package/dist/{global-workspace-JPJ3FKKY.js → global-workspace-2I2T7FL5.js} +2 -2
  29. package/dist/{global-workspace-PU7ZS3LS.js → global-workspace-NSILADOM.js} +2 -2
  30. package/dist/index.cjs +237 -83
  31. package/dist/index.d.cts +33 -11
  32. package/dist/index.d.ts +33 -11
  33. package/dist/index.js +39 -9
  34. package/dist/mcp/run-stdio.cjs +192 -78
  35. package/dist/mcp/run-stdio.js +7 -7
  36. package/dist/{orchestrator-3A6KUTNI.js → orchestrator-CMAVNELS.js} +5 -5
  37. package/dist/{orchestrator-Q33M24ZJ.js → orchestrator-I7GFXLHF.js} +4 -4
  38. package/dist/{workspaces-IMG7ZRUQ.js → workspaces-BOC6M7BW.js} +2 -2
  39. package/dist/{workspaces-U6P6DB44.js → workspaces-RFGZBUAR.js} +2 -2
  40. package/dist/{worktree-JSCDJPBU.js → worktree-G3ZLM4DC.js} +3 -1
  41. package/dist/{worktree-FL3UIUKQ.js → worktree-WSUQMMK4.js} +3 -1
  42. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3298,6 +3298,9 @@ function humanizeAgentFailDetail(detail) {
3298
3298
  if (/\[resource_exhausted\]|resource_exhausted|findfileswithripgrep/.test(lower)) {
3299
3299
  return "Cursor local file search failed (ripgrep / resource_exhausted). Wait a minute and retry; if it keeps happening, check Cursor usage.";
3300
3300
  }
3301
+ if (looksLikeCodexMcpError(raw)) {
3302
+ return `${raw} \u2014 skip that vendor MCP and continue (PostHog: Settings \u2192 Connectors + HTTP API).`;
3303
+ }
3301
3304
  if (/invalid user api key|invalid api key|not logged in|not authenticated|unauthorized|authentication|please run.*login|codex login|claude auth|cursor api/.test(
3302
3305
  lower
3303
3306
  )) {
@@ -3355,9 +3358,24 @@ function turnFailChatText(opts) {
3355
3358
 
3356
3359
  ${fail6}`;
3357
3360
  }
3361
+ function looksLikeCodexMcpError(text7) {
3362
+ const t = text7.trim().toLowerCase();
3363
+ if (!t) return false;
3364
+ if (/authrequired|authorization required/.test(t) && /mcp|posthog|rmcp|connector|plugin/.test(t)) {
3365
+ return true;
3366
+ }
3367
+ if (/\bposthog\b/.test(t) && /mcp|oauth|auth|unauthorized|401|plugin|app/.test(t)) {
3368
+ return true;
3369
+ }
3370
+ if (/\brmcp\b/.test(t) && /fatal|quit|transport|error|died|kill|auth/.test(t)) {
3371
+ return true;
3372
+ }
3373
+ return /mcp[_ -]?(server|tool|error|transport|client)/.test(t) && /fail|error|timeout|disconnect|auth|unauthorized/.test(t);
3374
+ }
3358
3375
  function shouldFeedErrorBackToAgent(opts) {
3359
3376
  const detail = opts.detail.trim();
3360
3377
  const chat = (opts.assistantText ?? "").trim();
3378
+ if (looksLikeCodexMcpError(detail)) return true;
3361
3379
  if (looksLikeAgentFailureMessage(detail) || looksLikeAgentFailureMessage(chat)) {
3362
3380
  return false;
3363
3381
  }
@@ -7728,6 +7746,7 @@ __export(worktree_exports, {
7728
7746
  canonicalizeRepoPath: () => canonicalizeRepoPath,
7729
7747
  collectTakenTeamSlugs: () => collectTakenTeamSlugs,
7730
7748
  commitAll: () => commitAll,
7749
+ connectedPrSelectors: () => connectedPrSelectors,
7731
7750
  countUnpushedVsOrigin: () => countUnpushedVsOrigin,
7732
7751
  createExistingBranchWorktree: () => createExistingBranchWorktree,
7733
7752
  createOrUpdatePr: () => createOrUpdatePr,
@@ -8061,26 +8080,21 @@ function resolvePrSelectors(thread) {
8061
8080
  function resolvePrSelector(thread) {
8062
8081
  return resolvePrSelectors(thread)[0] ?? null;
8063
8082
  }
8083
+ function connectedPrSelectors(thread, currentHeadPrUrl) {
8084
+ const out = [];
8085
+ const push = (value) => {
8086
+ const v = value?.trim();
8087
+ if (!v || out.includes(v)) return;
8088
+ out.push(v);
8089
+ };
8090
+ push(currentHeadPrUrl);
8091
+ for (const selector of resolvePrSelectors(thread)) push(selector);
8092
+ return out;
8093
+ }
8064
8094
  async function getPrForHeadBranch(repoPath, branch) {
8065
8095
  const head = normalizeGitBranchName(branch);
8066
8096
  if (!head || isDefaultishSourceRef(head)) return null;
8067
8097
  const slug = await resolveGithubRepoSlug(repoPath);
8068
- const viewArgs = [
8069
- "pr",
8070
- "view",
8071
- head,
8072
- "--json",
8073
- "number,title,headRefName,url,isCrossRepository,author"
8074
- ];
8075
- if (slug) viewArgs.push("--repo", slug);
8076
- const viewed = await gh(viewArgs, repoPath, { reject: false });
8077
- if (viewed.exitCode === 0 && viewed.stdout.trim()) {
8078
- try {
8079
- return JSON.parse(viewed.stdout);
8080
- } catch {
8081
- return null;
8082
- }
8083
- }
8084
8098
  const listHead = slug ? ghHeadRef(slug, head) : head;
8085
8099
  const listArgs = [
8086
8100
  "pr",
@@ -8096,10 +8110,28 @@ async function getPrForHeadBranch(repoPath, branch) {
8096
8110
  ];
8097
8111
  if (slug) listArgs.push("--repo", slug);
8098
8112
  const listed = await gh(listArgs, repoPath, { reject: false });
8099
- if (listed.exitCode !== 0 || !listed.stdout.trim()) return null;
8113
+ if (listed.exitCode === 0 && listed.stdout.trim()) {
8114
+ try {
8115
+ const rows = JSON.parse(listed.stdout);
8116
+ if (rows[0]) return rows[0];
8117
+ } catch {
8118
+ }
8119
+ }
8120
+ const viewArgs = [
8121
+ "pr",
8122
+ "view",
8123
+ head,
8124
+ "--json",
8125
+ "number,title,headRefName,url,isCrossRepository,author,state"
8126
+ ];
8127
+ if (slug) viewArgs.push("--repo", slug);
8128
+ const viewed = await gh(viewArgs, repoPath, { reject: false });
8129
+ if (viewed.exitCode !== 0 || !viewed.stdout.trim()) return null;
8100
8130
  try {
8101
- const rows = JSON.parse(listed.stdout);
8102
- return rows[0] ?? null;
8131
+ const info = JSON.parse(viewed.stdout);
8132
+ const state = (info.state ?? "OPEN").trim().toUpperCase();
8133
+ if (state !== "OPEN") return null;
8134
+ return info;
8103
8135
  } catch {
8104
8136
  return null;
8105
8137
  }
@@ -12869,7 +12901,18 @@ function toCodexDisableUserMcpArgs(names) {
12869
12901
  return args;
12870
12902
  }
12871
12903
  function toCodexUnattendedAppsArgs() {
12872
- return ["-c", "features.apps=false", "-c", "apps._default.enabled=false"];
12904
+ return [
12905
+ "-c",
12906
+ "features.apps=false",
12907
+ "-c",
12908
+ "apps._default.enabled=false",
12909
+ "-c",
12910
+ "apps.posthog.enabled=false",
12911
+ "-c",
12912
+ "features.plugins=false",
12913
+ "-c",
12914
+ "mcp_servers.posthog.enabled=false"
12915
+ ];
12873
12916
  }
12874
12917
  var import_node_fs32, import_node_os11, import_node_path33, import_smol_toml2;
12875
12918
  var init_orch_mcp_isolation = __esm({
@@ -14620,11 +14663,14 @@ function staleOk(now) {
14620
14663
  if (now - lastGood.at > CLAUDE_USAGE_STALE_OK_MS) return null;
14621
14664
  return lastGood.value;
14622
14665
  }
14666
+ function reuseLast() {
14667
+ return lastGood?.value ?? null;
14668
+ }
14623
14669
  async function getClaudePlanUsage(opts = {}) {
14624
14670
  const now = opts.now ?? Date.now();
14671
+ if (now < errorUntil) return reuseLast() ?? staleOk(now);
14625
14672
  if (!opts.force) {
14626
14673
  if (cache && now - cache.at < CLAUDE_USAGE_TTL_MS) return cache.value;
14627
- if (now < errorUntil) return staleOk(now);
14628
14674
  }
14629
14675
  if (inflight) return inflight;
14630
14676
  inflight = (async () => {
@@ -14655,6 +14701,12 @@ async function getClaudePlanUsage(opts = {}) {
14655
14701
  cache = { at: now, value: null };
14656
14702
  return null;
14657
14703
  }
14704
+ if (res.status === 429) {
14705
+ errorUntil = now + CLAUDE_USAGE_ERROR_TTL_MS;
14706
+ const last = reuseLast();
14707
+ if (last) cache = { at: now, value: last };
14708
+ return last;
14709
+ }
14658
14710
  if (!res.ok) {
14659
14711
  errorUntil = now + CLAUDE_USAGE_ERROR_TTL_MS;
14660
14712
  return staleOk(now);
@@ -15415,6 +15467,33 @@ var init_agent_git_actions = __esm({
15415
15467
  function normalizePrState(state) {
15416
15468
  return (state ?? "").trim().toUpperCase();
15417
15469
  }
15470
+ function sameLoginList(a, b) {
15471
+ const left = [...a ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
15472
+ const right = [...b ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
15473
+ return left.length === right.length && left.every((login, i) => login === right[i]);
15474
+ }
15475
+ function threadPrMetaPatch(thread, meta) {
15476
+ const prevState = normalizePrState(thread.prState);
15477
+ const nextState = normalizePrState(meta.state);
15478
+ const patch = {};
15479
+ if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
15480
+ if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
15481
+ if (nextState && nextState !== prevState) patch.prState = nextState;
15482
+ const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
15483
+ if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
15484
+ const nextAuthor = meta.authorLogin?.trim() || "";
15485
+ if (nextAuthor && nextAuthor !== (thread.prAuthorLogin ?? "").trim()) {
15486
+ patch.prAuthorLogin = nextAuthor;
15487
+ }
15488
+ const nextReviewers = meta.reviewerLogins ?? [];
15489
+ if (!sameLoginList(nextReviewers, thread.prReviewerLogins)) {
15490
+ patch.prReviewerLogins = nextReviewers;
15491
+ }
15492
+ if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
15493
+ patch.skipAutoArchiveOnMerge = false;
15494
+ }
15495
+ return patch;
15496
+ }
15418
15497
  function shouldAutoArchiveOnPrMerge(opts) {
15419
15498
  if (!opts.autoArchiveEnabled) return false;
15420
15499
  if (opts.isGlobal) return false;
@@ -17497,21 +17576,67 @@ var init_detect = __esm({
17497
17576
  // src/integrations/abletime-oauth.ts
17498
17577
  var abletime_oauth_exports = {};
17499
17578
  __export(abletime_oauth_exports, {
17579
+ ABLETIME_OAUTH_CALLBACK_PATH: () => ABLETIME_OAUTH_CALLBACK_PATH,
17500
17580
  ABLETIME_OAUTH_CANCELLED: () => ABLETIME_OAUTH_CANCELLED,
17581
+ ABLETIME_OAUTH_LOCAL_CALLBACK: () => ABLETIME_OAUTH_LOCAL_CALLBACK,
17501
17582
  ABLETIME_OAUTH_PORT: () => ABLETIME_OAUTH_PORT,
17502
17583
  ABLETIME_OAUTH_REDIRECT: () => ABLETIME_OAUTH_REDIRECT,
17503
17584
  ABLETIME_OAUTH_RESOURCE: () => ABLETIME_OAUTH_RESOURCE,
17504
17585
  AbleTimeOAuthCancelledError: () => AbleTimeOAuthCancelledError,
17505
17586
  BAKED_ABLETIME_OAUTH_CLIENT_ID: () => BAKED_ABLETIME_OAUTH_CLIENT_ID,
17506
17587
  ableTimeOAuthAuthorizeUrl: () => ableTimeOAuthAuthorizeUrl,
17588
+ ableTimeOAuthBouncePage: () => ableTimeOAuthBouncePage,
17507
17589
  ableTimeOAuthClientId: () => ableTimeOAuthClientId,
17590
+ ableTimeOAuthLocalBounceUrl: () => ableTimeOAuthLocalBounceUrl,
17591
+ ableTimeOAuthRedirectUri: () => ableTimeOAuthRedirectUri,
17508
17592
  ableTimeOAuthResource: () => ableTimeOAuthResource,
17509
17593
  createAbleTimePkce: () => createAbleTimePkce,
17510
17594
  ensureAbleTimeAccessToken: () => ensureAbleTimeAccessToken,
17511
17595
  hasBakedAbleTimeOAuth: () => hasBakedAbleTimeOAuth,
17512
17596
  isAbleTimeOAuthCancelled: () => isAbleTimeOAuthCancelled,
17597
+ parseAbleTimeOAuthCallbackUrl: () => parseAbleTimeOAuthCallbackUrl,
17513
17598
  startAbleTimeOAuth: () => startAbleTimeOAuth
17514
17599
  });
17600
+ function ableTimeOAuthRedirectUri() {
17601
+ return process.env.SIDEBOARD_ABLETIME_OAUTH_REDIRECT?.trim() || ABLETIME_OAUTH_REDIRECT;
17602
+ }
17603
+ function parseAbleTimeOAuthCallbackUrl(reqUrl) {
17604
+ let url;
17605
+ try {
17606
+ url = new URL(reqUrl, ABLETIME_OAUTH_REDIRECT);
17607
+ } catch {
17608
+ return null;
17609
+ }
17610
+ if (url.pathname !== ABLETIME_OAUTH_CALLBACK_PATH) return null;
17611
+ return url;
17612
+ }
17613
+ function ableTimeOAuthLocalBounceUrl(reqUrl) {
17614
+ const url = parseAbleTimeOAuthCallbackUrl(reqUrl);
17615
+ if (!url) return null;
17616
+ const dest = new URL(ABLETIME_OAUTH_LOCAL_CALLBACK);
17617
+ for (const key of BOUNCE_QUERY_KEYS) {
17618
+ const value = url.searchParams.get(key);
17619
+ if (value) dest.searchParams.set(key, value);
17620
+ }
17621
+ return dest.toString();
17622
+ }
17623
+ function escapeHtml(value) {
17624
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
17625
+ }
17626
+ function ableTimeOAuthBouncePage(localUrl) {
17627
+ const safe = escapeHtml(localUrl);
17628
+ return `<!doctype html><html><head><meta charset="utf-8">
17629
+ <meta http-equiv="refresh" content="0;url=${safe}">
17630
+ <title>Returning to Sideboard</title>
17631
+ <script>location.replace(${JSON.stringify(localUrl)})</script>
17632
+ <style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
17633
+ h1{font-size:1.25rem}p{line-height:1.5;color:#444}a{color:#0f7ed4}</style>
17634
+ </head><body>
17635
+ <h1>Returning to Sideboard</h1>
17636
+ <p>AbleTime authorized. Opening the app\u2026</p>
17637
+ <p><a href="${safe}">Open Sideboard</a> if it does not appear.</p>
17638
+ </body></html>`;
17639
+ }
17515
17640
  function hasBakedAbleTimeOAuth() {
17516
17641
  return Boolean(BAKED_ABLETIME_OAUTH_CLIENT_ID.trim());
17517
17642
  }
@@ -17531,7 +17656,7 @@ function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
17531
17656
  const params = new URLSearchParams({
17532
17657
  response_type: "code",
17533
17658
  client_id: clientId,
17534
- redirect_uri: ABLETIME_OAUTH_REDIRECT,
17659
+ redirect_uri: ableTimeOAuthRedirectUri(),
17535
17660
  code_challenge: codeChallenge,
17536
17661
  code_challenge_method: "S256",
17537
17662
  state,
@@ -17703,7 +17828,7 @@ async function startAbleTimeOAuth(opts) {
17703
17828
  const body = new URLSearchParams({
17704
17829
  grant_type: "authorization_code",
17705
17830
  code,
17706
- redirect_uri: ABLETIME_OAUTH_REDIRECT,
17831
+ redirect_uri: ableTimeOAuthRedirectUri(),
17707
17832
  client_id: clientId,
17708
17833
  code_verifier: pkce.verifier,
17709
17834
  resource: ableTimeOAuthResource(host)
@@ -17725,7 +17850,7 @@ async function startAbleTimeOAuth(opts) {
17725
17850
  return loadAppSettings();
17726
17851
  }
17727
17852
  }
17728
- var import_node_crypto10, import_node_http, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_REDIRECT, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
17853
+ var import_node_crypto10, import_node_http, ABLETIME_OAUTH_CALLBACK_PATH, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_LOCAL_CALLBACK, ABLETIME_OAUTH_REDIRECT, BOUNCE_QUERY_KEYS, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
17729
17854
  var init_abletime_oauth = __esm({
17730
17855
  "src/integrations/abletime-oauth.ts"() {
17731
17856
  "use strict";
@@ -17734,8 +17859,11 @@ var init_abletime_oauth = __esm({
17734
17859
  init_fetch();
17735
17860
  init_app_settings();
17736
17861
  init_abletime_mcp();
17862
+ ABLETIME_OAUTH_CALLBACK_PATH = "/oauth/abletime/callback";
17737
17863
  ABLETIME_OAUTH_PORT = 19849;
17738
- ABLETIME_OAUTH_REDIRECT = `http://127.0.0.1:${ABLETIME_OAUTH_PORT}/callback`;
17864
+ ABLETIME_OAUTH_LOCAL_CALLBACK = `http://127.0.0.1:${ABLETIME_OAUTH_PORT}/callback`;
17865
+ ABLETIME_OAUTH_REDIRECT = `https://www.sideboard.cloud${ABLETIME_OAUTH_CALLBACK_PATH}`;
17866
+ BOUNCE_QUERY_KEYS = ["code", "state", "error", "error_description"];
17739
17867
  BAKED_ABLETIME_OAUTH_CLIENT_ID = "https://www.sideboard.cloud/oauth/abletime-client.json";
17740
17868
  ABLETIME_OAUTH_RESOURCE = `${DEFAULT_ABLETIME_HOST}/api/public/v2/mcp`;
17741
17869
  REFRESH_SKEW_MS2 = 5 * 6e4;
@@ -17989,8 +18117,11 @@ function firstStringArg(args, keys) {
17989
18117
  }
17990
18118
  async function callAbleTimeRestTool(name, args = {}, opts) {
17991
18119
  const token = normalizeAbleTimeCredential(opts?.token ?? getAbleTimeAccessToken() ?? "");
17992
- if (ableTimeCredentialKind(token) !== "org") {
17993
- throw new Error("AbleTime REST transport is for organization API keys (atk_\u2026)");
18120
+ if (!token) {
18121
+ throw new Error("AbleTime is not connected \u2014 paste an API key or connect via browser");
18122
+ }
18123
+ if (ableTimeCredentialKind(token) === "pat") {
18124
+ throw new Error("AbleTime REST transport is for organization keys and OAuth tokens, not apt_\u2026");
17994
18125
  }
17995
18126
  const requestOpts = { ...opts, token };
17996
18127
  switch (name) {
@@ -18150,11 +18281,25 @@ function normalizeAbleTimeCredential(raw) {
18150
18281
  return raw.trim().replace(/^bearer\s+/i, "").trim();
18151
18282
  }
18152
18283
  function ableTimeCredentialKind(raw) {
18153
- const token = normalizeAbleTimeCredential(raw).toLowerCase();
18154
- if (token.startsWith(ABLETIME_PAT_PREFIX)) return "pat";
18155
- if (token.startsWith(ABLETIME_ORG_KEY_PREFIX)) return "org";
18284
+ const token = normalizeAbleTimeCredential(raw);
18285
+ const lower = token.toLowerCase();
18286
+ if (lower.startsWith(ABLETIME_PAT_PREFIX)) return "pat";
18287
+ if (lower.startsWith(ABLETIME_ORG_KEY_PREFIX)) return "org";
18288
+ if (lower.startsWith(ABLETIME_OAUTH_TOKEN_PREFIX) || token.startsWith("eyJ")) return "oauth";
18156
18289
  return "unknown";
18157
18290
  }
18291
+ function ableTimeUsesRest(raw) {
18292
+ const token = normalizeAbleTimeCredential(raw);
18293
+ const kind = ableTimeCredentialKind(token);
18294
+ if (kind === "org") return true;
18295
+ if (kind === "oauth") return token.startsWith("eyJ");
18296
+ return kind === "unknown";
18297
+ }
18298
+ function isAbleTimeRejectedCredential(message) {
18299
+ return /INTEGRATION_KEY_INVALID|INTEGRATION_KEY_MISSING|INTEGRATION_PAT_REQUIRED|\b401\b/i.test(
18300
+ message
18301
+ );
18302
+ }
18158
18303
  function assertAbleTimeMcpCredential(raw) {
18159
18304
  const token = normalizeAbleTimeCredential(raw);
18160
18305
  if (!token) {
@@ -18316,7 +18461,7 @@ async function callAbleTimeTool(name, args = {}, opts) {
18316
18461
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
18317
18462
  }
18318
18463
  const rawToken = opts?.token ?? await Promise.resolve().then(() => (init_abletime_oauth(), abletime_oauth_exports)).then((m) => m.ensureAbleTimeAccessToken());
18319
- if (rawToken && ableTimeCredentialKind(rawToken) === "org") {
18464
+ if (rawToken && ableTimeUsesRest(rawToken)) {
18320
18465
  const { callAbleTimeRestTool: callAbleTimeRestTool2 } = await Promise.resolve().then(() => (init_abletime_rest(), abletime_rest_exports));
18321
18466
  return callAbleTimeRestTool2(name, args, { ...opts, token: rawToken });
18322
18467
  }
@@ -18332,10 +18477,14 @@ async function callAbleTimeTool(name, args = {}, opts) {
18332
18477
  await initializeIfNeeded(requestOpts);
18333
18478
  return await call();
18334
18479
  }
18480
+ if (rawToken && ableTimeUsesRest(rawToken) && isAbleTimeRejectedCredential(message)) {
18481
+ const { callAbleTimeRestTool: callAbleTimeRestTool2 } = await Promise.resolve().then(() => (init_abletime_rest(), abletime_rest_exports));
18482
+ return callAbleTimeRestTool2(name, args, { ...opts, token: rawToken });
18483
+ }
18335
18484
  throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
18336
18485
  }
18337
18486
  }
18338
- var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, ABLETIME_MCP_PM_PATH, MCP_PROTOCOL_VERSION, ABLETIME_PM_TOOL_NAMES, ABLETIME_PAT_PREFIX, ABLETIME_ORG_KEY_PREFIX, ORG_KEY_HINT, REJECTED_CREDENTIAL_HINT, REWRITE_APPLIED;
18487
+ var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, ABLETIME_MCP_PM_PATH, MCP_PROTOCOL_VERSION, ABLETIME_PM_TOOL_NAMES, ABLETIME_PAT_PREFIX, ABLETIME_ORG_KEY_PREFIX, ABLETIME_OAUTH_TOKEN_PREFIX, ORG_KEY_HINT, REJECTED_CREDENTIAL_HINT, REWRITE_APPLIED;
18339
18488
  var init_abletime_mcp = __esm({
18340
18489
  "src/integrations/abletime-mcp.ts"() {
18341
18490
  "use strict";
@@ -18359,8 +18508,9 @@ var init_abletime_mcp = __esm({
18359
18508
  ];
18360
18509
  ABLETIME_PAT_PREFIX = "apt_";
18361
18510
  ABLETIME_ORG_KEY_PREFIX = "atk_";
18511
+ ABLETIME_OAUTH_TOKEN_PREFIX = "acn_";
18362
18512
  ORG_KEY_HINT = "AbleTime MCP needs a personal access token (apt_\u2026) from Edit Profile \u2192 API Access, not an organization API key (atk_\u2026) from Settings \u2192 Integrations \u2192 API Keys.";
18363
- REJECTED_CREDENTIAL_HINT = "AbleTime rejected this credential (unknown, revoked, or expired). Use a personal access token (apt_\u2026) from Edit Profile \u2192 API Access \u2014 organization API keys (atk_\u2026) cannot call MCP. Tokens are shown once; rotating replaces the previous one.";
18513
+ REJECTED_CREDENTIAL_HINT = "AbleTime rejected this credential (unknown, revoked, or expired). Reconnect via browser, or paste a personal access token (apt_\u2026) from Edit Profile \u2192 API Access. Organization API keys (atk_\u2026) use REST, not MCP.";
18364
18514
  REWRITE_APPLIED = /— Enable Agent access \(MCP\)|— AbleTime MCP needs a personal access token|— AbleTime plan does not include API access|— AbleTime rejected this credential/;
18365
18515
  }
18366
18516
  });
@@ -18542,7 +18692,7 @@ function isOpenTask(task) {
18542
18692
  async function getAbleTimeOrientation(opts) {
18543
18693
  const raw = await callAbleTimeTool("orientation", {}, opts);
18544
18694
  const record = asRecord7(raw);
18545
- const viewerRec = firstRecord(record, ["viewer", "user", "actor", "me", "person"]) ?? record;
18695
+ const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
18546
18696
  const viewer = {
18547
18697
  id: firstString(viewerRec, ["id", "user_id"]) || void 0,
18548
18698
  name: firstString(viewerRec, ["name", "display_name", "full_name"]) || firstString(record, ["name"]) || void 0
@@ -22237,11 +22387,6 @@ function isPidAlive(pid) {
22237
22387
  return false;
22238
22388
  }
22239
22389
  }
22240
- function sameLoginList(a, b) {
22241
- const left = [...a ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
22242
- const right = [...b ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
22243
- return left.length === right.length && left.every((login, i) => login === right[i]);
22244
- }
22245
22390
  async function waitForPidExit(pid, timeoutMs) {
22246
22391
  const start = Date.now();
22247
22392
  while (isPidAlive(pid)) {
@@ -24080,12 +24225,18 @@ var init_orchestrator = __esm({
24080
24225
  /** Resolve PR selectors and optionally persist `prUrl` when found. */
24081
24226
  async withPrSelector(threadRef) {
24082
24227
  const thread = this.requireThread(threadRef);
24083
- const selectors = resolvePrSelectors(thread);
24084
24228
  const cwd = thread.worktreePath;
24085
24229
  if (!cwd?.trim()) {
24086
24230
  throw new Error(`Thread ${threadRef} has no worktreePath`);
24087
24231
  }
24088
- return { thread, selectors, cwd };
24232
+ let headUrl = null;
24233
+ try {
24234
+ const head = await getPrForHeadBranch(cwd, thread.branchName);
24235
+ headUrl = head?.url?.trim() || null;
24236
+ } catch {
24237
+ headUrl = null;
24238
+ }
24239
+ return { thread, selectors: connectedPrSelectors(thread, headUrl), cwd };
24089
24240
  }
24090
24241
  async getPrChecks(threadRef) {
24091
24242
  const { selectors, cwd } = await this.withPrSelector(threadRef);
@@ -24113,29 +24264,16 @@ var init_orchestrator = __esm({
24113
24264
  async persistPrMetaAndMaybeArchive(thread, meta) {
24114
24265
  const prevState = normalizePrState(thread.prState);
24115
24266
  const nextState = normalizePrState(meta.state);
24116
- const patch = {};
24117
- if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
24118
- if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
24119
- if (nextState && nextState !== prevState) patch.prState = nextState;
24120
- const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
24121
- if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
24122
- const nextAuthor = meta.authorLogin?.trim() || "";
24123
- if (nextAuthor && nextAuthor !== (thread.prAuthorLogin ?? "").trim()) {
24124
- patch.prAuthorLogin = nextAuthor;
24125
- }
24126
- const nextReviewers = meta.reviewerLogins ?? [];
24127
- if (!sameLoginList(nextReviewers, thread.prReviewerLogins)) {
24128
- patch.prReviewerLogins = nextReviewers;
24267
+ const siblings = threadsSharingWorktree(thread.worktreePath);
24268
+ const targets = siblings.length > 0 ? siblings : [thread];
24269
+ for (const t of targets) {
24270
+ const patch = threadPrMetaPatch(t, meta);
24271
+ if (Object.keys(patch).length === 0) continue;
24272
+ updateThread(t.id, patch);
24129
24273
  }
24130
- if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
24131
- patch.skipAutoArchiveOnMerge = false;
24132
- }
24133
- if (Object.keys(patch).length > 0) {
24134
- updateThread(thread.id, patch);
24135
- const latest2 = this.requireThread(thread.id);
24136
- if (!latest2.userSetTitle && meta.title && latest2.title !== meta.title) {
24137
- updateThread(thread.id, { title: meta.title });
24138
- }
24274
+ const titled = this.requireThread(thread.id);
24275
+ if (!titled.userSetTitle && meta.title && titled.title !== meta.title) {
24276
+ updateThread(thread.id, { title: meta.title });
24139
24277
  }
24140
24278
  const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
24141
24279
  const latest = this.requireThread(thread.id);
@@ -24149,22 +24287,7 @@ var init_orchestrator = __esm({
24149
24287
  })) {
24150
24288
  return;
24151
24289
  }
24152
- const siblings = threadsSharingWorktree(latest.worktreePath);
24153
- for (const t of siblings) {
24154
- const sibPatch = { prState: "MERGED", prIsDraft: false };
24155
- if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
24156
- if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
24157
- const sibAuthor = meta.authorLogin?.trim() || "";
24158
- if (sibAuthor && sibAuthor !== (t.prAuthorLogin ?? "").trim()) {
24159
- sibPatch.prAuthorLogin = sibAuthor;
24160
- }
24161
- const sibReviewers = meta.reviewerLogins ?? [];
24162
- if (!sameLoginList(sibReviewers, t.prReviewerLogins)) {
24163
- sibPatch.prReviewerLogins = sibReviewers;
24164
- }
24165
- if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
24166
- }
24167
- for (const t of siblings) {
24290
+ for (const t of threadsSharingWorktree(latest.worktreePath)) {
24168
24291
  if (this.requireThread(t.id).status === "archived") continue;
24169
24292
  await this.archive(t.id);
24170
24293
  }
@@ -24682,10 +24805,13 @@ var index_exports = {};
24682
24805
  __export(index_exports, {
24683
24806
  ABLETIME_MCP_PATH: () => ABLETIME_MCP_PATH,
24684
24807
  ABLETIME_MCP_PM_PATH: () => ABLETIME_MCP_PM_PATH,
24808
+ ABLETIME_OAUTH_CALLBACK_PATH: () => ABLETIME_OAUTH_CALLBACK_PATH,
24685
24809
  ABLETIME_OAUTH_CANCELLED: () => ABLETIME_OAUTH_CANCELLED,
24810
+ ABLETIME_OAUTH_LOCAL_CALLBACK: () => ABLETIME_OAUTH_LOCAL_CALLBACK,
24686
24811
  ABLETIME_OAUTH_PORT: () => ABLETIME_OAUTH_PORT,
24687
24812
  ABLETIME_OAUTH_REDIRECT: () => ABLETIME_OAUTH_REDIRECT,
24688
24813
  ABLETIME_OAUTH_RESOURCE: () => ABLETIME_OAUTH_RESOURCE,
24814
+ ABLETIME_OAUTH_TOKEN_PREFIX: () => ABLETIME_OAUTH_TOKEN_PREFIX,
24689
24815
  ABLETIME_ORG_KEY_PREFIX: () => ABLETIME_ORG_KEY_PREFIX,
24690
24816
  ABLETIME_PAT_PREFIX: () => ABLETIME_PAT_PREFIX,
24691
24817
  ABLETIME_PM_TOOL_NAMES: () => ABLETIME_PM_TOOL_NAMES,
@@ -24785,8 +24911,12 @@ __export(index_exports, {
24785
24911
  ableTimeCredentialKind: () => ableTimeCredentialKind,
24786
24912
  ableTimeDependencyTarget: () => ableTimeDependencyTarget,
24787
24913
  ableTimeOAuthAuthorizeUrl: () => ableTimeOAuthAuthorizeUrl,
24914
+ ableTimeOAuthBouncePage: () => ableTimeOAuthBouncePage,
24788
24915
  ableTimeOAuthClientId: () => ableTimeOAuthClientId,
24916
+ ableTimeOAuthLocalBounceUrl: () => ableTimeOAuthLocalBounceUrl,
24917
+ ableTimeOAuthRedirectUri: () => ableTimeOAuthRedirectUri,
24789
24918
  ableTimeOAuthResource: () => ableTimeOAuthResource,
24919
+ ableTimeUsesRest: () => ableTimeUsesRest,
24790
24920
  abletimeMcpRequest: () => abletimeMcpRequest,
24791
24921
  abletimeMcpUrl: () => abletimeMcpUrl,
24792
24922
  abletimeToolUsesPm: () => abletimeToolUsesPm,
@@ -24891,6 +25021,7 @@ __export(index_exports, {
24891
25021
  connectOptionalService: () => connectOptionalService,
24892
25022
  connectSlackToken: () => connectSlackToken,
24893
25023
  connectedOptionalServices: () => connectedOptionalServices,
25024
+ connectedPrSelectors: () => connectedPrSelectors,
24894
25025
  contextTokens: () => contextTokens,
24895
25026
  coordinatorSystemPrompt: () => coordinatorSystemPrompt,
24896
25027
  coordinatorTurnReminder: () => coordinatorTurnReminder,
@@ -25111,6 +25242,7 @@ __export(index_exports, {
25111
25242
  invalidateThreadRecord: () => invalidateThreadRecord,
25112
25243
  isAbleTimeConnected: () => isAbleTimeConnected,
25113
25244
  isAbleTimeOAuthCancelled: () => isAbleTimeOAuthCancelled,
25245
+ isAbleTimeRejectedCredential: () => isAbleTimeRejectedCredential,
25114
25246
  isAskUserToolName: () => isAskUserToolName,
25115
25247
  isBrightsyConnected: () => isBrightsyConnected,
25116
25248
  isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
@@ -25263,6 +25395,7 @@ __export(index_exports, {
25263
25395
  originFetchBranch: () => originFetchBranch,
25264
25396
  originGhRepoEnv: () => originGhRepoEnv,
25265
25397
  packagedDetachedJobPath: () => packagedDetachedJobPath,
25398
+ parseAbleTimeOAuthCallbackUrl: () => parseAbleTimeOAuthCallbackUrl,
25266
25399
  parseClaudeUsagePayload: () => parseClaudeUsagePayload,
25267
25400
  parseCursorRunnerLine: () => parseCursorRunnerLine,
25268
25401
  parseDurationMs: () => parseDurationMs,
@@ -31496,11 +31629,11 @@ var SLACK_OAUTH_REDIRECT = `https://relay.sideboard.cloud${SLACK_OAUTH_CALLBACK_
31496
31629
  function slackOAuthRedirectUri() {
31497
31630
  return process.env.SIDEBOARD_SLACK_OAUTH_REDIRECT?.trim() || SLACK_OAUTH_REDIRECT;
31498
31631
  }
31499
- function escapeHtml(value) {
31632
+ function escapeHtml2(value) {
31500
31633
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
31501
31634
  }
31502
31635
  function slackOAuthHtmlPage(title, body) {
31503
- return `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>
31636
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml2(title)}</title>
31504
31637
  <style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
31505
31638
  h1{font-size:1.25rem}p{line-height:1.5;color:#444}</style></head><body>${body}</body></html>`;
31506
31639
  }
@@ -33184,6 +33317,7 @@ async function tryServeStatic(req, res, root) {
33184
33317
  }
33185
33318
 
33186
33319
  // src/slack/relay-server.ts
33320
+ init_abletime_oauth();
33187
33321
  function sendHtml(res, status, title, body) {
33188
33322
  res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
33189
33323
  res.end(slackOAuthHtmlPage(title, body));
@@ -33218,7 +33352,7 @@ async function startSlackRelayServer(opts) {
33218
33352
  res,
33219
33353
  400,
33220
33354
  "Slack",
33221
- `<h1>Authorization cancelled</h1><p>${escapeHtml(error)}</p>`
33355
+ `<h1>Authorization cancelled</h1><p>${escapeHtml2(error)}</p>`
33222
33356
  );
33223
33357
  return true;
33224
33358
  }
@@ -33257,7 +33391,7 @@ async function startSlackRelayServer(opts) {
33257
33391
  } catch (err) {
33258
33392
  const message = err instanceof Error ? err.message : String(err);
33259
33393
  pending.put(state, { ok: false, error: message });
33260
- sendHtml(res, 400, "Slack", `<h1>Could not connect Slack</h1><p>${escapeHtml(message)}</p>`);
33394
+ sendHtml(res, 400, "Slack", `<h1>Could not connect Slack</h1><p>${escapeHtml2(message)}</p>`);
33261
33395
  }
33262
33396
  return true;
33263
33397
  };
@@ -33282,6 +33416,16 @@ async function startSlackRelayServer(opts) {
33282
33416
  const reqUrl = req.url || "/";
33283
33417
  if (await handleCallback(reqUrl, res)) return;
33284
33418
  if (handleResult(reqUrl, res)) return;
33419
+ const ableTimeBounce = ableTimeOAuthLocalBounceUrl(reqUrl);
33420
+ if (ableTimeBounce && (req.method === "GET" || req.method === "HEAD")) {
33421
+ res.writeHead(200, {
33422
+ "Content-Type": "text/html; charset=utf-8",
33423
+ "Cache-Control": "no-store",
33424
+ Refresh: `0;url=${ableTimeBounce}`
33425
+ });
33426
+ res.end(req.method === "HEAD" ? "" : ableTimeOAuthBouncePage(ableTimeBounce));
33427
+ return;
33428
+ }
33285
33429
  if (req.url === "/health") {
33286
33430
  sendJson(res, 200, {
33287
33431
  ok: true,
@@ -33414,10 +33558,13 @@ init_outbound_watch();
33414
33558
  0 && (module.exports = {
33415
33559
  ABLETIME_MCP_PATH,
33416
33560
  ABLETIME_MCP_PM_PATH,
33561
+ ABLETIME_OAUTH_CALLBACK_PATH,
33417
33562
  ABLETIME_OAUTH_CANCELLED,
33563
+ ABLETIME_OAUTH_LOCAL_CALLBACK,
33418
33564
  ABLETIME_OAUTH_PORT,
33419
33565
  ABLETIME_OAUTH_REDIRECT,
33420
33566
  ABLETIME_OAUTH_RESOURCE,
33567
+ ABLETIME_OAUTH_TOKEN_PREFIX,
33421
33568
  ABLETIME_ORG_KEY_PREFIX,
33422
33569
  ABLETIME_PAT_PREFIX,
33423
33570
  ABLETIME_PM_TOOL_NAMES,
@@ -33517,8 +33664,12 @@ init_outbound_watch();
33517
33664
  ableTimeCredentialKind,
33518
33665
  ableTimeDependencyTarget,
33519
33666
  ableTimeOAuthAuthorizeUrl,
33667
+ ableTimeOAuthBouncePage,
33520
33668
  ableTimeOAuthClientId,
33669
+ ableTimeOAuthLocalBounceUrl,
33670
+ ableTimeOAuthRedirectUri,
33521
33671
  ableTimeOAuthResource,
33672
+ ableTimeUsesRest,
33522
33673
  abletimeMcpRequest,
33523
33674
  abletimeMcpUrl,
33524
33675
  abletimeToolUsesPm,
@@ -33623,6 +33774,7 @@ init_outbound_watch();
33623
33774
  connectOptionalService,
33624
33775
  connectSlackToken,
33625
33776
  connectedOptionalServices,
33777
+ connectedPrSelectors,
33626
33778
  contextTokens,
33627
33779
  coordinatorSystemPrompt,
33628
33780
  coordinatorTurnReminder,
@@ -33843,6 +33995,7 @@ init_outbound_watch();
33843
33995
  invalidateThreadRecord,
33844
33996
  isAbleTimeConnected,
33845
33997
  isAbleTimeOAuthCancelled,
33998
+ isAbleTimeRejectedCredential,
33846
33999
  isAskUserToolName,
33847
34000
  isBrightsyConnected,
33848
34001
  isBrightsyNdjsonLine,
@@ -33995,6 +34148,7 @@ init_outbound_watch();
33995
34148
  originFetchBranch,
33996
34149
  originGhRepoEnv,
33997
34150
  packagedDetachedJobPath,
34151
+ parseAbleTimeOAuthCallbackUrl,
33998
34152
  parseClaudeUsagePayload,
33999
34153
  parseCursorRunnerLine,
34000
34154
  parseDurationMs,