@scalequality/cli 0.4.9-rc.1 → 0.4.9-rc.3

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/connect.cjs CHANGED
@@ -33,123 +33,11 @@ var import_os4 = require("os");
33
33
  var import_path16 = require("path");
34
34
  var import_url = require("url");
35
35
 
36
- // src/application/services/workspaceSandbox/reasoning.ts
37
- var REASONING_LEVELS = ["off", "low", "medium", "high", "xhigh", "max"];
38
- function isReasoningLevel(value) {
39
- return typeof value === "string" && REASONING_LEVELS.includes(value);
40
- }
41
- function parseReasoningCapability(raw) {
42
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
43
- const r = raw;
44
- if (!Array.isArray(r.levels)) return null;
45
- const listed = [...r.levels, ...r.off === true ? ["off"] : []];
46
- const levels = REASONING_LEVELS.filter((l) => listed.includes(l));
47
- if (!levels.length) return null;
48
- const def = isReasoningLevel(r.default) && levels.includes(r.default) ? r.default : levels[0];
49
- return { levels, default: def };
50
- }
51
- function effectiveReasoning(requested, capability) {
52
- if (!capability) return null;
53
- if (requested && capability.levels.includes(requested)) return requested;
54
- return capability.default;
55
- }
56
- function isMaxMode(level, capability) {
57
- if (!level || level === "off" || !capability) return false;
58
- return capability.levels[capability.levels.length - 1] === level;
59
- }
60
- function turnReasoning(level, capability) {
61
- if (!capability || !level) return { options: {}, forceNoThinking: true, outputCeiling: false };
62
- if (level === "off") return { options: { thinking: { type: "disabled" } }, forceNoThinking: true, outputCeiling: false };
63
- return { options: { effort: level, thinking: { type: "adaptive" } }, forceNoThinking: false, outputCeiling: isMaxMode(level, capability) };
64
- }
65
- function engineModelCapabilities(aliases) {
66
- const entries = ["-mid_conv_system"];
67
- const seen = /* @__PURE__ */ new Set();
68
- for (const { alias, reasoning } of aliases) {
69
- const name = alias.trim();
70
- if (!name || seen.has(name) || /[;=,]/.test(name)) continue;
71
- seen.add(name);
72
- entries.push(reasoning ? `${name}=effort,${reasoning.levels.includes("max") ? "" : "-"}max_effort,${reasoning.levels.includes("xhigh") ? "" : "-"}xhigh_effort` : `${name}=-effort,-max_effort,-xhigh_effort`);
73
- }
74
- return entries.join(";");
75
- }
76
-
77
- // src/application/services/workspaceSandbox/autoRouting.ts
78
- var LONG_INSTRUCTION_CHARS = 3e3;
79
- var LONG_CODE_LINES = 60;
80
- var MANY_FILES = 5;
81
- var HARD_WORK = [
82
- [/\b(refactor|refator|refactoriz)\w*\b[\s\S]{0,80}\b(across|between|all|every|entre|todos|todas|varios|varias|modul|servic|packages?|pacotes?|layers?|camadas?)/, "refactor across modules"],
83
- [/\b(architect|arquitet|arquitect|redesign|re-architect)\w*/, "architecture"],
84
- [/\b(root cause|causa raiz|causa-raiz|debug|depur|investigat|investig|stack ?trace|flaky|race condition|condicao de corrida|memory leak|vazamento de memoria|deadlock|intermittent|intermitente)\w*/, "debugging, root cause"],
85
- [/\b(migrat|migra(c|t)a?o|migrar|migre|upgrade\b[\s\S]{0,40}\b(from|to|de|para)\b|port (this|the|it)\b[\s\S]{0,40}\bto\b)/, "migration"],
86
- [/\b(vulnerab|cve-\d|security (fix|issue|flaw|hole|bug)|injection|injecao|xss|csrf|ssrf|rce\b|seguranca|seguridad|exploit)\w*/, "security fix"],
87
- [/\b(every|all|each|todos os|todas as|cada|todos los|todas las) (the )?(files?|modules?|services?|endpoints?|packages?|arquivos?|modulos?|servicos?|archivos?)\b|\b(across|throughout) (the )?(whole )?(codebase|repo|repository|project|modules|services)\b|\b(codebase|base de codigo)[- ](wide|inteira|toda)\b/, "many files"],
88
- [/\b(refactor|refator|refactoriz)\w*/, "refactor"],
89
- [/\b(step[- ]by[- ]step|passo a passo|paso a paso|multi[- ]step|end[- ]to[- ]end|threat model|performance (issue|regression|problem)|regressao de performance|rewrite|reescrev|reescrib)\w*/, "multi-step work"]
90
- ];
91
- var normalize = (text3) => text3.toLowerCase().normalize("NFKD").replace(new RegExp("\\p{M}", "gu"), "");
92
- function namedFiles(text3) {
93
- const found = /* @__PURE__ */ new Set();
94
- for (const m of text3.matchAll(/(?:^|[\s`'"(\[])((?:\.{0,2}\/)?[\w.-]+(?:\/[\w.-]+)+\/?|[\w-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|rb|php|cs|cpp|c|h|swift|scala|sql|yml|yaml|json|tf|vue|svelte))(?=$|[\s`'"),.:;\]])/g)) {
95
- found.add(m[1].replace(/^\.\//, ""));
96
- }
97
- return found.size;
98
- }
99
- function classifyCodeTurn(s) {
100
- if (s.maxMode) return { hard: true, reason: "Max Mode" };
101
- if (s.previousTrouble) return { hard: true, reason: s.previousTrouble };
102
- const text3 = s.content;
103
- const codeLines = [...text3.matchAll(/```[\s\S]*?```/g)].reduce((n, block) => n + block[0].split("\n").length, 0);
104
- if (codeLines > LONG_CODE_LINES) return { hard: true, reason: `${codeLines} lines of code in the request` };
105
- if (text3.length > LONG_INSTRUCTION_CHARS) return { hard: true, reason: `long instruction (${text3.length} characters)` };
106
- const plain = normalize(text3);
107
- for (const [re, reason] of HARD_WORK) if (re.test(plain)) return { hard: true, reason };
108
- const files = namedFiles(text3);
109
- if (files >= MANY_FILES) return { hard: true, reason: `${files} files named` };
110
- return { hard: false, reason: text3.length > 600 || codeLines > 0 ? "bounded task" : "short, direct request" };
111
- }
112
- var STREAM_REASONS = ["ORG_OUTPUT_TEXT_RULES", "SEMANTIC_INSPECTOR", "APPLICATION_OUTPUT_TEXT_RULES", "DESTINATION_NO_STREAM"];
113
- function routeAutoTurn(boot, signals) {
114
- if (boot.model !== "sq-auto") return null;
115
- const aliases = boot.runtime.aliases ?? [];
116
- if (!aliases.some((a) => a.tier)) return null;
117
- const primary = boot.runtime.primaryModel || aliases.find((a) => a.tier === "MEDIUM")?.alias || null;
118
- if (!primary) return null;
119
- const decision = classifyCodeTurn(signals);
120
- const max = decision.hard ? aliases.find((a) => a.tier === "COMPLEX") ?? aliases.find((a) => a.alias === "sq-auto-max") : void 0;
121
- const chosen = max ?? aliases.find((a) => a.alias === primary) ?? { alias: primary, reasoning: null, tier: "MEDIUM" };
122
- const fallback = decision.hard && !max;
123
- const kind = decision.hard ? "hard task" : "everyday task";
124
- return {
125
- alias: chosen.alias,
126
- tier: chosen.tier ?? null,
127
- hard: decision.hard,
128
- reason: decision.reason,
129
- label: `SQ Auto \xB7 ${kind} \xB7 ${decision.reason}${fallback ? " (no hard-task model available, main model used)" : ""}`,
130
- capability: chosen.reasoning ?? null,
131
- maxOutputTokens: typeof chosen.maxOutputTokens === "number" && chosen.maxOutputTokens > 0 ? chosen.maxOutputTokens : null,
132
- fallback
133
- };
134
- }
135
- function credentialAlias(raw) {
136
- const tier = raw.tier === "LIGHT" || raw.tier === "MEDIUM" || raw.tier === "COMPLEX" ? raw.tier : null;
137
- const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? Math.trunc(v) : null;
138
- const max = positive(raw.maxOutputTokens), organization = positive(raw.organizationMaxOutputTokens);
139
- const source = raw.outputLimitSource === "ORGANIZATION" || raw.outputLimitSource === "MODEL" || raw.outputLimitSource === "PLATFORM" ? raw.outputLimitSource : null;
140
- const s = raw.stream && typeof raw.stream === "object" ? raw.stream : null;
141
- const mode = s && (s.mode === "LIVE" || s.mode === "BUFFERED" || s.mode === "BUFFERED_POLICY") ? s.mode : null;
142
- const reason = s && STREAM_REASONS.includes(s.reason) ? s.reason : null;
143
- return {
144
- alias: String(raw.alias),
145
- reasoning: parseReasoningCapability(raw.reasoning),
146
- ...tier ? { tier } : {},
147
- ...max ? { maxOutputTokens: max } : {},
148
- ...source ? { outputLimitSource: source } : {},
149
- ...organization ? { organizationMaxOutputTokens: organization } : {},
150
- ...mode ? { stream: { mode, ...reason ? { reason } : {} } } : {}
151
- };
152
- }
36
+ // src/application/services/workspaceSandbox/importers.ts
37
+ var import_fs = require("fs");
38
+ var import_promises = require("fs/promises");
39
+ var import_path = require("path");
40
+ var import_readline = require("readline");
153
41
 
154
42
  // src/application/services/workspaceSandbox/machineShared.ts
155
43
  var USER_CODE_ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789";
@@ -229,790 +117,902 @@ function localRepoId(rel) {
229
117
  return `local:${rel}`;
230
118
  }
231
119
 
232
- // src/application/services/workspaceSandbox/SessionTransport.ts
233
- var SessionGoneError = class extends Error {
234
- constructor(status) {
235
- super(`session unavailable (${status})`);
236
- this.status = status;
237
- this.name = "SessionGoneError";
120
+ // src/application/services/workspaceSandbox/secretScrubber.ts
121
+ var R = (kind) => `[REDACTED:${kind}]`;
122
+ function looksLikeSecretValue(v) {
123
+ if (v.length < 8) return false;
124
+ if (/^\[REDACTED:/.test(v)) return false;
125
+ if (/^(true|false|null|undefined|none|nil|string|number|boolean|required|optional|redacted|changeme|example|placeholder|xxx+|\*+)$/i.test(v)) return false;
126
+ if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)+(\(\))?$/.test(v) || /^[A-Za-z_$][\w$]*\(\)?$/.test(v)) return false;
127
+ if (/^(\$\{?[A-Za-z_][\w]*\}?|\{\{.*\}\}|<[^>]*>|%[A-Za-z_]+%)$/.test(v)) return false;
128
+ if (/^[A-Za-z_]+$/.test(v) && v.length < 24) return false;
129
+ return true;
130
+ }
131
+ var RULES = [
132
+ { kind: "PRIVATE_KEY", re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g },
133
+ { kind: "CONNECTION_STRING", re: /\b([a-z][a-z0-9+.-]{1,30}:\/\/)([^\s:@/'"]{1,200}):([^\s@/'"]{1,300})@/gi, replace: (_m, scheme, user) => `${scheme}${user}:${R("PASSWORD")}@` },
134
+ { kind: "AWS_ACCESS_KEY", re: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA|AIPA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
135
+ { kind: "AWS_SECRET_KEY", re: /\b(aws_?secret_?access_?key|aws_?secret|secretAccessKey)(["']?\s*[:=]\s*["']?)([A-Za-z0-9/+=]{40})\b/gi, replace: (_m, k, sep11) => `${k}${sep11}${R("AWS_SECRET_KEY")}` },
136
+ { kind: "ANTHROPIC_KEY", re: /\bsk-ant-[a-z]{2,10}\d{0,3}-[A-Za-z0-9_-]{20,}/g },
137
+ { kind: "OPENAI_KEY", re: /\bsk-(?:proj-|svcacct-|admin-|None-)?[A-Za-z0-9_-]{20,}/g },
138
+ { kind: "GITHUB_TOKEN", re: /\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{22,255})\b/g },
139
+ { kind: "GITLAB_TOKEN", re: /\bgl(?:pat|dt|rt|ptt|ft|cbt|imt|oas|soat|agent)-[A-Za-z0-9_-]{20,}/g },
140
+ { kind: "SLACK_TOKEN", re: /\bxox[abposre]-[A-Za-z0-9-]{10,}/g },
141
+ { kind: "SLACK_WEBHOOK", re: /https:\/\/hooks\.slack\.com\/(?:services|workflows|triggers)\/[A-Za-z0-9_/-]{20,}/g },
142
+ { kind: "STRIPE_KEY", re: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g },
143
+ { kind: "STRIPE_WEBHOOK_SECRET", re: /\bwhsec_[A-Za-z0-9]{24,}\b/g },
144
+ { kind: "GOOGLE_API_KEY", re: /\bAIza[0-9A-Za-z_-]{35}\b/g },
145
+ { kind: "JWT", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g },
146
+ { kind: "BEARER_TOKEN", re: /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{20,})/g, replace: (_m, p) => `${p}${R("BEARER_TOKEN")}` },
147
+ {
148
+ kind: "ASSIGNED_SECRET",
149
+ // password=..., "api_key": "...", SECRET_TOKEN: ..., --token ... (key names that say "secret").
150
+ re: /\b([A-Za-z0-9_.-]*(?:passw(?:or)?d|pwd|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key|client[_-]?secret|credential|auth[_-]?key)[A-Za-z0-9_-]*)(["']?\s*(?:=|:|\s)\s*)(["']?)([^\s"'`,;)}\]]{8,500})\3/gi,
151
+ replace: (_m, key, sep11, quote, value) => looksLikeSecretValue(value) ? `${key}${sep11}${quote}${R("SECRET")}${quote}` : null
238
152
  }
239
- status;
240
- };
241
- var TransportError = class extends Error {
242
- constructor(message, status, retryable) {
243
- super(message);
244
- this.status = status;
245
- this.retryable = retryable;
246
- this.name = "TransportError";
153
+ ];
154
+ function scrubSecrets(input) {
155
+ if (!input) return { text: input, count: 0 };
156
+ let text3 = input;
157
+ let count = 0;
158
+ for (const rule of RULES) {
159
+ text3 = text3.replace(rule.re, (...args) => {
160
+ const match = args[0];
161
+ const groups = args.slice(1, -2).map((g) => typeof g === "string" ? g : "");
162
+ if (!rule.replace) {
163
+ count++;
164
+ return R(rule.kind);
165
+ }
166
+ const out2 = rule.replace(match, ...groups);
167
+ if (out2 === null) return match;
168
+ count++;
169
+ return out2;
170
+ });
247
171
  }
248
- status;
249
- retryable;
250
- /** The API's error code (e.g. REPOSITORY_NOT_IN_SCOPE), when it sent one. */
251
- code;
252
- };
172
+ return { text: text3, count };
173
+ }
174
+ function countSecrets(input) {
175
+ return scrubSecrets(input).count;
176
+ }
253
177
 
254
- // src/application/services/workspaceSandbox/HttpSessionTransport.ts
255
- var GONE = [401, 403, 404, 410];
256
- var HttpSessionTransport = class {
257
- constructor(opts) {
258
- this.opts = opts;
259
- this.fetchImpl = opts.fetchImpl ?? fetch;
260
- this.gone = new Set(opts.goneStatuses ?? GONE);
261
- const base = opts.baseUrl.replace(/\/+$/, "");
262
- this.root = `${base}/api/ai-governance/internal/workspace-sessions/${encodeURIComponent(opts.sessionId)}`;
263
- }
264
- opts;
265
- fetchImpl;
266
- root;
267
- gone;
268
- async bootstrap() {
269
- const raw = await this.get(`/bootstrap?features=${ENGINE_FEATURES.join(",")}`, this.opts.requestTimeoutMs ?? 3e4);
270
- return toSessionBootstrap(raw);
271
- }
272
- async pollCommands(waitSeconds, signal) {
273
- const wait = Math.max(0, Math.min(25, Math.floor(waitSeconds)));
274
- const body = await this.get(`/commands?wait=${wait}`, (wait + 15) * 1e3, signal);
275
- return Array.isArray(body?.commands) ? body.commands : [];
276
- }
277
- async postEvents(events) {
278
- if (events.length === 0) return;
279
- await this.post("/events", { events }, this.opts.requestTimeoutMs ?? 3e4);
280
- }
281
- callTool(name, args, approvalId) {
282
- if (!/^[a-z][a-z0-9_]{0,63}$/.test(name)) throw new TransportError("invalid tool name", null, false);
283
- return this.post(`/tools/${name}`, approvalId ? { args, approvalId } : { args }, this.opts.toolTimeoutMs ?? 12e4);
178
+ // src/application/services/workspaceSandbox/importers.ts
179
+ function defaultImportSources(home, env = process.env) {
180
+ return { claudeDir: env.CLAUDE_CONFIG_DIR || (0, import_path.join)(home, ".claude"), codexDir: env.CODEX_HOME || (0, import_path.join)(home, ".codex") };
181
+ }
182
+ var MAX_LINE = 8 * 1024 * 1024;
183
+ var MAX_FILES = IMPORT_LIMITS.maxScanItems;
184
+ var READ_WINDOW_BYTES = 4 * IMPORT_LIMITS.maxBytes;
185
+ var WRAPPER = /^\s*<(command-[a-z-]+|local-command-[a-z-]+|bash-(?:input|stdout|stderr)|task-notification|system-reminder|user-prompt-submit-hook|environment_context|user_instructions|permissions instructions|recommended_plugins|app-context|skills_instructions|collaboration_mode|multi_agent_[a-z_]+|turn_aborted|user_shell_command)[^>]*>/i;
186
+ var oneLine = (s, max) => {
187
+ const t = s.replace(/\s+/g, " ").trim();
188
+ return t.length > max ? `${t.slice(0, max - 1)}\u2026` : t;
189
+ };
190
+ function summarizeToolInput(name, input) {
191
+ if (typeof input === "string") {
192
+ try {
193
+ return summarizeToolInput(name, JSON.parse(input));
194
+ } catch {
195
+ return oneLine(input, IMPORT_LIMITS.maxToolSummary);
196
+ }
284
197
  }
285
- async mcpTools() {
286
- const raw = await this.get("/mcp/tools", this.opts.requestTimeoutMs ?? 6e4);
287
- return orgMcpCatalogOf(raw);
198
+ if (!input || typeof input !== "object") return "";
199
+ const o = input;
200
+ for (const key of ["command", "cmd", "file_path", "path", "notebook_path", "pattern", "query", "url", "description", "prompt"]) {
201
+ const v = o[key];
202
+ if (typeof v === "string" && v.trim()) return oneLine(v, IMPORT_LIMITS.maxToolSummary);
203
+ if (Array.isArray(v) && v.every((x) => typeof x === "string")) return oneLine(v.join(" "), IMPORT_LIMITS.maxToolSummary);
288
204
  }
289
- mcpCall(req) {
290
- return this.post("/mcp/call", req, this.opts.toolTimeoutMs ?? 18e4);
291
- }
292
- openPullRequest(req) {
293
- return this.post("/pull-request", req, this.opts.toolTimeoutMs ?? 18e4);
294
- }
295
- async checkpoint(req) {
296
- await this.post("/checkpoint", req, this.opts.requestTimeoutMs ?? 6e4);
297
- }
298
- async openRepository(repoFullName) {
299
- const raw = await this.post("/repositories/open", { repoFullName }, this.opts.requestTimeoutMs ?? 3e4);
300
- const text3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
301
- const defaultBranch = text3(raw?.defaultBranch, "main");
302
- return {
303
- cloneUrl: text3(raw?.cloneUrl),
304
- scheme: text3(raw?.scheme),
305
- token: text3(raw?.token),
306
- provider: text3(raw?.provider),
307
- repoFullName: text3(raw?.repoFullName, repoFullName),
308
- defaultBranch,
309
- branch: typeof raw?.branch === "string" ? raw.branch : defaultBranch
310
- };
311
- }
312
- async importedHistory() {
313
- const raw = await this.get("/imported", this.opts.requestTimeoutMs ?? 6e4);
314
- const messages = Array.isArray(raw?.messages) ? raw.messages : [];
315
- return {
316
- messages: messages.filter((m) => !!m && typeof m === "object" && (m.role === "user" || m.role === "assistant")).map((m) => ({
317
- role: m.role,
318
- text: typeof m.text === "string" ? m.text : "",
319
- at: typeof m.at === "string" ? m.at : null,
320
- ...Array.isArray(m.tools) ? { tools: m.tools.filter((t) => !!t && typeof t.name === "string" && typeof t.summary === "string") } : {}
321
- }))
322
- };
323
- }
324
- async saveTranscript(t) {
325
- await this.post("/transcript", t, this.opts.requestTimeoutMs ?? 6e4);
205
+ const first = Object.values(o).find((v) => typeof v === "string" && v.trim());
206
+ return first ? oneLine(first, IMPORT_LIMITS.maxToolSummary) : "";
207
+ }
208
+ async function* lines(file) {
209
+ const rl = (0, import_readline.createInterface)({ input: (0, import_fs.createReadStream)(file, { encoding: "utf8" }), crlfDelay: Infinity });
210
+ try {
211
+ for await (const line of rl) {
212
+ if (!line || line.length > MAX_LINE || line[0] !== "{") continue;
213
+ try {
214
+ const rec = JSON.parse(line);
215
+ if (rec && typeof rec === "object") yield rec;
216
+ } catch {
217
+ }
218
+ }
219
+ } finally {
220
+ rl.close();
326
221
  }
327
- async saveBaseMeasurement(r) {
328
- await this.post("/measurements/base", r, this.opts.requestTimeoutMs ?? 3e4);
222
+ }
223
+ var MessageWindow = class {
224
+ messages = [];
225
+ bytes = 0;
226
+ total = 0;
227
+ push(m) {
228
+ this.messages.push(m);
229
+ this.total++;
230
+ this.bytes += importBytes([m]);
231
+ while (this.bytes > READ_WINDOW_BYTES && this.messages.length > 1) this.bytes -= importBytes([this.messages.shift()]);
329
232
  }
330
- async reply(commandId, reply) {
331
- if (!/^[A-Za-z0-9-]{1,80}$/.test(commandId)) throw new TransportError("invalid command id", null, false);
332
- await this.post(`/replies/${commandId}`, reply, this.opts.requestTimeoutMs ?? 3e4);
233
+ get last() {
234
+ return this.messages[this.messages.length - 1];
333
235
  }
334
- async refreshRuntime() {
335
- const raw = await this.post("/runtime", {}, this.opts.requestTimeoutMs ?? 3e4);
336
- const boot = toSessionBootstrap({ session: { model: raw?.model, reasoning: raw?.reasoning }, runtime: raw?.runtime, repository: null });
337
- return { model: boot.model, runtime: boot.runtime, reasoning: boot.reasoning ?? null };
236
+ grow(m, text3, tools) {
237
+ const before = importBytes([m]);
238
+ if (text3) m.text = m.text ? `${m.text}
239
+
240
+ ${text3}` : text3;
241
+ if (tools.length) m.tools = [...m.tools ?? [], ...tools].slice(0, IMPORT_LIMITS.maxToolsPerMessage);
242
+ this.bytes += importBytes([m]) - before;
338
243
  }
339
- headers(json) {
340
- return {
341
- "x-workspace-session-secret": this.opts.secret,
342
- accept: "application/json",
343
- ...this.opts.userAgent ? { "user-agent": this.opts.userAgent } : {},
344
- ...json ? { "content-type": "application/json" } : {}
345
- };
244
+ };
245
+ function claudeText(content, role) {
246
+ if (typeof content === "string") return { text: role === "user" && WRAPPER.test(content) ? "" : content, tools: [] };
247
+ if (!Array.isArray(content)) return { text: "", tools: [] };
248
+ const texts = [];
249
+ const tools = [];
250
+ for (const b of content) {
251
+ if (b?.type === "text" && typeof b.text === "string" && !(role === "user" && WRAPPER.test(b.text))) texts.push(b.text);
252
+ else if (b?.type === "tool_use" && typeof b.name === "string") tools.push({ name: b.name.slice(0, 200), summary: summarizeToolInput(b.name, b.input) });
253
+ else if (b?.type === "image") texts.push("[image]");
346
254
  }
347
- async get(path, timeoutMs, outer) {
348
- const attempts = Math.max(1, this.opts.getAttempts ?? 4);
349
- const delay = this.opts.retryDelayMs ?? 1e3;
350
- let last;
351
- for (let i = 0; i < attempts; i++) {
352
- if (outer?.aborted) throw new TransportError("aborted", null, false);
353
- try {
354
- return await this.request("GET", path, void 0, timeoutMs, outer);
355
- } catch (e) {
356
- last = e;
357
- if (e instanceof SessionGoneError) throw e;
358
- if (e instanceof TransportError && !e.retryable) throw e;
359
- if (i < attempts - 1) await sleep(delay * 2 ** i, outer);
255
+ return { text: texts.join("\n\n").trim(), tools };
256
+ }
257
+ async function parseClaudeCodeFile(file, opts = {}) {
258
+ const externalId = (0, import_path.basename)(file, ".jsonl");
259
+ if (!isExternalId(externalId)) return null;
260
+ const win = new MessageWindow();
261
+ let folder = null;
262
+ let customTitle = null;
263
+ let aiTitle = null;
264
+ let summary = null;
265
+ let firstUser = null;
266
+ let current = null;
267
+ for await (const r of lines(file)) {
268
+ if (opts.probe && win.total) break;
269
+ if (!folder && typeof r.cwd === "string") folder = r.cwd;
270
+ if (r.type === "custom-title" && typeof r.customTitle === "string") customTitle = r.customTitle;
271
+ else if (r.type === "ai-title" && typeof r.aiTitle === "string") aiTitle = r.aiTitle;
272
+ else if (r.type === "summary" && typeof r.summary === "string") summary = r.summary;
273
+ if (r.type !== "user" && r.type !== "assistant" || r.isSidechain === true || r.isMeta === true) continue;
274
+ const role = r.type;
275
+ const { text: text3, tools } = claudeText(r.message?.content, role);
276
+ const at = typeof r.timestamp === "string" ? r.timestamp : null;
277
+ if (role === "assistant") {
278
+ const id = typeof r.message?.id === "string" ? r.message.id : null;
279
+ if (!current || !id || current.id !== id) current = { id, msg: null, at };
280
+ if (!text3 && !tools.length) continue;
281
+ if (current.msg && win.last === current.msg) win.grow(current.msg, text3, tools);
282
+ else {
283
+ current.msg = { role, text: text3, at: current.at ?? at, ...tools.length ? { tools } : {} };
284
+ win.push(current.msg);
360
285
  }
286
+ continue;
361
287
  }
362
- throw last;
363
- }
364
- post(path, body, timeoutMs) {
365
- return this.request("POST", path, body, timeoutMs);
288
+ const onlyToolResults = Array.isArray(r.message?.content) && r.message.content.every((b) => b?.type === "tool_result");
289
+ if (!onlyToolResults) current = null;
290
+ if (!text3) continue;
291
+ const shown = r.isCompactSummary === true ? `[Summary of the earlier conversation]
292
+ ${text3}` : text3;
293
+ if (!firstUser && r.isCompactSummary !== true) firstUser = text3;
294
+ win.push({ role, text: shown, at });
366
295
  }
367
- async request(method, path, body, timeoutMs, outer) {
368
- const signals = [AbortSignal.timeout(timeoutMs), ...outer ? [outer] : []];
369
- const signal = signals.length > 1 ? anySignal(signals) : signals[0];
370
- let res;
371
- try {
372
- res = await this.fetchImpl(this.root + path, {
373
- method,
374
- headers: this.headers(body !== void 0),
375
- body: body !== void 0 ? JSON.stringify(body) : void 0,
376
- signal
377
- });
378
- } catch (e) {
379
- throw new TransportError(`${method} ${routeLabel(path)} failed: ${e.name}`, null, true);
380
- }
381
- if (this.gone.has(res.status)) {
382
- await res.body?.cancel().catch(() => void 0);
383
- throw new SessionGoneError(res.status);
384
- }
385
- if (!res.ok) {
386
- const code = await res.text().then((t) => JSON.parse(t)?.code, () => void 0).catch(() => void 0);
387
- const retryable = res.status === 429 || res.status >= 500;
388
- const error = new TransportError(`${method} ${routeLabel(path)} answered ${res.status}`, res.status, retryable);
389
- if (typeof code === "string" && /^[A-Z_]{3,80}$/.test(code)) error.code = code;
390
- throw error;
391
- }
392
- if (res.status === 204) return void 0;
393
- const text3 = await res.text();
394
- if (!text3) return void 0;
395
- try {
396
- return JSON.parse(text3);
397
- } catch {
398
- throw new TransportError(`${method} ${routeLabel(path)} returned invalid JSON`, res.status, false);
399
- }
296
+ if (!win.total) return null;
297
+ const title = oneLine(customTitle || aiTitle || summary || firstUser || "Untitled conversation", IMPORT_LIMITS.maxTitle);
298
+ const st = await (0, import_promises.lstat)(file).catch(() => null);
299
+ return { source: "CLAUDE_CODE", externalId, title, folder, messages: win.messages, updatedAt: st ? st.mtime.toISOString() : null };
300
+ }
301
+ async function regularFiles(dir, match) {
302
+ const out2 = [];
303
+ const names = await (0, import_promises.readdir)(dir).catch(() => []);
304
+ for (const name of names) {
305
+ if (!match(name)) continue;
306
+ const st = await (0, import_promises.lstat)((0, import_path.join)(dir, name)).catch(() => null);
307
+ if (st?.isFile()) out2.push({ path: (0, import_path.join)(dir, name), mtime: st.mtimeMs });
400
308
  }
401
- };
402
- function anySignal(signals) {
403
- const native = AbortSignal.any;
404
- if (typeof native === "function") return native.call(AbortSignal, signals);
405
- const ac = new AbortController();
406
- for (const s of signals) {
407
- if (s.aborted) {
408
- ac.abort(s.reason);
409
- return ac.signal;
410
- }
411
- s.addEventListener("abort", () => ac.abort(s.reason), { once: true });
309
+ return out2;
310
+ }
311
+ async function subdirs(dir) {
312
+ const out2 = [];
313
+ for (const name of await (0, import_promises.readdir)(dir).catch(() => [])) {
314
+ const st = await (0, import_promises.lstat)((0, import_path.join)(dir, name)).catch(() => null);
315
+ if (st?.isDirectory()) out2.push((0, import_path.join)(dir, name));
412
316
  }
413
- return ac.signal;
317
+ return out2;
414
318
  }
415
- function routeLabel(path) {
416
- return path.split("?")[0];
319
+ async function claudeCodeFiles(claudeDir) {
320
+ const files = [];
321
+ for (const project of await subdirs((0, import_path.join)(claudeDir, "projects"))) files.push(...await regularFiles(project, (n) => n.endsWith(".jsonl")));
322
+ return files;
417
323
  }
418
- function sleep(ms, signal) {
419
- return new Promise((resolve8) => {
420
- const t = setTimeout(resolve8, ms);
421
- signal?.addEventListener("abort", () => {
422
- clearTimeout(t);
423
- resolve8();
424
- }, { once: true });
425
- });
324
+ function codexText(content, role) {
325
+ if (typeof content === "string") return content;
326
+ if (!Array.isArray(content)) return "";
327
+ const want = role === "user" ? "input_text" : "output_text";
328
+ return content.filter((b) => b?.type === want && typeof b.text === "string" && !(role === "user" && WRAPPER.test(b.text))).map((b) => b.text).join("\n\n").trim();
426
329
  }
427
- var ENGINE_FEATURES = ["message_queue_v1"];
428
- function toSessionBootstrap(raw) {
429
- if (raw && raw.repo && !raw.repository) return raw;
430
- const session = raw?.session ?? {};
431
- const repository = raw?.repository && typeof raw.repository === "object" ? raw.repository : null;
432
- const runtime = raw?.runtime ?? {};
433
- const text3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
434
- const defaultBranch = text3(repository?.defaultBranch, "main");
435
- const repo2 = repository ? {
436
- cloneUrl: text3(repository.cloneUrl),
437
- scheme: text3(repository.scheme),
438
- token: text3(repository.token),
439
- provider: text3(repository.provider, text3(session.provider)),
440
- repoFullName: text3(repository.repoFullName, text3(session.repoFullName)),
441
- defaultBranch
442
- } : null;
443
- const repositories = scopeRepos(raw?.repositories) ?? scopeRepos(session.scope?.repos) ?? (repo2 ? [{ repoFullName: repo2.repoFullName, provider: repo2.provider, projectId: text3(session.projectId), defaultBranch: repo2.defaultBranch }] : []);
444
- const rawScope = session.scope && typeof session.scope === "object" ? session.scope : null;
445
- const kind = rawScope?.kind === "ALL" || rawScope?.kind === "TEAM" || rawScope?.kind === "BUSINESS_AREA" ? rawScope.kind : "PROJECTS";
446
- return {
447
- repo: repo2,
448
- repositories,
449
- scope: {
450
- kind,
451
- teamId: typeof rawScope?.teamId === "string" ? rawScope.teamId : null,
452
- projectIds: Array.isArray(rawScope?.projectIds) ? rawScope.projectIds.filter((p) => typeof p === "string") : typeof session.projectId === "string" && session.projectId ? [session.projectId] : [],
453
- repos: repositories
454
- },
455
- branch: text3(session.branch, repo2 ? defaultBranch : ""),
456
- projectId: text3(session.projectId),
457
- model: text3(session.model, "sq-auto"),
458
- runtime: {
459
- baseUrl: text3(runtime.baseUrl),
460
- token: text3(runtime.token),
461
- models: runtime.models,
462
- primaryModel: typeof runtime.primaryModel === "string" ? runtime.primaryModel : null,
463
- fastModel: typeof runtime.fastModel === "string" ? runtime.fastModel : null,
464
- maxOutputTokens: typeof runtime.maxOutputTokens === "number" ? runtime.maxOutputTokens : null,
465
- maxOutputTokensCeiling: typeof runtime.maxOutputTokensCeiling === "number" ? runtime.maxOutputTokensCeiling : null,
466
- reasoning: parseReasoningCapability(runtime.reasoning),
467
- aliases: Array.isArray(runtime.aliases) ? runtime.aliases.filter((a) => typeof a?.alias === "string").map(credentialAlias) : void 0
468
- },
469
- reasoning: isReasoningLevel(session.reasoning) ? session.reasoning : null,
470
- imported: importedInfo(session.imported),
471
- checkpointPatch: typeof raw?.checkpointPatch === "string" ? raw.checkpointPatch : null,
472
- sdkSessionId: typeof session.sdkSessionId === "string" ? session.sdkSessionId : null,
473
- workspaceKind: session.workspaceKind === "LOCAL" ? "LOCAL" : "CLOUD",
474
- projectName: typeof session.projectName === "string" ? session.projectName : typeof raw?.project?.name === "string" ? raw.project.name : null,
475
- folderLink: typeof session.folderLink?.projectId === "string" && session.folderLink.projectId ? { projectId: session.folderLink.projectId } : null,
476
- transcript: transcriptOf(raw?.transcript),
477
- baseMeasurements: raw?.baseMeasurements && typeof raw.baseMeasurements === "object" && !Array.isArray(raw.baseMeasurements) ? raw.baseMeasurements : {},
478
- pullRequests: pullRequestsOf(raw?.pullRequests),
479
- skills: skillsOf(raw?.skills),
480
- plugins: pluginsOf(raw?.plugins),
481
- environments: environmentsOf(raw?.environments)
330
+ async function parseCodexFile(file, opts = {}) {
331
+ const fromName = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i.exec(file)?.[1] ?? null;
332
+ let externalId = null;
333
+ let folder = null;
334
+ let firstUser = null;
335
+ const win = new MessageWindow();
336
+ for await (const r of lines(file)) {
337
+ if (opts.probe && win.total && externalId) break;
338
+ const p = r.payload;
339
+ if (!p || typeof p !== "object") continue;
340
+ if (r.type === "session_meta") {
341
+ if (p.source && typeof p.source === "object" && p.source.subagent) return null;
342
+ if (!externalId && typeof p.id === "string") externalId = p.id;
343
+ if (!folder && typeof p.cwd === "string") folder = p.cwd;
344
+ continue;
345
+ }
346
+ if (r.type !== "response_item") continue;
347
+ const at = typeof r.timestamp === "string" ? r.timestamp : null;
348
+ if (p.type === "message" && (p.role === "user" || p.role === "assistant")) {
349
+ const text3 = codexText(p.content, p.role);
350
+ if (!text3) continue;
351
+ if (p.role === "user" && !firstUser) firstUser = text3;
352
+ const last = win.last;
353
+ if (p.role === "assistant" && last?.role === "assistant" && !last.text) win.grow(last, text3, []);
354
+ else win.push({ role: p.role, text: text3, at });
355
+ } else if ((p.type === "function_call" || p.type === "custom_tool_call" || p.type === "local_shell_call") && (typeof p.name === "string" || p.type === "local_shell_call")) {
356
+ const name = typeof p.name === "string" ? p.name : "shell";
357
+ const tool = { name: name.slice(0, 200), summary: summarizeToolInput(name, p.arguments ?? p.input ?? p.action) };
358
+ const last = win.last;
359
+ if (last?.role === "assistant") win.grow(last, "", [tool]);
360
+ else win.push({ role: "assistant", text: "", at, tools: [tool] });
361
+ }
362
+ }
363
+ const id = externalId ?? fromName;
364
+ if (!id || !isExternalId(id) || !win.total) return null;
365
+ const st = await (0, import_promises.lstat)(file).catch(() => null);
366
+ return {
367
+ source: "CODEX",
368
+ externalId: id,
369
+ title: oneLine(firstUser || "Untitled conversation", IMPORT_LIMITS.maxTitle),
370
+ folder,
371
+ messages: win.messages,
372
+ updatedAt: st ? st.mtime.toISOString() : null
482
373
  };
483
374
  }
484
- function transcriptOf(raw) {
485
- const t = raw;
486
- if (!t || typeof t !== "object" || typeof t.sdkSessionId !== "string" || t.encoding !== "gzip-base64" || typeof t.data !== "string") return null;
487
- return { sdkSessionId: t.sdkSessionId, encoding: "gzip-base64", data: t.data, bytes: typeof t.bytes === "number" ? t.bytes : 0, sha256: typeof t.sha256 === "string" ? t.sha256 : "" };
375
+ async function codexFiles(codexDir) {
376
+ const files = [];
377
+ const walk = async (dir, depth) => {
378
+ files.push(...await regularFiles(dir, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl")));
379
+ if (depth < 4) for (const sub of await subdirs(dir)) await walk(sub, depth + 1);
380
+ };
381
+ await walk((0, import_path.join)(codexDir, "sessions"), 0);
382
+ return files;
488
383
  }
489
- var strings = (v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).filter(([, x]) => typeof x === "string")) : {};
490
- function environmentsOf(raw) {
491
- if (!Array.isArray(raw)) return [];
492
- return raw.filter((e) => !!e && typeof e === "object" && typeof e.projectId === "string").slice(0, 50).map((e) => ({ projectId: e.projectId, setupCommand: typeof e.setupCommand === "string" && e.setupCommand.trim() ? e.setupCommand : null, env: strings(e.env), secrets: strings(e.secrets) }));
384
+ function secretsIn(c) {
385
+ let n = 0;
386
+ for (const m of c.messages) {
387
+ n += countSecrets(m.text);
388
+ for (const t of m.tools ?? []) n += countSecrets(t.summary);
389
+ }
390
+ return n;
493
391
  }
494
- function pluginsOf(raw) {
495
- if (!Array.isArray(raw)) return [];
496
- return raw.filter((p) => !!p && typeof p === "object" && typeof p.name === "string" && Array.isArray(p.files)).slice(0, 20).map((p) => ({ name: p.name, files: p.files.filter((f) => !!f && typeof f.path === "string" && typeof f.content === "string").slice(0, 300) }));
392
+ async function importFiles(sources) {
393
+ return [
394
+ ...(await claudeCodeFiles(sources.claudeDir)).map((f) => ({ ...f, source: "CLAUDE_CODE" })),
395
+ ...(await codexFiles(sources.codexDir)).map((f) => ({ ...f, source: "CODEX" }))
396
+ ].sort((a, b) => b.mtime - a.mtime).slice(0, MAX_FILES);
497
397
  }
498
- function skillsOf(raw) {
499
- if (!Array.isArray(raw)) return [];
500
- return raw.filter((s) => !!s && typeof s === "object").filter((s) => typeof s.name === "string" && typeof s.body === "string").slice(0, 200).map((s) => ({
501
- name: s.name,
502
- description: typeof s.description === "string" ? s.description : "",
503
- body: s.body,
504
- argumentHint: typeof s.argumentHint === "string" ? s.argumentHint : null
505
- }));
398
+ async function scanImports(sources) {
399
+ const files = await importFiles(sources);
400
+ const items = [];
401
+ const seen = /* @__PURE__ */ new Set();
402
+ for (const f of files) {
403
+ const c = await (f.source === "CLAUDE_CODE" ? parseClaudeCodeFile(f.path) : parseCodexFile(f.path)).catch(() => null);
404
+ if (!c || seen.has(`${c.source}:${c.externalId}`)) continue;
405
+ seen.add(`${c.source}:${c.externalId}`);
406
+ items.push({
407
+ source: c.source,
408
+ externalId: c.externalId,
409
+ title: oneLine(scrubSecrets(c.title).text, IMPORT_LIMITS.maxTitle),
410
+ folder: c.folder,
411
+ messageCount: Math.min(c.messages.length, IMPORT_LIMITS.maxMessages),
412
+ updatedAt: c.updatedAt,
413
+ secretsFound: secretsIn(c)
414
+ });
415
+ }
416
+ return items;
506
417
  }
507
- function pullRequestsOf(raw) {
508
- const out2 = {};
509
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return out2;
510
- for (const [repo2, v] of Object.entries(raw)) {
511
- if (!v || typeof v.branch !== "string") continue;
512
- out2[repo2] = {
513
- branch: v.branch,
514
- number: typeof v.number === "number" ? v.number : null,
515
- url: typeof v.url === "string" ? v.url : null,
516
- files: Array.isArray(v.files) ? v.files.filter((f) => typeof f === "string") : []
517
- };
418
+ async function countImports(sources) {
419
+ const counts = { claudeCode: 0, codex: 0 };
420
+ const seen = /* @__PURE__ */ new Set();
421
+ for (const f of await importFiles(sources)) {
422
+ const c = await (f.source === "CLAUDE_CODE" ? parseClaudeCodeFile(f.path, { probe: true }) : parseCodexFile(f.path, { probe: true })).catch(() => null);
423
+ if (!c || seen.has(`${c.source}:${c.externalId}`)) continue;
424
+ seen.add(`${c.source}:${c.externalId}`);
425
+ if (c.source === "CLAUDE_CODE") counts.claudeCode++;
426
+ else counts.codex++;
427
+ }
428
+ return counts;
429
+ }
430
+ async function conversationFolders(sources, deadline) {
431
+ const claude = [];
432
+ for (const project of await subdirs((0, import_path.join)(sources.claudeDir, "projects"))) {
433
+ if (Date.now() > deadline) break;
434
+ const newest = (await regularFiles(project, (n) => n.endsWith(".jsonl"))).sort((a, b) => b.mtime - a.mtime)[0];
435
+ if (newest) claude.push({ ...newest, source: "CLAUDE_CODE" });
436
+ }
437
+ const codex = Date.now() > deadline ? [] : (await codexFiles(sources.codexDir)).map((f) => ({ ...f, source: "CODEX" }));
438
+ const files = [...claude, ...codex].sort((a, b) => b.mtime - a.mtime).slice(0, MAX_FILES);
439
+ const out2 = [];
440
+ const seen = /* @__PURE__ */ new Set();
441
+ for (const f of files) {
442
+ if (Date.now() > deadline) break;
443
+ let n = 0;
444
+ try {
445
+ for await (const r of lines(f.path)) {
446
+ if (++n > 50) break;
447
+ const cwd = f.source === "CLAUDE_CODE" ? r.cwd : r.type === "session_meta" && r.payload && typeof r.payload === "object" && !(r.payload.source && typeof r.payload.source === "object" && r.payload.source.subagent) ? r.payload.cwd : void 0;
448
+ if (typeof cwd === "string" && cwd) {
449
+ if (!seen.has(cwd)) {
450
+ seen.add(cwd);
451
+ out2.push(cwd);
452
+ }
453
+ break;
454
+ }
455
+ }
456
+ } catch {
457
+ }
518
458
  }
519
459
  return out2;
520
460
  }
521
- function importedInfo(raw) {
522
- if (!raw || typeof raw !== "object") return null;
523
- const r = raw;
524
- if (r.source !== "CLAUDE_CODE" && r.source !== "CODEX" || !isExternalId(r.externalId)) return null;
525
- return {
526
- source: r.source,
527
- externalId: r.externalId,
528
- title: typeof r.title === "string" ? r.title : "",
529
- folder: typeof r.folder === "string" ? r.folder : null,
530
- messageCount: typeof r.messageCount === "number" ? r.messageCount : 0,
531
- nativeResume: r.nativeResume === true
532
- };
461
+ async function findConversation(sources, source, externalId) {
462
+ if (!isExternalId(externalId)) return null;
463
+ if (source === "CLAUDE_CODE") {
464
+ const file = (await claudeCodeFiles(sources.claudeDir)).find((f) => (0, import_path.basename)(f.path, ".jsonl") === externalId);
465
+ return file ? parseClaudeCodeFile(file.path) : null;
466
+ }
467
+ const files = (await codexFiles(sources.codexDir)).filter((f) => f.path.includes(externalId)).sort((a, b) => b.mtime - a.mtime);
468
+ for (const f of files) {
469
+ const c = await parseCodexFile(f.path).catch(() => null);
470
+ if (c?.externalId === externalId) return c;
471
+ }
472
+ return null;
533
473
  }
534
- function scopeRepos(raw) {
535
- if (!Array.isArray(raw)) return null;
536
- return raw.filter((r) => r && typeof r === "object" && typeof r.repoFullName === "string").map((r) => ({
537
- repoFullName: r.repoFullName,
538
- provider: typeof r.provider === "string" ? r.provider : "",
539
- projectId: typeof r.projectId === "string" ? r.projectId : "",
540
- defaultBranch: typeof r.defaultBranch === "string" ? r.defaultBranch : null
474
+ function prepareImport(c) {
475
+ let removed = 0;
476
+ const scrub = (s) => {
477
+ const r = scrubSecrets(s);
478
+ removed += r.count;
479
+ return r.text;
480
+ };
481
+ const cap = (s, max) => Buffer.byteLength(s) > max ? `${Buffer.from(s).subarray(0, max - 32).toString("utf8").replace(/�+$/, "")}
482
+ [... cut ...]` : s;
483
+ const messages = c.messages.map((m) => ({
484
+ role: m.role,
485
+ text: cap(scrub(m.text), IMPORT_LIMITS.maxMessageBytes),
486
+ at: m.at,
487
+ ...m.tools?.length ? { tools: m.tools.slice(0, IMPORT_LIMITS.maxToolsPerMessage).map((t) => ({ name: t.name, summary: oneLine(scrub(t.summary), IMPORT_LIMITS.maxToolSummary) })) } : {}
541
488
  }));
489
+ const title = oneLine(scrubSecrets(c.title).text, IMPORT_LIMITS.maxTitle) || "Untitled conversation";
490
+ let start = Math.max(0, messages.length - IMPORT_LIMITS.maxMessages);
491
+ let bytes = importBytes(messages.slice(start));
492
+ while (bytes > IMPORT_LIMITS.maxBytes && start < messages.length - 1) bytes -= importBytes([messages[start++]]);
493
+ return { source: c.source, externalId: c.externalId, folder: c.folder, title, secretsRemoved: removed, messages: messages.slice(start), omitted: start };
542
494
  }
543
- function orgMcpCatalogOf(raw) {
544
- const r = raw ?? {};
545
- const servers = (Array.isArray(r.servers) ? r.servers : []).filter((s) => !!s && typeof s === "object" && typeof s.server === "string" && /^[a-z0-9][a-z0-9-]{0,31}$/.test(s.server)).slice(0, 20).map((s) => ({
546
- server: s.server,
547
- description: typeof s.description === "string" ? s.description : null,
548
- approval: typeof s.approval === "string" ? s.approval : "WRITE",
549
- tools: (Array.isArray(s.tools) ? s.tools : []).filter((t) => !!t && typeof t === "object" && typeof t.name === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(t.name)).slice(0, 100).map((t) => ({
550
- name: t.name,
551
- description: typeof t.description === "string" ? t.description : "",
552
- inputSchema: t.inputSchema && typeof t.inputSchema === "object" && !Array.isArray(t.inputSchema) ? t.inputSchema : { type: "object" },
553
- readOnly: t.readOnly === true
554
- }))
555
- }));
556
- const unavailable = (Array.isArray(r.unavailable) ? r.unavailable : []).filter((u) => !!u && typeof u.server === "string").map((u) => ({ server: String(u.server).slice(0, 32), error: typeof u.error === "string" ? u.error.slice(0, 60) : "MCP_FAILED" }));
557
- return { servers, unavailable };
495
+ function scrubRecord(record) {
496
+ let removed = 0;
497
+ const walk = (value, key) => {
498
+ if (typeof value === "string") {
499
+ if (key === "signature" || key === "data") return value;
500
+ const r = scrubSecrets(value);
501
+ removed += r.count;
502
+ return r.text;
503
+ }
504
+ if (Array.isArray(value)) {
505
+ const out2 = [];
506
+ for (const item of value) {
507
+ const block = item;
508
+ if (block && typeof block === "object" && block.type === "thinking" && typeof block.thinking === "string") {
509
+ const r = scrubSecrets(block.thinking);
510
+ if (r.count) {
511
+ removed += r.count;
512
+ continue;
513
+ }
514
+ out2.push(item);
515
+ continue;
516
+ }
517
+ if (block && typeof block === "object" && block.type === "redacted_thinking") {
518
+ out2.push(item);
519
+ continue;
520
+ }
521
+ out2.push(walk(item));
522
+ }
523
+ return out2;
524
+ }
525
+ if (value && typeof value === "object") {
526
+ const o = value;
527
+ for (const k of Object.keys(o)) o[k] = walk(o[k], k);
528
+ return o;
529
+ }
530
+ return value;
531
+ };
532
+ walk(record);
533
+ return removed;
534
+ }
535
+ async function writeScrubbedTranscript(source, target) {
536
+ const out2 = (0, import_fs.createWriteStream)(target, { mode: 384, flags: "wx" });
537
+ let removed = 0;
538
+ const done = new Promise((resolve8, reject) => {
539
+ out2.on("finish", resolve8);
540
+ out2.on("error", reject);
541
+ });
542
+ const rl = (0, import_readline.createInterface)({ input: (0, import_fs.createReadStream)(source, { encoding: "utf8" }), crlfDelay: Infinity });
543
+ try {
544
+ for await (const line of rl) {
545
+ if (!line) continue;
546
+ let record;
547
+ try {
548
+ record = JSON.parse(line);
549
+ } catch {
550
+ continue;
551
+ }
552
+ removed += scrubRecord(record);
553
+ if (!out2.write(`${JSON.stringify(record)}
554
+ `)) await new Promise((r) => out2.once("drain", () => r()));
555
+ }
556
+ } finally {
557
+ rl.close();
558
+ out2.end();
559
+ }
560
+ await done;
561
+ return removed;
558
562
  }
559
563
 
560
- // src/application/services/workspaceSandbox/importers.ts
561
- var import_fs = require("fs");
562
- var import_promises = require("fs/promises");
563
- var import_path = require("path");
564
- var import_readline = require("readline");
564
+ // src/application/services/workspaceSandbox/backgroundService.ts
565
+ var import_child_process2 = require("child_process");
566
+ var import_crypto3 = require("crypto");
567
+ var import_fs5 = require("fs");
568
+ var import_path6 = require("path");
565
569
 
566
- // src/application/services/workspaceSandbox/secretScrubber.ts
567
- var R = (kind) => `[REDACTED:${kind}]`;
568
- function looksLikeSecretValue(v) {
569
- if (v.length < 8) return false;
570
- if (/^\[REDACTED:/.test(v)) return false;
571
- if (/^(true|false|null|undefined|none|nil|string|number|boolean|required|optional|redacted|changeme|example|placeholder|xxx+|\*+)$/i.test(v)) return false;
572
- if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)+(\(\))?$/.test(v) || /^[A-Za-z_$][\w$]*\(\)?$/.test(v)) return false;
573
- if (/^(\$\{?[A-Za-z_][\w]*\}?|\{\{.*\}\}|<[^>]*>|%[A-Za-z_]+%)$/.test(v)) return false;
574
- if (/^[A-Za-z_]+$/.test(v) && v.length < 24) return false;
575
- return true;
570
+ // src/application/services/workspaceSandbox/reasoning.ts
571
+ var REASONING_LEVELS = ["off", "low", "medium", "high", "xhigh", "max"];
572
+ function isReasoningLevel(value) {
573
+ return typeof value === "string" && REASONING_LEVELS.includes(value);
576
574
  }
577
- var RULES = [
578
- { kind: "PRIVATE_KEY", re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g },
579
- { kind: "CONNECTION_STRING", re: /\b([a-z][a-z0-9+.-]{1,30}:\/\/)([^\s:@/'"]{1,200}):([^\s@/'"]{1,300})@/gi, replace: (_m, scheme, user) => `${scheme}${user}:${R("PASSWORD")}@` },
580
- { kind: "AWS_ACCESS_KEY", re: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA|AIPA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
581
- { kind: "AWS_SECRET_KEY", re: /\b(aws_?secret_?access_?key|aws_?secret|secretAccessKey)(["']?\s*[:=]\s*["']?)([A-Za-z0-9/+=]{40})\b/gi, replace: (_m, k, sep11) => `${k}${sep11}${R("AWS_SECRET_KEY")}` },
582
- { kind: "ANTHROPIC_KEY", re: /\bsk-ant-[a-z]{2,10}\d{0,3}-[A-Za-z0-9_-]{20,}/g },
583
- { kind: "OPENAI_KEY", re: /\bsk-(?:proj-|svcacct-|admin-|None-)?[A-Za-z0-9_-]{20,}/g },
584
- { kind: "GITHUB_TOKEN", re: /\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{22,255})\b/g },
585
- { kind: "GITLAB_TOKEN", re: /\bgl(?:pat|dt|rt|ptt|ft|cbt|imt|oas|soat|agent)-[A-Za-z0-9_-]{20,}/g },
586
- { kind: "SLACK_TOKEN", re: /\bxox[abposre]-[A-Za-z0-9-]{10,}/g },
587
- { kind: "SLACK_WEBHOOK", re: /https:\/\/hooks\.slack\.com\/(?:services|workflows|triggers)\/[A-Za-z0-9_/-]{20,}/g },
588
- { kind: "STRIPE_KEY", re: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g },
589
- { kind: "STRIPE_WEBHOOK_SECRET", re: /\bwhsec_[A-Za-z0-9]{24,}\b/g },
590
- { kind: "GOOGLE_API_KEY", re: /\bAIza[0-9A-Za-z_-]{35}\b/g },
591
- { kind: "JWT", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g },
592
- { kind: "BEARER_TOKEN", re: /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{20,})/g, replace: (_m, p) => `${p}${R("BEARER_TOKEN")}` },
593
- {
594
- kind: "ASSIGNED_SECRET",
595
- // password=..., "api_key": "...", SECRET_TOKEN: ..., --token ... (key names that say "secret").
596
- re: /\b([A-Za-z0-9_.-]*(?:passw(?:or)?d|pwd|secret|token|api[_-]?key|apikey|access[_-]?key|private[_-]?key|client[_-]?secret|credential|auth[_-]?key)[A-Za-z0-9_-]*)(["']?\s*(?:=|:|\s)\s*)(["']?)([^\s"'`,;)}\]]{8,500})\3/gi,
597
- replace: (_m, key, sep11, quote, value) => looksLikeSecretValue(value) ? `${key}${sep11}${quote}${R("SECRET")}${quote}` : null
575
+ function parseReasoningCapability(raw) {
576
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
577
+ const r = raw;
578
+ if (!Array.isArray(r.levels)) return null;
579
+ const listed = [...r.levels, ...r.off === true ? ["off"] : []];
580
+ const levels = REASONING_LEVELS.filter((l) => listed.includes(l));
581
+ if (!levels.length) return null;
582
+ const def = isReasoningLevel(r.default) && levels.includes(r.default) ? r.default : levels[0];
583
+ return { levels, default: def };
584
+ }
585
+ function effectiveReasoning(requested, capability) {
586
+ if (!capability) return null;
587
+ if (requested && capability.levels.includes(requested)) return requested;
588
+ return capability.default;
589
+ }
590
+ function isMaxMode(level, capability) {
591
+ if (!level || level === "off" || !capability) return false;
592
+ return capability.levels[capability.levels.length - 1] === level;
593
+ }
594
+ function turnReasoning(level, capability) {
595
+ if (!capability || !level) return { options: {}, forceNoThinking: true, outputCeiling: false };
596
+ if (level === "off") return { options: { thinking: { type: "disabled" } }, forceNoThinking: true, outputCeiling: false };
597
+ return { options: { effort: level, thinking: { type: "adaptive" } }, forceNoThinking: false, outputCeiling: isMaxMode(level, capability) };
598
+ }
599
+ function engineModelCapabilities(aliases) {
600
+ const entries = ["-mid_conv_system"];
601
+ const seen = /* @__PURE__ */ new Set();
602
+ for (const { alias, reasoning } of aliases) {
603
+ const name = alias.trim();
604
+ if (!name || seen.has(name) || /[;=,]/.test(name)) continue;
605
+ seen.add(name);
606
+ entries.push(reasoning ? `${name}=effort,${reasoning.levels.includes("max") ? "" : "-"}max_effort,${reasoning.levels.includes("xhigh") ? "" : "-"}xhigh_effort` : `${name}=-effort,-max_effort,-xhigh_effort`);
598
607
  }
599
- ];
600
- function scrubSecrets(input) {
601
- if (!input) return { text: input, count: 0 };
602
- let text3 = input;
603
- let count = 0;
604
- for (const rule of RULES) {
605
- text3 = text3.replace(rule.re, (...args) => {
606
- const match = args[0];
607
- const groups = args.slice(1, -2).map((g) => typeof g === "string" ? g : "");
608
- if (!rule.replace) {
609
- count++;
610
- return R(rule.kind);
611
- }
612
- const out2 = rule.replace(match, ...groups);
613
- if (out2 === null) return match;
614
- count++;
615
- return out2;
616
- });
608
+ return entries.join(";");
609
+ }
610
+
611
+ // src/application/services/workspaceSandbox/autoRouting.ts
612
+ var LONG_INSTRUCTION_CHARS = 3e3;
613
+ var LONG_CODE_LINES = 60;
614
+ var MANY_FILES = 5;
615
+ var HARD_WORK = [
616
+ [/\b(refactor|refator|refactoriz)\w*\b[\s\S]{0,80}\b(across|between|all|every|entre|todos|todas|varios|varias|modul|servic|packages?|pacotes?|layers?|camadas?)/, "refactor across modules"],
617
+ [/\b(architect|arquitet|arquitect|redesign|re-architect)\w*/, "architecture"],
618
+ [/\b(root cause|causa raiz|causa-raiz|debug|depur|investigat|investig|stack ?trace|flaky|race condition|condicao de corrida|memory leak|vazamento de memoria|deadlock|intermittent|intermitente)\w*/, "debugging, root cause"],
619
+ [/\b(migrat|migra(c|t)a?o|migrar|migre|upgrade\b[\s\S]{0,40}\b(from|to|de|para)\b|port (this|the|it)\b[\s\S]{0,40}\bto\b)/, "migration"],
620
+ [/\b(vulnerab|cve-\d|security (fix|issue|flaw|hole|bug)|injection|injecao|xss|csrf|ssrf|rce\b|seguranca|seguridad|exploit)\w*/, "security fix"],
621
+ [/\b(every|all|each|todos os|todas as|cada|todos los|todas las) (the )?(files?|modules?|services?|endpoints?|packages?|arquivos?|modulos?|servicos?|archivos?)\b|\b(across|throughout) (the )?(whole )?(codebase|repo|repository|project|modules|services)\b|\b(codebase|base de codigo)[- ](wide|inteira|toda)\b/, "many files"],
622
+ [/\b(refactor|refator|refactoriz)\w*/, "refactor"],
623
+ [/\b(step[- ]by[- ]step|passo a passo|paso a paso|multi[- ]step|end[- ]to[- ]end|threat model|performance (issue|regression|problem)|regressao de performance|rewrite|reescrev|reescrib)\w*/, "multi-step work"]
624
+ ];
625
+ var normalize = (text3) => text3.toLowerCase().normalize("NFKD").replace(new RegExp("\\p{M}", "gu"), "");
626
+ function namedFiles(text3) {
627
+ const found = /* @__PURE__ */ new Set();
628
+ for (const m of text3.matchAll(/(?:^|[\s`'"(\[])((?:\.{0,2}\/)?[\w.-]+(?:\/[\w.-]+)+\/?|[\w-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|rb|php|cs|cpp|c|h|swift|scala|sql|yml|yaml|json|tf|vue|svelte))(?=$|[\s`'"),.:;\]])/g)) {
629
+ found.add(m[1].replace(/^\.\//, ""));
617
630
  }
618
- return { text: text3, count };
631
+ return found.size;
619
632
  }
620
- function countSecrets(input) {
621
- return scrubSecrets(input).count;
633
+ function classifyCodeTurn(s) {
634
+ if (s.maxMode) return { hard: true, reason: "Max Mode" };
635
+ if (s.previousTrouble) return { hard: true, reason: s.previousTrouble };
636
+ const text3 = s.content;
637
+ const codeLines = [...text3.matchAll(/```[\s\S]*?```/g)].reduce((n, block) => n + block[0].split("\n").length, 0);
638
+ if (codeLines > LONG_CODE_LINES) return { hard: true, reason: `${codeLines} lines of code in the request` };
639
+ if (text3.length > LONG_INSTRUCTION_CHARS) return { hard: true, reason: `long instruction (${text3.length} characters)` };
640
+ const plain = normalize(text3);
641
+ for (const [re, reason] of HARD_WORK) if (re.test(plain)) return { hard: true, reason };
642
+ const files = namedFiles(text3);
643
+ if (files >= MANY_FILES) return { hard: true, reason: `${files} files named` };
644
+ return { hard: false, reason: text3.length > 600 || codeLines > 0 ? "bounded task" : "short, direct request" };
622
645
  }
623
-
624
- // src/application/services/workspaceSandbox/importers.ts
625
- function defaultImportSources(home, env = process.env) {
626
- return { claudeDir: env.CLAUDE_CONFIG_DIR || (0, import_path.join)(home, ".claude"), codexDir: env.CODEX_HOME || (0, import_path.join)(home, ".codex") };
646
+ var STREAM_REASONS = ["ORG_OUTPUT_TEXT_RULES", "SEMANTIC_INSPECTOR", "APPLICATION_OUTPUT_TEXT_RULES", "DESTINATION_NO_STREAM"];
647
+ function routeAutoTurn(boot, signals) {
648
+ if (boot.model !== "sq-auto") return null;
649
+ const aliases = boot.runtime.aliases ?? [];
650
+ if (!aliases.some((a) => a.tier)) return null;
651
+ const primary = boot.runtime.primaryModel || aliases.find((a) => a.tier === "MEDIUM")?.alias || null;
652
+ if (!primary) return null;
653
+ const decision = classifyCodeTurn(signals);
654
+ const max = decision.hard ? aliases.find((a) => a.tier === "COMPLEX") ?? aliases.find((a) => a.alias === "sq-auto-max") : void 0;
655
+ const chosen = max ?? aliases.find((a) => a.alias === primary) ?? { alias: primary, reasoning: null, tier: "MEDIUM" };
656
+ const fallback = decision.hard && !max;
657
+ const kind = decision.hard ? "hard task" : "everyday task";
658
+ return {
659
+ alias: chosen.alias,
660
+ tier: chosen.tier ?? null,
661
+ hard: decision.hard,
662
+ reason: decision.reason,
663
+ label: `SQ Auto \xB7 ${kind} \xB7 ${decision.reason}${fallback ? " (no hard-task model available, main model used)" : ""}`,
664
+ capability: chosen.reasoning ?? null,
665
+ maxOutputTokens: typeof chosen.maxOutputTokens === "number" && chosen.maxOutputTokens > 0 ? chosen.maxOutputTokens : null,
666
+ fallback
667
+ };
627
668
  }
628
- var MAX_LINE = 8 * 1024 * 1024;
629
- var MAX_FILES = IMPORT_LIMITS.maxScanItems;
630
- var READ_WINDOW_BYTES = 4 * IMPORT_LIMITS.maxBytes;
631
- var WRAPPER = /^\s*<(command-[a-z-]+|local-command-[a-z-]+|bash-(?:input|stdout|stderr)|task-notification|system-reminder|user-prompt-submit-hook|environment_context|user_instructions|permissions instructions|recommended_plugins|app-context|skills_instructions|collaboration_mode|multi_agent_[a-z_]+|turn_aborted|user_shell_command)[^>]*>/i;
632
- var oneLine = (s, max) => {
633
- const t = s.replace(/\s+/g, " ").trim();
634
- return t.length > max ? `${t.slice(0, max - 1)}\u2026` : t;
669
+ function credentialAlias(raw) {
670
+ const tier = raw.tier === "LIGHT" || raw.tier === "MEDIUM" || raw.tier === "COMPLEX" ? raw.tier : null;
671
+ const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? Math.trunc(v) : null;
672
+ const max = positive(raw.maxOutputTokens), organization = positive(raw.organizationMaxOutputTokens);
673
+ const source = raw.outputLimitSource === "ORGANIZATION" || raw.outputLimitSource === "MODEL" || raw.outputLimitSource === "PLATFORM" ? raw.outputLimitSource : null;
674
+ const s = raw.stream && typeof raw.stream === "object" ? raw.stream : null;
675
+ const mode = s && (s.mode === "LIVE" || s.mode === "BUFFERED" || s.mode === "BUFFERED_POLICY") ? s.mode : null;
676
+ const reason = s && STREAM_REASONS.includes(s.reason) ? s.reason : null;
677
+ return {
678
+ alias: String(raw.alias),
679
+ reasoning: parseReasoningCapability(raw.reasoning),
680
+ ...tier ? { tier } : {},
681
+ ...max ? { maxOutputTokens: max } : {},
682
+ ...source ? { outputLimitSource: source } : {},
683
+ ...organization ? { organizationMaxOutputTokens: organization } : {},
684
+ ...mode ? { stream: { mode, ...reason ? { reason } : {} } } : {}
685
+ };
686
+ }
687
+
688
+ // src/application/services/workspaceSandbox/SessionTransport.ts
689
+ var SessionGoneError = class extends Error {
690
+ constructor(status) {
691
+ super(`session unavailable (${status})`);
692
+ this.status = status;
693
+ this.name = "SessionGoneError";
694
+ }
695
+ status;
635
696
  };
636
- function summarizeToolInput(name, input) {
637
- if (typeof input === "string") {
638
- try {
639
- return summarizeToolInput(name, JSON.parse(input));
640
- } catch {
641
- return oneLine(input, IMPORT_LIMITS.maxToolSummary);
642
- }
697
+ var TransportError = class extends Error {
698
+ constructor(message, status, retryable) {
699
+ super(message);
700
+ this.status = status;
701
+ this.retryable = retryable;
702
+ this.name = "TransportError";
643
703
  }
644
- if (!input || typeof input !== "object") return "";
645
- const o = input;
646
- for (const key of ["command", "cmd", "file_path", "path", "notebook_path", "pattern", "query", "url", "description", "prompt"]) {
647
- const v = o[key];
648
- if (typeof v === "string" && v.trim()) return oneLine(v, IMPORT_LIMITS.maxToolSummary);
649
- if (Array.isArray(v) && v.every((x) => typeof x === "string")) return oneLine(v.join(" "), IMPORT_LIMITS.maxToolSummary);
704
+ status;
705
+ retryable;
706
+ /** The API's error code (e.g. REPOSITORY_NOT_IN_SCOPE), when it sent one. */
707
+ code;
708
+ };
709
+
710
+ // src/application/services/workspaceSandbox/HttpSessionTransport.ts
711
+ var GONE = [401, 403, 404, 410];
712
+ var HttpSessionTransport = class {
713
+ constructor(opts) {
714
+ this.opts = opts;
715
+ this.fetchImpl = opts.fetchImpl ?? fetch;
716
+ this.gone = new Set(opts.goneStatuses ?? GONE);
717
+ const base = opts.baseUrl.replace(/\/+$/, "");
718
+ this.root = `${base}/api/ai-governance/internal/workspace-sessions/${encodeURIComponent(opts.sessionId)}`;
650
719
  }
651
- const first = Object.values(o).find((v) => typeof v === "string" && v.trim());
652
- return first ? oneLine(first, IMPORT_LIMITS.maxToolSummary) : "";
653
- }
654
- async function* lines(file) {
655
- const rl = (0, import_readline.createInterface)({ input: (0, import_fs.createReadStream)(file, { encoding: "utf8" }), crlfDelay: Infinity });
656
- try {
657
- for await (const line of rl) {
658
- if (!line || line.length > MAX_LINE || line[0] !== "{") continue;
659
- try {
660
- const rec = JSON.parse(line);
661
- if (rec && typeof rec === "object") yield rec;
662
- } catch {
663
- }
664
- }
665
- } finally {
666
- rl.close();
720
+ opts;
721
+ fetchImpl;
722
+ root;
723
+ gone;
724
+ async bootstrap() {
725
+ const raw = await this.get(`/bootstrap?features=${ENGINE_FEATURES.join(",")}`, this.opts.requestTimeoutMs ?? 3e4);
726
+ return toSessionBootstrap(raw);
667
727
  }
668
- }
669
- var MessageWindow = class {
670
- messages = [];
671
- bytes = 0;
672
- total = 0;
673
- push(m) {
674
- this.messages.push(m);
675
- this.total++;
676
- this.bytes += importBytes([m]);
677
- while (this.bytes > READ_WINDOW_BYTES && this.messages.length > 1) this.bytes -= importBytes([this.messages.shift()]);
728
+ async pollCommands(waitSeconds, signal) {
729
+ const wait = Math.max(0, Math.min(25, Math.floor(waitSeconds)));
730
+ const body = await this.get(`/commands?wait=${wait}`, (wait + 15) * 1e3, signal);
731
+ return Array.isArray(body?.commands) ? body.commands : [];
678
732
  }
679
- get last() {
680
- return this.messages[this.messages.length - 1];
733
+ async postEvents(events) {
734
+ if (events.length === 0) return;
735
+ await this.post("/events", { events }, this.opts.requestTimeoutMs ?? 3e4);
681
736
  }
682
- grow(m, text3, tools) {
683
- const before = importBytes([m]);
684
- if (text3) m.text = m.text ? `${m.text}
685
-
686
- ${text3}` : text3;
687
- if (tools.length) m.tools = [...m.tools ?? [], ...tools].slice(0, IMPORT_LIMITS.maxToolsPerMessage);
688
- this.bytes += importBytes([m]) - before;
737
+ callTool(name, args, approvalId) {
738
+ if (!/^[a-z][a-z0-9_]{0,63}$/.test(name)) throw new TransportError("invalid tool name", null, false);
739
+ return this.post(`/tools/${name}`, approvalId ? { args, approvalId } : { args }, this.opts.toolTimeoutMs ?? 12e4);
689
740
  }
690
- };
691
- function claudeText(content, role) {
692
- if (typeof content === "string") return { text: role === "user" && WRAPPER.test(content) ? "" : content, tools: [] };
693
- if (!Array.isArray(content)) return { text: "", tools: [] };
694
- const texts = [];
695
- const tools = [];
696
- for (const b of content) {
697
- if (b?.type === "text" && typeof b.text === "string" && !(role === "user" && WRAPPER.test(b.text))) texts.push(b.text);
698
- else if (b?.type === "tool_use" && typeof b.name === "string") tools.push({ name: b.name.slice(0, 200), summary: summarizeToolInput(b.name, b.input) });
699
- else if (b?.type === "image") texts.push("[image]");
741
+ async mcpTools() {
742
+ const raw = await this.get("/mcp/tools", this.opts.requestTimeoutMs ?? 6e4);
743
+ return orgMcpCatalogOf(raw);
700
744
  }
701
- return { text: texts.join("\n\n").trim(), tools };
702
- }
703
- async function parseClaudeCodeFile(file, opts = {}) {
704
- const externalId = (0, import_path.basename)(file, ".jsonl");
705
- if (!isExternalId(externalId)) return null;
706
- const win = new MessageWindow();
707
- let folder = null;
708
- let customTitle = null;
709
- let aiTitle = null;
710
- let summary = null;
711
- let firstUser = null;
712
- let current = null;
713
- for await (const r of lines(file)) {
714
- if (opts.probe && win.total) break;
715
- if (!folder && typeof r.cwd === "string") folder = r.cwd;
716
- if (r.type === "custom-title" && typeof r.customTitle === "string") customTitle = r.customTitle;
717
- else if (r.type === "ai-title" && typeof r.aiTitle === "string") aiTitle = r.aiTitle;
718
- else if (r.type === "summary" && typeof r.summary === "string") summary = r.summary;
719
- if (r.type !== "user" && r.type !== "assistant" || r.isSidechain === true || r.isMeta === true) continue;
720
- const role = r.type;
721
- const { text: text3, tools } = claudeText(r.message?.content, role);
722
- const at = typeof r.timestamp === "string" ? r.timestamp : null;
723
- if (role === "assistant") {
724
- const id = typeof r.message?.id === "string" ? r.message.id : null;
725
- if (!current || !id || current.id !== id) current = { id, msg: null, at };
726
- if (!text3 && !tools.length) continue;
727
- if (current.msg && win.last === current.msg) win.grow(current.msg, text3, tools);
728
- else {
729
- current.msg = { role, text: text3, at: current.at ?? at, ...tools.length ? { tools } : {} };
730
- win.push(current.msg);
745
+ mcpCall(req) {
746
+ return this.post("/mcp/call", req, this.opts.toolTimeoutMs ?? 18e4);
747
+ }
748
+ openPullRequest(req) {
749
+ return this.post("/pull-request", req, this.opts.toolTimeoutMs ?? 18e4);
750
+ }
751
+ async checkpoint(req) {
752
+ await this.post("/checkpoint", req, this.opts.requestTimeoutMs ?? 6e4);
753
+ }
754
+ async openRepository(repoFullName) {
755
+ const raw = await this.post("/repositories/open", { repoFullName }, this.opts.requestTimeoutMs ?? 3e4);
756
+ const text3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
757
+ const defaultBranch = text3(raw?.defaultBranch, "main");
758
+ return {
759
+ cloneUrl: text3(raw?.cloneUrl),
760
+ scheme: text3(raw?.scheme),
761
+ token: text3(raw?.token),
762
+ provider: text3(raw?.provider),
763
+ repoFullName: text3(raw?.repoFullName, repoFullName),
764
+ defaultBranch,
765
+ branch: typeof raw?.branch === "string" ? raw.branch : defaultBranch
766
+ };
767
+ }
768
+ async importedHistory() {
769
+ const raw = await this.get("/imported", this.opts.requestTimeoutMs ?? 6e4);
770
+ const messages = Array.isArray(raw?.messages) ? raw.messages : [];
771
+ return {
772
+ messages: messages.filter((m) => !!m && typeof m === "object" && (m.role === "user" || m.role === "assistant")).map((m) => ({
773
+ role: m.role,
774
+ text: typeof m.text === "string" ? m.text : "",
775
+ at: typeof m.at === "string" ? m.at : null,
776
+ ...Array.isArray(m.tools) ? { tools: m.tools.filter((t) => !!t && typeof t.name === "string" && typeof t.summary === "string") } : {}
777
+ }))
778
+ };
779
+ }
780
+ async saveTranscript(t) {
781
+ await this.post("/transcript", t, this.opts.requestTimeoutMs ?? 6e4);
782
+ }
783
+ async saveBaseMeasurement(r) {
784
+ await this.post("/measurements/base", r, this.opts.requestTimeoutMs ?? 3e4);
785
+ }
786
+ async reply(commandId, reply) {
787
+ if (!/^[A-Za-z0-9-]{1,80}$/.test(commandId)) throw new TransportError("invalid command id", null, false);
788
+ await this.post(`/replies/${commandId}`, reply, this.opts.requestTimeoutMs ?? 3e4);
789
+ }
790
+ async refreshRuntime() {
791
+ const raw = await this.post("/runtime", {}, this.opts.requestTimeoutMs ?? 3e4);
792
+ const boot = toSessionBootstrap({ session: { model: raw?.model, reasoning: raw?.reasoning }, runtime: raw?.runtime, repository: null });
793
+ return { model: boot.model, runtime: boot.runtime, reasoning: boot.reasoning ?? null };
794
+ }
795
+ headers(json) {
796
+ return {
797
+ "x-workspace-session-secret": this.opts.secret,
798
+ accept: "application/json",
799
+ ...this.opts.userAgent ? { "user-agent": this.opts.userAgent } : {},
800
+ ...json ? { "content-type": "application/json" } : {}
801
+ };
802
+ }
803
+ async get(path, timeoutMs, outer) {
804
+ const attempts = Math.max(1, this.opts.getAttempts ?? 4);
805
+ const delay = this.opts.retryDelayMs ?? 1e3;
806
+ let last;
807
+ for (let i = 0; i < attempts; i++) {
808
+ if (outer?.aborted) throw new TransportError("aborted", null, false);
809
+ try {
810
+ return await this.request("GET", path, void 0, timeoutMs, outer);
811
+ } catch (e) {
812
+ last = e;
813
+ if (e instanceof SessionGoneError) throw e;
814
+ if (e instanceof TransportError && !e.retryable) throw e;
815
+ if (i < attempts - 1) await sleep(delay * 2 ** i, outer);
731
816
  }
732
- continue;
733
817
  }
734
- const onlyToolResults = Array.isArray(r.message?.content) && r.message.content.every((b) => b?.type === "tool_result");
735
- if (!onlyToolResults) current = null;
736
- if (!text3) continue;
737
- const shown = r.isCompactSummary === true ? `[Summary of the earlier conversation]
738
- ${text3}` : text3;
739
- if (!firstUser && r.isCompactSummary !== true) firstUser = text3;
740
- win.push({ role, text: shown, at });
818
+ throw last;
741
819
  }
742
- if (!win.total) return null;
743
- const title = oneLine(customTitle || aiTitle || summary || firstUser || "Untitled conversation", IMPORT_LIMITS.maxTitle);
744
- const st = await (0, import_promises.lstat)(file).catch(() => null);
745
- return { source: "CLAUDE_CODE", externalId, title, folder, messages: win.messages, updatedAt: st ? st.mtime.toISOString() : null };
746
- }
747
- async function regularFiles(dir, match) {
748
- const out2 = [];
749
- const names = await (0, import_promises.readdir)(dir).catch(() => []);
750
- for (const name of names) {
751
- if (!match(name)) continue;
752
- const st = await (0, import_promises.lstat)((0, import_path.join)(dir, name)).catch(() => null);
753
- if (st?.isFile()) out2.push({ path: (0, import_path.join)(dir, name), mtime: st.mtimeMs });
820
+ post(path, body, timeoutMs) {
821
+ return this.request("POST", path, body, timeoutMs);
754
822
  }
755
- return out2;
756
- }
757
- async function subdirs(dir) {
758
- const out2 = [];
759
- for (const name of await (0, import_promises.readdir)(dir).catch(() => [])) {
760
- const st = await (0, import_promises.lstat)((0, import_path.join)(dir, name)).catch(() => null);
761
- if (st?.isDirectory()) out2.push((0, import_path.join)(dir, name));
823
+ async request(method, path, body, timeoutMs, outer) {
824
+ const signals = [AbortSignal.timeout(timeoutMs), ...outer ? [outer] : []];
825
+ const signal = signals.length > 1 ? anySignal(signals) : signals[0];
826
+ let res;
827
+ try {
828
+ res = await this.fetchImpl(this.root + path, {
829
+ method,
830
+ headers: this.headers(body !== void 0),
831
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
832
+ signal
833
+ });
834
+ } catch (e) {
835
+ throw new TransportError(`${method} ${routeLabel(path)} failed: ${e.name}`, null, true);
836
+ }
837
+ if (this.gone.has(res.status)) {
838
+ await res.body?.cancel().catch(() => void 0);
839
+ throw new SessionGoneError(res.status);
840
+ }
841
+ if (!res.ok) {
842
+ const code = await res.text().then((t) => JSON.parse(t)?.code, () => void 0).catch(() => void 0);
843
+ const retryable = res.status === 429 || res.status >= 500;
844
+ const error = new TransportError(`${method} ${routeLabel(path)} answered ${res.status}`, res.status, retryable);
845
+ if (typeof code === "string" && /^[A-Z_]{3,80}$/.test(code)) error.code = code;
846
+ throw error;
847
+ }
848
+ if (res.status === 204) return void 0;
849
+ const text3 = await res.text();
850
+ if (!text3) return void 0;
851
+ try {
852
+ return JSON.parse(text3);
853
+ } catch {
854
+ throw new TransportError(`${method} ${routeLabel(path)} returned invalid JSON`, res.status, false);
855
+ }
762
856
  }
763
- return out2;
857
+ };
858
+ function anySignal(signals) {
859
+ const native = AbortSignal.any;
860
+ if (typeof native === "function") return native.call(AbortSignal, signals);
861
+ const ac = new AbortController();
862
+ for (const s of signals) {
863
+ if (s.aborted) {
864
+ ac.abort(s.reason);
865
+ return ac.signal;
866
+ }
867
+ s.addEventListener("abort", () => ac.abort(s.reason), { once: true });
868
+ }
869
+ return ac.signal;
764
870
  }
765
- async function claudeCodeFiles(claudeDir) {
766
- const files = [];
767
- for (const project of await subdirs((0, import_path.join)(claudeDir, "projects"))) files.push(...await regularFiles(project, (n) => n.endsWith(".jsonl")));
768
- return files;
871
+ function routeLabel(path) {
872
+ return path.split("?")[0];
769
873
  }
770
- function codexText(content, role) {
771
- if (typeof content === "string") return content;
772
- if (!Array.isArray(content)) return "";
773
- const want = role === "user" ? "input_text" : "output_text";
774
- return content.filter((b) => b?.type === want && typeof b.text === "string" && !(role === "user" && WRAPPER.test(b.text))).map((b) => b.text).join("\n\n").trim();
874
+ function sleep(ms, signal) {
875
+ return new Promise((resolve8) => {
876
+ const t = setTimeout(resolve8, ms);
877
+ signal?.addEventListener("abort", () => {
878
+ clearTimeout(t);
879
+ resolve8();
880
+ }, { once: true });
881
+ });
775
882
  }
776
- async function parseCodexFile(file, opts = {}) {
777
- const fromName = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i.exec(file)?.[1] ?? null;
778
- let externalId = null;
779
- let folder = null;
780
- let firstUser = null;
781
- const win = new MessageWindow();
782
- for await (const r of lines(file)) {
783
- if (opts.probe && win.total && externalId) break;
784
- const p = r.payload;
785
- if (!p || typeof p !== "object") continue;
786
- if (r.type === "session_meta") {
787
- if (p.source && typeof p.source === "object" && p.source.subagent) return null;
788
- if (!externalId && typeof p.id === "string") externalId = p.id;
789
- if (!folder && typeof p.cwd === "string") folder = p.cwd;
790
- continue;
791
- }
792
- if (r.type !== "response_item") continue;
793
- const at = typeof r.timestamp === "string" ? r.timestamp : null;
794
- if (p.type === "message" && (p.role === "user" || p.role === "assistant")) {
795
- const text3 = codexText(p.content, p.role);
796
- if (!text3) continue;
797
- if (p.role === "user" && !firstUser) firstUser = text3;
798
- const last = win.last;
799
- if (p.role === "assistant" && last?.role === "assistant" && !last.text) win.grow(last, text3, []);
800
- else win.push({ role: p.role, text: text3, at });
801
- } else if ((p.type === "function_call" || p.type === "custom_tool_call" || p.type === "local_shell_call") && (typeof p.name === "string" || p.type === "local_shell_call")) {
802
- const name = typeof p.name === "string" ? p.name : "shell";
803
- const tool = { name: name.slice(0, 200), summary: summarizeToolInput(name, p.arguments ?? p.input ?? p.action) };
804
- const last = win.last;
805
- if (last?.role === "assistant") win.grow(last, "", [tool]);
806
- else win.push({ role: "assistant", text: "", at, tools: [tool] });
807
- }
808
- }
809
- const id = externalId ?? fromName;
810
- if (!id || !isExternalId(id) || !win.total) return null;
811
- const st = await (0, import_promises.lstat)(file).catch(() => null);
883
+ var ENGINE_FEATURES = ["message_queue_v1"];
884
+ function toSessionBootstrap(raw) {
885
+ if (raw && raw.repo && !raw.repository) return raw;
886
+ const session = raw?.session ?? {};
887
+ const repository = raw?.repository && typeof raw.repository === "object" ? raw.repository : null;
888
+ const runtime = raw?.runtime ?? {};
889
+ const text3 = (v, fallback = "") => typeof v === "string" ? v : fallback;
890
+ const defaultBranch = text3(repository?.defaultBranch, "main");
891
+ const repo2 = repository ? {
892
+ cloneUrl: text3(repository.cloneUrl),
893
+ scheme: text3(repository.scheme),
894
+ token: text3(repository.token),
895
+ provider: text3(repository.provider, text3(session.provider)),
896
+ repoFullName: text3(repository.repoFullName, text3(session.repoFullName)),
897
+ defaultBranch
898
+ } : null;
899
+ const repositories = scopeRepos(raw?.repositories) ?? scopeRepos(session.scope?.repos) ?? (repo2 ? [{ repoFullName: repo2.repoFullName, provider: repo2.provider, projectId: text3(session.projectId), defaultBranch: repo2.defaultBranch }] : []);
900
+ const rawScope = session.scope && typeof session.scope === "object" ? session.scope : null;
901
+ const kind = rawScope?.kind === "ALL" || rawScope?.kind === "TEAM" || rawScope?.kind === "BUSINESS_AREA" ? rawScope.kind : "PROJECTS";
812
902
  return {
813
- source: "CODEX",
814
- externalId: id,
815
- title: oneLine(firstUser || "Untitled conversation", IMPORT_LIMITS.maxTitle),
816
- folder,
817
- messages: win.messages,
818
- updatedAt: st ? st.mtime.toISOString() : null
819
- };
820
- }
821
- async function codexFiles(codexDir) {
822
- const files = [];
823
- const walk = async (dir, depth) => {
824
- files.push(...await regularFiles(dir, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl")));
825
- if (depth < 4) for (const sub of await subdirs(dir)) await walk(sub, depth + 1);
903
+ repo: repo2,
904
+ repositories,
905
+ scope: {
906
+ kind,
907
+ teamId: typeof rawScope?.teamId === "string" ? rawScope.teamId : null,
908
+ projectIds: Array.isArray(rawScope?.projectIds) ? rawScope.projectIds.filter((p) => typeof p === "string") : typeof session.projectId === "string" && session.projectId ? [session.projectId] : [],
909
+ repos: repositories
910
+ },
911
+ branch: text3(session.branch, repo2 ? defaultBranch : ""),
912
+ projectId: text3(session.projectId),
913
+ model: text3(session.model, "sq-auto"),
914
+ runtime: {
915
+ baseUrl: text3(runtime.baseUrl),
916
+ token: text3(runtime.token),
917
+ models: runtime.models,
918
+ primaryModel: typeof runtime.primaryModel === "string" ? runtime.primaryModel : null,
919
+ fastModel: typeof runtime.fastModel === "string" ? runtime.fastModel : null,
920
+ maxOutputTokens: typeof runtime.maxOutputTokens === "number" ? runtime.maxOutputTokens : null,
921
+ maxOutputTokensCeiling: typeof runtime.maxOutputTokensCeiling === "number" ? runtime.maxOutputTokensCeiling : null,
922
+ reasoning: parseReasoningCapability(runtime.reasoning),
923
+ aliases: Array.isArray(runtime.aliases) ? runtime.aliases.filter((a) => typeof a?.alias === "string").map(credentialAlias) : void 0
924
+ },
925
+ reasoning: isReasoningLevel(session.reasoning) ? session.reasoning : null,
926
+ imported: importedInfo(session.imported),
927
+ checkpointPatch: typeof raw?.checkpointPatch === "string" ? raw.checkpointPatch : null,
928
+ sdkSessionId: typeof session.sdkSessionId === "string" ? session.sdkSessionId : null,
929
+ workspaceKind: session.workspaceKind === "LOCAL" ? "LOCAL" : "CLOUD",
930
+ projectName: typeof session.projectName === "string" ? session.projectName : typeof raw?.project?.name === "string" ? raw.project.name : null,
931
+ folderLink: typeof session.folderLink?.projectId === "string" && session.folderLink.projectId ? { projectId: session.folderLink.projectId } : null,
932
+ transcript: transcriptOf(raw?.transcript),
933
+ baseMeasurements: raw?.baseMeasurements && typeof raw.baseMeasurements === "object" && !Array.isArray(raw.baseMeasurements) ? raw.baseMeasurements : {},
934
+ pullRequests: pullRequestsOf(raw?.pullRequests),
935
+ skills: skillsOf(raw?.skills),
936
+ plugins: pluginsOf(raw?.plugins),
937
+ environments: environmentsOf(raw?.environments)
826
938
  };
827
- await walk((0, import_path.join)(codexDir, "sessions"), 0);
828
- return files;
829
939
  }
830
- function secretsIn(c) {
831
- let n = 0;
832
- for (const m of c.messages) {
833
- n += countSecrets(m.text);
834
- for (const t of m.tools ?? []) n += countSecrets(t.summary);
835
- }
836
- return n;
940
+ function transcriptOf(raw) {
941
+ const t = raw;
942
+ if (!t || typeof t !== "object" || typeof t.sdkSessionId !== "string" || t.encoding !== "gzip-base64" || typeof t.data !== "string") return null;
943
+ return { sdkSessionId: t.sdkSessionId, encoding: "gzip-base64", data: t.data, bytes: typeof t.bytes === "number" ? t.bytes : 0, sha256: typeof t.sha256 === "string" ? t.sha256 : "" };
837
944
  }
838
- async function importFiles(sources) {
839
- return [
840
- ...(await claudeCodeFiles(sources.claudeDir)).map((f) => ({ ...f, source: "CLAUDE_CODE" })),
841
- ...(await codexFiles(sources.codexDir)).map((f) => ({ ...f, source: "CODEX" }))
842
- ].sort((a, b) => b.mtime - a.mtime).slice(0, MAX_FILES);
945
+ var strings = (v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).filter(([, x]) => typeof x === "string")) : {};
946
+ function environmentsOf(raw) {
947
+ if (!Array.isArray(raw)) return [];
948
+ return raw.filter((e) => !!e && typeof e === "object" && typeof e.projectId === "string").slice(0, 50).map((e) => ({ projectId: e.projectId, setupCommand: typeof e.setupCommand === "string" && e.setupCommand.trim() ? e.setupCommand : null, env: strings(e.env), secrets: strings(e.secrets) }));
843
949
  }
844
- async function scanImports(sources) {
845
- const files = await importFiles(sources);
846
- const items = [];
847
- const seen = /* @__PURE__ */ new Set();
848
- for (const f of files) {
849
- const c = await (f.source === "CLAUDE_CODE" ? parseClaudeCodeFile(f.path) : parseCodexFile(f.path)).catch(() => null);
850
- if (!c || seen.has(`${c.source}:${c.externalId}`)) continue;
851
- seen.add(`${c.source}:${c.externalId}`);
852
- items.push({
853
- source: c.source,
854
- externalId: c.externalId,
855
- title: oneLine(scrubSecrets(c.title).text, IMPORT_LIMITS.maxTitle),
856
- folder: c.folder,
857
- messageCount: Math.min(c.messages.length, IMPORT_LIMITS.maxMessages),
858
- updatedAt: c.updatedAt,
859
- secretsFound: secretsIn(c)
860
- });
861
- }
862
- return items;
950
+ function pluginsOf(raw) {
951
+ if (!Array.isArray(raw)) return [];
952
+ return raw.filter((p) => !!p && typeof p === "object" && typeof p.name === "string" && Array.isArray(p.files)).slice(0, 20).map((p) => ({ name: p.name, files: p.files.filter((f) => !!f && typeof f.path === "string" && typeof f.content === "string").slice(0, 300) }));
863
953
  }
864
- async function countImports(sources) {
865
- const counts = { claudeCode: 0, codex: 0 };
866
- const seen = /* @__PURE__ */ new Set();
867
- for (const f of await importFiles(sources)) {
868
- const c = await (f.source === "CLAUDE_CODE" ? parseClaudeCodeFile(f.path, { probe: true }) : parseCodexFile(f.path, { probe: true })).catch(() => null);
869
- if (!c || seen.has(`${c.source}:${c.externalId}`)) continue;
870
- seen.add(`${c.source}:${c.externalId}`);
871
- if (c.source === "CLAUDE_CODE") counts.claudeCode++;
872
- else counts.codex++;
873
- }
874
- return counts;
954
+ function skillsOf(raw) {
955
+ if (!Array.isArray(raw)) return [];
956
+ return raw.filter((s) => !!s && typeof s === "object").filter((s) => typeof s.name === "string" && typeof s.body === "string").slice(0, 200).map((s) => ({
957
+ name: s.name,
958
+ description: typeof s.description === "string" ? s.description : "",
959
+ body: s.body,
960
+ argumentHint: typeof s.argumentHint === "string" ? s.argumentHint : null
961
+ }));
875
962
  }
876
- async function conversationFolders(sources, deadline) {
877
- const claude = [];
878
- for (const project of await subdirs((0, import_path.join)(sources.claudeDir, "projects"))) {
879
- if (Date.now() > deadline) break;
880
- const newest = (await regularFiles(project, (n) => n.endsWith(".jsonl"))).sort((a, b) => b.mtime - a.mtime)[0];
881
- if (newest) claude.push({ ...newest, source: "CLAUDE_CODE" });
882
- }
883
- const codex = Date.now() > deadline ? [] : (await codexFiles(sources.codexDir)).map((f) => ({ ...f, source: "CODEX" }));
884
- const files = [...claude, ...codex].sort((a, b) => b.mtime - a.mtime).slice(0, MAX_FILES);
885
- const out2 = [];
886
- const seen = /* @__PURE__ */ new Set();
887
- for (const f of files) {
888
- if (Date.now() > deadline) break;
889
- let n = 0;
890
- try {
891
- for await (const r of lines(f.path)) {
892
- if (++n > 50) break;
893
- const cwd = f.source === "CLAUDE_CODE" ? r.cwd : r.type === "session_meta" && r.payload && typeof r.payload === "object" && !(r.payload.source && typeof r.payload.source === "object" && r.payload.source.subagent) ? r.payload.cwd : void 0;
894
- if (typeof cwd === "string" && cwd) {
895
- if (!seen.has(cwd)) {
896
- seen.add(cwd);
897
- out2.push(cwd);
898
- }
899
- break;
900
- }
901
- }
902
- } catch {
903
- }
963
+ function pullRequestsOf(raw) {
964
+ const out2 = {};
965
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return out2;
966
+ for (const [repo2, v] of Object.entries(raw)) {
967
+ if (!v || typeof v.branch !== "string") continue;
968
+ out2[repo2] = {
969
+ branch: v.branch,
970
+ number: typeof v.number === "number" ? v.number : null,
971
+ url: typeof v.url === "string" ? v.url : null,
972
+ files: Array.isArray(v.files) ? v.files.filter((f) => typeof f === "string") : []
973
+ };
904
974
  }
905
975
  return out2;
906
976
  }
907
- async function findConversation(sources, source, externalId) {
908
- if (!isExternalId(externalId)) return null;
909
- if (source === "CLAUDE_CODE") {
910
- const file = (await claudeCodeFiles(sources.claudeDir)).find((f) => (0, import_path.basename)(f.path, ".jsonl") === externalId);
911
- return file ? parseClaudeCodeFile(file.path) : null;
912
- }
913
- const files = (await codexFiles(sources.codexDir)).filter((f) => f.path.includes(externalId)).sort((a, b) => b.mtime - a.mtime);
914
- for (const f of files) {
915
- const c = await parseCodexFile(f.path).catch(() => null);
916
- if (c?.externalId === externalId) return c;
917
- }
918
- return null;
919
- }
920
- function prepareImport(c) {
921
- let removed = 0;
922
- const scrub = (s) => {
923
- const r = scrubSecrets(s);
924
- removed += r.count;
925
- return r.text;
977
+ function importedInfo(raw) {
978
+ if (!raw || typeof raw !== "object") return null;
979
+ const r = raw;
980
+ if (r.source !== "CLAUDE_CODE" && r.source !== "CODEX" || !isExternalId(r.externalId)) return null;
981
+ return {
982
+ source: r.source,
983
+ externalId: r.externalId,
984
+ title: typeof r.title === "string" ? r.title : "",
985
+ folder: typeof r.folder === "string" ? r.folder : null,
986
+ messageCount: typeof r.messageCount === "number" ? r.messageCount : 0,
987
+ nativeResume: r.nativeResume === true
926
988
  };
927
- const cap = (s, max) => Buffer.byteLength(s) > max ? `${Buffer.from(s).subarray(0, max - 32).toString("utf8").replace(/�+$/, "")}
928
- [... cut ...]` : s;
929
- const messages = c.messages.map((m) => ({
930
- role: m.role,
931
- text: cap(scrub(m.text), IMPORT_LIMITS.maxMessageBytes),
932
- at: m.at,
933
- ...m.tools?.length ? { tools: m.tools.slice(0, IMPORT_LIMITS.maxToolsPerMessage).map((t) => ({ name: t.name, summary: oneLine(scrub(t.summary), IMPORT_LIMITS.maxToolSummary) })) } : {}
934
- }));
935
- const title = oneLine(scrubSecrets(c.title).text, IMPORT_LIMITS.maxTitle) || "Untitled conversation";
936
- let start = Math.max(0, messages.length - IMPORT_LIMITS.maxMessages);
937
- let bytes = importBytes(messages.slice(start));
938
- while (bytes > IMPORT_LIMITS.maxBytes && start < messages.length - 1) bytes -= importBytes([messages[start++]]);
939
- return { source: c.source, externalId: c.externalId, folder: c.folder, title, secretsRemoved: removed, messages: messages.slice(start), omitted: start };
940
989
  }
941
- function scrubRecord(record) {
942
- let removed = 0;
943
- const walk = (value, key) => {
944
- if (typeof value === "string") {
945
- if (key === "signature" || key === "data") return value;
946
- const r = scrubSecrets(value);
947
- removed += r.count;
948
- return r.text;
949
- }
950
- if (Array.isArray(value)) {
951
- const out2 = [];
952
- for (const item of value) {
953
- const block = item;
954
- if (block && typeof block === "object" && block.type === "thinking" && typeof block.thinking === "string") {
955
- const r = scrubSecrets(block.thinking);
956
- if (r.count) {
957
- removed += r.count;
958
- continue;
959
- }
960
- out2.push(item);
961
- continue;
962
- }
963
- if (block && typeof block === "object" && block.type === "redacted_thinking") {
964
- out2.push(item);
965
- continue;
966
- }
967
- out2.push(walk(item));
968
- }
969
- return out2;
970
- }
971
- if (value && typeof value === "object") {
972
- const o = value;
973
- for (const k of Object.keys(o)) o[k] = walk(o[k], k);
974
- return o;
975
- }
976
- return value;
977
- };
978
- walk(record);
979
- return removed;
990
+ function scopeRepos(raw) {
991
+ if (!Array.isArray(raw)) return null;
992
+ return raw.filter((r) => r && typeof r === "object" && typeof r.repoFullName === "string").map((r) => ({
993
+ repoFullName: r.repoFullName,
994
+ provider: typeof r.provider === "string" ? r.provider : "",
995
+ projectId: typeof r.projectId === "string" ? r.projectId : "",
996
+ defaultBranch: typeof r.defaultBranch === "string" ? r.defaultBranch : null
997
+ }));
980
998
  }
981
- async function writeScrubbedTranscript(source, target) {
982
- const out2 = (0, import_fs.createWriteStream)(target, { mode: 384, flags: "wx" });
983
- let removed = 0;
984
- const done = new Promise((resolve8, reject) => {
985
- out2.on("finish", resolve8);
986
- out2.on("error", reject);
987
- });
988
- const rl = (0, import_readline.createInterface)({ input: (0, import_fs.createReadStream)(source, { encoding: "utf8" }), crlfDelay: Infinity });
989
- try {
990
- for await (const line of rl) {
991
- if (!line) continue;
992
- let record;
993
- try {
994
- record = JSON.parse(line);
995
- } catch {
996
- continue;
997
- }
998
- removed += scrubRecord(record);
999
- if (!out2.write(`${JSON.stringify(record)}
1000
- `)) await new Promise((r) => out2.once("drain", () => r()));
1001
- }
1002
- } finally {
1003
- rl.close();
1004
- out2.end();
1005
- }
1006
- await done;
1007
- return removed;
999
+ function orgMcpCatalogOf(raw) {
1000
+ const r = raw ?? {};
1001
+ const servers = (Array.isArray(r.servers) ? r.servers : []).filter((s) => !!s && typeof s === "object" && typeof s.server === "string" && /^[a-z0-9][a-z0-9-]{0,31}$/.test(s.server)).slice(0, 20).map((s) => ({
1002
+ server: s.server,
1003
+ description: typeof s.description === "string" ? s.description : null,
1004
+ approval: typeof s.approval === "string" ? s.approval : "WRITE",
1005
+ tools: (Array.isArray(s.tools) ? s.tools : []).filter((t) => !!t && typeof t === "object" && typeof t.name === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(t.name)).slice(0, 100).map((t) => ({
1006
+ name: t.name,
1007
+ description: typeof t.description === "string" ? t.description : "",
1008
+ inputSchema: t.inputSchema && typeof t.inputSchema === "object" && !Array.isArray(t.inputSchema) ? t.inputSchema : { type: "object" },
1009
+ readOnly: t.readOnly === true
1010
+ }))
1011
+ }));
1012
+ const unavailable = (Array.isArray(r.unavailable) ? r.unavailable : []).filter((u) => !!u && typeof u.server === "string").map((u) => ({ server: String(u.server).slice(0, 32), error: typeof u.error === "string" ? u.error.slice(0, 60) : "MCP_FAILED" }));
1013
+ return { servers, unavailable };
1008
1014
  }
1009
1015
 
1010
- // src/application/services/workspaceSandbox/backgroundService.ts
1011
- var import_child_process2 = require("child_process");
1012
- var import_crypto3 = require("crypto");
1013
- var import_fs5 = require("fs");
1014
- var import_path6 = require("path");
1015
-
1016
1016
  // src/application/services/workspaceSandbox/localWorkspace.ts
1017
1017
  var import_promises4 = require("fs/promises");
1018
1018
  var import_os3 = require("os");
@@ -2532,6 +2532,37 @@ function parseConnectCode(code) {
2532
2532
  if (!/^[A-Za-z0-9_\-.~+/=]{32,128}$/.test(secret)) return null;
2533
2533
  return { sessionId, secret };
2534
2534
  }
2535
+ var NotLocalSessionError = class extends Error {
2536
+ };
2537
+ var LocalSessionTransport = class extends HttpSessionTransport {
2538
+ constructor(local) {
2539
+ super({ goneStatuses: [401, 403, 404, 409, 410], ...local });
2540
+ this.local = local;
2541
+ }
2542
+ local;
2543
+ refused = false;
2544
+ async bootstrap() {
2545
+ try {
2546
+ const boot = await super.bootstrap();
2547
+ if (boot.workspaceKind !== "LOCAL") {
2548
+ this.refused = true;
2549
+ if (boot.repo) boot.repo.token = "";
2550
+ throw new NotLocalSessionError("not a local session");
2551
+ }
2552
+ return boot;
2553
+ } catch (e) {
2554
+ this.local.onBootstrapError?.(e);
2555
+ throw e;
2556
+ }
2557
+ }
2558
+ async pollCommands(waitSeconds, signal) {
2559
+ return (await super.pollCommands(waitSeconds, signal)).filter((c) => !(c.kind === "approval" && this.local.decisions.offer(c.payload)));
2560
+ }
2561
+ async postEvents(events) {
2562
+ if (this.refused) return;
2563
+ await super.postEvents(events);
2564
+ }
2565
+ };
2535
2566
  function makeStyle(color) {
2536
2567
  const wrap = (open2, close) => (s) => color ? `\x1B[${open2}m${s}\x1B[${close}m` : s;
2537
2568
  return { bold: wrap(1, 22), dim: wrap(2, 22), red: wrap(31, 39), green: wrap(32, 39), yellow: wrap(33, 39) };
@@ -3065,18 +3096,29 @@ function writeFileSafely(file, content, mode = 420) {
3065
3096
  (0, import_fs5.renameSync)(tmp, file);
3066
3097
  }
3067
3098
  var failed = (r) => `${r.stderr || r.stdout}`.trim().split("\n").slice(-1)[0]?.slice(0, 200) || `exit ${r.code}`;
3099
+ var LAUNCHD_REPLACE_WAIT_MS = 3e4;
3100
+ var LAUNCHD_POLL_MS = 500;
3101
+ var LAUNCHD_BOOTSTRAP_ATTEMPTS = 6;
3068
3102
  async function installLaunchd(env, bin, api) {
3069
3103
  const names = serviceNames(api);
3070
3104
  const plist = launchAgentPath(env, api);
3071
3105
  (0, import_fs5.mkdirSync)(logDir(env), { recursive: true });
3072
3106
  writeFileSafely(plist, launchAgentPlist(env, bin, api));
3073
3107
  const domain = `gui/${env.uid}`;
3074
- await env.run("launchctl", ["bootout", `${domain}/${names.label}`]);
3075
- await env.run("launchctl", ["enable", `${domain}/${names.label}`]);
3076
- const boot = await env.run("launchctl", ["bootstrap", domain, plist]);
3108
+ const sleep2 = env.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
3109
+ const target = `${domain}/${names.label}`;
3110
+ const loaded = async () => (await env.run("launchctl", ["print", target])).code === 0;
3111
+ await env.run("launchctl", ["bootout", target]);
3112
+ for (let waited = 0; waited < LAUNCHD_REPLACE_WAIT_MS && await loaded(); waited += LAUNCHD_POLL_MS) await sleep2(LAUNCHD_POLL_MS);
3113
+ await env.run("launchctl", ["enable", target]);
3114
+ let boot = await env.run("launchctl", ["bootstrap", domain, plist]);
3115
+ for (let attempt = 1; boot.code !== 0 && attempt < LAUNCHD_BOOTSTRAP_ATTEMPTS; attempt++) {
3116
+ await sleep2(LAUNCHD_POLL_MS);
3117
+ boot = await env.run("launchctl", ["bootstrap", domain, plist]);
3118
+ }
3077
3119
  if (boot.code === 0) return { ok: true, manager: "launchd", hints: [] };
3078
3120
  const legacy = await env.run("launchctl", ["load", "-w", plist]);
3079
- if (legacy.code === 0) return { ok: true, manager: "launchd", hints: [] };
3121
+ if (legacy.code === 0 && await loaded()) return { ok: true, manager: "launchd", hints: [] };
3080
3122
  (0, import_fs5.rmSync)(plist, { force: true });
3081
3123
  return { ok: false, code: "LAUNCHD_REFUSED", message: `macOS did not accept the background service (${failed(boot)}). A device management profile may block login items.` };
3082
3124
  }
@@ -11905,6 +11947,8 @@ ${patch}`;
11905
11947
  /** The engine conversation, without secrets, kept by the API (only when it changed). */
11906
11948
  async saveTranscript() {
11907
11949
  if (!this.deps.persistTranscript || !this.sdkSessionId || !this.deps.transport.saveTranscript) return;
11950
+ const imported = this.boot?.imported;
11951
+ if (imported?.source === "CLAUDE_CODE" && (this.local && imported.nativeResume || imported.externalId === this.sdkSessionId)) return;
11908
11952
  const pack = await packTranscript(this.deps.configDir, this.sdkSessionId, this.redactor);
11909
11953
  if (!pack) return;
11910
11954
  if ("tooLarge" in pack) {
@@ -12582,8 +12626,6 @@ var HOME = (0, import_os4.homedir)();
12582
12626
  var SQ_HOME = (0, import_path16.join)(HOME, ".scalequality");
12583
12627
  var ENGINE_HOME = (0, import_path16.join)(SQ_HOME, "workspace");
12584
12628
  var credentials = new CredentialStore((0, import_path16.join)(SQ_HOME, "credentials.json"));
12585
- var NotLocalSessionError = class extends Error {
12586
- };
12587
12629
  function startFailure(e, api) {
12588
12630
  if (e instanceof NotLocalSessionError) return 'This code belongs to a cloud workspace session. In the AI Workspace, choose "Use a folder on your computer" to get a code for this command.';
12589
12631
  if (e instanceof SessionGoneError) {
@@ -12619,14 +12661,6 @@ function createLocalEngine(o) {
12619
12661
  if (o.verbose) say(style.dim(`${o.prefix ?? ""}[warn] ${msg} ${ctx ? JSON.stringify(ctx) : ""}`));
12620
12662
  }
12621
12663
  };
12622
- const http = new HttpSessionTransport({
12623
- baseUrl: o.api,
12624
- sessionId: o.sessionId,
12625
- secret: o.secret,
12626
- // 409 is SESSION_CLOSED on the session routes: end instead of retrying.
12627
- goneStatuses: [401, 403, 404, 409, 410],
12628
- userAgent: userAgent(o.mode)
12629
- });
12630
12664
  const decisions = new CommandDecisions();
12631
12665
  const gate = new BrowserCommandGate({
12632
12666
  root: o.root,
@@ -12638,31 +12672,16 @@ function createLocalEngine(o) {
12638
12672
  onWaiting: (command) => say(`${o.prefix ?? ""}${style.yellow("Waiting for your approval in the browser:")} $ ${visibleText(command.replace(/\s+/g, " ")).slice(0, 160)}`)
12639
12673
  });
12640
12674
  let startError = null;
12641
- let refused = false;
12642
- const transport = {
12643
- bootstrap: async () => {
12644
- try {
12645
- const boot = await http.bootstrap();
12646
- if (boot.workspaceKind !== "LOCAL") {
12647
- refused = true;
12648
- if (boot.repo) boot.repo.token = "";
12649
- throw new NotLocalSessionError("not a local session");
12650
- }
12651
- return boot;
12652
- } catch (e) {
12653
- startError = startFailure(e, o.api);
12654
- throw e;
12655
- }
12656
- },
12657
- pollCommands: async (w, s) => (await http.pollCommands(w, s)).filter((c) => !(c.kind === "approval" && decisions.offer(c.payload))),
12658
- postEvents: (ev) => refused ? Promise.resolve() : http.postEvents(ev),
12659
- callTool: (n, a, id) => http.callTool(n, a, id),
12660
- openPullRequest: (r) => http.openPullRequest(r),
12661
- checkpoint: (r) => http.checkpoint(r),
12662
- // Never called in local mode (the folder is never cloned); the API refuses it anyway.
12663
- openRepository: (r) => http.openRepository(r),
12664
- importedHistory: () => http.importedHistory()
12665
- };
12675
+ const transport = new LocalSessionTransport({
12676
+ baseUrl: o.api,
12677
+ sessionId: o.sessionId,
12678
+ secret: o.secret,
12679
+ userAgent: userAgent(o.mode),
12680
+ decisions,
12681
+ onBootstrapError: (e) => {
12682
+ startError = startFailure(e, o.api);
12683
+ }
12684
+ });
12666
12685
  const sources = defaultImportSources(HOME);
12667
12686
  return new WorkspaceEngine({
12668
12687
  transport,
@@ -12678,6 +12697,9 @@ function createLocalEngine(o) {
12678
12697
  return prepared;
12679
12698
  },
12680
12699
  createMeasurer: null,
12700
+ // The conversation is kept (without secrets) with each checkpoint, as in the cloud: moved to the cloud or
12701
+ // resumed on another computer, the session continues it. An imported Claude Code conversation never leaves.
12702
+ persistTranscript: true,
12681
12703
  // A new conversation is named from its first message, through the gateway with the session's credential.
12682
12704
  nameConversation: (r) => gatewayTitle(r),
12683
12705
  // The copies of files changed outside any repository of the folder, per session, outside the folder.