@buildautomaton/cli 0.1.47 → 0.1.49

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/cli.js CHANGED
@@ -25174,7 +25174,7 @@ var {
25174
25174
  } = import_index.default;
25175
25175
 
25176
25176
  // src/cli-version.ts
25177
- var CLI_VERSION = "0.1.47".length > 0 ? "0.1.47" : "0.0.0-dev";
25177
+ var CLI_VERSION = "0.1.49".length > 0 ? "0.1.49" : "0.0.0-dev";
25178
25178
 
25179
25179
  // src/cli/defaults.ts
25180
25180
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -25477,6 +25477,72 @@ function createCliE2eeRuntime(key) {
25477
25477
  };
25478
25478
  }
25479
25479
 
25480
+ // src/lib/e2ee/decrypt-stored-e2ee-content.ts
25481
+ function parseJsonObject(raw) {
25482
+ try {
25483
+ const parsed = JSON.parse(raw);
25484
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
25485
+ return parsed;
25486
+ }
25487
+ } catch {
25488
+ return null;
25489
+ }
25490
+ return null;
25491
+ }
25492
+ function storedStringHasE2eeEnvelope(raw) {
25493
+ const record2 = parseJsonObject(raw);
25494
+ return record2 != null && isE2eeEnvelope(record2.ee);
25495
+ }
25496
+ function e2eeKeyIdFromStoredString(raw) {
25497
+ const record2 = parseJsonObject(raw);
25498
+ if (record2 != null && isE2eeEnvelope(record2.ee)) return record2.ee.k;
25499
+ return null;
25500
+ }
25501
+ function decryptStoredE2eeRecord(record2, e2ee) {
25502
+ if (!isE2eeEnvelope(record2.ee)) return record2;
25503
+ if (!e2ee) {
25504
+ throw new Error(`E2EE_REQUIRED:${record2.ee.k}`);
25505
+ }
25506
+ if (record2.ee.k !== e2ee.keyId) {
25507
+ throw new Error(`E2EE_KEY_MISMATCH:encrypted=${record2.ee.k}:cli=${e2ee.keyId}`);
25508
+ }
25509
+ return e2ee.decryptMessage(record2);
25510
+ }
25511
+ function formatCliE2eeDecryptError(error40, e2ee) {
25512
+ const message = error40 instanceof Error ? error40.message : String(error40);
25513
+ const required2 = /^E2EE_REQUIRED:(.+)$/.exec(message.trim());
25514
+ if (required2) {
25515
+ const keyId = required2[1]?.trim() || "unknown";
25516
+ return `Parent session content is encrypted (key ${keyId}) but this bridge was not started with E2EE certificates. Restart the CLI with your E2EE certificate to read fork parent history.`;
25517
+ }
25518
+ const mismatch = /^E2EE_KEY_MISMATCH:encrypted=(.+):cli=(.*)$/.exec(message.trim());
25519
+ if (mismatch) {
25520
+ const encryptedKeyId = mismatch[1]?.trim() || "unknown";
25521
+ const cliKeyId = mismatch[2]?.trim() || e2ee?.keyId || "none";
25522
+ return `Parent session content is encrypted with key ${encryptedKeyId}, but this bridge certificate key is ${cliKeyId}. Use the same E2EE certificate that was used for the parent session.`;
25523
+ }
25524
+ if (message.includes("E2EE key mismatch")) {
25525
+ return `Parent session content could not be decrypted: ${message}`;
25526
+ }
25527
+ return `Parent session content could not be decrypted: ${message}`;
25528
+ }
25529
+ function decryptCliE2eeTranscriptPayload(payload, e2ee) {
25530
+ const record2 = parseJsonObject(payload);
25531
+ if (!record2 || !isE2eeEnvelope(record2.ee)) return payload;
25532
+ const decrypted = decryptStoredE2eeRecord(record2, e2ee);
25533
+ if (typeof decrypted.payload === "string") return decrypted.payload;
25534
+ return JSON.stringify(decrypted.payload ?? decrypted);
25535
+ }
25536
+ function decryptCliE2eeMessageField(payload, field, e2ee) {
25537
+ if (payload == null) return null;
25538
+ const record2 = parseJsonObject(payload);
25539
+ if (!record2 || !isE2eeEnvelope(record2.ee)) return payload;
25540
+ const decrypted = decryptStoredE2eeRecord(record2, e2ee);
25541
+ const value = decrypted[field];
25542
+ if (value == null) return null;
25543
+ return typeof value === "string" ? value : JSON.stringify(value);
25544
+ }
25545
+
25480
25546
  // src/e2e-certificates/certificates.ts
25481
25547
  var E2E_CERTIFICATE_ALGORITHM = "A1";
25482
25548
  var CERTIFICATE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".pem", ".key", ".crt"]);
@@ -27612,6 +27678,238 @@ var WebSocketMessageSchema = external_exports.object({
27612
27678
  error: external_exports.string().optional()
27613
27679
  });
27614
27680
 
27681
+ // ../types/src/integrations/link-detection/linear.ts
27682
+ function detectLinearLink(url2) {
27683
+ const linearMatch = url2.match(
27684
+ /https?:\/\/(?:app\.)?linear\.app\/([^/]+)\/issue\/([A-Z]+-\d+)/i
27685
+ );
27686
+ if (linearMatch) {
27687
+ return {
27688
+ url: url2,
27689
+ integrationType: "linear",
27690
+ resourceType: "work-item",
27691
+ resourceId: linearMatch[2],
27692
+ workspaceId: linearMatch[1]
27693
+ };
27694
+ }
27695
+ return null;
27696
+ }
27697
+
27698
+ // ../types/src/integrations/link-detection/github.ts
27699
+ function detectGitHubLink(url2) {
27700
+ const githubIssueMatch = url2.match(
27701
+ /https?:\/\/github\.com\/([^/]+)\/([^/]+)\/issues\/(\d+)/i
27702
+ );
27703
+ if (githubIssueMatch) {
27704
+ return {
27705
+ url: url2,
27706
+ integrationType: "github",
27707
+ resourceType: "issue",
27708
+ resourceId: githubIssueMatch[3],
27709
+ owner: githubIssueMatch[1],
27710
+ repo: githubIssueMatch[2]
27711
+ };
27712
+ }
27713
+ const githubPRMatch = url2.match(
27714
+ /https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/i
27715
+ );
27716
+ if (githubPRMatch) {
27717
+ return {
27718
+ url: url2,
27719
+ integrationType: "github",
27720
+ resourceType: "pull-request",
27721
+ resourceId: githubPRMatch[3],
27722
+ owner: githubPRMatch[1],
27723
+ repo: githubPRMatch[2]
27724
+ };
27725
+ }
27726
+ return null;
27727
+ }
27728
+
27729
+ // ../types/src/integrations/link-detection/jira.ts
27730
+ function detectJIRALink(url2) {
27731
+ const jiraMatch = url2.match(
27732
+ /https?:\/\/([^/]+)\.atlassian\.net\/(?:browse|jira\/software\/projects\/[^/]+\/issues)\/([A-Z]+-\d+)/i
27733
+ );
27734
+ if (jiraMatch) {
27735
+ return {
27736
+ url: url2,
27737
+ integrationType: "jira",
27738
+ resourceType: "issue",
27739
+ resourceId: jiraMatch[2],
27740
+ projectKey: jiraMatch[2].split("-")[0]
27741
+ };
27742
+ }
27743
+ return null;
27744
+ }
27745
+
27746
+ // ../types/src/integrations/link-detection/notion.ts
27747
+ function formatNotionPageId(hex) {
27748
+ if (hex.length !== 32) return hex;
27749
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
27750
+ }
27751
+ function detectNotionLink(url2) {
27752
+ const standardMatch = url2.match(
27753
+ /https?:\/\/(?:www\.)?notion\.(?:so|site)\/(?:[^/#?]+\/)?(?:[^/#?-]+-)?([a-f0-9]{32})/i
27754
+ );
27755
+ if (standardMatch) {
27756
+ return {
27757
+ url: url2,
27758
+ integrationType: "notion",
27759
+ resourceType: "page",
27760
+ resourceId: formatNotionPageId(standardMatch[1])
27761
+ };
27762
+ }
27763
+ const siteMatch = url2.match(
27764
+ /https?:\/\/([^.]+)\.notion\.(?:so|site)\/(?:[^/#?-]+-)?([a-f0-9]{32})/i
27765
+ );
27766
+ if (siteMatch) {
27767
+ return {
27768
+ url: url2,
27769
+ integrationType: "notion",
27770
+ resourceType: "page",
27771
+ resourceId: formatNotionPageId(siteMatch[2]),
27772
+ notionWorkspaceId: siteMatch[1]
27773
+ };
27774
+ }
27775
+ return null;
27776
+ }
27777
+
27778
+ // ../types/src/integrations/link-detection/slack.ts
27779
+ function detectSlackLink(url2) {
27780
+ const archiveThreadMatch = url2.match(
27781
+ /https?:\/\/(?:([^./]+)\.)?slack\.com\/archives\/([A-Z0-9]+)\/p(\d+)(?:\?.*)?$/i
27782
+ );
27783
+ if (archiveThreadMatch) {
27784
+ return {
27785
+ url: url2,
27786
+ integrationType: "slack",
27787
+ resourceType: "thread",
27788
+ resourceId: `${archiveThreadMatch[2]}-${archiveThreadMatch[3]}`,
27789
+ isChannel: false
27790
+ };
27791
+ }
27792
+ const appThreadMatch = url2.match(
27793
+ /https?:\/\/app\.slack\.com\/client\/([^/]+)\/([A-Z0-9]+)\/thread\/[A-Z0-9]+-(\d+)/i
27794
+ );
27795
+ if (appThreadMatch) {
27796
+ return {
27797
+ url: url2,
27798
+ integrationType: "slack",
27799
+ resourceType: "thread",
27800
+ resourceId: `${appThreadMatch[2]}-${appThreadMatch[3]}`,
27801
+ isChannel: false
27802
+ };
27803
+ }
27804
+ const channelOnlyMatch = url2.match(
27805
+ /https?:\/\/(?:app\.)?(?:([^./]+)\.)?slack\.com\/archives\/([A-Z0-9]+)\/?(\?.*)?$/i
27806
+ );
27807
+ if (channelOnlyMatch && !url2.includes("/p")) {
27808
+ return {
27809
+ url: url2,
27810
+ integrationType: "slack",
27811
+ resourceType: "channel",
27812
+ resourceId: channelOnlyMatch[2],
27813
+ isChannel: true
27814
+ };
27815
+ }
27816
+ return null;
27817
+ }
27818
+
27819
+ // ../types/src/integrations/link-detection/zoom.ts
27820
+ function detectZoomLink(url2) {
27821
+ try {
27822
+ const u = new URL(url2);
27823
+ const host = u.hostname.toLowerCase();
27824
+ if (!host.endsWith("zoom.us") && !host.endsWith("zoom.com")) return null;
27825
+ const pathParts = u.pathname.split("/").filter(Boolean);
27826
+ if (pathParts.length >= 2 && (pathParts[0] === "j" || pathParts[0] === "s" || pathParts[0] === "my")) {
27827
+ const meetingId = pathParts[1].replace(/\s/g, "");
27828
+ if (meetingId.length >= 9) {
27829
+ return {
27830
+ url: url2,
27831
+ integrationType: "zoom",
27832
+ resourceType: "meeting",
27833
+ resourceId: meetingId,
27834
+ zoomMeetingId: meetingId
27835
+ };
27836
+ }
27837
+ }
27838
+ } catch {
27839
+ }
27840
+ return null;
27841
+ }
27842
+
27843
+ // ../types/src/integrations/link-detection/asana.ts
27844
+ var ASANA_TASK_GID = /\d{10,}/;
27845
+ function parseAsanaTaskGid(segment) {
27846
+ const trimmed2 = segment.trim();
27847
+ if (!ASANA_TASK_GID.test(trimmed2)) return null;
27848
+ return trimmed2;
27849
+ }
27850
+ function detectAsanaLink(url2) {
27851
+ const v1ProjectMatch = url2.match(
27852
+ /https?:\/\/app\.asana\.com\/1\/[^/]+\/project\/([^/?#]+)\/task\/([^/?#]+)/i
27853
+ );
27854
+ if (v1ProjectMatch) {
27855
+ const taskGid = parseAsanaTaskGid(v1ProjectMatch[2]);
27856
+ if (!taskGid) return null;
27857
+ return {
27858
+ url: url2,
27859
+ integrationType: "asana",
27860
+ resourceType: "task",
27861
+ resourceId: taskGid,
27862
+ asanaProjectGid: v1ProjectMatch[1]
27863
+ };
27864
+ }
27865
+ const v1TaskMatch = url2.match(/https?:\/\/app\.asana\.com\/1\/[^/]+(?:\/[^/]+)*\/task\/([^/?#]+)/i);
27866
+ if (v1TaskMatch) {
27867
+ const taskGid = parseAsanaTaskGid(v1TaskMatch[1]);
27868
+ if (!taskGid) return null;
27869
+ return {
27870
+ url: url2,
27871
+ integrationType: "asana",
27872
+ resourceType: "task",
27873
+ resourceId: taskGid
27874
+ };
27875
+ }
27876
+ const legacyMatch = url2.match(/https?:\/\/app\.asana\.com\/0\/([^/?#]+)\/([^/?#]+)/i);
27877
+ if (legacyMatch) {
27878
+ const projectGid = legacyMatch[1];
27879
+ const taskGid = parseAsanaTaskGid(legacyMatch[2]);
27880
+ if (!taskGid) return null;
27881
+ if (projectGid === "inbox" || projectGid === "my_tasks") {
27882
+ return null;
27883
+ }
27884
+ return {
27885
+ url: url2,
27886
+ integrationType: "asana",
27887
+ resourceType: "task",
27888
+ resourceId: taskGid,
27889
+ asanaProjectGid: projectGid === "0" ? void 0 : projectGid
27890
+ };
27891
+ }
27892
+ return null;
27893
+ }
27894
+
27895
+ // ../types/src/integrations/link-detection/index.ts
27896
+ function detectIntegrationLink(url2) {
27897
+ return detectLinearLink(url2) || detectGitHubLink(url2) || detectJIRALink(url2) || detectNotionLink(url2) || detectSlackLink(url2) || detectZoomLink(url2) || detectAsanaLink(url2);
27898
+ }
27899
+ function extractIntegrationLinks(text) {
27900
+ const urlRegex = /https?:\/\/[^\s<>"{}|\\^`[\]]+/gi;
27901
+ const urls = text.match(urlRegex) || [];
27902
+ const links = [];
27903
+ for (const url2 of urls) {
27904
+ const detected = detectIntegrationLink(url2);
27905
+ if (detected) links.push(detected);
27906
+ }
27907
+ return links;
27908
+ }
27909
+ function extractFetchableIntegrationLinks(text) {
27910
+ return extractIntegrationLinks(text).filter((link) => !(link.integrationType === "slack" && link.isChannel));
27911
+ }
27912
+
27615
27913
  // ../types/src/checkpoints.ts
27616
27914
  init_zod();
27617
27915
  var CheckpointKindSchema = external_exports.enum(["daily", "weekly", "overall"]);
@@ -28039,6 +28337,16 @@ var GitRepoMetaSchema = external_exports.object({
28039
28337
  updatedAt: external_exports.string()
28040
28338
  });
28041
28339
 
28340
+ // ../types/src/bridge-mcp-tool-display.ts
28341
+ var GET_INTEGRATION_CONTENT_TOOL = "get_integration_content";
28342
+ var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
28343
+ var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
28344
+ var BRIDGE_MCP_TOOL_LABELS = {
28345
+ [GET_INTEGRATION_CONTENT_TOOL]: "Fetch external ticket content",
28346
+ [LIST_PARENT_SESSION_PROMPTS_TOOL]: "List parent session prompts",
28347
+ [GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL]: "Read parent session transcript"
28348
+ };
28349
+
28042
28350
  // ../types/src/claude-code-permission-mode.ts
28043
28351
  var CLAUDE_CODE_PERMISSION_MODES = [
28044
28352
  "default",
@@ -36081,54 +36389,109 @@ function buildForkedSessionAgentPrompt(userPrompt, childSessionId, parentSession
36081
36389
  ].join("\n");
36082
36390
  }
36083
36391
 
36084
- // src/mcp/tools/session-history/fetch-parent-session-cloud.ts
36392
+ // src/mcp/tools/session-history/cloud/internal-api.ts
36085
36393
  function internalApiBase(cloudApiBaseUrl) {
36086
36394
  return cloudApiBaseUrl.replace(/\/+$/, "");
36087
36395
  }
36088
- async function fetchCloudSessionMeta(params) {
36396
+ async function fetchInternalApiJson(params) {
36089
36397
  const token = params.getCloudAccessToken();
36090
36398
  if (!token) return { ok: false, error: "Missing cloud access token." };
36091
- const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`;
36092
- const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
36399
+ const url2 = `${internalApiBase(params.cloudApiBaseUrl)}${params.path}`;
36400
+ const method = params.method ?? "GET";
36401
+ const res = await fetch(url2, {
36402
+ method,
36403
+ headers: {
36404
+ Authorization: `Bearer ${token}`,
36405
+ ...params.body != null ? { "Content-Type": "application/json" } : {}
36406
+ },
36407
+ ...params.body != null ? { body: JSON.stringify(params.body) } : {}
36408
+ });
36093
36409
  if (!res.ok) {
36094
36410
  const t = await res.text().catch(() => "");
36095
- return { ok: false, error: `Session meta fetch failed (${res.status}): ${t.slice(0, 200)}` };
36411
+ return { ok: false, error: `${params.errorLabel} (${res.status}): ${t.slice(0, 200)}` };
36096
36412
  }
36097
36413
  const data = await res.json().catch(() => null);
36098
- if (!data) return { ok: false, error: "Invalid session meta response." };
36414
+ if (!data) return { ok: false, error: `Invalid ${params.errorLabel.toLowerCase()} response.` };
36099
36415
  return { ok: true, data };
36100
36416
  }
36101
- async function fetchCloudParentSessionPrompts(params) {
36102
- const token = params.getCloudAccessToken();
36103
- if (!token) return { ok: false, error: "Missing cloud access token." };
36104
- const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`;
36105
- const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
36106
- if (!res.ok) {
36107
- const t = await res.text().catch(() => "");
36108
- return { ok: false, error: `Parent prompts fetch failed (${res.status}): ${t.slice(0, 200)}` };
36417
+
36418
+ // src/mcp/tools/session-history/cloud/fetch-session-meta.ts
36419
+ async function fetchCloudSessionMeta(params) {
36420
+ return fetchInternalApiJson({
36421
+ ...params,
36422
+ path: `/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`,
36423
+ errorLabel: "Session meta fetch failed"
36424
+ });
36425
+ }
36426
+
36427
+ // src/mcp/tools/session-history/cloud/decrypt-parent-prompt-turns.ts
36428
+ function assertE2eeRuntimeForEnvelope(raw, e2ee) {
36429
+ if (storedStringHasE2eeEnvelope(raw) && !e2ee) {
36430
+ throw new Error(`E2EE_REQUIRED:${e2eeKeyIdFromStoredString(raw) ?? "unknown"}`);
36109
36431
  }
36110
- const data = await res.json().catch(() => null);
36111
- if (!data) return { ok: false, error: "Invalid parent prompts response." };
36112
- return { ok: true, data };
36113
36432
  }
36114
- async function fetchCloudParentTurnTranscript(params) {
36115
- const token = params.getCloudAccessToken();
36116
- if (!token) return { ok: false, error: "Missing cloud access token." };
36117
- const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`;
36118
- const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
36119
- if (!res.ok) {
36120
- const t = await res.text().catch(() => "");
36121
- return { ok: false, error: `Transcript fetch failed (${res.status}): ${t.slice(0, 200)}` };
36433
+ function decryptParentPromptTurns(turns, e2ee) {
36434
+ return turns.map((turn) => {
36435
+ const promptRaw = typeof turn.promptText === "string" ? turn.promptText : null;
36436
+ const titleRaw = typeof turn.title === "string" ? turn.title : null;
36437
+ if (promptRaw) assertE2eeRuntimeForEnvelope(promptRaw, e2ee);
36438
+ if (titleRaw) assertE2eeRuntimeForEnvelope(titleRaw, e2ee);
36439
+ return {
36440
+ ...turn,
36441
+ promptText: decryptCliE2eeMessageField(promptRaw, "prompt", e2ee),
36442
+ title: decryptCliE2eeMessageField(titleRaw, "title", e2ee)
36443
+ };
36444
+ });
36445
+ }
36446
+
36447
+ // src/mcp/tools/session-history/cloud/fetch-parent-prompts.ts
36448
+ async function fetchCloudParentSessionPrompts(params) {
36449
+ const fetched = await fetchInternalApiJson({
36450
+ ...params,
36451
+ path: `/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`,
36452
+ errorLabel: "Parent prompts fetch failed"
36453
+ });
36454
+ if (!fetched.ok) return fetched;
36455
+ const turns = Array.isArray(fetched.data.turns) ? fetched.data.turns : [];
36456
+ try {
36457
+ const decryptedTurns = decryptParentPromptTurns(turns, params.e2ee);
36458
+ return { ok: true, data: { ...fetched.data, turns: decryptedTurns } };
36459
+ } catch (error40) {
36460
+ return { ok: false, error: formatCliE2eeDecryptError(error40, params.e2ee) };
36122
36461
  }
36123
- const data = await res.json().catch(() => null);
36124
- const rows = Array.isArray(data?.transcript) ? data.transcript : [];
36462
+ }
36463
+
36464
+ // src/mcp/tools/session-history/cloud/format-parent-transcript-text.ts
36465
+ function formatParentTranscriptText(rows, e2ee) {
36125
36466
  const lines = rows.map((row) => {
36126
36467
  const kind = typeof row.kind === "string" ? row.kind : "event";
36127
- const payload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
36468
+ const rawPayload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
36469
+ if (storedStringHasE2eeEnvelope(rawPayload) && !e2ee) {
36470
+ throw new Error(`E2EE_REQUIRED:${e2eeKeyIdFromStoredString(rawPayload) ?? "unknown"}`);
36471
+ }
36472
+ const payload = decryptCliE2eeTranscriptPayload(rawPayload, e2ee);
36128
36473
  return `[${kind}] ${payload}`;
36129
36474
  });
36130
- return { ok: true, text: lines.join("\n") };
36475
+ return lines.join("\n");
36131
36476
  }
36477
+
36478
+ // src/mcp/tools/session-history/cloud/fetch-parent-transcript.ts
36479
+ async function fetchCloudParentTurnTranscript(params) {
36480
+ const fetched = await fetchInternalApiJson({
36481
+ ...params,
36482
+ path: `/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`,
36483
+ errorLabel: "Transcript fetch failed"
36484
+ });
36485
+ if (!fetched.ok) return fetched;
36486
+ const rows = Array.isArray(fetched.data.transcript) ? fetched.data.transcript : [];
36487
+ try {
36488
+ return { ok: true, text: formatParentTranscriptText(rows, params.e2ee) };
36489
+ } catch (error40) {
36490
+ return { ok: false, error: formatCliE2eeDecryptError(error40, params.e2ee) };
36491
+ }
36492
+ }
36493
+
36494
+ // src/mcp/tools/session-history/cloud/resolve-parent-session-id.ts
36132
36495
  async function resolveParentSessionIdForChild(params) {
36133
36496
  const meta = await fetchCloudSessionMeta({
36134
36497
  sessionId: params.childSessionId,
@@ -36159,6 +36522,39 @@ async function enrichForkedSessionPromptForAgent(params) {
36159
36522
  return buildForkedSessionAgentPrompt(params.promptText, params.sessionId, parentSessionId);
36160
36523
  }
36161
36524
 
36525
+ // src/agents/acp/build-integration-content-agent-prompt-note.ts
36526
+ function buildIntegrationContentAgentPromptNote(sessionId) {
36527
+ const sessionRef = sessionId.trim();
36528
+ return [
36529
+ "The user message includes link(s) to external integration resources (Linear, GitHub, Jira, Notion, Slack thread, Zoom, or Asana).",
36530
+ "Use the get_integration_content MCP tool on the buildautomaton-bridge server to fetch the full content of each link.",
36531
+ `When calling the tool, pass sessionId "${sessionRef}" and the URL as url.`,
36532
+ "By default the tool links the fetched URL as this session external ticket when the session has none yet. Pass associateAsSessionTicket: false when fetching additional links for context only (after the primary ticket is linked, or when you do not want association).",
36533
+ "If the tool reports unsupported_url, do not retry with the same URL. If it reports temporarily_unavailable, you may retry later."
36534
+ ].join("\n");
36535
+ }
36536
+ function injectIntegrationContentAgentPromptNote(agentPrompt, sessionId) {
36537
+ const note = buildIntegrationContentAgentPromptNote(sessionId);
36538
+ const marker = "\nUser request:\n";
36539
+ const idx = agentPrompt.indexOf(marker);
36540
+ if (idx >= 0) {
36541
+ return `${agentPrompt.slice(0, idx)}
36542
+ ${note}${agentPrompt.slice(idx)}`;
36543
+ }
36544
+ return `${note}
36545
+
36546
+ ${agentPrompt.trim()}`;
36547
+ }
36548
+
36549
+ // src/agents/acp/enrich-integration-content-prompt-for-agent.ts
36550
+ function enrichIntegrationContentPromptForAgent(params) {
36551
+ if (!params.sessionId.trim()) return params.agentPromptText;
36552
+ if (extractFetchableIntegrationLinks(params.originalPromptText).length === 0) {
36553
+ return params.agentPromptText;
36554
+ }
36555
+ return injectIntegrationContentAgentPromptNote(params.agentPromptText, params.sessionId);
36556
+ }
36557
+
36162
36558
  // src/agents/acp/send-prompt-to-agent.ts
36163
36559
  async function sendPromptToAgent(options) {
36164
36560
  const {
@@ -36189,6 +36585,13 @@ async function sendPromptToAgent(options) {
36189
36585
  getCloudAccessToken
36190
36586
  });
36191
36587
  }
36588
+ if (sessionId) {
36589
+ agentPromptText = enrichIntegrationContentPromptForAgent({
36590
+ sessionId,
36591
+ originalPromptText: promptText,
36592
+ agentPromptText
36593
+ });
36594
+ }
36192
36595
  let sendOpts = {};
36193
36596
  if (attachments && attachments.length > 0) {
36194
36597
  if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
@@ -42423,16 +42826,81 @@ async function warmupAgentCapabilitiesOnConnect(params) {
42423
42826
  })();
42424
42827
  }
42425
42828
 
42426
- // src/mcp/tools/session-history/fork-session-mcp-tools.ts
42427
- var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
42428
- var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
42829
+ // src/mcp/tools/session-history/fork/mcp-text-result.ts
42830
+ function mcpTextResult(text, isError = false) {
42831
+ return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
42832
+ }
42833
+
42834
+ // src/mcp/tools/session-history/fork/call-get-parent-session-turn-transcript.ts
42835
+ async function callGetParentSessionTurnTranscriptTool(ctx, cloud, childSessionId, args) {
42836
+ const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
42837
+ if (!turnId) return mcpTextResult("turnId is required.", true);
42838
+ const transcript = await fetchCloudParentTurnTranscript({
42839
+ childSessionId,
42840
+ turnId,
42841
+ cloudApiBaseUrl: cloud.cloudApiBaseUrl,
42842
+ getCloudAccessToken: cloud.getCloudAccessToken,
42843
+ e2ee: ctx.e2ee
42844
+ });
42845
+ if (!transcript.ok) return mcpTextResult(transcript.error, true);
42846
+ return mcpTextResult(transcript.text);
42847
+ }
42848
+
42849
+ // src/mcp/tools/session-history/fork/call-list-parent-session-prompts.ts
42850
+ async function callListParentSessionPromptsTool(ctx, cloud, childSessionId, parentSessionId) {
42851
+ const detail = await fetchCloudParentSessionPrompts({
42852
+ childSessionId,
42853
+ cloudApiBaseUrl: cloud.cloudApiBaseUrl,
42854
+ getCloudAccessToken: cloud.getCloudAccessToken,
42855
+ e2ee: ctx.e2ee
42856
+ });
42857
+ if (!detail.ok) return mcpTextResult(detail.error, true);
42858
+ const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
42859
+ const payload = turns.map((t) => ({
42860
+ turnId: typeof t.turnId === "string" ? t.turnId : "",
42861
+ title: typeof t.title === "string" ? t.title : null,
42862
+ status: typeof t.status === "string" ? t.status : null,
42863
+ promptText: typeof t.promptText === "string" ? t.promptText : null
42864
+ }));
42865
+ return mcpTextResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
42866
+ }
42867
+
42868
+ // src/mcp/tools/session-history/fork/tool-names.ts
42869
+ var LIST_PARENT_SESSION_PROMPTS_TOOL2 = "list_parent_session_prompts";
42870
+ var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL2 = "get_parent_session_turn_transcript";
42871
+
42872
+ // src/mcp/tools/session-history/fork/call-fork-session-mcp-tool.ts
42873
+ async function callForkSessionMcpTool(ctx, cloud, name, args) {
42874
+ const childSessionId = ctx.sessionId.trim();
42875
+ if (!childSessionId) {
42876
+ return mcpTextResult("sessionId is required.", true);
42877
+ }
42878
+ const parentResolved = await resolveParentSessionIdForChild({
42879
+ childSessionId,
42880
+ cloudApiBaseUrl: cloud.cloudApiBaseUrl,
42881
+ getCloudAccessToken: cloud.getCloudAccessToken
42882
+ });
42883
+ if (!parentResolved.ok) {
42884
+ return mcpTextResult(parentResolved.error, true);
42885
+ }
42886
+ const parentSessionId = parentResolved.parentSessionId;
42887
+ if (name === LIST_PARENT_SESSION_PROMPTS_TOOL2) {
42888
+ return callListParentSessionPromptsTool(ctx, cloud, childSessionId, parentSessionId);
42889
+ }
42890
+ if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL2) {
42891
+ return callGetParentSessionTurnTranscriptTool(ctx, cloud, childSessionId, args);
42892
+ }
42893
+ return mcpTextResult(`Unknown fork session tool: ${name}`, true);
42894
+ }
42895
+
42896
+ // src/mcp/tools/session-history/fork/tool-definitions.ts
42429
42897
  var SESSION_ID_SCHEMA = {
42430
42898
  type: "string",
42431
42899
  description: "Id of this forked (child) session \u2014 not the parent session id. Required on every tool call."
42432
42900
  };
42433
42901
  var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
42434
42902
  {
42435
- name: LIST_PARENT_SESSION_PROMPTS_TOOL,
42903
+ name: LIST_PARENT_SESSION_PROMPTS_TOOL2,
42436
42904
  description: "List user prompts (main turns) from the parent session a fork was created from. Requires sessionId. Returns an error if the session has no parent history (not a fork).",
42437
42905
  inputSchema: {
42438
42906
  type: "object",
@@ -42442,7 +42910,7 @@ var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
42442
42910
  }
42443
42911
  },
42444
42912
  {
42445
- name: GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL,
42913
+ name: GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL2,
42446
42914
  description: "Full transcript for one parent-session turn, including tool calls and agent messages. Requires sessionId and turnId. Returns an error if the session has no parent history (not a fork).",
42447
42915
  inputSchema: {
42448
42916
  type: "object",
@@ -42455,56 +42923,133 @@ var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
42455
42923
  }
42456
42924
  }
42457
42925
  ];
42458
- function textResult(text, isError = false) {
42459
- return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
42460
- }
42926
+
42927
+ // src/mcp/tools/session-history/fork/is-fork-session-mcp-tool-name.ts
42461
42928
  function isForkSessionMcpToolName(name) {
42462
- return name === LIST_PARENT_SESSION_PROMPTS_TOOL || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL;
42929
+ return name === LIST_PARENT_SESSION_PROMPTS_TOOL2 || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL2;
42463
42930
  }
42464
- async function callForkSessionMcpTool(ctx, cloud, name, args) {
42465
- const childSessionId = ctx.sessionId.trim();
42466
- if (!childSessionId) {
42467
- return textResult("sessionId is required.", true);
42931
+
42932
+ // src/mcp/tools/integration-content/tool-names.ts
42933
+ var GET_INTEGRATION_CONTENT_TOOL2 = "get_integration_content";
42934
+
42935
+ // src/mcp/tools/integration-content/cloud/fetch-integration-content.ts
42936
+ async function fetchCloudIntegrationContent(params) {
42937
+ return fetchInternalApiJson({
42938
+ ...params,
42939
+ path: `/internal/sessions/${encodeURIComponent(params.sessionId)}/integrations/content`,
42940
+ errorLabel: "Integration content fetch failed",
42941
+ method: "POST",
42942
+ body: {
42943
+ url: params.url,
42944
+ associateAsSessionTicket: params.associateAsSessionTicket !== false
42945
+ }
42946
+ });
42947
+ }
42948
+
42949
+ // src/mcp/tools/integration-content/format-integration-content-for-mcp.ts
42950
+ function formatIntegrationContentForMcp(result) {
42951
+ if (!result.ok) {
42952
+ const lines2 = [`status: ${result.code}`, `message: ${result.message}`];
42953
+ if (result.integrationType) lines2.push(`integrationType: ${result.integrationType}`);
42954
+ if (result.retryable) lines2.push("retryable: true");
42955
+ lines2.push(...formatSessionTicketAssociationLines(result.sessionTicketAssociation));
42956
+ return lines2.join("\n");
42468
42957
  }
42469
- const parentResolved = await resolveParentSessionIdForChild({
42470
- childSessionId,
42958
+ const lines = [
42959
+ formatIntegrationContentBody(result.integrationType, result.content),
42960
+ ...formatSessionTicketAssociationLines(result.sessionTicketAssociation)
42961
+ ];
42962
+ return lines.join("\n");
42963
+ }
42964
+ function formatSessionTicketAssociationLines(association) {
42965
+ if (association.status === "associated") {
42966
+ const title = association.externalTicket.displayTitle?.trim() || association.externalTicket.normalizedUrl;
42967
+ return ["", "--- session ticket ---", "status: associated", `displayTitle: ${title}`];
42968
+ }
42969
+ if (association.status === "failed") {
42970
+ return ["", "--- session ticket ---", "status: failed", `reason: ${association.reason}`];
42971
+ }
42972
+ return ["", "--- session ticket ---", `status: ${association.status}`, `reason: ${association.reason}`];
42973
+ }
42974
+ function formatIntegrationContentBody(integrationType, content) {
42975
+ const lines = [
42976
+ `integrationType: ${integrationType}`,
42977
+ `title: ${content.title}`,
42978
+ `url: ${content.url}`,
42979
+ "",
42980
+ "--- body ---",
42981
+ content.body
42982
+ ];
42983
+ if (content.comments?.length) {
42984
+ lines.push("", "--- comments ---");
42985
+ for (const comment of content.comments) {
42986
+ lines.push(`[${comment.createdAt}] ${comment.author}:`, comment.body, "");
42987
+ }
42988
+ }
42989
+ const threadMessages = content.threadMessages;
42990
+ if (threadMessages?.length) {
42991
+ lines.push("", "--- thread messages ---");
42992
+ for (const msg of threadMessages) {
42993
+ lines.push(`[${msg.ts}] ${msg.displayName}: ${msg.text}`);
42994
+ }
42995
+ }
42996
+ if (content.metadata && Object.keys(content.metadata).length > 0) {
42997
+ lines.push("", "--- metadata ---", JSON.stringify(content.metadata, null, 2));
42998
+ }
42999
+ return lines.join("\n");
43000
+ }
43001
+
43002
+ // src/mcp/tools/integration-content/call-integration-content-mcp-tool.ts
43003
+ async function callIntegrationContentMcpTool(ctx, cloud, name, args) {
43004
+ if (name !== GET_INTEGRATION_CONTENT_TOOL2) {
43005
+ return mcpTextResult(`Unknown integration content tool: ${name}`, true);
43006
+ }
43007
+ const sessionId = ctx.sessionId.trim();
43008
+ if (!sessionId) return mcpTextResult("sessionId is required.", true);
43009
+ const url2 = typeof args.url === "string" ? args.url.trim() : "";
43010
+ if (!url2) return mcpTextResult("url is required.", true);
43011
+ const associateAsSessionTicket = args.associateAsSessionTicket !== false;
43012
+ const fetched = await fetchCloudIntegrationContent({
43013
+ sessionId,
43014
+ url: url2,
43015
+ associateAsSessionTicket,
42471
43016
  cloudApiBaseUrl: cloud.cloudApiBaseUrl,
42472
43017
  getCloudAccessToken: cloud.getCloudAccessToken
42473
43018
  });
42474
- if (!parentResolved.ok) {
42475
- return textResult(parentResolved.error, true);
42476
- }
42477
- const parentSessionId = parentResolved.parentSessionId;
42478
- if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
42479
- const detail = await fetchCloudParentSessionPrompts({
42480
- childSessionId,
42481
- cloudApiBaseUrl: cloud.cloudApiBaseUrl,
42482
- getCloudAccessToken: cloud.getCloudAccessToken
42483
- });
42484
- if (!detail.ok) return textResult(detail.error, true);
42485
- const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
42486
- const payload = turns.map((t) => ({
42487
- turnId: typeof t.turnId === "string" ? t.turnId : "",
42488
- title: typeof t.title === "string" ? t.title : null,
42489
- status: typeof t.status === "string" ? t.status : null,
42490
- promptText: typeof t.promptText === "string" ? t.promptText : null
42491
- }));
42492
- return textResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
42493
- }
42494
- if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
42495
- const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
42496
- if (!turnId) return textResult("turnId is required.", true);
42497
- const transcript = await fetchCloudParentTurnTranscript({
42498
- childSessionId,
42499
- turnId,
42500
- cloudApiBaseUrl: cloud.cloudApiBaseUrl,
42501
- getCloudAccessToken: cloud.getCloudAccessToken,
42502
- e2ee: ctx.e2ee
42503
- });
42504
- if (!transcript.ok) return textResult(transcript.error, true);
42505
- return textResult(transcript.text);
43019
+ if (!fetched.ok) return mcpTextResult(fetched.error, true);
43020
+ const text = formatIntegrationContentForMcp(fetched.data);
43021
+ const isError = !fetched.data.ok;
43022
+ return mcpTextResult(text, isError);
43023
+ }
43024
+
43025
+ // src/mcp/tools/integration-content/tool-definitions.ts
43026
+ var SESSION_ID_SCHEMA2 = {
43027
+ type: "string",
43028
+ description: "Id of the current session. Required on every tool call so the workspace integration credentials can be resolved."
43029
+ };
43030
+ var INTEGRATION_CONTENT_MCP_TOOL_DEFINITIONS = [
43031
+ {
43032
+ name: GET_INTEGRATION_CONTENT_TOOL2,
43033
+ description: "Fetch the full content of a supported external integration link (Linear, GitHub, Jira, Notion, Slack thread, Zoom meeting, Asana). Requires sessionId and url. By default, links the URL as this session external ticket when the session has none yet. Pass associateAsSessionTicket: false to fetch content only (e.g. additional context links). Returns an error when the URL is unsupported, the integration is not connected, or content is temporarily unavailable (retry may succeed).",
43034
+ inputSchema: {
43035
+ type: "object",
43036
+ properties: {
43037
+ sessionId: SESSION_ID_SCHEMA2,
43038
+ url: { type: "string", description: "External integration URL to fetch" },
43039
+ associateAsSessionTicket: {
43040
+ type: "boolean",
43041
+ description: "Defaults to true. When true and the session has no linked external ticket yet, links this URL as the session external ticket. Pass false when fetching additional links for context only."
43042
+ }
43043
+ },
43044
+ required: ["sessionId", "url"],
43045
+ additionalProperties: false
43046
+ }
42506
43047
  }
42507
- return textResult(`Unknown fork session tool: ${name}`, true);
43048
+ ];
43049
+
43050
+ // src/mcp/tools/integration-content/index.ts
43051
+ function isIntegrationContentMcpToolName(name) {
43052
+ return name === GET_INTEGRATION_CONTENT_TOOL2;
42508
43053
  }
42509
43054
 
42510
43055
  // src/mcp/bridge-mcp-tools.ts
@@ -42517,7 +43062,7 @@ function requireCloud(shared) {
42517
43062
  function createBridgeMcpToolRegistry(shared) {
42518
43063
  const cloud = requireCloud(shared);
42519
43064
  return {
42520
- listTools: async () => [...FORK_SESSION_MCP_TOOL_DEFINITIONS],
43065
+ listTools: async () => [...FORK_SESSION_MCP_TOOL_DEFINITIONS, ...INTEGRATION_CONTENT_MCP_TOOL_DEFINITIONS],
42521
43066
  callTool: async (name, args) => {
42522
43067
  if (isForkSessionMcpToolName(name)) {
42523
43068
  if (!cloud) {
@@ -42535,6 +43080,22 @@ function createBridgeMcpToolRegistry(shared) {
42535
43080
  }
42536
43081
  return callForkSessionMcpTool({ ...shared, sessionId }, cloud, name, args);
42537
43082
  }
43083
+ if (isIntegrationContentMcpToolName(name)) {
43084
+ if (!cloud) {
43085
+ return {
43086
+ content: [{ type: "text", text: "Bridge MCP tools require cloud API credentials." }],
43087
+ isError: true
43088
+ };
43089
+ }
43090
+ const sessionId = typeof args.sessionId === "string" ? args.sessionId.trim() : "";
43091
+ if (!sessionId) {
43092
+ return {
43093
+ content: [{ type: "text", text: "sessionId is required." }],
43094
+ isError: true
43095
+ };
43096
+ }
43097
+ return callIntegrationContentMcpTool({ ...shared, sessionId }, cloud, name, args);
43098
+ }
42538
43099
  throw new Error(`Unknown bridge MCP tool: ${name}`);
42539
43100
  }
42540
43101
  };