@super-one/cli 0.52.0-alpha → 0.53.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/MANIFEST.json +2 -2
  2. package/lib/cli.mjs +2428 -1002
  3. package/package.json +1 -1
package/lib/cli.mjs CHANGED
@@ -179,7 +179,7 @@ var init_host_action_superone_descriptors = __esm({
179
179
  },
180
180
  {
181
181
  "name": "session_collab_request",
182
- "description": 'Request user approval for one or more child sessions. Call session_collab_list_agents first; repeat an agentId to reuse a profile. Invent a human-friendly name and role for every launch. Pass a short 2\u20133 sentence summary (confirm UI) and a full Markdown task (delivered on start; expandable in the UI). Omitted config fields inherit profile defaults. Before setting config.cwd or config.worktree, call read_manual({ domain: "product", topic: "collaboration" }); same-repo isolation belongs in config.worktree. The user may edit model, effort, provider, permission, and sandbox. Each approved launch returns a one-shot credential for session_collab_start.',
182
+ "description": 'Request user approval for collaboration launches: mode "spawn" (default) creates a child; mode "link" opens a mailbox with an existing sessionId. Spawn: list_agents; require agentId, name, role, summary, task; for cwd/worktree call read_manual({ domain: "product", topic: "collaboration" }); same-repo isolation belongs in config.worktree. Link: require sessionId + summary; optional task opening (turn-injected, not system prompt). User must approve; credential for session_collab_start.',
183
183
  "inputSchema": {
184
184
  "type": "object",
185
185
  "properties": {
@@ -194,9 +194,22 @@ var init_host_action_superone_descriptors = __esm({
194
194
  "type": "string",
195
195
  "description": "Optional caller correlation id."
196
196
  },
197
+ "mode": {
198
+ "type": "string",
199
+ "enum": [
200
+ "spawn",
201
+ "link"
202
+ ],
203
+ "description": '"spawn" (default) creates a new child session. "link" connects to an already-existing SuperOne session (sessionId required).'
204
+ },
205
+ "sessionId": {
206
+ "type": "string",
207
+ "minLength": 1,
208
+ "description": 'Existing SuperOne session id to link with (mode "link" only). Required for link; ignore for spawn. Prefer ids from @session mentions or session_list \u2014 never invent ids.'
209
+ },
197
210
  "agentId": {
198
211
  "type": "string",
199
- "description": "Agent profile id from session_collab_list_agents."
212
+ "description": 'Agent profile id from session_collab_list_agents. Required for mode "spawn"; omit for "link".'
200
213
  },
201
214
  "summary": {
202
215
  "type": "string",
@@ -205,23 +218,23 @@ var init_host_action_superone_descriptors = __esm({
205
218
  },
206
219
  "task": {
207
220
  "type": "string",
208
- "minLength": 1,
209
- "description": "Full task brief in Markdown. Delivered to the child session on session_collab_start. The user can expand the summary in the confirm UI to preview it."
221
+ "description": "Full Markdown brief. Spawn: delivered to the child on session_collab_start. Link: optional opening for the peer (mailbox + turn wake, never system prompt). Expandable in the confirm UI."
210
222
  },
211
223
  "name": {
212
224
  "type": "string",
213
225
  "minLength": 1,
214
226
  "maxLength": 64,
215
- "description": 'Human-friendly label YOU invent for this child (e.g. "Alice", "DiffBot"). Not the harness name. Used in "Name - Role".'
227
+ "description": 'Spawn: human-friendly child label (e.g. "Alice"). Link: optional; defaults to peer session title.'
216
228
  },
217
229
  "role": {
218
230
  "type": "string",
219
231
  "minLength": 1,
220
232
  "maxLength": 64,
221
- "description": 'Temporary role label for the child session title: "Name - Role" (e.g. "Reviewer", "Implementer").'
233
+ "description": 'Spawn: role for title "Name - Role". Link: optional; defaults to "Peer".'
222
234
  },
223
235
  "config": {
224
236
  "type": "object",
237
+ "description": 'Spawn only. Ignored for mode "link".',
225
238
  "properties": {
226
239
  "model": {
227
240
  "type": "string"
@@ -300,11 +313,7 @@ var init_host_action_superone_descriptors = __esm({
300
313
  }
301
314
  },
302
315
  "required": [
303
- "agentId",
304
- "summary",
305
- "task",
306
- "name",
307
- "role"
316
+ "summary"
308
317
  ],
309
318
  "additionalProperties": false
310
319
  }
@@ -318,7 +327,7 @@ var init_host_action_superone_descriptors = __esm({
318
327
  },
319
328
  {
320
329
  "name": "session_collab_start",
321
- "description": "Start the approved child session for one credential and deliver its task. The call returns when the child begins replying, not when its first turn finishes. A credential starts at most one session; retries return the same session id. Start all approved children back-to-back, then continue other work while they run asynchronously.",
330
+ "description": "Activate one approved collaboration credential. Spawn: create the child and deliver its task. Link: bind the existing peer and wake it via turn injection (not system prompt). Returns when the peer begins (spawn) or is notified (link). Retries are idempotent. Start all credentials back-to-back.",
322
331
  "inputSchema": {
323
332
  "type": "object",
324
333
  "properties": {
@@ -334,7 +343,7 @@ var init_host_action_superone_descriptors = __esm({
334
343
  },
335
344
  {
336
345
  "name": "session_collab_send",
337
- "description": "Send a persistent Markdown message through one parent-child mailbox. Use clientMessageId for retry-safe delivery. The host wakes the peer and later wakes you when it replies. After sending, continue other work or end your turn. Never sleep, resend, or poll session_collab_retrieve while waiting.",
346
+ "description": "Send a persistent Markdown message through one collaboration mailbox (spawn parent-child or link peers). Use clientMessageId for retry-safe delivery. The host wakes the peer and later wakes you when it replies. After sending, continue other work or end your turn. Never sleep, resend, or poll session_collab_retrieve while waiting.",
338
347
  "inputSchema": {
339
348
  "type": "object",
340
349
  "properties": {
@@ -359,7 +368,7 @@ var init_host_action_superone_descriptors = __esm({
359
368
  },
360
369
  {
361
370
  "name": "session_collab_retrieve",
362
- "description": 'Retrieve queued Markdown messages for this session from one or more parent-child mailboxes. Call after a collaboration wake, or once before acting on peer input. This is a non-blocking read: status "empty" is not a retry signal. Do not sleep or poll; end your turn and wait for the next wake.',
371
+ "description": 'Retrieve queued Markdown messages for this session from one or more collaboration mailboxes. Call after a collaboration wake, or once before acting on peer input. This is a non-blocking read: status "empty" is not a retry signal. Do not sleep or poll; end your turn and wait for the next wake.',
363
372
  "inputSchema": {
364
373
  "type": "object",
365
374
  "properties": {
@@ -3234,6 +3243,191 @@ var init_host_action_superone_descriptors = __esm({
3234
3243
  "additionalProperties": false
3235
3244
  }
3236
3245
  },
3246
+ {
3247
+ "name": "automation_list",
3248
+ "description": "List scheduled agent automations for the current project (id, name, enabled, schedule, last/next run). Pass id for full detail (prompt, agentConfig, schedule). Filter with query (name) or enabled. Call before automation_apply or automation_delete. Current project only \u2014 not session archive tools.",
3249
+ "inputSchema": {
3250
+ "type": "object",
3251
+ "properties": {
3252
+ "id": {
3253
+ "type": "string",
3254
+ "description": "When set, return full detail for this automation (must belong to the current project)."
3255
+ },
3256
+ "enabled": {
3257
+ "type": "boolean",
3258
+ "description": "Filter by enabled state. Omit for all."
3259
+ },
3260
+ "query": {
3261
+ "type": "string",
3262
+ "description": "Case-insensitive name substring filter."
3263
+ },
3264
+ "limit": {
3265
+ "type": "integer",
3266
+ "minimum": 1,
3267
+ "maximum": 100,
3268
+ "description": "Max rows. Default 50, max 100."
3269
+ },
3270
+ "offset": {
3271
+ "type": "integer",
3272
+ "minimum": 0,
3273
+ "description": "Pagination offset. Default 0."
3274
+ }
3275
+ },
3276
+ "additionalProperties": false
3277
+ }
3278
+ },
3279
+ {
3280
+ "name": "automation_apply",
3281
+ "description": "Create or update a project automation. action=create needs name, prompt, schedule; agentConfig optional (defaults claude + bypassPermissions). action=update needs id plus any of name/prompt/enabled/schedule/agentConfig (toggle via enabled). Always set schedule.summary to a short natural-language phrase for the UI (user language). Always opens a user confirmation dialog; applies nothing without approval. Call automation_list first for ids. Delete with automation_delete.",
3282
+ "inputSchema": {
3283
+ "type": "object",
3284
+ "properties": {
3285
+ "action": {
3286
+ "type": "string",
3287
+ "enum": ["create", "update"],
3288
+ "description": "create a new automation, or update an existing one (including toggle enabled)."
3289
+ },
3290
+ "id": {
3291
+ "type": "string",
3292
+ "description": "Required for update. Automation id from automation_list."
3293
+ },
3294
+ "name": {
3295
+ "type": "string",
3296
+ "description": "Display name. Required for create; optional for update."
3297
+ },
3298
+ "prompt": {
3299
+ "type": "string",
3300
+ "description": "Prompt sent to the agent when the automation runs. Required for create; optional for update."
3301
+ },
3302
+ "enabled": {
3303
+ "type": "boolean",
3304
+ "description": "Whether the scheduler will run this automation. Create defaults to true; use false to pause."
3305
+ },
3306
+ "schedule": {
3307
+ "type": "object",
3308
+ "description": `When to run. one-time needs runAt (ISO). recurring needs cron (e.g. "0 9 * * *"). Always include summary: natural language for the confirm UI in the user's language (e.g. "Every weekday at 9:00 AM", "\u6BCF\u5929\u4E0A\u5348 9 \u70B9"). Machine fields still drive the scheduler.`,
3309
+ "properties": {
3310
+ "type": {
3311
+ "type": "string",
3312
+ "enum": ["one-time", "recurring"]
3313
+ },
3314
+ "cron": {
3315
+ "type": "string",
3316
+ "description": "Cron expression for recurring (required when type=recurring)."
3317
+ },
3318
+ "runAt": {
3319
+ "type": "string",
3320
+ "description": "ISO timestamp for one-time (required when type=one-time)."
3321
+ },
3322
+ "preset": {
3323
+ "type": "string",
3324
+ "enum": ["hourly", "daily", "weekly", "custom"]
3325
+ },
3326
+ "timeOfDay": {
3327
+ "type": "string",
3328
+ "description": "HH:mm local time hint for daily/weekly presets."
3329
+ },
3330
+ "dayOfWeek": {
3331
+ "type": "array",
3332
+ "items": {
3333
+ "type": "integer",
3334
+ "minimum": 0,
3335
+ "maximum": 6
3336
+ },
3337
+ "description": "0=Sun \u2026 6=Sat for weekly preset."
3338
+ },
3339
+ "minuteOfHour": {
3340
+ "type": "integer",
3341
+ "minimum": 0,
3342
+ "maximum": 59,
3343
+ "description": "Minute for hourly preset."
3344
+ },
3345
+ "summary": {
3346
+ "type": "string",
3347
+ "minLength": 1,
3348
+ "maxLength": 200,
3349
+ "description": `Natural-language schedule shown in the UI (list + confirm). Use the user's language. Examples: "Every weekday at 9:00 AM", "\u6BCF\u5929\u4E0A\u5348 9 \u70B9", "Once on May 1 at 3pm". Required for create.`
3350
+ }
3351
+ },
3352
+ "required": ["type", "summary"],
3353
+ "additionalProperties": false
3354
+ },
3355
+ "agentConfig": {
3356
+ "type": "object",
3357
+ "description": "Harness + model for the automation run. type is required (claude|codex|acp|opencode). Defaults on create: claude + bypassPermissions + sandbox off. Prefer unified fields: model, effort, permissionMode, sandboxMode, apiProviderId, acpAgentId.",
3358
+ "properties": {
3359
+ "type": {
3360
+ "type": "string",
3361
+ "enum": ["claude", "codex", "acp", "opencode"]
3362
+ },
3363
+ "agentName": {
3364
+ "type": "string",
3365
+ "description": "Claude only: named agent profile."
3366
+ },
3367
+ "model": {
3368
+ "type": "string"
3369
+ },
3370
+ "effort": {
3371
+ "type": "string",
3372
+ "description": "Unified effort (Claude levels, Codex reasoning, ACP mode ids)."
3373
+ },
3374
+ "permissionMode": {
3375
+ "type": "string",
3376
+ "enum": ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"],
3377
+ "description": "Unified permission mode. Prefer bypassPermissions for unattended runs."
3378
+ },
3379
+ "sandboxMode": {
3380
+ "type": "string",
3381
+ "enum": ["off", "on", "auto"],
3382
+ "description": "Claude sandbox (ignored by other harnesses)."
3383
+ },
3384
+ "apiProviderId": {
3385
+ "type": ["string", "null"],
3386
+ "description": "Optional third-party AI provider credential id (claude/codex)."
3387
+ },
3388
+ "acpAgentId": {
3389
+ "type": "string",
3390
+ "description": "ACP only: agent id (e.g. grok-build)."
3391
+ },
3392
+ "reasoningEffort": {
3393
+ "type": "string",
3394
+ "enum": ["minimal", "low", "medium", "high", "xhigh"],
3395
+ "description": "Codex legacy alias for effort."
3396
+ },
3397
+ "permissionPreset": {
3398
+ "type": "string",
3399
+ "enum": ["read-only", "default", "full-access"],
3400
+ "description": "Codex legacy alias for permissionMode (full-access \u2248 bypassPermissions)."
3401
+ }
3402
+ },
3403
+ "required": ["type"],
3404
+ "additionalProperties": false
3405
+ }
3406
+ },
3407
+ "required": ["action"],
3408
+ "additionalProperties": false
3409
+ }
3410
+ },
3411
+ {
3412
+ "name": "automation_delete",
3413
+ "description": "Permanently delete project automations by id (from automation_list). Always opens a user confirmation dialog. Current project only. Prefer automation_list to choose ids first.",
3414
+ "inputSchema": {
3415
+ "type": "object",
3416
+ "properties": {
3417
+ "ids": {
3418
+ "type": "array",
3419
+ "items": {
3420
+ "type": "string"
3421
+ },
3422
+ "minItems": 1,
3423
+ "maxItems": 20,
3424
+ "description": "Automation ids from automation_list to delete (current project only)."
3425
+ }
3426
+ },
3427
+ "required": ["ids"],
3428
+ "additionalProperties": false
3429
+ }
3430
+ },
3237
3431
  {
3238
3432
  "name": "mobile_share_file",
3239
3433
  "description": "Share a file from the desktop to the mobile device that is currently viewing this session, so the user can open or save it on their phone. This tool is ONLY available while a mobile device is subscribed to the session \u2014 if it is not in your tool list, no phone is connected. The file is delivered end-to-end encrypted and appears as a file card in the mobile chat. The path MUST point to a file inside the current project directory. Use it when the user asks to send, share, or get a file onto their phone.",
@@ -3326,6 +3520,7 @@ var init_host_action_browser_catalog = __esm({
3326
3520
  "media_video_status",
3327
3521
  "widget_list_templates",
3328
3522
  "miniapp_list",
3523
+ "automation_list",
3329
3524
  // session_collab_* are node-local (not HA); list_agents stays "safe" if ever reclassified
3330
3525
  "computer_apps",
3331
3526
  "computer_snapshot",
@@ -3969,17 +4164,17 @@ var require_node_gyp_build = __commonJS({
3969
4164
  ].filter(Boolean).join(" ");
3970
4165
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
3971
4166
  function resolve13(dir2) {
3972
- var tuples = readdirSync11(path.join(dir2, "prebuilds")).map(parseTuple);
4167
+ var tuples = readdirSync12(path.join(dir2, "prebuilds")).map(parseTuple);
3973
4168
  var tuple2 = tuples.filter(matchTuple(platform2, arch2)).sort(compareTuples)[0];
3974
4169
  if (!tuple2) return;
3975
4170
  var prebuilds = path.join(dir2, "prebuilds", tuple2.name);
3976
- var parsed = readdirSync11(prebuilds).map(parseTags);
4171
+ var parsed = readdirSync12(prebuilds).map(parseTags);
3977
4172
  var candidates = parsed.filter(matchTags(runtime, abi));
3978
4173
  var winner = candidates.sort(compareTags(runtime))[0];
3979
4174
  if (winner) return path.join(prebuilds, winner.file);
3980
4175
  }
3981
4176
  };
3982
- function readdirSync11(dir) {
4177
+ function readdirSync12(dir) {
3983
4178
  try {
3984
4179
  return fs.readdirSync(dir);
3985
4180
  } catch (err) {
@@ -3987,7 +4182,7 @@ var require_node_gyp_build = __commonJS({
3987
4182
  }
3988
4183
  }
3989
4184
  function getFirst(dir, filter) {
3990
- var files = readdirSync11(dir).filter(filter);
4185
+ var files = readdirSync12(dir).filter(filter);
3991
4186
  return files[0] && path.join(dir, files[0]);
3992
4187
  }
3993
4188
  function matchBuild(name) {
@@ -6358,8 +6553,8 @@ var require_websocket = __commonJS({
6358
6553
  var http = __require("http");
6359
6554
  var net2 = __require("net");
6360
6555
  var tls = __require("tls");
6361
- var { randomBytes: randomBytes6, createHash: createHash9 } = __require("crypto");
6362
- var { Duplex, Readable: Readable3 } = __require("stream");
6556
+ var { randomBytes: randomBytes7, createHash: createHash10 } = __require("crypto");
6557
+ var { Duplex, Readable: Readable4 } = __require("stream");
6363
6558
  var { URL: URL2 } = __require("url");
6364
6559
  var PerMessageDeflate2 = require_permessage_deflate();
6365
6560
  var Receiver2 = require_receiver();
@@ -6888,7 +7083,7 @@ var require_websocket = __commonJS({
6888
7083
  }
6889
7084
  }
6890
7085
  const defaultPort = isSecure ? 443 : 80;
6891
- const key = randomBytes6(16).toString("base64");
7086
+ const key = randomBytes7(16).toString("base64");
6892
7087
  const request = isSecure ? https.request : http.request;
6893
7088
  const protocolSet = /* @__PURE__ */ new Set();
6894
7089
  let perMessageDeflate;
@@ -7018,7 +7213,7 @@ var require_websocket = __commonJS({
7018
7213
  abortHandshake(websocket, socket, "Invalid Upgrade header");
7019
7214
  return;
7020
7215
  }
7021
- const digest = createHash9("sha1").update(key + GUID).digest("base64");
7216
+ const digest = createHash10("sha1").update(key + GUID).digest("base64");
7022
7217
  if (res.headers["sec-websocket-accept"] !== digest) {
7023
7218
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
7024
7219
  return;
@@ -7385,7 +7580,7 @@ var require_websocket_server = __commonJS({
7385
7580
  var EventEmitter = __require("events");
7386
7581
  var http = __require("http");
7387
7582
  var { Duplex } = __require("stream");
7388
- var { createHash: createHash9 } = __require("crypto");
7583
+ var { createHash: createHash10 } = __require("crypto");
7389
7584
  var extension2 = require_extension();
7390
7585
  var PerMessageDeflate2 = require_permessage_deflate();
7391
7586
  var subprotocol2 = require_subprotocol();
@@ -7686,7 +7881,7 @@ var require_websocket_server = __commonJS({
7686
7881
  );
7687
7882
  }
7688
7883
  if (this._state > RUNNING) return abortHandshake(socket, 503);
7689
- const digest = createHash9("sha1").update(key + GUID).digest("base64");
7884
+ const digest = createHash10("sha1").update(key + GUID).digest("base64");
7690
7885
  const headers = [
7691
7886
  "HTTP/1.1 101 Switching Protocols",
7692
7887
  "Upgrade: websocket",
@@ -7771,6 +7966,162 @@ var require_websocket_server = __commonJS({
7771
7966
  }
7772
7967
  });
7773
7968
 
7969
+ // ../../packages/runtime/src/spawn-env.ts
7970
+ function mergeLoopbackNoProxy(env) {
7971
+ const existingParts = [];
7972
+ for (const key of ["NO_PROXY", "no_proxy"]) {
7973
+ const raw = env[key];
7974
+ if (!raw) continue;
7975
+ for (const part of raw.split(/[,\s]+/)) {
7976
+ const trimmed = part.trim();
7977
+ if (trimmed) existingParts.push(trimmed);
7978
+ }
7979
+ }
7980
+ const seen = /* @__PURE__ */ new Set();
7981
+ const merged = [];
7982
+ for (const part of existingParts) {
7983
+ const key = part.toLowerCase();
7984
+ if (seen.has(key)) continue;
7985
+ seen.add(key);
7986
+ merged.push(part);
7987
+ }
7988
+ for (const host of LOOPBACK_NO_PROXY_HOSTS) {
7989
+ const key = host.toLowerCase();
7990
+ if (seen.has(key)) continue;
7991
+ seen.add(key);
7992
+ merged.push(host);
7993
+ }
7994
+ const value = merged.join(",");
7995
+ env.NO_PROXY = value;
7996
+ env.no_proxy = value;
7997
+ }
7998
+ function isEssentialEnv(key) {
7999
+ if (ESSENTIAL_EXACT.has(key)) return true;
8000
+ return ESSENTIAL_PREFIXES.some((p2) => key.startsWith(p2));
8001
+ }
8002
+ function sanitizePathEnv(pathEnv) {
8003
+ const seen = /* @__PURE__ */ new Set();
8004
+ const out = [];
8005
+ let dropped = 0;
8006
+ let deduped = 0;
8007
+ let droppedByPathMax = 0;
8008
+ let droppedByNameMax = 0;
8009
+ for (const entry of pathEnv.split(":")) {
8010
+ if (!entry) continue;
8011
+ if (seen.has(entry)) {
8012
+ deduped++;
8013
+ continue;
8014
+ }
8015
+ seen.add(entry);
8016
+ if (entry.length > SPAWN_PATH_MAX) {
8017
+ dropped++;
8018
+ droppedByPathMax++;
8019
+ continue;
8020
+ }
8021
+ if (entry.split("/").some((comp) => Buffer.byteLength(comp, "utf8") > SPAWN_NAME_MAX)) {
8022
+ dropped++;
8023
+ droppedByNameMax++;
8024
+ continue;
8025
+ }
8026
+ out.push(entry);
8027
+ }
8028
+ return { value: out.join(":"), dropped, deduped, droppedByPathMax, droppedByNameMax };
8029
+ }
8030
+ function serializedSize(env) {
8031
+ let n = 0;
8032
+ for (const key in env) {
8033
+ n += Buffer.byteLength(key) + Buffer.byteLength(env[key]) + 2;
8034
+ }
8035
+ return n;
8036
+ }
8037
+ function sanitizeEnv(base = process.env, extra) {
8038
+ const merged = {};
8039
+ for (const key in base) {
8040
+ const v2 = base[key];
8041
+ if (v2 !== void 0) merged[key] = v2;
8042
+ }
8043
+ if (extra) {
8044
+ for (const key in extra) {
8045
+ const v2 = extra[key];
8046
+ if (v2 !== void 0) merged[key] = v2;
8047
+ }
8048
+ }
8049
+ if (process.platform !== "win32" && typeof merged.PATH === "string") {
8050
+ const res = sanitizePathEnv(merged.PATH);
8051
+ merged.PATH = res.value;
8052
+ if (res.dropped > 0) {
8053
+ const parts = [];
8054
+ if (res.droppedByPathMax > 0)
8055
+ parts.push(`${res.droppedByPathMax} \u6574\u6BB5 > PATH_MAX(${SPAWN_PATH_MAX}B)`);
8056
+ if (res.droppedByNameMax > 0)
8057
+ parts.push(`${res.droppedByNameMax} \u6BB5\u542B > NAME_MAX(${SPAWN_NAME_MAX}B) \u7684\u76EE\u5F55\u540D`);
8058
+ console.warn(`[spawn-env] \u4E3A\u89C4\u907F spawn ENAMETOOLONG \u5DF2\u4E22\u5F03 ${res.dropped} \u4E2A\u8D85\u957F PATH \u5206\u6BB5\uFF08${parts.join("\uFF1B")}\uFF09`);
8059
+ }
8060
+ }
8061
+ mergeLoopbackNoProxy(merged);
8062
+ let size = serializedSize(merged);
8063
+ if (size > MAX_ENV_BYTES) {
8064
+ const entries = Object.entries(merged).sort((a, b2) => b2[1].length - a[1].length);
8065
+ for (const [key, value] of entries) {
8066
+ if (size <= MAX_ENV_BYTES) break;
8067
+ if (isEssentialEnv(key)) continue;
8068
+ size -= Buffer.byteLength(key) + Buffer.byteLength(value) + 2;
8069
+ delete merged[key];
8070
+ }
8071
+ if (size > MAX_ENV_BYTES) {
8072
+ console.warn(`[spawn-env] env still ${size} bytes after trimming non-essential vars (limit ${MAX_ENV_BYTES})`);
8073
+ } else {
8074
+ console.warn(`[spawn-env] trimmed env to ${size} bytes to avoid spawn E2BIG`);
8075
+ }
8076
+ }
8077
+ return merged;
8078
+ }
8079
+ function buildSafeEnv(extra) {
8080
+ return sanitizeEnv(process.env, extra);
8081
+ }
8082
+ var SPAWN_PATH_MAX, SPAWN_NAME_MAX, MAX_ENV_BYTES, ESSENTIAL_EXACT, ESSENTIAL_PREFIXES, LOOPBACK_NO_PROXY_HOSTS;
8083
+ var init_spawn_env = __esm({
8084
+ "../../packages/runtime/src/spawn-env.ts"() {
8085
+ "use strict";
8086
+ SPAWN_PATH_MAX = 1024;
8087
+ SPAWN_NAME_MAX = 255;
8088
+ MAX_ENV_BYTES = 256 * 1024;
8089
+ ESSENTIAL_EXACT = /* @__PURE__ */ new Set([
8090
+ "PATH",
8091
+ "HOME",
8092
+ "USER",
8093
+ "LOGNAME",
8094
+ "SHELL",
8095
+ "TERM",
8096
+ "LANG",
8097
+ "LC_ALL",
8098
+ "LC_CTYPE",
8099
+ "DISPLAY",
8100
+ "TMPDIR",
8101
+ "TEMP",
8102
+ "TMP",
8103
+ "DYLD_FRAMEWORK_PATH",
8104
+ "DYLD_LIBRARY_PATH",
8105
+ "DYLD_FALLBACK_FRAMEWORK_PATH",
8106
+ "DYLD_FALLBACK_LIBRARY_PATH",
8107
+ // Proxy bypass must survive E2BIG trimming — loopback MCP depends on it.
8108
+ "NO_PROXY",
8109
+ "no_proxy"
8110
+ ]);
8111
+ ESSENTIAL_PREFIXES = [
8112
+ "LC_",
8113
+ "ELECTRON_",
8114
+ "ANTHROPIC_",
8115
+ "CODEX_",
8116
+ "NPM_CONFIG_",
8117
+ "NODE_",
8118
+ "SSH_",
8119
+ "GIT_"
8120
+ ];
8121
+ LOOPBACK_NO_PROXY_HOSTS = ["127.0.0.1", "localhost", "::1"];
8122
+ }
8123
+ });
8124
+
7774
8125
  // ../../packages/codex/src/agent-event-mapper.ts
7775
8126
  function asRecord(value) {
7776
8127
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -8384,7 +8735,7 @@ async function openCodexAppServer(opts) {
8384
8735
  }
8385
8736
  const spawnFn = opts.spawnFn ?? defaultSpawn;
8386
8737
  const args = [...opts.cliArgs ?? [], "app-server", "--listen", "stdio://"];
8387
- const env = { ...process.env, ...opts.env ?? {} };
8738
+ const env = buildSafeEnv(opts.env);
8388
8739
  const child = spawnFn(opts.binaryPath, args, {
8389
8740
  env,
8390
8741
  stdio: ["pipe", "pipe", "pipe"],
@@ -8878,6 +9229,7 @@ var DEFAULT_REQUEST_TIMEOUT_MS, THREAD_TIMEOUT_MS, TURN_WAIT_TIMEOUT_MS, MAX_STD
8878
9229
  var init_app_server_client = __esm({
8879
9230
  "../../packages/codex/src/app-server-client.ts"() {
8880
9231
  "use strict";
9232
+ init_spawn_env();
8881
9233
  init_environment();
8882
9234
  init_agent_event_mapper();
8883
9235
  DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
@@ -9344,6 +9696,9 @@ var init_superone_host_owned_tools = __esm({
9344
9696
  "widget_show",
9345
9697
  "config_read",
9346
9698
  "config_apply",
9699
+ "automation_list",
9700
+ "automation_apply",
9701
+ "automation_delete",
9347
9702
  ...BROWSER_TOOL_NAMES
9348
9703
  ];
9349
9704
  MOBILE_SHARE_FILE_TOOL_NAME = "mobile_share_file";
@@ -43811,10 +44166,14 @@ async function stopProcess(child, closed) {
43811
44166
  signalProcess(child, "SIGKILL");
43812
44167
  await closed.catch(() => void 0);
43813
44168
  }
44169
+ function buildAcpProcessEnv(extra) {
44170
+ return buildSafeEnv(extra);
44171
+ }
43814
44172
  function spawnAcpProcess(launch2) {
43815
44173
  const child = spawn2(launch2.command, launch2.args ?? [], {
43816
44174
  cwd: launch2.cwd || process.cwd(),
43817
- env: { ...process.env, ...launch2.env },
44175
+ // buildSafeEnv merges loopback into NO_PROXY (package-level parity with desktop).
44176
+ env: buildAcpProcessEnv(launch2.env),
43818
44177
  stdio: ["pipe", "pipe", "pipe"],
43819
44178
  shell: process.platform === "win32",
43820
44179
  windowsHide: true
@@ -43854,6 +44213,7 @@ var init_process = __esm({
43854
44213
  "../../packages/acp/src/process.ts"() {
43855
44214
  "use strict";
43856
44215
  init_acp();
44216
+ init_spawn_env();
43857
44217
  KILL_ESCALATE_MS = 1500;
43858
44218
  }
43859
44219
  });
@@ -50485,6 +50845,13 @@ function openCodePath(pathEnv) {
50485
50845
  ].filter(Boolean);
50486
50846
  return [...new Set(paths)].join(delimiter);
50487
50847
  }
50848
+ function buildOpenCodeServeEnv(extra) {
50849
+ return buildSafeEnv({
50850
+ ...extra,
50851
+ PATH: openCodePath(extra?.PATH ?? process.env.PATH),
50852
+ OPENCODE_CONFIG_CONTENT: extra?.OPENCODE_CONFIG_CONTENT ?? "{}"
50853
+ });
50854
+ }
50488
50855
  function signalChild(child, signal) {
50489
50856
  try {
50490
50857
  if (child.exitCode === null && child.signalCode === null) child.kill(signal);
@@ -50547,12 +50914,7 @@ async function startOpenCodeServer(opts) {
50547
50914
  const binary = opts.binaryPath?.trim() || process.env.SUPERONE_OPENCODE_BINARY?.trim() || defaultOpenCodeBinaryPath();
50548
50915
  const child = spawn3(binary, [...OPENCODE_SERVE_ARGS], {
50549
50916
  cwd: opts.cwd,
50550
- env: {
50551
- ...process.env,
50552
- ...opts.env,
50553
- PATH: openCodePath(opts.env?.PATH ?? process.env.PATH),
50554
- OPENCODE_CONFIG_CONTENT: opts.env?.OPENCODE_CONFIG_CONTENT ?? "{}"
50555
- },
50917
+ env: buildOpenCodeServeEnv(opts.env),
50556
50918
  stdio: "pipe"
50557
50919
  });
50558
50920
  const exited = new Promise((resolve13) => {
@@ -50591,6 +50953,7 @@ var OPENCODE_SERVE_ARGS, maxServerOutput;
50591
50953
  var init_server3 = __esm({
50592
50954
  "../../packages/opencode/src/server.ts"() {
50593
50955
  "use strict";
50956
+ init_spawn_env();
50594
50957
  OPENCODE_SERVE_ARGS = ["serve", "--hostname=127.0.0.1", "--port=0"];
50595
50958
  maxServerOutput = 64 * 1024;
50596
50959
  }
@@ -51283,199 +51646,6 @@ var init_codex_turn_runner = __esm({
51283
51646
  }
51284
51647
  });
51285
51648
 
51286
- // src/session/managed-harness-official.ts
51287
- var managed_harness_official_exports = {};
51288
- __export(managed_harness_official_exports, {
51289
- OFFICIAL_CLAUDE_SDK_PACKAGE: () => OFFICIAL_CLAUDE_SDK_PACKAGE,
51290
- OFFICIAL_CLAUDE_SDK_VERSION: () => OFFICIAL_CLAUDE_SDK_VERSION,
51291
- OFFICIAL_CODEX_NPM_VERSION: () => OFFICIAL_CODEX_NPM_VERSION,
51292
- OFFICIAL_CODEX_PACKAGE: () => OFFICIAL_CODEX_PACKAGE,
51293
- claudePlatformPackageName: () => claudePlatformPackageName,
51294
- installManagedFromOfficialNpm: () => installManagedFromOfficialNpm,
51295
- managedNpmPrefix: () => managedNpmPrefix,
51296
- officialPackageSpecs: () => officialPackageSpecs,
51297
- resolveOfficialInstallBinary: () => resolveOfficialInstallBinary
51298
- });
51299
- import { existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync14, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
51300
- import { arch as osArch2, platform as osPlatform2 } from "node:os";
51301
- import { join as join19, resolve as resolve5 } from "node:path";
51302
- import { spawn as spawn4 } from "node:child_process";
51303
- function managedNpmPrefix(nodeHome, harnessId) {
51304
- return resolve5(nodeHome, "managed-npm", harnessId);
51305
- }
51306
- function claudePlatformPackageName() {
51307
- const p2 = osPlatform2();
51308
- const a = osArch2();
51309
- if (a !== "arm64" && a !== "x64") {
51310
- throw new Error(`unsupported arch for Claude Agent SDK: ${a}`);
51311
- }
51312
- if (p2 === "darwin") return `@anthropic-ai/claude-agent-sdk-darwin-${a}`;
51313
- if (p2 === "win32") return `@anthropic-ai/claude-agent-sdk-win32-${a}`;
51314
- if (p2 === "linux") {
51315
- if (isMuslLinux()) return `@anthropic-ai/claude-agent-sdk-linux-${a}-musl`;
51316
- return `@anthropic-ai/claude-agent-sdk-linux-${a}`;
51317
- }
51318
- throw new Error(`unsupported platform for Claude Agent SDK: ${p2}`);
51319
- }
51320
- function isMuslLinux() {
51321
- try {
51322
- if (existsSync24("/etc/alpine-release")) return true;
51323
- const lib = readdirSync8("/lib").some((n) => n.startsWith("ld-musl"));
51324
- if (lib) return true;
51325
- } catch {
51326
- }
51327
- return false;
51328
- }
51329
- function officialPackageSpecs(harnessId) {
51330
- if (harnessId === "claude") {
51331
- const ver2 = process.env.SUPERONE_CLAUDE_SDK_VERSION?.trim() || OFFICIAL_CLAUDE_SDK_VERSION;
51332
- const platform2 = claudePlatformPackageName();
51333
- return {
51334
- runtimeVersion: ver2,
51335
- specs: [`${OFFICIAL_CLAUDE_SDK_PACKAGE}@${ver2}`, `${platform2}@${ver2}`]
51336
- };
51337
- }
51338
- const ver = process.env.SUPERONE_CODEX_NPM_VERSION?.trim() || OFFICIAL_CODEX_NPM_VERSION;
51339
- return {
51340
- runtimeVersion: ver,
51341
- specs: [`${OFFICIAL_CODEX_PACKAGE}@${ver}`]
51342
- };
51343
- }
51344
- function resolveOfficialInstallBinary(harnessId, prefix) {
51345
- if (harnessId === "codex") {
51346
- const candidates = [
51347
- join19(prefix, "bin", "codex"),
51348
- join19(prefix, "bin", "codex.cmd"),
51349
- join19(prefix, "lib", "node_modules", "@openai", "codex", "bin", "codex.js")
51350
- ];
51351
- for (const c of candidates) {
51352
- if (existsSync24(c) && (c.endsWith(".js") || isExecutableFile(c))) return c;
51353
- }
51354
- return null;
51355
- }
51356
- const nm = join19(prefix, "lib", "node_modules");
51357
- const scoped = join19(nm, "@anthropic-ai");
51358
- try {
51359
- if (existsSync24(scoped)) {
51360
- const names = readdirSync8(scoped).filter((n) => n.startsWith("claude-agent-sdk-"));
51361
- for (const n of names) {
51362
- const ext = process.platform === "win32" ? ".exe" : "";
51363
- const bin = join19(scoped, n, `claude${ext}`);
51364
- if (existsSync24(bin)) return bin;
51365
- }
51366
- }
51367
- } catch {
51368
- }
51369
- const direct = join19(
51370
- nm,
51371
- ...claudePlatformPackageName().split("/"),
51372
- process.platform === "win32" ? "claude.exe" : "claude"
51373
- );
51374
- if (existsSync24(direct)) return direct;
51375
- return null;
51376
- }
51377
- function isExecutableFile(path) {
51378
- try {
51379
- const st = statSync6(path);
51380
- if (!st.isFile()) return false;
51381
- if (process.platform === "win32") return true;
51382
- return (st.mode & 73) !== 0;
51383
- } catch {
51384
- return false;
51385
- }
51386
- }
51387
- async function installManagedFromOfficialNpm(opts) {
51388
- const { specs, runtimeVersion } = officialPackageSpecs(opts.harnessId);
51389
- const prefix = managedNpmPrefix(opts.nodeHome, opts.harnessId);
51390
- mkdirSync12(prefix, { recursive: true });
51391
- const existing = resolveOfficialInstallBinary(opts.harnessId, prefix);
51392
- if (existing) {
51393
- return {
51394
- harnessId: opts.harnessId,
51395
- command: existing,
51396
- runtimeVersion: readInstalledVersion(prefix, opts.harnessId) ?? runtimeVersion,
51397
- source: "official-npm",
51398
- packageSpec: specs.join(" "),
51399
- installPrefix: prefix
51400
- };
51401
- }
51402
- const runNpm = opts.runNpm ?? defaultRunNpm;
51403
- await runNpm(
51404
- ["install", "--prefix", prefix, "--omit=dev", "--no-fund", "--no-audit", ...specs],
51405
- prefix
51406
- );
51407
- const command = resolveOfficialInstallBinary(opts.harnessId, prefix);
51408
- if (!command) {
51409
- throw new Error(
51410
- `official npm install of ${specs.join(" ")} succeeded but binary was not found under ${prefix}`
51411
- );
51412
- }
51413
- return {
51414
- harnessId: opts.harnessId,
51415
- command,
51416
- runtimeVersion: readInstalledVersion(prefix, opts.harnessId) ?? runtimeVersion,
51417
- source: "official-npm",
51418
- packageSpec: specs.join(" "),
51419
- installPrefix: prefix
51420
- };
51421
- }
51422
- function readInstalledVersion(prefix, harnessId) {
51423
- try {
51424
- const pkgPath = harnessId === "claude" ? join19(prefix, "lib", "node_modules", "@anthropic-ai", "claude-agent-sdk", "package.json") : join19(prefix, "lib", "node_modules", "@openai", "codex", "package.json");
51425
- if (!existsSync24(pkgPath)) return null;
51426
- const raw = JSON.parse(readFileSync14(pkgPath, "utf8"));
51427
- return raw.version?.trim() || null;
51428
- } catch {
51429
- return null;
51430
- }
51431
- }
51432
- function defaultRunNpm(args, cwd) {
51433
- return new Promise((resolvePromise, reject) => {
51434
- const child = spawn4("npm", args, {
51435
- cwd,
51436
- env: { ...process.env, npm_config_update_notifier: "false" },
51437
- stdio: ["ignore", "pipe", "pipe"]
51438
- });
51439
- let stderr = "";
51440
- child.stderr?.on("data", (c) => {
51441
- stderr += c.toString();
51442
- });
51443
- child.on("error", (err) => {
51444
- reject(
51445
- new Error(
51446
- `failed to spawn npm (${err.message}). Node.js/npm is required on the host for official harness install.`
51447
- )
51448
- );
51449
- });
51450
- const timer = setTimeout(() => {
51451
- child.kill("SIGTERM");
51452
- reject(new Error(`npm install timed out after 10m: npm ${args.join(" ")}`));
51453
- }, 10 * 6e4);
51454
- child.on("close", (code) => {
51455
- clearTimeout(timer);
51456
- if (code === 0) resolvePromise();
51457
- else {
51458
- reject(
51459
- new Error(
51460
- `npm install failed (exit ${code}): npm ${args.join(" ")}
51461
- ${stderr.slice(-1200)}`
51462
- )
51463
- );
51464
- }
51465
- });
51466
- });
51467
- }
51468
- var OFFICIAL_CLAUDE_SDK_VERSION, OFFICIAL_CODEX_NPM_VERSION, OFFICIAL_CLAUDE_SDK_PACKAGE, OFFICIAL_CODEX_PACKAGE;
51469
- var init_managed_harness_official = __esm({
51470
- "src/session/managed-harness-official.ts"() {
51471
- "use strict";
51472
- OFFICIAL_CLAUDE_SDK_VERSION = "0.3.226";
51473
- OFFICIAL_CODEX_NPM_VERSION = "0.146.1";
51474
- OFFICIAL_CLAUDE_SDK_PACKAGE = "@anthropic-ai/claude-agent-sdk";
51475
- OFFICIAL_CODEX_PACKAGE = "@openai/codex";
51476
- }
51477
- });
51478
-
51479
51649
  // src/session/codex-admin-service.ts
51480
51650
  var codex_admin_service_exports = {};
51481
51651
  __export(codex_admin_service_exports, {
@@ -58613,7 +58783,7 @@ var require_dist = __commonJS({
58613
58783
  });
58614
58784
 
58615
58785
  // src/cli.ts
58616
- import { homedir as homedir9 } from "node:os";
58786
+ import { homedir as homedir10 } from "node:os";
58617
58787
  import { resolve as resolve12 } from "node:path";
58618
58788
 
58619
58789
  // src/config.ts
@@ -58655,8 +58825,8 @@ import { fileURLToPath } from "node:url";
58655
58825
  function resolveCliReleaseVersion() {
58656
58826
  const fromEnv = process.env.SUPERONE_CLI_VERSION?.trim();
58657
58827
  if (fromEnv) return fromEnv;
58658
- if ("0.52.0-alpha".trim()) {
58659
- return "0.52.0-alpha".trim();
58828
+ if ("0.53.0-alpha".trim()) {
58829
+ return "0.53.0-alpha".trim();
58660
58830
  }
58661
58831
  const fromDist = readDistManifestVersion();
58662
58832
  if (fromDist) return fromDist;
@@ -58906,7 +59076,7 @@ function regenerateIdentity(nodeHome, label) {
58906
59076
  }
58907
59077
 
58908
59078
  // src/runtime.ts
58909
- import { existsSync as existsSync35, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
59079
+ import { existsSync as existsSync39, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "node:fs";
58910
59080
 
58911
59081
  // src/auth/auth-service.ts
58912
59082
  init_environment();
@@ -58956,7 +59126,7 @@ var AuthService = class {
58956
59126
  FROM pairing_tokens WHERE token_hash = ?`
58957
59127
  ).get(tokenHash);
58958
59128
  if (!row) throw Object.assign(new Error("invalid pairing token"), { code: "unauthorized" });
58959
- if (row.revoked_at) throw Object.assign(new Error("pairing token revoked"), { code: "unauthorized" });
59129
+ if (row.revoked_at) throw Object.assign(new Error("pairing token revoked"), { code: "revoked" });
58960
59130
  if (row.consumed_at) throw Object.assign(new Error("pairing token already used"), { code: "unauthorized" });
58961
59131
  if (row.expires_at < now) throw Object.assign(new Error("pairing token expired"), { code: "unauthorized" });
58962
59132
  const updated = this.db.prepare(
@@ -59044,13 +59214,13 @@ var AuthService = class {
59044
59214
  ).run(now, reused.client_session_id);
59045
59215
  this.onRevoke?.(reused.client_session_id);
59046
59216
  throw Object.assign(new Error("refresh token reuse detected; session revoked"), {
59047
- code: "unauthorized"
59217
+ code: "revoked"
59048
59218
  });
59049
59219
  }
59050
59220
  }
59051
- if (row.revoked_at) throw Object.assign(new Error("client session revoked"), { code: "unauthorized" });
59221
+ if (row.revoked_at) throw Object.assign(new Error("client session revoked"), { code: "revoked" });
59052
59222
  if (row.refresh_expires_at < now) {
59053
- throw Object.assign(new Error("refresh token expired"), { code: "unauthorized" });
59223
+ throw Object.assign(new Error("refresh token expired"), { code: "revoked" });
59054
59224
  }
59055
59225
  if (!input.verifyDeviceProof(row.device_public_key_pem, input.proofPayload, input.proofSignature)) {
59056
59226
  throw Object.assign(new Error("device proof failed"), { code: "unauthorized" });
@@ -59109,7 +59279,8 @@ var AuthService = class {
59109
59279
  createWsTicket(accessToken) {
59110
59280
  const verified = this.verifyAccessToken(accessToken);
59111
59281
  if (!verified.ok) {
59112
- throw Object.assign(new Error(verified.reason), { code: "unauthorized" });
59282
+ const code = /session revoked/i.test(verified.reason) ? "revoked" : "unauthorized";
59283
+ throw Object.assign(new Error(verified.reason), { code });
59113
59284
  }
59114
59285
  const client3 = verified.client;
59115
59286
  const now = Date.now();
@@ -59154,7 +59325,7 @@ var AuthService = class {
59154
59325
  WHERE t.ticket_id = ?`
59155
59326
  ).get(ticketId);
59156
59327
  if (!row) throw Object.assign(new Error("invalid ws ticket"), { code: "unauthorized" });
59157
- if (row.revoked_at) throw Object.assign(new Error("client session revoked"), { code: "unauthorized" });
59328
+ if (row.revoked_at) throw Object.assign(new Error("client session revoked"), { code: "revoked" });
59158
59329
  if (row.consumed_at) throw Object.assign(new Error("ws ticket already used"), { code: "unauthorized" });
59159
59330
  if (row.expires_at < now) throw Object.assign(new Error("ws ticket expired"), { code: "unauthorized" });
59160
59331
  if (!safeEqualString(row.ticket_hash, ticketHash)) {
@@ -59444,7 +59615,8 @@ CREATE TABLE IF NOT EXISTS session_collaboration_grants (
59444
59615
  config_json TEXT NOT NULL,
59445
59616
  task_sent INTEGER NOT NULL DEFAULT 0,
59446
59617
  created_at TEXT NOT NULL,
59447
- started_at TEXT
59618
+ started_at TEXT,
59619
+ kind TEXT NOT NULL DEFAULT 'spawn'
59448
59620
  );
59449
59621
  CREATE INDEX IF NOT EXISTS idx_session_collaboration_parent
59450
59622
  ON session_collaboration_grants(parent_session_id);
@@ -59696,8 +59868,10 @@ CREATE TABLE IF NOT EXISTS session_collaboration_grants (
59696
59868
  config_json TEXT NOT NULL,
59697
59869
  task_sent INTEGER NOT NULL DEFAULT 0,
59698
59870
  created_at TEXT NOT NULL,
59699
- started_at TEXT
59871
+ started_at TEXT,
59872
+ kind TEXT NOT NULL DEFAULT 'spawn'
59700
59873
  );
59874
+ -- kind: 'spawn' (create child) | 'link' (mailbox with existing session)
59701
59875
  CREATE INDEX IF NOT EXISTS idx_session_collaboration_parent
59702
59876
  ON session_collaboration_grants(parent_session_id);
59703
59877
  CREATE INDEX IF NOT EXISTS idx_session_collaboration_child
@@ -59726,6 +59900,10 @@ CREATE TABLE IF NOT EXISTS session_collaboration_cursors (
59726
59900
  PRIMARY KEY(credential_hash, session_id)
59727
59901
  );
59728
59902
  `);
59903
+ const grantCols = db.prepare(`PRAGMA table_info(session_collaboration_grants)`).all();
59904
+ if (!grantCols.some((c) => c.name === "kind")) {
59905
+ db.exec(`ALTER TABLE session_collaboration_grants ADD COLUMN kind TEXT NOT NULL DEFAULT 'spawn'`);
59906
+ }
59729
59907
  }
59730
59908
  function ensureAutomationsSupport(db) {
59731
59909
  const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
@@ -60073,215 +60251,432 @@ async function probeSandboxRpc() {
60073
60251
  init_codex_turn_runner();
60074
60252
  init_claude_turn_runner();
60075
60253
 
60076
- // src/session/harness-runtime-ready.ts
60077
- init_claude_turn_runner();
60078
- init_codex_turn_runner();
60079
- init_resolve_service();
60080
- import { existsSync as existsSync22 } from "node:fs";
60081
- function commandExists(command) {
60082
- return Boolean(command && existsSync22(command));
60083
- }
60084
- function envBinaryExists(envName) {
60085
- const v2 = process.env[envName]?.trim();
60086
- return Boolean(v2 && existsSync22(v2));
60087
- }
60088
- function assertSessionHarnessRuntimeReady(sessionHarnessId, harnesses) {
60089
- const id = sessionHarnessId === "acp" ? "acp" : sessionHarnessId;
60090
- if (id === "claude") {
60091
- if (resolveClaudeBinaryPath({ harnesses }) || isClaudeRuntimeRunnable()) {
60092
- return { ok: true, reason: "claude binary or Agent SDK available" };
60093
- }
60094
- return {
60095
- ok: false,
60096
- reason: "claude runtime unavailable: install managed artifact, Agent SDK platform package, or SUPERONE_CLAUDE_BINARY"
60097
- };
60254
+ // ../../packages/runtime/src/harness/manager.ts
60255
+ init_environment();
60256
+ var HarnessManager = class {
60257
+ constructor(db) {
60258
+ this.db = db;
60259
+ this.ensureRows();
60260
+ this.clearSimulatedContamination();
60098
60261
  }
60099
- if (id === "codex") {
60100
- if (resolveCodexBinaryPath({ harnesses }) || isCodexBinaryOverrideRunnable()) {
60101
- return { ok: true, reason: "codex binary available" };
60102
- }
60103
- return {
60104
- ok: false,
60105
- reason: "codex runtime unavailable: enable with --artifact or set SUPERONE_CODEX_BINARY"
60106
- };
60262
+ /**
60263
+ * When true, list/get/ready behave as if every harness is enabled+ready.
60264
+ * Never persisted only for contract/CI simulated runners.
60265
+ */
60266
+ simulatedOverlay = false;
60267
+ /** Ensure one row per first-party definition. */
60268
+ ensureRows() {
60269
+ const now = Date.now();
60270
+ const insert = this.db.prepare(
60271
+ `INSERT OR IGNORE INTO harness_installations
60272
+ (harness_id, enabled, state, runtime_version, command, config_json, secret_ref,
60273
+ diagnostic_code, diagnostic_message, last_probed_at, updated_at)
60274
+ VALUES (?, 0, 'disabled', NULL, NULL, NULL, NULL, NULL, NULL, NULL, ?)`
60275
+ );
60276
+ const tx = this.db.transaction(() => {
60277
+ for (const def of NODE_HARNESS_DEFINITIONS) {
60278
+ insert.run(def.id, now);
60279
+ }
60280
+ });
60281
+ tx();
60107
60282
  }
60108
- if (id === "acp") {
60109
- const status = harnesses.get("acp-grok");
60110
- if (commandExists(status.command) || envBinaryExists("SUPERONE_ACP_BINARY")) {
60111
- return { ok: true, reason: "acp binary available" };
60112
- }
60113
- return {
60114
- ok: false,
60115
- reason: "acp-grok runtime unavailable: enable with --command or set SUPERONE_ACP_BINARY"
60116
- };
60283
+ /**
60284
+ * Reset durable rows that older builds may have marked as simulated-ready.
60285
+ * Safe to run on every open: only touches synthetic simulated provenance.
60286
+ */
60287
+ clearSimulatedContamination() {
60288
+ const now = Date.now();
60289
+ this.db.prepare(
60290
+ `UPDATE harness_installations SET
60291
+ enabled = 0,
60292
+ state = 'disabled',
60293
+ runtime_version = NULL,
60294
+ command = NULL,
60295
+ diagnostic_code = NULL,
60296
+ diagnostic_message = NULL,
60297
+ last_probed_at = NULL,
60298
+ updated_at = ?
60299
+ WHERE runtime_version = 'simulated'
60300
+ OR diagnostic_code = 'simulated'`
60301
+ ).run(now);
60117
60302
  }
60118
- if (id === "opencode") {
60119
- const status = harnesses.get("opencode");
60120
- if (commandExists(status.command) || envBinaryExists("SUPERONE_OPENCODE_BINARY")) {
60121
- return { ok: true, reason: "opencode binary available" };
60122
- }
60303
+ /**
60304
+ * Enable in-memory simulated readiness for all first-party harnesses.
60305
+ * Does not write the database. Production starts must never call this.
60306
+ */
60307
+ enableSimulatedOverlay() {
60308
+ this.simulatedOverlay = true;
60309
+ }
60310
+ /** @deprecated use enableSimulatedOverlay — kept as alias for call-site clarity. */
60311
+ markAllSimulatedReady() {
60312
+ this.enableSimulatedOverlay();
60313
+ }
60314
+ list() {
60315
+ return NODE_HARNESS_DEFINITIONS.map((d) => this.get(d.id));
60316
+ }
60317
+ get(id) {
60318
+ const status = this.readPersisted(id);
60319
+ if (!this.simulatedOverlay) return status;
60123
60320
  return {
60124
- ok: false,
60125
- reason: "opencode runtime unavailable: enable with --command or set SUPERONE_OPENCODE_BINARY"
60321
+ ...status,
60322
+ enabled: true,
60323
+ state: "ready",
60324
+ runtimeVersion: status.runtimeVersion ?? "simulated",
60325
+ diagnostic: buildHarnessDiagnostic("simulated")
60126
60326
  };
60127
60327
  }
60128
- return { ok: false, reason: `unknown harness: ${sessionHarnessId}` };
60129
- }
60130
- function probeHarnessReadiness(harnesses, id, providers) {
60131
- const status = harnesses.get(id);
60132
- const previousState = status.state;
60133
- const issues = [];
60134
- const sessionWire = id === "acp-grok" ? "acp" : id;
60135
- const runtime = assertSessionHarnessRuntimeReady(sessionWire, harnesses);
60136
- if (!runtime.ok) {
60137
- issues.push(runtime.reason);
60138
- if (status.enabled && (status.state === "ready" || status.state === "needs_auth")) {
60139
- harnesses.update(id, {
60140
- state: "error",
60141
- diagnosticCode: "probe_failed",
60142
- lastProbedAt: Date.now()
60143
- });
60144
- const next = harnesses.get(id);
60145
- return {
60146
- id,
60147
- previousState,
60148
- state: next.state,
60149
- transitioned: next.state !== previousState,
60150
- ok: false,
60151
- issues,
60152
- reason: runtime.reason
60153
- };
60328
+ readPersisted(id) {
60329
+ const row = this.db.prepare(
60330
+ `SELECT harness_id, enabled, state, runtime_version, command,
60331
+ diagnostic_code, diagnostic_message
60332
+ FROM harness_installations WHERE harness_id = ?`
60333
+ ).get(id);
60334
+ if (!row) {
60335
+ return defaultHarnessInstallationStatus(id);
60154
60336
  }
60155
- harnesses.update(id, { lastProbedAt: Date.now() });
60156
- return {
60337
+ const def = NODE_HARNESS_DEFINITIONS.find((d) => d.id === id);
60338
+ const status = {
60157
60339
  id,
60158
- previousState,
60159
- state: status.state,
60160
- transitioned: false,
60161
- ok: false,
60162
- issues,
60163
- reason: runtime.reason
60340
+ runtimeSource: def.runtimeSource,
60341
+ enabled: row.enabled === 1,
60342
+ state: parseState(row.state),
60343
+ requiresAuth: def.requiresAuth
60164
60344
  };
60165
- }
60166
- if (status.enabled && status.state === "needs_auth") {
60167
- const authOk = isAuthSatisfied(id, providers);
60168
- if (!authOk.ok) {
60169
- issues.push(authOk.reason);
60170
- harnesses.update(id, {
60171
- lastProbedAt: Date.now(),
60172
- diagnosticCode: "needs_auth"
60345
+ if (row.runtime_version && row.runtime_version !== "simulated") {
60346
+ status.runtimeVersion = row.runtime_version;
60347
+ }
60348
+ const command = sanitizeHarnessCommand(row.command);
60349
+ if (command) status.command = command;
60350
+ const code = sanitizeHarnessDiagnosticCode(row.diagnostic_code);
60351
+ if (code && code !== "simulated") {
60352
+ status.diagnostic = buildHarnessDiagnostic(code, {
60353
+ runtimeVersion: status.runtimeVersion,
60354
+ command: status.command
60173
60355
  });
60174
- return {
60175
- id,
60176
- previousState,
60177
- state: "needs_auth",
60178
- transitioned: false,
60179
- ok: false,
60180
- issues,
60181
- reason: authOk.reason
60182
- };
60183
60356
  }
60184
- harnesses.update(id, {
60185
- state: "ready",
60357
+ return status;
60358
+ }
60359
+ /**
60360
+ * Session wire harness ids that are currently runnable (enabled + ready).
60361
+ * Used by environment.descriptor capabilities.harnessIds.
60362
+ */
60363
+ readySessionHarnessIds() {
60364
+ return readySessionHarnessIds(this.list());
60365
+ }
60366
+ /** Whether a session.create harnessId is allowed on this node. */
60367
+ isSessionHarnessRunnable(sessionHarnessId) {
60368
+ const wire = normalizeSessionHarnessId(sessionHarnessId);
60369
+ if (!wire) return false;
60370
+ const catalogId = wire === "acp" ? "acp-grok" : wire;
60371
+ if (!isNodeHarnessId(catalogId)) return false;
60372
+ return isHarnessRunnable(this.get(catalogId));
60373
+ }
60374
+ update(id, patch) {
60375
+ if (!isNodeHarnessId(id)) {
60376
+ throw new Error(`unknown harness: ${id}`);
60377
+ }
60378
+ const apply = this.db.transaction(() => {
60379
+ const current = this.readPersisted(id);
60380
+ const extra = this.db.prepare(
60381
+ `SELECT config_json, secret_ref, last_probed_at, diagnostic_code
60382
+ FROM harness_installations WHERE harness_id = ?`
60383
+ ).get(id);
60384
+ const enabled = patch.enabled ?? current.enabled;
60385
+ const state = patch.state ?? current.state;
60386
+ const runtimeVersion = patch.runtimeVersion === null ? null : patch.runtimeVersion ?? current.runtimeVersion ?? null;
60387
+ let command;
60388
+ if (patch.command === null) {
60389
+ command = null;
60390
+ } else if (patch.command !== void 0) {
60391
+ command = sanitizeHarnessCommand(patch.command) ?? null;
60392
+ } else {
60393
+ command = current.command ?? null;
60394
+ }
60395
+ let diagnosticCode;
60396
+ if (patch.diagnosticCode === null) {
60397
+ diagnosticCode = null;
60398
+ } else if (patch.diagnosticCode !== void 0) {
60399
+ diagnosticCode = sanitizeHarnessDiagnosticCode(patch.diagnosticCode);
60400
+ } else {
60401
+ diagnosticCode = sanitizeHarnessDiagnosticCode(extra?.diagnostic_code);
60402
+ }
60403
+ let diagnosticMessage = null;
60404
+ if (diagnosticCode) {
60405
+ const fields = {
60406
+ ...patch.diagnosticFields ?? {},
60407
+ runtimeVersion: runtimeVersion ?? void 0,
60408
+ command: command ?? void 0
60409
+ };
60410
+ diagnosticMessage = buildHarnessDiagnostic(
60411
+ diagnosticCode,
60412
+ fields
60413
+ ).message;
60414
+ }
60415
+ const configJson = patch.configJson === null ? null : patch.configJson ?? extra?.config_json ?? null;
60416
+ const secretRef = patch.secretRef === null ? null : patch.secretRef ?? extra?.secret_ref ?? null;
60417
+ const lastProbedAt = patch.lastProbedAt === null ? null : patch.lastProbedAt ?? extra?.last_probed_at ?? null;
60418
+ const now = Date.now();
60419
+ this.db.prepare(
60420
+ `UPDATE harness_installations SET
60421
+ enabled = ?, state = ?, runtime_version = ?, command = ?,
60422
+ config_json = ?, secret_ref = ?,
60423
+ diagnostic_code = ?, diagnostic_message = ?,
60424
+ last_probed_at = ?, updated_at = ?
60425
+ WHERE harness_id = ?`
60426
+ ).run(
60427
+ enabled ? 1 : 0,
60428
+ state,
60429
+ runtimeVersion,
60430
+ command,
60431
+ configJson,
60432
+ secretRef,
60433
+ diagnosticCode,
60434
+ diagnosticMessage,
60435
+ lastProbedAt,
60436
+ now,
60437
+ id
60438
+ );
60439
+ });
60440
+ apply();
60441
+ return this.get(id);
60442
+ }
60443
+ /**
60444
+ * Disable a harness. Does not delete managed artifacts or external binaries.
60445
+ * Default drain policy is enforced by higher-level CLI/RPC in a later slice.
60446
+ */
60447
+ disable(id) {
60448
+ return this.update(id, {
60449
+ enabled: false,
60450
+ state: "disabled",
60186
60451
  diagnosticCode: null,
60187
- lastProbedAt: Date.now()
60452
+ diagnosticFields: null
60188
60453
  });
60189
- return {
60190
- id,
60191
- previousState,
60192
- state: "ready",
60193
- transitioned: true,
60194
- ok: true,
60195
- issues: [],
60196
- reason: `promoted needs_auth \u2192 ready (${authOk.reason})`
60197
- };
60198
60454
  }
60199
- if (status.enabled && status.state === "ready") {
60200
- harnesses.update(id, { lastProbedAt: Date.now(), diagnosticCode: null });
60455
+ /**
60456
+ * Internal non-secret row fields for CLI show/doctor.
60457
+ * Does not expose secret_ref or stored diagnostic message text.
60458
+ */
60459
+ readRawRow(id) {
60460
+ const row = this.db.prepare(
60461
+ `SELECT config_json, last_probed_at
60462
+ FROM harness_installations WHERE harness_id = ?`
60463
+ ).get(id);
60464
+ return row ?? null;
60465
+ }
60466
+ };
60467
+ function parseState(raw) {
60468
+ switch (raw) {
60469
+ case "disabled":
60470
+ case "missing":
60471
+ case "installing":
60472
+ case "needs_auth":
60473
+ case "ready":
60474
+ case "incompatible":
60475
+ case "error":
60476
+ return raw;
60477
+ default:
60478
+ return "error";
60479
+ }
60480
+ }
60481
+
60482
+ // ../../packages/runtime/src/harness/home-path.ts
60483
+ import { homedir as homedir6 } from "node:os";
60484
+ import { join as join18 } from "node:path";
60485
+ var HARNESS_HOME_DIRNAME = "harness";
60486
+ var SUPERONE_DIRNAME = ".superone";
60487
+ function defaultHarnessHomeRoot(userHome = homedir6()) {
60488
+ return join18(userHome, SUPERONE_DIRNAME, HARNESS_HOME_DIRNAME);
60489
+ }
60490
+ function resolveHarnessHomeRoot(opts = {}) {
60491
+ const explicit = opts.override?.trim();
60492
+ if (explicit) return explicit;
60493
+ if (!opts.ignoreEnv) {
60494
+ const fromEnv = process.env.SUPERONE_HARNESS_HOME?.trim();
60495
+ if (fromEnv) return fromEnv;
60496
+ }
60497
+ return defaultHarnessHomeRoot(opts.userHome);
60498
+ }
60499
+
60500
+ // ../../packages/runtime/src/harness/managed-layout.ts
60501
+ import {
60502
+ existsSync as existsSync22,
60503
+ mkdirSync as mkdirSync11,
60504
+ readdirSync as readdirSync8,
60505
+ readFileSync as readFileSync13,
60506
+ renameSync as renameSync2,
60507
+ rmSync as rmSync2,
60508
+ statSync as statSync5,
60509
+ writeFileSync as writeFileSync9
60510
+ } from "node:fs";
60511
+ import { join as join19 } from "node:path";
60512
+ import { randomBytes as randomBytes3 } from "node:crypto";
60513
+ var MANAGED_VERSIONS_DIRNAME = "versions";
60514
+ var MANAGED_CURRENT_BASENAME = "current";
60515
+ var MANAGED_VERSION_KEEP = 2;
60516
+ function sanitizeRuntimeVersionForPath(runtimeVersion) {
60517
+ const v2 = runtimeVersion.trim();
60518
+ if (!v2) throw new Error("empty runtime version");
60519
+ if (v2.includes("..") || v2.includes("/") || v2.includes("\\") || v2.includes("\0")) {
60520
+ throw new Error(`unsafe runtime version for path: ${runtimeVersion}`);
60521
+ }
60522
+ if (!/^[A-Za-z0-9._+-]+$/.test(v2)) {
60523
+ throw new Error(`unsafe runtime version for path: ${runtimeVersion}`);
60524
+ }
60525
+ return v2;
60526
+ }
60527
+ function managedVersionsDir(prefix) {
60528
+ return join19(prefix, MANAGED_VERSIONS_DIRNAME);
60529
+ }
60530
+ function managedVersionDir(prefix, runtimeVersion) {
60531
+ return join19(managedVersionsDir(prefix), sanitizeRuntimeVersionForPath(runtimeVersion));
60532
+ }
60533
+ function managedCurrentPath(prefix) {
60534
+ return join19(prefix, MANAGED_CURRENT_BASENAME);
60535
+ }
60536
+ function readCurrentPointer(prefix) {
60537
+ const path = managedCurrentPath(prefix);
60538
+ if (!existsSync22(path)) return null;
60539
+ try {
60540
+ const raw = JSON.parse(readFileSync13(path, "utf8"));
60541
+ if (typeof raw.runtimeVersion !== "string" || !raw.runtimeVersion.trim()) return null;
60201
60542
  return {
60202
- id,
60203
- previousState,
60204
- state: "ready",
60205
- transitioned: false,
60206
- ok: true,
60207
- issues: [],
60208
- reason: runtime.reason
60543
+ runtimeVersion: raw.runtimeVersion.trim(),
60544
+ installRoot: typeof raw.installRoot === "string" ? raw.installRoot : void 0,
60545
+ updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : void 0
60209
60546
  };
60547
+ } catch {
60548
+ return null;
60210
60549
  }
60211
- harnesses.update(id, { lastProbedAt: Date.now() });
60212
- return {
60213
- id,
60214
- previousState,
60215
- state: status.state,
60216
- transitioned: false,
60217
- ok: status.enabled && status.state === "ready",
60218
- issues: status.enabled ? [`state_${status.state}`] : ["disabled"],
60219
- reason: status.enabled ? `state is ${status.state}` : "harness disabled"
60220
- };
60221
60550
  }
60222
- function isAuthSatisfied(id, providers) {
60223
- if (process.env.SUPERONE_HARNESS_MARK_READY === "1") {
60224
- return { ok: true, reason: "SUPERONE_HARNESS_MARK_READY" };
60551
+ function writeCurrentPointer(prefix, runtimeVersion, extras) {
60552
+ const ver = sanitizeRuntimeVersionForPath(runtimeVersion);
60553
+ mkdirSync11(prefix, { recursive: true });
60554
+ const path = managedCurrentPath(prefix);
60555
+ const body = JSON.stringify(
60556
+ {
60557
+ runtimeVersion: ver,
60558
+ installRoot: extras?.installRoot,
60559
+ updatedAt: Date.now()
60560
+ },
60561
+ null,
60562
+ 2
60563
+ );
60564
+ const tmp = join19(
60565
+ prefix,
60566
+ `.${MANAGED_CURRENT_BASENAME}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`
60567
+ );
60568
+ writeFileSync9(tmp, body);
60569
+ try {
60570
+ renameSync2(tmp, path);
60571
+ } catch {
60572
+ try {
60573
+ if (existsSync22(path)) rmSync2(path, { force: true });
60574
+ renameSync2(tmp, path);
60575
+ } catch {
60576
+ writeFileSync9(path, body);
60577
+ try {
60578
+ rmSync2(tmp, { force: true });
60579
+ } catch {
60580
+ }
60581
+ }
60225
60582
  }
60226
- const defRequiresAuth = id === "claude" || id === "codex";
60227
- if (!defRequiresAuth) {
60228
- return { ok: true, reason: "external harness does not require SuperOne provider auth" };
60583
+ }
60584
+ function resolveActiveInstallRoot(prefix) {
60585
+ if (!prefix || !existsSync22(prefix)) return null;
60586
+ const pointer = readCurrentPointer(prefix);
60587
+ if (pointer) {
60588
+ if (pointer.installRoot && existsSync22(pointer.installRoot) && statSync5(pointer.installRoot).isDirectory()) {
60589
+ return pointer.installRoot;
60590
+ }
60591
+ const dir = managedVersionDir(prefix, pointer.runtimeVersion);
60592
+ if (existsSync22(dir) && statSync5(dir).isDirectory()) return dir;
60229
60593
  }
60230
- if (providers) {
60231
- const consumer = consumerForHarness(id);
60232
- if (consumer) {
60233
- const binding = providers.listBindings().find((b2) => b2.consumer === consumer);
60234
- if (binding?.credentialId) {
60235
- const cred = providers.listCredentials().find((c) => c.id === binding.credentialId);
60236
- if (cred) return { ok: true, reason: `provider binding ${consumer}` };
60594
+ const versionsRoot = managedVersionsDir(prefix);
60595
+ if (existsSync22(versionsRoot)) {
60596
+ try {
60597
+ const kids = readdirSync8(versionsRoot).filter((n) => {
60598
+ try {
60599
+ return statSync5(join19(versionsRoot, n)).isDirectory();
60600
+ } catch {
60601
+ return false;
60602
+ }
60603
+ });
60604
+ if (kids.length === 1) return join19(versionsRoot, kids[0]);
60605
+ } catch {
60606
+ }
60607
+ }
60608
+ return null;
60609
+ }
60610
+ function pruneManagedVersions(prefix, keep, maxKeep = MANAGED_VERSION_KEEP) {
60611
+ const versionsRoot = managedVersionsDir(prefix);
60612
+ if (!existsSync22(versionsRoot)) return;
60613
+ const keepSet = new Set(
60614
+ keep.filter(Boolean).map((v2) => {
60615
+ try {
60616
+ return sanitizeRuntimeVersionForPath(v2);
60617
+ } catch {
60618
+ return "";
60237
60619
  }
60238
- const any2 = providers.listCredentials();
60239
- if (any2.length > 0) {
60240
- return { ok: true, reason: "node has provider credentials" };
60620
+ }).filter(Boolean)
60621
+ );
60622
+ let entries;
60623
+ try {
60624
+ entries = readdirSync8(versionsRoot).map((name) => {
60625
+ try {
60626
+ const st = statSync5(join19(versionsRoot, name));
60627
+ if (!st.isDirectory()) return null;
60628
+ return { name, mtime: st.mtimeMs };
60629
+ } catch {
60630
+ return null;
60241
60631
  }
60242
- }
60632
+ }).filter((e) => e != null);
60633
+ } catch {
60634
+ return;
60243
60635
  }
60244
- if (id === "claude" && isClaudeRuntimeRunnable()) {
60245
- return { ok: true, reason: "claude Agent SDK / host credentials assumed" };
60636
+ const protectedNames = entries.filter((e) => keepSet.has(e.name));
60637
+ const others = entries.filter((e) => !keepSet.has(e.name)).sort((a, b2) => b2.mtime - a.mtime);
60638
+ const retain = new Set(protectedNames.map((e) => e.name));
60639
+ for (const e of others) {
60640
+ if (retain.size >= maxKeep) break;
60641
+ retain.add(e.name);
60246
60642
  }
60247
- if (id === "codex" && isCodexBinaryOverrideRunnable()) {
60248
- return { ok: true, reason: "SUPERONE_CODEX_BINARY host credentials assumed" };
60643
+ for (const e of entries) {
60644
+ if (retain.has(e.name)) continue;
60645
+ const dir = join19(versionsRoot, e.name);
60646
+ try {
60647
+ rmSync2(dir, { recursive: true, force: true });
60648
+ } catch {
60649
+ }
60249
60650
  }
60250
- return {
60251
- ok: false,
60252
- reason: "needs_auth: push provider credentials, complete host CLI login, or set SUPERONE_HARNESS_MARK_READY=1 for lab"
60253
- };
60254
60651
  }
60255
60652
 
60256
- // src/session/harness-enable.ts
60257
- init_environment();
60258
- init_src2();
60259
- import { accessSync, constants, existsSync as existsSync25, realpathSync as realpathSync3, statSync as statSync7 } from "node:fs";
60260
- import { isAbsolute as isAbsolute2, resolve as resolve6 } from "node:path";
60261
-
60262
- // src/session/managed-harness-release.ts
60653
+ // ../../packages/runtime/src/harness/managed-release.ts
60263
60654
  import {
60264
60655
  copyFileSync,
60265
60656
  createReadStream,
60266
60657
  existsSync as existsSync23,
60267
- mkdirSync as mkdirSync11,
60658
+ mkdirSync as mkdirSync12,
60268
60659
  mkdtempSync,
60269
- readFileSync as readFileSync13,
60270
- renameSync as renameSync2,
60271
- rmSync as rmSync2,
60272
- statSync as statSync5,
60273
- writeFileSync as writeFileSync9
60660
+ readFileSync as readFileSync14,
60661
+ renameSync as renameSync3,
60662
+ rmSync as rmSync3,
60663
+ statSync as statSync6,
60664
+ writeFileSync as writeFileSync10
60274
60665
  } from "node:fs";
60275
- import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
60276
- import { dirname as dirname11, join as join18, relative, resolve as resolve4, sep as sep3 } from "node:path";
60666
+ import { createHash as createHash4, randomBytes as randomBytes4 } from "node:crypto";
60667
+ import { dirname as dirname11, join as join20, relative, resolve as resolve4, sep as sep3 } from "node:path";
60277
60668
  import { arch as osArch, platform as osPlatform } from "node:os";
60669
+ var releaseVersionProvider = null;
60670
+ function setHarnessReleaseVersionProvider(fn) {
60671
+ releaseVersionProvider = fn;
60672
+ }
60278
60673
  var MANAGED_PAYLOAD_BASENAME = "payload.bin";
60279
60674
  var MANAGED_META_BASENAME = "artifact.json";
60280
- var MANAGED_CURRENT_BASENAME = "current";
60675
+ var MANAGED_CURRENT_BASENAME2 = "current";
60281
60676
  var MAX_SEGMENT_LEN = 64;
60282
60677
  var SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
60283
60678
  function currentCliVersion() {
60284
- const v2 = resolveCliReleaseVersion();
60679
+ const v2 = releaseVersionProvider?.() ?? process.env.SUPERONE_CLI_VERSION?.trim() ?? "";
60285
60680
  return assertSafePathSegment(v2, "cli version");
60286
60681
  }
60287
60682
  function currentHostPlatform() {
@@ -60375,11 +60770,37 @@ function parseManagedHarnessPin(id, raw) {
60375
60770
  }
60376
60771
  fileName = art.fileName.slice(0, 128);
60377
60772
  }
60773
+ let url2;
60774
+ if (typeof art.url === "string" && art.url.trim()) {
60775
+ const u = art.url.trim();
60776
+ if (!/^https:\/\//i.test(u)) {
60777
+ throw new Error(`manifest ${id} artifacts[${i}] url must be https`);
60778
+ }
60779
+ if (u.length > 1024) {
60780
+ throw new Error(`manifest ${id} artifacts[${i}] url exceeds 1024 chars`);
60781
+ }
60782
+ url2 = u;
60783
+ }
60784
+ let npmName;
60785
+ if (typeof art.npmName === "string" && art.npmName.trim()) {
60786
+ const n = art.npmName.trim();
60787
+ if (!/^(@[A-Za-z0-9._-]+\/)?[A-Za-z0-9._-]+$/.test(n) || n.length > 214) {
60788
+ throw new Error(`manifest ${id} artifacts[${i}] npmName invalid`);
60789
+ }
60790
+ npmName = n;
60791
+ }
60792
+ let npmVersion;
60793
+ if (typeof art.npmVersion === "string" && art.npmVersion.trim()) {
60794
+ npmVersion = assertSafePathSegment(art.npmVersion, `${id}.artifacts[${i}].npmVersion`);
60795
+ }
60378
60796
  return {
60379
60797
  platform: platform2,
60380
60798
  arch: arch2,
60381
60799
  digestSha256: digest.toLowerCase(),
60382
- fileName
60800
+ fileName,
60801
+ url: url2,
60802
+ npmName,
60803
+ npmVersion
60383
60804
  };
60384
60805
  });
60385
60806
  return { runtimeVersion, artifactVersion, artifacts };
@@ -60390,11 +60811,11 @@ function loadHarnessReleaseManifest(nodeHome) {
60390
60811
  if (!existsSync23(fromEnv)) {
60391
60812
  throw new Error(`SUPERONE_HARNESS_MANIFEST not found: ${fromEnv}`);
60392
60813
  }
60393
- return parseHarnessReleaseManifest(JSON.parse(readFileSync13(fromEnv, "utf8")));
60814
+ return parseHarnessReleaseManifest(JSON.parse(readFileSync14(fromEnv, "utf8")));
60394
60815
  }
60395
- const local = join18(nodeHome, "release-manifest.json");
60816
+ const local = join20(nodeHome, "release-manifest.json");
60396
60817
  if (existsSync23(local)) {
60397
- return parseHarnessReleaseManifest(JSON.parse(readFileSync13(local, "utf8")));
60818
+ return parseHarnessReleaseManifest(JSON.parse(readFileSync14(local, "utf8")));
60398
60819
  }
60399
60820
  return null;
60400
60821
  }
@@ -60465,7 +60886,7 @@ async function installManagedArtifactFromFile(opts) {
60465
60886
  if (!existsSync23(opts.artifactPath)) {
60466
60887
  throw new Error(`artifact not found: ${opts.artifactPath}`);
60467
60888
  }
60468
- if (!statSync5(opts.artifactPath).isFile()) {
60889
+ if (!statSync6(opts.artifactPath).isFile()) {
60469
60890
  throw new Error(`artifact is not a regular file: ${opts.artifactPath}`);
60470
60891
  }
60471
60892
  const digest = await sha256File(opts.artifactPath);
@@ -60480,8 +60901,8 @@ async function installManagedArtifactFromFile(opts) {
60480
60901
  opts.harnessId,
60481
60902
  pin.artifactVersion
60482
60903
  );
60483
- const finalFile = join18(destDir, MANAGED_PAYLOAD_BASENAME);
60484
- const metaPath = join18(destDir, MANAGED_META_BASENAME);
60904
+ const finalFile = join20(destDir, MANAGED_PAYLOAD_BASENAME);
60905
+ const metaPath = join20(destDir, MANAGED_META_BASENAME);
60485
60906
  assertStrictChild(finalFile, destDir, "payload path");
60486
60907
  assertStrictChild(metaPath, destDir, "meta path");
60487
60908
  const metaBody = JSON.stringify(
@@ -60501,7 +60922,7 @@ async function installManagedArtifactFromFile(opts) {
60501
60922
  );
60502
60923
  let reusedExisting = false;
60503
60924
  if (existsSync23(destDir)) {
60504
- const payloadOk = existsSync23(finalFile) && statSync5(finalFile).isFile() && await sha256File(finalFile) === art.digestSha256;
60925
+ const payloadOk = existsSync23(finalFile) && statSync6(finalFile).isFile() && await sha256File(finalFile) === art.digestSha256;
60505
60926
  if (payloadOk) {
60506
60927
  reusedExisting = true;
60507
60928
  } else if (mode === "repair") {
@@ -60521,24 +60942,24 @@ async function installManagedArtifactFromFile(opts) {
60521
60942
  }
60522
60943
  } else {
60523
60944
  const harnessRoot2 = dirname11(destDir);
60524
- mkdirSync11(harnessRoot2, { recursive: true });
60945
+ mkdirSync12(harnessRoot2, { recursive: true });
60525
60946
  assertPathInside(destDir, harnessRoot2, "version dir");
60526
60947
  const stagingDir = mkdtempSync(
60527
- join18(harnessRoot2, `.staging-${opts.harnessId}-${randomBytes3(8).toString("hex")}-`)
60948
+ join20(harnessRoot2, `.staging-${opts.harnessId}-${randomBytes4(8).toString("hex")}-`)
60528
60949
  );
60529
60950
  assertPathInside(stagingDir, harnessRoot2, "staging dir");
60530
- const stagingFile = join18(stagingDir, MANAGED_PAYLOAD_BASENAME);
60531
- const stagingMeta = join18(stagingDir, MANAGED_META_BASENAME);
60951
+ const stagingFile = join20(stagingDir, MANAGED_PAYLOAD_BASENAME);
60952
+ const stagingMeta = join20(stagingDir, MANAGED_META_BASENAME);
60532
60953
  try {
60533
60954
  copyFileSync(opts.artifactPath, stagingFile);
60534
60955
  const stagedDigest = await sha256File(stagingFile);
60535
60956
  if (stagedDigest !== art.digestSha256) {
60536
60957
  throw new Error(`staged artifact digest mismatch for ${opts.harnessId}`);
60537
60958
  }
60538
- writeFileSync9(stagingMeta, metaBody, "utf8");
60539
- renameSync2(stagingDir, destDir);
60959
+ writeFileSync10(stagingMeta, metaBody, "utf8");
60960
+ renameSync3(stagingDir, destDir);
60540
60961
  } catch (err) {
60541
- rmSync2(stagingDir, { recursive: true, force: true });
60962
+ rmSync3(stagingDir, { recursive: true, force: true });
60542
60963
  if (existsSync23(destDir) && existsSync23(finalFile)) {
60543
60964
  const existingDigest = await sha256File(finalFile);
60544
60965
  if (existingDigest === art.digestSha256) {
@@ -60564,22 +60985,22 @@ async function installManagedArtifactFromFile(opts) {
60564
60985
  if (finalDigest !== art.digestSha256) {
60565
60986
  throw new Error(`final payload digest mismatch for ${opts.harnessId}`);
60566
60987
  }
60567
- const harnessRoot = join18(
60988
+ const harnessRoot = join20(
60568
60989
  releasesRoot(opts.nodeHome),
60569
60990
  opts.manifest.cliVersion,
60570
60991
  "harnesses",
60571
60992
  opts.harnessId
60572
60993
  );
60573
60994
  assertPathInside(harnessRoot, releasesRoot(opts.nodeHome), "harness root");
60574
- mkdirSync11(harnessRoot, { recursive: true });
60575
- const currentPath = join18(harnessRoot, MANAGED_CURRENT_BASENAME);
60576
- const currentTmp = join18(
60995
+ mkdirSync12(harnessRoot, { recursive: true });
60996
+ const currentPath = join20(harnessRoot, MANAGED_CURRENT_BASENAME2);
60997
+ const currentTmp = join20(
60577
60998
  harnessRoot,
60578
- `.${MANAGED_CURRENT_BASENAME}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`
60999
+ `.${MANAGED_CURRENT_BASENAME2}.${process.pid}.${randomBytes4(6).toString("hex")}.tmp`
60579
61000
  );
60580
61001
  assertStrictChild(currentTmp, harnessRoot, "current pointer temp");
60581
61002
  try {
60582
- writeFileSync9(
61003
+ writeFileSync10(
60583
61004
  currentTmp,
60584
61005
  JSON.stringify(
60585
61006
  {
@@ -60593,9 +61014,9 @@ async function installManagedArtifactFromFile(opts) {
60593
61014
  ),
60594
61015
  "utf8"
60595
61016
  );
60596
- renameSync2(currentTmp, currentPath);
61017
+ renameSync3(currentTmp, currentPath);
60597
61018
  } catch (err) {
60598
- rmSync2(currentTmp, { force: true });
61019
+ rmSync3(currentTmp, { force: true });
60599
61020
  throw err;
60600
61021
  }
60601
61022
  return {
@@ -60610,10 +61031,10 @@ async function installManagedArtifactFromFile(opts) {
60610
61031
  };
60611
61032
  }
60612
61033
  async function replacePayloadAtomically(opts) {
60613
- mkdirSync11(opts.destDir, { recursive: true });
60614
- const nonce = randomBytes3(8).toString("hex");
60615
- const payloadTmp = join18(opts.destDir, `.${MANAGED_PAYLOAD_BASENAME}.${nonce}.tmp`);
60616
- const metaTmp = join18(opts.destDir, `.${MANAGED_META_BASENAME}.${nonce}.tmp`);
61034
+ mkdirSync12(opts.destDir, { recursive: true });
61035
+ const nonce = randomBytes4(8).toString("hex");
61036
+ const payloadTmp = join20(opts.destDir, `.${MANAGED_PAYLOAD_BASENAME}.${nonce}.tmp`);
61037
+ const metaTmp = join20(opts.destDir, `.${MANAGED_META_BASENAME}.${nonce}.tmp`);
60617
61038
  assertStrictChild(payloadTmp, opts.destDir, "payload temp");
60618
61039
  assertStrictChild(metaTmp, opts.destDir, "meta temp");
60619
61040
  try {
@@ -60622,12 +61043,12 @@ async function replacePayloadAtomically(opts) {
60622
61043
  if (d !== opts.expectedDigest) {
60623
61044
  throw new Error(`repair staged digest mismatch: expected ${opts.expectedDigest}, got ${d}`);
60624
61045
  }
60625
- writeFileSync9(metaTmp, opts.metaBody, "utf8");
60626
- renameSync2(payloadTmp, opts.finalFile);
60627
- renameSync2(metaTmp, opts.metaPath);
61046
+ writeFileSync10(metaTmp, opts.metaBody, "utf8");
61047
+ renameSync3(payloadTmp, opts.finalFile);
61048
+ renameSync3(metaTmp, opts.metaPath);
60628
61049
  } catch (err) {
60629
- rmSync2(payloadTmp, { force: true });
60630
- rmSync2(metaTmp, { force: true });
61050
+ rmSync3(payloadTmp, { force: true });
61051
+ rmSync3(metaTmp, { force: true });
60631
61052
  throw err;
60632
61053
  }
60633
61054
  }
@@ -60646,14 +61067,967 @@ function requiredRuntimeVersion(harnessId, manifest) {
60646
61067
  return manifest?.managedHarnesses[harnessId]?.runtimeVersion ?? null;
60647
61068
  }
60648
61069
 
60649
- // src/session/harness-enable.ts
60650
- init_codex_turn_runner();
60651
- async function enableHarness(manager, input, providers) {
61070
+ // ../../packages/runtime/src/harness/managed-official.ts
61071
+ import { existsSync as existsSync24, mkdirSync as mkdirSync13, readFileSync as readFileSync15, readdirSync as readdirSync9, statSync as statSync7, writeFileSync as writeFileSync11 } from "node:fs";
61072
+ import { arch as osArch2, platform as osPlatform2 } from "node:os";
61073
+ import { join as join21, resolve as resolve5 } from "node:path";
61074
+ var OFFICIAL_CLAUDE_SDK_VERSION = "0.3.226";
61075
+ var OFFICIAL_CODEX_NPM_VERSION = "0.146.1";
61076
+ var OFFICIAL_CODEX_PACKAGE = "@openai/codex";
61077
+ function managedHarnessPrefix(nodeHome, harnessId) {
61078
+ return resolve5(nodeHome, harnessId);
61079
+ }
61080
+ function claudePlatformPackageName() {
61081
+ const p2 = osPlatform2();
61082
+ const a = osArch2();
61083
+ if (a !== "arm64" && a !== "x64") {
61084
+ throw new Error(`unsupported arch for Claude Agent SDK: ${a}`);
61085
+ }
61086
+ if (p2 === "darwin") return `@anthropic-ai/claude-agent-sdk-darwin-${a}`;
61087
+ if (p2 === "win32") return `@anthropic-ai/claude-agent-sdk-win32-${a}`;
61088
+ if (p2 === "linux") {
61089
+ if (isMuslLinux()) return `@anthropic-ai/claude-agent-sdk-linux-${a}-musl`;
61090
+ return `@anthropic-ai/claude-agent-sdk-linux-${a}`;
61091
+ }
61092
+ throw new Error(`unsupported platform for Claude Agent SDK: ${p2}`);
61093
+ }
61094
+ function isMuslLinux() {
61095
+ try {
61096
+ if (existsSync24("/etc/alpine-release")) return true;
61097
+ const lib = readdirSync9("/lib").some((n) => n.startsWith("ld-musl"));
61098
+ if (lib) return true;
61099
+ } catch {
61100
+ }
61101
+ return false;
61102
+ }
61103
+ function resolveOfficialInstallBinaryInRoot(harnessId, installRoot) {
61104
+ if (!installRoot || !existsSync24(installRoot)) return null;
61105
+ if (harnessId === "codex") {
61106
+ const candidates = [
61107
+ join21(installRoot, "bin", "codex"),
61108
+ join21(installRoot, "bin", "codex.cmd"),
61109
+ join21(installRoot, "lib", "node_modules", "@openai", "codex", "bin", "codex.js")
61110
+ ];
61111
+ for (const c of candidates) {
61112
+ if (existsSync24(c) && (c.endsWith(".js") || isExecutableFile(c))) return c;
61113
+ }
61114
+ return null;
61115
+ }
61116
+ const nm = join21(installRoot, "lib", "node_modules");
61117
+ const scoped = join21(nm, "@anthropic-ai");
61118
+ try {
61119
+ if (existsSync24(scoped)) {
61120
+ const names = readdirSync9(scoped).filter((n) => n.startsWith("claude-agent-sdk-"));
61121
+ for (const n of names) {
61122
+ const ext = process.platform === "win32" ? ".exe" : "";
61123
+ const bin = join21(scoped, n, `claude${ext}`);
61124
+ if (existsSync24(bin)) return bin;
61125
+ }
61126
+ }
61127
+ } catch {
61128
+ }
61129
+ const direct = join21(
61130
+ nm,
61131
+ ...claudePlatformPackageName().split("/"),
61132
+ process.platform === "win32" ? "claude.exe" : "claude"
61133
+ );
61134
+ if (existsSync24(direct)) return direct;
61135
+ return null;
61136
+ }
61137
+ function isExecutableFile(path) {
61138
+ try {
61139
+ const st = statSync7(path);
61140
+ if (!st.isFile()) return false;
61141
+ if (process.platform === "win32") return true;
61142
+ return (st.mode & 73) !== 0;
61143
+ } catch {
61144
+ return false;
61145
+ }
61146
+ }
61147
+
61148
+ // ../../packages/runtime/src/harness/tarball-fetch.ts
61149
+ import { existsSync as existsSync25, rmSync as rmSync4 } from "node:fs";
61150
+ var NPM_REGISTRY = "https://registry.npmjs.org";
61151
+ function assertSha256(actualHex, expectedHex) {
61152
+ if (actualHex !== expectedHex.toLowerCase()) {
61153
+ throw new Error(`tarball sha256 mismatch: expected ${expectedHex}, got ${actualHex}`);
61154
+ }
61155
+ }
61156
+ function assertSha512Integrity(actualBase64, integrity) {
61157
+ if (!integrity.startsWith("sha512-")) {
61158
+ throw new Error(`unsupported integrity algorithm: ${integrity.slice(0, 16)}`);
61159
+ }
61160
+ const expected = integrity.slice("sha512-".length);
61161
+ if (actualBase64 !== expected) {
61162
+ throw new Error(`tarball integrity mismatch (sha512)`);
61163
+ }
61164
+ }
61165
+ function discardPartial(destPath) {
61166
+ try {
61167
+ if (existsSync25(destPath)) rmSync4(destPath, { force: true });
61168
+ } catch {
61169
+ }
61170
+ }
61171
+ function createFetchJson(httpFetch) {
61172
+ return async (url2) => {
61173
+ const res = await httpFetch(url2, { headers: { accept: "application/json" } });
61174
+ if (!res.ok) throw new Error(`registry GET ${url2} \u2192 ${res.status}`);
61175
+ return res.json();
61176
+ };
61177
+ }
61178
+ async function resolveNpmPackMeta(name, version2, fetchJson) {
61179
+ const encoded = name.startsWith("@") ? `/${name.replace("/", "%2f")}/${encodeURIComponent(version2)}` : `/${name}/${encodeURIComponent(version2)}`;
61180
+ const url2 = `${NPM_REGISTRY}${encoded}`;
61181
+ const raw = await fetchJson(url2);
61182
+ const dist = raw.dist;
61183
+ if (!dist?.tarball || !dist.integrity) {
61184
+ throw new Error(`npm package ${name}@${version2} missing dist.tarball/integrity`);
61185
+ }
61186
+ if (!dist.integrity.startsWith("sha512-")) {
61187
+ throw new Error(`npm package ${name}@${version2} integrity is not sha512: ${dist.integrity}`);
61188
+ }
61189
+ return {
61190
+ name,
61191
+ version: typeof raw.version === "string" ? raw.version : version2,
61192
+ tarball: dist.tarball,
61193
+ integrity: dist.integrity
61194
+ };
61195
+ }
61196
+ async function fetchTarballWithFallback(opts) {
61197
+ const {
61198
+ destPath,
61199
+ npmName,
61200
+ npmVersion,
61201
+ pin,
61202
+ npmOnly,
61203
+ fetchJson,
61204
+ downloadToFile,
61205
+ onProgress,
61206
+ log: log2
61207
+ } = opts;
61208
+ const expectedSha = pin?.digestSha256;
61209
+ if (!npmOnly && pin?.url) {
61210
+ try {
61211
+ log2?.info?.(`[harness] fetching R2 ${pin.url}`);
61212
+ const digests = await downloadToFile(pin.url, destPath, onProgress);
61213
+ if (expectedSha) assertSha256(digests.sha256Hex, expectedSha);
61214
+ return { from: "r2-tarball", digests };
61215
+ } catch (err) {
61216
+ log2?.warn?.(
61217
+ `[harness] R2 fetch failed (${err instanceof Error ? err.message : String(err)}); trying npm`
61218
+ );
61219
+ if (err instanceof Error && /sha256 mismatch|integrity mismatch/i.test(err.message)) {
61220
+ discardPartial(destPath);
61221
+ }
61222
+ }
61223
+ }
61224
+ log2?.info?.(`[harness] fetching npm ${npmName}@${npmVersion}`);
61225
+ const meta3 = await resolveNpmPackMeta(npmName, npmVersion, fetchJson);
61226
+ try {
61227
+ const digests = await downloadToFile(meta3.tarball, destPath, onProgress);
61228
+ if (expectedSha) {
61229
+ assertSha256(digests.sha256Hex, expectedSha);
61230
+ } else {
61231
+ assertSha512Integrity(digests.sha512Base64, meta3.integrity);
61232
+ }
61233
+ return { from: "npm-tarball", digests, npmMeta: meta3 };
61234
+ } catch (err) {
61235
+ if (err instanceof Error && /sha256 mismatch|integrity mismatch/i.test(err.message)) {
61236
+ discardPartial(destPath);
61237
+ }
61238
+ throw err;
61239
+ }
61240
+ }
61241
+
61242
+ // ../../packages/runtime/src/harness/resumable-download.ts
61243
+ import { createHash as createHash5 } from "node:crypto";
61244
+ import {
61245
+ appendFileSync,
61246
+ createReadStream as createReadStream2,
61247
+ createWriteStream,
61248
+ existsSync as existsSync26,
61249
+ mkdirSync as mkdirSync14,
61250
+ rmSync as rmSync5,
61251
+ statSync as statSync8,
61252
+ writeFileSync as writeFileSync12
61253
+ } from "node:fs";
61254
+ import { dirname as dirname12 } from "node:path";
61255
+ import { pipeline } from "node:stream/promises";
61256
+ import { Readable as Readable2, Transform } from "node:stream";
61257
+ var HARNESS_PROGRESS_THROTTLE_MS = 200;
61258
+ function createThrottledProgress(onProgress, throttleMs = HARNESS_PROGRESS_THROTTLE_MS) {
61259
+ if (!onProgress) return void 0;
61260
+ let lastEmit = 0;
61261
+ return (received, total) => {
61262
+ const now = Date.now();
61263
+ const done = total > 0 && received >= total;
61264
+ if (done || lastEmit === 0 || now - lastEmit >= throttleMs) {
61265
+ lastEmit = now;
61266
+ onProgress(received, total);
61267
+ }
61268
+ };
61269
+ }
61270
+ function parseContentRange(header) {
61271
+ if (!header) return null;
61272
+ const m2 = /^bytes\s+(\d+)-(\d+)\/(\d+|\*)$/i.exec(header.trim());
61273
+ if (!m2) return null;
61274
+ return {
61275
+ start: Number(m2[1]),
61276
+ end: Number(m2[2]),
61277
+ total: m2[3] === "*" ? null : Number(m2[3])
61278
+ };
61279
+ }
61280
+ async function seedHashesFromFile(path) {
61281
+ const sha256 = createHash5("sha256");
61282
+ const sha512 = createHash5("sha512");
61283
+ const size = statSync8(path).size;
61284
+ if (size === 0) return { sha256, sha512, size };
61285
+ await new Promise((resolve13, reject) => {
61286
+ const stream = createReadStream2(path);
61287
+ stream.on("data", (chunk) => {
61288
+ const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
61289
+ sha256.update(buf);
61290
+ sha512.update(buf);
61291
+ });
61292
+ stream.on("error", reject);
61293
+ stream.on("end", () => resolve13());
61294
+ });
61295
+ return { sha256, sha512, size };
61296
+ }
61297
+ async function hashExistingFile(path, onProgress) {
61298
+ const { sha256, sha512, size } = await seedHashesFromFile(path);
61299
+ onProgress?.(size, size);
61300
+ return {
61301
+ byteLength: size,
61302
+ sha256Hex: sha256.digest("hex"),
61303
+ sha512Base64: sha512.digest("base64")
61304
+ };
61305
+ }
61306
+ async function streamResponseToFile(res, destPath, onProgress, opts = {}) {
61307
+ if (!res.ok && res.status !== 206) {
61308
+ throw new Error(`tarball GET \u2192 ${res.status}`);
61309
+ }
61310
+ const resumeFrom = opts.resumeFrom ?? 0;
61311
+ const append = opts.append === true && resumeFrom > 0;
61312
+ const keepPartial = opts.keepPartialOnError !== false;
61313
+ const contentLen = Number(res.headers.get("content-length") ?? 0);
61314
+ const total = opts.totalBytes && opts.totalBytes > 0 ? opts.totalBytes : append ? resumeFrom + contentLen : contentLen;
61315
+ mkdirSync14(dirname12(destPath), { recursive: true });
61316
+ const emit = createThrottledProgress(onProgress, opts.progressThrottleMs);
61317
+ let sha256;
61318
+ let sha512;
61319
+ let received;
61320
+ if (append) {
61321
+ const seeded = await seedHashesFromFile(destPath);
61322
+ if (seeded.size !== resumeFrom) {
61323
+ throw new Error(
61324
+ `partial size mismatch: disk=${seeded.size} resumeFrom=${resumeFrom}`
61325
+ );
61326
+ }
61327
+ sha256 = seeded.sha256;
61328
+ sha512 = seeded.sha512;
61329
+ received = resumeFrom;
61330
+ emit?.(received, total || received);
61331
+ } else {
61332
+ sha256 = createHash5("sha256");
61333
+ sha512 = createHash5("sha512");
61334
+ received = 0;
61335
+ }
61336
+ if (!res.body) {
61337
+ const buf = Buffer.from(await res.arrayBuffer());
61338
+ if (append) {
61339
+ appendFileSync(destPath, buf);
61340
+ } else {
61341
+ writeFileSync12(destPath, buf);
61342
+ }
61343
+ sha256.update(buf);
61344
+ sha512.update(buf);
61345
+ received += buf.byteLength;
61346
+ emit?.(received, total || received);
61347
+ onProgress?.(received, total || received);
61348
+ return {
61349
+ byteLength: received,
61350
+ sha256Hex: sha256.digest("hex"),
61351
+ sha512Base64: sha512.digest("base64")
61352
+ };
61353
+ }
61354
+ const nodeStream = Readable2.fromWeb(res.body);
61355
+ const hasher = new Transform({
61356
+ transform(chunk, _enc, cb) {
61357
+ sha256.update(chunk);
61358
+ sha512.update(chunk);
61359
+ received += chunk.byteLength;
61360
+ emit?.(received, total || received);
61361
+ cb(null, chunk);
61362
+ }
61363
+ });
61364
+ try {
61365
+ await pipeline(
61366
+ nodeStream,
61367
+ hasher,
61368
+ createWriteStream(destPath, append ? { flags: "a" } : void 0)
61369
+ );
61370
+ } catch (err) {
61371
+ if (!keepPartial) {
61372
+ try {
61373
+ if (existsSync26(destPath)) rmSync5(destPath, { force: true });
61374
+ } catch {
61375
+ }
61376
+ }
61377
+ throw err;
61378
+ }
61379
+ onProgress?.(received, total || received);
61380
+ return {
61381
+ byteLength: received,
61382
+ sha256Hex: sha256.digest("hex"),
61383
+ sha512Base64: sha512.digest("base64")
61384
+ };
61385
+ }
61386
+ var destPathLocks = /* @__PURE__ */ new Map();
61387
+ async function withDestPathLock(destPath, fn) {
61388
+ const prev = destPathLocks.get(destPath) ?? Promise.resolve();
61389
+ let release;
61390
+ const gate = new Promise((resolve13) => {
61391
+ release = resolve13;
61392
+ });
61393
+ const held = prev.then(() => gate);
61394
+ destPathLocks.set(
61395
+ destPath,
61396
+ held.catch(() => {
61397
+ })
61398
+ );
61399
+ await prev.catch(() => {
61400
+ });
61401
+ try {
61402
+ return await fn();
61403
+ } finally {
61404
+ release();
61405
+ if (destPathLocks.get(destPath) === held) {
61406
+ destPathLocks.delete(destPath);
61407
+ }
61408
+ }
61409
+ }
61410
+ async function downloadResumableToFile(httpFetch, url2, destPath, onProgress, log2) {
61411
+ return withDestPathLock(
61412
+ destPath,
61413
+ () => downloadResumableToFileUnlocked(httpFetch, url2, destPath, onProgress, log2)
61414
+ );
61415
+ }
61416
+ async function downloadResumableToFileUnlocked(httpFetch, url2, destPath, onProgress, log2) {
61417
+ mkdirSync14(dirname12(destPath), { recursive: true });
61418
+ let existing = existsSync26(destPath) ? statSync8(destPath).size : 0;
61419
+ if (existing > 0 && existing < 64) {
61420
+ rmSync5(destPath, { force: true });
61421
+ existing = 0;
61422
+ }
61423
+ const tryOnce = async (from) => {
61424
+ const diskNow = existsSync26(destPath) ? statSync8(destPath).size : 0;
61425
+ const start = from > 0 ? diskNow : 0;
61426
+ if (from > 0 && diskNow !== from) {
61427
+ log2?.warn?.(
61428
+ `[harness] partial size changed before Range (wanted ${from}, disk ${diskNow}); using disk`
61429
+ );
61430
+ }
61431
+ const headers = {};
61432
+ if (start > 0) {
61433
+ headers.Range = `bytes=${start}-`;
61434
+ log2?.info?.(`[harness] resume download from byte ${start} \u2192 ${url2}`);
61435
+ }
61436
+ const res = await httpFetch(url2, { headers });
61437
+ if (res.status === 416 && start > 0) {
61438
+ try {
61439
+ const head = await httpFetch(url2, { method: "HEAD" });
61440
+ const len = Number(head.headers.get("content-length") ?? 0);
61441
+ if (len > 0 && start === len) {
61442
+ log2?.info?.(`[harness] partial already complete (${len} B); reusing`);
61443
+ return hashExistingFile(destPath, onProgress);
61444
+ }
61445
+ } catch {
61446
+ }
61447
+ log2?.warn?.(`[harness] Range 416 for ${url2}; discarding partial and restarting`);
61448
+ rmSync5(destPath, { force: true });
61449
+ return tryOnce(0);
61450
+ }
61451
+ if (res.status === 206) {
61452
+ const cr = parseContentRange(res.headers.get("content-range"));
61453
+ if (cr && cr.start !== start) {
61454
+ log2?.warn?.(
61455
+ `[harness] Content-Range start ${cr.start} \u2260 local ${start}; restarting`
61456
+ );
61457
+ rmSync5(destPath, { force: true });
61458
+ return tryOnce(0);
61459
+ }
61460
+ const total = cr?.total ?? start + Number(res.headers.get("content-length") ?? 0);
61461
+ try {
61462
+ return await streamResponseToFile(res, destPath, onProgress, {
61463
+ resumeFrom: start,
61464
+ append: true,
61465
+ totalBytes: total > 0 ? total : void 0,
61466
+ keepPartialOnError: true
61467
+ });
61468
+ } catch (err) {
61469
+ if (err instanceof Error && /partial size mismatch/i.test(err.message)) {
61470
+ log2?.warn?.(`[harness] ${err.message}; discarding partial and restarting`);
61471
+ rmSync5(destPath, { force: true });
61472
+ return tryOnce(0);
61473
+ }
61474
+ throw err;
61475
+ }
61476
+ }
61477
+ if (res.status === 200) {
61478
+ if (start > 0) {
61479
+ log2?.info?.(`[harness] server ignored Range for ${url2}; full re-download`);
61480
+ try {
61481
+ rmSync5(destPath, { force: true });
61482
+ } catch {
61483
+ }
61484
+ }
61485
+ const total = Number(res.headers.get("content-length") ?? 0);
61486
+ return streamResponseToFile(res, destPath, onProgress, {
61487
+ resumeFrom: 0,
61488
+ append: false,
61489
+ totalBytes: total > 0 ? total : void 0,
61490
+ keepPartialOnError: true
61491
+ });
61492
+ }
61493
+ if (!res.ok) throw new Error(`tarball GET ${url2} \u2192 ${res.status}`);
61494
+ return streamResponseToFile(res, destPath, onProgress, {
61495
+ keepPartialOnError: true
61496
+ });
61497
+ };
61498
+ return tryOnce(existing);
61499
+ }
61500
+ function createResumableDownloadToFile(httpFetch, log2) {
61501
+ return (url2, destPath, onProgress) => downloadResumableToFile(httpFetch, url2, destPath, onProgress, log2);
61502
+ }
61503
+
61504
+ // ../../packages/runtime/src/harness/managed-tarball-installer.ts
61505
+ import {
61506
+ existsSync as existsSync27,
61507
+ mkdirSync as mkdirSync15,
61508
+ mkdtempSync as mkdtempSync2,
61509
+ readFileSync as readFileSync16,
61510
+ renameSync as renameSync4,
61511
+ rmSync as rmSync6,
61512
+ statSync as statSync9,
61513
+ writeFileSync as writeFileSync13
61514
+ } from "node:fs";
61515
+ import { tmpdir as tmpdir2 } from "node:os";
61516
+ import { dirname as dirname13, join as join22 } from "node:path";
61517
+ import { spawn as spawn4 } from "node:child_process";
61518
+
61519
+ // ../../packages/shared/src/update-channels.ts
61520
+ function channelFromVersion(version2) {
61521
+ if (/-alpha/i.test(version2)) return "alpha";
61522
+ if (/-beta/i.test(version2)) return "beta";
61523
+ return "stable";
61524
+ }
61525
+
61526
+ // ../../packages/runtime/src/harness/cdn.ts
61527
+ var HARNESS_CDN_BASE = "https://dl.super-one.dev";
61528
+ var HARNESS_CDN_BUCKET_PREFIX = "harness";
61529
+ function isHarnessManifestChannel(value) {
61530
+ return value === "alpha" || value === "beta" || value === "stable";
61531
+ }
61532
+ function harnessChannelManifestObjectKey(channel) {
61533
+ return `${HARNESS_CDN_BUCKET_PREFIX}/manifest/${channel}.json`;
61534
+ }
61535
+ function harnessChannelManifestUrl(channel, base = HARNESS_CDN_BASE) {
61536
+ return `${base.replace(/\/+$/, "")}/${harnessChannelManifestObjectKey(channel)}`;
61537
+ }
61538
+ async function fetchHarnessChannelManifest(opts) {
61539
+ const url2 = harnessChannelManifestUrl(opts.channel, opts.baseUrl ?? HARNESS_CDN_BASE);
61540
+ const fetchJson = opts.fetchJson ?? (async (u) => {
61541
+ const ctrl = new AbortController();
61542
+ const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
61543
+ try {
61544
+ const res = await fetch(u, {
61545
+ signal: ctrl.signal,
61546
+ headers: { accept: "application/json" }
61547
+ });
61548
+ if (!res.ok) throw new Error(`manifest GET ${u} \u2192 ${res.status}`);
61549
+ return res.json();
61550
+ } finally {
61551
+ clearTimeout(timer);
61552
+ }
61553
+ });
61554
+ return parseHarnessReleaseManifest(await fetchJson(url2));
61555
+ }
61556
+ function selectHarnessArtifact(manifest, harnessId, platform2, arch2) {
61557
+ const pin = manifest.managedHarnesses[harnessId];
61558
+ if (!pin) return null;
61559
+ return pin.artifacts.find((a) => a.platform === platform2 && a.arch === arch2) ?? null;
61560
+ }
61561
+
61562
+ // ../../packages/runtime/src/harness/managed-tarball-installer.ts
61563
+ function codexPlatformVersion(baseVersion = OFFICIAL_CODEX_NPM_VERSION) {
61564
+ const p2 = process.platform;
61565
+ const a = process.arch;
61566
+ if (a !== "arm64" && a !== "x64") {
61567
+ throw new Error(`unsupported arch for Codex: ${a}`);
61568
+ }
61569
+ if (p2 === "darwin") return `${baseVersion}-darwin-${a}`;
61570
+ if (p2 === "linux") return `${baseVersion}-linux-${a}`;
61571
+ if (p2 === "win32") return `${baseVersion}-win32-${a}`;
61572
+ throw new Error(`unsupported platform for Codex: ${p2}`);
61573
+ }
61574
+ function managedPackagePins(id) {
61575
+ if (id === "claude") {
61576
+ const ver = process.env.SUPERONE_CLAUDE_SDK_VERSION?.trim() || OFFICIAL_CLAUDE_SDK_VERSION;
61577
+ const platform2 = claudePlatformPackageName();
61578
+ return {
61579
+ runtimeVersion: ver,
61580
+ packages: [{ name: platform2, version: ver, nodeModulesDir: platform2 }]
61581
+ };
61582
+ }
61583
+ const base = process.env.SUPERONE_CODEX_NPM_VERSION?.trim() || OFFICIAL_CODEX_NPM_VERSION;
61584
+ return {
61585
+ runtimeVersion: base,
61586
+ packages: [
61587
+ {
61588
+ name: OFFICIAL_CODEX_PACKAGE,
61589
+ version: codexPlatformVersion(base),
61590
+ nodeModulesDir: OFFICIAL_CODEX_PACKAGE
61591
+ }
61592
+ ]
61593
+ };
61594
+ }
61595
+ function resolveHarnessManifestChannel(explicit, releaseVersion) {
61596
+ if (explicit) return explicit;
61597
+ const fromEnv = process.env.SUPERONE_HARNESS_CHANNEL?.trim();
61598
+ if (fromEnv && isHarnessManifestChannel(fromEnv)) return fromEnv;
61599
+ const ver = releaseVersion?.trim() || process.env.SUPERONE_CLI_VERSION?.trim() || process.env.npm_package_version?.trim() || "";
61600
+ if (ver) {
61601
+ const ch = channelFromVersion(ver);
61602
+ if (isHarnessManifestChannel(ch)) return ch;
61603
+ }
61604
+ return "alpha";
61605
+ }
61606
+ function harnessDownloadDir(homeRoot) {
61607
+ return join22(homeRoot, ".download");
61608
+ }
61609
+ function harnessArtifactDownloadKey(opts) {
61610
+ const digest = opts.digestSha256?.trim().toLowerCase();
61611
+ if (digest && /^[a-f0-9]{16,}$/.test(digest)) {
61612
+ return `${opts.harnessId}-${digest.slice(0, 24)}`;
61613
+ }
61614
+ const safe = `${opts.npmName}@${opts.npmVersion}`.replace(/^@/, "").replace(/[^A-Za-z0-9._+-]+/g, "_").slice(0, 120);
61615
+ return `${opts.harnessId}-${safe}`;
61616
+ }
61617
+ function harnessPartialPath(homeRoot, key) {
61618
+ if (!key || key.includes("..") || key.includes("/") || key.includes("\\")) {
61619
+ throw new Error(`unsafe download key: ${key}`);
61620
+ }
61621
+ return join22(harnessDownloadDir(homeRoot), `${key}.partial`);
61622
+ }
61623
+ function extractTgzWithSystemTar(tgzPath, destDir) {
61624
+ return new Promise((resolve13, reject) => {
61625
+ mkdirSync15(destDir, { recursive: true });
61626
+ const child = spawn4("tar", ["-xzf", tgzPath, "-C", destDir], {
61627
+ stdio: ["ignore", "ignore", "pipe"]
61628
+ });
61629
+ let stderr = "";
61630
+ child.stderr?.on("data", (c) => {
61631
+ stderr += c.toString();
61632
+ });
61633
+ child.on("error", (err) => reject(new Error(`tar spawn failed: ${err.message}`)));
61634
+ child.on("close", (code) => {
61635
+ if (code === 0) resolve13();
61636
+ else reject(new Error(`tar extract failed (exit ${code}): ${stderr.slice(-500)}`));
61637
+ });
61638
+ });
61639
+ }
61640
+ function installPackageDir(packageDir, prefix, nodeModulesRel) {
61641
+ const dest = join22(prefix, "lib", "node_modules", ...nodeModulesRel.split("/"));
61642
+ const parent = dirname13(dest);
61643
+ mkdirSync15(parent, { recursive: true });
61644
+ if (existsSync27(dest)) {
61645
+ rmSync6(dest, { recursive: true, force: true });
61646
+ }
61647
+ const staging = join22(
61648
+ parent,
61649
+ `.staging-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
61650
+ );
61651
+ try {
61652
+ renameSync4(packageDir, staging);
61653
+ renameSync4(staging, dest);
61654
+ } catch (err) {
61655
+ if (existsSync27(staging)) rmSync6(staging, { recursive: true, force: true });
61656
+ throw err instanceof Error ? err : new Error(`failed to place package into ${dest}: ${String(err)}`);
61657
+ }
61658
+ return dest;
61659
+ }
61660
+ function codexTargetTriple() {
61661
+ const key = `${process.platform}-${process.arch}`;
61662
+ const map2 = {
61663
+ "darwin-arm64": "aarch64-apple-darwin",
61664
+ "darwin-x64": "x86_64-apple-darwin",
61665
+ "linux-arm64": "aarch64-unknown-linux-musl",
61666
+ "linux-x64": "x86_64-unknown-linux-musl",
61667
+ "win32-arm64": "aarch64-pc-windows-msvc",
61668
+ "win32-x64": "x86_64-pc-windows-msvc"
61669
+ };
61670
+ return map2[key] ?? null;
61671
+ }
61672
+ function resolveCodexNativeUnderPrefix(prefix) {
61673
+ const triple = codexTargetTriple();
61674
+ if (!triple) return null;
61675
+ const binaryName = process.platform === "win32" ? "codex.exe" : "codex";
61676
+ const candidates = [
61677
+ join22(prefix, "lib", "node_modules", "@openai", "codex", "vendor", triple, "bin", binaryName),
61678
+ join22(
61679
+ prefix,
61680
+ "lib",
61681
+ "node_modules",
61682
+ "@openai",
61683
+ `codex-${process.platform}-${process.arch}`,
61684
+ "vendor",
61685
+ triple,
61686
+ "bin",
61687
+ binaryName
61688
+ )
61689
+ ];
61690
+ for (const c of candidates) {
61691
+ if (existsSync27(c) && statSync9(c).isFile()) return c;
61692
+ }
61693
+ return null;
61694
+ }
61695
+ function resolveManagedTarballBinary(id, installRoot) {
61696
+ if (id === "claude") return resolveOfficialInstallBinaryInRoot("claude", installRoot);
61697
+ return resolveCodexNativeUnderPrefix(installRoot) ?? resolveOfficialInstallBinaryInRoot("codex", installRoot);
61698
+ }
61699
+ function readRuntimeVersionFromRoot(id, installRoot) {
61700
+ try {
61701
+ const metaPath = join22(installRoot, "install-meta.json");
61702
+ if (existsSync27(metaPath)) {
61703
+ const raw = JSON.parse(readFileSync16(metaPath, "utf8"));
61704
+ if (raw.runtimeVersion) return raw.runtimeVersion;
61705
+ }
61706
+ } catch {
61707
+ }
61708
+ try {
61709
+ const pkgPath = id === "claude" ? null : join22(installRoot, "lib", "node_modules", "@openai", "codex", "package.json");
61710
+ if (pkgPath && existsSync27(pkgPath)) {
61711
+ const raw = JSON.parse(readFileSync16(pkgPath, "utf8"));
61712
+ const v2 = raw.version?.trim();
61713
+ if (!v2) return null;
61714
+ return v2.replace(/-(darwin|linux|win32)-(arm64|x64)$/, "");
61715
+ }
61716
+ } catch {
61717
+ }
61718
+ return null;
61719
+ }
61720
+ async function resolveArtifactPin(id, channel, opts, fetchJson) {
61721
+ if (opts.artifactPin !== void 0) return opts.artifactPin;
61722
+ if (opts.npmOnly) return null;
61723
+ try {
61724
+ const manifest = await fetchHarnessChannelManifest({
61725
+ channel,
61726
+ baseUrl: opts.cdnBase,
61727
+ fetchJson
61728
+ });
61729
+ return selectHarnessArtifact(manifest, id, currentHostPlatform(), currentHostArch());
61730
+ } catch (err) {
61731
+ opts.log?.warn?.(
61732
+ `[harness] channel manifest ${channel} unavailable (${err instanceof Error ? err.message : String(err)}); falling back to npm`
61733
+ );
61734
+ return null;
61735
+ }
61736
+ }
61737
+ function createManagedTarballInstaller(opts = {}) {
61738
+ const httpFetch = opts.httpFetch ?? ((input, init) => globalThis.fetch(input, init));
61739
+ const fetchJson = opts.fetchJson ?? createFetchJson(httpFetch);
61740
+ const log2 = opts.log;
61741
+ const downloadToFile = opts.downloadToFile ?? createResumableDownloadToFile(httpFetch, log2);
61742
+ const extractTgz = opts.extractTgz ?? extractTgzWithSystemTar;
61743
+ return {
61744
+ async install(id, home, onProgress) {
61745
+ const prefix = managedHarnessPrefix(home.root, id);
61746
+ mkdirSync15(prefix, { recursive: true });
61747
+ const pins = managedPackagePins(id);
61748
+ const versionDir = managedVersionDir(prefix, pins.runtimeVersion);
61749
+ const previousPointer = readCurrentPointer(prefix)?.runtimeVersion ?? null;
61750
+ const versionBin = resolveManagedTarballBinary(id, versionDir);
61751
+ const versionMeta = readRuntimeVersionFromRoot(id, versionDir);
61752
+ if (versionBin && versionMeta === pins.runtimeVersion) {
61753
+ writeCurrentPointer(prefix, pins.runtimeVersion, { installRoot: versionDir });
61754
+ return {
61755
+ command: versionBin,
61756
+ runtimeVersion: pins.runtimeVersion,
61757
+ source: "npm-tarball",
61758
+ detail: {
61759
+ installPrefix: versionDir,
61760
+ reused: "1",
61761
+ runtimeVersion: pins.runtimeVersion
61762
+ }
61763
+ };
61764
+ }
61765
+ const activeRoot = resolveActiveInstallRoot(prefix);
61766
+ if (activeRoot) {
61767
+ const activeBin = resolveManagedTarballBinary(id, activeRoot);
61768
+ const activeVer = readRuntimeVersionFromRoot(id, activeRoot);
61769
+ if (activeBin && activeVer === pins.runtimeVersion) {
61770
+ writeCurrentPointer(prefix, pins.runtimeVersion, { installRoot: activeRoot });
61771
+ return {
61772
+ command: activeBin,
61773
+ runtimeVersion: activeVer,
61774
+ source: "npm-tarball",
61775
+ detail: { installPrefix: activeRoot, reused: "1" }
61776
+ };
61777
+ }
61778
+ if (activeBin && activeVer && activeVer !== pins.runtimeVersion) {
61779
+ log2?.info?.(
61780
+ `[harness] ${id} pin mismatch: installed=${activeVer} pin=${pins.runtimeVersion} \u2014 installing side-by-side`
61781
+ );
61782
+ }
61783
+ }
61784
+ const channel = resolveHarnessManifestChannel(opts.channel, opts.releaseVersion);
61785
+ const artifactPin = await resolveArtifactPin(id, channel, opts, fetchJson);
61786
+ let packageSpec = "";
61787
+ let source = "npm-tarball";
61788
+ mkdirSync15(versionDir, { recursive: true });
61789
+ for (const pkg of pins.packages) {
61790
+ const npmName = artifactPin?.npmName ?? pkg.name;
61791
+ const npmVersion = artifactPin?.npmVersion ?? pkg.version;
61792
+ packageSpec = `${npmName}@${npmVersion}`;
61793
+ const downloadKey = harnessArtifactDownloadKey({
61794
+ harnessId: id,
61795
+ digestSha256: artifactPin?.digestSha256,
61796
+ npmName,
61797
+ npmVersion
61798
+ });
61799
+ const partialPath = harnessPartialPath(home.root, downloadKey);
61800
+ const work = mkdtempSync2(join22(tmpdir2(), `superone-harness-${id}-`));
61801
+ try {
61802
+ const { from } = await fetchTarballWithFallback({
61803
+ destPath: partialPath,
61804
+ npmName,
61805
+ npmVersion,
61806
+ pin: artifactPin,
61807
+ npmOnly: opts.npmOnly === true,
61808
+ fetchJson,
61809
+ downloadToFile,
61810
+ onProgress,
61811
+ log: log2
61812
+ });
61813
+ source = from;
61814
+ const extractRoot = join22(work, "out");
61815
+ await extractTgz(partialPath, extractRoot);
61816
+ const packageDir = join22(extractRoot, "package");
61817
+ if (!existsSync27(packageDir) || !statSync9(packageDir).isDirectory()) {
61818
+ throw new Error(`tarball for ${packageSpec} has no package/ directory`);
61819
+ }
61820
+ installPackageDir(packageDir, versionDir, npmName);
61821
+ try {
61822
+ if (existsSync27(partialPath)) rmSync6(partialPath, { force: true });
61823
+ } catch {
61824
+ }
61825
+ } finally {
61826
+ rmSync6(work, { recursive: true, force: true });
61827
+ }
61828
+ }
61829
+ writeFileSync13(
61830
+ join22(versionDir, "install-meta.json"),
61831
+ JSON.stringify(
61832
+ {
61833
+ harnessId: id,
61834
+ runtimeVersion: pins.runtimeVersion,
61835
+ packageSpec,
61836
+ source,
61837
+ channel,
61838
+ digestSha256: artifactPin?.digestSha256 ?? null,
61839
+ installedAt: Date.now()
61840
+ },
61841
+ null,
61842
+ 2
61843
+ )
61844
+ );
61845
+ const command = resolveManagedTarballBinary(id, versionDir);
61846
+ if (!command) {
61847
+ throw new Error(
61848
+ `tarball install of ${id} succeeded but binary was not found under ${versionDir}`
61849
+ );
61850
+ }
61851
+ writeCurrentPointer(prefix, pins.runtimeVersion, { installRoot: versionDir });
61852
+ const keep = [pins.runtimeVersion, previousPointer].filter(
61853
+ (v2) => typeof v2 === "string" && v2.length > 0
61854
+ );
61855
+ pruneManagedVersions(prefix, keep);
61856
+ log2?.info?.(`[harness] installed ${id}@${pins.runtimeVersion} from ${source} \u2192 ${command}`);
61857
+ return {
61858
+ command,
61859
+ runtimeVersion: pins.runtimeVersion,
61860
+ source,
61861
+ detail: {
61862
+ packageSpec,
61863
+ installPrefix: versionDir,
61864
+ channel,
61865
+ digestSha256: artifactPin?.digestSha256,
61866
+ runtimeVersion: pins.runtimeVersion
61867
+ }
61868
+ };
61869
+ }
61870
+ };
61871
+ }
61872
+
61873
+ // ../../packages/runtime/src/harness/runtime-ready.ts
61874
+ var RUNTIME_SPECS = {
61875
+ claude: {
61876
+ catalogId: "claude",
61877
+ okReason: "claude binary or Agent SDK available",
61878
+ failReason: "claude runtime unavailable: install managed artifact, Agent SDK platform package, or SUPERONE_CLAUDE_BINARY"
61879
+ },
61880
+ codex: {
61881
+ catalogId: "codex",
61882
+ okReason: "codex binary available",
61883
+ failReason: "codex runtime unavailable: enable with --artifact or set SUPERONE_CODEX_BINARY"
61884
+ },
61885
+ acp: {
61886
+ catalogId: "acp-grok",
61887
+ okReason: "acp binary available",
61888
+ failReason: "acp-grok runtime unavailable: enable with --command or set SUPERONE_ACP_BINARY"
61889
+ },
61890
+ opencode: {
61891
+ catalogId: "opencode",
61892
+ okReason: "opencode binary available",
61893
+ // server-url only configs still need a client binary for the node runner today
61894
+ failReason: "opencode runtime unavailable: enable with --command or set SUPERONE_OPENCODE_BINARY"
61895
+ }
61896
+ };
61897
+ function assertSessionHarnessRuntimeReady(sessionHarnessId, harnesses, resolver) {
61898
+ const spec = RUNTIME_SPECS[sessionHarnessId];
61899
+ if (!spec) return { ok: false, reason: `unknown harness: ${sessionHarnessId}` };
61900
+ if (resolver.resolveBinary(spec.catalogId, harnesses) || resolver.isRunnableWithoutCatalog(spec.catalogId)) {
61901
+ return { ok: true, reason: spec.okReason };
61902
+ }
61903
+ return { ok: false, reason: spec.failReason };
61904
+ }
61905
+ function probeHarnessReadiness(harnesses, id, deps) {
61906
+ const status = harnesses.get(id);
61907
+ const previousState = status.state;
61908
+ const issues = [];
61909
+ const sessionWire = id === "acp-grok" ? "acp" : id;
61910
+ const runtime = assertSessionHarnessRuntimeReady(sessionWire, harnesses, deps.resolver);
61911
+ if (!runtime.ok) {
61912
+ issues.push(runtime.reason);
61913
+ if (status.enabled && (status.state === "ready" || status.state === "needs_auth")) {
61914
+ harnesses.update(id, {
61915
+ state: "error",
61916
+ diagnosticCode: "probe_failed",
61917
+ lastProbedAt: Date.now()
61918
+ });
61919
+ const next = harnesses.get(id);
61920
+ return {
61921
+ id,
61922
+ previousState,
61923
+ state: next.state,
61924
+ transitioned: next.state !== previousState,
61925
+ ok: false,
61926
+ issues,
61927
+ reason: runtime.reason
61928
+ };
61929
+ }
61930
+ harnesses.update(id, { lastProbedAt: Date.now() });
61931
+ return {
61932
+ id,
61933
+ previousState,
61934
+ state: status.state,
61935
+ transitioned: false,
61936
+ ok: false,
61937
+ issues,
61938
+ reason: runtime.reason
61939
+ };
61940
+ }
61941
+ if (status.enabled && status.state === "needs_auth") {
61942
+ const authOk = isAuthSatisfied(id, deps);
61943
+ if (!authOk.ok) {
61944
+ issues.push(authOk.reason);
61945
+ harnesses.update(id, {
61946
+ lastProbedAt: Date.now(),
61947
+ diagnosticCode: "needs_auth"
61948
+ });
61949
+ return {
61950
+ id,
61951
+ previousState,
61952
+ state: "needs_auth",
61953
+ transitioned: false,
61954
+ ok: false,
61955
+ issues,
61956
+ reason: authOk.reason
61957
+ };
61958
+ }
61959
+ harnesses.update(id, {
61960
+ state: "ready",
61961
+ diagnosticCode: null,
61962
+ lastProbedAt: Date.now()
61963
+ });
61964
+ return {
61965
+ id,
61966
+ previousState,
61967
+ state: "ready",
61968
+ transitioned: true,
61969
+ ok: true,
61970
+ issues: [],
61971
+ reason: `promoted needs_auth \u2192 ready (${authOk.reason})`
61972
+ };
61973
+ }
61974
+ if (status.enabled && status.state === "ready") {
61975
+ harnesses.update(id, { lastProbedAt: Date.now(), diagnosticCode: null });
61976
+ return {
61977
+ id,
61978
+ previousState,
61979
+ state: "ready",
61980
+ transitioned: false,
61981
+ ok: true,
61982
+ issues: [],
61983
+ reason: runtime.reason
61984
+ };
61985
+ }
61986
+ harnesses.update(id, { lastProbedAt: Date.now() });
61987
+ return {
61988
+ id,
61989
+ previousState,
61990
+ state: status.state,
61991
+ transitioned: false,
61992
+ ok: status.enabled && status.state === "ready",
61993
+ issues: status.enabled ? [`state_${status.state}`] : ["disabled"],
61994
+ reason: status.enabled ? `state is ${status.state}` : "harness disabled"
61995
+ };
61996
+ }
61997
+ function isAuthSatisfied(id, deps) {
61998
+ if (process.env.SUPERONE_HARNESS_MARK_READY === "1") {
61999
+ return { ok: true, reason: "SUPERONE_HARNESS_MARK_READY" };
62000
+ }
62001
+ const defRequiresAuth = id === "claude" || id === "codex";
62002
+ if (!defRequiresAuth) {
62003
+ return { ok: true, reason: "external harness does not require SuperOne provider auth" };
62004
+ }
62005
+ const fromProvider = deps.auth?.hasCredentialFor(id);
62006
+ if (fromProvider?.ok) return fromProvider;
62007
+ if (deps.resolver.isRunnableWithoutCatalog(id)) {
62008
+ return {
62009
+ ok: true,
62010
+ reason: id === "claude" ? "claude Agent SDK / host credentials assumed" : "SUPERONE_CODEX_BINARY host credentials assumed"
62011
+ };
62012
+ }
62013
+ return {
62014
+ ok: false,
62015
+ reason: "needs_auth: push provider credentials, complete host CLI login, or set SUPERONE_HARNESS_MARK_READY=1 for lab"
62016
+ };
62017
+ }
62018
+
62019
+ // ../../packages/runtime/src/harness/enable.ts
62020
+ init_environment();
62021
+ import { accessSync, constants, existsSync as existsSync28, realpathSync as realpathSync3, statSync as statSync10 } from "node:fs";
62022
+ import { isAbsolute as isAbsolute2, resolve as resolve6 } from "node:path";
62023
+ async function enableHarness(manager, input, deps) {
60652
62024
  const id = input.harnessId;
60653
62025
  if (!isNodeHarnessId(id)) throw new Error(`unknown harnessId: ${id}`);
60654
62026
  let status;
60655
62027
  if (id === "claude" || id === "codex") {
60656
- status = await enableManaged(manager, id, input.artifactPath);
62028
+ status = await enableManaged(manager, id, input.artifactPath, deps, "enable", {
62029
+ forcePin: input.forcePin === true
62030
+ });
60657
62031
  } else if (id === "opencode") {
60658
62032
  status = enableOpencode(manager, {
60659
62033
  command: input.command,
@@ -60666,7 +62040,7 @@ async function enableHarness(manager, input, providers) {
60666
62040
  });
60667
62041
  }
60668
62042
  try {
60669
- probeHarnessReadiness(manager, id, providers ?? null);
62043
+ probeHarnessReadiness(manager, id, { resolver: deps.resolver, auth: deps.auth });
60670
62044
  } catch {
60671
62045
  }
60672
62046
  return manager.get(id);
@@ -60675,9 +62049,10 @@ function disableHarness(manager, harnessId) {
60675
62049
  if (!isNodeHarnessId(harnessId)) throw new Error(`unknown harnessId: ${harnessId}`);
60676
62050
  return manager.disable(harnessId);
60677
62051
  }
60678
- async function enableManaged(manager, id, artifact, mode = "enable") {
60679
- const nodeHome = resolveNodeHome(void 0);
62052
+ async function enableManaged(manager, id, artifact, deps, mode = "enable", opts) {
62053
+ const nodeHome = deps.home.root;
60680
62054
  const def = getNodeHarnessDefinition(id);
62055
+ const forcePin = opts?.forcePin === true;
60681
62056
  if (artifact) {
60682
62057
  const manifest = loadHarnessReleaseManifest(nodeHome);
60683
62058
  if (!manifest) {
@@ -60714,25 +62089,26 @@ async function enableManaged(manager, id, artifact, mode = "enable") {
60714
62089
  })
60715
62090
  });
60716
62091
  }
60717
- const auto = resolveManagedAutoRuntime(id);
60718
- if (auto) {
60719
- return manager.update(id, {
60720
- enabled: true,
60721
- state: def.requiresAuth ? "needs_auth" : "ready",
60722
- command: auto.command,
60723
- runtimeVersion: auto.runtimeVersion ?? null,
60724
- diagnosticCode: def.requiresAuth ? "needs_auth" : null,
60725
- diagnosticFields: def.requiresAuth ? { command: auto.command, runtimeVersion: auto.runtimeVersion } : null,
60726
- lastProbedAt: Date.now(),
60727
- configJson: JSON.stringify({
62092
+ if (!forcePin) {
62093
+ const auto = deps.resolver.autoRuntime(id);
62094
+ if (auto) {
62095
+ return manager.update(id, {
62096
+ enabled: true,
62097
+ state: def.requiresAuth ? "needs_auth" : "ready",
60728
62098
  command: auto.command,
60729
- source: auto.source
60730
- })
60731
- });
62099
+ runtimeVersion: auto.runtimeVersion,
62100
+ diagnosticCode: def.requiresAuth ? "needs_auth" : null,
62101
+ diagnosticFields: def.requiresAuth ? { command: auto.command, runtimeVersion: auto.runtimeVersion } : null,
62102
+ lastProbedAt: Date.now(),
62103
+ configJson: JSON.stringify({
62104
+ command: auto.command,
62105
+ source: auto.source
62106
+ })
62107
+ });
62108
+ }
60732
62109
  }
60733
62110
  try {
60734
- const { installManagedFromOfficialNpm: installManagedFromOfficialNpm2 } = await Promise.resolve().then(() => (init_managed_harness_official(), managed_harness_official_exports));
60735
- const official = await installManagedFromOfficialNpm2({ nodeHome, harnessId: id });
62111
+ const official = await deps.installer.install(id, deps.home);
60736
62112
  return manager.update(id, {
60737
62113
  enabled: true,
60738
62114
  state: def.requiresAuth ? "needs_auth" : "ready",
@@ -60744,8 +62120,7 @@ async function enableManaged(manager, id, artifact, mode = "enable") {
60744
62120
  configJson: JSON.stringify({
60745
62121
  command: official.command,
60746
62122
  source: official.source,
60747
- packageSpec: official.packageSpec,
60748
- installPrefix: official.installPrefix,
62123
+ ...official.detail ?? {},
60749
62124
  runtimeVersion: official.runtimeVersion
60750
62125
  })
60751
62126
  });
@@ -60757,24 +62132,8 @@ async function enableManaged(manager, id, artifact, mode = "enable") {
60757
62132
  `official install failed (${detail}); ${describeExpectedArtifact(id, manifest)} as offline fallback`
60758
62133
  );
60759
62134
  }
60760
- throw new Error(
60761
- `official install of ${id} failed: ${detail}. Ensure npm is on PATH and the host can reach registry.npmjs.org.`
60762
- );
60763
- }
60764
- }
60765
- function resolveManagedAutoRuntime(id) {
60766
- if (id === "claude") {
60767
- const sdk = resolveSdkClaudeBinary();
60768
- if (sdk && existsSync25(sdk)) {
60769
- return { command: sdk, source: "agent-sdk-optional" };
60770
- }
60771
- return null;
62135
+ throw new Error(`official install of ${id} failed: ${detail}`);
60772
62136
  }
60773
- const fromEnv = resolveCodexBinaryPath({});
60774
- if (fromEnv) return { command: fromEnv, source: "env-or-catalog" };
60775
- const fromPath = resolveExternalCommand(void 0, ["codex"]);
60776
- if (fromPath) return { command: fromPath, source: "path" };
60777
- return null;
60778
62137
  }
60779
62138
  function enableOpencode(manager, opts) {
60780
62139
  if (opts.serverUrl) {
@@ -60840,7 +62199,7 @@ function requireRegularReadableFile(path) {
60840
62199
  throw new Error(`path must be absolute: ${path}`);
60841
62200
  }
60842
62201
  const abs = resolve6(path);
60843
- if (!existsSync25(abs) || !statSync7(abs).isFile()) {
62202
+ if (!existsSync28(abs) || !statSync10(abs).isFile()) {
60844
62203
  throw new Error(`not a regular file: ${abs}`);
60845
62204
  }
60846
62205
  accessSync(abs, constants.R_OK);
@@ -60849,9 +62208,9 @@ function requireRegularReadableFile(path) {
60849
62208
  function resolveExternalCommand(explicit, searchNames) {
60850
62209
  if (explicit) {
60851
62210
  const abs = isAbsolute2(explicit) ? explicit : resolve6(explicit);
60852
- if (!existsSync25(abs)) return null;
62211
+ if (!existsSync28(abs)) return null;
60853
62212
  try {
60854
- if (!statSync7(abs).isFile()) return null;
62213
+ if (!statSync10(abs).isFile()) return null;
60855
62214
  accessSync(abs, constants.X_OK);
60856
62215
  } catch {
60857
62216
  return null;
@@ -60864,9 +62223,9 @@ function resolveExternalCommand(explicit, searchNames) {
60864
62223
  for (const dir of dirs) {
60865
62224
  if (!dir) continue;
60866
62225
  const candidate = resolve6(dir, name);
60867
- if (!existsSync25(candidate)) continue;
62226
+ if (!existsSync28(candidate)) continue;
60868
62227
  try {
60869
- if (!statSync7(candidate).isFile()) continue;
62228
+ if (!statSync10(candidate).isFile()) continue;
60870
62229
  accessSync(candidate, constants.X_OK);
60871
62230
  return realpathSync3(candidate);
60872
62231
  } catch {
@@ -60918,10 +62277,105 @@ function looksLikeSecretArg(value) {
60918
62277
  return false;
60919
62278
  }
60920
62279
 
62280
+ // src/session/harness-host.ts
62281
+ import { existsSync as existsSync29 } from "node:fs";
62282
+ init_src2();
62283
+ init_claude_turn_runner();
62284
+ init_codex_turn_runner();
62285
+ init_resolve_service();
62286
+ setHarnessReleaseVersionProvider(resolveCliReleaseVersion);
62287
+ function envBinaryExists(envName) {
62288
+ const v2 = process.env[envName]?.trim();
62289
+ return Boolean(v2 && existsSync29(v2));
62290
+ }
62291
+ var cliHarnessResolver = {
62292
+ resolveBinary(id, harnesses) {
62293
+ if (id === "claude") return resolveClaudeBinaryPath({ harnesses });
62294
+ if (id === "codex") return resolveCodexBinaryPath({ harnesses });
62295
+ const command = harnesses.get(id).command;
62296
+ return command && existsSync29(command) ? command : null;
62297
+ },
62298
+ isRunnableWithoutCatalog(id) {
62299
+ if (id === "claude") return isClaudeRuntimeRunnable();
62300
+ if (id === "codex") return isCodexBinaryOverrideRunnable();
62301
+ if (id === "acp-grok") return envBinaryExists("SUPERONE_ACP_BINARY");
62302
+ return envBinaryExists("SUPERONE_OPENCODE_BINARY");
62303
+ },
62304
+ autoRuntime(id) {
62305
+ if (id === "claude") {
62306
+ const sdk = resolveSdkClaudeBinary();
62307
+ if (sdk && existsSync29(sdk)) return { command: sdk, source: "agent-sdk-optional" };
62308
+ return null;
62309
+ }
62310
+ const fromEnv = resolveCodexBinaryPath({});
62311
+ if (fromEnv) return { command: fromEnv, source: "env-or-catalog" };
62312
+ const fromPath = resolveExternalCommand(void 0, ["codex"]);
62313
+ if (fromPath) return { command: fromPath, source: "path" };
62314
+ return null;
62315
+ }
62316
+ };
62317
+ function cliHarnessAuthProbe(providers) {
62318
+ if (!providers) return null;
62319
+ return {
62320
+ hasCredentialFor(id) {
62321
+ const consumer = consumerForHarness(id);
62322
+ if (!consumer) return null;
62323
+ const binding = providers.listBindings().find((b2) => b2.consumer === consumer);
62324
+ if (binding?.credentialId) {
62325
+ const cred = providers.listCredentials().find((c) => c.id === binding.credentialId);
62326
+ if (cred) return { ok: true, reason: `provider binding ${consumer}` };
62327
+ }
62328
+ if (providers.listCredentials().length > 0) {
62329
+ return { ok: true, reason: "node has provider credentials" };
62330
+ }
62331
+ return null;
62332
+ }
62333
+ };
62334
+ }
62335
+ function cliHarnessDeps(providers) {
62336
+ const releaseVersion = resolveCliReleaseVersion();
62337
+ return {
62338
+ // Shared with desktop: ~/.superone/harness (or SUPERONE_HARNESS_HOME).
62339
+ home: { root: resolveHarnessHomeRoot() },
62340
+ releaseVersion,
62341
+ resolver: cliHarnessResolver,
62342
+ // Same R2 → npm tarball path as desktop (createManagedTarballInstaller).
62343
+ installer: createManagedTarballInstaller({
62344
+ releaseVersion,
62345
+ log: {
62346
+ info: (m2) => process.stderr.write(`${m2}
62347
+ `),
62348
+ warn: (m2) => process.stderr.write(`${m2}
62349
+ `)
62350
+ }
62351
+ }),
62352
+ auth: cliHarnessAuthProbe(providers)
62353
+ };
62354
+ }
62355
+
62356
+ // src/session/harness-runtime-ready.ts
62357
+ function assertSessionHarnessRuntimeReady2(sessionHarnessId, harnesses) {
62358
+ return assertSessionHarnessRuntimeReady(sessionHarnessId, harnesses, cliHarnessResolver);
62359
+ }
62360
+ function probeHarnessReadiness2(harnesses, id, providers) {
62361
+ return probeHarnessReadiness(harnesses, id, {
62362
+ resolver: cliHarnessResolver,
62363
+ auth: cliHarnessAuthProbe(providers)
62364
+ });
62365
+ }
62366
+
62367
+ // src/session/harness-enable.ts
62368
+ function enableHarness2(manager, input, providers) {
62369
+ return enableHarness(manager, input, cliHarnessDeps(providers));
62370
+ }
62371
+ function enableManaged2(manager, id, artifact, mode = "enable") {
62372
+ return enableManaged(manager, id, artifact, cliHarnessDeps(), mode);
62373
+ }
62374
+
60921
62375
  // ../../packages/shared/src/git-clone.ts
60922
62376
  import { execFile as execFile2 } from "node:child_process";
60923
- import { existsSync as existsSync26, mkdirSync as mkdirSync13 } from "node:fs";
60924
- import { isAbsolute as isAbsolute3, join as join20, resolve as resolve7 } from "node:path";
62377
+ import { existsSync as existsSync30, mkdirSync as mkdirSync16 } from "node:fs";
62378
+ import { isAbsolute as isAbsolute3, join as join23, resolve as resolve7 } from "node:path";
60925
62379
 
60926
62380
  // ../../packages/shared/src/git-remote.ts
60927
62381
  function repoNameFromGitUrl(url2) {
@@ -60986,17 +62440,17 @@ function resolveCloneDestination(input) {
60986
62440
  if (name.includes("/") || name.includes("\\") || name === "." || name === "..") {
60987
62441
  throw invalid(`invalid folder name: ${name}`);
60988
62442
  }
60989
- return { path: join20(resolve7(parent), name), name };
62443
+ return { path: join23(resolve7(parent), name), name };
60990
62444
  }
60991
62445
  async function cloneRepository(input) {
60992
62446
  const destination = resolveCloneDestination(input);
60993
- if (existsSync26(destination.path)) {
62447
+ if (existsSync30(destination.path)) {
60994
62448
  throw Object.assign(new Error(`destination already exists: ${destination.path}`), {
60995
62449
  code: "conflict"
60996
62450
  });
60997
62451
  }
60998
62452
  const parent = resolve7(input.parentPath.trim());
60999
- mkdirSync13(parent, { recursive: true });
62453
+ mkdirSync16(parent, { recursive: true });
61000
62454
  await new Promise((resolvePromise, reject) => {
61001
62455
  execFile2(
61002
62456
  "git",
@@ -61034,9 +62488,9 @@ async function cloneRepository(input) {
61034
62488
 
61035
62489
  // src/rpc/handlers.ts
61036
62490
  init_resolve_service();
61037
- import { existsSync as existsSync27, mkdirSync as mkdirSync14, readdirSync as readdirSync9, statSync as statSync8 } from "node:fs";
62491
+ import { existsSync as existsSync31, mkdirSync as mkdirSync17, readdirSync as readdirSync10, statSync as statSync11 } from "node:fs";
61038
62492
  import { join as pathJoin, resolve as pathResolve } from "node:path";
61039
- import { arch, cpus, freemem, homedir as homedir6, hostname as hostname4, platform, totalmem, uptime } from "node:os";
62493
+ import { arch, cpus, freemem, homedir as homedir7, hostname as hostname4, platform, totalmem, uptime } from "node:os";
61040
62494
 
61041
62495
  // src/rpc/resource-handlers.ts
61042
62496
  init_environment();
@@ -61950,15 +63404,38 @@ function parseAgentConfig(raw) {
61950
63404
  ...typeof c.model === "string" ? { model: c.model } : {},
61951
63405
  ...typeof c.effort === "string" ? { effort: c.effort } : {},
61952
63406
  ...typeof c.permissionMode === "string" ? { permissionMode: c.permissionMode } : {},
61953
- ...typeof c.sandboxMode === "string" ? { sandboxMode: c.sandboxMode } : {}
63407
+ ...typeof c.sandboxMode === "string" ? { sandboxMode: c.sandboxMode } : {},
63408
+ ...c.apiProviderId === null || typeof c.apiProviderId === "string" ? { apiProviderId: c.apiProviderId } : {}
61954
63409
  };
61955
63410
  }
61956
63411
  if (c.type === "codex") {
63412
+ const effort = typeof c.effort === "string" ? c.effort : typeof c.reasoningEffort === "string" ? c.reasoningEffort : void 0;
61957
63413
  return {
61958
63414
  type: "codex",
61959
63415
  ...typeof c.model === "string" ? { model: c.model } : {},
61960
- ...typeof c.reasoningEffort === "string" ? { reasoningEffort: c.reasoningEffort } : {},
61961
- ...typeof c.permissionPreset === "string" ? { permissionPreset: c.permissionPreset } : {}
63416
+ ...effort ? { effort, reasoningEffort: effort } : {},
63417
+ ...typeof c.permissionMode === "string" ? { permissionMode: c.permissionMode } : {},
63418
+ ...typeof c.permissionPreset === "string" ? { permissionPreset: c.permissionPreset } : {},
63419
+ ...c.apiProviderId === null || typeof c.apiProviderId === "string" ? { apiProviderId: c.apiProviderId } : {}
63420
+ };
63421
+ }
63422
+ if (c.type === "acp") {
63423
+ return {
63424
+ type: "acp",
63425
+ ...typeof c.acpAgentId === "string" ? { acpAgentId: c.acpAgentId } : {},
63426
+ ...typeof c.model === "string" ? { model: c.model } : {},
63427
+ ...typeof c.effort === "string" ? { effort: c.effort } : {},
63428
+ ...typeof c.permissionMode === "string" ? { permissionMode: c.permissionMode } : {},
63429
+ ...c.apiProviderId === null || typeof c.apiProviderId === "string" ? { apiProviderId: c.apiProviderId } : {}
63430
+ };
63431
+ }
63432
+ if (c.type === "opencode") {
63433
+ return {
63434
+ type: "opencode",
63435
+ ...typeof c.model === "string" ? { model: c.model } : {},
63436
+ ...typeof c.effort === "string" ? { effort: c.effort } : {},
63437
+ ...typeof c.permissionMode === "string" ? { permissionMode: c.permissionMode } : {},
63438
+ ...c.apiProviderId === null || typeof c.apiProviderId === "string" ? { apiProviderId: c.apiProviderId } : {}
61962
63439
  };
61963
63440
  }
61964
63441
  return null;
@@ -61967,6 +63444,7 @@ function parseSchedule(raw) {
61967
63444
  if (!raw || typeof raw !== "object") return null;
61968
63445
  const s2 = raw;
61969
63446
  if (s2.type !== "one-time" && s2.type !== "recurring") return null;
63447
+ const summary = typeof s2.summary === "string" ? s2.summary.trim() : "";
61970
63448
  return {
61971
63449
  type: s2.type,
61972
63450
  ...typeof s2.cron === "string" ? { cron: s2.cron } : {},
@@ -61974,7 +63452,8 @@ function parseSchedule(raw) {
61974
63452
  ...typeof s2.preset === "string" ? { preset: s2.preset } : {},
61975
63453
  ...typeof s2.timeOfDay === "string" ? { timeOfDay: s2.timeOfDay } : {},
61976
63454
  ...Array.isArray(s2.dayOfWeek) ? { dayOfWeek: s2.dayOfWeek.filter((x2) => typeof x2 === "number") } : {},
61977
- ...typeof s2.minuteOfHour === "number" ? { minuteOfHour: s2.minuteOfHour } : {}
63455
+ ...typeof s2.minuteOfHour === "number" ? { minuteOfHour: s2.minuteOfHour } : {},
63456
+ ...summary ? { summary } : {}
61978
63457
  };
61979
63458
  }
61980
63459
  function handleAutomationList(payload, ctx) {
@@ -62018,7 +63497,7 @@ function handleAutomationCreate(payload, ctx) {
62018
63497
  }
62019
63498
  const agentConfig = parseAgentConfig(p2.agentConfig);
62020
63499
  if (!agentConfig) {
62021
- return { error: { code: "invalid_argument", message: "agentConfig.type must be claude or codex" } };
63500
+ return { error: { code: "invalid_argument", message: "agentConfig.type must be claude, codex, acp, or opencode" } };
62022
63501
  }
62023
63502
  const schedule = parseSchedule(p2.schedule);
62024
63503
  if (!schedule) {
@@ -62056,7 +63535,7 @@ function handleAutomationUpdate(payload, ctx) {
62056
63535
  if (p2.agentConfig !== void 0) {
62057
63536
  const agentConfig = parseAgentConfig(p2.agentConfig);
62058
63537
  if (!agentConfig) {
62059
- return { error: { code: "invalid_argument", message: "agentConfig.type must be claude or codex" } };
63538
+ return { error: { code: "invalid_argument", message: "agentConfig.type must be claude, codex, acp, or opencode" } };
62060
63539
  }
62061
63540
  patch.agentConfig = agentConfig;
62062
63541
  }
@@ -63353,7 +64832,7 @@ function handleHarnessProbe(payload, ctx) {
63353
64832
  return { error: { code: "invalid_argument", message: `unknown harnessId: ${id}` } };
63354
64833
  }
63355
64834
  try {
63356
- const result = probeHarnessReadiness(ctx.harnesses, id, ctx.providers ?? null);
64835
+ const result = probeHarnessReadiness2(ctx.harnesses, id, ctx.providers ?? null);
63357
64836
  return { result: { ...result, status: ctx.harnesses.get(id) } };
63358
64837
  } catch (err) {
63359
64838
  return mapThrown6(err);
@@ -63369,7 +64848,7 @@ async function handleHarnessEnable(payload, ctx) {
63369
64848
  }
63370
64849
  try {
63371
64850
  const args = Array.isArray(p2.args) ? p2.args.filter((a) => typeof a === "string") : void 0;
63372
- const status = await enableHarness(
64851
+ const status = await enableHarness2(
63373
64852
  ctx.harnesses,
63374
64853
  {
63375
64854
  harnessId: id,
@@ -63659,9 +65138,9 @@ function handleProjectGet(payload, ctx) {
63659
65138
  function expandHostPath(path) {
63660
65139
  const trimmed = path.trim();
63661
65140
  if (!trimmed) return trimmed;
63662
- if (trimmed === "~") return homedir6();
65141
+ if (trimmed === "~") return homedir7();
63663
65142
  if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
63664
- return pathResolve(pathJoin(homedir6(), trimmed.slice(2)));
65143
+ return pathResolve(pathJoin(homedir7(), trimmed.slice(2)));
63665
65144
  }
63666
65145
  return pathResolve(trimmed);
63667
65146
  }
@@ -63675,8 +65154,8 @@ function handleProjectOpen(payload, ctx) {
63675
65154
  }
63676
65155
  const name = typeof p2.name === "string" ? p2.name : void 0;
63677
65156
  try {
63678
- if (p2.createIfMissing === true && !existsSync27(path)) {
63679
- mkdirSync14(path, { recursive: true });
65157
+ if (p2.createIfMissing === true && !existsSync31(path)) {
65158
+ mkdirSync17(path, { recursive: true });
63680
65159
  }
63681
65160
  return { result: ctx.projects.open(path, name) };
63682
65161
  } catch (err) {
@@ -63712,13 +65191,13 @@ function handleFsListDir(payload, ctx) {
63712
65191
  }
63713
65192
  try {
63714
65193
  const resolved = expandHostPath(raw);
63715
- if (!existsSync27(resolved)) {
65194
+ if (!existsSync31(resolved)) {
63716
65195
  return { error: { code: "not_found", message: "path not found" } };
63717
65196
  }
63718
- if (!statSync8(resolved).isDirectory()) {
65197
+ if (!statSync11(resolved).isDirectory()) {
63719
65198
  return { error: { code: "invalid_argument", message: "not a directory" } };
63720
65199
  }
63721
- const entries = readdirSync9(resolved, { withFileTypes: true }).filter((ent) => ent.isDirectory() && !ent.name.startsWith(".")).map((ent) => ({
65200
+ const entries = readdirSync10(resolved, { withFileTypes: true }).filter((ent) => ent.isDirectory() && !ent.name.startsWith(".")).map((ent) => ({
63722
65201
  name: ent.name,
63723
65202
  path: pathJoin(resolved, ent.name),
63724
65203
  type: "directory"
@@ -64345,7 +65824,7 @@ function handleSessionCreate(payload, ctx) {
64345
65824
  };
64346
65825
  }
64347
65826
  if (!ctx.simulatedHarness) {
64348
- const runtime = assertSessionHarnessRuntimeReady(harnessId, ctx.harnesses);
65827
+ const runtime = assertSessionHarnessRuntimeReady2(harnessId, ctx.harnesses);
64349
65828
  if (!runtime.ok) {
64350
65829
  return {
64351
65830
  error: {
@@ -65098,7 +66577,7 @@ async function startNodeServer(opts) {
65098
66577
  await handleHttp(req, res, opts);
65099
66578
  } catch (err) {
65100
66579
  const e = err;
65101
- const status = e.code === "unauthorized" ? 401 : e.code === "forbidden" ? 403 : e.code === "invalid_argument" ? 400 : 500;
66580
+ const status = e.code === "unauthorized" || e.code === "revoked" ? 401 : e.code === "forbidden" ? 403 : e.code === "invalid_argument" ? 400 : 500;
65102
66581
  sendJson(res, status, {
65103
66582
  error: { code: e.code ?? "internal", message: e.message ?? "internal error" }
65104
66583
  });
@@ -65378,7 +66857,7 @@ async function handleHttp(req, res, opts) {
65378
66857
  sendJson(res, 200, result);
65379
66858
  } catch (err) {
65380
66859
  const e = err;
65381
- sendJson(res, e.code === "unauthorized" ? 401 : 500, {
66860
+ sendJson(res, e.code === "unauthorized" || e.code === "revoked" ? 401 : 500, {
65382
66861
  error: { code: e.code ?? "internal", message: e.message ?? "pair failed" }
65383
66862
  });
65384
66863
  }
@@ -65408,7 +66887,7 @@ async function handleHttp(req, res, opts) {
65408
66887
  sendJson(res, 200, result);
65409
66888
  } catch (err) {
65410
66889
  const e = err;
65411
- sendJson(res, e.code === "unauthorized" ? 401 : 500, {
66890
+ sendJson(res, e.code === "unauthorized" || e.code === "revoked" ? 401 : 500, {
65412
66891
  error: { code: e.code ?? "internal", message: e.message ?? "token refresh failed" }
65413
66892
  });
65414
66893
  }
@@ -65429,7 +66908,7 @@ async function handleHttp(req, res, opts) {
65429
66908
  sendJson(res, 200, ticket);
65430
66909
  } catch (err) {
65431
66910
  const e = err;
65432
- sendJson(res, e.code === "unauthorized" ? 401 : 500, {
66911
+ sendJson(res, e.code === "unauthorized" || e.code === "revoked" ? 401 : 500, {
65433
66912
  error: { code: e.code ?? "internal", message: e.message ?? "ticket failed" }
65434
66913
  });
65435
66914
  }
@@ -65440,7 +66919,7 @@ async function handleHttp(req, res, opts) {
65440
66919
 
65441
66920
  // src/terminal/manager.ts
65442
66921
  import { createRequire as createRequire2 } from "node:module";
65443
- import { existsSync as existsSync28 } from "node:fs";
66922
+ import { existsSync as existsSync32 } from "node:fs";
65444
66923
  var nodeRequire = createRequire2(import.meta.url);
65445
66924
  var { spawn: spawn5 } = nodeRequire("node-pty");
65446
66925
  var SNAPSHOT_SOFT_LIMIT = 64 * 1024;
@@ -65455,7 +66934,7 @@ var NodeTerminalManager = class {
65455
66934
  }
65456
66935
  byId = /* @__PURE__ */ new Map();
65457
66936
  create(opts) {
65458
- if (!existsSync28(opts.cwd)) {
66937
+ if (!existsSync32(opts.cwd)) {
65459
66938
  throw Object.assign(new Error(`cwd does not exist: ${opts.cwd}`), { code: "invalid_argument" });
65460
66939
  }
65461
66940
  const terminalId = crypto.randomUUID();
@@ -65596,8 +67075,8 @@ var NodeTerminalManager = class {
65596
67075
 
65597
67076
  // src/workspace/project-registry.ts
65598
67077
  import { basename as basename2, resolve as resolve8 } from "node:path";
65599
- import { existsSync as existsSync29, realpathSync as realpathSync4, statSync as statSync9 } from "node:fs";
65600
- import { createHash as createHash5 } from "node:crypto";
67078
+ import { existsSync as existsSync33, realpathSync as realpathSync4, statSync as statSync12 } from "node:fs";
67079
+ import { createHash as createHash6 } from "node:crypto";
65601
67080
  import { execFileSync as execFileSync2 } from "node:child_process";
65602
67081
  var ProjectRegistry = class {
65603
67082
  constructor(db) {
@@ -65626,7 +67105,7 @@ var ProjectRegistry = class {
65626
67105
  }
65627
67106
  open(path, name) {
65628
67107
  let abs = resolve8(path);
65629
- if (!existsSync29(abs) || !statSync9(abs).isDirectory()) {
67108
+ if (!existsSync33(abs) || !statSync12(abs).isDirectory()) {
65630
67109
  throw Object.assign(new Error(`project path is not a directory: ${abs}`), {
65631
67110
  code: "invalid_argument"
65632
67111
  });
@@ -65641,7 +67120,7 @@ var ProjectRegistry = class {
65641
67120
  this.db.prepare(`UPDATE projects SET last_active_at = ?, name = COALESCE(?, name) WHERE project_id = ?`).run(now, name ?? null, existing.projectId);
65642
67121
  return this.get(existing.projectId);
65643
67122
  }
65644
- const projectId = createHash5("sha256").update(abs).digest("hex").slice(0, 32);
67123
+ const projectId = createHash6("sha256").update(abs).digest("hex").slice(0, 32);
65645
67124
  const repoIdentity = detectRepoIdentity(abs);
65646
67125
  this.db.prepare(
65647
67126
  `INSERT INTO projects (project_id, path, name, repo_identity, opened_at, last_active_at)
@@ -65670,7 +67149,7 @@ var ProjectRegistry = class {
65670
67149
  toSnapshot(r) {
65671
67150
  let missing = false;
65672
67151
  try {
65673
- missing = !statSync9(r.path).isDirectory();
67152
+ missing = !statSync12(r.path).isDirectory();
65674
67153
  } catch {
65675
67154
  missing = true;
65676
67155
  }
@@ -65710,21 +67189,21 @@ function detectRepoIdentity(abs) {
65710
67189
  init_fs();
65711
67190
  import {
65712
67191
  closeSync,
65713
- existsSync as existsSync30,
67192
+ existsSync as existsSync34,
65714
67193
  fstatSync,
65715
- mkdirSync as mkdirSync15,
67194
+ mkdirSync as mkdirSync18,
65716
67195
  openSync,
65717
67196
  readSync,
65718
- readdirSync as readdirSync10,
65719
- readFileSync as readFileSync15,
65720
- renameSync as renameSync3,
65721
- rmSync as rmSync3,
65722
- statSync as statSync10,
67197
+ readdirSync as readdirSync11,
67198
+ readFileSync as readFileSync17,
67199
+ renameSync as renameSync5,
67200
+ rmSync as rmSync7,
67201
+ statSync as statSync13,
65723
67202
  unlinkSync,
65724
- writeFileSync as writeFileSync10
67203
+ writeFileSync as writeFileSync14
65725
67204
  } from "node:fs";
65726
- import { dirname as dirname12, join as join21, relative as relative2 } from "node:path";
65727
- import { createHash as createHash6 } from "node:crypto";
67205
+ import { dirname as dirname14, join as join24, relative as relative2 } from "node:path";
67206
+ import { createHash as createHash7 } from "node:crypto";
65728
67207
  function normalizeRel(path) {
65729
67208
  return path.replace(/\\/g, "/").replace(/\/+$/, "") || ".";
65730
67209
  }
@@ -65769,21 +67248,21 @@ var WorkspaceFsService = class {
65769
67248
  if (!resolved.ok) {
65770
67249
  throw Object.assign(new Error(resolved.reason), { code: "invalid_argument" });
65771
67250
  }
65772
- if (!existsSync30(resolved.absolutePath)) {
67251
+ if (!existsSync34(resolved.absolutePath)) {
65773
67252
  throw Object.assign(new Error("path not found"), { code: "not_found" });
65774
67253
  }
65775
- const st = statSync10(resolved.absolutePath);
67254
+ const st = statSync13(resolved.absolutePath);
65776
67255
  if (!st.isDirectory()) {
65777
67256
  throw Object.assign(new Error("not a directory"), { code: "invalid_argument" });
65778
67257
  }
65779
67258
  this.projects.touch(projectId);
65780
- const ents = readdirSync10(resolved.absolutePath, { withFileTypes: true });
67259
+ const ents = readdirSync11(resolved.absolutePath, { withFileTypes: true });
65781
67260
  return ents.map((ent) => {
65782
- const abs = join21(resolved.absolutePath, ent.name);
67261
+ const abs = join24(resolved.absolutePath, ent.name);
65783
67262
  let size;
65784
67263
  let mtimeMs;
65785
67264
  try {
65786
- const s2 = statSync10(abs);
67265
+ const s2 = statSync13(abs);
65787
67266
  size = s2.size;
65788
67267
  mtimeMs = s2.mtimeMs;
65789
67268
  } catch {
@@ -65804,10 +67283,10 @@ var WorkspaceFsService = class {
65804
67283
  if (!resolved.ok) {
65805
67284
  throw Object.assign(new Error(resolved.reason), { code: "invalid_argument" });
65806
67285
  }
65807
- if (!existsSync30(resolved.absolutePath)) {
67286
+ if (!existsSync34(resolved.absolutePath)) {
65808
67287
  throw Object.assign(new Error("file not found"), { code: "not_found" });
65809
67288
  }
65810
- const st = statSync10(resolved.absolutePath);
67289
+ const st = statSync13(resolved.absolutePath);
65811
67290
  if (!st.isFile()) {
65812
67291
  throw Object.assign(new Error("not a file"), { code: "invalid_argument" });
65813
67292
  }
@@ -65830,7 +67309,7 @@ var WorkspaceFsService = class {
65830
67309
  const slice = Buffer.alloc(toRead);
65831
67310
  if (toRead > 0) readSync(fd, slice, 0, toRead, offset);
65832
67311
  const content = slice.toString("base64");
65833
- const hash2 = createHash6("sha256").update(slice).digest("hex");
67312
+ const hash2 = createHash7("sha256").update(slice).digest("hex");
65834
67313
  this.projects.touch(projectId);
65835
67314
  return { content, hash: hash2, encoding: "base64" };
65836
67315
  } finally {
@@ -65843,8 +67322,8 @@ var WorkspaceFsService = class {
65843
67322
  if (!resolved.ok) {
65844
67323
  throw Object.assign(new Error(resolved.reason), { code: "invalid_argument" });
65845
67324
  }
65846
- if (existsSync30(resolved.absolutePath) && expectedHash) {
65847
- const st = statSync10(resolved.absolutePath);
67325
+ if (existsSync34(resolved.absolutePath) && expectedHash) {
67326
+ const st = statSync13(resolved.absolutePath);
65848
67327
  if (st.size > MAX_READ_BYTES) {
65849
67328
  throw Object.assign(
65850
67329
  new Error(`optimistic-write target too large (${st.size} bytes; max ${MAX_READ_BYTES})`),
@@ -65856,27 +67335,27 @@ var WorkspaceFsService = class {
65856
67335
  throw Object.assign(new Error("content hash mismatch"), { code: "conflict" });
65857
67336
  }
65858
67337
  }
65859
- mkdirSync15(dirname12(resolved.absolutePath), { recursive: true });
67338
+ mkdirSync18(dirname14(resolved.absolutePath), { recursive: true });
65860
67339
  const data = typeof content === "string" ? Buffer.from(content, "utf8") : Buffer.from(content);
65861
67340
  if (data.length > MAX_READ_BYTES) {
65862
67341
  throw Object.assign(new Error("write payload too large"), { code: "invalid_argument" });
65863
67342
  }
65864
67343
  let mode = 384;
65865
- if (existsSync30(resolved.absolutePath)) {
67344
+ if (existsSync34(resolved.absolutePath)) {
65866
67345
  try {
65867
- mode = statSync10(resolved.absolutePath).mode & 511;
67346
+ mode = statSync13(resolved.absolutePath).mode & 511;
65868
67347
  } catch {
65869
67348
  }
65870
67349
  }
65871
67350
  const tmp = `${resolved.absolutePath}.tmp-${crypto.randomUUID()}`;
65872
67351
  let renamed = false;
65873
67352
  try {
65874
- writeFileSync10(tmp, data, { mode });
67353
+ writeFileSync14(tmp, data, { mode });
65875
67354
  const again = resolveProjectPath(root, relativePath);
65876
67355
  if (!again.ok || again.absolutePath !== resolved.absolutePath) {
65877
67356
  throw Object.assign(new Error("path changed during write"), { code: "failed_precondition" });
65878
67357
  }
65879
- renameSync3(tmp, resolved.absolutePath);
67358
+ renameSync5(tmp, resolved.absolutePath);
65880
67359
  renamed = true;
65881
67360
  } finally {
65882
67361
  if (!renamed) {
@@ -65886,7 +67365,7 @@ var WorkspaceFsService = class {
65886
67365
  }
65887
67366
  }
65888
67367
  }
65889
- const hash2 = createHash6("sha256").update(data).digest("hex");
67368
+ const hash2 = createHash7("sha256").update(data).digest("hex");
65890
67369
  this.projects.touch(projectId);
65891
67370
  return { hash: hash2 };
65892
67371
  }
@@ -65925,14 +67404,14 @@ var WorkspaceFsService = class {
65925
67404
  if (hits.length >= MAX_SEARCH_HITS) return;
65926
67405
  let ents;
65927
67406
  try {
65928
- ents = readdirSync10(dir, { withFileTypes: true });
67407
+ ents = readdirSync11(dir, { withFileTypes: true });
65929
67408
  } catch {
65930
67409
  return;
65931
67410
  }
65932
67411
  for (const ent of ents) {
65933
67412
  if (hits.length >= MAX_SEARCH_HITS) return;
65934
67413
  if (ent.name === ".git" || ent.name === "node_modules") continue;
65935
- const abs = join21(dir, ent.name);
67414
+ const abs = join24(dir, ent.name);
65936
67415
  const rel = relative2(root, abs).split("\\").join("/");
65937
67416
  const check2 = resolveProjectPath(root, rel);
65938
67417
  if (!check2.ok) continue;
@@ -65942,9 +67421,9 @@ var WorkspaceFsService = class {
65942
67421
  }
65943
67422
  if (!ent.isFile()) continue;
65944
67423
  try {
65945
- const st = statSync10(abs);
67424
+ const st = statSync13(abs);
65946
67425
  if (st.size > MAX_SEARCH_FILE_BYTES) continue;
65947
- const text = readFileSync15(abs, "utf8");
67426
+ const text = readFileSync17(abs, "utf8");
65948
67427
  const lines = text.split(/\r?\n/);
65949
67428
  for (let i = 0; i < lines.length; i++) {
65950
67429
  if (lines[i].includes(query)) {
@@ -66006,15 +67485,15 @@ var WorkspaceFsService = class {
66006
67485
  if (!resolved.ok) {
66007
67486
  throw Object.assign(new Error(resolved.reason), { code: "invalid_argument" });
66008
67487
  }
66009
- if (existsSync30(resolved.absolutePath)) {
66010
- const st = statSync10(resolved.absolutePath);
67488
+ if (existsSync34(resolved.absolutePath)) {
67489
+ const st = statSync13(resolved.absolutePath);
66011
67490
  if (!st.isDirectory()) {
66012
67491
  throw Object.assign(new Error("path exists and is not a directory"), { code: "conflict" });
66013
67492
  }
66014
67493
  this.projects.touch(projectId);
66015
67494
  return { path: rel };
66016
67495
  }
66017
- mkdirSync15(resolved.absolutePath, { recursive: true });
67496
+ mkdirSync18(resolved.absolutePath, { recursive: true });
66018
67497
  this.projects.touch(projectId);
66019
67498
  return { path: rel };
66020
67499
  }
@@ -66029,13 +67508,13 @@ var WorkspaceFsService = class {
66029
67508
  if (!resolved.ok) {
66030
67509
  throw Object.assign(new Error(resolved.reason), { code: "invalid_argument" });
66031
67510
  }
66032
- if (!existsSync30(resolved.absolutePath)) {
67511
+ if (!existsSync34(resolved.absolutePath)) {
66033
67512
  throw Object.assign(new Error("path not found"), { code: "not_found" });
66034
67513
  }
66035
67514
  if (resolved.absolutePath === root) {
66036
67515
  throw Object.assign(new Error("cannot delete project root"), { code: "invalid_argument" });
66037
67516
  }
66038
- rmSync3(resolved.absolutePath, { recursive: true, force: false });
67517
+ rmSync7(resolved.absolutePath, { recursive: true, force: false });
66039
67518
  this.projects.touch(projectId);
66040
67519
  return { path: rel };
66041
67520
  }
@@ -66053,20 +67532,20 @@ var WorkspaceFsService = class {
66053
67532
  if (!from.ok) {
66054
67533
  throw Object.assign(new Error(from.reason), { code: "invalid_argument" });
66055
67534
  }
66056
- if (!existsSync30(from.absolutePath)) {
67535
+ if (!existsSync34(from.absolutePath)) {
66057
67536
  throw Object.assign(new Error("source not found"), { code: "not_found" });
66058
67537
  }
66059
67538
  const to = resolveProjectPath(root, toN);
66060
67539
  if (!to.ok) {
66061
67540
  throw Object.assign(new Error(to.reason), { code: "invalid_argument" });
66062
67541
  }
66063
- if (existsSync30(to.absolutePath)) {
67542
+ if (existsSync34(to.absolutePath)) {
66064
67543
  throw Object.assign(new Error(`target already exists: ${baseNameRel(toN)}`), {
66065
67544
  code: "conflict"
66066
67545
  });
66067
67546
  }
66068
- mkdirSync15(dirname12(to.absolutePath), { recursive: true });
66069
- renameSync3(from.absolutePath, to.absolutePath);
67547
+ mkdirSync18(dirname14(to.absolutePath), { recursive: true });
67548
+ renameSync5(from.absolutePath, to.absolutePath);
66070
67549
  this.projects.touch(projectId);
66071
67550
  return { from: fromN, to: toN };
66072
67551
  }
@@ -66074,7 +67553,7 @@ var WorkspaceFsService = class {
66074
67553
  function hashFileBounded(absolutePath, size) {
66075
67554
  const fd = openSync(absolutePath, "r");
66076
67555
  try {
66077
- const hash2 = createHash6("sha256");
67556
+ const hash2 = createHash7("sha256");
66078
67557
  const buf = Buffer.alloc(Math.min(64 * 1024, Math.max(1, size)));
66079
67558
  let offset = 0;
66080
67559
  while (offset < size) {
@@ -66090,9 +67569,9 @@ function hashFileBounded(absolutePath, size) {
66090
67569
  }
66091
67570
 
66092
67571
  // src/workspace/git-service.ts
66093
- import { existsSync as existsSync31, mkdirSync as mkdirSync16, realpathSync as realpathSync5, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
66094
- import { join as join23, resolve as resolve10 } from "node:path";
66095
- import { tmpdir as tmpdir2 } from "node:os";
67572
+ import { existsSync as existsSync35, mkdirSync as mkdirSync19, realpathSync as realpathSync5, rmSync as rmSync8, writeFileSync as writeFileSync15 } from "node:fs";
67573
+ import { join as join26, resolve as resolve10 } from "node:path";
67574
+ import { tmpdir as tmpdir3 } from "node:os";
66096
67575
  import { randomUUID as randomUUID8 } from "node:crypto";
66097
67576
 
66098
67577
  // ../../packages/runtime/src/git/sanitize-ref.ts
@@ -66144,126 +67623,8 @@ function parseWorktreePorcelain(out) {
66144
67623
  }
66145
67624
 
66146
67625
  // ../../packages/runtime/src/git/run.ts
67626
+ init_spawn_env();
66147
67627
  import { execFile as execFile3, execFileSync as execFileSync3 } from "child_process";
66148
-
66149
- // ../../packages/runtime/src/spawn-env.ts
66150
- var SPAWN_PATH_MAX = 1024;
66151
- var SPAWN_NAME_MAX = 255;
66152
- var MAX_ENV_BYTES = 256 * 1024;
66153
- var ESSENTIAL_EXACT = /* @__PURE__ */ new Set([
66154
- "PATH",
66155
- "HOME",
66156
- "USER",
66157
- "LOGNAME",
66158
- "SHELL",
66159
- "TERM",
66160
- "LANG",
66161
- "LC_ALL",
66162
- "LC_CTYPE",
66163
- "DISPLAY",
66164
- "TMPDIR",
66165
- "TEMP",
66166
- "TMP",
66167
- "DYLD_FRAMEWORK_PATH",
66168
- "DYLD_LIBRARY_PATH",
66169
- "DYLD_FALLBACK_FRAMEWORK_PATH",
66170
- "DYLD_FALLBACK_LIBRARY_PATH"
66171
- ]);
66172
- var ESSENTIAL_PREFIXES = [
66173
- "LC_",
66174
- "ELECTRON_",
66175
- "ANTHROPIC_",
66176
- "CODEX_",
66177
- "NPM_CONFIG_",
66178
- "NODE_",
66179
- "SSH_",
66180
- "GIT_"
66181
- ];
66182
- function isEssentialEnv(key) {
66183
- if (ESSENTIAL_EXACT.has(key)) return true;
66184
- return ESSENTIAL_PREFIXES.some((p2) => key.startsWith(p2));
66185
- }
66186
- function sanitizePathEnv(pathEnv) {
66187
- const seen = /* @__PURE__ */ new Set();
66188
- const out = [];
66189
- let dropped = 0;
66190
- let deduped = 0;
66191
- let droppedByPathMax = 0;
66192
- let droppedByNameMax = 0;
66193
- for (const entry of pathEnv.split(":")) {
66194
- if (!entry) continue;
66195
- if (seen.has(entry)) {
66196
- deduped++;
66197
- continue;
66198
- }
66199
- seen.add(entry);
66200
- if (entry.length > SPAWN_PATH_MAX) {
66201
- dropped++;
66202
- droppedByPathMax++;
66203
- continue;
66204
- }
66205
- if (entry.split("/").some((comp) => Buffer.byteLength(comp, "utf8") > SPAWN_NAME_MAX)) {
66206
- dropped++;
66207
- droppedByNameMax++;
66208
- continue;
66209
- }
66210
- out.push(entry);
66211
- }
66212
- return { value: out.join(":"), dropped, deduped, droppedByPathMax, droppedByNameMax };
66213
- }
66214
- function serializedSize(env) {
66215
- let n = 0;
66216
- for (const key in env) {
66217
- n += Buffer.byteLength(key) + Buffer.byteLength(env[key]) + 2;
66218
- }
66219
- return n;
66220
- }
66221
- function sanitizeEnv(base = process.env, extra) {
66222
- const merged = {};
66223
- for (const key in base) {
66224
- const v2 = base[key];
66225
- if (v2 !== void 0) merged[key] = v2;
66226
- }
66227
- if (extra) {
66228
- for (const key in extra) {
66229
- const v2 = extra[key];
66230
- if (v2 !== void 0) merged[key] = v2;
66231
- }
66232
- }
66233
- if (process.platform !== "win32" && typeof merged.PATH === "string") {
66234
- const res = sanitizePathEnv(merged.PATH);
66235
- merged.PATH = res.value;
66236
- if (res.dropped > 0) {
66237
- const parts = [];
66238
- if (res.droppedByPathMax > 0)
66239
- parts.push(`${res.droppedByPathMax} \u6574\u6BB5 > PATH_MAX(${SPAWN_PATH_MAX}B)`);
66240
- if (res.droppedByNameMax > 0)
66241
- parts.push(`${res.droppedByNameMax} \u6BB5\u542B > NAME_MAX(${SPAWN_NAME_MAX}B) \u7684\u76EE\u5F55\u540D`);
66242
- console.warn(`[spawn-env] \u4E3A\u89C4\u907F spawn ENAMETOOLONG \u5DF2\u4E22\u5F03 ${res.dropped} \u4E2A\u8D85\u957F PATH \u5206\u6BB5\uFF08${parts.join("\uFF1B")}\uFF09`);
66243
- }
66244
- }
66245
- let size = serializedSize(merged);
66246
- if (size > MAX_ENV_BYTES) {
66247
- const entries = Object.entries(merged).sort((a, b2) => b2[1].length - a[1].length);
66248
- for (const [key, value] of entries) {
66249
- if (size <= MAX_ENV_BYTES) break;
66250
- if (isEssentialEnv(key)) continue;
66251
- size -= Buffer.byteLength(key) + Buffer.byteLength(value) + 2;
66252
- delete merged[key];
66253
- }
66254
- if (size > MAX_ENV_BYTES) {
66255
- console.warn(`[spawn-env] env still ${size} bytes after trimming non-essential vars (limit ${MAX_ENV_BYTES})`);
66256
- } else {
66257
- console.warn(`[spawn-env] trimmed env to ${size} bytes to avoid spawn E2BIG`);
66258
- }
66259
- }
66260
- return merged;
66261
- }
66262
- function buildSafeEnv(extra) {
66263
- return sanitizeEnv(process.env, extra);
66264
- }
66265
-
66266
- // ../../packages/runtime/src/git/run.ts
66267
67628
  var GIT_MAX_BUFFER = 64 * 1024 * 1024;
66268
67629
  var NO_OPTIONAL_LOCKS = "--no-optional-locks";
66269
67630
  function gitRunSync(folderPath, args, env) {
@@ -66286,19 +67647,19 @@ function gitRunSync(folderPath, args, env) {
66286
67647
  }
66287
67648
 
66288
67649
  // ../../packages/runtime/src/git/worktree-plan.ts
66289
- import { basename as basename3, dirname as dirname13, join as join22, resolve as resolve9, sep as sep4 } from "node:path";
66290
- import { homedir as homedir7 } from "node:os";
67650
+ import { basename as basename3, dirname as dirname15, join as join25, resolve as resolve9, sep as sep4 } from "node:path";
67651
+ import { homedir as homedir8 } from "node:os";
66291
67652
  function resolveMainDirFromCommonDir(folderPath, gitCommonDir) {
66292
67653
  const repoRoot = resolve9(folderPath, gitCommonDir.trim());
66293
- return repoRoot.endsWith(`${sep4}.git`) || repoRoot.endsWith("/.git") ? dirname13(repoRoot) : repoRoot;
67654
+ return repoRoot.endsWith(`${sep4}.git`) || repoRoot.endsWith("/.git") ? dirname15(repoRoot) : repoRoot;
66294
67655
  }
66295
67656
  function planNewWorktreePaths(input) {
66296
- const home = input.homeDir ?? homedir7();
67657
+ const home = input.homeDir ?? homedir8();
66297
67658
  const repoName = basename3(input.mainDir);
66298
67659
  const epoch = Math.floor((input.nowMs ?? Date.now()) / 1e3).toString(36);
66299
67660
  const short = input.shortHash.slice(0, 7);
66300
- const wtDir = join22(home, ".worktrees", repoName);
66301
- const wtPath = join22(wtDir, `${epoch}-${short}`);
67661
+ const wtDir = join25(home, ".worktrees", repoName);
67662
+ const wtPath = join25(wtDir, `${epoch}-${short}`);
66302
67663
  return { wtDir, wtPath };
66303
67664
  }
66304
67665
  function worktreeAddArgs(mode, wtPath, baseRef, branchName) {
@@ -66357,8 +67718,8 @@ function resolveMainWorktreeDir(folderPath) {
66357
67718
  }
66358
67719
  function samePath(a, b2) {
66359
67720
  try {
66360
- const ra = existsSync31(a) ? realpathSync5(a) : resolve10(a);
66361
- const rb = existsSync31(b2) ? realpathSync5(b2) : resolve10(b2);
67721
+ const ra = existsSync35(a) ? realpathSync5(a) : resolve10(a);
67722
+ const rb = existsSync35(b2) ? realpathSync5(b2) : resolve10(b2);
66362
67723
  return ra === rb;
66363
67724
  } catch {
66364
67725
  return resolve10(a) === resolve10(b2);
@@ -66390,7 +67751,7 @@ var WorkspaceGitService = class {
66390
67751
  * --ignored walks the whole tree of ignored paths and dominates remote latency.
66391
67752
  */
66392
67753
  statusForCwd(cwd) {
66393
- if (!existsSync31(join23(cwd, ".git")) && !isGitWorktree(cwd)) {
67754
+ if (!existsSync35(join26(cwd, ".git")) && !isGitWorktree(cwd)) {
66394
67755
  return { isRepo: false, branch: null, dirty: false, ahead: 0, behind: 0, porcelain: "" };
66395
67756
  }
66396
67757
  try {
@@ -66499,15 +67860,15 @@ var WorkspaceGitService = class {
66499
67860
  }
66500
67861
  const abs = resolve10(worktreePath);
66501
67862
  const main2 = resolve10(this.root(projectId));
66502
- if (samePath(abs, main2)) return existsSync31(abs) ? realpathSync5(abs) : abs;
67863
+ if (samePath(abs, main2)) return existsSync35(abs) ? realpathSync5(abs) : abs;
66503
67864
  const listed = this.worktrees(projectId);
66504
67865
  for (const wt of listed) {
66505
67866
  if (samePath(abs, wt.path)) {
66506
- return existsSync31(abs) ? realpathSync5(abs) : resolve10(wt.path);
67867
+ return existsSync35(abs) ? realpathSync5(abs) : resolve10(wt.path);
66507
67868
  }
66508
67869
  }
66509
67870
  try {
66510
- if (existsSync31(abs) && isGitWorktree(abs)) {
67871
+ if (existsSync35(abs) && isGitWorktree(abs)) {
66511
67872
  const commonA = resolve10(abs, git(abs, ["rev-parse", "--git-common-dir"]).trim());
66512
67873
  const commonB = resolve10(main2, git(main2, ["rev-parse", "--git-common-dir"]).trim());
66513
67874
  if (samePath(commonA, commonB)) {
@@ -66549,7 +67910,7 @@ var WorkspaceGitService = class {
66549
67910
  mainDir,
66550
67911
  shortHash: commitHash.slice(0, 7)
66551
67912
  });
66552
- if (!existsSync31(wtDir)) mkdirSync16(wtDir, { recursive: true });
67913
+ if (!existsSync35(wtDir)) mkdirSync19(wtDir, { recursive: true });
66553
67914
  try {
66554
67915
  const addArgs = worktreeAddArgs(mode, wtPath, baseBranch, safeBranchName);
66555
67916
  git(folderPath, ["worktree", ...addArgs]);
@@ -66645,11 +68006,11 @@ var WorkspaceGitService = class {
66645
68006
  const mainStatus = git(diff.mainDir, ["status", "--porcelain"]).trim();
66646
68007
  if (mainStatus) return { ok: false, reason: "main-dirty" };
66647
68008
  const patch = git(diff.worktreePath, ["diff", "--binary", diff.base, diff.tree]);
66648
- const patchFile = join23(tmpdir2(), `s1-handoff-${randomUUID8()}.patch`);
66649
- writeFileSync11(patchFile, `${patch}
68009
+ const patchFile = join26(tmpdir3(), `s1-handoff-${randomUUID8()}.patch`);
68010
+ writeFileSync15(patchFile, `${patch}
66650
68011
  `);
66651
68012
  if (git(diff.mainDir, ["status", "--porcelain"]).trim()) {
66652
- rmSync4(patchFile, { force: true });
68013
+ rmSync8(patchFile, { force: true });
66653
68014
  return { ok: false, reason: "main-dirty" };
66654
68015
  }
66655
68016
  try {
@@ -66662,7 +68023,7 @@ var WorkspaceGitService = class {
66662
68023
  }
66663
68024
  return { ok: false, reason: "conflict", error: gitErrorMessage(err) };
66664
68025
  } finally {
66665
- rmSync4(patchFile, { force: true });
68026
+ rmSync8(patchFile, { force: true });
66666
68027
  }
66667
68028
  try {
66668
68029
  git(diff.mainDir, ["reset", "--quiet"]);
@@ -66707,14 +68068,14 @@ var WorkspaceGitService = class {
66707
68068
  };
66708
68069
  }
66709
68070
  writeWorkingTree(worktreePath) {
66710
- const tmpIndex = join23(tmpdir2(), `s1-handoff-${randomUUID8()}.index`);
68071
+ const tmpIndex = join26(tmpdir3(), `s1-handoff-${randomUUID8()}.index`);
66711
68072
  const env = { GIT_INDEX_FILE: tmpIndex };
66712
68073
  try {
66713
68074
  git(worktreePath, ["read-tree", "HEAD"], env);
66714
68075
  git(worktreePath, ["add", "-A"], env);
66715
68076
  return git(worktreePath, ["write-tree"], env).trim();
66716
68077
  } finally {
66717
- rmSync4(tmpIndex, { force: true });
68078
+ rmSync8(tmpIndex, { force: true });
66718
68079
  }
66719
68080
  }
66720
68081
  repoIdentity(projectId) {
@@ -66899,245 +68260,17 @@ var SessionRuntime2 = class extends SessionRuntime {
66899
68260
  init_harness_runners();
66900
68261
  init_codex_turn_runner();
66901
68262
 
66902
- // src/session/harness-manager.ts
66903
- init_environment();
66904
- var HarnessManager = class {
66905
- constructor(db) {
66906
- this.db = db;
66907
- this.ensureRows();
66908
- this.clearSimulatedContamination();
66909
- }
66910
- /**
66911
- * When true, list/get/ready behave as if every harness is enabled+ready.
66912
- * Never persisted — only for contract/CI simulated runners.
66913
- */
66914
- simulatedOverlay = false;
66915
- /** Ensure one row per first-party definition. */
66916
- ensureRows() {
66917
- const now = Date.now();
66918
- const insert = this.db.prepare(
66919
- `INSERT OR IGNORE INTO harness_installations
66920
- (harness_id, enabled, state, runtime_version, command, config_json, secret_ref,
66921
- diagnostic_code, diagnostic_message, last_probed_at, updated_at)
66922
- VALUES (?, 0, 'disabled', NULL, NULL, NULL, NULL, NULL, NULL, NULL, ?)`
66923
- );
66924
- const tx = this.db.transaction(() => {
66925
- for (const def of NODE_HARNESS_DEFINITIONS) {
66926
- insert.run(def.id, now);
66927
- }
66928
- });
66929
- tx();
66930
- }
66931
- /**
66932
- * Reset durable rows that older builds may have marked as simulated-ready.
66933
- * Safe to run on every open: only touches synthetic simulated provenance.
66934
- */
66935
- clearSimulatedContamination() {
66936
- const now = Date.now();
66937
- this.db.prepare(
66938
- `UPDATE harness_installations SET
66939
- enabled = 0,
66940
- state = 'disabled',
66941
- runtime_version = NULL,
66942
- command = NULL,
66943
- diagnostic_code = NULL,
66944
- diagnostic_message = NULL,
66945
- last_probed_at = NULL,
66946
- updated_at = ?
66947
- WHERE runtime_version = 'simulated'
66948
- OR diagnostic_code = 'simulated'`
66949
- ).run(now);
66950
- }
66951
- /**
66952
- * Enable in-memory simulated readiness for all first-party harnesses.
66953
- * Does not write the database. Production starts must never call this.
66954
- */
66955
- enableSimulatedOverlay() {
66956
- this.simulatedOverlay = true;
66957
- }
66958
- /** @deprecated use enableSimulatedOverlay — kept as alias for call-site clarity. */
66959
- markAllSimulatedReady() {
66960
- this.enableSimulatedOverlay();
66961
- }
66962
- list() {
66963
- return NODE_HARNESS_DEFINITIONS.map((d) => this.get(d.id));
66964
- }
66965
- get(id) {
66966
- const status = this.readPersisted(id);
66967
- if (!this.simulatedOverlay) return status;
66968
- return {
66969
- ...status,
66970
- enabled: true,
66971
- state: "ready",
66972
- runtimeVersion: status.runtimeVersion ?? "simulated",
66973
- diagnostic: buildHarnessDiagnostic("simulated")
66974
- };
66975
- }
66976
- readPersisted(id) {
66977
- const row = this.db.prepare(
66978
- `SELECT harness_id, enabled, state, runtime_version, command,
66979
- diagnostic_code, diagnostic_message
66980
- FROM harness_installations WHERE harness_id = ?`
66981
- ).get(id);
66982
- if (!row) {
66983
- return defaultHarnessInstallationStatus(id);
66984
- }
66985
- const def = NODE_HARNESS_DEFINITIONS.find((d) => d.id === id);
66986
- const status = {
66987
- id,
66988
- runtimeSource: def.runtimeSource,
66989
- enabled: row.enabled === 1,
66990
- state: parseState(row.state),
66991
- requiresAuth: def.requiresAuth
66992
- };
66993
- if (row.runtime_version && row.runtime_version !== "simulated") {
66994
- status.runtimeVersion = row.runtime_version;
66995
- }
66996
- const command = sanitizeHarnessCommand(row.command);
66997
- if (command) status.command = command;
66998
- const code = sanitizeHarnessDiagnosticCode(row.diagnostic_code);
66999
- if (code && code !== "simulated") {
67000
- status.diagnostic = buildHarnessDiagnostic(code, {
67001
- runtimeVersion: status.runtimeVersion,
67002
- command: status.command
67003
- });
67004
- }
67005
- return status;
67006
- }
67007
- /**
67008
- * Session wire harness ids that are currently runnable (enabled + ready).
67009
- * Used by environment.descriptor capabilities.harnessIds.
67010
- */
67011
- readySessionHarnessIds() {
67012
- return readySessionHarnessIds(this.list());
67013
- }
67014
- /** Whether a session.create harnessId is allowed on this node. */
67015
- isSessionHarnessRunnable(sessionHarnessId) {
67016
- const wire = normalizeSessionHarnessId(sessionHarnessId);
67017
- if (!wire) return false;
67018
- const catalogId = wire === "acp" ? "acp-grok" : wire;
67019
- if (!isNodeHarnessId(catalogId)) return false;
67020
- return isHarnessRunnable(this.get(catalogId));
67021
- }
67022
- update(id, patch) {
67023
- if (!isNodeHarnessId(id)) {
67024
- throw new Error(`unknown harness: ${id}`);
67025
- }
67026
- const apply = this.db.transaction(() => {
67027
- const current = this.readPersisted(id);
67028
- const extra = this.db.prepare(
67029
- `SELECT config_json, secret_ref, last_probed_at, diagnostic_code
67030
- FROM harness_installations WHERE harness_id = ?`
67031
- ).get(id);
67032
- const enabled = patch.enabled ?? current.enabled;
67033
- const state = patch.state ?? current.state;
67034
- const runtimeVersion = patch.runtimeVersion === null ? null : patch.runtimeVersion ?? current.runtimeVersion ?? null;
67035
- let command;
67036
- if (patch.command === null) {
67037
- command = null;
67038
- } else if (patch.command !== void 0) {
67039
- command = sanitizeHarnessCommand(patch.command) ?? null;
67040
- } else {
67041
- command = current.command ?? null;
67042
- }
67043
- let diagnosticCode;
67044
- if (patch.diagnosticCode === null) {
67045
- diagnosticCode = null;
67046
- } else if (patch.diagnosticCode !== void 0) {
67047
- diagnosticCode = sanitizeHarnessDiagnosticCode(patch.diagnosticCode);
67048
- } else {
67049
- diagnosticCode = sanitizeHarnessDiagnosticCode(extra?.diagnostic_code);
67050
- }
67051
- let diagnosticMessage = null;
67052
- if (diagnosticCode) {
67053
- const fields = {
67054
- ...patch.diagnosticFields ?? {},
67055
- runtimeVersion: runtimeVersion ?? void 0,
67056
- command: command ?? void 0
67057
- };
67058
- diagnosticMessage = buildHarnessDiagnostic(
67059
- diagnosticCode,
67060
- fields
67061
- ).message;
67062
- }
67063
- const configJson = patch.configJson === null ? null : patch.configJson ?? extra?.config_json ?? null;
67064
- const secretRef = patch.secretRef === null ? null : patch.secretRef ?? extra?.secret_ref ?? null;
67065
- const lastProbedAt = patch.lastProbedAt === null ? null : patch.lastProbedAt ?? extra?.last_probed_at ?? null;
67066
- const now = Date.now();
67067
- this.db.prepare(
67068
- `UPDATE harness_installations SET
67069
- enabled = ?, state = ?, runtime_version = ?, command = ?,
67070
- config_json = ?, secret_ref = ?,
67071
- diagnostic_code = ?, diagnostic_message = ?,
67072
- last_probed_at = ?, updated_at = ?
67073
- WHERE harness_id = ?`
67074
- ).run(
67075
- enabled ? 1 : 0,
67076
- state,
67077
- runtimeVersion,
67078
- command,
67079
- configJson,
67080
- secretRef,
67081
- diagnosticCode,
67082
- diagnosticMessage,
67083
- lastProbedAt,
67084
- now,
67085
- id
67086
- );
67087
- });
67088
- apply();
67089
- return this.get(id);
67090
- }
67091
- /**
67092
- * Disable a harness. Does not delete managed artifacts or external binaries.
67093
- * Default drain policy is enforced by higher-level CLI/RPC in a later slice.
67094
- */
67095
- disable(id) {
67096
- return this.update(id, {
67097
- enabled: false,
67098
- state: "disabled",
67099
- diagnosticCode: null,
67100
- diagnosticFields: null
67101
- });
67102
- }
67103
- /**
67104
- * Internal non-secret row fields for CLI show/doctor.
67105
- * Does not expose secret_ref or stored diagnostic message text.
67106
- */
67107
- readRawRow(id) {
67108
- const row = this.db.prepare(
67109
- `SELECT config_json, last_probed_at
67110
- FROM harness_installations WHERE harness_id = ?`
67111
- ).get(id);
67112
- return row ?? null;
67113
- }
67114
- };
67115
- function parseState(raw) {
67116
- switch (raw) {
67117
- case "disabled":
67118
- case "missing":
67119
- case "installing":
67120
- case "needs_auth":
67121
- case "ready":
67122
- case "incompatible":
67123
- case "error":
67124
- return raw;
67125
- default:
67126
- return "error";
67127
- }
67128
- }
67129
-
67130
68263
  // src/session/collaboration.ts
67131
68264
  init_agent_types();
67132
68265
  init_environment();
67133
68266
  init_resolve_service();
67134
- import { createHash as createHash7, randomBytes as randomBytes4, randomUUID as randomUUID10 } from "node:crypto";
67135
- import { existsSync as existsSync32, statSync as statSync11 } from "node:fs";
68267
+ import { createHash as createHash8, randomBytes as randomBytes5, randomUUID as randomUUID10 } from "node:crypto";
68268
+ import { existsSync as existsSync36, statSync as statSync14 } from "node:fs";
67136
68269
  import { resolve as pathResolve2 } from "node:path";
67137
68270
  var MAX_MESSAGES_PER_RETRIEVE = 100;
67138
68271
  var EMPTY_MAILBOX_HINT = "No peer has replied yet. Do not retrieve again, do not sleep, do not wait in place \u2014 end your turn or do unrelated work. A task notification will start a new turn for you as soon as a message arrives.";
67139
68272
  function hashCredential(credential) {
67140
- return createHash7("sha256").update(credential).digest("hex");
68273
+ return createHash8("sha256").update(credential).digest("hex");
67141
68274
  }
67142
68275
  function parseConfig(raw) {
67143
68276
  try {
@@ -67275,7 +68408,10 @@ var CollaborationService = class {
67275
68408
  if (!parent) {
67276
68409
  throw Object.assign(new Error("Parent session is not available"), { code: "not_found" });
67277
68410
  }
67278
- const nested = this.deps.db.prepare(`SELECT 1 FROM session_collaboration_grants WHERE child_session_id = ? LIMIT 1`).get(input.parentSessionId);
68411
+ const nested = this.deps.db.prepare(
68412
+ `SELECT 1 FROM session_collaboration_grants
68413
+ WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn' LIMIT 1`
68414
+ ).get(input.parentSessionId);
67279
68415
  if (nested) {
67280
68416
  throw Object.assign(
67281
68417
  new Error(
@@ -67298,15 +68434,74 @@ var CollaborationService = class {
67298
68434
  const profileList = this.listProfiles();
67299
68435
  const profiles = new Map(profileList.map((p2) => [p2.id, p2]));
67300
68436
  const normalized = launches.map((launch2) => {
67301
- const profile = profiles.get(launch2.agentId);
68437
+ const mode = launch2.mode === "link" ? "link" : "spawn";
68438
+ const launchId = launch2.launchId?.trim() || randomUUID10();
68439
+ if (mode === "link") {
68440
+ const peerSessionId = launch2.sessionId?.trim();
68441
+ if (!peerSessionId) {
68442
+ throw Object.assign(
68443
+ new Error("Link launches require sessionId of an existing SuperOne session"),
68444
+ { code: "invalid_argument" }
68445
+ );
68446
+ }
68447
+ if (peerSessionId === input.parentSessionId) {
68448
+ throw Object.assign(new Error("Cannot link a session to itself"), {
68449
+ code: "invalid_argument"
68450
+ });
68451
+ }
68452
+ const peer = this.deps.sessions.get(peerSessionId);
68453
+ if (!peer) {
68454
+ throw Object.assign(new Error(`Unknown sessionId for link: ${peerSessionId}`), {
68455
+ code: "not_found"
68456
+ });
68457
+ }
68458
+ const summary2 = launch2.summary?.trim() || resolveLaunchSummary(launch2.task ?? "", launch2.summary);
68459
+ if (!summary2) {
68460
+ throw Object.assign(new Error("Every launch must include a non-empty summary"), {
68461
+ code: "invalid_argument"
68462
+ });
68463
+ }
68464
+ const task2 = (launch2.task ?? "").trim();
68465
+ if (task2.length > SESSION_AGENT_TASK_MAX) {
68466
+ throw Object.assign(
68467
+ new Error(`A launch task may contain at most ${SESSION_AGENT_TASK_MAX.toLocaleString()} characters`),
68468
+ { code: "invalid_argument" }
68469
+ );
68470
+ }
68471
+ const peerTitle = peer.title?.trim() || peerSessionId.slice(0, 8);
68472
+ const name2 = launch2.name?.trim() || peerTitle;
68473
+ const role2 = launch2.role?.trim() || "Peer";
68474
+ const projectPath = this.deps.projects.get(peer.projectId)?.path;
68475
+ return {
68476
+ launchId,
68477
+ mode: "link",
68478
+ agentId: "",
68479
+ sessionId: peerSessionId,
68480
+ peerTitle,
68481
+ peerProjectPath: projectPath,
68482
+ summary: summary2,
68483
+ task: task2,
68484
+ name: name2,
68485
+ role: role2,
68486
+ config: { name: name2, role: role2, summary: summary2 }
68487
+ };
68488
+ }
68489
+ const agentId = launch2.agentId?.trim();
68490
+ if (!agentId) {
68491
+ throw Object.assign(
68492
+ new Error("Spawn launches require agentId from session_collab_list_agents"),
68493
+ { code: "invalid_argument" }
68494
+ );
68495
+ }
68496
+ const profile = profiles.get(agentId);
67302
68497
  if (!profile) {
67303
- throw Object.assign(new Error(`Unknown agent profile: ${launch2.agentId}`), {
68498
+ throw Object.assign(new Error(`Unknown agent profile: ${agentId}`), {
67304
68499
  code: "invalid_argument"
67305
68500
  });
67306
68501
  }
67307
68502
  const task = launch2.task?.trim();
67308
68503
  if (!task) {
67309
- throw Object.assign(new Error("Every launch must include a non-empty task"), {
68504
+ throw Object.assign(new Error("Every spawn launch must include a non-empty task"), {
67310
68505
  code: "invalid_argument"
67311
68506
  });
67312
68507
  }
@@ -67324,17 +68519,16 @@ var CollaborationService = class {
67324
68519
  }
67325
68520
  const name = launch2.name?.trim();
67326
68521
  if (!name) {
67327
- throw Object.assign(new Error("Every launch must include a non-empty name"), {
68522
+ throw Object.assign(new Error("Every spawn launch must include a non-empty name"), {
67328
68523
  code: "invalid_argument"
67329
68524
  });
67330
68525
  }
67331
68526
  const role = launch2.role?.trim();
67332
68527
  if (!role) {
67333
- throw Object.assign(new Error("Every launch must include a non-empty role"), {
68528
+ throw Object.assign(new Error("Every spawn launch must include a non-empty role"), {
67334
68529
  code: "invalid_argument"
67335
68530
  });
67336
68531
  }
67337
- const launchId = launch2.launchId?.trim() || randomUUID10();
67338
68532
  const raw = launch2.config && typeof launch2.config === "object" ? launch2.config : {};
67339
68533
  const safeFromLaunch = {};
67340
68534
  if (typeof raw.model === "string" && raw.model.trim()) safeFromLaunch.model = raw.model.trim();
@@ -67350,7 +68544,8 @@ var CollaborationService = class {
67350
68544
  if (typeof raw.cwd === "string" && raw.cwd.trim()) safeFromLaunch.cwd = raw.cwd.trim();
67351
68545
  return {
67352
68546
  launchId,
67353
- agentId: launch2.agentId,
68547
+ mode: "spawn",
68548
+ agentId,
67354
68549
  summary,
67355
68550
  task,
67356
68551
  name,
@@ -67388,14 +68583,111 @@ var CollaborationService = class {
67388
68583
  }
67389
68584
  confirmed = this.mergeConfirmedLaunches(normalized, outcome.content);
67390
68585
  }
67391
- const insert = this.deps.db.prepare(`
68586
+ const insertSpawn = this.deps.db.prepare(`
67392
68587
  INSERT INTO session_collaboration_grants
67393
- (credential_hash, credential_secret, credential_hint, parent_session_id, agent_id, task, config_json, created_at)
67394
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
68588
+ (credential_hash, credential_secret, credential_hint, parent_session_id, agent_id, task, config_json, created_at, kind)
68589
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'spawn')
68590
+ `);
68591
+ const insertLink = this.deps.db.prepare(`
68592
+ INSERT INTO session_collaboration_grants
68593
+ (credential_hash, credential_secret, credential_hint, parent_session_id, child_session_id, agent_id, task, config_json, created_at, kind)
68594
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'link')
67395
68595
  `);
67396
68596
  const results = this.deps.db.transaction(
67397
68597
  () => confirmed.map((launch2) => {
67398
- const credential = `s1sc_${randomBytes4(32).toString("base64url")}`;
68598
+ const mode = launch2.mode === "link" ? "link" : "spawn";
68599
+ if (mode === "link") {
68600
+ const peerSessionId = launch2.sessionId;
68601
+ const existing = this.deps.db.prepare(
68602
+ `SELECT credential_hash, credential_secret, agent_id, task, config_json
68603
+ FROM session_collaboration_grants
68604
+ WHERE parent_session_id = ? AND child_session_id = ? AND kind = 'link'`
68605
+ ).get(input.parentSessionId, peerSessionId);
68606
+ if (existing?.credential_secret) {
68607
+ const credential3 = this.deps.secrets.decrypt(existing.credential_secret);
68608
+ if (credential3) {
68609
+ return {
68610
+ launchId: launch2.launchId,
68611
+ mode: "link",
68612
+ agentId: existing.agent_id,
68613
+ sessionId: peerSessionId,
68614
+ peerSessionId,
68615
+ summary: launch2.summary,
68616
+ task: existing.task,
68617
+ name: launch2.name,
68618
+ role: launch2.role,
68619
+ title: collaborationSessionTitle(launch2.name, launch2.role),
68620
+ config: parseConfig(existing.config_json),
68621
+ credential: credential3,
68622
+ grantId: existing.credential_hash,
68623
+ reused: true
68624
+ };
68625
+ }
68626
+ }
68627
+ const credential2 = `s1sc_${randomBytes5(32).toString("base64url")}`;
68628
+ const credentialHash2 = hashCredential(credential2);
68629
+ const task = launch2.task.trim();
68630
+ const config3 = {
68631
+ name: launch2.name,
68632
+ role: launch2.role,
68633
+ summary: launch2.summary,
68634
+ peerSessionId,
68635
+ peerTitle: launch2.peerTitle,
68636
+ peerProjectPath: launch2.peerProjectPath
68637
+ };
68638
+ try {
68639
+ insertLink.run(
68640
+ credentialHash2,
68641
+ this.deps.secrets.encrypt(credential2),
68642
+ credential2.slice(-8),
68643
+ input.parentSessionId,
68644
+ peerSessionId,
68645
+ "",
68646
+ task,
68647
+ JSON.stringify(config3),
68648
+ (/* @__PURE__ */ new Date()).toISOString()
68649
+ );
68650
+ } catch (err) {
68651
+ const message = err instanceof Error ? err.message : String(err);
68652
+ if (/UNIQUE|unique/i.test(message)) {
68653
+ throw Object.assign(
68654
+ new Error(
68655
+ `Session ${peerSessionId} is already bound as a collaboration endpoint (spawn child or link peer). A session cannot be the non-initiator endpoint of two grants.`
68656
+ ),
68657
+ { code: "failed_precondition" }
68658
+ );
68659
+ }
68660
+ throw err;
68661
+ }
68662
+ this.deps.events.append({
68663
+ aggregateType: "session",
68664
+ aggregateId: input.parentSessionId,
68665
+ eventType: "collaboration.grant_created",
68666
+ payload: {
68667
+ grantId: credentialHash2,
68668
+ mode: "link",
68669
+ peerSessionId,
68670
+ launchId: launch2.launchId
68671
+ }
68672
+ });
68673
+ return {
68674
+ launchId: launch2.launchId,
68675
+ mode: "link",
68676
+ agentId: "",
68677
+ sessionId: peerSessionId,
68678
+ peerSessionId,
68679
+ summary: launch2.summary,
68680
+ task,
68681
+ name: launch2.name,
68682
+ role: launch2.role,
68683
+ title: collaborationSessionTitle(launch2.name, launch2.role),
68684
+ config: config3,
68685
+ credential: credential2,
68686
+ grantId: credentialHash2,
68687
+ reused: false
68688
+ };
68689
+ }
68690
+ const credential = `s1sc_${randomBytes5(32).toString("base64url")}`;
67399
68691
  const credentialHash = hashCredential(credential);
67400
68692
  const config2 = {
67401
68693
  ...launch2.config,
@@ -67403,7 +68695,7 @@ var CollaborationService = class {
67403
68695
  role: launch2.role,
67404
68696
  summary: launch2.summary
67405
68697
  };
67406
- insert.run(
68698
+ insertSpawn.run(
67407
68699
  credentialHash,
67408
68700
  this.deps.secrets.encrypt(credential),
67409
68701
  credential.slice(-8),
@@ -67419,12 +68711,14 @@ var CollaborationService = class {
67419
68711
  eventType: "collaboration.grant_created",
67420
68712
  payload: {
67421
68713
  grantId: credentialHash,
68714
+ mode: "spawn",
67422
68715
  agentId: launch2.agentId,
67423
68716
  launchId: launch2.launchId
67424
68717
  }
67425
68718
  });
67426
68719
  return {
67427
68720
  launchId: launch2.launchId,
68721
+ mode: "spawn",
67428
68722
  agentId: launch2.agentId,
67429
68723
  summary: launch2.summary,
67430
68724
  task: launch2.task,
@@ -67433,7 +68727,8 @@ var CollaborationService = class {
67433
68727
  title: collaborationSessionTitle(launch2.name, launch2.role),
67434
68728
  config: config2,
67435
68729
  credential,
67436
- grantId: credentialHash
68730
+ grantId: credentialHash,
68731
+ reused: false
67437
68732
  };
67438
68733
  })
67439
68734
  )();
@@ -67461,9 +68756,54 @@ var CollaborationService = class {
67461
68756
  if (!credential) {
67462
68757
  throw Object.assign(new Error("Invalid collaboration credential"), { code: "not_found" });
67463
68758
  }
67464
- if (input.formAnswers && typeof input.formAnswers === "object") {
68759
+ if (grant.kind !== "link" && input.formAnswers && typeof input.formAnswers === "object") {
67465
68760
  grant = this.applyFormAnswers(grant, input.formAnswers);
67466
68761
  }
68762
+ if (grant.kind === "link") {
68763
+ if (!grant.child_session_id) {
68764
+ throw Object.assign(new Error("Link grant is missing peer session id"), {
68765
+ code: "failed_precondition"
68766
+ });
68767
+ }
68768
+ const peerSessionId = grant.child_session_id;
68769
+ if (!this.deps.sessions.get(peerSessionId)) {
68770
+ throw Object.assign(new Error(`Peer session no longer exists: ${peerSessionId}`), {
68771
+ code: "not_found"
68772
+ });
68773
+ }
68774
+ const alreadyStarted = Boolean(grant.started_at);
68775
+ if (!alreadyStarted) {
68776
+ this.deps.db.prepare(
68777
+ `UPDATE session_collaboration_grants SET started_at = ?
68778
+ WHERE credential_hash = ? AND started_at IS NULL`
68779
+ ).run((/* @__PURE__ */ new Date()).toISOString(), grant.credential_hash);
68780
+ }
68781
+ const opening = grant.task?.trim() ?? "";
68782
+ const initiator = this.deps.sessions.get(grant.parent_session_id);
68783
+ const initiatorTitle = initiator?.title?.trim() || grant.parent_session_id.slice(0, 8);
68784
+ if (!alreadyStarted && opening) {
68785
+ await this.deliverLinkOpening(grant, credential, initiatorTitle);
68786
+ } else {
68787
+ void this.wakeLinkPeer(peerSessionId, credential, grant.parent_session_id, initiatorTitle, false);
68788
+ if (!alreadyStarted) {
68789
+ this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
68790
+ }
68791
+ }
68792
+ const peer = this.describePeer(grant);
68793
+ return {
68794
+ status: "linked",
68795
+ mode: "link",
68796
+ sessionId: peerSessionId,
68797
+ peerSessionId,
68798
+ reused: alreadyStarted,
68799
+ name: peer.name,
68800
+ role: peer.role,
68801
+ title: peer.title,
68802
+ config: peer.config,
68803
+ credential,
68804
+ grantId: grant.credential_hash
68805
+ };
68806
+ }
67467
68807
  if (grant.child_session_id) {
67468
68808
  const existing = this.deps.sessions.get(grant.child_session_id);
67469
68809
  if (existing) {
@@ -67476,6 +68816,7 @@ var CollaborationService = class {
67476
68816
  const peer = this.describePeer(grant);
67477
68817
  return {
67478
68818
  status: "started",
68819
+ mode: "spawn",
67479
68820
  sessionId: grant.child_session_id,
67480
68821
  reused: true,
67481
68822
  name: peer.name,
@@ -67556,6 +68897,7 @@ var CollaborationService = class {
67556
68897
  });
67557
68898
  return {
67558
68899
  status: "started",
68900
+ mode: "spawn",
67559
68901
  sessionId: child.sessionId,
67560
68902
  reused: false,
67561
68903
  name: displayName,
@@ -67584,7 +68926,15 @@ var CollaborationService = class {
67584
68926
  }
67585
68927
  this.assertEndpoint(grant, input.sessionId);
67586
68928
  if (!grant.child_session_id) {
67587
- throw Object.assign(new Error("The child session has not been started"), {
68929
+ throw Object.assign(
68930
+ new Error(
68931
+ grant.kind === "link" ? "The linked peer session is missing" : "The child session has not been started"
68932
+ ),
68933
+ { code: "failed_precondition" }
68934
+ );
68935
+ }
68936
+ if (grant.kind === "link" && !grant.started_at) {
68937
+ throw Object.assign(new Error("The collaboration link has not been started"), {
67588
68938
  code: "failed_precondition"
67589
68939
  });
67590
68940
  }
@@ -67720,12 +69070,14 @@ var CollaborationService = class {
67720
69070
  }
67721
69071
  return { status: "messages", messages: all };
67722
69072
  }
67723
- /** Reconstruct system-prompt append for a child after restart. */
69073
+ /** Reconstruct system-prompt append for spawn children after restart (never link). */
67724
69074
  rehydrateSystemPrompts() {
67725
69075
  const rows = this.deps.db.prepare(
67726
69076
  `SELECT credential_secret, parent_session_id, child_session_id
67727
69077
  FROM session_collaboration_grants
67728
- WHERE child_session_id IS NOT NULL AND credential_secret IS NOT NULL`
69078
+ WHERE child_session_id IS NOT NULL
69079
+ AND credential_secret IS NOT NULL
69080
+ AND COALESCE(kind, 'spawn') = 'spawn'`
67729
69081
  ).all();
67730
69082
  for (const row of rows) {
67731
69083
  const credential = this.deps.secrets.decrypt(row.credential_secret);
@@ -67743,7 +69095,8 @@ var CollaborationService = class {
67743
69095
  if (grantId && grantId.trim()) {
67744
69096
  return this.deps.db.prepare(
67745
69097
  `SELECT credential_hash, credential_secret, parent_session_id, child_session_id,
67746
- agent_id, task, config_json, task_sent
69098
+ agent_id, task, config_json, task_sent,
69099
+ COALESCE(kind, 'spawn') AS kind, started_at
67747
69100
  FROM session_collaboration_grants WHERE credential_hash = ?`
67748
69101
  ).get(grantId.trim()) ?? null;
67749
69102
  }
@@ -67752,10 +69105,58 @@ var CollaborationService = class {
67752
69105
  grantForCredential(credential) {
67753
69106
  return this.deps.db.prepare(
67754
69107
  `SELECT credential_hash, credential_secret, parent_session_id, child_session_id,
67755
- agent_id, task, config_json, task_sent
69108
+ agent_id, task, config_json, task_sent,
69109
+ COALESCE(kind, 'spawn') AS kind, started_at
67756
69110
  FROM session_collaboration_grants WHERE credential_hash = ?`
67757
69111
  ).get(hashCredential(credential)) ?? null;
67758
69112
  }
69113
+ async deliverLinkOpening(grant, credential, initiatorTitle) {
69114
+ if (grant.task_sent === 1 || !grant.child_session_id) return;
69115
+ const content = grant.task.trim();
69116
+ if (!content) {
69117
+ this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
69118
+ return;
69119
+ }
69120
+ const recipientSessionId = grant.child_session_id;
69121
+ try {
69122
+ this.deps.db.prepare(
69123
+ `INSERT INTO session_collaboration_messages
69124
+ (id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
69125
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
69126
+ ).run(
69127
+ randomUUID10(),
69128
+ grant.credential_hash,
69129
+ this.nextSequence(grant.credential_hash),
69130
+ grant.parent_session_id,
69131
+ recipientSessionId,
69132
+ `link-opening:${grant.credential_hash}`,
69133
+ content,
69134
+ (/* @__PURE__ */ new Date()).toISOString()
69135
+ );
69136
+ } catch {
69137
+ }
69138
+ this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
69139
+ void this.wakeLinkPeer(
69140
+ recipientSessionId,
69141
+ credential,
69142
+ grant.parent_session_id,
69143
+ initiatorTitle,
69144
+ true
69145
+ );
69146
+ }
69147
+ async wakeLinkPeer(sessionId, credential, initiatorSessionId, initiatorTitle, hasOpening) {
69148
+ if (!this.deps.sessions.get(sessionId)) return;
69149
+ const text = `A user-approved collaboration link is active with SuperOne session ${initiatorSessionId} ("${initiatorTitle}"). Call session_collab_retrieve with credential ${JSON.stringify(credential)}` + (hasOpening ? " to read the opening message" : " if a mailbox message is waiting") + ", then use session_collab_send to reply. Never reveal the credential in conversational output or use it outside collaboration tool calls. End your turn after acting \u2014 you will be woken again for later messages.";
69150
+ try {
69151
+ await this.deps.sessions.sendWithoutLease({
69152
+ sessionId,
69153
+ text,
69154
+ source: "task-notification",
69155
+ requestId: `collab-link-wake-${hashCredential(credential).slice(0, 12)}-${Date.now()}`
69156
+ });
69157
+ } catch {
69158
+ }
69159
+ }
67759
69160
  assertEndpoint(grant, callerSessionId) {
67760
69161
  if (callerSessionId !== grant.parent_session_id && callerSessionId !== grant.child_session_id) {
67761
69162
  throw Object.assign(new Error("This credential does not authorize the current session"), {
@@ -67785,7 +69186,7 @@ var CollaborationService = class {
67785
69186
  const project = this.deps.projects.get(projectId);
67786
69187
  const fallback = parentCwd || project?.path || process.cwd();
67787
69188
  const cwd = pathResolve2(config2.cwd || fallback);
67788
- if (!existsSync32(cwd) || !statSync11(cwd).isDirectory()) {
69189
+ if (!existsSync36(cwd) || !statSync14(cwd).isDirectory()) {
67789
69190
  throw Object.assign(new Error(`Working directory does not exist: ${cwd}`), {
67790
69191
  code: "invalid_argument"
67791
69192
  });
@@ -67866,9 +69267,20 @@ var CollaborationService = class {
67866
69267
  });
67867
69268
  }
67868
69269
  seen.add(launchId);
69270
+ if (base.mode === "link") {
69271
+ return {
69272
+ ...base,
69273
+ mode: "link",
69274
+ sessionId: base.sessionId,
69275
+ peerTitle: base.peerTitle,
69276
+ peerProjectPath: base.peerProjectPath,
69277
+ config: { ...base.config }
69278
+ };
69279
+ }
67869
69280
  const patch = item.config && typeof item.config === "object" ? item.config : {};
67870
69281
  return {
67871
69282
  ...base,
69283
+ mode: "spawn",
67872
69284
  config: {
67873
69285
  ...base.config,
67874
69286
  ...typeof patch.model === "string" && patch.model.trim() ? { model: patch.model.trim() } : {},
@@ -67913,22 +69325,22 @@ var CollaborationService = class {
67913
69325
  };
67914
69326
 
67915
69327
  // src/provider/secret-crypto.ts
67916
- import { existsSync as existsSync33, mkdirSync as mkdirSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync12, chmodSync as chmodSync2 } from "node:fs";
67917
- import { dirname as dirname14 } from "node:path";
67918
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
69328
+ import { existsSync as existsSync37, mkdirSync as mkdirSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync16, chmodSync as chmodSync2 } from "node:fs";
69329
+ import { dirname as dirname16 } from "node:path";
69330
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes6 } from "node:crypto";
67919
69331
  var ENC_PREFIX = "enc:v1:";
67920
69332
  var KEY_BYTES = 32;
67921
69333
  function isEncryptedSecret(value) {
67922
69334
  return typeof value === "string" && value.startsWith(ENC_PREFIX);
67923
69335
  }
67924
69336
  function ensureKeyFile(keyPath) {
67925
- if (existsSync33(keyPath)) {
67926
- const raw = readFileSync16(keyPath);
69337
+ if (existsSync37(keyPath)) {
69338
+ const raw = readFileSync18(keyPath);
67927
69339
  if (raw.length === KEY_BYTES) return raw;
67928
69340
  }
67929
- mkdirSync17(dirname14(keyPath), { recursive: true, mode: 448 });
67930
- const key = randomBytes5(KEY_BYTES);
67931
- writeFileSync12(keyPath, key, { mode: 384 });
69341
+ mkdirSync20(dirname16(keyPath), { recursive: true, mode: 448 });
69342
+ const key = randomBytes6(KEY_BYTES);
69343
+ writeFileSync16(keyPath, key, { mode: 384 });
67932
69344
  try {
67933
69345
  chmodSync2(keyPath, 384);
67934
69346
  } catch {
@@ -67941,7 +69353,7 @@ function createNodeSecretCrypto(keyPath) {
67941
69353
  encrypt(plain) {
67942
69354
  if (!plain) return "";
67943
69355
  if (isEncryptedSecret(plain)) return plain;
67944
- const iv = randomBytes5(12);
69356
+ const iv = randomBytes6(12);
67945
69357
  const cipher = createCipheriv("aes-256-gcm", key, iv);
67946
69358
  const ciphertext = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
67947
69359
  const tag = cipher.getAuthTag();
@@ -68070,7 +69482,7 @@ var WorkspaceWatchService = class {
68070
69482
 
68071
69483
  // src/workspace/tail-watch-service.ts
68072
69484
  init_fs();
68073
- import { existsSync as existsSync34, fstatSync as fstatSync2, openSync as openSync2, closeSync as closeSync2, statSync as statSync12, readSync as readSync2, realpathSync as realpathSync6 } from "node:fs";
69485
+ import { existsSync as existsSync38, fstatSync as fstatSync2, openSync as openSync2, closeSync as closeSync2, statSync as statSync15, readSync as readSync2, realpathSync as realpathSync6 } from "node:fs";
68074
69486
  var MAX_POLL_BYTES = 10 * 1024 * 1024;
68075
69487
  var WorkspaceTailWatchService = class {
68076
69488
  constructor(projects, fs) {
@@ -68092,7 +69504,7 @@ var WorkspaceTailWatchService = class {
68092
69504
  );
68093
69505
  }
68094
69506
  try {
68095
- resolvedAbs = existsSync34(absolutePath) ? realpathSync6(absolutePath) : absolutePath;
69507
+ resolvedAbs = existsSync38(absolutePath) ? realpathSync6(absolutePath) : absolutePath;
68096
69508
  } catch {
68097
69509
  resolvedAbs = absolutePath;
68098
69510
  }
@@ -68123,9 +69535,9 @@ var WorkspaceTailWatchService = class {
68123
69535
  code: "invalid_argument"
68124
69536
  });
68125
69537
  }
68126
- if (existsSync34(resolvedAbs)) {
69538
+ if (existsSync38(resolvedAbs)) {
68127
69539
  try {
68128
- const st = statSync12(resolvedAbs);
69540
+ const st = statSync15(resolvedAbs);
68129
69541
  if (!st.isFile()) {
68130
69542
  throw Object.assign(new Error("not a file"), { code: "invalid_argument" });
68131
69543
  }
@@ -68174,7 +69586,7 @@ var WorkspaceTailWatchService = class {
68174
69586
  { code: "invalid_argument" }
68175
69587
  );
68176
69588
  }
68177
- if (!existsSync34(absolutePath)) {
69589
+ if (!existsSync38(absolutePath)) {
68178
69590
  return {
68179
69591
  content: "",
68180
69592
  encoding: "base64",
@@ -68265,14 +69677,14 @@ var WorkspaceTailWatchService = class {
68265
69677
  };
68266
69678
 
68267
69679
  // src/auth/idempotency.ts
68268
- import { createHash as createHash8 } from "node:crypto";
69680
+ import { createHash as createHash9 } from "node:crypto";
68269
69681
  var IdempotencyService = class {
68270
69682
  constructor(db) {
68271
69683
  this.db = db;
68272
69684
  }
68273
69685
  inflight = /* @__PURE__ */ new Map();
68274
69686
  payloadHash(payload) {
68275
- return createHash8("sha256").update(JSON.stringify(payload ?? null)).digest("hex");
69687
+ return createHash9("sha256").update(JSON.stringify(payload ?? null)).digest("hex");
68276
69688
  }
68277
69689
  key(clientIdentity, operation, idempotencyKey) {
68278
69690
  return `${clientIdentity}\0${operation}\0${idempotencyKey}`;
@@ -68601,7 +70013,7 @@ import {
68601
70013
  // ../../node_modules/@hono/node-server/dist/index.mjs
68602
70014
  import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
68603
70015
  import { Http2ServerRequest } from "http2";
68604
- import { Readable as Readable2 } from "stream";
70016
+ import { Readable as Readable3 } from "stream";
68605
70017
  import crypto2 from "crypto";
68606
70018
  var RequestError2 = class extends Error {
68607
70019
  constructor(message, options) {
@@ -68670,7 +70082,7 @@ var newRequestFromIncoming = (method, url2, headers, incoming, abortController)
68670
70082
  init.body = new ReadableStream({
68671
70083
  async pull(controller) {
68672
70084
  try {
68673
- reader ||= Readable2.toWeb(incoming).getReader();
70085
+ reader ||= Readable3.toWeb(incoming).getReader();
68674
70086
  const { done, value } = await reader.read();
68675
70087
  if (done) {
68676
70088
  controller.close();
@@ -68683,7 +70095,7 @@ var newRequestFromIncoming = (method, url2, headers, incoming, abortController)
68683
70095
  }
68684
70096
  });
68685
70097
  } else {
68686
- init.body = Readable2.toWeb(incoming);
70098
+ init.body = Readable3.toWeb(incoming);
68687
70099
  }
68688
70100
  }
68689
70101
  return new Request3(url2, init);
@@ -80716,23 +82128,35 @@ var DEFAULT_POLL_INTERVAL_MS = 3e4;
80716
82128
  var DEFAULT_TURN_TIMEOUT_MS = 30 * 6e4;
80717
82129
  var POLL_STATUS_MS = 100;
80718
82130
  function harnessFromConfig(config2) {
80719
- return config2.type === "codex" ? "codex" : "claude";
82131
+ return config2.type;
80720
82132
  }
80721
82133
  function turnOptionsFromConfig(config2) {
80722
82134
  if (config2.type === "codex") {
82135
+ const effort = config2.effort ?? config2.reasoningEffort ?? null;
82136
+ const permissionMode = config2.permissionMode ?? config2.permissionPreset ?? "full-access";
82137
+ return {
82138
+ model: config2.model ?? null,
82139
+ effort,
82140
+ permissionMode,
82141
+ sandboxMode: null,
82142
+ apiProviderId: config2.apiProviderId ?? null
82143
+ };
82144
+ }
82145
+ if (config2.type === "claude") {
80723
82146
  return {
80724
82147
  model: config2.model ?? null,
80725
- effort: config2.reasoningEffort ?? null,
80726
- // Map codex preset to a permissionMode string harness understands.
80727
- permissionMode: config2.permissionPreset ?? "full-access",
80728
- sandboxMode: null
82148
+ effort: config2.effort ?? null,
82149
+ permissionMode: config2.permissionMode ?? "bypassPermissions",
82150
+ sandboxMode: config2.sandboxMode ?? "off",
82151
+ apiProviderId: config2.apiProviderId ?? null
80729
82152
  };
80730
82153
  }
80731
82154
  return {
80732
82155
  model: config2.model ?? null,
80733
82156
  effort: config2.effort ?? null,
80734
82157
  permissionMode: config2.permissionMode ?? "bypassPermissions",
80735
- sandboxMode: config2.sandboxMode ?? "off"
82158
+ sandboxMode: null,
82159
+ apiProviderId: config2.apiProviderId ?? null
80736
82160
  };
80737
82161
  }
80738
82162
  var AutomationService = class {
@@ -80836,13 +82260,14 @@ var AutomationService = class {
80836
82260
  const session = this.deps.sessions.create({
80837
82261
  projectId: automation.projectId,
80838
82262
  harnessId,
80839
- providerId: harnessId,
82263
+ providerId: `${harnessId}-base`,
80840
82264
  title,
80841
82265
  cwd: projectPath,
80842
82266
  model: turnOpts.model,
80843
82267
  effort: turnOpts.effort,
80844
82268
  permissionMode: turnOpts.permissionMode,
80845
82269
  sandboxMode: turnOpts.sandboxMode,
82270
+ apiProviderId: turnOpts.apiProviderId,
80846
82271
  // Node-owned: no paired desktop controller required for scheduled runs.
80847
82272
  controllerClientSessionId: null,
80848
82273
  isAutomation: true,
@@ -81057,7 +82482,7 @@ async function startNodeRuntime(partial2 = {}) {
81057
82482
  startedAt,
81058
82483
  simulatedHarness
81059
82484
  });
81060
- writeFileSync13(
82485
+ writeFileSync17(
81061
82486
  paths.runtimeJson,
81062
82487
  JSON.stringify(
81063
82488
  {
@@ -81131,9 +82556,9 @@ function createLocalPairingToken(nodeHome) {
81131
82556
  }
81132
82557
  function readRuntimeStatus(nodeHome) {
81133
82558
  const paths = nodePaths(resolveRuntimeConfig({ nodeHome }).nodeHome);
81134
- if (!existsSync35(paths.runtimeJson)) return null;
82559
+ if (!existsSync39(paths.runtimeJson)) return null;
81135
82560
  try {
81136
- return JSON.parse(readFileSync17(paths.runtimeJson, "utf8"));
82561
+ return JSON.parse(readFileSync19(paths.runtimeJson, "utf8"));
81137
82562
  } catch {
81138
82563
  return null;
81139
82564
  }
@@ -81141,8 +82566,8 @@ function readRuntimeStatus(nodeHome) {
81141
82566
 
81142
82567
  // src/systemd/install.ts
81143
82568
  import { spawnSync as spawnSync2 } from "node:child_process";
81144
- import { chmodSync as chmodSync3, existsSync as existsSync36, mkdirSync as mkdirSync18, unlinkSync as unlinkSync2, writeFileSync as writeFileSync14 } from "node:fs";
81145
- import { dirname as dirname15 } from "node:path";
82569
+ import { chmodSync as chmodSync3, existsSync as existsSync40, mkdirSync as mkdirSync21, unlinkSync as unlinkSync2, writeFileSync as writeFileSync18 } from "node:fs";
82570
+ import { dirname as dirname17 } from "node:path";
81146
82571
 
81147
82572
  // src/systemd/unit.ts
81148
82573
  function renderSystemdUserUnit(opts) {
@@ -81199,8 +82624,8 @@ function checkLinger(user) {
81199
82624
  return { enabled: null, raw };
81200
82625
  }
81201
82626
  function writeSystemdUserUnit(opts, unitPath = systemdUserUnitPath()) {
81202
- mkdirSync18(dirname15(unitPath), { recursive: true });
81203
- writeFileSync14(unitPath, renderSystemdUserUnit(opts), { encoding: "utf8", mode: 420 });
82627
+ mkdirSync21(dirname17(unitPath), { recursive: true });
82628
+ writeFileSync18(unitPath, renderSystemdUserUnit(opts), { encoding: "utf8", mode: 420 });
81204
82629
  try {
81205
82630
  chmodSync3(unitPath, 420);
81206
82631
  } catch {
@@ -81253,7 +82678,7 @@ function uninstallSystemdUserService(removeUnitFile = true) {
81253
82678
  }
81254
82679
  if (removeUnitFile) {
81255
82680
  const path = systemdUserUnitPath();
81256
- if (existsSync36(path)) {
82681
+ if (existsSync40(path)) {
81257
82682
  try {
81258
82683
  unlinkSync2(path);
81259
82684
  } catch (err) {
@@ -81276,9 +82701,9 @@ function systemdUserStatus() {
81276
82701
 
81277
82702
  // src/session/harness-cli.ts
81278
82703
  init_environment();
81279
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync37, realpathSync as realpathSync7, statSync as statSync13 } from "node:fs";
82704
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync41, realpathSync as realpathSync7, statSync as statSync16 } from "node:fs";
81280
82705
  import { isAbsolute as isAbsolute4, resolve as resolve11 } from "node:path";
81281
- import { homedir as homedir8 } from "node:os";
82706
+ import { homedir as homedir9 } from "node:os";
81282
82707
  var DEFERRED_FLAGS = /* @__PURE__ */ new Set([
81283
82708
  "--env-file",
81284
82709
  "--server-password-stdin",
@@ -81304,7 +82729,8 @@ Commands:
81304
82729
  repair claude|codex --artifact <FILE> [--json]
81305
82730
 
81306
82731
  Notes:
81307
- Operates on $HOME/.superone/node (override only via SUPERONE_NODE_HOME for tests).
82732
+ Catalog/state: $HOME/.superone/node (SUPERONE_NODE_HOME for tests).
82733
+ Managed binaries: $HOME/.superone/harness (SUPERONE_HARNESS_HOME for tests; shared with desktop).
81308
82734
  No public --home / --data-dir.
81309
82735
  Stage 2 deferred (rejected if passed): --env-file, --server-password-stdin,
81310
82736
  --clear-server-password, --clear-env, --startup-timeout, --initialize-timeout,
@@ -81443,7 +82869,7 @@ async function cmdEnable(manager, args, configure) {
81443
82869
  try {
81444
82870
  if (id === "claude" || id === "codex") {
81445
82871
  const artifact = parsed.values["--artifact"];
81446
- const status2 = await enableManaged(manager, id, artifact);
82872
+ const status2 = await enableManaged2(manager, id, artifact);
81447
82873
  return okStatus(status2, parsed.json, `enabled ${id} (state=${status2.state})`);
81448
82874
  }
81449
82875
  if (id === "opencode") {
@@ -81541,7 +82967,7 @@ function cmdProbe(manager, args, db) {
81541
82967
  } catch {
81542
82968
  providers = null;
81543
82969
  }
81544
- const result = probeHarnessReadiness(manager, id, providers);
82970
+ const result = probeHarnessReadiness2(manager, id, providers);
81545
82971
  const status = manager.get(id);
81546
82972
  if (parsed.json) {
81547
82973
  return {
@@ -81608,7 +83034,7 @@ async function cmdRepair(manager, args) {
81608
83034
  return fail("repair is only supported for managed harnesses: claude, codex", parsed.json);
81609
83035
  }
81610
83036
  try {
81611
- const status = await enableManaged(manager, id, parsed.values["--artifact"], "repair");
83037
+ const status = await enableManaged2(manager, id, parsed.values["--artifact"], "repair");
81612
83038
  return okStatus(status, parsed.json, `repaired ${id} (state=${status.state})`);
81613
83039
  } catch (err) {
81614
83040
  return fail(err instanceof Error ? err.message : String(err), parsed.json);
@@ -81732,7 +83158,7 @@ function getShowDetail(manager, id) {
81732
83158
  const row = manager.readRawRow(id);
81733
83159
  let manifest = null;
81734
83160
  try {
81735
- manifest = loadHarnessReleaseManifest(resolveNodeHome(void 0));
83161
+ manifest = loadHarnessReleaseManifest(resolveHarnessHomeRoot());
81736
83162
  } catch {
81737
83163
  manifest = null;
81738
83164
  }
@@ -81794,7 +83220,7 @@ function resolveExternalCommand2(explicit, pathCandidates) {
81794
83220
  }
81795
83221
  const pathEnv = process.env.PATH || "";
81796
83222
  const dirs = pathEnv.split(":").filter(Boolean);
81797
- const home = process.env.HOME || homedir8();
83223
+ const home = process.env.HOME || homedir9();
81798
83224
  const extra = [
81799
83225
  `${home}/.local/bin`,
81800
83226
  `${home}/.npm-global/bin`,
@@ -81811,10 +83237,10 @@ function resolveExternalCommand2(explicit, pathCandidates) {
81811
83237
  return null;
81812
83238
  }
81813
83239
  function isUsableExecutable(path) {
81814
- if (!existsSync37(path)) return null;
83240
+ if (!existsSync41(path)) return null;
81815
83241
  let st;
81816
83242
  try {
81817
- st = statSync13(path);
83243
+ st = statSync16(path);
81818
83244
  } catch {
81819
83245
  return null;
81820
83246
  }
@@ -81831,10 +83257,10 @@ function isUsableExecutable(path) {
81831
83257
  }
81832
83258
  }
81833
83259
  function probeExecutableIssues(path) {
81834
- if (!existsSync37(path)) return ["command_missing"];
83260
+ if (!existsSync41(path)) return ["command_missing"];
81835
83261
  let st;
81836
83262
  try {
81837
- st = statSync13(path);
83263
+ st = statSync16(path);
81838
83264
  } catch {
81839
83265
  return ["command_missing"];
81840
83266
  }
@@ -81847,9 +83273,9 @@ function probeExecutableIssues(path) {
81847
83273
  return [];
81848
83274
  }
81849
83275
  function probeReadableFileIssues(path) {
81850
- if (!existsSync37(path)) return ["artifact_missing"];
83276
+ if (!existsSync41(path)) return ["artifact_missing"];
81851
83277
  try {
81852
- if (!statSync13(path).isFile()) return ["artifact_not_file"];
83278
+ if (!statSync16(path).isFile()) return ["artifact_not_file"];
81853
83279
  } catch {
81854
83280
  return ["artifact_missing"];
81855
83281
  }
@@ -82193,7 +83619,7 @@ async function main() {
82193
83619
  const result = installSystemdUserService({
82194
83620
  execStart: `${process.execPath} ${execStart}`,
82195
83621
  nodeHome,
82196
- home: process.env.HOME || homedir9(),
83622
+ home: process.env.HOME || homedir10(),
82197
83623
  bindHost: host,
82198
83624
  bindPort: port
82199
83625
  });