@tangle-network/agent-runtime 0.279.0 → 0.281.0

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 (58) hide show
  1. package/dist/{activation-B5NExoOz.d.ts → activation-BcTWDNOD.d.ts} +151 -25
  2. package/dist/{activation-BkUdrvqh.js → activation-C1ZAyQrQ.js} +2 -2
  3. package/dist/{activation-BkUdrvqh.js.map → activation-C1ZAyQrQ.js.map} +1 -1
  4. package/dist/agent.d.ts +1 -1
  5. package/dist/agent.js +2 -2
  6. package/dist/{coordination-driver-BBL_OgRw.js → coordination-driver-B-HOpGN4.js} +2 -2
  7. package/dist/{coordination-driver-BBL_OgRw.js.map → coordination-driver-B-HOpGN4.js.map} +1 -1
  8. package/dist/{delegate-CZc7B-Tl.js → delegate-C1eIBN70.js} +2 -2
  9. package/dist/{delegate-CZc7B-Tl.js.map → delegate-C1eIBN70.js.map} +1 -1
  10. package/dist/durable.d.ts +2 -2
  11. package/dist/durable.js +768 -499
  12. package/dist/durable.js.map +1 -1
  13. package/dist/{graph-Ci8Ufs_f.js → graph-KjJaTmbO.js} +4 -4
  14. package/dist/{graph-Ci8Ufs_f.js.map → graph-KjJaTmbO.js.map} +1 -1
  15. package/dist/{improvement-cycle-Bi43xCVa.js → improvement-cycle-DCTCTmNl.js} +763 -43
  16. package/dist/improvement-cycle-DCTCTmNl.js.map +1 -0
  17. package/dist/{index-Dm8SHDGW.d.ts → index-BSSGMvUd.d.ts} +97 -144
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +8 -8
  20. package/dist/intelligence.d.ts +1 -1
  21. package/dist/intelligence.js +3 -3
  22. package/dist/kernel.d.ts +1 -1
  23. package/dist/kernel.js +8 -8
  24. package/dist/{loop-runner-bin-C9FJshTP.js → loop-runner-bin-Co2eBFbb.js} +3 -3
  25. package/dist/{loop-runner-bin-C9FJshTP.js.map → loop-runner-bin-Co2eBFbb.js.map} +1 -1
  26. package/dist/{loop-runner-bin-D6khQtoC.d.ts → loop-runner-bin-DgeZHpWJ.d.ts} +2 -2
  27. package/dist/loop-runner-bin.d.ts +1 -1
  28. package/dist/loop-runner-bin.js +1 -1
  29. package/dist/mcp/bin.js +3 -3
  30. package/dist/mcp/index.d.ts +1 -1
  31. package/dist/mcp/index.js +4 -4
  32. package/dist/platform.d.ts +34 -36
  33. package/dist/platform.js +122 -29
  34. package/dist/platform.js.map +1 -1
  35. package/dist/{provision-supervisor-BvnihiyB.js → provision-supervisor-CkN8RRjv.js} +3 -3
  36. package/dist/{provision-supervisor-BvnihiyB.js.map → provision-supervisor-CkN8RRjv.js.map} +1 -1
  37. package/dist/{runtime-DsAHXig2.js → runtime-B_yRPBjj.js} +16 -966
  38. package/dist/runtime-B_yRPBjj.js.map +1 -0
  39. package/dist/{server-Dyc1zvug.js → server-dOyG5Tw6.js} +3 -3
  40. package/dist/{server-Dyc1zvug.js.map → server-dOyG5Tw6.js.map} +1 -1
  41. package/dist/{structural-rollout-D-0X6lsU.js → shared-box-CZFdugL1.js} +959 -9
  42. package/dist/shared-box-CZFdugL1.js.map +1 -0
  43. package/dist/{supervise-DBQdrp7H.js → supervise-zt91I62x.js} +3 -3
  44. package/dist/{supervise-DBQdrp7H.js.map → supervise-zt91I62x.js.map} +1 -1
  45. package/dist/{supervisor-DtlPj9me.js → supervisor-UXgaPisu.js} +49 -58
  46. package/dist/supervisor-UXgaPisu.js.map +1 -0
  47. package/dist/testing.d.ts +1 -1
  48. package/dist/testing.js +12 -12
  49. package/dist/tui/index.d.ts +1 -1
  50. package/dist/tui/index.js +1 -1
  51. package/package.json +4 -4
  52. package/skills/profile-authoring/SKILL.md +210 -0
  53. package/skills/supervise/SKILL.md +2 -2
  54. package/dist/improvement-cycle-Bi43xCVa.js.map +0 -1
  55. package/dist/runtime-DsAHXig2.js.map +0 -1
  56. package/dist/structural-rollout-D-0X6lsU.js.map +0 -1
  57. package/dist/supervisor-DtlPj9me.js.map +0 -1
  58. package/skills/supervise/references/profile-authoring.md +0 -19
@@ -1,16 +1,16 @@
1
- import { $i as detachedSnapshot, $n as LEAF_CONTINUATION_TASK, Ai as readCommittedJsonLines, At as createExecutor, Bn as defaultSelectWinner, Bt as InMemoryResultBlobStore, Ca as stringifySafe, Gt as insideCursorNamespace, Hn as runAgentRounds, Ht as SpawnEventIndex, Ia as armDeadlineTimer, Jn as routerBrain, Kn as assertBoxlessPromptOptions, M as withDriverExecutor, Nt as createWorktreeCliExecutor, O as verdictFromJudgeScore, Oa as zeroSpend, Qi as detachedFrozen, Qt as isTraceAnalysisStore, Sa as sleep, Tt as rollingDispatch, Un as createSandboxLineage, Ut as assertContentAddress, Vt as InMemorySpawnJournal, W as settledToIteration, Wn as probeSandboxCapabilities, Wt as encodeResultBlob, Xi as executableAgentProfileSnapshot, Xn as canonicalObservedModelParts, Yn as runBrainLoop, Zi as executableAgentSpecSnapshot, _ as CheckUnavailableError, ai as harnessInvocation, ar as unavailablePauseMs, ga as cloneSpend, ha as chargedTokens, ir as sandboxClientAsProvider, ji as writeAllBytes, jr as contentAddress, jt as createExecutorRegistry, ki as prepareJsonlAppend, ma as addSpend, or as unavailableSignalOfFailure, qn as readPromptOptions, r as createSupervisor, sr as createPushTraceSource, ua as notifySandboxEventObserver, va as hasCompleteCacheBreakdown, xa as randomSuffix, ya as isAbortError } from "./supervisor-DtlPj9me.js";
1
+ import { $i as detachedSnapshot, $t as isTraceAnalysisStore, Ai as readCommittedJsonLines, Ca as stringifySafe, Et as rollingDispatch, Gn as probeSandboxCapabilities, Gt as encodeResultBlob, Ht as InMemorySpawnJournal, Ia as armDeadlineTimer, Jn as readPromptOptions, Kt as insideCursorNamespace, M as withDriverExecutor, Mt as createExecutorRegistry, O as verdictFromJudgeScore, Oa as zeroSpend, Pt as createWorktreeCliExecutor, Qi as detachedFrozen, Sa as sleep, Un as runAgentRounds, Ut as SpawnEventIndex, Vn as defaultSelectWinner, Vt as InMemoryResultBlobStore, W as settledToIteration, Wn as createSandboxLineage, Wt as assertContentAddress, Xi as executableAgentProfileSnapshot, Xn as runBrainLoop, Yn as routerBrain, Zi as executableAgentSpecSnapshot, Zn as canonicalObservedModelParts, _ as CheckUnavailableError, cr as createPushTraceSource, ga as cloneSpend, ha as chargedTokens, ji as writeAllBytes, jt as createExecutor, ki as prepareJsonlAppend, kr as contentAddress, ma as addSpend, qn as assertBoxlessPromptOptions, r as createSupervisor, ua as notifySandboxEventObserver, va as hasCompleteCacheBreakdown, xa as randomSuffix, ya as isAbortError } from "./supervisor-UXgaPisu.js";
2
2
  import { m as ValidationError, n as AnalystError, s as PlannerError } from "./errors-DodWX-cb.js";
3
- import { E as profileChatClient, S as ObservationError, T as renderReport, b as sample, k as strategyAuthorMethod, v as refine, w as observe, x as sampleThenRefine, y as runAgentic } from "./structural-rollout-D-0X6lsU.js";
4
- import { a as concreteProfileModel, c as profileModelExecutionSettings, i as concreteModelId, l as profileProviderModel, o as enforceTokenLimits, t as assertExecutableAgentProfile } from "./model-policy-BbSCSak0.js";
3
+ import { D as sample, E as runAgentic, I as strategyAuthorMethod, M as renderReport, N as profileChatClient, O as sampleThenRefine, T as refine, j as observe, k as ObservationError } from "./shared-box-CZFdugL1.js";
4
+ import { c as profileModelExecutionSettings, i as concreteModelId, l as profileProviderModel, o as enforceTokenLimits, t as assertExecutableAgentProfile } from "./model-policy-BbSCSak0.js";
5
5
  import { C as canonicalCandidateDigest$1, D as immutableCandidateValue, k as sha256Bytes$1, m as captureMaterializedWorkspace } from "./workspace-archive-C9lgf77y.js";
6
6
  import { i as notifyRuntimeHookEvent } from "./runtime-hooks-tXpAarhW.js";
7
7
  import { i as redactProtectedValue, r as redactProtectedReason } from "./protected-redaction-DylCC_ot.js";
8
- import { S as foldCoordinationRecords, y as createInMemoryRunContext } from "./coordination-driver-BBL_OgRw.js";
9
- import { C as gateOnDeliverable, r as supervise, w as mapExecutorResult } from "./supervise-DBQdrp7H.js";
8
+ import { S as foldCoordinationRecords, y as createInMemoryRunContext } from "./coordination-driver-B-HOpGN4.js";
9
+ import { C as gateOnDeliverable, r as supervise, w as mapExecutorResult } from "./supervise-zt91I62x.js";
10
10
  import "./run-layout-vwd4SwX_.js";
11
- import "./provision-supervisor-BvnihiyB.js";
12
- import "./delegate-CZc7B-Tl.js";
13
- import "./graph-Ci8Ufs_f.js";
11
+ import "./provision-supervisor-CkN8RRjv.js";
12
+ import "./delegate-C1eIBN70.js";
13
+ import "./graph-KjJaTmbO.js";
14
14
  import { agentProfileSchema, canonicalAgentProfileDigest, canonicalCandidateDigest, sha256Bytes, validateAgentProfileSecurity } from "@tangle-network/agent-interface";
15
15
  import { CODING_HARNESSES, DEFAULT_TRACE_ANALYST_KINDS, InMemoryTraceStore, OUTPUT_VALUE, benjaminiHochberg, buildTrajectory, computeFindingId as computeFindingId$1, confidenceInterval, createTraceAnalyst, expandProfileAxes, makeFinding as makeFinding$1, pairedBootstrap, paretoFrontier, wilcoxonSignedRank, wilson } from "@tangle-network/agent-eval";
16
16
  import { createAgentRunOutcomeTracker } from "@tangle-network/sandbox/runtime";
@@ -19,13 +19,11 @@ import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
19
19
  import { appendFileSync, chmodSync, constants, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
20
20
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
21
21
  import { cp, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
22
- import { materializeProfile } from "@tangle-network/agent-profile-materialize";
23
22
  import { tmpdir } from "node:os";
24
23
  import { isDeepStrictEqual, promisify } from "node:util";
25
24
  import { collectAgentResponseText } from "@tangle-network/sandbox";
26
25
  import { heldoutSignificance, runProfileMatrix } from "@tangle-network/agent-eval/campaign";
27
26
  import { createInterface } from "node:readline";
28
- import { harnessSystemPromptIntents as harnessSystemPromptIntents$1 } from "@tangle-network/agent-interface/harness-capabilities";
29
27
  import { pathToFileURL } from "node:url";
30
28
  import { gzipSync } from "node:zlib";
31
29
  import { createContext, runInContext } from "node:vm";
@@ -3872,7 +3870,7 @@ function fanout(items, opts) {
3872
3870
  if (res.ok) opened += 1;
3873
3871
  else rejected.push(`${unit.opts.label}: not admitted (${res.reason})`);
3874
3872
  }
3875
- drained = opened === 0 ? emptyRound() : await drain$1(scope);
3873
+ drained = opened === 0 ? emptyRound() : await drain(scope);
3876
3874
  }
3877
3875
  if (opened === 0) return blocked(rejected.length > 0 ? rejected : ["fanout: budget admitted no item (fanout fully rejected)"]);
3878
3876
  if (drained.done.length === 0) return blocked(orderedBlockers(drained.blockers, rejected, "fanout: every item settled without a usable result"));
@@ -4134,7 +4132,7 @@ function splitOnce(text, sep) {
4134
4132
  if (at < 0) return [text, ""];
4135
4133
  return [text.slice(0, at), text.slice(at + sep.length)];
4136
4134
  }
4137
- async function drain$1(scope) {
4135
+ async function drain(scope) {
4138
4136
  const iterations = [];
4139
4137
  const done = [];
4140
4138
  const blockers = [];
@@ -5411,7 +5409,7 @@ async function openSandboxRun(client, options, deliverable) {
5411
5409
  eventTypes: eventTypeCounts(result.events),
5412
5410
  ...result.readError !== void 0 ? { readError: result.readError } : {}
5413
5411
  } : {},
5414
- ...error !== void 0 ? { error: errorMessage$1(error) } : {}
5412
+ ...error !== void 0 ? { error: errorMessage(error) } : {}
5415
5413
  });
5416
5414
  async function settle(box, events, turnIndex, turnKind) {
5417
5415
  const collected = [];
@@ -5519,7 +5517,7 @@ async function openSandboxRun(client, options, deliverable) {
5519
5517
  payload: {
5520
5518
  ...runPayload(),
5521
5519
  turnCount,
5522
- error: errorMessage$1(error)
5520
+ error: errorMessage(error)
5523
5521
  }
5524
5522
  });
5525
5523
  throw error;
@@ -5563,7 +5561,7 @@ async function openSandboxRun(client, options, deliverable) {
5563
5561
  payload: {
5564
5562
  ...runPayload(),
5565
5563
  turnCount,
5566
- error: errorMessage$1(error)
5564
+ error: errorMessage(error)
5567
5565
  }
5568
5566
  });
5569
5567
  throw error;
@@ -5601,956 +5599,8 @@ function hashText(value) {
5601
5599
  }
5602
5600
  return (hash >>> 0).toString(16).padStart(8, "0");
5603
5601
  }
5604
- function errorMessage$1(error) {
5605
- return error instanceof Error ? error.message : String(error);
5606
- }
5607
- //#endregion
5608
- //#region src/runtime/shared-box.ts
5609
- /**
5610
- * Shared-box placement: many harness workers in one Sandbox box, each as its own process.
5611
- *
5612
- * The default provider placement gives every worker its own Sandbox box. A box costs a create
5613
- * call, a sidecar, an egress proxy, a preview link and a memory reservation sized for the box,
5614
- * while one opencode worker uses about half a gigabyte. This module packs workers into a small
5615
- * pool of boxes instead. Each worker runs `opencode run` as a separate process with its own
5616
- * working directory and its own HOME, so each worker has its own harness session, its own
5617
- * materialized profile and its own transcript files.
5618
- *
5619
- * Why processes and not sidecar sessions: the sidecar serves every session in one box from one
5620
- * workspace and writes each session's profile into the same files. Measured 2026-09-24 on
5621
- * sandbox-ff52a989e7c9: eight concurrent sessions with different instructions, and seven of eight
5622
- * answered with another session's instructions. A process with its own directory, its own HOME and
5623
- * `OPENCODE_DISABLE_PROJECT_CONFIG` reads only its own configuration: sixteen of sixteen concurrent
5624
- * workers answered with their own instructions and wrote only into their own directory.
5625
- *
5626
- * The boundary is cooperative, not a security boundary. Every worker runs as the same Linux user,
5627
- * so a worker can read a co-tenant's files through its shell. A profile that writes a repository,
5628
- * runs untrusted code, holds a credential of its own, or must stay blind to its siblings keeps a
5629
- * dedicated box: {@link SharedBoxPlacement.refusal} names the profile features this placement
5630
- * cannot carry, and Runtime then uses the dedicated provider.
5631
- *
5632
- * A worker whose model provider refuses a turn for capacity keeps its process's session and
5633
- * directory, pauses by the driver's rule, and continues the same opencode session, the way a
5634
- * dedicated leaf does (`ProviderExecutorOptions.unavailablePause`). Measured 2026-09-24: 15 of 23
5635
- * down children of play anomaly-referee-v3d ended on `provider_quota_exhausted`, and opencode
5636
- * itself gives up on a refused model after about 70 seconds of its own retries.
5637
- *
5638
- * The workers of one box share the box's router key. Each worker names itself to the router with
5639
- * the `x-tangle-client` header, which the router stores as `clientName` on every usage row, so a
5640
- * keeper can price one worker from the key's per-client rows (`tangle-admin router-spend --key
5641
- * <box key>`). A request without the header stays charged to the box.
5642
- *
5643
- * `workersPerBox` is the one structural cap, and it protects the box. A Tangle box has a fixed
5644
- * 512-task pids limit. An idle box uses about 118 tasks and one opencode worker about 34, so
5645
- * twelve workers reach the limit. At that limit the sidecar's own process spawn fails with EAGAIN
5646
- * and the sidecar restarts, which ends every worker in the box (measured on the same box: 28
5647
- * workers crashed the sidecar twice; 16 workers crashed it once).
5648
- */
5649
- /**
5650
- * The most workers one default box carries at once.
5651
- *
5652
- * It keeps a worker count that the 512-task pids limit of a Tangle box can hold with headroom
5653
- * for the sidecar's own processes and the workers' tool shells: 118 idle tasks plus 8 workers at
5654
- * about 34 tasks each is 390. Memory is not the binding limit: 8 workers used about 4 GB in a
5655
- * 16 GB box. Raise it only for a box whose pids limit was raised with it.
5656
- */
5657
- const DEFAULT_SHARED_BOX_WORKERS = 8;
5658
- /** The box shape the default placement creates: memory for 8 workers at about 0.5 GB each plus
5659
- * the sidecar, and 4 cores, because the workers wait on the model most of the time. */
5660
- const DEFAULT_SHARED_BOX_RESOURCES = Object.freeze({
5661
- cpuCores: 4,
5662
- memoryMB: 8192
5663
- });
5664
- const DEFAULT_WORKER_ROOT = "/home/agent/workers";
5665
- const HARNESS = "opencode";
5666
- const SHARED_PROVIDER_NAME = "tangle-shared-box";
5667
- const DEFAULT_EXEC_TIMEOUT_MS = 12e4;
5668
- /** The request header the router records as a usage row's `clientName`. */
5669
- const ROUTER_CLIENT_HEADER = "x-tangle-client";
5670
- /**
5671
- * The router client name of the worker that runs supervised node `nodeId`.
5672
- *
5673
- * The node id is already in the spawn journal, so a keeper joins a router row to a node without
5674
- * a new record: the row whose `clientName` is `agent-runtime-node/<nodeId>` is that node's spend.
5675
- */
5676
- function sharedWorkerClientName(nodeId) {
5677
- if (!/^[!-~]+$/u.test(nodeId)) throw new ValidationError("sharedWorkerClientName: a node id must be visible ASCII");
5678
- return `agent-runtime-node/${nodeId}`;
5679
- }
5680
- /**
5681
- * Attempts for one Sandbox API call that failed on the way to the box.
5682
- *
5683
- * Every call re-verifies the API key with the platform, and a burst of workers makes that check
5684
- * time out: 8 of 64 workers failed with `502 platform_unavailable` (reason `platform_timeout`) in
5685
- * one burst on 2026-09-24, and 3 more lost their transcript enumeration the same way. The check
5686
- * refuses the request before it reaches the box, so repeating it is safe; five attempts over about
5687
- * fifteen seconds rode out every such failure measured.
5688
- */
5689
- const TRANSIENT_ATTEMPTS = 5;
5690
- const TRANSIENT_BASE_DELAY_MS = 1e3;
5691
- /** A failure that a repeated request can change: the platform, the gateway or the network. */
5692
- function isTransientSandboxFailure(error) {
5693
- if (error === null || typeof error !== "object") return false;
5694
- const record = error;
5695
- if (typeof record.status === "number" && [
5696
- 408,
5697
- 425,
5698
- 429,
5699
- 500,
5700
- 502,
5701
- 503,
5702
- 504
5703
- ].includes(record.status)) return true;
5704
- const text = `${String(record.code ?? "")} ${String(record.name ?? "")} ${String(record.message ?? "")}`;
5705
- return /platform_unavailable|platform_timeout|platform_unreachable|fetch failed|ECONNRESET|ETIMEDOUT|EAI_AGAIN|socket hang up|UND_ERR|bad gateway|gateway timeout/iu.test(text);
5706
- }
5707
- async function transientRetry(operation, options = {}) {
5708
- const delayMs = options.delayMs ?? TRANSIENT_BASE_DELAY_MS;
5709
- for (let attempt = 1;; attempt += 1) try {
5710
- return await operation(attempt);
5711
- } catch (error) {
5712
- if (attempt >= TRANSIENT_ATTEMPTS || !isTransientSandboxFailure(error)) throw error;
5713
- options.signal?.throwIfAborted();
5714
- await sleep(delayMs * 2 ** (attempt - 1), options.signal);
5715
- }
5716
- }
5717
- /**
5718
- * Why a shared box cannot carry `profile`, or `undefined` when it can.
5719
- *
5720
- * A shared box carries a profile whose whole behavior reaches an opencode process through its own
5721
- * working directory, its own configuration and the box's own model credential. Everything else
5722
- * keeps a dedicated box: another harness, a model the box credential cannot reach, a replaced
5723
- * system prompt, MCP servers, connections, hooks, subagents, extensions, tool grants, and any
5724
- * resource the materializer does not lower into the worker's directory.
5725
- */
5726
- function sharedBoxRefusal(profile) {
5727
- if (profile.harness !== HARNESS) return `harness ${String(profile.harness)} is not ${HARNESS}`;
5728
- if (concreteProfileModel(profile) === void 0) return "profile names no concrete model";
5729
- if (profile.model?.provider === void 0) return "profile names no model provider";
5730
- if (profile.model?.reasoningEffort !== void 0) {
5731
- if (tryInvocation(profile) === void 0) return "model.reasoningEffort has no opencode variant";
5732
- }
5733
- if (profile.prompt?.systemPrompt !== void 0) return "prompt.systemPrompt replaces a system prompt";
5734
- for (const field of [
5735
- "mcp",
5736
- "connections",
5737
- "hooks",
5738
- "subagents",
5739
- "extensions",
5740
- "confidential",
5741
- "modes"
5742
- ]) if (profile[field] !== void 0) return `profile declares ${field}`;
5743
- if (profile.tools !== void 0 && Object.keys(profile.tools).length > 0) return "profile grants tools";
5744
- const resources = profile.resources;
5745
- if (resources !== void 0) {
5746
- for (const field of [
5747
- "skills",
5748
- "agents",
5749
- "commands",
5750
- "tools"
5751
- ]) if (resources[field] !== void 0) return `profile declares resources.${field}`;
5752
- for (const file of resources.files ?? []) if (file.resource.kind !== "inline") return `resource file ${file.path} is not inline`;
5753
- if (resources.instructions !== void 0 && typeof resources.instructions !== "string" && resources.instructions.kind !== "inline") return "resources.instructions is not inline";
5754
- }
5755
- let plan;
5756
- try {
5757
- plan = materializeProfile(profile, HARNESS);
5758
- } catch (error) {
5759
- return `opencode materialization failed: ${error instanceof Error ? error.message : String(error)}`;
5760
- }
5761
- if (plan.unsupported.length > 0) return `opencode cannot carry ${plan.unsupported.map((row) => row.dimension).join(", ")}`;
5762
- if (Object.keys(plan.env).length > 0 || plan.flags.length > 0) return "profile needs launch environment or flags";
5763
- for (const file of plan.files) {
5764
- if (file.root === "agent") return `profile writes ${file.relPath} into the harness home`;
5765
- if ((file.secretSlots?.length ?? 0) > 0) return `profile file ${file.relPath} carries a secret`;
5766
- if (isUnsafeRelativePath(file.relPath)) return `profile file ${file.relPath} leaves its directory`;
5767
- }
5768
- }
5769
- function tryInvocation(profile) {
5770
- try {
5771
- return harnessInvocation(HARNESS, invocationProfile(profile), "probe").args;
5772
- } catch {
5773
- return;
5774
- }
5775
- }
5776
- /**
5777
- * The profile the invocation table reads. The prompt reaches opencode through its configuration
5778
- * and the model through the provider this placement declares, so only the reasoning effort is
5779
- * left for the table to lower into `--variant`.
5780
- */
5781
- function invocationProfile(profile) {
5782
- const { prompt: _prompt, model, ...rest } = profile;
5783
- return {
5784
- ...rest,
5785
- ...model?.reasoningEffort === void 0 ? {} : { model: { reasoningEffort: model.reasoningEffort } }
5786
- };
5787
- }
5788
- function isUnsafeRelativePath(path) {
5789
- return path.length === 0 || path.startsWith("/") || path.split("/").some((segment) => segment === ".." || segment === "");
5790
- }
5791
- /**
5792
- * Place accepted workers as processes in a pool of shared Sandbox boxes.
5793
- *
5794
- * The pool fills a box before it creates the next one and deletes a box when its last worker
5795
- * releases it. Runtime reaches this placement through `createExecutor({ backend: 'provider',
5796
- * shared })`: a profile that {@link SharedBoxPlacement.refusal} accepts runs here, and every other
5797
- * profile keeps the dedicated provider.
5798
- */
5799
- function sharedBoxPlacement(options) {
5800
- const workersPerBox = options.workersPerBox ?? 8;
5801
- if (!Number.isSafeInteger(workersPerBox) || workersPerBox < 1) throw new ValidationError("sharedBoxPlacement: workersPerBox must be a positive integer");
5802
- const workerRoot = options.workerRoot ?? DEFAULT_WORKER_ROOT;
5803
- if (!workerRoot.startsWith("/") || isUnsafeRelativePath(workerRoot.slice(1))) throw new ValidationError("sharedBoxPlacement: workerRoot must be an absolute, normalized path");
5804
- const boxOptions = {
5805
- resources: { ...DEFAULT_SHARED_BOX_RESOURCES },
5806
- ...options.box ?? {}
5807
- };
5808
- if (boxOptions.backend !== void 0) throw new ValidationError("sharedBoxPlacement: box.backend is owned by Runtime");
5809
- const identity = {
5810
- id: "shared-box",
5811
- digest: canonicalCandidateDigest({
5812
- kind: "shared-box-placement.v1",
5813
- harness: HARNESS,
5814
- workersPerBox,
5815
- workerRoot,
5816
- box: publicBoxOptions(boxOptions)
5817
- })
5818
- };
5819
- const retryDelayMs = options.retryDelayMs ?? TRANSIENT_BASE_DELAY_MS;
5820
- const pool = createBoxPool(options.client, boxOptions, workersPerBox, retryDelayMs);
5821
- const providerFor = (worker = {}) => {
5822
- const clientName = worker.nodeId === void 0 ? void 0 : sharedWorkerClientName(worker.nodeId);
5823
- return sandboxClientAsProvider({ async create(createOptions) {
5824
- const profile = workerProfile(createOptions);
5825
- const refusal = sharedBoxRefusal(profile);
5826
- if (refusal !== void 0) throw new ValidationError(`sharedBoxPlacement: ${refusal}`);
5827
- assertWorkerCreateOptions(createOptions);
5828
- const lease = await pool.acquire();
5829
- try {
5830
- return await createWorker(lease, workerRoot, profile, {
5831
- retryDelayMs,
5832
- clientName,
5833
- unavailablePause: options.unavailablePause ?? {}
5834
- });
5835
- } catch (error) {
5836
- await lease.release();
5837
- throw error;
5838
- }
5839
- } }, {
5840
- name: SHARED_PROVIDER_NAME,
5841
- capabilities: sharedBoxCapabilities()
5842
- });
5843
- };
5844
- return {
5845
- providerFor,
5846
- identity,
5847
- refusal: sharedBoxRefusal,
5848
- stats: () => pool.stats(),
5849
- close: () => pool.close()
5850
- };
5851
- }
5852
- function publicBoxOptions(box) {
5853
- const { env, secrets, ...rest } = box;
5854
- return {
5855
- ...rest,
5856
- ...env === void 0 ? {} : { environmentVariableNames: Object.keys(env).sort() },
5857
- ...secrets === void 0 ? {} : { secrets }
5858
- };
5859
- }
5860
- function sharedBoxCapabilities() {
5861
- return {
5862
- profile: {
5863
- namedProfiles: false,
5864
- systemPrompt: {
5865
- ...harnessSystemPromptIntents$1(HARNESS),
5866
- replace: false
5867
- },
5868
- instructions: true,
5869
- tools: false,
5870
- permissions: true,
5871
- mcp: false,
5872
- subagents: false,
5873
- resources: {
5874
- files: true,
5875
- instructions: true
5876
- },
5877
- runtimeUpdate: false,
5878
- validation: false
5879
- },
5880
- streaming: {
5881
- live: true,
5882
- replay: false,
5883
- detach: false,
5884
- turnIdempotency: false
5885
- },
5886
- sessions: {
5887
- continue: false,
5888
- list: false,
5889
- messages: false
5890
- },
5891
- workspace: {
5892
- read: true,
5893
- write: true,
5894
- exec: true,
5895
- git: false,
5896
- upload: false,
5897
- download: false
5898
- },
5899
- branching: {
5900
- checkpoint: false,
5901
- fork: false
5902
- },
5903
- placement: true,
5904
- usage: true,
5905
- confidential: false
5906
- };
5907
- }
5908
- function workerProfile(createOptions) {
5909
- const raw = (createOptions?.backend)?.profile;
5910
- const parsed = agentProfileSchema.safeParse(raw);
5911
- if (!parsed.success) throw new ValidationError("sharedBoxPlacement: an exact AgentProfile is required at create");
5912
- return parsed.data;
5913
- }
5914
- /**
5915
- * A worker create carries only what a slot in a shared box can honor. Box-level settings belong
5916
- * to the placement's own box options, and a per-worker request for them is refused rather than
5917
- * dropped: a worker that asked for an environment variable, a repository or its own resources
5918
- * would otherwise run without them and report nothing.
5919
- */
5920
- function assertWorkerCreateOptions(createOptions) {
5921
- const options = createOptions ?? {};
5922
- for (const key of [
5923
- "git",
5924
- "fromSnapshot",
5925
- "fromSandboxId",
5926
- "environment",
5927
- "cwd",
5928
- "resources"
5929
- ]) if (options[key] !== void 0) throw new ValidationError(`sharedBoxPlacement: a shared worker cannot set ${key}`);
5930
- const env = options.env;
5931
- if (env !== void 0 && Object.keys(env).length > 0) throw new ValidationError("sharedBoxPlacement: a shared worker cannot set env");
5932
- const secrets = options.secrets;
5933
- if (Array.isArray(secrets) && secrets.length > 0) throw new ValidationError("sharedBoxPlacement: a shared worker cannot request secrets");
5934
- const backend = options.backend;
5935
- if (backend?.runtimeAttachments !== void 0) throw new ValidationError("sharedBoxPlacement: a shared worker cannot carry runtime attachments");
5936
- if (backend?.type !== void 0 && backend.type !== HARNESS) throw new ValidationError(`sharedBoxPlacement: backend ${String(backend.type)} is not ${HARNESS}`);
5937
- }
5938
- function createBoxPool(client, boxOptions, workersPerBox, retryDelayMs) {
5939
- const poolId = randomUUID();
5940
- const boxes = [];
5941
- const history = [];
5942
- let seq = 0;
5943
- let placed = 0;
5944
- let peak = 0;
5945
- let closing = false;
5946
- const retire = async (entry) => {
5947
- if (entry.retired) return void 0;
5948
- entry.retired = true;
5949
- const index = boxes.indexOf(entry);
5950
- if (index >= 0) boxes.splice(index, 1);
5951
- let handle;
5952
- try {
5953
- handle = await entry.ready;
5954
- } catch {
5955
- return;
5956
- }
5957
- try {
5958
- await transientRetry(() => handle.delete(), { delayMs: retryDelayMs });
5959
- entry.deleted = true;
5960
- return {
5961
- boxId: handle.id,
5962
- deleted: true
5963
- };
5964
- } catch (error) {
5965
- return {
5966
- boxId: handle.id,
5967
- deleted: false,
5968
- error: error instanceof Error ? error.message : String(error)
5969
- };
5970
- }
5971
- };
5972
- return {
5973
- async acquire() {
5974
- if (closing) throw new ValidationError("sharedBoxPlacement: the placement is closed");
5975
- let entry = boxes.find((candidate) => !candidate.retired && candidate.workers < workersPerBox);
5976
- if (entry === void 0) {
5977
- const boxSeq = seq++;
5978
- const request = {
5979
- ...boxOptions,
5980
- name: boxOptions.name ?? `shared-box-${poolId.slice(0, 8)}-${boxSeq}`,
5981
- idempotencyKey: `shared-box-${poolId}-${boxSeq}`,
5982
- backend: {
5983
- type: HARNESS,
5984
- profile: {
5985
- name: "shared-box",
5986
- harness: HARNESS
5987
- }
5988
- }
5989
- };
5990
- const requestedAt = Date.now();
5991
- const created = {
5992
- seq: boxSeq,
5993
- ready: transientRetry(() => client.create(request), { delayMs: retryDelayMs }).then((box) => box),
5994
- workers: 0,
5995
- served: 0,
5996
- retired: false,
5997
- deleted: false
5998
- };
5999
- created.ready.then((box) => {
6000
- created.id = box.id;
6001
- created.createMs = Date.now() - requestedAt;
6002
- }, () => {
6003
- const index = boxes.indexOf(created);
6004
- if (index >= 0) boxes.splice(index, 1);
6005
- created.retired = true;
6006
- });
6007
- boxes.push(created);
6008
- history.push(created);
6009
- entry = created;
6010
- }
6011
- const leased = entry;
6012
- leased.workers += 1;
6013
- leased.served += 1;
6014
- placed += 1;
6015
- peak = Math.max(peak, leased.workers);
6016
- let box;
6017
- try {
6018
- box = await leased.ready;
6019
- } catch (error) {
6020
- leased.workers -= 1;
6021
- throw error;
6022
- }
6023
- let released = false;
6024
- return {
6025
- box,
6026
- async release() {
6027
- if (released) return;
6028
- released = true;
6029
- leased.workers -= 1;
6030
- if (leased.workers === 0 && !closing) await retire(leased);
6031
- }
6032
- };
6033
- },
6034
- stats() {
6035
- return {
6036
- boxesCreated: history.length,
6037
- boxesLive: history.filter((entry) => !entry.retired).length,
6038
- workersPlaced: placed,
6039
- workersLive: history.reduce((sum, entry) => sum + entry.workers, 0),
6040
- peakWorkersPerBox: peak,
6041
- boxes: history.filter((entry) => entry.id !== void 0).map((entry) => ({
6042
- id: entry.id,
6043
- workersServed: entry.served,
6044
- deleted: entry.deleted,
6045
- createMs: entry.createMs ?? 0
6046
- }))
6047
- };
6048
- },
6049
- async close() {
6050
- closing = true;
6051
- return (await Promise.all(history.map((entry) => retire(entry)))).filter((receipt) => receipt !== void 0);
6052
- }
6053
- };
6054
- }
6055
- /** The environment one worker's processes see: its own directory, HOME and XDG roots. */
6056
- function workerEnv(paths) {
6057
- return [
6058
- "OPENCODE_DISABLE_PROJECT_CONFIG=1",
6059
- `OPENCODE_CONFIG_DIR=${paths.dir}/.opencode`,
6060
- `PWD=${paths.dir}`,
6061
- `HOME=${paths.home}`,
6062
- `XDG_DATA_HOME=${paths.home}/.local/share`,
6063
- `XDG_CONFIG_HOME=${paths.home}/.config`,
6064
- `XDG_STATE_HOME=${paths.home}/.local/state`,
6065
- `XDG_CACHE_HOME=${paths.cache}`
6066
- ];
6067
- }
6068
- async function createWorker(lease, root, createProfile, placement) {
6069
- const { clientName, unavailablePause } = placement;
6070
- const box = resilientBox(lease.box, placement.retryDelayMs);
6071
- const workerId = `w-${randomUUID()}`;
6072
- const paths = {
6073
- dir: `${root}/${workerId}`,
6074
- home: `${root}/${workerId}/.home`,
6075
- cache: `${root}/.cache`
6076
- };
6077
- const made = await box.exec(`mkdir -p '${paths.home}' '${paths.cache}'`, { timeoutMs: DEFAULT_EXEC_TIMEOUT_MS });
6078
- if ((made.exitCode ?? 1) !== 0) throw new Error(`sharedBoxPlacement: could not create ${paths.dir}: ${made.stderr ?? ""}`);
6079
- const env = workerEnv(paths);
6080
- let running;
6081
- let turnActive = false;
6082
- let released = false;
6083
- const started = /* @__PURE__ */ new Set();
6084
- const run = async (args, timeoutMs, extraEnv) => {
6085
- const process = await box.spawnInDirectory("/usr/bin/env", [...env, ...args], {
6086
- cwd: paths.dir,
6087
- ...extraEnv === void 0 ? {} : { env: extraEnv },
6088
- timeoutMs
6089
- }, started);
6090
- started.add(process.pid);
6091
- return process;
6092
- };
6093
- const collect = async (process) => {
6094
- const [stdout, stderr, exitCode] = await Promise.all([
6095
- drain(box.output(process, "stdout")),
6096
- drain(box.output(process, "stderr")),
6097
- box.wait(process)
6098
- ]);
6099
- return {
6100
- exitCode,
6101
- stdout,
6102
- stderr
6103
- };
6104
- };
6105
- const resolvePath = (path) => {
6106
- if (path.startsWith("/")) return path;
6107
- if (isUnsafeRelativePath(path)) throw new ValidationError(`sharedBoxPlacement: path ${path} leaves the worker directory`);
6108
- return `${paths.dir}/${path}`;
6109
- };
6110
- /**
6111
- * One turn: one or more opencode runs of the same session. The first runs `message`; a later run
6112
- * continues the session after the model provider refused a run for capacity.
6113
- */
6114
- async function* turn(message, turnProfile, options) {
6115
- const signal = options?.signal;
6116
- const timeoutMs = options?.timeoutMs ?? 0;
6117
- const turnDeadline = timeoutMs > 0 ? Date.now() + timeoutMs : void 0;
6118
- let sessionId;
6119
- const subagents = /* @__PURE__ */ new Set();
6120
- let refusalsInARow = 0;
6121
- let prompt = message;
6122
- for (let invocation = 1;; invocation += 1) {
6123
- const launch = await materializeWorker(box, paths, turnProfile, prompt, options?.backend?.model, clientName, sessionId);
6124
- signal?.throwIfAborted();
6125
- const remainingMs = turnDeadline === void 0 ? 0 : Math.max(1, turnDeadline - Date.now());
6126
- const process = await run(launch.args, remainingMs, { OPENCODE_CONFIG_CONTENT: launch.config });
6127
- running = process;
6128
- const onAbort = () => {
6129
- process.kill("SIGKILL", { tree: true }).catch(() => void 0);
6130
- };
6131
- signal?.addEventListener("abort", onAbort, { once: true });
6132
- const stderr = drain(box.output(process, "stderr"));
6133
- const lines = lineReader();
6134
- const parsed = createOpencodeEventParser(launch.model);
6135
- let exitCode;
6136
- let errorText;
6137
- try {
6138
- for await (const chunk of box.output(process, "stdout")) for (const line of lines.push(chunk)) yield* parsed.line(line);
6139
- for (const line of lines.end()) yield* parsed.line(line);
6140
- exitCode = await box.wait(process);
6141
- errorText = (await stderr).slice(-4e3);
6142
- } finally {
6143
- signal?.removeEventListener("abort", onAbort);
6144
- running = void 0;
6145
- }
6146
- sessionId = parsed.sessionId() ?? sessionId;
6147
- for (const child of parsed.subagentSessionIds()) subagents.add(child);
6148
- const failure = parsed.failure(exitCode, errorText);
6149
- const refusal = failure === void 0 || unavailablePause === false || signal?.aborted ? void 0 : unavailableSignalOfFailure({ error: failure });
6150
- refusalsInARow = parsed.madeProgress() ? 1 : refusalsInARow + 1;
6151
- const pauseMs = refusal === void 0 || unavailablePause === false ? 0 : unavailablePauseMs(refusalsInARow, unavailablePause);
6152
- if (refusal === void 0 || turnDeadline !== void 0 && Date.now() + pauseMs >= turnDeadline) {
6153
- if (sessionId !== void 0) await exportSession(run, collect, paths, sessionId);
6154
- for (const child of subagents) if (child !== sessionId) await exportSession(run, collect, paths, child);
6155
- signal?.throwIfAborted();
6156
- yield* parsed.finish(exitCode, errorText, invocation);
6157
- return;
6158
- }
6159
- await sleep(pauseMs, signal);
6160
- signal?.throwIfAborted();
6161
- if (released) throw new ValidationError("sharedBoxPlacement: this worker was released");
6162
- prompt = sessionId === void 0 ? message : LEAF_CONTINUATION_TASK;
6163
- }
6164
- }
6165
- return {
6166
- id: `${box.id}/${workerId}`,
6167
- name: workerId,
6168
- status: "running",
6169
- metadata: {
6170
- sharedBoxId: box.id,
6171
- workerId,
6172
- workerDir: paths.dir
6173
- },
6174
- async *streamPrompt(message, options) {
6175
- if (released) throw new ValidationError("sharedBoxPlacement: this worker was released");
6176
- if (turnActive) throw new ValidationError("sharedBoxPlacement: a worker runs one turn at a time");
6177
- const turnProfile = options?.backend?.profile === void 0 ? createProfile : agentProfileSchema.parse(options.backend.profile);
6178
- if (canonicalAgentProfileDigest(turnProfile) !== canonicalAgentProfileDigest(createProfile) && sharedBoxRefusal(turnProfile) !== void 0) throw new ValidationError(`sharedBoxPlacement: ${sharedBoxRefusal(turnProfile)}`);
6179
- if (options?.backend?.type !== void 0 && options.backend.type !== HARNESS) throw new ValidationError(`sharedBoxPlacement: backend ${options.backend.type} is not ${HARNESS}`);
6180
- turnActive = true;
6181
- try {
6182
- yield* turn(message, turnProfile, options);
6183
- } finally {
6184
- turnActive = false;
6185
- }
6186
- },
6187
- async exec(command, options) {
6188
- const process = await run([
6189
- "/bin/sh",
6190
- "-c",
6191
- command
6192
- ], options?.timeoutMs ?? DEFAULT_EXEC_TIMEOUT_MS);
6193
- return await collect(process);
6194
- },
6195
- async read(path) {
6196
- return await box.fs.read(resolvePath(path));
6197
- },
6198
- async write(path, content) {
6199
- await box.fs.write(resolvePath(path), content);
6200
- },
6201
- async refresh() {},
6202
- async delete() {
6203
- if (released) return;
6204
- released = true;
6205
- try {
6206
- await running?.kill("SIGKILL", { tree: true });
6207
- await box.exec(`rm -rf '${paths.dir}'`, { timeoutMs: DEFAULT_EXEC_TIMEOUT_MS });
6208
- } finally {
6209
- await lease.release();
6210
- }
6211
- }
6212
- };
6213
- }
6214
- /**
6215
- * The Sandbox calls one worker makes, each repeated on a transient failure.
6216
- *
6217
- * A launch is repeated only after the pool confirms the box did not start it: a failed request
6218
- * may have reached the box before its answer was lost, so the repeat first looks for a process
6219
- * in the worker's own directory that this worker did not start yet, and adopts it. A process log
6220
- * that breaks mid-stream is read again from the start, because the box replays a process's
6221
- * buffered output, and the part already delivered is skipped.
6222
- */
6223
- function resilientBox(box, delayMs) {
6224
- const retry = (operation) => transientRetry(operation, { delayMs });
6225
- return {
6226
- id: box.id,
6227
- exec: (command, options) => retry(() => box.exec(command, options)),
6228
- fs: {
6229
- read: (path) => retry(() => box.fs.read(path)),
6230
- write: (path, content) => retry(() => box.fs.write(path, content))
6231
- },
6232
- async spawnInDirectory(executable, args, options, known) {
6233
- return await transientRetry(async (attempt) => {
6234
- if (attempt > 1) {
6235
- const launched = (await box.process.list()).find((entry) => entry.cwd === options.cwd && entry.command === executable && !known.has(entry.pid));
6236
- const adopted = launched === void 0 ? null : await box.process.get(launched.pid);
6237
- if (adopted !== null) return adopted;
6238
- }
6239
- return await box.process.spawnExact(executable, args, options);
6240
- }, { delayMs });
6241
- },
6242
- async wait(process) {
6243
- return await retry(async () => {
6244
- try {
6245
- return await process.wait();
6246
- } catch (error) {
6247
- if (!isTransientSandboxFailure(error)) throw error;
6248
- const again = await box.process.get(process.pid);
6249
- if (again === null) throw error;
6250
- return await again.wait();
6251
- }
6252
- });
6253
- },
6254
- async *output(process, stream) {
6255
- let delivered = 0;
6256
- let source = process;
6257
- for (let attempt = 1;; attempt += 1) {
6258
- let seen = 0;
6259
- try {
6260
- for await (const chunk of source[stream]()) {
6261
- const fresh = seen + chunk.length <= delivered ? "" : chunk.slice(Math.max(0, delivered - seen));
6262
- seen += chunk.length;
6263
- if (fresh.length > 0) {
6264
- delivered += fresh.length;
6265
- yield fresh;
6266
- }
6267
- }
6268
- return;
6269
- } catch (error) {
6270
- if (attempt >= TRANSIENT_ATTEMPTS || !isTransientSandboxFailure(error)) throw error;
6271
- await sleep(delayMs * 2 ** (attempt - 1));
6272
- source = await retry(() => box.process.get(process.pid)) ?? source;
6273
- }
6274
- }
6275
- },
6276
- delete: () => retry(() => box.delete())
6277
- };
6278
- }
6279
- async function drain(stream) {
6280
- let text = "";
6281
- for await (const chunk of stream) text += chunk;
6282
- return text;
6283
- }
6284
- function lineReader() {
6285
- let pending = "";
6286
- return {
6287
- push(chunk) {
6288
- pending += chunk;
6289
- const lines = pending.split("\n");
6290
- pending = lines.pop() ?? "";
6291
- return lines.filter((line) => line.trim().length > 0);
6292
- },
6293
- end() {
6294
- const rest = pending.trim();
6295
- pending = "";
6296
- return rest.length > 0 ? [rest] : [];
6297
- }
6298
- };
6299
- }
6300
- /**
6301
- * Write the worker's materialized profile into its directory and build its launch.
6302
- *
6303
- * The files come from the same materializer the Sandbox sidecar uses, so a shared worker gets the
6304
- * same instruction file, permissions and resource files a dedicated box would. The generated
6305
- * `opencode.json` is passed as `OPENCODE_CONFIG_CONTENT` with its instruction paths made absolute,
6306
- * because opencode resolves an instruction path in that variable against nothing.
6307
- */
6308
- async function materializeWorker(box, paths, profile, task, turnModel, clientName, continueSession) {
6309
- const refusal = sharedBoxRefusal(profile);
6310
- if (refusal !== void 0) throw new ValidationError(`sharedBoxPlacement: ${refusal}`);
6311
- if (turnModel?.authFiles !== void 0 || turnModel?.authMode === "oauth") throw new ValidationError("sharedBoxPlacement: a shared worker cannot carry auth files");
6312
- const plan = materializeProfile(profile, HARNESS);
6313
- let generated = {};
6314
- for (const file of plan.files) {
6315
- if (file.relPath === "opencode.json" && file.source === "generated") {
6316
- generated = JSON.parse(file.content);
6317
- continue;
6318
- }
6319
- if (continueSession === void 0) await box.fs.write(`${paths.dir}/${file.relPath}`, file.content);
6320
- }
6321
- const instructions = Array.isArray(generated.instructions) ? generated.instructions.map((entry) => typeof entry === "string" && !entry.startsWith("/") ? `${paths.dir}/${entry}` : entry) : void 0;
6322
- const providerId = profile.model.provider;
6323
- const modelId = profileProviderModel(profile);
6324
- const apiKeyEnv = typeof turnModel?.apiKeyEnv === "string" ? turnModel.apiKeyEnv : "OPENCODE_MODEL_API_KEY";
6325
- const baseURL = typeof turnModel?.baseUrl === "string" ? turnModel.baseUrl : "{env:OPENCODE_MODEL_BASE_URL}";
6326
- const config = {
6327
- $schema: "https://opencode.ai/config.json",
6328
- ...generated,
6329
- ...instructions === void 0 ? {} : { instructions },
6330
- permission: {
6331
- external_directory: {
6332
- "*": "deny",
6333
- "/tmp/**": "allow"
6334
- },
6335
- ...generated.permission ?? {}
6336
- },
6337
- provider: { [providerId]: {
6338
- npm: "@ai-sdk/openai-compatible",
6339
- name: providerId,
6340
- options: {
6341
- baseURL,
6342
- apiKey: `{env:${apiKeyEnv}}`,
6343
- ...clientName === void 0 ? {} : { headers: { [ROUTER_CLIENT_HEADER]: clientName } }
6344
- },
6345
- models: { [modelId]: { name: modelId } }
6346
- } }
6347
- };
6348
- const invocation = harnessInvocation(HARNESS, invocationProfile(profile), task, { dangerouslySkipPermissions: true });
6349
- const model = `${providerId}/${modelId}`;
6350
- if (continueSession !== void 0 && !/^[A-Za-z0-9_-]+$/u.test(continueSession)) throw new ValidationError(`sharedBoxPlacement: session id ${continueSession} is not opencode's`);
6351
- return {
6352
- args: [
6353
- invocation.command,
6354
- ...invocation.args,
6355
- ...continueSession === void 0 ? [] : ["--session", continueSession],
6356
- "--format",
6357
- "json",
6358
- "-m",
6359
- model
6360
- ],
6361
- config: JSON.stringify(config),
6362
- model
6363
- };
6364
- }
6365
- /**
6366
- * Save the harness's own record of the session where the transcript capture reads it.
6367
- *
6368
- * Current opencode keeps sessions in a SQLite database, which the capture cannot carry as text.
6369
- * `opencode export` writes the same session as JSON.
6370
- */
6371
- async function exportSession(run, collect, paths, sessionId) {
6372
- if (!SESSION_ID.test(sessionId)) return;
6373
- await collect(await run([
6374
- "/bin/sh",
6375
- "-c",
6376
- "mkdir -p \"$(dirname \"$2\")\" && exec opencode export \"$1\" > \"$2\"",
6377
- "export-session",
6378
- sessionId,
6379
- `${paths.home}/.local/share/opencode/export/${sessionId}.json`
6380
- ], DEFAULT_EXEC_TIMEOUT_MS));
6381
- }
6382
- const SESSION_ID = /^[A-Za-z0-9_-]+$/u;
6383
- /** A subagent's session id in a `task` result (`<task id="ses_…">`) or failure (`task_id: ses_…`). */
6384
- const TASK_SESSION = /(?:<task id="|task_id: )(ses_[A-Za-z0-9]+)/u;
6385
- /**
6386
- * The session a harness subagent ran in, named by its parent's `task` tool part.
6387
- *
6388
- * opencode starts each subagent in a child session and refuses a nested one unless the
6389
- * configuration raises `subagent_depth` above 1, so under the default the parent's parts name
6390
- * every subagent session. A running part carries the id in `state.metadata.sessionId`; a finished
6391
- * one also carries it in its output, and a failed one in its error.
6392
- */
6393
- function subagentSessionOf(part) {
6394
- if (part.type !== "tool" || part.tool !== "task") return void 0;
6395
- const state = part.state;
6396
- if (state === void 0 || state === null || typeof state !== "object") return void 0;
6397
- const { metadata, output, error } = state;
6398
- const named = metadata !== null && typeof metadata === "object" ? metadata.sessionId : void 0;
6399
- if (typeof named === "string" && SESSION_ID.test(named)) return named;
6400
- for (const text of [output, error]) {
6401
- const match = typeof text === "string" ? TASK_SESSION.exec(text) : null;
6402
- if (match !== null) return match[1];
6403
- }
6404
- }
6405
- /**
6406
- * Translate opencode's JSON event lines into the Sandbox event wire the provider executor reads.
6407
- *
6408
- * Every part becomes `message.part.updated`, which is how the sidecar forwards opencode parts, so
6409
- * the existing trace and progress readers decode them unchanged. Each `step-finish` part also
6410
- * becomes one canonical `llm_call` receipt with its token counts. opencode reports no dollar
6411
- * receipt for a custom provider, so every receipt says the cost is unknown and Runtime prices the
6412
- * tokens as an estimate instead of a bill.
6413
- */
6414
- function createOpencodeEventParser(model) {
6415
- let sessionId;
6416
- let finalMessageId;
6417
- const textByMessage = /* @__PURE__ */ new Map();
6418
- const subagentSessions = /* @__PURE__ */ new Set();
6419
- const errors = [];
6420
- let malformed = 0;
6421
- let spentTokens = false;
6422
- const failureOf = (exitCode, stderr) => {
6423
- if (exitCode !== 0) {
6424
- const said = [...errors, stderr.trim().slice(-1e3)].filter((text) => text.length > 0);
6425
- return `opencode exited ${exitCode}${said.length > 0 ? `: ${said.join("; ")}` : ""}`;
6426
- }
6427
- return errors.length > 0 && finalMessageId === void 0 ? errors.join("; ") : void 0;
6428
- };
6429
- return {
6430
- sessionId: () => sessionId,
6431
- subagentSessionIds: () => [...subagentSessions],
6432
- /** Why this run failed, or `undefined` when it answered. */
6433
- failure: failureOf,
6434
- /** Whether this run spent any model tokens before it ended. */
6435
- madeProgress: () => spentTokens,
6436
- *line(raw) {
6437
- let event;
6438
- try {
6439
- event = JSON.parse(raw);
6440
- } catch {
6441
- malformed += 1;
6442
- return;
6443
- }
6444
- if (typeof event.sessionID === "string") sessionId ??= event.sessionID;
6445
- if (event.type === "error") {
6446
- const message = errorMessage(event.error);
6447
- errors.push(message);
6448
- yield {
6449
- type: "error",
6450
- data: { message }
6451
- };
6452
- return;
6453
- }
6454
- const part = event.part;
6455
- if (part === void 0 || part === null || typeof part !== "object") return;
6456
- const subagent = subagentSessionOf(part);
6457
- if (subagent !== void 0) subagentSessions.add(subagent);
6458
- yield {
6459
- type: "message.part.updated",
6460
- data: { part }
6461
- };
6462
- const messageId = typeof part.messageID === "string" ? part.messageID : void 0;
6463
- if (part.type === "text" && messageId !== void 0 && typeof part.text === "string") {
6464
- const texts = textByMessage.get(messageId) ?? [];
6465
- texts.push(part.text);
6466
- textByMessage.set(messageId, texts);
6467
- }
6468
- if (part.type === "step-finish") {
6469
- if (messageId !== void 0) finalMessageId = messageId;
6470
- const receipt = stepReceipt(part, model);
6471
- if (receipt !== void 0) {
6472
- const data = receipt.data;
6473
- if ((data?.tokensIn ?? 0) > 0 || (data?.tokensOut ?? 0) > 0) spentTokens = true;
6474
- yield receipt;
6475
- }
6476
- }
6477
- },
6478
- /** The turn's terminal frames, from its last run. `runs` counts the runs of the session. */
6479
- *finish(exitCode, stderr, runs = 1) {
6480
- const finalText = (finalMessageId === void 0 ? void 0 : textByMessage.get(finalMessageId)?.join("")) ?? "";
6481
- const failure = failureOf(exitCode, stderr);
6482
- const counted = {
6483
- ...malformed > 0 ? { malformedLines: malformed } : {},
6484
- ...runs > 1 ? { sessionRuns: runs } : {}
6485
- };
6486
- if (failure !== void 0) {
6487
- yield {
6488
- type: "result",
6489
- data: {
6490
- success: false,
6491
- status: "failed",
6492
- error: failure,
6493
- finalText,
6494
- ...counted
6495
- }
6496
- };
6497
- yield {
6498
- type: "done",
6499
- data: { outcome: { type: "failed" } }
6500
- };
6501
- return;
6502
- }
6503
- yield {
6504
- type: "result",
6505
- data: {
6506
- success: true,
6507
- finalText,
6508
- ...counted
6509
- }
6510
- };
6511
- yield {
6512
- type: "done",
6513
- data: { outcome: { type: "completed" } }
6514
- };
6515
- }
6516
- };
6517
- }
6518
- function stepReceipt(part, model) {
6519
- const tokens = part.tokens;
6520
- if (tokens === void 0 || tokens === null || typeof tokens !== "object") return void 0;
6521
- const count = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
6522
- const cache = tokens.cache ?? {};
6523
- const input = count(tokens.input);
6524
- const output = count(tokens.output);
6525
- const reasoning = count(tokens.reasoning) ?? 0;
6526
- const cacheRead = count(cache.read) ?? 0;
6527
- const cacheWrite = count(cache.write) ?? 0;
6528
- const known = input !== void 0 && output !== void 0;
6529
- return {
6530
- type: "llm_call",
6531
- ...typeof part.id === "string" ? { id: part.id } : {},
6532
- data: {
6533
- usageMode: "delta",
6534
- model,
6535
- ...known ? {
6536
- tokensIn: input + cacheRead + cacheWrite,
6537
- tokensOut: output + reasoning,
6538
- ...reasoning > 0 ? { reasoningTokens: reasoning } : {}
6539
- } : { tokensKnown: false },
6540
- costKnown: false
6541
- }
6542
- };
6543
- }
6544
5602
  function errorMessage(error) {
6545
- if (typeof error === "string") return error;
6546
- if (error !== null && typeof error === "object") {
6547
- const record = error;
6548
- const data = record.data;
6549
- const text = typeof data?.message === "string" ? data.message : typeof record.message === "string" ? record.message : typeof record.name === "string" ? record.name : "opencode reported an error";
6550
- const status = data?.statusCode;
6551
- return typeof status === "number" && Number.isSafeInteger(status) ? `${text} (status code ${status})` : text;
6552
- }
6553
- return "opencode reported an error";
5603
+ return error instanceof Error ? error.message : String(error);
6554
5604
  }
6555
5605
  //#endregion
6556
5606
  //#region src/runtime/authored-code.ts
@@ -9191,6 +8241,6 @@ function tail(s) {
9191
8241
  return s.slice(-400);
9192
8242
  }
9193
8243
  //#endregion
9194
- export { equalKOnCost as $, SqlResultBlobStore as $t, unsafeInProcessRunner as A, loopDispatch as At, strategyAuthorContract as B, deterministicCompletion as Bt, withUntrackedArtifacts as C, HarvestError as Ct, patchDelivered as D, localSandboxClient as Dt, createSqlRunContext as E, resolveSandboxClient as Et, pickChampion as F, declaredCheckDigest as Ft, sharedBoxPlacement as G, renderLeaderboardHtml as Gt, DEFAULT_SHARED_BOX_RESOURCES as H, stopSentinel as Ht, runStrategyEvolution as I, declaredCheckJudge as It, SandboxRunAbortError as J, renderPairwiseMarkdown as Jt, sharedBoxRefusal as K, renderLeaderboardMarkdown as Kt, selectChampion as L, readDeclaredCheck as Lt, chatWorkerSeam as M, assertDeclaredCheck as Mt, createChatSessionStore as N, checkProgramDigest as Nt, runCoderChecks as O, inlineSandboxClient as Ot, discriminatingMeans as P, declaredCheckDeliverable as Pt, promotionGate as Q, openSqlRunStore as Qt, assertStrategyContract as R, runIsolatedCheck as Rt, copyUntrackedIntoClone as S, inProcessSandboxClient as St, createFencedSqlRunContext as T, defineLeaderboard as Tt, DEFAULT_SHARED_BOX_WORKERS as U, leaderboard as Ut, strategyAuthorSystemPrompt as V, sentinelCompletion as Vt, ROUTER_CLIENT_HEADER as W, pairwiseSignificance as Wt, printBenchmarkReport as X, defaultAuditorInstruction as Xt, openSandboxRun as Y, auditIntent as Yt, runBenchmark as Z, SqlRunOwnershipError as Zt, NOTE_MAX_CHARS as _, assertTraceDerivedFindings as _t, localShell as a, sanitizeMcpToolSchema as an, registerShape as at, composeWorkerEvidence as b, registryScopeAnalyst as bt, createVerifierEnvironment as c, resolveMcpServerLaunch as cn, renderCorpusToInstructions as ct, harvestSurfaceDiffs as d, createTangleSandboxExactProcessProvider as dn, loopUntil as dt, SqlSpawnJournal as en, trajectoryReport as et, analystsFromRegistry as f, panel as ft, EVIDENCE_MAX_CHARS as g, widen as gt, worktreeFanout as h, verify as ht, jjWorkspace as i, createMcpEnvironment as in, createShapeRegistry as it, chatTransportExecutor as j, superviseDispatch as jt, codeModeSupervisorTools as k, loopCampaignDispatch as kt, boxSurfaceReader as l, resolveSecretEnv as ln, fanout as lt, superviseSurface as m, selectValidWinner as mt, makeFinding$1 as n, connectStdioMcp as nn, runPersonified as nt, runInWorkspace as o, envKeyProvider as on, FileCorpus as ot, failuresAnalyst as p, pipeline as pt, sharedWorkerClientName as q, renderLeaderboardSvg as qt, gitWorkspace as r, materializeLocalMcp as rn, builtinShapes as rt, createWaterfallCollector as s, mcpSecretEnvMetadataKey as sn, InMemoryCorpus as st, computeFindingId$1 as t, McpSpawnFault as tn, definePersona as tt, fsSurfaceReader as u, secretEnvOfMcpServer as un, flatWidenGate as ut, VERIFY_TAIL_CHARS as v, buildSteerContext as vt, analyzeTrace as w, harvestCorpus as wt, settledWorkerOut as x, observationFromRegistry as xt, closingWorkerNote as y, createScopeAnalyst as yt, authorStrategy as z, completionAuthorizes as zt };
8244
+ export { registerShape as $, sanitizeMcpToolSchema as $t, unsafeInProcessRunner as A, declaredCheckJudge as At, strategyAuthorContract as B, renderLeaderboardMarkdown as Bt, withUntrackedArtifacts as C, loopCampaignDispatch as Ct, patchDelivered as D, checkProgramDigest as Dt, createSqlRunContext as E, assertDeclaredCheck as Et, pickChampion as F, sentinelCompletion as Ft, runBenchmark as G, SqlRunOwnershipError as Gt, SandboxRunAbortError as H, renderPairwiseMarkdown as Ht, runStrategyEvolution as I, stopSentinel as It, trajectoryReport as J, SqlSpawnJournal as Jt, promotionGate as K, openSqlRunStore as Kt, selectChampion as L, leaderboard as Lt, chatWorkerSeam as M, runIsolatedCheck as Mt, createChatSessionStore as N, completionAuthorizes as Nt, runCoderChecks as O, declaredCheckDeliverable as Ot, discriminatingMeans as P, deterministicCompletion as Pt, createShapeRegistry as Q, createMcpEnvironment as Qt, assertStrategyContract as R, pairwiseSignificance as Rt, copyUntrackedIntoClone as S, inlineSandboxClient as St, createFencedSqlRunContext as T, superviseDispatch as Tt, openSandboxRun as U, auditIntent as Ut, strategyAuthorSystemPrompt as V, renderLeaderboardSvg as Vt, printBenchmarkReport as W, defaultAuditorInstruction as Wt, runPersonified as X, connectStdioMcp as Xt, definePersona as Y, McpSpawnFault as Yt, builtinShapes as Z, materializeLocalMcp as Zt, NOTE_MAX_CHARS as _, HarvestError as _t, localShell as a, createTangleSandboxExactProcessProvider as an, loopUntil as at, composeWorkerEvidence as b, resolveSandboxClient as bt, createVerifierEnvironment as c, selectValidWinner as ct, harvestSurfaceDiffs as d, assertTraceDerivedFindings as dt, envKeyProvider as en, FileCorpus as et, analystsFromRegistry as f, buildSteerContext as ft, EVIDENCE_MAX_CHARS as g, inProcessSandboxClient as gt, worktreeFanout as h, observationFromRegistry as ht, jjWorkspace as i, secretEnvOfMcpServer as in, flatWidenGate as it, chatTransportExecutor as j, readDeclaredCheck as jt, codeModeSupervisorTools as k, declaredCheckDigest as kt, boxSurfaceReader as l, verify as lt, superviseSurface as m, registryScopeAnalyst as mt, makeFinding$1 as n, resolveMcpServerLaunch as nn, renderCorpusToInstructions as nt, runInWorkspace as o, panel as ot, failuresAnalyst as p, createScopeAnalyst as pt, equalKOnCost as q, SqlResultBlobStore as qt, gitWorkspace as r, resolveSecretEnv as rn, fanout as rt, createWaterfallCollector as s, pipeline as st, computeFindingId$1 as t, mcpSecretEnvMetadataKey as tn, InMemoryCorpus as tt, fsSurfaceReader as u, widen as ut, VERIFY_TAIL_CHARS as v, harvestCorpus as vt, analyzeTrace as w, loopDispatch as wt, settledWorkerOut as x, localSandboxClient as xt, closingWorkerNote as y, defineLeaderboard as yt, authorStrategy as z, renderLeaderboardHtml as zt };
9195
8245
 
9196
- //# sourceMappingURL=runtime-DsAHXig2.js.map
8246
+ //# sourceMappingURL=runtime-B_yRPBjj.js.map