@sideboard-ai/core 0.1.324 → 0.1.325

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 (83) hide show
  1. package/dist/{abletime-5ZY3ZUUO.js → abletime-M7STTMZT.js} +6 -5
  2. package/dist/{abletime-CU2UPMJF.js → abletime-VL234MCP.js} +5 -4
  3. package/dist/{abletime-oauth-7EOSBEFM.js → abletime-oauth-LMMGMAPA.js} +3 -3
  4. package/dist/{abletime-oauth-4TTCR3CT.js → abletime-oauth-P4ZFBEAL.js} +3 -3
  5. package/dist/{abletime-rest-3TG4G7LW.js → abletime-rest-DLV7EIWS.js} +3 -3
  6. package/dist/{abletime-rest-KOKE7V4B.js → abletime-rest-GOI7HELR.js} +3 -3
  7. package/dist/agents/claude-runner.cjs +59 -22
  8. package/dist/agents/claude-runner.js +39 -16
  9. package/dist/agents/cursor-runner.cjs +96 -31
  10. package/dist/agents/cursor-runner.js +74 -24
  11. package/dist/{agents-YK65UDLT.js → agents-LDRM3BTY.js} +9 -8
  12. package/dist/{agents-LUSTCMDN.js → agents-MBIEAETM.js} +12 -11
  13. package/dist/{app-settings-I5HM5RMP.js → app-settings-E7DMMEKC.js} +1 -1
  14. package/dist/{app-settings-XIF4EC4K.js → app-settings-UQHUNOLV.js} +1 -1
  15. package/dist/{chunk-INBUV7HY.js → chunk-2UP3XD2D.js} +2 -2
  16. package/dist/{chunk-EQ52JR3Q.js → chunk-5MXLDDYF.js} +1 -1
  17. package/dist/{chunk-NCHOTN3C.js → chunk-5YCKATG6.js} +4 -3
  18. package/dist/{chunk-BYUCNIXK.js → chunk-7G27TB4M.js} +6 -6
  19. package/dist/chunk-7LUOLYFM.js +1445 -0
  20. package/dist/{chunk-Y3WREIPQ.js → chunk-A76MKMSF.js} +12 -210
  21. package/dist/{chunk-JE3JLHMO.js → chunk-APKUI4BP.js} +3 -3
  22. package/dist/{chunk-FR3SAYUL.js → chunk-AQEKPJWU.js} +5 -5
  23. package/dist/{chunk-QOS4465Y.js → chunk-BOFYI6LC.js} +1 -1
  24. package/dist/{chunk-FUFNMUNR.js → chunk-BTF3ATST.js} +7 -8
  25. package/dist/{chunk-OIHRC2ZJ.js → chunk-CI4M54LS.js} +22 -2
  26. package/dist/chunk-DBKHL7NC.js +214 -0
  27. package/dist/{chunk-ROOZDDFA.js → chunk-EO6ZFLG6.js} +7 -8
  28. package/dist/chunk-EZR6W4B3.js +1259 -0
  29. package/dist/{chunk-MZIZTSR2.js → chunk-F65JMKUR.js} +2 -2
  30. package/dist/chunk-F6GE7V3Q.js +216 -0
  31. package/dist/{chunk-7MQSYPAY.js → chunk-GNLKBS3U.js} +206 -188
  32. package/dist/{chunk-EKU7FQVG.js → chunk-H36GKGU2.js} +2 -2
  33. package/dist/{chunk-7D5QIU3N.js → chunk-HG6KS7OT.js} +294 -265
  34. package/dist/chunk-MBT6WDAB.js +28 -0
  35. package/dist/{chunk-JH5SMVM5.js → chunk-MFMDZAA6.js} +262 -233
  36. package/dist/{chunk-7PVT73H6.js → chunk-NWGINMEA.js} +12 -210
  37. package/dist/{chunk-QIC2JXHW.js → chunk-OOXRMTU6.js} +1 -1
  38. package/dist/{chunk-5XNT6BCG.js → chunk-OPXEJJBM.js} +1 -1
  39. package/dist/chunk-OYRN356W.js +80 -0
  40. package/dist/chunk-PPTJMBUY.js +25 -0
  41. package/dist/{chunk-B4CDSSRN.js → chunk-Q3FHTGIW.js} +2 -0
  42. package/dist/{chunk-EJ66LEKH.js → chunk-RZJTGBXY.js} +22 -2
  43. package/dist/{chunk-DF5BM5OY.js → chunk-SACHHTBJ.js} +207 -186
  44. package/dist/{chunk-5GDJSFCT.js → chunk-SK6NYGPD.js} +2 -2
  45. package/dist/{chunk-74OPNF7U.js → chunk-SQQBU7Z6.js} +2 -2
  46. package/dist/{chunk-Q3YJZPQI.js → chunk-SS6CYGLF.js} +3 -3
  47. package/dist/{chunk-YDUHLWI5.js → chunk-SUZOPJZC.js} +21 -7
  48. package/dist/{chunk-ZHGMRBSC.js → chunk-U5WO6MEB.js} +3 -3
  49. package/dist/{chunk-NXUN4U5P.js → chunk-VAFQS4D2.js} +2 -2
  50. package/dist/chunk-W3ZLE4PO.js +77 -0
  51. package/dist/{chunk-RR4WCGNB.js → chunk-WYEST76H.js} +7 -7
  52. package/dist/{chunk-ODH3XHMI.js → chunk-ZEUMWHOH.js} +5 -5
  53. package/dist/{chunk-ZIRQZUZY.js → chunk-ZHDXLDLA.js} +3 -3
  54. package/dist/{chunk-IFWGG2M2.js → chunk-ZSHS46V5.js} +2 -0
  55. package/dist/{conductor-JFABMXDA.js → conductor-D4KFZF54.js} +6 -6
  56. package/dist/{conductor-SPZOTC7W.js → conductor-KB7L7NQN.js} +7 -7
  57. package/dist/{coordinator-prompt-K66RGNWK.js → coordinator-prompt-OBXEC63K.js} +4 -4
  58. package/dist/{coordinator-prompt-Z6UFHFDH.js → coordinator-prompt-SUO4FRY3.js} +4 -4
  59. package/dist/{cursor-recover-TEBFKAZ4.js → cursor-recover-7F46PAJ5.js} +1 -1
  60. package/dist/{cursor-recover-OWPQUHFH.js → cursor-recover-BDIAZIE3.js} +1 -1
  61. package/dist/{global-workspace-A6UO6PJV.js → global-workspace-UHU2FXTY.js} +4 -4
  62. package/dist/{global-workspace-RHEO55B4.js → global-workspace-Z2SURNHA.js} +4 -4
  63. package/dist/index.cjs +21112 -20864
  64. package/dist/index.d.cts +25 -9
  65. package/dist/index.d.ts +25 -9
  66. package/dist/index.js +343 -1678
  67. package/dist/linear-HFUOUJ5H.js +67 -0
  68. package/dist/linear-SIHEBU3X.js +69 -0
  69. package/dist/linear-list-63I7QUC6.js +6 -0
  70. package/dist/linear-list-JFV3EES6.js +8 -0
  71. package/dist/mcp/run-stdio.cjs +15654 -15398
  72. package/dist/mcp/run-stdio.js +67 -1247
  73. package/dist/{optional-cli-RMMS5L6F.js → optional-cli-U2GUA2X6.js} +3 -3
  74. package/dist/{optional-cli-Y3Z3GIPZ.js → optional-cli-ULDP3O5S.js} +4 -4
  75. package/dist/{orchestrator-7VTAUY24.js → orchestrator-3H4KNGPN.js} +15 -13
  76. package/dist/{orchestrator-2O6OJQLX.js → orchestrator-6ROMEWBP.js} +14 -12
  77. package/dist/{thread-store-3F7GR64A.js → thread-store-4ZN3DS2W.js} +1 -1
  78. package/dist/{thread-store-JRSL4HQY.js → thread-store-IC4BQXDW.js} +1 -1
  79. package/dist/{workspaces-NRKEXOYR.js → workspaces-MG5JHRKW.js} +4 -4
  80. package/dist/{workspaces-42E6AGXD.js → workspaces-QJNKEGEM.js} +4 -4
  81. package/dist/{worktree-ZDA2UJ7C.js → worktree-KSJPTHGZ.js} +3 -3
  82. package/dist/{worktree-4NGXCCUO.js → worktree-MYJYUHZM.js} +3 -3
  83. package/package.json +1 -1
@@ -3,223 +3,31 @@
3
3
  import {
4
4
  abletimeRestUrl,
5
5
  textFromAbleTimeDoc
6
- } from "./chunk-NXUN4U5P.js";
6
+ } from "./chunk-VAFQS4D2.js";
7
7
  import {
8
8
  assertAbleTimeCredential,
9
9
  callAbleTimeTool,
10
10
  normalizeAbleTimeHost,
11
11
  rewriteAbleTimeError
12
- } from "./chunk-FR3SAYUL.js";
12
+ } from "./chunk-AQEKPJWU.js";
13
+ import {
14
+ downloadAuthenticatedFile,
15
+ extractHttpUrls,
16
+ hostMatches,
17
+ issueAttachmentWorktreePath,
18
+ looksLikeFileUrl,
19
+ looksLikeHttpUrl,
20
+ mergeIssueAttachments
21
+ } from "./chunk-F6GE7V3Q.js";
13
22
  import {
14
23
  httpFetch
15
24
  } from "./chunk-2GPQZZLY.js";
16
- import {
17
- ATTACHMENTS_DIR,
18
- attachmentsGitignoreBody
19
- } from "./chunk-N2WW62LF.js";
20
25
  import {
21
26
  getAbleTimeAccessToken,
22
27
  getAbleTimeHost,
23
28
  isAbleTimeConnected,
24
29
  saveAbleTimeConnection
25
- } from "./chunk-OIHRC2ZJ.js";
26
-
27
- // src/integrations/issue-attachments.ts
28
- import { existsSync, mkdirSync, writeFileSync } from "fs";
29
- import { basename, extname, join } from "path";
30
- import { randomUUID } from "crypto";
31
- var ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
32
- var MARKDOWN_IMAGE_RE = /!\[[^\]]*]\(\s*<?(https?:\/\/[^)\s>]+)>?/gi;
33
- var MARKDOWN_LINK_RE = /(?<!!)\[[^\]]*]\(\s*<?(https?:\/\/[^)\s>]+)>?/gi;
34
- var HTML_SRC_RE = /\b(?:src|href)\s*=\s*["'](https?:\/\/[^"']+)["']/gi;
35
- var BARE_URL_RE = /https?:\/\/[^\s<>"'`)]+/gi;
36
- var EXT_BY_MIME = {
37
- "image/png": ".png",
38
- "image/jpeg": ".jpg",
39
- "image/jpg": ".jpg",
40
- "image/gif": ".gif",
41
- "image/webp": ".webp",
42
- "image/svg+xml": ".svg",
43
- "application/pdf": ".pdf",
44
- "text/plain": ".txt",
45
- "text/markdown": ".md",
46
- "application/json": ".json",
47
- "application/zip": ".zip"
48
- };
49
- function issueAttachmentWorktreePath(repoPath) {
50
- const raw = (repoPath ?? "").trim();
51
- return raw || process.cwd();
52
- }
53
- function ensureIssueAttachmentsDir(worktreePath) {
54
- const dir = join(worktreePath, ATTACHMENTS_DIR);
55
- mkdirSync(dir, { recursive: true });
56
- const gi = join(dir, ".gitignore");
57
- if (!existsSync(gi)) {
58
- writeFileSync(gi, attachmentsGitignoreBody(), "utf8");
59
- }
60
- return dir;
61
- }
62
- function hostnameOf(url) {
63
- try {
64
- return new URL(url).hostname.toLowerCase();
65
- } catch {
66
- return "";
67
- }
68
- }
69
- function hostMatches(url, suffixes) {
70
- const host = hostnameOf(url);
71
- if (!host) return false;
72
- return suffixes.some((suffix) => host === suffix || host.endsWith(`.${suffix}`));
73
- }
74
- function looksLikeHttpUrl(value) {
75
- return /^https?:\/\//i.test(value.trim());
76
- }
77
- function looksLikeFileUrl(url) {
78
- try {
79
- const path = new URL(url).pathname;
80
- return /\.[a-z0-9]{2,8}$/i.test(path);
81
- } catch {
82
- return false;
83
- }
84
- }
85
- function filenameFromUrl(url, fallback = "attachment") {
86
- try {
87
- const parsed = new URL(url);
88
- const last = decodeURIComponent(basename(parsed.pathname)).replace(/[/\\]/g, "_");
89
- if (last && last !== "/" && last !== ".") return last;
90
- } catch {
91
- }
92
- return fallback;
93
- }
94
- function filenameFromContentDisposition(header) {
95
- if (!header) return void 0;
96
- const star = header.match(/filename\*\s*=\s*(?:UTF-8''|utf-8'')([^;]+)/i);
97
- if (star?.[1]) {
98
- try {
99
- return decodeURIComponent(star[1].trim().replace(/^["']|["']$/g, ""));
100
- } catch {
101
- return star[1].trim().replace(/^["']|["']$/g, "");
102
- }
103
- }
104
- const plain = header.match(/filename\s*=\s*("(?:\\.|[^"])*"|[^;]+)/i);
105
- if (!plain?.[1]) return void 0;
106
- return plain[1].trim().replace(/^["']|["']$/g, "").replace(/\\"/g, '"');
107
- }
108
- function uniqueAttachmentFileName(dir, originalName) {
109
- const safe = originalName.replace(/[/\\]/g, "_").replace(/^\.+/, "") || "file";
110
- if (!existsSync(join(dir, safe))) return safe;
111
- const ext = extname(safe);
112
- const stem = ext ? safe.slice(0, -ext.length) : safe;
113
- for (let i = 1; i < 1e4; i++) {
114
- const candidate = `${stem}-${i}${ext}`;
115
- if (!existsSync(join(dir, candidate))) return candidate;
116
- }
117
- return `${stem}-${randomUUID()}${ext}`;
118
- }
119
- function extractHttpUrls(markdown) {
120
- const text = markdown ?? "";
121
- if (!text.trim()) return [];
122
- const found = [];
123
- const seen = /* @__PURE__ */ new Set();
124
- const push = (raw) => {
125
- const url = raw.replace(/[).,;]+$/, "");
126
- if (!looksLikeHttpUrl(url) || seen.has(url)) return;
127
- seen.add(url);
128
- found.push(url);
129
- };
130
- for (const re of [MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE]) {
131
- re.lastIndex = 0;
132
- let match;
133
- while (match = re.exec(text)) {
134
- if (match[1]) push(match[1]);
135
- else if (re === BARE_URL_RE) push(match[0]);
136
- }
137
- }
138
- return found;
139
- }
140
- function mergeIssueAttachments(items) {
141
- const out = [];
142
- const seen = /* @__PURE__ */ new Set();
143
- for (const item of items) {
144
- if (!item) continue;
145
- const url = item.url.trim();
146
- const id = item.id.trim() || url;
147
- if (!id && !url) continue;
148
- const key = url || id;
149
- if (seen.has(key) || seen.has(id)) continue;
150
- seen.add(key);
151
- if (id !== key) seen.add(id);
152
- out.push({
153
- id: id || url,
154
- name: item.name.trim() || filenameFromUrl(url),
155
- url,
156
- ...item.contentType ? { contentType: item.contentType } : {},
157
- ...item.sourceType ? { sourceType: item.sourceType } : {}
158
- });
159
- }
160
- return out;
161
- }
162
- function extensionForContentType(contentType, name) {
163
- if (extname(name)) return name;
164
- const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
165
- const ext = EXT_BY_MIME[mime];
166
- return ext ? `${name}${ext}` : name;
167
- }
168
- function assertNotHtml(contentType, url) {
169
- const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
170
- if (mime === "text/html" || mime === "application/xhtml+xml") {
171
- throw new Error(
172
- `Attachment at ${url} returned HTML, not a file. Use the listed file URL (uploads / user-attachments), not a ticket or PR page.`
173
- );
174
- }
175
- }
176
- async function downloadAuthenticatedFile(opts) {
177
- const url = opts.url.trim();
178
- if (!looksLikeHttpUrl(url)) {
179
- throw new Error(`Attachment URL must be http(s) (got ${url || "empty"})`);
180
- }
181
- const res = await httpFetch(url, {
182
- method: "GET",
183
- redirect: "follow",
184
- headers: {
185
- Accept: "*/*",
186
- ...opts.headers ?? {}
187
- }
188
- });
189
- if (!res.ok) {
190
- const detail = await res.text().catch(() => "");
191
- throw new Error(
192
- `Attachment download failed ${res.status}${detail ? `: ${detail.slice(0, 200)}` : ""} (${url})`
193
- );
194
- }
195
- const contentType = res.headers.get("content-type") ?? void 0;
196
- assertNotHtml(contentType, url);
197
- const lengthHeader = res.headers.get("content-length");
198
- if (lengthHeader) {
199
- const length = Number(lengthHeader);
200
- if (Number.isFinite(length) && length > ISSUE_ATTACHMENT_MAX_BYTES) {
201
- throw new Error(
202
- `Attachment is ${length} bytes; max is ${ISSUE_ATTACHMENT_MAX_BYTES} (${url})`
203
- );
204
- }
205
- }
206
- const buf = Buffer.from(await res.arrayBuffer());
207
- if (buf.length > ISSUE_ATTACHMENT_MAX_BYTES) {
208
- throw new Error(
209
- `Attachment is ${buf.length} bytes; max is ${ISSUE_ATTACHMENT_MAX_BYTES} (${url})`
210
- );
211
- }
212
- const dir = ensureIssueAttachmentsDir(opts.worktreePath);
213
- const hinted = opts.name?.trim() || filenameFromContentDisposition(res.headers.get("content-disposition")) || filenameFromUrl(url);
214
- const name = uniqueAttachmentFileName(dir, extensionForContentType(contentType, hinted));
215
- writeFileSync(join(dir, name), buf);
216
- return {
217
- name,
218
- path: `${ATTACHMENTS_DIR}/${name}`,
219
- bytes: buf.length,
220
- ...contentType ? { contentType } : {}
221
- };
222
- }
30
+ } from "./chunk-CI4M54LS.js";
223
31
 
224
32
  // src/integrations/abletime-attachments.ts
225
33
  function asRecord(value) {
@@ -841,12 +649,6 @@ async function verifyAbleTimeConnection(input) {
841
649
  }
842
650
 
843
651
  export {
844
- issueAttachmentWorktreePath,
845
- hostMatches,
846
- looksLikeHttpUrl,
847
- extractHttpUrls,
848
- mergeIssueAttachments,
849
- downloadAuthenticatedFile,
850
652
  downloadAbleTimeAttachment,
851
653
  taskUrl,
852
654
  mapAbleTimeTask,
@@ -5,7 +5,7 @@ import {
5
5
  resolveGithubRepoSlug,
6
6
  resolveOriginRemote,
7
7
  resolveRepoRoot
8
- } from "./chunk-FUFNMUNR.js";
8
+ } from "./chunk-BTF3ATST.js";
9
9
  import {
10
10
  allocateTeamName,
11
11
  createEmptyThread,
@@ -14,7 +14,7 @@ import {
14
14
  teamSlugFromName,
15
15
  updateThread,
16
16
  writeThread
17
- } from "./chunk-IFWGG2M2.js";
17
+ } from "./chunk-ZSHS46V5.js";
18
18
  import {
19
19
  ATTACHMENTS_DIR,
20
20
  attachmentsGitignoreBody
@@ -31,7 +31,7 @@ import {
31
31
  resolveThreadDefaults,
32
32
  resolveViewerProfile,
33
33
  resolveViewerProfileForRepo
34
- } from "./chunk-EJ66LEKH.js";
34
+ } from "./chunk-RZJTGBXY.js";
35
35
  import {
36
36
  appDataDir,
37
37
  globalAgentCwd,
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  getAbleTimeHost,
8
8
  isAbleTimeConnected
9
- } from "./chunk-OIHRC2ZJ.js";
9
+ } from "./chunk-CI4M54LS.js";
10
10
 
11
11
  // src/integrations/abletime-mcp.ts
12
12
  var DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
@@ -155,7 +155,7 @@ function textsFromUnknown(value) {
155
155
  return "";
156
156
  }
157
157
  async function abletimeMcpRequest(method, params, opts) {
158
- const rawToken = opts?.token ?? await import("./abletime-oauth-4TTCR3CT.js").then((m) => m.ensureAbleTimeAccessToken());
158
+ const rawToken = opts?.token ?? await import("./abletime-oauth-P4ZFBEAL.js").then((m) => m.ensureAbleTimeAccessToken());
159
159
  if (!rawToken?.trim()) {
160
160
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
161
161
  }
@@ -226,9 +226,9 @@ async function callAbleTimeTool(name, args = {}, opts) {
226
226
  if (!opts?.token && !isAbleTimeConnected()) {
227
227
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
228
228
  }
229
- const rawToken = opts?.token ?? await import("./abletime-oauth-4TTCR3CT.js").then((m) => m.ensureAbleTimeAccessToken());
229
+ const rawToken = opts?.token ?? await import("./abletime-oauth-P4ZFBEAL.js").then((m) => m.ensureAbleTimeAccessToken());
230
230
  if (rawToken && ableTimeUsesRest(rawToken)) {
231
- const { callAbleTimeRestTool } = await import("./abletime-rest-3TG4G7LW.js");
231
+ const { callAbleTimeRestTool } = await import("./abletime-rest-DLV7EIWS.js");
232
232
  return callAbleTimeRestTool(name, args, { ...opts, token: rawToken });
233
233
  }
234
234
  const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
@@ -244,7 +244,7 @@ async function callAbleTimeTool(name, args = {}, opts) {
244
244
  return await call();
245
245
  }
246
246
  if (rawToken && ableTimeUsesRest(rawToken) && isAbleTimeRejectedCredential(message)) {
247
- const { callAbleTimeRestTool } = await import("./abletime-rest-3TG4G7LW.js");
247
+ const { callAbleTimeRestTool } = await import("./abletime-rest-DLV7EIWS.js");
248
248
  return callAbleTimeRestTool(name, args, { ...opts, token: rawToken });
249
249
  }
250
250
  throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
@@ -125,7 +125,7 @@ async function connectedCliPresence(integrations) {
125
125
  const ids = connectedOptionalServices(integrations).filter((spec) => spec.cli).map((spec) => spec.id);
126
126
  if (ids.length === 0) return void 0;
127
127
  try {
128
- const { optionalCliPresence } = await import("./optional-cli-RMMS5L6F.js");
128
+ const { optionalCliPresence } = await import("./optional-cli-U2GUA2X6.js");
129
129
  return await optionalCliPresence(ids);
130
130
  } catch {
131
131
  return void 0;
@@ -4,7 +4,7 @@ import {
4
4
  normalizeTakenSlug,
5
5
  normalizeWorktreePath,
6
6
  takenSlugsFromThread
7
- } from "./chunk-IFWGG2M2.js";
7
+ } from "./chunk-ZSHS46V5.js";
8
8
  import {
9
9
  ensureWorktreeSideboardIgnored
10
10
  } from "./chunk-QKHEVSB5.js";
@@ -25,7 +25,7 @@ import {
25
25
  import {
26
26
  getGithubGitAuthMode,
27
27
  getGithubPat
28
- } from "./chunk-EJ66LEKH.js";
28
+ } from "./chunk-RZJTGBXY.js";
29
29
  import {
30
30
  worktreesRoot
31
31
  } from "./chunk-H4UEFJRI.js";
@@ -291,12 +291,11 @@ function codexSandboxWritableRootsArgs(roots) {
291
291
  return ["-c", `sandbox_workspace_write.writable_roots=${JSON.stringify(abs)}`];
292
292
  }
293
293
  function codexUnattendedGitConfigArgs(sandbox, opts) {
294
- const args = [
295
- "-c",
296
- 'shell_environment_policy.inherit="all"',
297
- "-c",
298
- "shell_environment_policy.ignore_default_excludes=true"
299
- ];
294
+ const args = ["-c", 'shell_environment_policy.inherit="all"'];
295
+ for (const [key, value] of Object.entries(opts?.preserveEnv ?? {})) {
296
+ if (!/^GIT_CONFIG_KEY_\d+$/.test(key) || !value) continue;
297
+ args.push("-c", `shell_environment_policy.set.${key}=${JSON.stringify(value)}`);
298
+ }
300
299
  if (sandbox === "workspace-write") {
301
300
  args.push("-c", "sandbox_workspace_write.network_access=true");
302
301
  args.push(...codexSandboxWritableRootsArgs(opts?.writableRoots ?? []));
@@ -471,6 +471,20 @@ function fromInclusiveInputUsage(opts) {
471
471
  function requestOccupancy(u) {
472
472
  return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
473
473
  }
474
+ function subtractUsage(total, part) {
475
+ if (!total) return null;
476
+ const cacheRead = Math.max(0, (total.cacheReadTokens ?? 0) - (part.cacheReadTokens ?? 0));
477
+ const cacheWrite = Math.max(0, (total.cacheWriteTokens ?? 0) - (part.cacheWriteTokens ?? 0));
478
+ return {
479
+ inputTokens: Math.max(0, total.inputTokens - part.inputTokens),
480
+ outputTokens: Math.max(0, total.outputTokens - part.outputTokens),
481
+ cacheReadTokens: cacheRead || void 0,
482
+ cacheWriteTokens: cacheWrite || void 0,
483
+ costUsd: total.costUsd != null && part.costUsd != null ? Math.max(0, total.costUsd - part.costUsd) : total.costUsd,
484
+ sessionCostUsd: total.sessionCostUsd,
485
+ lastRequestTokens: total.lastRequestTokens
486
+ };
487
+ }
474
488
  function mergeUsage(a, b) {
475
489
  return {
476
490
  inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
@@ -562,7 +576,8 @@ function previousCostBaseline(messages) {
562
576
  }
563
577
  return {
564
578
  sessionCostUsd: lastSession ?? summed,
565
- lastCostUsd: lastCost
579
+ lastCostUsd: lastCost,
580
+ hasSessionSnapshot: lastSession != null
566
581
  };
567
582
  }
568
583
  function turnCostUsdFromProviderSnapshot(opts) {
@@ -576,6 +591,9 @@ function turnCostUsdFromProviderSnapshot(opts) {
576
591
  if (session + USD_EPS >= previous) {
577
592
  return roundUsd(Math.max(0, session - previous));
578
593
  }
594
+ if (opts.previousHasSessionSnapshot) {
595
+ return roundUsd(session);
596
+ }
579
597
  if (last != null && session + USD_EPS >= last) {
580
598
  return roundUsd(Math.max(0, session - last));
581
599
  }
@@ -590,7 +608,8 @@ function withTurnScopedCost(usage, previousMessages) {
590
608
  reportedTurnCostUsd: usage.costUsd,
591
609
  sessionCostUsd: usage.sessionCostUsd,
592
610
  previousSessionCostUsd: baseline.sessionCostUsd,
593
- previousLastCostUsd: baseline.lastCostUsd
611
+ previousLastCostUsd: baseline.lastCostUsd,
612
+ previousHasSessionSnapshot: baseline.hasSessionSnapshot
594
613
  });
595
614
  if (costUsd == null) {
596
615
  if (usage.costUsd == null) return usage;
@@ -2371,6 +2390,7 @@ export {
2371
2390
  formatViewerContextDirective,
2372
2391
  formatViewerContextReminder,
2373
2392
  fromInclusiveInputUsage,
2393
+ subtractUsage,
2374
2394
  sumUsageList,
2375
2395
  applyTurnUsage,
2376
2396
  contextTokens,
@@ -0,0 +1,214 @@
1
+ import {
2
+ httpFetch
3
+ } from "./chunk-JAT3JFOS.js";
4
+ import {
5
+ ATTACHMENTS_DIR,
6
+ attachmentsGitignoreBody
7
+ } from "./chunk-6K64FJDD.js";
8
+
9
+ // src/integrations/issue-attachments.ts
10
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
11
+ import { basename, extname, join } from "path";
12
+ import { randomUUID } from "crypto";
13
+ var ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
14
+ var MARKDOWN_IMAGE_RE = /!\[[^\]]*]\(\s*<?(https?:\/\/[^)\s>]+)>?/gi;
15
+ var MARKDOWN_LINK_RE = /(?<!!)\[[^\]]*]\(\s*<?(https?:\/\/[^)\s>]+)>?/gi;
16
+ var HTML_SRC_RE = /\b(?:src|href)\s*=\s*["'](https?:\/\/[^"']+)["']/gi;
17
+ var BARE_URL_RE = /https?:\/\/[^\s<>"'`)]+/gi;
18
+ var EXT_BY_MIME = {
19
+ "image/png": ".png",
20
+ "image/jpeg": ".jpg",
21
+ "image/jpg": ".jpg",
22
+ "image/gif": ".gif",
23
+ "image/webp": ".webp",
24
+ "image/svg+xml": ".svg",
25
+ "application/pdf": ".pdf",
26
+ "text/plain": ".txt",
27
+ "text/markdown": ".md",
28
+ "application/json": ".json",
29
+ "application/zip": ".zip"
30
+ };
31
+ function issueAttachmentWorktreePath(repoPath) {
32
+ const raw = (repoPath ?? "").trim();
33
+ return raw || process.cwd();
34
+ }
35
+ function ensureIssueAttachmentsDir(worktreePath) {
36
+ const dir = join(worktreePath, ATTACHMENTS_DIR);
37
+ mkdirSync(dir, { recursive: true });
38
+ const gi = join(dir, ".gitignore");
39
+ if (!existsSync(gi)) {
40
+ writeFileSync(gi, attachmentsGitignoreBody(), "utf8");
41
+ }
42
+ return dir;
43
+ }
44
+ function hostnameOf(url) {
45
+ try {
46
+ return new URL(url).hostname.toLowerCase();
47
+ } catch {
48
+ return "";
49
+ }
50
+ }
51
+ function hostMatches(url, suffixes) {
52
+ const host = hostnameOf(url);
53
+ if (!host) return false;
54
+ return suffixes.some((suffix) => host === suffix || host.endsWith(`.${suffix}`));
55
+ }
56
+ function looksLikeHttpUrl(value) {
57
+ return /^https?:\/\//i.test(value.trim());
58
+ }
59
+ function looksLikeFileUrl(url) {
60
+ try {
61
+ const path = new URL(url).pathname;
62
+ return /\.[a-z0-9]{2,8}$/i.test(path);
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+ function filenameFromUrl(url, fallback = "attachment") {
68
+ try {
69
+ const parsed = new URL(url);
70
+ const last = decodeURIComponent(basename(parsed.pathname)).replace(/[/\\]/g, "_");
71
+ if (last && last !== "/" && last !== ".") return last;
72
+ } catch {
73
+ }
74
+ return fallback;
75
+ }
76
+ function filenameFromContentDisposition(header) {
77
+ if (!header) return void 0;
78
+ const star = header.match(/filename\*\s*=\s*(?:UTF-8''|utf-8'')([^;]+)/i);
79
+ if (star?.[1]) {
80
+ try {
81
+ return decodeURIComponent(star[1].trim().replace(/^["']|["']$/g, ""));
82
+ } catch {
83
+ return star[1].trim().replace(/^["']|["']$/g, "");
84
+ }
85
+ }
86
+ const plain = header.match(/filename\s*=\s*("(?:\\.|[^"])*"|[^;]+)/i);
87
+ if (!plain?.[1]) return void 0;
88
+ return plain[1].trim().replace(/^["']|["']$/g, "").replace(/\\"/g, '"');
89
+ }
90
+ function uniqueAttachmentFileName(dir, originalName) {
91
+ const safe = originalName.replace(/[/\\]/g, "_").replace(/^\.+/, "") || "file";
92
+ if (!existsSync(join(dir, safe))) return safe;
93
+ const ext = extname(safe);
94
+ const stem = ext ? safe.slice(0, -ext.length) : safe;
95
+ for (let i = 1; i < 1e4; i++) {
96
+ const candidate = `${stem}-${i}${ext}`;
97
+ if (!existsSync(join(dir, candidate))) return candidate;
98
+ }
99
+ return `${stem}-${randomUUID()}${ext}`;
100
+ }
101
+ function extractHttpUrls(markdown) {
102
+ const text = markdown ?? "";
103
+ if (!text.trim()) return [];
104
+ const found = [];
105
+ const seen = /* @__PURE__ */ new Set();
106
+ const push = (raw) => {
107
+ const url = raw.replace(/[).,;]+$/, "");
108
+ if (!looksLikeHttpUrl(url) || seen.has(url)) return;
109
+ seen.add(url);
110
+ found.push(url);
111
+ };
112
+ for (const re of [MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE]) {
113
+ re.lastIndex = 0;
114
+ let match;
115
+ while (match = re.exec(text)) {
116
+ if (match[1]) push(match[1]);
117
+ else if (re === BARE_URL_RE) push(match[0]);
118
+ }
119
+ }
120
+ return found;
121
+ }
122
+ function mergeIssueAttachments(items) {
123
+ const out = [];
124
+ const seen = /* @__PURE__ */ new Set();
125
+ for (const item of items) {
126
+ if (!item) continue;
127
+ const url = item.url.trim();
128
+ const id = item.id.trim() || url;
129
+ if (!id && !url) continue;
130
+ const key = url || id;
131
+ if (seen.has(key) || seen.has(id)) continue;
132
+ seen.add(key);
133
+ if (id !== key) seen.add(id);
134
+ out.push({
135
+ id: id || url,
136
+ name: item.name.trim() || filenameFromUrl(url),
137
+ url,
138
+ ...item.contentType ? { contentType: item.contentType } : {},
139
+ ...item.sourceType ? { sourceType: item.sourceType } : {}
140
+ });
141
+ }
142
+ return out;
143
+ }
144
+ function extensionForContentType(contentType, name) {
145
+ if (extname(name)) return name;
146
+ const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
147
+ const ext = EXT_BY_MIME[mime];
148
+ return ext ? `${name}${ext}` : name;
149
+ }
150
+ function assertNotHtml(contentType, url) {
151
+ const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
152
+ if (mime === "text/html" || mime === "application/xhtml+xml") {
153
+ throw new Error(
154
+ `Attachment at ${url} returned HTML, not a file. Use the listed file URL (uploads / user-attachments), not a ticket or PR page.`
155
+ );
156
+ }
157
+ }
158
+ async function downloadAuthenticatedFile(opts) {
159
+ const url = opts.url.trim();
160
+ if (!looksLikeHttpUrl(url)) {
161
+ throw new Error(`Attachment URL must be http(s) (got ${url || "empty"})`);
162
+ }
163
+ const res = await httpFetch(url, {
164
+ method: "GET",
165
+ redirect: "follow",
166
+ headers: {
167
+ Accept: "*/*",
168
+ ...opts.headers ?? {}
169
+ }
170
+ });
171
+ if (!res.ok) {
172
+ const detail = await res.text().catch(() => "");
173
+ throw new Error(
174
+ `Attachment download failed ${res.status}${detail ? `: ${detail.slice(0, 200)}` : ""} (${url})`
175
+ );
176
+ }
177
+ const contentType = res.headers.get("content-type") ?? void 0;
178
+ assertNotHtml(contentType, url);
179
+ const lengthHeader = res.headers.get("content-length");
180
+ if (lengthHeader) {
181
+ const length = Number(lengthHeader);
182
+ if (Number.isFinite(length) && length > ISSUE_ATTACHMENT_MAX_BYTES) {
183
+ throw new Error(
184
+ `Attachment is ${length} bytes; max is ${ISSUE_ATTACHMENT_MAX_BYTES} (${url})`
185
+ );
186
+ }
187
+ }
188
+ const buf = Buffer.from(await res.arrayBuffer());
189
+ if (buf.length > ISSUE_ATTACHMENT_MAX_BYTES) {
190
+ throw new Error(
191
+ `Attachment is ${buf.length} bytes; max is ${ISSUE_ATTACHMENT_MAX_BYTES} (${url})`
192
+ );
193
+ }
194
+ const dir = ensureIssueAttachmentsDir(opts.worktreePath);
195
+ const hinted = opts.name?.trim() || filenameFromContentDisposition(res.headers.get("content-disposition")) || filenameFromUrl(url);
196
+ const name = uniqueAttachmentFileName(dir, extensionForContentType(contentType, hinted));
197
+ writeFileSync(join(dir, name), buf);
198
+ return {
199
+ name,
200
+ path: `${ATTACHMENTS_DIR}/${name}`,
201
+ bytes: buf.length,
202
+ ...contentType ? { contentType } : {}
203
+ };
204
+ }
205
+
206
+ export {
207
+ issueAttachmentWorktreePath,
208
+ hostMatches,
209
+ looksLikeHttpUrl,
210
+ looksLikeFileUrl,
211
+ extractHttpUrls,
212
+ mergeIssueAttachments,
213
+ downloadAuthenticatedFile
214
+ };
@@ -20,14 +20,14 @@ import {
20
20
  import {
21
21
  getGithubGitAuthMode,
22
22
  getGithubPat
23
- } from "./chunk-OIHRC2ZJ.js";
23
+ } from "./chunk-CI4M54LS.js";
24
24
  import {
25
25
  allocateTeamName,
26
26
  listThreads,
27
27
  normalizeTakenSlug,
28
28
  normalizeWorktreePath,
29
29
  takenSlugsFromThread
30
- } from "./chunk-B4CDSSRN.js";
30
+ } from "./chunk-Q3FHTGIW.js";
31
31
  import {
32
32
  worktreesRoot
33
33
  } from "./chunk-WOU343SI.js";
@@ -282,12 +282,11 @@ function codexSandboxWritableRootsArgs(roots) {
282
282
  return ["-c", `sandbox_workspace_write.writable_roots=${JSON.stringify(abs)}`];
283
283
  }
284
284
  function codexUnattendedGitConfigArgs(sandbox, opts) {
285
- const args = [
286
- "-c",
287
- 'shell_environment_policy.inherit="all"',
288
- "-c",
289
- "shell_environment_policy.ignore_default_excludes=true"
290
- ];
285
+ const args = ["-c", 'shell_environment_policy.inherit="all"'];
286
+ for (const [key, value] of Object.entries(opts?.preserveEnv ?? {})) {
287
+ if (!/^GIT_CONFIG_KEY_\d+$/.test(key) || !value) continue;
288
+ args.push("-c", `shell_environment_policy.set.${key}=${JSON.stringify(value)}`);
289
+ }
291
290
  if (sandbox === "workspace-write") {
292
291
  args.push("-c", "sandbox_workspace_write.network_access=true");
293
292
  args.push(...codexSandboxWritableRootsArgs(opts?.writableRoots ?? []));