@jmanuelcorral/openteam 0.21.0 → 0.22.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 (36) hide show
  1. package/README.es.md +32 -1
  2. package/README.md +30 -1
  3. package/dist/certificates/graph-release-certificate.json +2 -2
  4. package/dist/certificates/graph-shadow-certificate.json +2 -2
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +3706 -1023
  7. package/dist/commands/dispatch.d.ts +9 -4
  8. package/dist/commands/dispatch.d.ts.map +1 -1
  9. package/dist/commands/doctor.d.ts +2 -0
  10. package/dist/commands/doctor.d.ts.map +1 -1
  11. package/dist/commands/setup.d.ts +137 -4
  12. package/dist/commands/setup.d.ts.map +1 -1
  13. package/dist/commands/types.d.ts +4 -0
  14. package/dist/commands/types.d.ts.map +1 -1
  15. package/dist/commands/upgrade.d.ts +42 -0
  16. package/dist/commands/upgrade.d.ts.map +1 -0
  17. package/dist/config/pluginSpec.d.ts +43 -0
  18. package/dist/config/pluginSpec.d.ts.map +1 -0
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2809 -911
  22. package/dist/local/lemonade.d.ts.map +1 -1
  23. package/dist/local/openai-compatible.d.ts.map +1 -1
  24. package/dist/local/types.d.ts +16 -0
  25. package/dist/local/types.d.ts.map +1 -1
  26. package/dist/messages/commands.d.ts +9 -0
  27. package/dist/messages/commands.d.ts.map +1 -1
  28. package/dist/messages/executionSetup.d.ts +43 -0
  29. package/dist/messages/executionSetup.d.ts.map +1 -1
  30. package/dist/messages/index.d.ts +1 -0
  31. package/dist/messages/index.d.ts.map +1 -1
  32. package/dist/messages/upgrade.d.ts +129 -0
  33. package/dist/messages/upgrade.d.ts.map +1 -0
  34. package/dist/plugin/commandTool.d.ts +4 -4
  35. package/dist/plugin/commandTool.d.ts.map +1 -1
  36. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@ import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
4
  // src/index.ts
5
- import { readFile as readFile3 } from "node:fs/promises";
6
- import { basename as basename3, dirname as dirname5 } from "node:path";
5
+ import { readFile as readFile4 } from "node:fs/promises";
6
+ import { basename as basename4, dirname as dirname6 } from "node:path";
7
7
 
8
8
  // src/capabilities/curated.ts
9
9
  var CURATED_FRONTIER_PROFILES = [
@@ -5389,161 +5389,2040 @@ function createPurgeAdapter(root) {
5389
5389
  };
5390
5390
  }
5391
5391
 
5392
- // src/config/graphFeatureGate.ts
5393
- function checkCertificate(status, name) {
5394
- if (status.status === "absent") {
5395
- return {
5396
- code: `${name}-certificate-absent`,
5397
- detail: `${name} certificate not found — run the ${name} certification suite to produce it`
5398
- };
5399
- }
5400
- if (status.status === "invalid") {
5401
- return {
5402
- code: `${name}-certificate-invalid`,
5403
- detail: `${name} certificate rejected by recomputing parser: ${status.code} — ${status.detail}`
5404
- };
5392
+ // src/commands/upgrade.ts
5393
+ import { execFile as execFile2 } from "node:child_process";
5394
+ import { createHash as createHash2, randomUUID } from "node:crypto";
5395
+ import {
5396
+ chmod,
5397
+ lstat,
5398
+ mkdir as mkdir2,
5399
+ open,
5400
+ readFile as readFile2,
5401
+ rename,
5402
+ rm as rm2
5403
+ } from "node:fs/promises";
5404
+ import { basename as basename2, dirname as dirname4, join as join9, relative, resolve as resolve3, win32 } from "node:path";
5405
+ import { fileURLToPath } from "node:url";
5406
+ import { promisify as promisify2 } from "node:util";
5407
+ import { z as z15 } from "zod";
5408
+ // package.json
5409
+ var package_default = {
5410
+ name: "@jmanuelcorral/openteam",
5411
+ version: "0.22.0",
5412
+ packageManager: "bun@1.3.14",
5413
+ description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
5414
+ license: "MIT",
5415
+ author: "Jose Manuel Corral",
5416
+ repository: {
5417
+ type: "git",
5418
+ url: "git+https://github.com/jmanuelcorral/openteam.git"
5419
+ },
5420
+ homepage: "https://github.com/jmanuelcorral/openteam#readme",
5421
+ bugs: {
5422
+ url: "https://github.com/jmanuelcorral/openteam/issues"
5423
+ },
5424
+ keywords: [
5425
+ "opencode",
5426
+ "opencode-plugin",
5427
+ "llm",
5428
+ "routing",
5429
+ "local-llm",
5430
+ "ollama",
5431
+ "lm-studio",
5432
+ "foundry-local",
5433
+ "cost-optimization",
5434
+ "multi-agent"
5435
+ ],
5436
+ engines: {
5437
+ bun: ">=1.3",
5438
+ node: "^22.22.2 || ^24.15.0 || >=26.0.0"
5439
+ },
5440
+ type: "module",
5441
+ main: "./dist/index.js",
5442
+ module: "./dist/index.js",
5443
+ types: "./dist/index.d.ts",
5444
+ bin: {
5445
+ openteam: "./dist/cli.js"
5446
+ },
5447
+ exports: {
5448
+ ".": {
5449
+ types: "./dist/index.d.ts",
5450
+ import: "./dist/index.js"
5451
+ },
5452
+ "./package.json": "./package.json"
5453
+ },
5454
+ files: [
5455
+ "dist",
5456
+ "README.md",
5457
+ "LICENSE",
5458
+ "AGENTS.md",
5459
+ ".opencode/openteam.example.json",
5460
+ ".opencode/command/openteam.md"
5461
+ ],
5462
+ publishConfig: {
5463
+ access: "public",
5464
+ registry: "https://registry.npmjs.org/"
5465
+ },
5466
+ trustedDependencies: [],
5467
+ sideEffects: false,
5468
+ scripts: {
5469
+ prebuild: "bun run clean",
5470
+ build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
5471
+ "build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
5472
+ "build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
5473
+ "build:types": "tsc -p tsconfig.build.json",
5474
+ clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
5475
+ test: "bun test",
5476
+ "test:cov": "bun test --coverage",
5477
+ "certify:shadow": "bun run scripts/certify-artifact.ts shadow",
5478
+ "certify:release": "bun run scripts/certify-artifact.ts release",
5479
+ "build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
5480
+ "coverage:check": "node scripts/check-coverage.mjs",
5481
+ typecheck: "tsc --noEmit",
5482
+ lint: "biome check .",
5483
+ "format:check": "biome format .",
5484
+ "docs:install": "cd docs && bun install --frozen-lockfile --ignore-scripts",
5485
+ "docs:dev": "cd docs && bun run docs:dev",
5486
+ "docs:build": "cd docs && bun run docs:build",
5487
+ "docs:preview": "cd docs && bun run docs:preview",
5488
+ prepublishOnly: "bun run build",
5489
+ "link:local": "bun run build && npm link",
5490
+ "hooks:install": "git config core.hooksPath .githooks"
5491
+ },
5492
+ dependencies: {
5493
+ "@clack/prompts": "1.7.0",
5494
+ "@langchain/core": "1.2.9",
5495
+ "@langchain/langgraph": "1.4.12",
5496
+ "@opencode-ai/plugin": "1.18.19",
5497
+ "@opencode-ai/sdk": "1.18.19",
5498
+ "@opentelemetry/api": "1.9.1",
5499
+ "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
5500
+ "@opentelemetry/resources": "2.10.0",
5501
+ "@opentelemetry/sdk-trace-base": "2.10.0",
5502
+ zod: "4.4.3"
5503
+ },
5504
+ devDependencies: {
5505
+ "@biomejs/biome": "2.5.9",
5506
+ "@types/bun": "1.3.14",
5507
+ typescript: "7.0.2"
5405
5508
  }
5406
- return;
5407
- }
5408
- function evaluateGraphGate(input) {
5409
- const violations = [];
5410
- const advisories = [];
5411
- const shadowV = checkCertificate(input.shadow, "shadow");
5412
- if (shadowV !== undefined)
5413
- violations.push(shadowV);
5414
- const releaseV = checkCertificate(input.release, "release");
5415
- if (releaseV !== undefined)
5416
- violations.push(releaseV);
5417
- const soakV = checkCertificate(input.soak, "soak");
5418
- if (soakV !== undefined)
5419
- advisories.push(soakV);
5420
- if (input.migration === "pending") {
5421
- violations.push({
5422
- code: "migration-pending",
5423
- detail: "legacy migration has pending work — run the migration tool to completion before enabling active mode"
5424
- });
5425
- } else if (input.migration === "unknown") {
5426
- violations.push({
5427
- code: "migration-unknown",
5428
- detail: "could not determine migration status — check ledger readability and storage access"
5429
- });
5509
+ };
5510
+
5511
+ // src/version.ts
5512
+ var PACKAGE_VERSION = package_default.version;
5513
+
5514
+ // src/config/pluginSpec.ts
5515
+ var OPENTEAM_PACKAGE_NAME = "@jmanuelcorral/openteam";
5516
+ function pinnedPluginSpec(version) {
5517
+ const v = version ?? PACKAGE_VERSION;
5518
+ return `${OPENTEAM_PACKAGE_NAME}@${v}`;
5519
+ }
5520
+ var STRICT_SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
5521
+ function parseStrictSemverParts(version) {
5522
+ const match = STRICT_SEMVER_RE.exec(version);
5523
+ if (match === null) {
5524
+ return;
5430
5525
  }
5431
- if (!input.operatorApproval) {
5432
- violations.push({
5433
- code: "operator-approval-missing",
5434
- detail: "set graph.operatorApproval to true in .opencode/openteam.json after reviewing all certificates and completing migration — this is a declaration of intent, not a proof"
5435
- });
5526
+ const major = Number(match[1]);
5527
+ const minor = Number(match[2]);
5528
+ const patch = Number(match[3]);
5529
+ if (!Number.isSafeInteger(major) || !Number.isSafeInteger(minor) || !Number.isSafeInteger(patch)) {
5530
+ return;
5436
5531
  }
5437
- return {
5438
- allowed: violations.length === 0,
5439
- violations,
5440
- advisories
5441
- };
5442
- }
5443
- function isGateApplicable(mode) {
5444
- return mode === "active";
5532
+ return [major, minor, patch];
5445
5533
  }
5446
-
5447
- // src/config/opencode.ts
5448
- function stripJsoncComments(src) {
5449
- const out = [];
5450
- let i = 0;
5451
- while (i < src.length) {
5452
- const ch = src[i];
5453
- if (ch === undefined)
5454
- break;
5455
- if (ch === '"') {
5456
- out.push(ch);
5457
- i++;
5458
- while (i < src.length) {
5459
- const c = src[i];
5460
- if (c === undefined)
5461
- break;
5462
- out.push(c);
5463
- i++;
5464
- if (c === "\\") {
5465
- const escaped = src[i];
5466
- if (escaped !== undefined) {
5467
- out.push(escaped);
5468
- i++;
5469
- }
5470
- } else if (c === '"') {
5471
- break;
5472
- }
5473
- }
5474
- } else if (ch === "/" && i + 1 < src.length) {
5475
- if (src[i + 1] === "/") {
5476
- i += 2;
5477
- while (i < src.length && src[i] !== `
5478
- `) {
5479
- i++;
5480
- }
5481
- } else if (src[i + 1] === "*") {
5482
- i += 2;
5483
- while (i < src.length && !(src[i] === "*" && src[i + 1] === "/")) {
5484
- i++;
5485
- }
5486
- if (i < src.length) {
5487
- i += 2;
5488
- }
5489
- } else {
5490
- out.push(ch);
5491
- i++;
5492
- }
5493
- } else {
5494
- out.push(ch);
5495
- i++;
5496
- }
5534
+ function readStrictSemverParts(version) {
5535
+ const parsed = parseStrictSemverParts(version);
5536
+ if (parsed === undefined) {
5537
+ throw new Error(`invalid-strict-semver:${version}`);
5497
5538
  }
5498
- return out.join("");
5539
+ return parsed;
5499
5540
  }
5500
- function isRecord3(v) {
5501
- return typeof v === "object" && v !== null && !Array.isArray(v);
5541
+ function isStrictSemver(v) {
5542
+ return parseStrictSemverParts(v) !== undefined;
5502
5543
  }
5503
- var OPENCODE_CONFIG_CANDIDATES = [
5504
- ".opencode/opencode.json",
5505
- "opencode.json"
5506
- ];
5507
- function mergeOpencodeConfigs(base, override) {
5508
- const result = { ...base };
5509
- for (const [key, value] of Object.entries(override)) {
5510
- const baseVal = result[key];
5511
- if (isRecord3(baseVal) && isRecord3(value)) {
5512
- result[key] = mergeOpencodeConfigs(baseVal, value);
5513
- } else {
5514
- result[key] = value;
5515
- }
5516
- }
5517
- return result;
5544
+ function compareSemver(a, b) {
5545
+ const [aM, am, ap] = readStrictSemverParts(a);
5546
+ const [bM, bm, bp] = readStrictSemverParts(b);
5547
+ if (aM !== bM)
5548
+ return aM - bM;
5549
+ if (am !== bm)
5550
+ return am - bm;
5551
+ return ap - bp;
5518
5552
  }
5519
5553
 
5520
- // src/graph/certificate.ts
5521
- import { z as z16 } from "zod";
5522
-
5523
- // src/contract/opencode.ts
5524
- var SUPPORTED_OPENCODE_VERSIONS = [
5525
- "1.17.13",
5526
- "1.18.18",
5527
- "1.18.19"
5528
- ];
5529
- var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
5530
- function parseSemver(version) {
5531
- const normalized = version.trim().replace(/^[=^~v]+/, "");
5532
- const match = SEMVER_RE.exec(normalized);
5533
- if (match === null) {
5534
- throw new Error(`invalid semver: "${version}"`);
5535
- }
5536
- return {
5537
- major: Number(match[1]),
5538
- minor: Number(match[2]),
5539
- patch: Number(match[3])
5540
- };
5541
- }
5542
- function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
5543
- if (certifiedVersion === liveVersion) {
5544
- return true;
5545
- }
5546
- let certified;
5554
+ // src/messages/commands.ts
5555
+ var clearCacheMessages = {
5556
+ header: "openteam clear-cache — frozen plugin cache entries:",
5557
+ columns: {
5558
+ specDir: "spec dir",
5559
+ pinned: "spec-pinned",
5560
+ installed: "installed",
5561
+ mtime: "mtime"
5562
+ },
5563
+ reparseSkipSuffix: " [SKIP reparse point]",
5564
+ deletedLabel: "deleted.",
5565
+ lockedLabel: (message) => `[LOCKED] ${message}`,
5566
+ pathOutsideWarning: (specDir, absolutePath) => ` ⚠ ${specDir}: path outside cacheRoot (${absolutePath}), skipped.`,
5567
+ processed: (count) => `${count} entry(ies) processed.`,
5568
+ found: (count) => `${count} entry(ies) found. Use --delete to remove them.`
5569
+ };
5570
+ var baselineMessages = {
5571
+ effectiveAuto: "cheapest-capable (auto)",
5572
+ pinnedSuffix: (ref) => `${ref} (pinned)`,
5573
+ summary: (params) => [
5574
+ "openteam baseline:",
5575
+ ` mode: ${params.mode}`,
5576
+ ` pinned: ${params.pinned}`,
5577
+ ` hardDefault: ${params.hardDefault}`,
5578
+ ` effective: ${params.effective}`
5579
+ ],
5580
+ invalidModel: (input) => `Invalid model "${input}". Use the provider/model format, e.g. anthropic/claude-sonnet-4-5.`,
5581
+ pinnedTo: (ref) => `Baseline pinned to ${ref} (pinned mode).`,
5582
+ autoMode: "Baseline set to auto mode (cheapest-capable)."
5583
+ };
5584
+ var localMessages = {
5585
+ help: {
5586
+ status: " openteam local status Show the execution mode (local / frontier / mixed)",
5587
+ off: " openteam local off Use frontier models only",
5588
+ only: " openteam local only Use local models only",
5589
+ on: " openteam local on Enable mixed local/frontier execution"
5590
+ },
5591
+ noRuntimes: "none",
5592
+ modeFrontier: "frontier",
5593
+ modeLocal: "local",
5594
+ modeMixed: "mixed",
5595
+ summary: (params) => [
5596
+ "openteam local:",
5597
+ ` execution mode: ${params.mode}`,
5598
+ ` local runtimes: ${params.runtimes}`
5599
+ ],
5600
+ frontierOnlyNoChange: "No change: execution mode is already frontier.",
5601
+ frontierOnlyEnabled: "Frontier execution enabled.",
5602
+ localOnlyNoRuntimes: "Cannot enable local execution: no enabled local runtime is configured. Run 'openteam setup' or enable a local runtime in .opencode/openteam.json first.",
5603
+ localOnlyNoChange: "No change: execution mode is already local.",
5604
+ localOnlyEnabled: "Local execution enabled.",
5605
+ localFirstNoChange: "No change: execution mode is already mixed.",
5606
+ localFirstReEnabled: "Mixed local/frontier execution enabled. Make sure each configured provider is reachable.",
5607
+ unknownSubcommand: (subcommand, help) => `Unknown local subcommand: ${subcommand}
5608
+
5609
+ ${help}`
5610
+ };
5611
+ var localRuntimeMessages = {
5612
+ modelListHttpError: (status) => `GET /models failed with HTTP ${status}`,
5613
+ malformedModelsJson: (detail) => `Malformed JSON from /models: ${detail}`,
5614
+ malformedModelsResponseExpectedDataArray: "Malformed /models response: expected data array",
5615
+ malformedModelsResponseModelId: "Malformed /models response: model id must be a string",
5616
+ dnsLookupFailedFor: (hostname, code) => `DNS lookup failed for ${hostname} (${code})`,
5617
+ dnsLookupFailed: (code) => `DNS lookup failed (${code})`
5618
+ };
5619
+ var yoloMessages = {
5620
+ updated: (path, status, agentNote) => `${path} updated. ${status}${agentNote}
5621
+ Restart opencode (or reload) so agents pick up the new permissions.`
5622
+ };
5623
+ var migrateMessages = {
5624
+ header: "openteam migrate:",
5625
+ nothingToMigrate: " nothing to migrate — the P15b layout is already in place.",
5626
+ counts: (params) => [
5627
+ ` moved: ${params.moved} file(s)`,
5628
+ ` deduped: ${params.deduped} file(s) (destination already identical)`,
5629
+ ` conflicts: ${params.conflicts} file(s)`
5630
+ ],
5631
+ relocatedHeader: " relocated:",
5632
+ relocatedEntry: (from, to, deduped) => ` · ${from} → ${to}${deduped ? " (deduped)" : ""}`,
5633
+ conflictsHeader: " ⚠ left in place (destination exists with different content — no data lost):",
5634
+ conflictEntry: (from, to) => ` · ${from} → ${to}`,
5635
+ manualWorktreesHeader: " ⚠ legacy git worktree(s) need a manual move (run `git worktree move`):",
5636
+ manualWorktreeEntry: (label, from, to) => ` · ${label}: ${from} → ${to}`,
5637
+ success: " ✓ machine-local runtime output relocated under .opencode/openteam-local/."
5638
+ };
5639
+ var rolesInitMessages = {
5640
+ help: " openteam roles init Compose per-role execution, model, and fallback policy",
5641
+ header: "openteam roles init:",
5642
+ title: "openteam roles init",
5643
+ noRoster: (path) => ` ✗ no roster at ${path}.`,
5644
+ noRosterRemedy: " Cast the team first: run `openteam setup`, then ask the orchestrator to register the cast.",
5645
+ unparseableRoster: (path, error) => ` ✗ ${path} is present but unparseable: ${error}`,
5646
+ unparseableRosterRemedy: " Run `openteam doctor` for the remedy, then re-run this command.",
5647
+ allProfiled: (count) => ` ✓ ${count} roster role(s) checked; no unprofiled roles — every role resolves a routing profile.`,
5648
+ skipLabel: "Skip for now",
5649
+ skipHint: "leaves the role on its built-in or fallback policy; doctor keeps reporting the missing explicit override",
5650
+ runtimeQuestion: (roleID) => `Preferred local runtime(s) for "${roleID}" (in failover order)`,
5651
+ runtimeHint: "machine-local — written to the git-ignored overlay",
5652
+ cancelled: (reason) => ` ✗ cancelled (${reason}); nothing was written.`,
5653
+ invalidResult: (reason) => ` ✗ refusing to write: the resulting config is invalid — ${reason}`,
5654
+ deprecation: (message) => ` ⚠ ${message}`,
5655
+ nothingWritten: (skipped) => ` · skipped ${skipped}; nothing written.`,
5656
+ nothingToWriteOutro: "Nothing to write.",
5657
+ runtimeBinding: (path) => ` ✓ runtime binding: ${path} (git-ignored, machine-local)`,
5658
+ skippedSummary: (roleIDs) => ` · skipped: ${roleIDs} — using built-in or fallback policy; re-run this command to add an explicit override.`,
5659
+ outro: "Role policy updated."
5660
+ };
5661
+
5662
+ // src/messages/executionSetup.ts
5663
+ var executionSetupMessages = {
5664
+ setupIntro: "openteam setup — configure local, frontier, or mixed team execution",
5665
+ runtimeQuestion: "Which local runtimes do you want to enable? (choose 'None' for frontier-only)",
5666
+ runtimeNoneLabel: "None — use frontier models only",
5667
+ runtimeNoneHint: "no local runtime (frontier execution)",
5668
+ executionModeQuestion: "Team execution mode",
5669
+ executionModeChoices: {
5670
+ local: {
5671
+ label: "local — every agent stays on configured local providers",
5672
+ hint: "requires at least one enabled local runtime"
5673
+ },
5674
+ frontier: {
5675
+ label: "frontier — every agent uses frontier providers",
5676
+ hint: "does not require a local runtime"
5677
+ },
5678
+ mixed: {
5679
+ label: "mixed — policies may select local or frontier models",
5680
+ hint: "the global mode is an upper bound; per-agent policies may narrow it"
5681
+ }
5682
+ },
5683
+ frontierOnlyNote: [
5684
+ "No local runtime is enabled, so the team will use frontier providers.",
5685
+ "The selected primary model is written consistently to opencode.json and",
5686
+ ".opencode/agent/openteam.md."
5687
+ ].join(`
5688
+ `),
5689
+ frontierOnlyTitle: "Frontier execution",
5690
+ localOnlyNote: (model) => [
5691
+ "Every team agent is restricted to configured local providers.",
5692
+ `The primary coordinator is pinned to ${model}.`,
5693
+ "No frontier provider is required for execution while this mode is active."
5694
+ ].join(`
5695
+ `),
5696
+ localOnlyTitle: "Local execution",
5697
+ localRequiredError: "local execution requires an enabled local runtime",
5698
+ mixedPrimaryQuestion: "Primary coordinator model",
5699
+ mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
5700
+ mixedPrimaryFrontier: "Choose a frontier model",
5701
+ frontierModelQuestion: "Frontier model (type to search; cheapest capable entries first)",
5702
+ primaryPolicySummary: (params) => [
5703
+ `Execution mode: ${params.executionMode}`,
5704
+ `Primary model: ${params.primaryModel}`,
5705
+ `Primary fallbacks: ${params.fallbacks}`,
5706
+ "Worker model policy: auto within the global execution mode"
5707
+ ],
5708
+ noFallbacks: "none",
5709
+ yoloQuestion: "Enable YOLO mode? (opencode auto-approves all permissions)",
5710
+ configuredSummary: (params) => [
5711
+ ...executionSetupMessages.primaryPolicySummary({
5712
+ executionMode: params.executionMode,
5713
+ primaryModel: params.primaryModel,
5714
+ fallbacks: executionSetupMessages.noFallbacks
5715
+ }),
5716
+ `Frontier baseline: ${params.frontierBaseline}`,
5717
+ `Local runtimes: ${params.localRuntimes}`,
5718
+ `YOLO mode: ${params.yolo ? "enabled (auto-approves permissions)" : "disabled"}`,
5719
+ "Web Console: launched separately with 'openteam console' (multi-session, loopback)",
5720
+ `Wrote: ${params.opencodePath}, ${params.configPath}, ${params.agentPath}, ${params.agentDir}/*.md (${params.roleAgentCount} standard subagents), ${params.commandDir}/*.md (${params.commandCount} commands); ensured ${params.gitignorePath}`,
5721
+ "",
5722
+ "Next steps:",
5723
+ params.executionMode === "local" ? " 1. No frontier authentication is required while local execution is active" : " 1. Authenticate the frontier provider: opencode auth login",
5724
+ params.executionMode === "local" ? executionSetupMessages.localRuntimeNextStep : params.executionMode === "mixed" ? executionSetupMessages.mixedNextStep : executionSetupMessages.frontierNextStep,
5725
+ " 3. Press Tab and pick the 'openteam' agent, or type / and pick an /openteam… command"
5726
+ ],
5727
+ configuredTitle: "openteam configured",
5728
+ complete: (paths) => `openteam setup complete: ${paths}`,
5729
+ error: (message) => `setup error: ${message}`,
5730
+ localRuntimeNextStep: " 2. Make sure every configured local runtime is reachable",
5731
+ frontierNextStep: " 2. Open opencode in this repo; openteam will use frontier models",
5732
+ mixedNextStep: " 2. Make sure local runtimes are reachable; unavailable candidates can use configured fallbacks",
5733
+ done: "Done. Open opencode and select the 'openteam' agent.",
5734
+ pluginDowngradeNote: (existingVersion, binaryVersion) => [
5735
+ `The installed opencode.json pin is @${existingVersion}, which is newer than this binary (${binaryVersion}).`,
5736
+ "Re-running setup with an older binary would quietly downgrade the pin.",
5737
+ "Confirm below to downgrade, or cancel to keep the existing pin."
5738
+ ].join(`
5739
+ `),
5740
+ pluginDowngradeTitle: "Plugin pin downgrade detected",
5741
+ pluginDowngradeConfirm: (existingVersion, binaryVersion) => `Downgrade plugin pin from ${existingVersion} to ${binaryVersion}?`,
5742
+ pluginDowngradePreservedNote: (existingVersion) => `Preserved existing plugin pin @${existingVersion} (not downgraded).`,
5743
+ rerun: {
5744
+ unsafeExistingConfig: (path, detail) => `${path} cannot be safely updated. Fix the file first, then re-run setup. Detail: ${detail}`,
5745
+ emptyConfigObject: "the file is empty; expected a JSON object at the top level",
5746
+ topLevelObjectRequired: "expected a JSON object at the top level",
5747
+ configFormatKinds: {
5748
+ comments: "JSONC comments",
5749
+ trailingCommas: "trailing commas",
5750
+ commentsAndTrailingCommas: "JSONC comments and trailing commas"
5751
+ },
5752
+ configFormatNormalizationNote: (path, kinds) => `${path} uses ${kinds}. Re-running setup rewrites it as standard JSON and removes them.`,
5753
+ configFormatNormalizationTitle: "Config format will be normalized",
5754
+ configFormatNormalizationConfirm: (path) => `Rewrite ${path} as standard JSON?`,
5755
+ providerModelsObjectRequired: (providerID) => `provider.${providerID}.models must be a JSON object`,
5756
+ duplicateTopLevelKeys: (keys) => `duplicate top-level key(s): ${keys.map((key) => JSON.stringify(key)).join(", ")}`,
5757
+ unknownLocalPluginIdentity: (spec) => `plugin entry ${JSON.stringify(spec)} points to a local file whose package identity could not be verified; keep the existing local loader or replace it with an explicit ${JSON.stringify("@jmanuelcorral/openteam@x.y.z")} npm pin before re-running setup`,
5758
+ providerModelObjectRequired: (providerID, modelID) => `provider.${providerID}.models.${modelID} must be a JSON object`,
5759
+ providerModelLimitObjectRequired: (providerID, modelID) => `provider.${providerID}.models.${modelID}.limit must be a JSON object`,
5760
+ providerModelLimitPositiveInteger: (providerID, modelID, field) => `provider.${providerID}.models.${modelID}.limit.${field} must be a positive integer`
5761
+ },
5762
+ rolePolicy: {
5763
+ whyTitle: "Configure execution and model selection",
5764
+ why: (count) => `${count} roster role(s) have no explicit execution/model override. Configure a compact policy now, or leave them on the built-in automatic policy.`,
5765
+ modeQuestion: (roleID, agentName) => `Execution mode for "${roleID}" (${agentName})`,
5766
+ modeInheritLabel: "Inherit the team execution mode",
5767
+ modeLocalLabel: "Local only",
5768
+ modeFrontierLabel: "Frontier only",
5769
+ modelQuestion: (roleID) => `Model selection for "${roleID}"`,
5770
+ modelAutoLabel: "Auto — choose the cheapest capable eligible model",
5771
+ modelExactLabel: "Exact provider/model pin",
5772
+ modelExactQuestion: (roleID) => `Exact model for "${roleID}" as provider/model`,
5773
+ modelExactPlaceholder: "provider/model",
5774
+ fallbacksQuestion: (roleID) => `Ordered fallbacks for "${roleID}" (comma-separated provider/model, blank for none)`,
5775
+ fallbacksPlaceholder: "provider/model, provider/model",
5776
+ configured: (roleIDs, path) => ` ✓ execution/model policy: ${roleIDs} → ${path}`,
5777
+ invalidModelRef: (value) => `invalid model "${value}"; expected provider/model`
5778
+ }
5779
+ };
5780
+ var executionPolicyMessages = {
5781
+ doctorHelp: " openteam doctor Diagnose runtimes, execution policies, and config",
5782
+ agentsHelp: " openteam agents List agents, model identity, and execution policy",
5783
+ section: "Configured execution policies:",
5784
+ globalMode: (mode) => ` execution mode: ${mode}`,
5785
+ line: (policy) => ` ${policy.owner}: mode=${policy.executionMode ?? "inherit"} · model=${policy.model} · fallbacks=${policy.fallbacks.length > 0 ? policy.fallbacks.join(" → ") : "none"}`,
5786
+ retryLimits: (maxRetries, maxModelsPerNode) => ` retry limits: ${maxRetries} retries per model · ${maxModelsPerNode} model(s) per node`,
5787
+ inheritedDefaultUnset: "inherits the opencode.json default (not set); execution follows the configured team and agent policy",
5788
+ inheritedDefault: (subscription) => `inherits default → ${subscription}; execution follows the configured team and agent policy`,
5789
+ unprofiledRoleRouting: " routing: no explicit override — the role inherits its built-in or fallback policy within the global execution-mode upper bound. Roster prose is not executable configuration.",
5790
+ unprofiledRoleRemedy: " remedy: run `openteam roles init`, or add executionMode, model, and ordered fallbacks under orchestrator.roles in .opencode/openteam.json.",
5791
+ noLocalMixed: " ⚠ No local runtime reachable: mixed execution currently has only frontier candidates.",
5792
+ noLocalBlocked: " ✗ No local runtime reachable: local execution is blocked until a configured runtime is available.",
5793
+ primaryLocalMismatch: (name, providerID) => ` ✗ local execution is configured, but primary agent '${name}' points at provider '${providerID}', which opencode.json does not configure.`,
5794
+ primaryLocalMismatchCause: " cause: the primary agent artifact and opencode.json no longer agree on the selected provider/model identity.",
5795
+ primaryLocalMismatchConsequence: " consequence: opencode cannot start the coordinator, so it cannot read the roster or distribute work.",
5796
+ primaryLocalMismatchRemedy: (sourceFile) => ` remedy: re-run \`openteam setup\` to regenerate ${sourceFile} and opencode.json from the same primary policy, then re-run \`openteam doctor\`.`
5797
+ };
5798
+ var doctorMessages = {
5799
+ localModelLimits: {
5800
+ section: " local model limits:",
5801
+ healthy: " ✓ enabled local provider models declare valid output and context limits in opencode.json.",
5802
+ warningSummary: (count) => ` local model limits: ${count} warning(s)`,
5803
+ modelWarning: (providerID, modelID, problems) => ` ⚠ ${providerID}/${modelID}: ${problems}`,
5804
+ invalidLimitBlock: "invalid limit block (expected an object)",
5805
+ missingOutput: "no limit.output (opencode falls back to its 32 000-token default instead of openteam's 8 192-token client policy)",
5806
+ missingContext: "no limit.context (the runtime's usable prompt window remains unknown to opencode)",
5807
+ invalidOutput: "invalid limit.output (expected a positive integer token count)",
5808
+ invalidContext: "invalid limit.context (expected a positive integer token count)",
5809
+ invalidInput: "invalid limit.input (expected a positive integer token count)",
5810
+ outputExceedsContext: "limit.output is greater than or equal to limit.context, so the configured context leaves no usable input window",
5811
+ outputExceedsInput: "limit.input is less than or equal to the reserved output budget, so compaction would have no usable input threshold",
5812
+ outputRemedy: " remedy: re-run `openteam setup` to write openteam's 8 192-token local output default, then re-run `openteam doctor`.",
5813
+ detectedContextRemedy: " remedy: re-run `openteam setup` while the runtime is reachable to copy the detected usable context budget, then re-run `openteam doctor`.",
5814
+ unknownContextRemedy: " remedy: add `limit.context` manually for models whose runtime does not advertise a context window; `openteam setup` will not invent one.",
5815
+ invalidLimitRemedy: " remedy: fix the invalid local limit values in opencode.json before relying on this provider configuration."
5816
+ }
5817
+ };
5818
+ var localModeChangeMessages = {
5819
+ localProviderInFrontierDomain: (path, domainPath, providerID, modelID) => `${path}: model "${providerID}/${modelID}" uses provider "${providerID}", which is configured for local dispatch and cannot be selected when ${domainPath} is "frontier". Update this policy or use \`openteam local on\`.`,
5820
+ frontierProviderInLocalDomain: (path, domainPath, providerID, modelID) => `${path}: model "${providerID}/${modelID}" uses provider "${providerID}", which is not a configured local provider and cannot be selected when ${domainPath} is "local". Update this policy or use \`openteam local on\`.`
5821
+ };
5822
+
5823
+ // src/messages/upgrade.ts
5824
+ var upgradeMessages = {
5825
+ header: "openteam upgrade:",
5826
+ help: {
5827
+ command: " openteam upgrade Update the openteam plugin pin to the latest published version",
5828
+ check: " openteam upgrade --check Show current/target versions without making changes",
5829
+ version: " openteam upgrade --version x.y.z Pin to a specific published version",
5830
+ usage: " openteam upgrade [--check] [--version x.y.z]"
5831
+ },
5832
+ usageLabel: "Usage:",
5833
+ invalidArguments: (detail) => ` ✗ ${detail}`,
5834
+ duplicateCheck: "Flag --check may be provided only once.",
5835
+ duplicateVersion: "Flag --version may be provided only once.",
5836
+ missingVersionValue: "Flag --version requires a plain x.y.z value.",
5837
+ unknownFlag: (flag) => `Unknown flag ${flag}.`,
5838
+ unexpectedArgument: (value) => `Unexpected argument ${value}.`,
5839
+ checkNeedsUpdate: (params) => ` ${params.path}: ${params.current !== null ? params.current : "(unpinned)"} → ${params.target}`,
5840
+ checkAlreadyCurrent: (params) => ` ${params.path}: already at ${params.version}`,
5841
+ checkHint: (version) => version === undefined ? " Run 'openteam upgrade' (without --check) to apply." : ` Run 'openteam upgrade --version ${version}' (without --check) to apply.`,
5842
+ alreadyCurrent: (version) => ` Already at ${version}; nothing to update.`,
5843
+ updatedHeader: (version) => ` ✓ Plugin pin updated to ${version} in:`,
5844
+ partialHeader: (version) => ` ⚠ Plugin pin updated to ${version} in:`,
5845
+ partialFailuresHeader: " The following file(s) were not changed:",
5846
+ nothingChanged: " ✗ No files were changed.",
5847
+ writtenEntry: (path) => ` ✓ ${path}`,
5848
+ failedEntry: (params) => ` ✗ ${params.path}: ${params.error}`,
5849
+ backupNote: (directory) => ` Backups of the previous file contents were saved under ${directory}.`,
5850
+ restartHint: " Restart opencode to load the new plugin version.",
5851
+ cacheCleanupHint: " Cache cleanup is optional; run 'openteam clear-cache --delete' if you want to remove stale cache entries.",
5852
+ resolveFailuresBeforeRestart: " Resolve the failed file(s) before restarting opencode.",
5853
+ rerunAfterFailures: " Resolve the failed file(s) and re-run the command; no restart is needed.",
5854
+ noConfigFound: (paths) => `No opencode config found (searched: ${paths.join(", ")}). Run 'openteam setup' first.`,
5855
+ noPluginEntry: "No openteam plugin entry found in opencode config. Run 'openteam setup' first.",
5856
+ localFileSkipped: (params) => ` ⚠ Skipped local openteam file entry in ${params.path}: ${params.spec} — local development installs are not managed by upgrade.`,
5857
+ localFileOnlyHeader: " Local openteam file plugin entries were found:",
5858
+ localFileOnlyEntry: (params) => ` ${params.path}: ${params.spec}`,
5859
+ localFileOnlyRemedy: ' Upgrade manages npm pins only. Keep the local development install, or replace the entry manually with "@jmanuelcorral/openteam@x.y.z".',
5860
+ invalidVersion: (v) => `Invalid version "${v}": must be a plain semver (e.g. 1.2.3) with no leading zeros, range operators, or dist-tags.`,
5861
+ versionNotFound: (v) => `Version ${v} was not found in the npm registry. Verify that the version is published.`,
5862
+ fetchError: (message) => `Failed to resolve version from the npm registry: ${message}`,
5863
+ noDowngrade: (params) => `Automatic latest (${params.target}) is older than the current pin (${params.current}). ` + `The npm registry metadata may be stale. Use --version ${params.current} to keep the ` + `current pin, or --version ${params.target} to explicitly downgrade.`,
5864
+ portNotConfigured: "The upgrade port is not configured in this environment.",
5865
+ genericFetchFailed: "fetch failed",
5866
+ networkRequestFailed: "network request failed",
5867
+ networkRequestFailedWithDetail: (detail) => `network request failed: ${detail}`,
5868
+ requestTimedOut: (timeoutMs) => `request timed out after ${timeoutMs} ms`,
5869
+ responseReadFailed: "response body could not be read completely",
5870
+ responseTooLarge: (limitBytes) => `response body exceeded the ${limitBytes}-byte limit`,
5871
+ missingRegistryBody: "registry response body was empty",
5872
+ invalidRegistryJson: "invalid JSON in registry response",
5873
+ unterminatedBlockComment: "unterminated block comment",
5874
+ invalidRegistryPayload: 'unexpected registry response: expected a JSON object with string "name" and "version" fields',
5875
+ unexpectedVersionField: (raw) => `unexpected registry response: version field is ${raw}`,
5876
+ wrongPackageIdentity: (name) => `registry returned wrong package identity "${name}"`,
5877
+ unexpectedPublishedVersion: (params) => `registry returned version "${params.actual}" while verifying ${params.expected}`,
5878
+ httpStatus: (status) => `HTTP ${status}`,
5879
+ readConfigError: (path, message) => `Error reading ${path}: ${message}`,
5880
+ parseConfigError: (path, message) => `Cannot parse ${path} (malformed JSONC): ${message}`,
5881
+ configRootMustBeObject: (path, kind) => `Cannot parse ${path}: top-level value must be a JSON object (found ${kind}).`,
5882
+ pluginFieldMustBeArray: (path) => `Cannot parse ${path}: the top-level plugin field must be an array when present.`,
5883
+ pluginArrayEditUnavailable: (path) => `Cannot update ${path}: could not locate the top-level plugin array for a source-preserving edit.`,
5884
+ effectivePluginUpdateMismatch: (path) => `Cannot update ${path}: the source-preserving edit did not change the effective top-level plugin entry as expected.`,
5885
+ concurrentEdit: (path) => `${path} changed since it was read; refusing to overwrite it.`,
5886
+ writeTargetMissing: (path) => `${path} disappeared before it could be updated.`,
5887
+ unsafeFilesystemEntry: "expected a regular file inside the workspace; symlinks are not supported",
5888
+ backupLocationUnavailable: (path) => `Cannot update config: backup location ${path} is unavailable or unsafe.`,
5889
+ backupConflict: (path) => `Refusing to overwrite unexpected backup file ${path}.`,
5890
+ backupGitignoreUnexpected: (directory) => `Cannot write upgrade backups: the .gitignore in ${directory} has unexpected content. ` + `It must contain only "*\\n" (ignore everything). Restore it or remove it so the upgrade can manage it.`,
5891
+ windowsDaclPrepFailed: (path, message) => `Cannot update ${path}: failed to apply file ACL to staging file before write: ${message}`,
5892
+ windowsReplaceFailed: (path, message) => `Cannot update ${path} while preserving Windows file ACLs: ${message}`
5893
+ };
5894
+
5895
+ // src/commands/upgrade.ts
5896
+ var NPM_ORIGIN = "https://registry.npmjs.org";
5897
+ var NPM_PACKAGE_PATH = "/@jmanuelcorral%2Fopenteam";
5898
+ var NPM_LATEST_URL = `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/latest`;
5899
+ var FETCH_TIMEOUT_MS = 5000;
5900
+ var MAX_RESPONSE_BYTES = 64 * 1024;
5901
+ var MAX_FETCH_ATTEMPTS = 2;
5902
+ var PLUGIN_KEY = "plugin";
5903
+ var PRIVATE_DIRECTORY_MODE = 448;
5904
+ var PRIVATE_FILE_MODE = 384;
5905
+ var UPGRADE_BACKUP_GITIGNORE_CONTENT = `*
5906
+ `;
5907
+ var WINDOWS_POWERSHELL_EXE = join9(process.env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
5908
+ var WINDOWS_PS_TIMEOUT_MS = 1e4;
5909
+ var UPGRADE_BACKUP_SUFFIX = ".openteam-upgrade.bak";
5910
+ var UPGRADE_BACKUP_DIRECTORY = ".opencode/openteam-local/upgrade-backups";
5911
+ function npmVersionUrl(version) {
5912
+ return `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/${version}`;
5913
+ }
5914
+ var RegistryPackageIdentitySchema = z15.object({
5915
+ name: z15.string(),
5916
+ version: z15.string()
5917
+ }).strict();
5918
+
5919
+ class TransientFetchError extends Error {
5920
+ constructor(message) {
5921
+ super(message);
5922
+ this.name = "TransientFetchError";
5923
+ }
5924
+ }
5925
+
5926
+ class PermanentFetchError extends Error {
5927
+ constructor(message) {
5928
+ super(message);
5929
+ this.name = "PermanentFetchError";
5930
+ }
5931
+ }
5932
+ var GENERIC_FETCH_FAILURE_MESSAGES = new Set([
5933
+ upgradeMessages.genericFetchFailed,
5934
+ upgradeMessages.networkRequestFailed
5935
+ ]);
5936
+ var execFileAsync2 = promisify2(execFile2);
5937
+ function isMissingFile(error) {
5938
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
5939
+ }
5940
+ function isAlreadyExistsFile(error) {
5941
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
5942
+ }
5943
+ function isRecord3(value) {
5944
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5945
+ }
5946
+ function isTuplePluginEntry(value) {
5947
+ return Array.isArray(value) && value.length >= 1 && typeof value[0] === "string";
5948
+ }
5949
+ function extractSpecString(entry) {
5950
+ if (typeof entry === "string") {
5951
+ return entry;
5952
+ }
5953
+ if (isTuplePluginEntry(entry)) {
5954
+ return entry[0];
5955
+ }
5956
+ return;
5957
+ }
5958
+ function isLocalFileSpec(spec) {
5959
+ const lower = spec.toLowerCase();
5960
+ return lower.startsWith("file:") || spec.startsWith("./") || spec.startsWith(".\\") || spec.startsWith("../") || spec.startsWith("..\\") || spec.startsWith("/") || spec.startsWith("\\") || win32.isAbsolute(spec);
5961
+ }
5962
+ function isOurSpec(spec) {
5963
+ return spec === OPENTEAM_PACKAGE_NAME || spec.startsWith(`${OPENTEAM_PACKAGE_NAME}@`);
5964
+ }
5965
+ function currentPinnedVersion(spec) {
5966
+ if (spec === OPENTEAM_PACKAGE_NAME) {
5967
+ return null;
5968
+ }
5969
+ const version = spec.slice(`${OPENTEAM_PACKAGE_NAME}@`.length);
5970
+ return version.length > 0 ? version : null;
5971
+ }
5972
+ function describeJsonRootKind(value) {
5973
+ if (value === null) {
5974
+ return "null";
5975
+ }
5976
+ if (Array.isArray(value)) {
5977
+ return "array";
5978
+ }
5979
+ switch (typeof value) {
5980
+ case "boolean":
5981
+ return "boolean";
5982
+ case "number":
5983
+ return "number";
5984
+ default:
5985
+ return "string";
5986
+ }
5987
+ }
5988
+ function findBlockCommentEnd(src, start, limit = src.length) {
5989
+ let i = start + 2;
5990
+ while (i < limit && !(src[i] === "*" && src[i + 1] === "/")) {
5991
+ i += 1;
5992
+ }
5993
+ return i < limit ? i + 2 : undefined;
5994
+ }
5995
+ function skipTrivia(src, start, limit = src.length) {
5996
+ let i = start;
5997
+ while (i < limit) {
5998
+ const ch = src[i];
5999
+ if (ch === undefined) {
6000
+ break;
6001
+ }
6002
+ if (/\s/u.test(ch)) {
6003
+ i += 1;
6004
+ continue;
6005
+ }
6006
+ if (ch === "/" && src[i + 1] === "/") {
6007
+ i += 2;
6008
+ while (i < limit && src[i] !== `
6009
+ `) {
6010
+ i += 1;
6011
+ }
6012
+ continue;
6013
+ }
6014
+ if (ch === "/" && src[i + 1] === "*") {
6015
+ const blockCommentEnd = findBlockCommentEnd(src, i, limit);
6016
+ if (blockCommentEnd === undefined) {
6017
+ return limit;
6018
+ }
6019
+ i = blockCommentEnd;
6020
+ continue;
6021
+ }
6022
+ break;
6023
+ }
6024
+ return i;
6025
+ }
6026
+ function readStringToken(src, start, limit = src.length) {
6027
+ if (src[start] !== '"') {
6028
+ return;
6029
+ }
6030
+ let i = start + 1;
6031
+ while (i < limit) {
6032
+ const ch = src[i];
6033
+ if (ch === undefined) {
6034
+ return;
6035
+ }
6036
+ if (ch === "\\") {
6037
+ i += 2;
6038
+ continue;
6039
+ }
6040
+ if (ch === '"') {
6041
+ return { end: i + 1 };
6042
+ }
6043
+ i += 1;
6044
+ }
6045
+ return;
6046
+ }
6047
+ function normalizeJsoncForParse(src) {
6048
+ const out = [];
6049
+ let i = 0;
6050
+ while (i < src.length) {
6051
+ const ch = src[i];
6052
+ if (ch === undefined) {
6053
+ break;
6054
+ }
6055
+ if (ch === '"') {
6056
+ const token = readStringToken(src, i);
6057
+ if (token === undefined) {
6058
+ out.push(ch);
6059
+ i += 1;
6060
+ continue;
6061
+ }
6062
+ out.push(src.slice(i, token.end));
6063
+ i = token.end;
6064
+ continue;
6065
+ }
6066
+ if (ch === "/" && src[i + 1] === "/") {
6067
+ i = skipTrivia(src, i);
6068
+ continue;
6069
+ }
6070
+ if (ch === "/" && src[i + 1] === "*") {
6071
+ const blockCommentEnd = findBlockCommentEnd(src, i);
6072
+ if (blockCommentEnd === undefined) {
6073
+ throw new Error(upgradeMessages.unterminatedBlockComment);
6074
+ }
6075
+ i = blockCommentEnd;
6076
+ continue;
6077
+ }
6078
+ if (ch === ",") {
6079
+ const next = skipTrivia(src, i + 1);
6080
+ const nextChar = src[next];
6081
+ if (nextChar === "]" || nextChar === "}") {
6082
+ i += 1;
6083
+ continue;
6084
+ }
6085
+ }
6086
+ out.push(ch);
6087
+ i += 1;
6088
+ }
6089
+ return out.join("");
6090
+ }
6091
+ function parseConfigDocument(path, content) {
6092
+ let parsed;
6093
+ try {
6094
+ const normalized = normalizeJsoncForParse(content);
6095
+ parsed = JSON.parse(normalized);
6096
+ } catch (error) {
6097
+ const message = error instanceof Error ? error.message : String(error);
6098
+ return { ok: false, error: upgradeMessages.parseConfigError(path, message) };
6099
+ }
6100
+ if (!isRecord3(parsed)) {
6101
+ return {
6102
+ ok: false,
6103
+ error: upgradeMessages.configRootMustBeObject(path, describeJsonRootKind(parsed))
6104
+ };
6105
+ }
6106
+ return { ok: true, value: parsed };
6107
+ }
6108
+ function readJsoncValueEnd(src, start, limit) {
6109
+ const ch = src[start];
6110
+ if (ch === undefined) {
6111
+ return;
6112
+ }
6113
+ if (ch === '"') {
6114
+ return readStringToken(src, start, limit)?.end;
6115
+ }
6116
+ if (ch === "{" || ch === "[") {
6117
+ const stack = [];
6118
+ let i2 = start;
6119
+ while (i2 < limit) {
6120
+ const next = skipTrivia(src, i2, limit);
6121
+ i2 = next;
6122
+ const current = src[i2];
6123
+ if (current === undefined) {
6124
+ return;
6125
+ }
6126
+ if (current === '"') {
6127
+ const token = readStringToken(src, i2, limit);
6128
+ if (token === undefined) {
6129
+ return;
6130
+ }
6131
+ i2 = token.end;
6132
+ continue;
6133
+ }
6134
+ if (current === "{" || current === "[") {
6135
+ stack.push(current);
6136
+ i2 += 1;
6137
+ continue;
6138
+ }
6139
+ if (current === "}" || current === "]") {
6140
+ const open2 = stack[stack.length - 1];
6141
+ if (current === "}" && open2 !== "{" || current === "]" && open2 !== "[") {
6142
+ return;
6143
+ }
6144
+ stack.pop();
6145
+ i2 += 1;
6146
+ if (stack.length === 0) {
6147
+ return i2;
6148
+ }
6149
+ continue;
6150
+ }
6151
+ i2 += 1;
6152
+ }
6153
+ return;
6154
+ }
6155
+ let i = start;
6156
+ while (i < limit) {
6157
+ const current = src[i];
6158
+ if (current === undefined) {
6159
+ break;
6160
+ }
6161
+ if (/\s/u.test(current) || current === "," || current === "]" || current === "}" || current === "/" && (src[i + 1] === "/" || src[i + 1] === "*")) {
6162
+ break;
6163
+ }
6164
+ i += 1;
6165
+ }
6166
+ return i > start ? i : undefined;
6167
+ }
6168
+ function findTopLevelPluginArray(raw) {
6169
+ let found;
6170
+ let i = 0;
6171
+ let depth = 0;
6172
+ while (i < raw.length) {
6173
+ i = skipTrivia(raw, i);
6174
+ const ch = raw[i];
6175
+ if (ch === undefined) {
6176
+ break;
6177
+ }
6178
+ if (ch === "{") {
6179
+ depth += 1;
6180
+ i += 1;
6181
+ continue;
6182
+ }
6183
+ if (ch === "}") {
6184
+ if (depth > 0) {
6185
+ depth -= 1;
6186
+ }
6187
+ i += 1;
6188
+ continue;
6189
+ }
6190
+ if (ch === "[") {
6191
+ depth += 1;
6192
+ i += 1;
6193
+ continue;
6194
+ }
6195
+ if (ch === "]") {
6196
+ if (depth > 0) {
6197
+ depth -= 1;
6198
+ }
6199
+ i += 1;
6200
+ continue;
6201
+ }
6202
+ if (ch !== '"') {
6203
+ i += 1;
6204
+ continue;
6205
+ }
6206
+ const token = readStringToken(raw, i);
6207
+ if (token === undefined) {
6208
+ return;
6209
+ }
6210
+ const key = readJsonStringValue(raw, { start: i, end: token.end });
6211
+ if (key === undefined) {
6212
+ return;
6213
+ }
6214
+ i = token.end;
6215
+ if (key !== PLUGIN_KEY || depth !== 1) {
6216
+ continue;
6217
+ }
6218
+ i = skipTrivia(raw, i);
6219
+ if (raw[i] !== ":") {
6220
+ continue;
6221
+ }
6222
+ i += 1;
6223
+ i = skipTrivia(raw, i);
6224
+ if (raw[i] !== "[") {
6225
+ continue;
6226
+ }
6227
+ const arrayEnd = readJsoncValueEnd(raw, i, raw.length);
6228
+ if (arrayEnd === undefined) {
6229
+ return;
6230
+ }
6231
+ found = { start: i, end: arrayEnd };
6232
+ }
6233
+ return found;
6234
+ }
6235
+ function scanTopLevelArrayElements(raw, arrayStart, arrayEnd) {
6236
+ const elements = [];
6237
+ let i = skipTrivia(raw, arrayStart + 1, arrayEnd);
6238
+ while (i < arrayEnd) {
6239
+ const ch = raw[i];
6240
+ if (ch === undefined || ch === "]") {
6241
+ break;
6242
+ }
6243
+ const end = readJsoncValueEnd(raw, i, arrayEnd);
6244
+ if (end === undefined) {
6245
+ return;
6246
+ }
6247
+ elements.push({ start: i, end });
6248
+ i = skipTrivia(raw, end, arrayEnd);
6249
+ const next = raw[i];
6250
+ if (next === ",") {
6251
+ i = skipTrivia(raw, i + 1, arrayEnd);
6252
+ if (raw[i] === "]") {
6253
+ break;
6254
+ }
6255
+ continue;
6256
+ }
6257
+ if (next === "]") {
6258
+ break;
6259
+ }
6260
+ return;
6261
+ }
6262
+ return elements;
6263
+ }
6264
+ function readJsonStringValue(raw, span) {
6265
+ try {
6266
+ const parsed = JSON.parse(raw.slice(span.start, span.end));
6267
+ return typeof parsed === "string" ? parsed : undefined;
6268
+ } catch {
6269
+ return;
6270
+ }
6271
+ }
6272
+ function findEntrySpecStringSpan(raw, element, entry, expectedSpec) {
6273
+ if (typeof entry === "string") {
6274
+ const token2 = readStringToken(raw, element.start, element.end);
6275
+ if (token2 === undefined) {
6276
+ return;
6277
+ }
6278
+ const span2 = { start: element.start, end: token2.end };
6279
+ return readJsonStringValue(raw, span2) === expectedSpec ? span2 : undefined;
6280
+ }
6281
+ if (!isTuplePluginEntry(entry)) {
6282
+ return;
6283
+ }
6284
+ if (raw[element.start] !== "[") {
6285
+ return;
6286
+ }
6287
+ const firstValueStart = skipTrivia(raw, element.start + 1, element.end);
6288
+ const token = readStringToken(raw, firstValueStart, element.end);
6289
+ if (token === undefined) {
6290
+ return;
6291
+ }
6292
+ const span = { start: firstValueStart, end: token.end };
6293
+ return readJsonStringValue(raw, span) === expectedSpec ? span : undefined;
6294
+ }
6295
+ function buildUpdatedContent(originalContent, replacements, nextSpec) {
6296
+ const jsonSpec = JSON.stringify(nextSpec);
6297
+ let nextContent = originalContent;
6298
+ for (const replacement of [...replacements].sort((left, right) => right.start - left.start)) {
6299
+ nextContent = nextContent.slice(0, replacement.start) + jsonSpec + nextContent.slice(replacement.end);
6300
+ }
6301
+ return nextContent;
6302
+ }
6303
+ function validateEffectivePluginUpdate(path, nextContent, expectedManagedEntries, nextSpec) {
6304
+ const parsed = parseConfigDocument(path, nextContent);
6305
+ if (!parsed.ok) {
6306
+ return parsed.error;
6307
+ }
6308
+ const pluginValue = parsed.value[PLUGIN_KEY];
6309
+ if (!Array.isArray(pluginValue)) {
6310
+ return upgradeMessages.effectivePluginUpdateMismatch(path);
6311
+ }
6312
+ let managedEntries = 0;
6313
+ let updatedEntries = 0;
6314
+ for (const entry of pluginValue) {
6315
+ const spec = extractSpecString(entry);
6316
+ if (spec === undefined || !isOurSpec(spec)) {
6317
+ continue;
6318
+ }
6319
+ managedEntries += 1;
6320
+ if (spec === nextSpec) {
6321
+ updatedEntries += 1;
6322
+ }
6323
+ }
6324
+ return managedEntries === expectedManagedEntries && updatedEntries === expectedManagedEntries ? undefined : upgradeMessages.effectivePluginUpdateMismatch(path);
6325
+ }
6326
+ function parseUpgradePositionals(positionals) {
6327
+ let check = false;
6328
+ let targetVersion;
6329
+ for (let i = 1;i < positionals.length; i += 1) {
6330
+ const arg = positionals[i];
6331
+ if (arg === "--check") {
6332
+ if (check) {
6333
+ return { ok: false, error: upgradeMessages.duplicateCheck };
6334
+ }
6335
+ check = true;
6336
+ continue;
6337
+ }
6338
+ if (arg === "--version") {
6339
+ if (targetVersion !== undefined) {
6340
+ return { ok: false, error: upgradeMessages.duplicateVersion };
6341
+ }
6342
+ const next = positionals[i + 1];
6343
+ if (next === undefined || next.startsWith("--")) {
6344
+ return { ok: false, error: upgradeMessages.missingVersionValue };
6345
+ }
6346
+ targetVersion = next;
6347
+ i += 1;
6348
+ continue;
6349
+ }
6350
+ if (arg?.startsWith("--")) {
6351
+ return { ok: false, error: upgradeMessages.unknownFlag(arg) };
6352
+ }
6353
+ if (arg !== undefined) {
6354
+ return { ok: false, error: upgradeMessages.unexpectedArgument(arg) };
6355
+ }
6356
+ }
6357
+ return { ok: true, value: { check, targetVersion } };
6358
+ }
6359
+ async function raceWithAbort(promise, signal, message) {
6360
+ if (signal.aborted) {
6361
+ throw new TransientFetchError(message);
6362
+ }
6363
+ let onAbort;
6364
+ try {
6365
+ return await Promise.race([
6366
+ promise,
6367
+ new Promise((_, reject) => {
6368
+ onAbort = () => reject(new TransientFetchError(message));
6369
+ signal.addEventListener("abort", onAbort, { once: true });
6370
+ })
6371
+ ]);
6372
+ } finally {
6373
+ if (onAbort !== undefined) {
6374
+ signal.removeEventListener("abort", onAbort);
6375
+ }
6376
+ }
6377
+ }
6378
+ function scheduleCleanup(action) {
6379
+ try {
6380
+ const promise = action();
6381
+ promise?.catch(() => {});
6382
+ } catch {}
6383
+ }
6384
+ function scheduleResponseBodyCancel(response) {
6385
+ scheduleCleanup(() => response.body?.cancel());
6386
+ }
6387
+ function scheduleReaderCancel(reader) {
6388
+ scheduleCleanup(() => reader.cancel());
6389
+ }
6390
+ function extractFetchFailureDetail(error) {
6391
+ const seen = new Set;
6392
+ let current = error;
6393
+ let fallback;
6394
+ while (current instanceof Error && !seen.has(current)) {
6395
+ seen.add(current);
6396
+ const message = current.message.trim();
6397
+ if (message !== "" && !GENERIC_FETCH_FAILURE_MESSAGES.has(message.toLowerCase())) {
6398
+ return message;
6399
+ }
6400
+ if (message !== "" && fallback === undefined) {
6401
+ fallback = message;
6402
+ }
6403
+ current = "cause" in current ? current.cause : undefined;
6404
+ }
6405
+ if (typeof current === "string") {
6406
+ const message = current.trim();
6407
+ if (message !== "") {
6408
+ return message;
6409
+ }
6410
+ }
6411
+ return fallback;
6412
+ }
6413
+ function classifyUnexpectedFetchFailure(error, timedOut) {
6414
+ if (timedOut) {
6415
+ return new TransientFetchError(upgradeMessages.requestTimedOut(FETCH_TIMEOUT_MS));
6416
+ }
6417
+ const detail = extractFetchFailureDetail(error);
6418
+ return new TransientFetchError(detail === undefined ? upgradeMessages.networkRequestFailed : upgradeMessages.networkRequestFailedWithDetail(detail));
6419
+ }
6420
+ async function readBoundedJsonResponse(response, signal) {
6421
+ const contentLength = Number(response.headers.get("content-length"));
6422
+ if (Number.isFinite(contentLength) && contentLength > MAX_RESPONSE_BYTES) {
6423
+ scheduleResponseBodyCancel(response);
6424
+ throw new PermanentFetchError(upgradeMessages.responseTooLarge(MAX_RESPONSE_BYTES));
6425
+ }
6426
+ const body = response.body;
6427
+ if (body === null) {
6428
+ throw new PermanentFetchError(upgradeMessages.missingRegistryBody);
6429
+ }
6430
+ const reader = body.getReader();
6431
+ const cancelReader = () => {
6432
+ scheduleReaderCancel(reader);
6433
+ };
6434
+ signal.addEventListener("abort", cancelReader, { once: true });
6435
+ const chunks = [];
6436
+ let total = 0;
6437
+ try {
6438
+ while (true) {
6439
+ const chunk = await raceWithAbort(reader.read(), signal, upgradeMessages.requestTimedOut(FETCH_TIMEOUT_MS));
6440
+ if (chunk.done) {
6441
+ break;
6442
+ }
6443
+ total += chunk.value.byteLength;
6444
+ if (total > MAX_RESPONSE_BYTES) {
6445
+ throw new PermanentFetchError(upgradeMessages.responseTooLarge(MAX_RESPONSE_BYTES));
6446
+ }
6447
+ chunks.push(chunk.value);
6448
+ }
6449
+ } catch (error) {
6450
+ scheduleReaderCancel(reader);
6451
+ if (error instanceof PermanentFetchError) {
6452
+ throw error;
6453
+ }
6454
+ throw new TransientFetchError(signal.aborted ? upgradeMessages.requestTimedOut(FETCH_TIMEOUT_MS) : upgradeMessages.responseReadFailed);
6455
+ } finally {
6456
+ signal.removeEventListener("abort", cancelReader);
6457
+ reader.releaseLock();
6458
+ }
6459
+ const bytes = new Uint8Array(total);
6460
+ let offset = 0;
6461
+ for (const chunk of chunks) {
6462
+ bytes.set(chunk, offset);
6463
+ offset += chunk.byteLength;
6464
+ }
6465
+ try {
6466
+ return JSON.parse(new TextDecoder().decode(bytes));
6467
+ } catch {
6468
+ throw new PermanentFetchError(upgradeMessages.invalidRegistryJson);
6469
+ }
6470
+ }
6471
+ async function fetchFromNpm(url, fetchFn) {
6472
+ for (let attempt = 1;attempt <= MAX_FETCH_ATTEMPTS; attempt += 1) {
6473
+ const controller = new AbortController;
6474
+ const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
6475
+ try {
6476
+ const response = await raceWithAbort(fetchFn(url, { signal: controller.signal }), controller.signal, upgradeMessages.requestTimedOut(FETCH_TIMEOUT_MS));
6477
+ if (!response.ok) {
6478
+ scheduleResponseBodyCancel(response);
6479
+ return { ok: false, kind: "http", status: response.status };
6480
+ }
6481
+ const data = await readBoundedJsonResponse(response, controller.signal);
6482
+ return { ok: true, data };
6483
+ } catch (error) {
6484
+ const timedOut = controller.signal.aborted;
6485
+ controller.abort();
6486
+ if (error instanceof PermanentFetchError) {
6487
+ return { ok: false, kind: "error", error: error.message };
6488
+ }
6489
+ const transientError = error instanceof TransientFetchError ? error : classifyUnexpectedFetchFailure(error, timedOut);
6490
+ if (attempt === MAX_FETCH_ATTEMPTS) {
6491
+ return { ok: false, kind: "error", error: transientError.message };
6492
+ }
6493
+ } finally {
6494
+ clearTimeout(timeoutId);
6495
+ }
6496
+ }
6497
+ return { ok: false, kind: "error", error: upgradeMessages.networkRequestFailed };
6498
+ }
6499
+ function validateRegistryPackageResponse(data) {
6500
+ const parsed = RegistryPackageIdentitySchema.safeParse(isRecord3(data) ? { name: data.name, version: data.version } : data);
6501
+ if (!parsed.success) {
6502
+ return { ok: false, error: upgradeMessages.invalidRegistryPayload };
6503
+ }
6504
+ if (parsed.data.name !== OPENTEAM_PACKAGE_NAME) {
6505
+ return { ok: false, error: upgradeMessages.wrongPackageIdentity(parsed.data.name) };
6506
+ }
6507
+ if (!isStrictSemver(parsed.data.version)) {
6508
+ return {
6509
+ ok: false,
6510
+ error: upgradeMessages.unexpectedVersionField(JSON.stringify(parsed.data.version))
6511
+ };
6512
+ }
6513
+ return { ok: true, version: parsed.data.version };
6514
+ }
6515
+ async function fetchLatestVersion(fetchFn) {
6516
+ const result = await fetchFromNpm(NPM_LATEST_URL, fetchFn);
6517
+ if (!result.ok) {
6518
+ if (result.kind === "http") {
6519
+ return { ok: false, error: upgradeMessages.fetchError(upgradeMessages.httpStatus(result.status)) };
6520
+ }
6521
+ return { ok: false, error: upgradeMessages.fetchError(result.error) };
6522
+ }
6523
+ const validated = validateRegistryPackageResponse(result.data);
6524
+ if (!validated.ok) {
6525
+ return { ok: false, error: upgradeMessages.fetchError(validated.error) };
6526
+ }
6527
+ return { ok: true, version: validated.version };
6528
+ }
6529
+ async function verifyVersionExists(version, fetchFn) {
6530
+ const result = await fetchFromNpm(npmVersionUrl(version), fetchFn);
6531
+ if (!result.ok) {
6532
+ if (result.kind === "http") {
6533
+ return result.status === 404 ? { ok: false, error: upgradeMessages.versionNotFound(version) } : { ok: false, error: upgradeMessages.fetchError(upgradeMessages.httpStatus(result.status)) };
6534
+ }
6535
+ return { ok: false, error: upgradeMessages.fetchError(result.error) };
6536
+ }
6537
+ const validated = validateRegistryPackageResponse(result.data);
6538
+ if (!validated.ok) {
6539
+ return { ok: false, error: upgradeMessages.fetchError(validated.error) };
6540
+ }
6541
+ if (validated.version !== version) {
6542
+ return {
6543
+ ok: false,
6544
+ error: upgradeMessages.fetchError(upgradeMessages.unexpectedPublishedVersion({
6545
+ expected: version,
6546
+ actual: validated.version
6547
+ }))
6548
+ };
6549
+ }
6550
+ return { ok: true };
6551
+ }
6552
+ async function analyseConfigFiles(paths, port) {
6553
+ const files = [];
6554
+ const localOpenteamEntries = [];
6555
+ let foundConfigFile = false;
6556
+ for (const path of paths) {
6557
+ let content;
6558
+ try {
6559
+ content = await port.readOpencodeConfigFile(path);
6560
+ } catch (error) {
6561
+ const message = error instanceof Error ? error.message : String(error);
6562
+ return {
6563
+ files,
6564
+ foundConfigFile,
6565
+ localOpenteamEntries,
6566
+ parseError: upgradeMessages.readConfigError(path, message)
6567
+ };
6568
+ }
6569
+ if (content === undefined) {
6570
+ continue;
6571
+ }
6572
+ foundConfigFile = true;
6573
+ const parsed = parseConfigDocument(path, content);
6574
+ if (!parsed.ok) {
6575
+ return {
6576
+ files,
6577
+ foundConfigFile,
6578
+ localOpenteamEntries,
6579
+ parseError: parsed.error
6580
+ };
6581
+ }
6582
+ const pluginValue = parsed.value[PLUGIN_KEY];
6583
+ if (pluginValue === undefined) {
6584
+ continue;
6585
+ }
6586
+ if (!Array.isArray(pluginValue)) {
6587
+ return {
6588
+ files,
6589
+ foundConfigFile,
6590
+ localOpenteamEntries,
6591
+ parseError: upgradeMessages.pluginFieldMustBeArray(path)
6592
+ };
6593
+ }
6594
+ const managedCandidates = [];
6595
+ for (let index = 0;index < pluginValue.length; index += 1) {
6596
+ const entry = pluginValue[index];
6597
+ const spec = extractSpecString(entry);
6598
+ if (spec === undefined) {
6599
+ continue;
6600
+ }
6601
+ if (isLocalFileSpec(spec)) {
6602
+ const localKind = await port.classifyLocalPluginSpec({
6603
+ configPath: path,
6604
+ spec
6605
+ });
6606
+ if (localKind === "ours") {
6607
+ localOpenteamEntries.push({ path, spec });
6608
+ }
6609
+ continue;
6610
+ }
6611
+ if (!isOurSpec(spec)) {
6612
+ continue;
6613
+ }
6614
+ managedCandidates.push({
6615
+ currentVersion: currentPinnedVersion(spec),
6616
+ entry,
6617
+ index,
6618
+ spec
6619
+ });
6620
+ }
6621
+ if (managedCandidates.length === 0) {
6622
+ continue;
6623
+ }
6624
+ const pluginArray = findTopLevelPluginArray(content);
6625
+ if (pluginArray === undefined) {
6626
+ return {
6627
+ files,
6628
+ foundConfigFile,
6629
+ localOpenteamEntries,
6630
+ parseError: upgradeMessages.pluginArrayEditUnavailable(path)
6631
+ };
6632
+ }
6633
+ const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
6634
+ if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
6635
+ return {
6636
+ files,
6637
+ foundConfigFile,
6638
+ localOpenteamEntries,
6639
+ parseError: upgradeMessages.pluginArrayEditUnavailable(path)
6640
+ };
6641
+ }
6642
+ const replacements = [];
6643
+ for (const candidate of managedCandidates) {
6644
+ const elementSpan = elementSpans[candidate.index];
6645
+ if (elementSpan === undefined) {
6646
+ return {
6647
+ files,
6648
+ foundConfigFile,
6649
+ localOpenteamEntries,
6650
+ parseError: upgradeMessages.pluginArrayEditUnavailable(path)
6651
+ };
6652
+ }
6653
+ const specSpan = findEntrySpecStringSpan(content, elementSpan, candidate.entry, candidate.spec);
6654
+ if (specSpan === undefined) {
6655
+ return {
6656
+ files,
6657
+ foundConfigFile,
6658
+ localOpenteamEntries,
6659
+ parseError: upgradeMessages.pluginArrayEditUnavailable(path)
6660
+ };
6661
+ }
6662
+ replacements.push({
6663
+ ...specSpan,
6664
+ currentVersion: candidate.currentVersion
6665
+ });
6666
+ }
6667
+ files.push({
6668
+ currentVersions: replacements.map((replacement) => replacement.currentVersion),
6669
+ path,
6670
+ originalContent: content,
6671
+ replacements
6672
+ });
6673
+ }
6674
+ return {
6675
+ files,
6676
+ foundConfigFile,
6677
+ localOpenteamEntries,
6678
+ parseError: undefined
6679
+ };
6680
+ }
6681
+ function renderArgError(error) {
6682
+ return {
6683
+ exitCode: 1,
6684
+ stdout: [
6685
+ upgradeMessages.header,
6686
+ upgradeMessages.invalidArguments(error),
6687
+ "",
6688
+ upgradeMessages.usageLabel,
6689
+ upgradeMessages.help.usage
6690
+ ].join(`
6691
+ `)
6692
+ };
6693
+ }
6694
+ function renderFailedWrites(localWarnings, failed) {
6695
+ return {
6696
+ exitCode: 1,
6697
+ stdout: [
6698
+ upgradeMessages.header,
6699
+ ...localWarnings,
6700
+ upgradeMessages.nothingChanged,
6701
+ ...failed.map((entry) => upgradeMessages.failedEntry(entry)),
6702
+ "",
6703
+ upgradeMessages.rerunAfterFailures
6704
+ ].join(`
6705
+ `)
6706
+ };
6707
+ }
6708
+ function renderPartialSuccess(version, localWarnings, written, failed) {
6709
+ return {
6710
+ exitCode: 1,
6711
+ stdout: [
6712
+ upgradeMessages.header,
6713
+ ...localWarnings,
6714
+ upgradeMessages.partialHeader(version),
6715
+ ...written.map((path) => upgradeMessages.writtenEntry(path)),
6716
+ upgradeMessages.partialFailuresHeader,
6717
+ ...failed.map((entry) => upgradeMessages.failedEntry(entry)),
6718
+ "",
6719
+ upgradeMessages.backupNote(UPGRADE_BACKUP_DIRECTORY),
6720
+ upgradeMessages.resolveFailuresBeforeRestart
6721
+ ].join(`
6722
+ `)
6723
+ };
6724
+ }
6725
+ function renderSuccess(version, localWarnings, written) {
6726
+ return {
6727
+ exitCode: 0,
6728
+ stdout: [
6729
+ upgradeMessages.header,
6730
+ ...localWarnings,
6731
+ upgradeMessages.updatedHeader(version),
6732
+ ...written.map((path) => upgradeMessages.writtenEntry(path)),
6733
+ "",
6734
+ upgradeMessages.backupNote(UPGRADE_BACKUP_DIRECTORY),
6735
+ upgradeMessages.restartHint,
6736
+ upgradeMessages.cacheCleanupHint
6737
+ ].join(`
6738
+ `)
6739
+ };
6740
+ }
6741
+ function resolveProjectPath(workspaceRoot, configPath) {
6742
+ const absoluteRoot = resolve3(workspaceRoot);
6743
+ const absolutePath = resolve3(absoluteRoot, configPath);
6744
+ const rel = relative(absoluteRoot, absolutePath);
6745
+ if (rel === "" || rel.startsWith("..") || win32.isAbsolute(rel)) {
6746
+ throw new Error("path-outside-root");
6747
+ }
6748
+ return absolutePath;
6749
+ }
6750
+ function resolveLocalSpecPath(workspaceRoot, configPath, spec) {
6751
+ if (spec.toLowerCase().startsWith("file:")) {
6752
+ try {
6753
+ return fileURLToPath(new URL(spec));
6754
+ } catch {
6755
+ return;
6756
+ }
6757
+ }
6758
+ if (win32.isAbsolute(spec)) {
6759
+ return spec;
6760
+ }
6761
+ const configAbsolutePath = resolveProjectPath(workspaceRoot, configPath);
6762
+ return resolve3(dirname4(configAbsolutePath), spec);
6763
+ }
6764
+ async function readPackageManifestName(packageJsonPath) {
6765
+ let content;
6766
+ try {
6767
+ content = await readFile2(packageJsonPath, "utf8");
6768
+ } catch (error) {
6769
+ return isMissingFile(error) ? { kind: "missing" } : { kind: "invalid" };
6770
+ }
6771
+ let parsed;
6772
+ try {
6773
+ parsed = JSON.parse(content);
6774
+ } catch {
6775
+ return { kind: "invalid" };
6776
+ }
6777
+ if (!isRecord3(parsed) || typeof parsed.name !== "string") {
6778
+ return { kind: "invalid" };
6779
+ }
6780
+ return { kind: "name", name: parsed.name };
6781
+ }
6782
+ async function manifestSearchStart(targetPath, spec) {
6783
+ try {
6784
+ const stats = await lstat(targetPath);
6785
+ return stats.isDirectory() ? targetPath : dirname4(targetPath);
6786
+ } catch {
6787
+ return spec.endsWith("/") || spec.endsWith("\\") ? targetPath : dirname4(targetPath);
6788
+ }
6789
+ }
6790
+ function splitRelativeProjectPath(workspaceRoot, targetPath) {
6791
+ const rel = relative(workspaceRoot, targetPath);
6792
+ if (rel === "") {
6793
+ return [];
6794
+ }
6795
+ if (rel.startsWith("..") || win32.isAbsolute(rel)) {
6796
+ throw new Error("path-outside-root");
6797
+ }
6798
+ return rel.split(/[\\/]+/u).filter((segment) => segment.length > 0);
6799
+ }
6800
+ async function validateExistingAncestorDirectories(workspaceRoot, targetDirectoryPath) {
6801
+ const absoluteRoot = resolve3(workspaceRoot);
6802
+ const rootStats = await lstat(absoluteRoot);
6803
+ if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
6804
+ throw new Error(upgradeMessages.unsafeFilesystemEntry);
6805
+ }
6806
+ let current = absoluteRoot;
6807
+ for (const segment of splitRelativeProjectPath(absoluteRoot, targetDirectoryPath)) {
6808
+ current = join9(current, segment);
6809
+ let stats;
6810
+ try {
6811
+ stats = await lstat(current);
6812
+ } catch (error) {
6813
+ if (isMissingFile(error)) {
6814
+ return false;
6815
+ }
6816
+ throw error;
6817
+ }
6818
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
6819
+ throw new Error(upgradeMessages.unsafeFilesystemEntry);
6820
+ }
6821
+ }
6822
+ return true;
6823
+ }
6824
+ async function ensurePrivateDirectoryChain(workspaceRoot, targetDirectoryPath) {
6825
+ const absoluteRoot = resolve3(workspaceRoot);
6826
+ const rootStats = await lstat(absoluteRoot);
6827
+ if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
6828
+ throw new Error(upgradeMessages.backupLocationUnavailable(UPGRADE_BACKUP_DIRECTORY));
6829
+ }
6830
+ let current = absoluteRoot;
6831
+ for (const segment of splitRelativeProjectPath(absoluteRoot, targetDirectoryPath)) {
6832
+ current = join9(current, segment);
6833
+ try {
6834
+ const stats = await lstat(current);
6835
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
6836
+ throw new Error(upgradeMessages.backupLocationUnavailable(UPGRADE_BACKUP_DIRECTORY));
6837
+ }
6838
+ continue;
6839
+ } catch (error) {
6840
+ if (!isMissingFile(error)) {
6841
+ throw error;
6842
+ }
6843
+ }
6844
+ try {
6845
+ await mkdir2(current, { mode: PRIVATE_DIRECTORY_MODE });
6846
+ } catch (error) {
6847
+ if (!isAlreadyExistsFile(error)) {
6848
+ throw error;
6849
+ }
6850
+ const stats = await lstat(current);
6851
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
6852
+ throw new Error(upgradeMessages.backupLocationUnavailable(UPGRADE_BACKUP_DIRECTORY));
6853
+ }
6854
+ }
6855
+ }
6856
+ }
6857
+ async function readSafeTextFileSnapshot(workspaceRoot, diskPath) {
6858
+ const parentsExist = await validateExistingAncestorDirectories(workspaceRoot, dirname4(diskPath));
6859
+ if (!parentsExist) {
6860
+ return;
6861
+ }
6862
+ let stats;
6863
+ try {
6864
+ stats = await lstat(diskPath);
6865
+ } catch (error) {
6866
+ if (isMissingFile(error)) {
6867
+ return;
6868
+ }
6869
+ throw error;
6870
+ }
6871
+ if (!stats.isFile() || stats.isSymbolicLink()) {
6872
+ throw new Error(upgradeMessages.unsafeFilesystemEntry);
6873
+ }
6874
+ const content = await readFile2(diskPath, "utf8");
6875
+ return {
6876
+ content,
6877
+ gid: stats.gid,
6878
+ mode: stats.mode & 511,
6879
+ uid: stats.uid
6880
+ };
6881
+ }
6882
+ async function writePrivateTextFile(path, content, flags) {
6883
+ const handle = await open(path, flags, PRIVATE_FILE_MODE);
6884
+ try {
6885
+ await handle.writeFile(content, "utf8");
6886
+ await handle.sync();
6887
+ } finally {
6888
+ await handle.close();
6889
+ }
6890
+ }
6891
+ async function writeAtomicPrivateFile(path, content, finalMode, options) {
6892
+ const temporary = join9(dirname4(path), `.${basename2(path)}.${randomUUID()}.next`);
6893
+ const handle = await open(temporary, "wx", PRIVATE_FILE_MODE);
6894
+ try {
6895
+ if (process.platform !== "win32") {
6896
+ if (finalMode !== PRIVATE_FILE_MODE) {
6897
+ await handle.chmod(finalMode);
6898
+ }
6899
+ const owner = options?.originalOwner;
6900
+ if (owner !== undefined) {
6901
+ await handle.chown(owner.uid, owner.gid);
6902
+ }
6903
+ }
6904
+ await handle.writeFile(content, "utf8");
6905
+ await handle.sync();
6906
+ } catch (error) {
6907
+ await handle.close();
6908
+ await rm2(temporary, { force: true }).catch(() => {});
6909
+ throw error;
6910
+ }
6911
+ await handle.close();
6912
+ try {
6913
+ await rename(temporary, path);
6914
+ options?.onReplaced?.();
6915
+ } finally {
6916
+ await rm2(temporary, { force: true }).catch(() => {});
6917
+ }
6918
+ }
6919
+ async function replaceWindowsFilePreservingAcl(sourcePath, destinationPath, backupPath, displayPath) {
6920
+ const script = `$ErrorActionPreference = 'Stop'; $source = $env:OPENTEAM_UPGRADE_SOURCE; $destination = $env:OPENTEAM_UPGRADE_DESTINATION; $backup = $env:OPENTEAM_UPGRADE_BACKUP; if ([string]::IsNullOrEmpty($backup)) { [System.IO.File]::Replace($source, $destination, $null) } else { [System.IO.File]::Replace($source, $destination, $backup) }`;
6921
+ try {
6922
+ await execFileAsync2(WINDOWS_POWERSHELL_EXE, ["-NoProfile", "-NonInteractive", "-Command", script], {
6923
+ encoding: "utf8",
6924
+ env: {
6925
+ ...process.env,
6926
+ OPENTEAM_UPGRADE_BACKUP: backupPath ?? "",
6927
+ OPENTEAM_UPGRADE_DESTINATION: destinationPath,
6928
+ OPENTEAM_UPGRADE_SOURCE: sourcePath
6929
+ },
6930
+ timeout: WINDOWS_PS_TIMEOUT_MS,
6931
+ windowsHide: true
6932
+ });
6933
+ } catch (error) {
6934
+ const message = error instanceof Error ? error.message : String(error);
6935
+ throw new Error(upgradeMessages.windowsReplaceFailed(displayPath, message));
6936
+ }
6937
+ }
6938
+ async function applyWindowsFileDaclToTemp(sourcePath, targetPath, displayPath) {
6939
+ const script = `$ErrorActionPreference = 'Stop'; $source = $env:OPENTEAM_UPGRADE_ACL_SOURCE; $target = $env:OPENTEAM_UPGRADE_ACL_TARGET; $acl = New-Object System.Security.AccessControl.FileSecurity($source, [System.Security.AccessControl.AccessControlSections]::Access); $acl.SetAccessRuleProtection($acl.AreAccessRulesProtected, $acl.AreAccessRulesCanonical); [System.IO.File]::SetAccessControl($target, $acl)`;
6940
+ try {
6941
+ await execFileAsync2(WINDOWS_POWERSHELL_EXE, ["-NoProfile", "-NonInteractive", "-Command", script], {
6942
+ encoding: "utf8",
6943
+ env: {
6944
+ ...process.env,
6945
+ OPENTEAM_UPGRADE_ACL_SOURCE: sourcePath,
6946
+ OPENTEAM_UPGRADE_ACL_TARGET: targetPath
6947
+ },
6948
+ timeout: WINDOWS_PS_TIMEOUT_MS,
6949
+ windowsHide: true
6950
+ });
6951
+ } catch (error) {
6952
+ const message = error instanceof Error ? error.message : String(error);
6953
+ throw new Error(upgradeMessages.windowsDaclPrepFailed(displayPath, message));
6954
+ }
6955
+ }
6956
+ async function writeWindowsFileReplacingPreservedAcl(destinationPath, content, backupPath, displayPath) {
6957
+ const temporary = join9(dirname4(destinationPath), `.${basename2(destinationPath)}.${randomUUID()}.next`);
6958
+ const handle = await open(temporary, "wx", PRIVATE_FILE_MODE);
6959
+ try {
6960
+ await applyWindowsFileDaclToTemp(destinationPath, temporary, displayPath);
6961
+ await handle.writeFile(content, "utf8");
6962
+ await handle.sync();
6963
+ } catch (error) {
6964
+ await handle.close();
6965
+ await rm2(temporary, { force: true }).catch(() => {});
6966
+ throw error;
6967
+ }
6968
+ await handle.close();
6969
+ try {
6970
+ await replaceWindowsFilePreservingAcl(temporary, destinationPath, backupPath, displayPath);
6971
+ } finally {
6972
+ await rm2(temporary, { force: true }).catch(() => {});
6973
+ }
6974
+ }
6975
+ function backupFileNameForConfig(configPath, content) {
6976
+ const safeStem = configPath.replace(/^[./\\]+/u, "").replace(/[\\/]+/gu, "__").replace(/[^A-Za-z0-9._-]/gu, "_");
6977
+ const stem = safeStem === "" ? "opencode" : safeStem;
6978
+ const digest = createHash2("sha256").update(content).digest("hex").slice(0, 12);
6979
+ return `${stem}.${digest}${UPGRADE_BACKUP_SUFFIX}`;
6980
+ }
6981
+ async function ensureUpgradeBackupRoot(workspaceRoot) {
6982
+ const backupRoot = resolveProjectPath(workspaceRoot, UPGRADE_BACKUP_DIRECTORY);
6983
+ await ensurePrivateDirectoryChain(workspaceRoot, backupRoot);
6984
+ if (process.platform !== "win32") {
6985
+ const dirStats = await lstat(backupRoot);
6986
+ if ((dirStats.mode & 511) !== PRIVATE_DIRECTORY_MODE) {
6987
+ await chmod(backupRoot, PRIVATE_DIRECTORY_MODE);
6988
+ }
6989
+ }
6990
+ const ignorePath = join9(backupRoot, ".gitignore");
6991
+ const ignoreFile = await readSafeTextFileSnapshot(workspaceRoot, ignorePath);
6992
+ if (ignoreFile !== undefined) {
6993
+ if (ignoreFile.content !== UPGRADE_BACKUP_GITIGNORE_CONTENT) {
6994
+ throw new Error(upgradeMessages.backupGitignoreUnexpected(UPGRADE_BACKUP_DIRECTORY));
6995
+ }
6996
+ return backupRoot;
6997
+ }
6998
+ try {
6999
+ await writePrivateTextFile(ignorePath, UPGRADE_BACKUP_GITIGNORE_CONTENT, "wx");
7000
+ } catch (error) {
7001
+ if (!isAlreadyExistsFile(error)) {
7002
+ throw error;
7003
+ }
7004
+ const concurrent = await readSafeTextFileSnapshot(workspaceRoot, ignorePath);
7005
+ if (concurrent === undefined) {
7006
+ throw error;
7007
+ }
7008
+ if (concurrent.content !== UPGRADE_BACKUP_GITIGNORE_CONTENT) {
7009
+ throw new Error(upgradeMessages.backupGitignoreUnexpected(UPGRADE_BACKUP_DIRECTORY));
7010
+ }
7011
+ }
7012
+ return backupRoot;
7013
+ }
7014
+ async function ensureUpgradeBackupFile(workspaceRoot, configPath, expectedContent) {
7015
+ try {
7016
+ await ensureUpgradeBackupRoot(workspaceRoot);
7017
+ } catch {
7018
+ throw new Error(upgradeMessages.backupLocationUnavailable(UPGRADE_BACKUP_DIRECTORY));
7019
+ }
7020
+ const backupRelativePath = `${UPGRADE_BACKUP_DIRECTORY}/` + backupFileNameForConfig(configPath, expectedContent);
7021
+ const backupDiskPath = resolveProjectPath(workspaceRoot, backupRelativePath);
7022
+ let existingBackup;
7023
+ try {
7024
+ existingBackup = await readSafeTextFileSnapshot(workspaceRoot, backupDiskPath);
7025
+ } catch {
7026
+ throw new Error(upgradeMessages.backupLocationUnavailable(UPGRADE_BACKUP_DIRECTORY));
7027
+ }
7028
+ if (existingBackup !== undefined) {
7029
+ if (existingBackup.content !== expectedContent) {
7030
+ throw new Error(upgradeMessages.backupConflict(backupRelativePath));
7031
+ }
7032
+ return { created: false, diskPath: backupDiskPath, reuseExisting: true };
7033
+ }
7034
+ if (process.platform === "win32") {
7035
+ return { created: false, diskPath: backupDiskPath, reuseExisting: false };
7036
+ }
7037
+ try {
7038
+ await writeAtomicPrivateFile(backupDiskPath, expectedContent, PRIVATE_FILE_MODE);
7039
+ } catch {
7040
+ throw new Error(upgradeMessages.backupLocationUnavailable(UPGRADE_BACKUP_DIRECTORY));
7041
+ }
7042
+ return { created: true, diskPath: backupDiskPath, reuseExisting: false };
7043
+ }
7044
+ async function readExpectedConfigSnapshot(workspaceRoot, configPath, expectedContent) {
7045
+ const diskPath = resolveProjectPath(workspaceRoot, configPath);
7046
+ const snapshot = await readSafeTextFileSnapshot(workspaceRoot, diskPath);
7047
+ if (snapshot === undefined) {
7048
+ throw new Error(upgradeMessages.writeTargetMissing(configPath));
7049
+ }
7050
+ if (snapshot.content !== expectedContent) {
7051
+ throw new Error(upgradeMessages.concurrentEdit(configPath));
7052
+ }
7053
+ return snapshot;
7054
+ }
7055
+ function createFsUpgradePort(workspaceRoot, fetch) {
7056
+ return {
7057
+ async readOpencodeConfigFile(path) {
7058
+ const diskPath = resolveProjectPath(workspaceRoot, path);
7059
+ const snapshot = await readSafeTextFileSnapshot(workspaceRoot, diskPath);
7060
+ return snapshot?.content;
7061
+ },
7062
+ async writeOpencodeConfigFile(request) {
7063
+ const diskPath = resolveProjectPath(workspaceRoot, request.path);
7064
+ await readExpectedConfigSnapshot(workspaceRoot, request.path, request.expectedContent);
7065
+ const backup = await ensureUpgradeBackupFile(workspaceRoot, request.path, request.expectedContent);
7066
+ let configWritten = false;
7067
+ try {
7068
+ const current = await readExpectedConfigSnapshot(workspaceRoot, request.path, request.expectedContent);
7069
+ if (process.platform === "win32") {
7070
+ await writeWindowsFileReplacingPreservedAcl(diskPath, request.nextContent, backup.reuseExisting ? undefined : backup.diskPath, request.path);
7071
+ configWritten = true;
7072
+ } else {
7073
+ await writeAtomicPrivateFile(diskPath, request.nextContent, current.mode, {
7074
+ originalOwner: { gid: current.gid, uid: current.uid },
7075
+ onReplaced: () => {
7076
+ configWritten = true;
7077
+ }
7078
+ });
7079
+ }
7080
+ } catch (error) {
7081
+ if (backup.created && !configWritten) {
7082
+ await rm2(backup.diskPath, { force: true }).catch(() => {});
7083
+ }
7084
+ throw error;
7085
+ }
7086
+ },
7087
+ async classifyLocalPluginSpec({ configPath, spec }) {
7088
+ const resolved = resolveLocalSpecPath(workspaceRoot, configPath, spec);
7089
+ if (resolved === undefined) {
7090
+ return "unknown";
7091
+ }
7092
+ let current = await manifestSearchStart(resolved, spec);
7093
+ while (true) {
7094
+ const manifest = await readPackageManifestName(join9(current, "package.json"));
7095
+ if (manifest.kind === "name") {
7096
+ return manifest.name === OPENTEAM_PACKAGE_NAME ? "ours" : "other";
7097
+ }
7098
+ if (manifest.kind === "invalid") {
7099
+ return "unknown";
7100
+ }
7101
+ const parent = dirname4(current);
7102
+ if (parent === current) {
7103
+ return "unknown";
7104
+ }
7105
+ current = parent;
7106
+ }
7107
+ },
7108
+ fetch
7109
+ };
7110
+ }
7111
+ async function runUpgrade(positionals, port, opencodeConfigPaths) {
7112
+ const parsedArgs = parseUpgradePositionals(positionals);
7113
+ if (!parsedArgs.ok) {
7114
+ return renderArgError(parsedArgs.error);
7115
+ }
7116
+ const explicitVersion = parsedArgs.value.targetVersion;
7117
+ if (explicitVersion !== undefined && !isStrictSemver(explicitVersion)) {
7118
+ return {
7119
+ exitCode: 1,
7120
+ stdout: [upgradeMessages.header, upgradeMessages.invalidVersion(explicitVersion)].join(`
7121
+ `)
7122
+ };
7123
+ }
7124
+ const analysis = await analyseConfigFiles(opencodeConfigPaths, port);
7125
+ if (analysis.parseError !== undefined) {
7126
+ return {
7127
+ exitCode: 1,
7128
+ stdout: [upgradeMessages.header, analysis.parseError].join(`
7129
+ `)
7130
+ };
7131
+ }
7132
+ if (analysis.files.length === 0) {
7133
+ if (analysis.localOpenteamEntries.length > 0) {
7134
+ return {
7135
+ exitCode: 1,
7136
+ stdout: [
7137
+ upgradeMessages.header,
7138
+ upgradeMessages.localFileOnlyHeader,
7139
+ ...analysis.localOpenteamEntries.map((entry) => upgradeMessages.localFileOnlyEntry(entry)),
7140
+ upgradeMessages.localFileOnlyRemedy
7141
+ ].join(`
7142
+ `)
7143
+ };
7144
+ }
7145
+ return {
7146
+ exitCode: 1,
7147
+ stdout: [
7148
+ upgradeMessages.header,
7149
+ analysis.foundConfigFile ? upgradeMessages.noPluginEntry : upgradeMessages.noConfigFound(opencodeConfigPaths)
7150
+ ].join(`
7151
+ `)
7152
+ };
7153
+ }
7154
+ let targetVersion;
7155
+ if (explicitVersion !== undefined) {
7156
+ const verified = await verifyVersionExists(explicitVersion, port.fetch);
7157
+ if (!verified.ok) {
7158
+ return { exitCode: 1, stdout: [upgradeMessages.header, verified.error].join(`
7159
+ `) };
7160
+ }
7161
+ targetVersion = explicitVersion;
7162
+ } else {
7163
+ const latest = await fetchLatestVersion(port.fetch);
7164
+ if (!latest.ok) {
7165
+ return { exitCode: 1, stdout: [upgradeMessages.header, latest.error].join(`
7166
+ `) };
7167
+ }
7168
+ targetVersion = latest.version;
7169
+ }
7170
+ const localWarnings = analysis.localOpenteamEntries.map((entry) => upgradeMessages.localFileSkipped(entry));
7171
+ if (explicitVersion === undefined) {
7172
+ for (const file of analysis.files) {
7173
+ for (const currentVersion of file.currentVersions) {
7174
+ if (currentVersion !== null && isStrictSemver(currentVersion) && compareSemver(currentVersion, targetVersion) > 0) {
7175
+ return {
7176
+ exitCode: 1,
7177
+ stdout: [
7178
+ upgradeMessages.header,
7179
+ upgradeMessages.noDowngrade({
7180
+ current: currentVersion,
7181
+ target: targetVersion
7182
+ })
7183
+ ].join(`
7184
+ `)
7185
+ };
7186
+ }
7187
+ }
7188
+ }
7189
+ }
7190
+ if (parsedArgs.value.check) {
7191
+ const lines = [upgradeMessages.header];
7192
+ let needsUpdate = false;
7193
+ for (const file of analysis.files) {
7194
+ for (const currentVersion of file.currentVersions) {
7195
+ if (currentVersion === targetVersion) {
7196
+ lines.push(upgradeMessages.checkAlreadyCurrent({
7197
+ path: file.path,
7198
+ version: targetVersion
7199
+ }));
7200
+ } else {
7201
+ lines.push(upgradeMessages.checkNeedsUpdate({
7202
+ path: file.path,
7203
+ current: currentVersion,
7204
+ target: targetVersion
7205
+ }));
7206
+ needsUpdate = true;
7207
+ }
7208
+ }
7209
+ }
7210
+ lines.push(...localWarnings);
7211
+ if (needsUpdate) {
7212
+ lines.push(upgradeMessages.checkHint(explicitVersion));
7213
+ }
7214
+ return { exitCode: 0, stdout: lines.join(`
7215
+ `) };
7216
+ }
7217
+ const nextSpec = pinnedPluginSpec(targetVersion);
7218
+ const plans = [];
7219
+ for (const file of analysis.files) {
7220
+ const nextContent = buildUpdatedContent(file.originalContent, file.replacements, nextSpec);
7221
+ if (nextContent === file.originalContent) {
7222
+ continue;
7223
+ }
7224
+ const validationError = validateEffectivePluginUpdate(file.path, nextContent, file.replacements.length, nextSpec);
7225
+ if (validationError !== undefined) {
7226
+ return {
7227
+ exitCode: 1,
7228
+ stdout: [upgradeMessages.header, validationError].join(`
7229
+ `)
7230
+ };
7231
+ }
7232
+ plans.push({
7233
+ path: file.path,
7234
+ expectedContent: file.originalContent,
7235
+ nextContent
7236
+ });
7237
+ }
7238
+ if (plans.length === 0) {
7239
+ return {
7240
+ exitCode: 0,
7241
+ stdout: [
7242
+ upgradeMessages.header,
7243
+ ...localWarnings,
7244
+ upgradeMessages.alreadyCurrent(targetVersion)
7245
+ ].join(`
7246
+ `)
7247
+ };
7248
+ }
7249
+ const written = [];
7250
+ const failed = [];
7251
+ for (const plan2 of plans) {
7252
+ try {
7253
+ await port.writeOpencodeConfigFile(plan2);
7254
+ written.push(plan2.path);
7255
+ } catch (error) {
7256
+ failed.push({
7257
+ path: plan2.path,
7258
+ error: error instanceof Error ? error.message : String(error)
7259
+ });
7260
+ }
7261
+ }
7262
+ if (failed.length === 0) {
7263
+ return renderSuccess(targetVersion, localWarnings, written);
7264
+ }
7265
+ if (written.length === 0) {
7266
+ return renderFailedWrites(localWarnings, failed);
7267
+ }
7268
+ return renderPartialSuccess(targetVersion, localWarnings, written, failed);
7269
+ }
7270
+
7271
+ // src/config/graphFeatureGate.ts
7272
+ function checkCertificate(status, name) {
7273
+ if (status.status === "absent") {
7274
+ return {
7275
+ code: `${name}-certificate-absent`,
7276
+ detail: `${name} certificate not found — run the ${name} certification suite to produce it`
7277
+ };
7278
+ }
7279
+ if (status.status === "invalid") {
7280
+ return {
7281
+ code: `${name}-certificate-invalid`,
7282
+ detail: `${name} certificate rejected by recomputing parser: ${status.code} — ${status.detail}`
7283
+ };
7284
+ }
7285
+ return;
7286
+ }
7287
+ function evaluateGraphGate(input) {
7288
+ const violations = [];
7289
+ const advisories = [];
7290
+ const shadowV = checkCertificate(input.shadow, "shadow");
7291
+ if (shadowV !== undefined)
7292
+ violations.push(shadowV);
7293
+ const releaseV = checkCertificate(input.release, "release");
7294
+ if (releaseV !== undefined)
7295
+ violations.push(releaseV);
7296
+ const soakV = checkCertificate(input.soak, "soak");
7297
+ if (soakV !== undefined)
7298
+ advisories.push(soakV);
7299
+ if (input.migration === "pending") {
7300
+ violations.push({
7301
+ code: "migration-pending",
7302
+ detail: "legacy migration has pending work — run the migration tool to completion before enabling active mode"
7303
+ });
7304
+ } else if (input.migration === "unknown") {
7305
+ violations.push({
7306
+ code: "migration-unknown",
7307
+ detail: "could not determine migration status — check ledger readability and storage access"
7308
+ });
7309
+ }
7310
+ if (!input.operatorApproval) {
7311
+ violations.push({
7312
+ code: "operator-approval-missing",
7313
+ detail: "set graph.operatorApproval to true in .opencode/openteam.json after reviewing all certificates and completing migration — this is a declaration of intent, not a proof"
7314
+ });
7315
+ }
7316
+ return {
7317
+ allowed: violations.length === 0,
7318
+ violations,
7319
+ advisories
7320
+ };
7321
+ }
7322
+ function isGateApplicable(mode) {
7323
+ return mode === "active";
7324
+ }
7325
+
7326
+ // src/config/opencode.ts
7327
+ function stripJsoncComments(src) {
7328
+ const out = [];
7329
+ let i = 0;
7330
+ while (i < src.length) {
7331
+ const ch = src[i];
7332
+ if (ch === undefined)
7333
+ break;
7334
+ if (ch === '"') {
7335
+ out.push(ch);
7336
+ i++;
7337
+ while (i < src.length) {
7338
+ const c = src[i];
7339
+ if (c === undefined)
7340
+ break;
7341
+ out.push(c);
7342
+ i++;
7343
+ if (c === "\\") {
7344
+ const escaped = src[i];
7345
+ if (escaped !== undefined) {
7346
+ out.push(escaped);
7347
+ i++;
7348
+ }
7349
+ } else if (c === '"') {
7350
+ break;
7351
+ }
7352
+ }
7353
+ } else if (ch === "/" && i + 1 < src.length) {
7354
+ if (src[i + 1] === "/") {
7355
+ i += 2;
7356
+ while (i < src.length && src[i] !== `
7357
+ `) {
7358
+ i++;
7359
+ }
7360
+ } else if (src[i + 1] === "*") {
7361
+ i += 2;
7362
+ while (i < src.length && !(src[i] === "*" && src[i + 1] === "/")) {
7363
+ i++;
7364
+ }
7365
+ if (i < src.length) {
7366
+ i += 2;
7367
+ }
7368
+ } else {
7369
+ out.push(ch);
7370
+ i++;
7371
+ }
7372
+ } else {
7373
+ out.push(ch);
7374
+ i++;
7375
+ }
7376
+ }
7377
+ return out.join("");
7378
+ }
7379
+ function isRecord4(v) {
7380
+ return typeof v === "object" && v !== null && !Array.isArray(v);
7381
+ }
7382
+ var OPENCODE_CONFIG_CANDIDATES = [
7383
+ ".opencode/opencode.json",
7384
+ "opencode.json"
7385
+ ];
7386
+ function mergeOpencodeConfigs(base, override) {
7387
+ const result = { ...base };
7388
+ for (const [key, value] of Object.entries(override)) {
7389
+ const baseVal = result[key];
7390
+ if (isRecord4(baseVal) && isRecord4(value)) {
7391
+ result[key] = mergeOpencodeConfigs(baseVal, value);
7392
+ } else {
7393
+ result[key] = value;
7394
+ }
7395
+ }
7396
+ return result;
7397
+ }
7398
+
7399
+ // src/graph/certificate.ts
7400
+ import { z as z17 } from "zod";
7401
+
7402
+ // src/contract/opencode.ts
7403
+ var SUPPORTED_OPENCODE_VERSIONS = [
7404
+ "1.17.13",
7405
+ "1.18.18",
7406
+ "1.18.19"
7407
+ ];
7408
+ var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
7409
+ function parseSemver(version) {
7410
+ const normalized = version.trim().replace(/^[=^~v]+/, "");
7411
+ const match = SEMVER_RE.exec(normalized);
7412
+ if (match === null) {
7413
+ throw new Error(`invalid semver: "${version}"`);
7414
+ }
7415
+ return {
7416
+ major: Number(match[1]),
7417
+ minor: Number(match[2]),
7418
+ patch: Number(match[3])
7419
+ };
7420
+ }
7421
+ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
7422
+ if (certifiedVersion === liveVersion) {
7423
+ return true;
7424
+ }
7425
+ let certified;
5547
7426
  let live;
5548
7427
  try {
5549
7428
  certified = parseSemver(certifiedVersion);
@@ -5558,31 +7437,31 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
5558
7437
  }
5559
7438
 
5560
7439
  // src/graph/soakLedger.ts
5561
- import { z as z15 } from "zod";
5562
- var Sha256Schema2 = z15.string().regex(/^[0-9a-f]{64}$/);
5563
- var SoakPlatformSchema = z15.enum(["linux", "win32", "darwin"]);
5564
- var PrivacyPairSchema = z15.object({
5565
- surfacesScanned: z15.number().int().nonnegative(),
5566
- rawFindings: z15.number().int().nonnegative()
7440
+ import { z as z16 } from "zod";
7441
+ var Sha256Schema2 = z16.string().regex(/^[0-9a-f]{64}$/);
7442
+ var SoakPlatformSchema = z16.enum(["linux", "win32", "darwin"]);
7443
+ var PrivacyPairSchema = z16.object({
7444
+ surfacesScanned: z16.number().int().nonnegative(),
7445
+ rawFindings: z16.number().int().nonnegative()
5567
7446
  }).strict();
5568
- var DuplicateEffectPairSchema = z15.object({
5569
- effectsExamined: z15.number().int().nonnegative(),
5570
- duplicatesFound: z15.number().int().nonnegative()
7447
+ var DuplicateEffectPairSchema = z16.object({
7448
+ effectsExamined: z16.number().int().nonnegative(),
7449
+ duplicatesFound: z16.number().int().nonnegative()
5571
7450
  }).strict();
5572
- var ModelVerificationPairSchema = z15.object({
5573
- nodesChecked: z15.number().int().nonnegative(),
5574
- unverified: z15.number().int().nonnegative(),
5575
- mismatched: z15.number().int().nonnegative()
7451
+ var ModelVerificationPairSchema = z16.object({
7452
+ nodesChecked: z16.number().int().nonnegative(),
7453
+ unverified: z16.number().int().nonnegative(),
7454
+ mismatched: z16.number().int().nonnegative()
5576
7455
  }).strict();
5577
- var SoakObservationSchema = z15.object({
5578
- version: z15.literal(1),
5579
- seq: z15.number().int().nonnegative(),
5580
- timestamp: z15.string().datetime(),
7456
+ var SoakObservationSchema = z16.object({
7457
+ version: z16.literal(1),
7458
+ seq: z16.number().int().nonnegative(),
7459
+ timestamp: z16.string().datetime(),
5581
7460
  platform: SoakPlatformSchema,
5582
- opencodeVersion: z15.string().min(1),
5583
- provenance: z15.enum(["genuine-usage", "ci-synthetic"]),
7461
+ opencodeVersion: z16.string().min(1),
7462
+ provenance: z16.enum(["genuine-usage", "ci-synthetic"]),
5584
7463
  traceDigest: Sha256Schema2,
5585
- criticalDivergences: z15.number().int().nonnegative().nullable(),
7464
+ criticalDivergences: z16.number().int().nonnegative().nullable(),
5586
7465
  privacy: PrivacyPairSchema.nullable(),
5587
7466
  duplicateEffects: DuplicateEffectPairSchema.nullable(),
5588
7467
  modelVerification: ModelVerificationPairSchema.nullable(),
@@ -5653,7 +7532,7 @@ function canonicalLedger(chains) {
5653
7532
 
5654
7533
  // src/graph/certificate.ts
5655
7534
  var SHADOW_CERTIFICATE_MIN_RUNS = 1000;
5656
- var DivergenceCodeSchema = z16.enum([
7535
+ var DivergenceCodeSchema = z17.enum([
5657
7536
  "status",
5658
7537
  "order",
5659
7538
  "roles",
@@ -5661,50 +7540,50 @@ var DivergenceCodeSchema = z16.enum([
5661
7540
  "fixes",
5662
7541
  "outcome"
5663
7542
  ]);
5664
- var ProvenanceSchema = z16.enum([
7543
+ var ProvenanceSchema = z17.enum([
5665
7544
  "recomputed",
5666
7545
  "compiled",
5667
7546
  "validated",
5668
7547
  "carried"
5669
7548
  ]);
5670
- var EvidenceSchema = z16.object({
5671
- opencodeVersion: z16.string().min(1),
5672
- parity: z16.object({
5673
- runs: z16.number().int().nonnegative(),
5674
- match: z16.number().int().nonnegative(),
5675
- divergent: z16.number().int().nonnegative(),
5676
- inconclusive: z16.number().int().nonnegative(),
5677
- divergences: z16.array(DivergenceCodeSchema)
7549
+ var EvidenceSchema = z17.object({
7550
+ opencodeVersion: z17.string().min(1),
7551
+ parity: z17.object({
7552
+ runs: z17.number().int().nonnegative(),
7553
+ match: z17.number().int().nonnegative(),
7554
+ divergent: z17.number().int().nonnegative(),
7555
+ inconclusive: z17.number().int().nonnegative(),
7556
+ divergences: z17.array(DivergenceCodeSchema)
5678
7557
  }).strict(),
5679
- legacy: z16.object({
5680
- observed: z16.number().int().nonnegative(),
5681
- duplicated: z16.number().int().nonnegative()
7558
+ legacy: z17.object({
7559
+ observed: z17.number().int().nonnegative(),
7560
+ duplicated: z17.number().int().nonnegative()
5682
7561
  }).strict(),
5683
- privacy: z16.object({
5684
- surfacesScanned: z16.number().int().nonnegative(),
5685
- rawFindings: z16.number().int().nonnegative()
7562
+ privacy: z17.object({
7563
+ surfacesScanned: z17.number().int().nonnegative(),
7564
+ rawFindings: z17.number().int().nonnegative()
5686
7565
  }).strict(),
5687
- overhead: z16.object({
5688
- p95Millis: z16.number().nonnegative(),
5689
- budgetMillis: z16.number().positive(),
5690
- samples: z16.number().int().nonnegative()
7566
+ overhead: z17.object({
7567
+ p95Millis: z17.number().nonnegative(),
7568
+ budgetMillis: z17.number().positive(),
7569
+ samples: z17.number().int().nonnegative()
5691
7570
  }).strict(),
5692
- provenance: z16.record(DivergenceCodeSchema, ProvenanceSchema)
7571
+ provenance: z17.record(DivergenceCodeSchema, ProvenanceSchema)
5693
7572
  }).strict();
5694
- var CertificateSchema = z16.object({
5695
- version: z16.literal(1),
5696
- certificate: z16.literal("graph-shadow"),
5697
- opencodeVersion: z16.string().min(1),
7573
+ var CertificateSchema = z17.object({
7574
+ version: z17.literal(1),
7575
+ certificate: z17.literal("graph-shadow"),
7576
+ opencodeVersion: z17.string().min(1),
5698
7577
  evidence: EvidenceSchema,
5699
- verdict: z16.enum(["pass", "fail"]),
5700
- failedGates: z16.array(z16.enum([
7578
+ verdict: z17.enum(["pass", "fail"]),
7579
+ failedGates: z17.array(z17.enum([
5701
7580
  "sample",
5702
7581
  "parity",
5703
7582
  "one-legacy-execution",
5704
7583
  "privacy",
5705
7584
  "overhead"
5706
7585
  ])),
5707
- digest: z16.string().regex(/^[0-9a-f]{64}$/)
7586
+ digest: z17.string().regex(/^[0-9a-f]{64}$/)
5708
7587
  }).strict();
5709
7588
 
5710
7589
  class ShadowCertificateError extends Error {
@@ -5819,37 +7698,37 @@ function parseShadowCertificate(value, opencodeVersion) {
5819
7698
  }
5820
7699
  return certificate;
5821
7700
  }
5822
- var ReleaseDeterminismSchema = z16.object({ runs: z16.number().int().nonnegative(), allMatch: z16.boolean() }).strict();
5823
- var ReleaseCrashRecoverySchema = z16.object({
5824
- scenarios: z16.number().int().nonnegative(),
5825
- allConverge: z16.boolean()
7701
+ var ReleaseDeterminismSchema = z17.object({ runs: z17.number().int().nonnegative(), allMatch: z17.boolean() }).strict();
7702
+ var ReleaseCrashRecoverySchema = z17.object({
7703
+ scenarios: z17.number().int().nonnegative(),
7704
+ allConverge: z17.boolean()
5826
7705
  }).strict();
5827
- var ReleaseReplayEquivalenceSchema = z16.object({
5828
- checks: z16.number().int().nonnegative(),
5829
- allEquivalent: z16.boolean()
7706
+ var ReleaseReplayEquivalenceSchema = z17.object({
7707
+ checks: z17.number().int().nonnegative(),
7708
+ allEquivalent: z17.boolean()
5830
7709
  }).strict();
5831
- var ReleasePrivacySchema = z16.object({
5832
- surfacesScanned: z16.number().int().nonnegative(),
5833
- rawFindings: z16.number().int().nonnegative()
7710
+ var ReleasePrivacySchema = z17.object({
7711
+ surfacesScanned: z17.number().int().nonnegative(),
7712
+ rawFindings: z17.number().int().nonnegative()
5834
7713
  }).strict();
5835
- var ReleaseContractSchema = z16.object({
5836
- roundTrips: z16.number().int().nonnegative(),
5837
- allSettled: z16.boolean()
7714
+ var ReleaseContractSchema = z17.object({
7715
+ roundTrips: z17.number().int().nonnegative(),
7716
+ allSettled: z17.boolean()
5838
7717
  }).strict();
5839
- var ReleaseHealthSchema = z16.object({
5840
- samples: z16.number().int().nonnegative(),
5841
- bounded: z16.boolean(),
5842
- idempotent: z16.boolean()
7718
+ var ReleaseHealthSchema = z17.object({
7719
+ samples: z17.number().int().nonnegative(),
7720
+ bounded: z17.boolean(),
7721
+ idempotent: z17.boolean()
5843
7722
  }).strict();
5844
- var ReleasePerformanceSchema = z16.object({
5845
- p95Millis: z16.number().nonnegative(),
5846
- budgetMillis: z16.number().positive(),
5847
- samples: z16.number().int().nonnegative()
7723
+ var ReleasePerformanceSchema = z17.object({
7724
+ p95Millis: z17.number().nonnegative(),
7725
+ budgetMillis: z17.number().positive(),
7726
+ samples: z17.number().int().nonnegative()
5848
7727
  }).strict();
5849
- var ReleaseEvidenceSchema = z16.object({
5850
- opencodeVersion: z16.string().min(1),
5851
- platform: z16.enum(["linux", "win32"]),
5852
- shadowCertificateDigest: z16.string().regex(/^[0-9a-f]{64}$/),
7728
+ var ReleaseEvidenceSchema = z17.object({
7729
+ opencodeVersion: z17.string().min(1),
7730
+ platform: z17.enum(["linux", "win32"]),
7731
+ shadowCertificateDigest: z17.string().regex(/^[0-9a-f]{64}$/),
5853
7732
  determinism: ReleaseDeterminismSchema,
5854
7733
  crashRecovery: ReleaseCrashRecoverySchema,
5855
7734
  replayEquivalence: ReleaseReplayEquivalenceSchema,
@@ -5858,15 +7737,15 @@ var ReleaseEvidenceSchema = z16.object({
5858
7737
  health: ReleaseHealthSchema,
5859
7738
  performance: ReleasePerformanceSchema
5860
7739
  }).strict();
5861
- var ReleaseCertificateSchema = z16.object({
5862
- version: z16.literal(2),
5863
- certificate: z16.literal("graph-release"),
5864
- platform: z16.enum(["linux", "win32"]),
5865
- opencodeVersion: z16.string().min(1),
5866
- shadowCertificateDigest: z16.string().regex(/^[0-9a-f]{64}$/),
7740
+ var ReleaseCertificateSchema = z17.object({
7741
+ version: z17.literal(2),
7742
+ certificate: z17.literal("graph-release"),
7743
+ platform: z17.enum(["linux", "win32"]),
7744
+ opencodeVersion: z17.string().min(1),
7745
+ shadowCertificateDigest: z17.string().regex(/^[0-9a-f]{64}$/),
5867
7746
  evidence: ReleaseEvidenceSchema,
5868
- verdict: z16.enum(["pass", "fail"]),
5869
- failedGates: z16.array(z16.enum([
7747
+ verdict: z17.enum(["pass", "fail"]),
7748
+ failedGates: z17.array(z17.enum([
5870
7749
  "shadow-valid",
5871
7750
  "determinism",
5872
7751
  "crash-recovery",
@@ -5876,7 +7755,7 @@ var ReleaseCertificateSchema = z16.object({
5876
7755
  "health",
5877
7756
  "performance"
5878
7757
  ])),
5879
- digest: z16.string().regex(/^[0-9a-f]{64}$/)
7758
+ digest: z17.string().regex(/^[0-9a-f]{64}$/)
5880
7759
  }).strict();
5881
7760
 
5882
7761
  class ReleaseCertificateError extends Error {
@@ -6004,7 +7883,7 @@ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
6004
7883
  }
6005
7884
  return certificate;
6006
7885
  }
6007
- var ShadowDivergenceCodeSchema = z16.enum([
7886
+ var ShadowDivergenceCodeSchema = z17.enum([
6008
7887
  "status",
6009
7888
  "order",
6010
7889
  "roles",
@@ -6013,13 +7892,13 @@ var ShadowDivergenceCodeSchema = z16.enum([
6013
7892
  "outcome"
6014
7893
  ]);
6015
7894
  var RECOMPUTED_DIVERGENCE_CODES = ["fixes"];
6016
- var RecomputedDivergenceCodeSchema = z16.enum(RECOMPUTED_DIVERGENCE_CODES);
6017
- var SoakPolicySchema = z16.object({
6018
- minimumGenuineObservations: z16.number().int().positive(),
6019
- minimumDistinctDays: z16.number().int().positive(),
6020
- requireGenuineUsageOnAllPlatforms: z16.boolean(),
6021
- supportedOpencodeVersions: z16.array(z16.string().min(1)).min(1),
6022
- criticalDivergenceCodes: z16.array(RecomputedDivergenceCodeSchema).min(1)
7895
+ var RecomputedDivergenceCodeSchema = z17.enum(RECOMPUTED_DIVERGENCE_CODES);
7896
+ var SoakPolicySchema = z17.object({
7897
+ minimumGenuineObservations: z17.number().int().positive(),
7898
+ minimumDistinctDays: z17.number().int().positive(),
7899
+ requireGenuineUsageOnAllPlatforms: z17.boolean(),
7900
+ supportedOpencodeVersions: z17.array(z17.string().min(1)).min(1),
7901
+ criticalDivergenceCodes: z17.array(RecomputedDivergenceCodeSchema).min(1)
6023
7902
  }).strict();
6024
7903
  var DEFAULT_SOAK_POLICY = {
6025
7904
  minimumGenuineObservations: 100,
@@ -6028,46 +7907,46 @@ var DEFAULT_SOAK_POLICY = {
6028
7907
  supportedOpencodeVersions: [...SUPPORTED_OPENCODE_VERSIONS],
6029
7908
  criticalDivergenceCodes: ["fixes"]
6030
7909
  };
6031
- var SoakPlatformEnumSchema = z16.enum(["linux", "win32", "darwin"]);
6032
- var SoakPrivacyPairEvidenceSchema = z16.object({
6033
- surfacesScanned: z16.number().int().nonnegative(),
6034
- rawFindings: z16.number().int().nonnegative()
7910
+ var SoakPlatformEnumSchema = z17.enum(["linux", "win32", "darwin"]);
7911
+ var SoakPrivacyPairEvidenceSchema = z17.object({
7912
+ surfacesScanned: z17.number().int().nonnegative(),
7913
+ rawFindings: z17.number().int().nonnegative()
6035
7914
  }).strict();
6036
- var SoakDuplicateEffectPairEvidenceSchema = z16.object({
6037
- effectsExamined: z16.number().int().nonnegative(),
6038
- duplicatesFound: z16.number().int().nonnegative()
7915
+ var SoakDuplicateEffectPairEvidenceSchema = z17.object({
7916
+ effectsExamined: z17.number().int().nonnegative(),
7917
+ duplicatesFound: z17.number().int().nonnegative()
6039
7918
  }).strict();
6040
- var SoakModelVerificationPairEvidenceSchema = z16.object({
6041
- nodesChecked: z16.number().int().nonnegative(),
6042
- unverified: z16.number().int().nonnegative(),
6043
- mismatched: z16.number().int().nonnegative()
7919
+ var SoakModelVerificationPairEvidenceSchema = z17.object({
7920
+ nodesChecked: z17.number().int().nonnegative(),
7921
+ unverified: z17.number().int().nonnegative(),
7922
+ mismatched: z17.number().int().nonnegative()
6044
7923
  }).strict();
6045
- var SoakEvidenceSchema = z16.object({
6046
- totalObservations: z16.number().int().nonnegative(),
6047
- genuineUsageObservations: z16.number().int().nonnegative(),
6048
- ciSyntheticObservations: z16.number().int().nonnegative(),
6049
- recorderCount: z16.number().int().nonnegative(),
6050
- ineligibleObservations: z16.number().int().nonnegative(),
6051
- firstTimestamp: z16.string().datetime().nullable(),
6052
- lastTimestamp: z16.string().datetime().nullable(),
6053
- distinctDays: z16.number().int().nonnegative(),
6054
- criticalDivergences: z16.number().int().nonnegative().nullable(),
7924
+ var SoakEvidenceSchema = z17.object({
7925
+ totalObservations: z17.number().int().nonnegative(),
7926
+ genuineUsageObservations: z17.number().int().nonnegative(),
7927
+ ciSyntheticObservations: z17.number().int().nonnegative(),
7928
+ recorderCount: z17.number().int().nonnegative(),
7929
+ ineligibleObservations: z17.number().int().nonnegative(),
7930
+ firstTimestamp: z17.string().datetime().nullable(),
7931
+ lastTimestamp: z17.string().datetime().nullable(),
7932
+ distinctDays: z17.number().int().nonnegative(),
7933
+ criticalDivergences: z17.number().int().nonnegative().nullable(),
6055
7934
  privacy: SoakPrivacyPairEvidenceSchema.nullable(),
6056
7935
  duplicateEffects: SoakDuplicateEffectPairEvidenceSchema.nullable(),
6057
7936
  modelVerification: SoakModelVerificationPairEvidenceSchema.nullable(),
6058
- platformsGenuine: z16.array(SoakPlatformEnumSchema),
6059
- platformsSynthetic: z16.array(SoakPlatformEnumSchema),
6060
- opencodeVersionsObserved: z16.array(z16.string().min(1)),
6061
- invalidChains: z16.number().int().nonnegative()
7937
+ platformsGenuine: z17.array(SoakPlatformEnumSchema),
7938
+ platformsSynthetic: z17.array(SoakPlatformEnumSchema),
7939
+ opencodeVersionsObserved: z17.array(z17.string().min(1)),
7940
+ invalidChains: z17.number().int().nonnegative()
6062
7941
  }).strict();
6063
- var SoakCertificateSchema = z16.object({
6064
- version: z16.literal(1),
6065
- certificate: z16.literal("graph-soak"),
6066
- trustBoundary: z16.literal("contributors"),
7942
+ var SoakCertificateSchema = z17.object({
7943
+ version: z17.literal(1),
7944
+ certificate: z17.literal("graph-soak"),
7945
+ trustBoundary: z17.literal("contributors"),
6067
7946
  policy: SoakPolicySchema,
6068
7947
  evidence: SoakEvidenceSchema,
6069
- verdict: z16.enum(["pass", "fail"]),
6070
- failedGates: z16.array(z16.enum([
7948
+ verdict: z17.enum(["pass", "fail"]),
7949
+ failedGates: z17.array(z17.enum([
6071
7950
  "minimum-observations",
6072
7951
  "minimum-duration",
6073
7952
  "chain-integrity",
@@ -6078,8 +7957,8 @@ var SoakCertificateSchema = z16.object({
6078
7957
  "os-diversity",
6079
7958
  "opencode-pin"
6080
7959
  ])),
6081
- ledgerDigest: z16.string().regex(/^[0-9a-f]{64}$/),
6082
- digest: z16.string().regex(/^[0-9a-f]{64}$/)
7960
+ ledgerDigest: z17.string().regex(/^[0-9a-f]{64}$/),
7961
+ digest: z17.string().regex(/^[0-9a-f]{64}$/)
6083
7962
  }).strict();
6084
7963
 
6085
7964
  class SoakCertificateError extends Error {
@@ -6205,7 +8084,7 @@ function parseSoakCertificate(value, expectedPolicy, expectedLedgerDigest) {
6205
8084
  }
6206
8085
 
6207
8086
  // src/lifecycle/root.ts
6208
- import { isAbsolute, normalize, relative, resolve as resolve3 } from "node:path";
8087
+ import { isAbsolute, normalize, relative as relative2, resolve as resolve4 } from "node:path";
6209
8088
 
6210
8089
  // src/messages/lifecycleStorage.ts
6211
8090
  var lifecycleStorageMessages = {
@@ -6214,12 +8093,12 @@ var lifecycleStorageMessages = {
6214
8093
 
6215
8094
  // src/lifecycle/root.ts
6216
8095
  var resolveLifecycleRoot = (options) => {
6217
- const workspaceRoot = resolve3(options.workspaceRoot);
8096
+ const workspaceRoot = resolve4(options.workspaceRoot);
6218
8097
  if (isAbsolute(options.configuredRoot)) {
6219
- return normalize(resolve3(options.configuredRoot));
8098
+ return normalize(resolve4(options.configuredRoot));
6220
8099
  }
6221
- const resolved = resolve3(workspaceRoot, options.configuredRoot);
6222
- const workspaceRelative = relative(workspaceRoot, resolved);
8100
+ const resolved = resolve4(workspaceRoot, options.configuredRoot);
8101
+ const workspaceRelative = relative2(workspaceRoot, resolved);
6223
8102
  if (workspaceRelative === ".." || workspaceRelative.startsWith(`..\\`) || workspaceRelative.startsWith("../") || isAbsolute(workspaceRelative)) {
6224
8103
  throw new Error(lifecycleStorageMessages.configuredRootEscapesWorkspace);
6225
8104
  }
@@ -6227,16 +8106,16 @@ var resolveLifecycleRoot = (options) => {
6227
8106
  };
6228
8107
 
6229
8108
  // src/lifecycle/schema.ts
6230
- import { z as z17 } from "zod";
6231
- var nonEmptyString = z17.string().min(1);
6232
- var nonNegativeInteger = z17.number().int().nonnegative();
6233
- var positiveInteger = z17.number().int().positive();
6234
- var modelSelectionSchema2 = z17.object({
8109
+ import { z as z18 } from "zod";
8110
+ var nonEmptyString = z18.string().min(1);
8111
+ var nonNegativeInteger = z18.number().int().nonnegative();
8112
+ var positiveInteger = z18.number().int().positive();
8113
+ var modelSelectionSchema2 = z18.object({
6235
8114
  providerID: nonEmptyString,
6236
8115
  modelID: nonEmptyString
6237
8116
  }).strict();
6238
- var lifecycleFailureSchema = z17.object({
6239
- class: z17.enum([
8117
+ var lifecycleFailureSchema = z18.object({
8118
+ class: z18.enum([
6240
8119
  "routing-blocked",
6241
8120
  "create-rejected",
6242
8121
  "create-no-id",
@@ -6258,25 +8137,25 @@ var lifecycleFailureSchema = z17.object({
6258
8137
  "interrupted",
6259
8138
  "unknown"
6260
8139
  ]),
6261
- statusCode: z17.number().int().min(100).max(599).optional(),
6262
- retryable: z17.boolean().optional()
8140
+ statusCode: z18.number().int().min(100).max(599).optional(),
8141
+ retryable: z18.boolean().optional()
6263
8142
  }).strict();
6264
- var lifecycleRootOperationSchema = z17.object({
8143
+ var lifecycleRootOperationSchema = z18.object({
6265
8144
  operationID: nonEmptyString,
6266
8145
  taskID: nonEmptyString,
6267
- kind: z17.enum(["coordinator", "graph-node"]),
8146
+ kind: z18.enum(["coordinator", "graph-node"]),
6268
8147
  roleID: nonEmptyString,
6269
8148
  agentID: nonEmptyString.optional(),
6270
8149
  sessionID: nonEmptyString.optional()
6271
8150
  }).strict();
6272
- var lifecycleRunStartInputSchema = z17.object({
8151
+ var lifecycleRunStartInputSchema = z18.object({
6273
8152
  runID: nonEmptyString,
6274
- source: z17.enum(["plugin", "cli"]),
6275
- executionAuthority: z17.enum(["coordinator", "graph"]),
8153
+ source: z18.enum(["plugin", "cli"]),
8154
+ executionAuthority: z18.enum(["coordinator", "graph"]),
6276
8155
  root: lifecycleRootOperationSchema
6277
8156
  }).strict();
6278
8157
  var LifecycleRunMetadataSchema = lifecycleRunStartInputSchema.extend({
6279
- version: z17.literal(1),
8158
+ version: z18.literal(1),
6280
8159
  createdAt: nonNegativeInteger
6281
8160
  }).strict();
6282
8161
  var attemptRefShape = {
@@ -6288,34 +8167,34 @@ var attemptRefShape = {
6288
8167
  retryIndex: nonNegativeInteger
6289
8168
  };
6290
8169
  var eventEnvelopeShape = {
6291
- v: z17.literal(1),
8170
+ v: z18.literal(1),
6292
8171
  seq: nonNegativeInteger,
6293
8172
  runID: nonEmptyString,
6294
8173
  at: nonNegativeInteger
6295
8174
  };
6296
8175
  var runStartedBodyShape = {
6297
- type: z17.literal("run.started"),
6298
- source: z17.enum(["plugin", "cli"]),
6299
- executionAuthority: z17.enum(["coordinator", "graph"]),
8176
+ type: z18.literal("run.started"),
8177
+ source: z18.enum(["plugin", "cli"]),
8178
+ executionAuthority: z18.enum(["coordinator", "graph"]),
6300
8179
  root: lifecycleRootOperationSchema
6301
8180
  };
6302
8181
  var operationQueuedBodyShape = {
6303
- type: z17.literal("operation.queued"),
8182
+ type: z18.literal("operation.queued"),
6304
8183
  operationID: nonEmptyString,
6305
8184
  taskID: nonEmptyString,
6306
- kind: z17.enum(["role", "graph-node"]),
8185
+ kind: z18.enum(["role", "graph-node"]),
6307
8186
  roleID: nonEmptyString,
6308
8187
  agentID: nonEmptyString.optional(),
6309
8188
  parentOperationID: nonEmptyString,
6310
- dependsOnOperationIDs: z17.array(nonEmptyString).readonly()
8189
+ dependsOnOperationIDs: z18.array(nonEmptyString).readonly()
6311
8190
  };
6312
8191
  var modelSelectedBodyShape = {
6313
- type: z17.literal("attempt.model-selected"),
8192
+ type: z18.literal("attempt.model-selected"),
6314
8193
  ...attemptRefShape,
6315
8194
  decisionID: nonEmptyString,
6316
8195
  model: modelSelectionSchema2,
6317
- routeKind: z17.enum(["local", "frontier"]),
6318
- selectionCause: z17.enum([
8196
+ routeKind: z18.enum(["local", "frontier"]),
8197
+ selectionCause: z18.enum([
6319
8198
  "initial",
6320
8199
  "retry-incomplete",
6321
8200
  "retry-transport",
@@ -6325,31 +8204,31 @@ var modelSelectedBodyShape = {
6325
8204
  runtimeID: nonEmptyString.optional()
6326
8205
  };
6327
8206
  var attemptQueuedBodyShape = {
6328
- type: z17.literal("attempt.queued"),
8207
+ type: z18.literal("attempt.queued"),
6329
8208
  ...attemptRefShape,
6330
8209
  runtimeID: nonEmptyString.optional()
6331
8210
  };
6332
8211
  var attemptStartedBodyShape = {
6333
- type: z17.literal("attempt.started"),
8212
+ type: z18.literal("attempt.started"),
6334
8213
  ...attemptRefShape
6335
8214
  };
6336
8215
  var sessionCreatedBodyShape = {
6337
- type: z17.literal("session.created"),
8216
+ type: z18.literal("session.created"),
6338
8217
  ...attemptRefShape,
6339
8218
  sessionID: nonEmptyString,
6340
8219
  parentSessionID: nonEmptyString.optional()
6341
8220
  };
6342
8221
  var attemptTerminalBodyShape = {
6343
- type: z17.literal("attempt.terminal"),
8222
+ type: z18.literal("attempt.terminal"),
6344
8223
  ...attemptRefShape,
6345
- outcome: z17.enum([
8224
+ outcome: z18.enum([
6346
8225
  "succeeded",
6347
8226
  "failed",
6348
8227
  "cancelled",
6349
8228
  "interrupted",
6350
8229
  "unknown"
6351
8230
  ]),
6352
- continuation: z17.enum([
8231
+ continuation: z18.enum([
6353
8232
  "none",
6354
8233
  "retry-same-model",
6355
8234
  "fallback-next-model",
@@ -6359,10 +8238,10 @@ var attemptTerminalBodyShape = {
6359
8238
  failure: lifecycleFailureSchema.optional()
6360
8239
  };
6361
8240
  var operationTerminalBodyShape = {
6362
- type: z17.literal("operation.terminal"),
8241
+ type: z18.literal("operation.terminal"),
6363
8242
  operationID: nonEmptyString,
6364
8243
  taskID: nonEmptyString,
6365
- outcome: z17.enum([
8244
+ outcome: z18.enum([
6366
8245
  "succeeded",
6367
8246
  "failed",
6368
8247
  "cancelled",
@@ -6375,8 +8254,8 @@ var operationTerminalBodyShape = {
6375
8254
  failure: lifecycleFailureSchema.optional()
6376
8255
  };
6377
8256
  var runTerminalBodyShape = {
6378
- type: z17.literal("run.terminal"),
6379
- outcome: z17.enum([
8257
+ type: z18.literal("run.terminal"),
8258
+ outcome: z18.enum([
6380
8259
  "succeeded",
6381
8260
  "failed",
6382
8261
  "cancelled",
@@ -6387,39 +8266,39 @@ var runTerminalBodyShape = {
6387
8266
  failure: lifecycleFailureSchema.optional()
6388
8267
  };
6389
8268
  var bodySchemas = [
6390
- z17.object(runStartedBodyShape).strict(),
6391
- z17.object(operationQueuedBodyShape).strict(),
6392
- z17.object(modelSelectedBodyShape).strict(),
6393
- z17.object(attemptQueuedBodyShape).strict(),
6394
- z17.object(attemptStartedBodyShape).strict(),
6395
- z17.object(sessionCreatedBodyShape).strict(),
6396
- z17.object(attemptTerminalBodyShape).strict(),
6397
- z17.object(operationTerminalBodyShape).strict(),
6398
- z17.object(runTerminalBodyShape).strict()
8269
+ z18.object(runStartedBodyShape).strict(),
8270
+ z18.object(operationQueuedBodyShape).strict(),
8271
+ z18.object(modelSelectedBodyShape).strict(),
8272
+ z18.object(attemptQueuedBodyShape).strict(),
8273
+ z18.object(attemptStartedBodyShape).strict(),
8274
+ z18.object(sessionCreatedBodyShape).strict(),
8275
+ z18.object(attemptTerminalBodyShape).strict(),
8276
+ z18.object(operationTerminalBodyShape).strict(),
8277
+ z18.object(runTerminalBodyShape).strict()
6399
8278
  ];
6400
8279
  var appendBodySchemas = bodySchemas.slice(1);
6401
- var LifecycleAppendEventSchema = z17.discriminatedUnion("type", appendBodySchemas);
6402
- var LifecycleEventSchema = z17.discriminatedUnion("type", [
6403
- z17.object({ ...eventEnvelopeShape, ...runStartedBodyShape }).strict(),
6404
- z17.object({ ...eventEnvelopeShape, ...operationQueuedBodyShape }).strict(),
6405
- z17.object({ ...eventEnvelopeShape, ...modelSelectedBodyShape }).strict(),
6406
- z17.object({ ...eventEnvelopeShape, ...attemptQueuedBodyShape }).strict(),
6407
- z17.object({ ...eventEnvelopeShape, ...attemptStartedBodyShape }).strict(),
6408
- z17.object({ ...eventEnvelopeShape, ...sessionCreatedBodyShape }).strict(),
6409
- z17.object({ ...eventEnvelopeShape, ...attemptTerminalBodyShape }).strict(),
6410
- z17.object({ ...eventEnvelopeShape, ...operationTerminalBodyShape }).strict(),
6411
- z17.object({ ...eventEnvelopeShape, ...runTerminalBodyShape }).strict()
8280
+ var LifecycleAppendEventSchema = z18.discriminatedUnion("type", appendBodySchemas);
8281
+ var LifecycleEventSchema = z18.discriminatedUnion("type", [
8282
+ z18.object({ ...eventEnvelopeShape, ...runStartedBodyShape }).strict(),
8283
+ z18.object({ ...eventEnvelopeShape, ...operationQueuedBodyShape }).strict(),
8284
+ z18.object({ ...eventEnvelopeShape, ...modelSelectedBodyShape }).strict(),
8285
+ z18.object({ ...eventEnvelopeShape, ...attemptQueuedBodyShape }).strict(),
8286
+ z18.object({ ...eventEnvelopeShape, ...attemptStartedBodyShape }).strict(),
8287
+ z18.object({ ...eventEnvelopeShape, ...sessionCreatedBodyShape }).strict(),
8288
+ z18.object({ ...eventEnvelopeShape, ...attemptTerminalBodyShape }).strict(),
8289
+ z18.object({ ...eventEnvelopeShape, ...operationTerminalBodyShape }).strict(),
8290
+ z18.object({ ...eventEnvelopeShape, ...runTerminalBodyShape }).strict()
6412
8291
  ]);
6413
- var LifecycleWriterLeaseInputSchema = z17.object({
6414
- writer: z17.object({
8292
+ var LifecycleWriterLeaseInputSchema = z18.object({
8293
+ writer: z18.object({
6415
8294
  processID: positiveInteger,
6416
8295
  processInstanceID: nonEmptyString
6417
8296
  }).strict(),
6418
8297
  now: nonNegativeInteger,
6419
8298
  leaseDurationMs: positiveInteger
6420
8299
  }).strict();
6421
- var LifecycleWriterLeaseSchema = z17.object({
6422
- version: z17.literal(1),
8300
+ var LifecycleWriterLeaseSchema = z18.object({
8301
+ version: z18.literal(1),
6423
8302
  runID: nonEmptyString,
6424
8303
  writerToken: nonEmptyString,
6425
8304
  processID: positiveInteger,
@@ -7304,9 +9183,9 @@ function createRuntimeConcurrencyLimiter(options) {
7304
9183
  }
7305
9184
  const state = stateFor(runtimeId);
7306
9185
  const queuedAt = options.now();
7307
- return new Promise((resolve4) => {
9186
+ return new Promise((resolve5) => {
7308
9187
  state.queues[priority].push((release) => {
7309
- resolve4({ release, waitedMs: options.now() - queuedAt });
9188
+ resolve5({ release, waitedMs: options.now() - queuedAt });
7310
9189
  });
7311
9190
  });
7312
9191
  },
@@ -7341,30 +9220,82 @@ function embeddingsEndpoint(baseURL) {
7341
9220
  return `${normalizeBaseURL(baseURL)}/embeddings`;
7342
9221
  }
7343
9222
  function errorMessage(error) {
9223
+ const chain = errorChain(error);
9224
+ const dnsDetail = dnsErrorMessage(chain);
9225
+ if (dnsDetail !== undefined) {
9226
+ return dnsDetail;
9227
+ }
9228
+ for (const entry of chain) {
9229
+ if (!(entry instanceof Error)) {
9230
+ continue;
9231
+ }
9232
+ if (!isGenericTransportMessage(entry.message)) {
9233
+ return entry.message;
9234
+ }
9235
+ }
7344
9236
  if (error instanceof Error) {
7345
9237
  return error.message;
7346
9238
  }
7347
9239
  return String(error);
7348
9240
  }
9241
+ function errorChain(error) {
9242
+ const chain = [];
9243
+ const seen = new Set;
9244
+ let current = error;
9245
+ while (current !== undefined && !seen.has(current)) {
9246
+ chain.push(current);
9247
+ seen.add(current);
9248
+ if (typeof current === "object" && current !== null && "cause" in current) {
9249
+ current = current.cause;
9250
+ continue;
9251
+ }
9252
+ break;
9253
+ }
9254
+ return chain;
9255
+ }
9256
+ function errorCode(error) {
9257
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : undefined;
9258
+ }
9259
+ function errorHostname(error) {
9260
+ return typeof error === "object" && error !== null && "hostname" in error && typeof error.hostname === "string" ? error.hostname : undefined;
9261
+ }
9262
+ function isGenericTransportMessage(message) {
9263
+ const normalized = message.trim().toLowerCase();
9264
+ return normalized.length === 0 || normalized === "fetch failed" || normalized === "network error" || normalized === "network request failed";
9265
+ }
9266
+ function dnsErrorMessage(chain) {
9267
+ for (const entry of chain) {
9268
+ const code = errorCode(entry);
9269
+ if (code !== "ENOTFOUND" && code !== "EAI_AGAIN") {
9270
+ continue;
9271
+ }
9272
+ if (entry instanceof Error && !isGenericTransportMessage(entry.message)) {
9273
+ return entry.message;
9274
+ }
9275
+ const hostname = errorHostname(entry);
9276
+ return hostname !== undefined && hostname.length > 0 ? localRuntimeMessages.dnsLookupFailedFor(hostname, code) : localRuntimeMessages.dnsLookupFailed(code);
9277
+ }
9278
+ return;
9279
+ }
7349
9280
  async function listOpenAICompatibleModels(baseURL, fetch) {
7350
9281
  const response = await fetch(modelsEndpoint(baseURL), {
7351
9282
  method: "GET",
7352
9283
  headers: { accept: "application/json" }
7353
9284
  });
7354
9285
  if (!response.ok) {
7355
- throw new Error(`GET /models failed with HTTP ${response.status}`);
9286
+ throw new Error(localRuntimeMessages.modelListHttpError(response.status));
7356
9287
  }
7357
9288
  let payload;
7358
9289
  try {
7359
9290
  payload = await response.json();
7360
9291
  } catch (error) {
7361
- throw new Error(`Malformed JSON from /models: ${errorMessage(error)}`);
9292
+ throw new Error(localRuntimeMessages.malformedModelsJson(errorMessage(error)));
7362
9293
  }
7363
9294
  return parseOpenAIModels(payload);
7364
9295
  }
7365
9296
  function parseOpenAIModels(payload) {
7366
9297
  if (!isObject(payload) || !Array.isArray(payload.data)) {
7367
- throw new Error("Malformed /models response: expected data array");
9298
+ throw new Error(localRuntimeMessages.malformedModelsResponseExpectedDataArray);
7368
9299
  }
7369
9300
  return payload.data.map(parseOpenAIModel);
7370
9301
  }
@@ -7399,7 +9330,7 @@ async function probeOpenAICompatibleRuntime(input) {
7399
9330
  }
7400
9331
  function parseOpenAIModel(value) {
7401
9332
  if (!isObject(value) || typeof value.id !== "string" || value.id.length === 0) {
7402
- throw new Error("Malformed /models response: model id must be a string");
9333
+ throw new Error(localRuntimeMessages.malformedModelsResponseModelId);
7403
9334
  }
7404
9335
  const contextWindow = readContextWindow(value);
7405
9336
  const model = {
@@ -7597,9 +9528,12 @@ var NON_TEXT_LABELS = new Set([
7597
9528
  "embedding",
7598
9529
  "reranking"
7599
9530
  ]);
7600
- var PARALLEL_ARG = /(?:^|\s)(?:--parallel|-np)[\s=]+(\d+)(?!\d)/;
9531
+ var PARALLEL_ARG = /(?:^|\s)(?:--parallel|-np)[\s=]+(\d+)(?=\s|$)/;
7601
9532
  var PARALLEL_FLAGS = new Set(["--parallel", "-np"]);
7602
9533
  var PARALLEL_FUSED = /^(?:--parallel|-np)=(\d+)$/;
9534
+ var CTX_SIZE_FUSED = /^--ctx-size=(\d+)$/;
9535
+ var CTX_SIZE_FLAGS = new Set(["--ctx-size"]);
9536
+ var CTX_SIZE_ARG = /(?:^|\s)--ctx-size[\s=]+(\d+)(?=\s|$)/;
7603
9537
  function createLemonadeAdapter() {
7604
9538
  const listModels = async (options) => {
7605
9539
  const baseURL = normalizeBaseURL(options.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
@@ -7638,13 +9572,13 @@ async function listLemonadeModels(baseURL, fetch) {
7638
9572
  headers: { accept: "application/json" }
7639
9573
  });
7640
9574
  if (!response.ok) {
7641
- throw new Error(`GET /models failed with HTTP ${response.status}`);
9575
+ throw new Error(localRuntimeMessages.modelListHttpError(response.status));
7642
9576
  }
7643
9577
  let payload;
7644
9578
  try {
7645
9579
  payload = await response.json();
7646
9580
  } catch (error) {
7647
- throw new Error(`Malformed JSON from /models: ${errorMessage(error)}`);
9581
+ throw new Error(localRuntimeMessages.malformedModelsJson(errorMessage(error)));
7648
9582
  }
7649
9583
  const nonText = readNonTextModelIDs(payload);
7650
9584
  return parseOpenAIModels(payload).filter((model) => !nonText.has(model.modelID));
@@ -7695,13 +9629,19 @@ async function readLoadedModels(baseURL, fetch) {
7695
9629
  if (entry.loaded === false) {
7696
9630
  continue;
7697
9631
  }
7698
- loaded.set(entry.model_name, { slots: readSlotCount(entry) });
9632
+ loaded.set(entry.model_name, {
9633
+ slots: readSlotCount(entry),
9634
+ totalContextWindow: readLoadedContextWindow(entry)
9635
+ });
7699
9636
  }
7700
9637
  return loaded;
7701
9638
  }
7702
9639
  function readSlotCount(entry) {
7703
9640
  return slotsFromLlamacppArgs(entry) ?? slotsFromLaunchCommand(entry);
7704
9641
  }
9642
+ function readLoadedContextWindow(entry) {
9643
+ return contextWindowFromRecipeOptions(entry) ?? contextWindowFromLaunchCommand(entry);
9644
+ }
7705
9645
  function slotsFromLlamacppArgs(entry) {
7706
9646
  const options = entry.recipe_options;
7707
9647
  if (!isObject2(options) || typeof options.llamacpp_args !== "string") {
@@ -7712,7 +9652,7 @@ function slotsFromLlamacppArgs(entry) {
7712
9652
  }
7713
9653
  function slotsFromLaunchCommand(entry) {
7714
9654
  const argv = entry.launch_command;
7715
- if (!Array.isArray(argv)) {
9655
+ if (!Array.isArray(argv) || !launchCommandLooksLikeLlamacpp(argv)) {
7716
9656
  return;
7717
9657
  }
7718
9658
  for (let index = 0;index < argv.length; index += 1) {
@@ -7738,23 +9678,119 @@ function slotsFromLaunchCommand(entry) {
7738
9678
  }
7739
9679
  return;
7740
9680
  }
9681
+ function contextWindowFromRecipeOptions(entry) {
9682
+ const options = entry.recipe_options;
9683
+ if (!isObject2(options) || !hasLlamacppEvidence(entry)) {
9684
+ return;
9685
+ }
9686
+ const direct = positiveIntegerValue(options.ctx_size);
9687
+ if (direct !== undefined) {
9688
+ return direct;
9689
+ }
9690
+ if (typeof options.llamacpp_args !== "string") {
9691
+ return;
9692
+ }
9693
+ const match = CTX_SIZE_ARG.exec(options.llamacpp_args);
9694
+ return match === null ? undefined : positiveSlotCount(match[1]);
9695
+ }
9696
+ function contextWindowFromLaunchCommand(entry) {
9697
+ const argv = entry.launch_command;
9698
+ if (!Array.isArray(argv) || !launchCommandLooksLikeLlamacpp(argv)) {
9699
+ return;
9700
+ }
9701
+ for (let index = 0;index < argv.length; index += 1) {
9702
+ const token = argv[index];
9703
+ if (typeof token !== "string") {
9704
+ continue;
9705
+ }
9706
+ const fused = CTX_SIZE_FUSED.exec(token);
9707
+ if (fused !== null) {
9708
+ const contextWindow = positiveSlotCount(fused[1]);
9709
+ if (contextWindow !== undefined) {
9710
+ return contextWindow;
9711
+ }
9712
+ continue;
9713
+ }
9714
+ if (CTX_SIZE_FLAGS.has(token)) {
9715
+ const next = argv[index + 1];
9716
+ const contextWindow = typeof next === "string" ? positiveSlotCount(next) : undefined;
9717
+ if (contextWindow !== undefined) {
9718
+ return contextWindow;
9719
+ }
9720
+ }
9721
+ }
9722
+ return;
9723
+ }
7741
9724
  function positiveSlotCount(raw) {
7742
9725
  if (raw === undefined || !/^\d+$/.test(raw)) {
7743
9726
  return;
7744
9727
  }
7745
- const parsed = Number.parseInt(raw, 10);
7746
- return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
9728
+ const parsed = Number.parseInt(raw, 10);
9729
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
9730
+ }
9731
+ function positiveIntegerValue(value) {
9732
+ if (typeof value === "number") {
9733
+ return Number.isSafeInteger(value) && value > 0 ? value : undefined;
9734
+ }
9735
+ return typeof value === "string" ? positiveSlotCount(value) : undefined;
9736
+ }
9737
+ function hasLlamacppEvidence(entry) {
9738
+ const recipe = entry.recipe;
9739
+ if (typeof recipe === "string" && recipe.toLowerCase().includes("llamacpp")) {
9740
+ return true;
9741
+ }
9742
+ const options = entry.recipe_options;
9743
+ if (isObject2(options) && typeof options.llamacpp_args === "string") {
9744
+ return true;
9745
+ }
9746
+ return Array.isArray(entry.launch_command) && launchCommandLooksLikeLlamacpp(entry.launch_command);
9747
+ }
9748
+ function launchCommandLooksLikeLlamacpp(argv) {
9749
+ const firstToken = argv.find((token) => typeof token === "string" && token.length > 0);
9750
+ if (firstToken === undefined) {
9751
+ return false;
9752
+ }
9753
+ const normalized = firstToken.replace(/\\/g, "/").toLowerCase();
9754
+ return normalized === "llama-server" || normalized.endsWith("/llama-server") || normalized === "llama-server.exe" || normalized.endsWith("/llama-server.exe");
9755
+ }
9756
+ function derivedConservativeContextWindow(reportedContextWindow, loaded) {
9757
+ if (loaded.totalContextWindow === undefined || loaded.slots === undefined || loaded.slots <= 0) {
9758
+ return;
9759
+ }
9760
+ const derived = Math.floor(loaded.totalContextWindow / loaded.slots);
9761
+ if (derived <= 0) {
9762
+ return;
9763
+ }
9764
+ return reportedContextWindow === undefined ? derived : Math.min(reportedContextWindow, derived);
9765
+ }
9766
+ function withoutUnverifiedContextWindow(model) {
9767
+ const {
9768
+ contextWindow: _contextWindow,
9769
+ contextWindowProvenance: _contextWindowProvenance,
9770
+ ...withoutContext
9771
+ } = model;
9772
+ return withoutContext;
7747
9773
  }
7748
9774
  function enrichWithLoadState(models, loaded) {
7749
9775
  if (loaded === undefined) {
7750
- return [...models];
9776
+ return models.map((model) => withoutUnverifiedContextWindow(model));
7751
9777
  }
7752
9778
  return models.map((model) => {
9779
+ const base = withoutUnverifiedContextWindow(model);
7753
9780
  const hit = loaded.get(model.modelID);
7754
9781
  if (hit === undefined) {
7755
- return { ...model, loaded: false };
9782
+ return { ...base, loaded: false };
7756
9783
  }
7757
- return hit.slots === undefined ? { ...model, loaded: true } : { ...model, loaded: true, slots: hit.slots };
9784
+ const contextWindow = derivedConservativeContextWindow(model.contextWindow, hit);
9785
+ return {
9786
+ ...base,
9787
+ loaded: true,
9788
+ ...hit.slots !== undefined ? { slots: hit.slots } : {},
9789
+ ...contextWindow !== undefined ? {
9790
+ contextWindow,
9791
+ contextWindowProvenance: "derived-conservative"
9792
+ } : {}
9793
+ };
7758
9794
  });
7759
9795
  }
7760
9796
  function isObject2(value) {
@@ -8007,24 +10043,24 @@ var reportArtifactExists = async (reportPath) => {
8007
10043
  };
8008
10044
 
8009
10045
  // src/orchestrator/reportArtifactReader.ts
8010
- import { readFile as readFile2 } from "node:fs/promises";
10046
+ import { readFile as readFile3 } from "node:fs/promises";
8011
10047
  var readReportArtifact = async (reportPath) => {
8012
10048
  try {
8013
- return await readFile2(reportPath, "utf8");
10049
+ return await readFile3(reportPath, "utf8");
8014
10050
  } catch {
8015
10051
  return;
8016
10052
  }
8017
10053
  };
8018
10054
 
8019
10055
  // src/orchestrator/roster.ts
8020
- import { z as z18 } from "zod";
8021
- var RosterEntrySchema = z18.object({
8022
- roleID: z18.string().min(1),
8023
- agentName: z18.string().min(1)
10056
+ import { z as z19 } from "zod";
10057
+ var RosterEntrySchema = z19.object({
10058
+ roleID: z19.string().min(1),
10059
+ agentName: z19.string().min(1)
8024
10060
  }).strict();
8025
- var RosterSchema = z18.object({
8026
- universe: z18.string().min(1),
8027
- entries: z18.array(RosterEntrySchema)
10061
+ var RosterSchema = z19.object({
10062
+ universe: z19.string().min(1),
10063
+ entries: z19.array(RosterEntrySchema)
8028
10064
  }).strict();
8029
10065
  var ROSTER_JSON_FENCE = /```json\s*([\s\S]*?)```/;
8030
10066
 
@@ -8393,9 +10429,9 @@ async function withTimeout(promise, timeoutMs) {
8393
10429
  return { kind: "settled", value: await promise };
8394
10430
  }
8395
10431
  let timeoutID;
8396
- const timeout = new Promise((resolve4) => {
10432
+ const timeout = new Promise((resolve5) => {
8397
10433
  timeoutID = setTimeout(() => {
8398
- resolve4({ kind: "timeout" });
10434
+ resolve5({ kind: "timeout" });
8399
10435
  }, timeoutMs);
8400
10436
  });
8401
10437
  try {
@@ -8415,8 +10451,8 @@ async function beforeDeadline(request, deadline, deps) {
8415
10451
  const controller = new AbortController;
8416
10452
  let timeoutID;
8417
10453
  const observedRequest = Promise.resolve().then(() => request(controller.signal)).then((value) => ({ kind: "settled", value }), (error) => ({ kind: "rejected", error }));
8418
- const timeout = new Promise((resolve4) => {
8419
- timeoutID = setTimeout(() => resolve4({ kind: "timeout" }), remainingMs);
10454
+ const timeout = new Promise((resolve5) => {
10455
+ timeoutID = setTimeout(() => resolve5({ kind: "timeout" }), remainingMs);
8420
10456
  });
8421
10457
  try {
8422
10458
  const outcome = await Promise.race([observedRequest, timeout]);
@@ -8935,260 +10971,47 @@ function sessionEndpointEventFrom(sessionID, url, now, meta = {}) {
8935
10971
  event.title = meta.title;
8936
10972
  }
8937
10973
  return event;
8938
- }
8939
- var ACTIVITY_SUMMARIES = {
8940
- "session.created": "Session created",
8941
- "session.idle": "Session idle",
8942
- "session.error": "Session error"
8943
- };
8944
- function sessionIDFromEvent(event) {
8945
- const properties = event.properties;
8946
- if (properties === null || typeof properties !== "object") {
8947
- return;
8948
- }
8949
- const record = properties;
8950
- if (typeof record.sessionID === "string") {
8951
- return record.sessionID;
8952
- }
8953
- if (typeof record.info?.id === "string") {
8954
- return record.info.id;
8955
- }
8956
- return;
8957
- }
8958
- function activityEventFrom(event, now) {
8959
- const summary = ACTIVITY_SUMMARIES[event.type];
8960
- if (summary === undefined) {
8961
- return;
8962
- }
8963
- const sessionID = sessionIDFromEvent(event);
8964
- if (sessionID === undefined) {
8965
- return;
8966
- }
8967
- return {
8968
- v: EVENT_SCHEMA_VERSION,
8969
- type: "activity",
8970
- ts: now(),
8971
- sessionID,
8972
- kind: "agent",
8973
- summary
8974
- };
8975
- }
8976
-
8977
- // src/plugin/commandTool.ts
8978
- import { tool } from "@opencode-ai/plugin";
8979
-
8980
- // src/messages/commands.ts
8981
- var clearCacheMessages = {
8982
- header: "openteam clear-cache — frozen plugin cache entries:",
8983
- columns: {
8984
- specDir: "spec dir",
8985
- pinned: "spec-pinned",
8986
- installed: "installed",
8987
- mtime: "mtime"
8988
- },
8989
- reparseSkipSuffix: " [SKIP — reparse point]",
8990
- deletedLabel: "deleted.",
8991
- lockedLabel: (message) => `[LOCKED] ${message}`,
8992
- pathOutsideWarning: (specDir, absolutePath) => ` ⚠ ${specDir}: path outside cacheRoot (${absolutePath}), skipped.`,
8993
- processed: (count) => `${count} entry(ies) processed.`,
8994
- found: (count) => `${count} entry(ies) found. Use --delete to remove them.`
8995
- };
8996
- var baselineMessages = {
8997
- effectiveAuto: "cheapest-capable (auto)",
8998
- pinnedSuffix: (ref) => `${ref} (pinned)`,
8999
- summary: (params) => [
9000
- "openteam baseline:",
9001
- ` mode: ${params.mode}`,
9002
- ` pinned: ${params.pinned}`,
9003
- ` hardDefault: ${params.hardDefault}`,
9004
- ` effective: ${params.effective}`
9005
- ],
9006
- invalidModel: (input) => `Invalid model "${input}". Use the provider/model format, e.g. anthropic/claude-sonnet-4-5.`,
9007
- pinnedTo: (ref) => `Baseline pinned to ${ref} (pinned mode).`,
9008
- autoMode: "Baseline set to auto mode (cheapest-capable)."
9009
- };
9010
- var localMessages = {
9011
- help: {
9012
- status: " openteam local status Show the execution mode (local / frontier / mixed)",
9013
- off: " openteam local off Use frontier models only",
9014
- only: " openteam local only Use local models only",
9015
- on: " openteam local on Enable mixed local/frontier execution"
9016
- },
9017
- noRuntimes: "none",
9018
- modeFrontier: "frontier",
9019
- modeLocal: "local",
9020
- modeMixed: "mixed",
9021
- summary: (params) => [
9022
- "openteam local:",
9023
- ` execution mode: ${params.mode}`,
9024
- ` local runtimes: ${params.runtimes}`
9025
- ],
9026
- frontierOnlyNoChange: "No change: execution mode is already frontier.",
9027
- frontierOnlyEnabled: "Frontier execution enabled.",
9028
- localOnlyNoRuntimes: "Cannot enable local execution: no enabled local runtime is configured. Run 'openteam setup' or enable a local runtime in .opencode/openteam.json first.",
9029
- localOnlyNoChange: "No change: execution mode is already local.",
9030
- localOnlyEnabled: "Local execution enabled.",
9031
- localFirstNoChange: "No change: execution mode is already mixed.",
9032
- localFirstReEnabled: "Mixed local/frontier execution enabled. Make sure each configured provider is reachable.",
9033
- unknownSubcommand: (subcommand, help) => `Unknown local subcommand: ${subcommand}
9034
-
9035
- ${help}`
9036
- };
9037
- var yoloMessages = {
9038
- updated: (path2, status, agentNote) => `${path2} updated. ${status}${agentNote}
9039
- Restart opencode (or reload) so agents pick up the new permissions.`
9040
- };
9041
- var migrateMessages = {
9042
- header: "openteam migrate:",
9043
- nothingToMigrate: " nothing to migrate — the P15b layout is already in place.",
9044
- counts: (params) => [
9045
- ` moved: ${params.moved} file(s)`,
9046
- ` deduped: ${params.deduped} file(s) (destination already identical)`,
9047
- ` conflicts: ${params.conflicts} file(s)`
9048
- ],
9049
- relocatedHeader: " relocated:",
9050
- relocatedEntry: (from, to, deduped) => ` · ${from} → ${to}${deduped ? " (deduped)" : ""}`,
9051
- conflictsHeader: " ⚠ left in place (destination exists with different content — no data lost):",
9052
- conflictEntry: (from, to) => ` · ${from} → ${to}`,
9053
- manualWorktreesHeader: " ⚠ legacy git worktree(s) need a manual move (run `git worktree move`):",
9054
- manualWorktreeEntry: (label, from, to) => ` · ${label}: ${from} → ${to}`,
9055
- success: " ✓ machine-local runtime output relocated under .opencode/openteam-local/."
9056
- };
9057
- var rolesInitMessages = {
9058
- help: " openteam roles init Compose per-role execution, model, and fallback policy",
9059
- header: "openteam roles init:",
9060
- title: "openteam roles init",
9061
- noRoster: (path2) => ` ✗ no roster at ${path2}.`,
9062
- noRosterRemedy: " Cast the team first: run `openteam setup`, then ask the orchestrator to register the cast.",
9063
- unparseableRoster: (path2, error) => ` ✗ ${path2} is present but unparseable: ${error}`,
9064
- unparseableRosterRemedy: " Run `openteam doctor` for the remedy, then re-run this command.",
9065
- allProfiled: (count) => ` ✓ ${count} roster role(s) checked; no unprofiled roles — every role resolves a routing profile.`,
9066
- skipLabel: "Skip for now",
9067
- skipHint: "leaves the role on its built-in or fallback policy; doctor keeps reporting the missing explicit override",
9068
- runtimeQuestion: (roleID) => `Preferred local runtime(s) for "${roleID}" (in failover order)`,
9069
- runtimeHint: "machine-local — written to the git-ignored overlay",
9070
- cancelled: (reason) => ` ✗ cancelled (${reason}); nothing was written.`,
9071
- invalidResult: (reason) => ` ✗ refusing to write: the resulting config is invalid — ${reason}`,
9072
- deprecation: (message) => ` ⚠ ${message}`,
9073
- nothingWritten: (skipped) => ` · skipped ${skipped}; nothing written.`,
9074
- nothingToWriteOutro: "Nothing to write.",
9075
- runtimeBinding: (path2) => ` ✓ runtime binding: ${path2} (git-ignored, machine-local)`,
9076
- skippedSummary: (roleIDs) => ` · skipped: ${roleIDs} — using built-in or fallback policy; re-run this command to add an explicit override.`,
9077
- outro: "Role policy updated."
9078
- };
9079
-
9080
- // src/messages/executionSetup.ts
9081
- var executionSetupMessages = {
9082
- setupIntro: "openteam setup — configure local, frontier, or mixed team execution",
9083
- runtimeQuestion: "Which local runtimes do you want to enable? (choose 'None' for frontier-only)",
9084
- runtimeNoneLabel: "None — use frontier models only",
9085
- runtimeNoneHint: "no local runtime (frontier execution)",
9086
- executionModeQuestion: "Team execution mode",
9087
- executionModeChoices: {
9088
- local: {
9089
- label: "local — every agent stays on configured local providers",
9090
- hint: "requires at least one enabled local runtime"
9091
- },
9092
- frontier: {
9093
- label: "frontier — every agent uses frontier providers",
9094
- hint: "does not require a local runtime"
9095
- },
9096
- mixed: {
9097
- label: "mixed — policies may select local or frontier models",
9098
- hint: "the global mode is an upper bound; per-agent policies may narrow it"
9099
- }
9100
- },
9101
- frontierOnlyNote: [
9102
- "No local runtime is enabled, so the team will use frontier providers.",
9103
- "The selected primary model is written consistently to opencode.json and",
9104
- ".opencode/agent/openteam.md."
9105
- ].join(`
9106
- `),
9107
- frontierOnlyTitle: "Frontier execution",
9108
- localOnlyNote: (model) => [
9109
- "Every team agent is restricted to configured local providers.",
9110
- `The primary coordinator is pinned to ${model}.`,
9111
- "No frontier provider is required for execution while this mode is active."
9112
- ].join(`
9113
- `),
9114
- localOnlyTitle: "Local execution",
9115
- localRequiredError: "local execution requires an enabled local runtime",
9116
- mixedPrimaryQuestion: "Primary coordinator model",
9117
- mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
9118
- mixedPrimaryFrontier: "Choose a frontier model",
9119
- frontierModelQuestion: "Frontier model (type to search; cheapest capable entries first)",
9120
- primaryPolicySummary: (params) => [
9121
- `Execution mode: ${params.executionMode}`,
9122
- `Primary model: ${params.primaryModel}`,
9123
- `Primary fallbacks: ${params.fallbacks}`,
9124
- "Worker model policy: auto within the global execution mode"
9125
- ],
9126
- noFallbacks: "none",
9127
- yoloQuestion: "Enable YOLO mode? (opencode auto-approves all permissions)",
9128
- configuredSummary: (params) => [
9129
- ...executionSetupMessages.primaryPolicySummary({
9130
- executionMode: params.executionMode,
9131
- primaryModel: params.primaryModel,
9132
- fallbacks: executionSetupMessages.noFallbacks
9133
- }),
9134
- `Frontier baseline: ${params.frontierBaseline}`,
9135
- `Local runtimes: ${params.localRuntimes}`,
9136
- `YOLO mode: ${params.yolo ? "enabled (auto-approves permissions)" : "disabled"}`,
9137
- "Web Console: launched separately with 'openteam console' (multi-session, loopback)",
9138
- `Wrote: ${params.opencodePath}, ${params.configPath}, ${params.agentPath}, ${params.agentDir}/*.md (${params.roleAgentCount} standard subagents), ${params.commandDir}/*.md (${params.commandCount} commands); ensured ${params.gitignorePath}`,
9139
- "",
9140
- "Next steps:",
9141
- params.executionMode === "local" ? " 1. No frontier authentication is required while local execution is active" : " 1. Authenticate the frontier provider: opencode auth login",
9142
- params.executionMode === "local" ? executionSetupMessages.localRuntimeNextStep : params.executionMode === "mixed" ? executionSetupMessages.mixedNextStep : executionSetupMessages.frontierNextStep,
9143
- " 3. Press Tab and pick the 'openteam' agent, or type / and pick an /openteam… command"
9144
- ],
9145
- configuredTitle: "openteam configured",
9146
- complete: (paths) => `openteam setup complete: ${paths}`,
9147
- error: (message) => `setup error: ${message}`,
9148
- localRuntimeNextStep: " 2. Make sure every configured local runtime is reachable",
9149
- frontierNextStep: " 2. Open opencode in this repo; openteam will use frontier models",
9150
- mixedNextStep: " 2. Make sure local runtimes are reachable; unavailable candidates can use configured fallbacks",
9151
- done: "Done. Open opencode and select the 'openteam' agent.",
9152
- rolePolicy: {
9153
- whyTitle: "Configure execution and model selection",
9154
- why: (count) => `${count} roster role(s) have no explicit execution/model override. Configure a compact policy now, or leave them on the built-in automatic policy.`,
9155
- modeQuestion: (roleID, agentName) => `Execution mode for "${roleID}" (${agentName})`,
9156
- modeInheritLabel: "Inherit the team execution mode",
9157
- modeLocalLabel: "Local only",
9158
- modeFrontierLabel: "Frontier only",
9159
- modelQuestion: (roleID) => `Model selection for "${roleID}"`,
9160
- modelAutoLabel: "Auto — choose the cheapest capable eligible model",
9161
- modelExactLabel: "Exact provider/model pin",
9162
- modelExactQuestion: (roleID) => `Exact model for "${roleID}" as provider/model`,
9163
- modelExactPlaceholder: "provider/model",
9164
- fallbacksQuestion: (roleID) => `Ordered fallbacks for "${roleID}" (comma-separated provider/model, blank for none)`,
9165
- fallbacksPlaceholder: "provider/model, provider/model",
9166
- configured: (roleIDs, path2) => ` ✓ execution/model policy: ${roleIDs} → ${path2}`,
9167
- invalidModelRef: (value) => `invalid model "${value}"; expected provider/model`
9168
- }
9169
- };
9170
- var executionPolicyMessages = {
9171
- doctorHelp: " openteam doctor Diagnose runtimes, execution policies, and config",
9172
- agentsHelp: " openteam agents List agents, model identity, and execution policy",
9173
- section: "Configured execution policies:",
9174
- globalMode: (mode) => ` execution mode: ${mode}`,
9175
- line: (policy) => ` ${policy.owner}: mode=${policy.executionMode ?? "inherit"} · model=${policy.model} · fallbacks=${policy.fallbacks.length > 0 ? policy.fallbacks.join(" → ") : "none"}`,
9176
- retryLimits: (maxRetries, maxModelsPerNode) => ` retry limits: ${maxRetries} retries per model · ${maxModelsPerNode} model(s) per node`,
9177
- inheritedDefaultUnset: "inherits the opencode.json default (not set); execution follows the configured team and agent policy",
9178
- inheritedDefault: (subscription) => `inherits default → ${subscription}; execution follows the configured team and agent policy`,
9179
- unprofiledRoleRouting: " routing: no explicit override — the role inherits its built-in or fallback policy within the global execution-mode upper bound. Roster prose is not executable configuration.",
9180
- unprofiledRoleRemedy: " remedy: run `openteam roles init`, or add executionMode, model, and ordered fallbacks under orchestrator.roles in .opencode/openteam.json.",
9181
- noLocalMixed: " ⚠ No local runtime reachable: mixed execution currently has only frontier candidates.",
9182
- noLocalBlocked: " ✗ No local runtime reachable: local execution is blocked until a configured runtime is available.",
9183
- primaryLocalMismatch: (name, providerID) => ` ✗ local execution is configured, but primary agent '${name}' points at provider '${providerID}', which opencode.json does not configure.`,
9184
- primaryLocalMismatchCause: " cause: the primary agent artifact and opencode.json no longer agree on the selected provider/model identity.",
9185
- primaryLocalMismatchConsequence: " consequence: opencode cannot start the coordinator, so it cannot read the roster or distribute work.",
9186
- primaryLocalMismatchRemedy: (sourceFile) => ` remedy: re-run \`openteam setup\` to regenerate ${sourceFile} and opencode.json from the same primary policy, then re-run \`openteam doctor\`.`
9187
- };
9188
- var localModeChangeMessages = {
9189
- localProviderInFrontierDomain: (path2, domainPath, providerID, modelID) => `${path2}: model "${providerID}/${modelID}" uses provider "${providerID}", which is configured for local dispatch and cannot be selected when ${domainPath} is "frontier". Update this policy or use \`openteam local on\`.`,
9190
- frontierProviderInLocalDomain: (path2, domainPath, providerID, modelID) => `${path2}: model "${providerID}/${modelID}" uses provider "${providerID}", which is not a configured local provider and cannot be selected when ${domainPath} is "local". Update this policy or use \`openteam local on\`.`
10974
+ }
10975
+ var ACTIVITY_SUMMARIES = {
10976
+ "session.created": "Session created",
10977
+ "session.idle": "Session idle",
10978
+ "session.error": "Session error"
9191
10979
  };
10980
+ function sessionIDFromEvent(event) {
10981
+ const properties = event.properties;
10982
+ if (properties === null || typeof properties !== "object") {
10983
+ return;
10984
+ }
10985
+ const record = properties;
10986
+ if (typeof record.sessionID === "string") {
10987
+ return record.sessionID;
10988
+ }
10989
+ if (typeof record.info?.id === "string") {
10990
+ return record.info.id;
10991
+ }
10992
+ return;
10993
+ }
10994
+ function activityEventFrom(event, now) {
10995
+ const summary = ACTIVITY_SUMMARIES[event.type];
10996
+ if (summary === undefined) {
10997
+ return;
10998
+ }
10999
+ const sessionID = sessionIDFromEvent(event);
11000
+ if (sessionID === undefined) {
11001
+ return;
11002
+ }
11003
+ return {
11004
+ v: EVENT_SCHEMA_VERSION,
11005
+ type: "activity",
11006
+ ts: now(),
11007
+ sessionID,
11008
+ kind: "agent",
11009
+ summary
11010
+ };
11011
+ }
11012
+
11013
+ // src/plugin/commandTool.ts
11014
+ import { tool } from "@opencode-ai/plugin";
9192
11015
 
9193
11016
  // src/orchestrator/worktreeReconciler.ts
9194
11017
  function planOne(wt) {
@@ -9244,7 +11067,7 @@ function planWorktreeReconciliation(input) {
9244
11067
  }
9245
11068
 
9246
11069
  // src/telemetry/otelConfig.ts
9247
- import { z as z19 } from "zod";
11070
+ import { z as z20 } from "zod";
9248
11071
 
9249
11072
  // src/telemetry/fanout.ts
9250
11073
  function createFanOutSink(deps) {
@@ -9322,10 +11145,10 @@ function createOtelSink(deps) {
9322
11145
  }
9323
11146
 
9324
11147
  // src/telemetry/otelConfig.ts
9325
- var OtelBackendConfigSchema = z19.object({
9326
- backend: z19.literal("opentelemetry"),
9327
- connectionEnv: z19.string().min(1),
9328
- serviceName: z19.string().min(1).optional()
11148
+ var OtelBackendConfigSchema = z20.object({
11149
+ backend: z20.literal("opentelemetry"),
11150
+ connectionEnv: z20.string().min(1),
11151
+ serviceName: z20.string().min(1).optional()
9329
11152
  }).strict();
9330
11153
  function parseConnectionString(raw) {
9331
11154
  const pairs = new Map;
@@ -9728,22 +11551,22 @@ var KNOWN_FRONTIER_PROVIDER_IDS = new Set([
9728
11551
  "openrouter"
9729
11552
  ]);
9730
11553
  var VIRTUAL_ROUTER_PROVIDER_ID = "openteam-router";
9731
- function isRecord4(value) {
11554
+ function isRecord5(value) {
9732
11555
  return typeof value === "object" && value !== null && !Array.isArray(value);
9733
11556
  }
9734
11557
  function parseOpencodeProviders(opencodeConfig) {
9735
11558
  const result = new Map;
9736
11559
  const provider = opencodeConfig?.provider;
9737
- if (!isRecord4(provider)) {
11560
+ if (!isRecord5(provider)) {
9738
11561
  return result;
9739
11562
  }
9740
11563
  for (const [id, value] of Object.entries(provider)) {
9741
- if (!isRecord4(value)) {
11564
+ if (!isRecord5(value)) {
9742
11565
  result.set(id, {});
9743
11566
  continue;
9744
11567
  }
9745
11568
  const models = value.models;
9746
- result.set(id, isRecord4(models) ? { models: new Set(Object.keys(models)) } : {});
11569
+ result.set(id, isRecord5(models) ? { models: new Set(Object.keys(models)) } : {});
9747
11570
  }
9748
11571
  return result;
9749
11572
  }
@@ -9997,6 +11820,54 @@ function renderConsoleStatus(console_) {
9997
11820
  }
9998
11821
 
9999
11822
  // src/commands/doctor.ts
11823
+ var DEFAULT_COMPACTION_RESERVED_MAX = 20000;
11824
+ function isRecord6(value) {
11825
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11826
+ }
11827
+ function hasOwn(record, key) {
11828
+ return Object.hasOwn(record, key);
11829
+ }
11830
+ function positiveInteger2(value) {
11831
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0 ? value : undefined;
11832
+ }
11833
+ function recordAt(source, key) {
11834
+ const value = source?.[key];
11835
+ return isRecord6(value) ? value : undefined;
11836
+ }
11837
+ function enabledLocalProviderIDs(config) {
11838
+ const providers = new Set;
11839
+ for (const runtime of config.local.runtimes) {
11840
+ if (!runtime.enabled) {
11841
+ continue;
11842
+ }
11843
+ providers.add(runtime.defaultModel.providerID);
11844
+ }
11845
+ return providers;
11846
+ }
11847
+ function detectedContextsByProvider(config, snapshots) {
11848
+ const reachableByRuntime = new Map(snapshots.filter((snapshot) => snapshot.reachable).map((snapshot) => [snapshot.id, snapshot]));
11849
+ const detected = new Map;
11850
+ for (const runtime of config.local.runtimes) {
11851
+ if (!runtime.enabled) {
11852
+ continue;
11853
+ }
11854
+ const snapshot = reachableByRuntime.get(runtime.id);
11855
+ if (snapshot === undefined) {
11856
+ continue;
11857
+ }
11858
+ const providerID = runtime.defaultModel.providerID;
11859
+ const byModel = detected.get(providerID) ?? new Map;
11860
+ for (const model of snapshot.models) {
11861
+ if (model.contextWindow !== undefined) {
11862
+ byModel.set(model.modelID, model.contextWindow);
11863
+ }
11864
+ }
11865
+ if (byModel.size > 0) {
11866
+ detected.set(providerID, byModel);
11867
+ }
11868
+ }
11869
+ return detected;
11870
+ }
10000
11871
  function runtimeClassLine(runtime) {
10001
11872
  if (runtime === undefined) {
10002
11873
  return;
@@ -10281,6 +12152,118 @@ function agentModelsSection(diagnostics, searchedPaths, localOnly) {
10281
12152
  lines.push(" note: static checks are offline and deterministic; live checks need a reachable local runtime and can be inconclusive.");
10282
12153
  return lines;
10283
12154
  }
12155
+ function collectLocalModelLimitWarnings(input) {
12156
+ if (input.opencodeConfig === undefined) {
12157
+ return;
12158
+ }
12159
+ const enabledProviders = enabledLocalProviderIDs(input.config);
12160
+ if (enabledProviders.size === 0) {
12161
+ return { inspectedModels: 0, warnings: [] };
12162
+ }
12163
+ const providerConfig = recordAt(input.opencodeConfig, "provider");
12164
+ const detectedContexts = detectedContextsByProvider(input.config, input.snapshots);
12165
+ const compactionReserved = positiveInteger2(recordAt(input.opencodeConfig, "compaction")?.reserved);
12166
+ const warnings = [];
12167
+ let inspectedModels = 0;
12168
+ for (const providerID of enabledProviders) {
12169
+ const provider = recordAt(providerConfig, providerID);
12170
+ const models = recordAt(provider, "models");
12171
+ if (models === undefined) {
12172
+ continue;
12173
+ }
12174
+ for (const [modelID, modelValue] of Object.entries(models)) {
12175
+ inspectedModels += 1;
12176
+ const problems = [];
12177
+ const remedies = new Set;
12178
+ if (!isRecord6(modelValue)) {
12179
+ problems.push(doctorMessages.localModelLimits.invalidLimitBlock);
12180
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12181
+ warnings.push({
12182
+ providerID,
12183
+ modelID,
12184
+ problems,
12185
+ remedies: [...remedies]
12186
+ });
12187
+ continue;
12188
+ }
12189
+ if (hasOwn(modelValue, "limit") && recordAt(modelValue, "limit") === undefined) {
12190
+ problems.push(doctorMessages.localModelLimits.invalidLimitBlock);
12191
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12192
+ warnings.push({
12193
+ providerID,
12194
+ modelID,
12195
+ problems,
12196
+ remedies: [...remedies]
12197
+ });
12198
+ continue;
12199
+ }
12200
+ const limit = recordAt(modelValue, "limit");
12201
+ const rawOutput = limit?.output;
12202
+ const rawContext = limit?.context;
12203
+ const rawInput = limit?.input;
12204
+ const output = positiveInteger2(rawOutput);
12205
+ const context = positiveInteger2(rawContext);
12206
+ const inputLimit = positiveInteger2(rawInput);
12207
+ if (limit === undefined || !hasOwn(limit, "output")) {
12208
+ problems.push(doctorMessages.localModelLimits.missingOutput);
12209
+ remedies.add(doctorMessages.localModelLimits.outputRemedy);
12210
+ } else if (output === undefined) {
12211
+ problems.push(doctorMessages.localModelLimits.invalidOutput);
12212
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12213
+ }
12214
+ if (limit === undefined || !hasOwn(limit, "context")) {
12215
+ problems.push(doctorMessages.localModelLimits.missingContext);
12216
+ const runtimeContext = detectedContexts.get(providerID)?.get(modelID);
12217
+ remedies.add(runtimeContext === undefined ? doctorMessages.localModelLimits.unknownContextRemedy : doctorMessages.localModelLimits.detectedContextRemedy);
12218
+ } else if (context === undefined) {
12219
+ problems.push(doctorMessages.localModelLimits.invalidContext);
12220
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12221
+ }
12222
+ if (limit !== undefined && hasOwn(limit, "input") && inputLimit === undefined) {
12223
+ problems.push(doctorMessages.localModelLimits.invalidInput);
12224
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12225
+ }
12226
+ if (output !== undefined && context !== undefined && output >= context) {
12227
+ problems.push(doctorMessages.localModelLimits.outputExceedsContext);
12228
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12229
+ }
12230
+ if (output !== undefined && inputLimit !== undefined && inputLimit <= (compactionReserved ?? Math.min(DEFAULT_COMPACTION_RESERVED_MAX, output))) {
12231
+ problems.push(doctorMessages.localModelLimits.outputExceedsInput);
12232
+ remedies.add(doctorMessages.localModelLimits.invalidLimitRemedy);
12233
+ }
12234
+ if (problems.length > 0) {
12235
+ warnings.push({
12236
+ providerID,
12237
+ modelID,
12238
+ problems,
12239
+ remedies: [...remedies]
12240
+ });
12241
+ }
12242
+ }
12243
+ }
12244
+ return { inspectedModels, warnings };
12245
+ }
12246
+ function localModelLimitsSection(input) {
12247
+ const report = collectLocalModelLimitWarnings(input);
12248
+ if (report === undefined || report.inspectedModels === 0 && report.warnings.length === 0) {
12249
+ return [];
12250
+ }
12251
+ if (report.warnings.length === 0) {
12252
+ return [doctorMessages.localModelLimits.section, doctorMessages.localModelLimits.healthy];
12253
+ }
12254
+ const lines = [doctorMessages.localModelLimits.warningSummary(report.warnings.length)];
12255
+ const remedies = new Set;
12256
+ for (const warning of report.warnings) {
12257
+ lines.push(doctorMessages.localModelLimits.modelWarning(warning.providerID, warning.modelID, warning.problems.join("; ")));
12258
+ for (const remedy of warning.remedies) {
12259
+ remedies.add(remedy);
12260
+ }
12261
+ }
12262
+ for (const remedy of remedies) {
12263
+ lines.push(remedy);
12264
+ }
12265
+ return lines;
12266
+ }
10284
12267
  function renderDoctor(input) {
10285
12268
  const enabledRuntimes = input.config.local.runtimes.filter((r) => r.enabled);
10286
12269
  const reachable = input.snapshots.filter((s) => s.reachable).length;
@@ -10339,6 +12322,7 @@ function renderDoctor(input) {
10339
12322
  if (input.gitignore !== undefined) {
10340
12323
  lines.push(...gitignoreSection(input.gitignore));
10341
12324
  }
12325
+ lines.push(...localModelLimitsSection(input));
10342
12326
  if (input.otelBackend !== undefined) {
10343
12327
  const otel = input.otelBackend;
10344
12328
  lines.push(" opentelemetry:");
@@ -10873,9 +12857,7 @@ ${line}`);
10873
12857
  }
10874
12858
  return observation;
10875
12859
  }
10876
-
10877
12860
  // src/commands/setup.ts
10878
- var OPENTEAM_PLUGIN_SPEC = "@jmanuelcorral/openteam";
10879
12861
  var OPENCODE_GITIGNORE_PATH = ".opencode/.gitignore";
10880
12862
  var GENERATED_OPENTEAM_STATE_PATHS = [
10881
12863
  DEFAULT_TELEMETRY_PATH,
@@ -11006,7 +12988,7 @@ function isInsideRepo(repoRoot, relPath) {
11006
12988
  function isOpenteamEntry(entry) {
11007
12989
  if (typeof entry !== "string")
11008
12990
  return false;
11009
- return entry === OPENTEAM_PLUGIN_SPEC || entry.startsWith(`${OPENTEAM_PLUGIN_SPEC}@`);
12991
+ return entry === OPENTEAM_PACKAGE_NAME || entry.startsWith(`${OPENTEAM_PACKAGE_NAME}@`);
11010
12992
  }
11011
12993
  function escapeRegex(s) {
11012
12994
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -11181,7 +13163,7 @@ function stripOpenteamFromOpencodeConfig(raw) {
11181
13163
  try {
11182
13164
  parsed = JSON.parse(stripped);
11183
13165
  } catch {
11184
- if (raw.includes(OPENTEAM_PLUGIN_SPEC)) {
13166
+ if (raw.includes(OPENTEAM_PACKAGE_NAME)) {
11185
13167
  return {
11186
13168
  status: "failed",
11187
13169
  reason: "document is not valid JSON/JSONC; remove the @jmanuelcorral/openteam plugin entry manually"
@@ -11190,7 +13172,7 @@ function stripOpenteamFromOpencodeConfig(raw) {
11190
13172
  return { status: "not-present" };
11191
13173
  }
11192
13174
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
11193
- if (raw.includes(OPENTEAM_PLUGIN_SPEC)) {
13175
+ if (raw.includes(OPENTEAM_PACKAGE_NAME)) {
11194
13176
  return {
11195
13177
  status: "failed",
11196
13178
  reason: "top-level value is not a JSON object; remove the @jmanuelcorral/openteam plugin entry manually"
@@ -11201,7 +13183,7 @@ function stripOpenteamFromOpencodeConfig(raw) {
11201
13183
  const record = parsed;
11202
13184
  const plugins = record.plugin;
11203
13185
  if (!Array.isArray(plugins)) {
11204
- if (raw.includes(OPENTEAM_PLUGIN_SPEC)) {
13186
+ if (raw.includes(OPENTEAM_PACKAGE_NAME)) {
11205
13187
  return {
11206
13188
  status: "failed",
11207
13189
  reason: "could not locate a top-level plugin array; remove the @jmanuelcorral/openteam plugin entry manually"
@@ -11224,7 +13206,7 @@ function stripOpenteamFromOpencodeConfig(raw) {
11224
13206
  const { keyStart, arrayStart, arrayEnd } = bounds;
11225
13207
  if (remaining.length === 0) {
11226
13208
  const contents2 = removePluginKeyFromObject(raw, keyStart, arrayEnd);
11227
- const residue2 = contents2.includes(OPENTEAM_PLUGIN_SPEC) ? "openteam is still referenced in a nested context — remove it manually" : undefined;
13209
+ const residue2 = contents2.includes(OPENTEAM_PACKAGE_NAME) ? "openteam is still referenced in a nested context — remove it manually" : undefined;
11228
13210
  return residue2 !== undefined ? { status: "removed", contents: contents2, residue: residue2 } : { status: "removed", contents: contents2 };
11229
13211
  }
11230
13212
  let arrayText = raw.slice(arrayStart, arrayEnd + 1);
@@ -11232,7 +13214,7 @@ function stripOpenteamFromOpencodeConfig(raw) {
11232
13214
  arrayText = removeEntryFromArrayText(arrayText, JSON.stringify(entry));
11233
13215
  }
11234
13216
  const contents = raw.slice(0, arrayStart) + arrayText + raw.slice(arrayEnd + 1);
11235
- const residue = contents.includes(OPENTEAM_PLUGIN_SPEC) ? "openteam is still referenced in a nested context — remove it manually" : undefined;
13217
+ const residue = contents.includes(OPENTEAM_PACKAGE_NAME) ? "openteam is still referenced in a nested context — remove it manually" : undefined;
11236
13218
  return residue !== undefined ? { status: "removed", contents, residue } : { status: "removed", contents };
11237
13219
  }
11238
13220
  function stripOpenteamFromGitignore(raw) {
@@ -11296,7 +13278,7 @@ function describeItem(item) {
11296
13278
  case "agent":
11297
13279
  return `${item.path} (${item.name})`;
11298
13280
  case "opencode-config":
11299
- return `${item.path} (remove the ${OPENTEAM_PLUGIN_SPEC} plugin entry)`;
13281
+ return `${item.path} (remove the ${OPENTEAM_PACKAGE_NAME} plugin entry)`;
11300
13282
  case "gitignore":
11301
13283
  return `${item.path} (remove openteam's generated-state rules)`;
11302
13284
  case "worktree":
@@ -11708,6 +13690,9 @@ var HELP = [
11708
13690
  " openteam report Cost/savings summary (telemetry)",
11709
13691
  " openteam clear-cache List the plugin's frozen cache entries",
11710
13692
  " openteam clear-cache --delete Delete the frozen cache entries",
13693
+ upgradeMessages.help.command,
13694
+ upgradeMessages.help.check,
13695
+ upgradeMessages.help.version,
11711
13696
  " openteam purge List openteam's footprint (dry run, removes nothing)",
11712
13697
  " openteam purge --delete Interactively remove openteam category by category",
11713
13698
  " openteam purge --yes Remove every openteam category without prompting",
@@ -12285,6 +14270,7 @@ async function runCli(argv, deps) {
12285
14270
  telemetryRecords: records.length,
12286
14271
  agentModels,
12287
14272
  opencodeConfigPaths,
14273
+ ...opencodeConfig !== undefined ? { opencodeConfig } : {},
12288
14274
  ...queueWait !== undefined ? { queueWait } : {},
12289
14275
  ...rosterAudit !== undefined ? { rosterAudit } : {},
12290
14276
  ...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
@@ -12386,6 +14372,15 @@ async function runCli(argv, deps) {
12386
14372
  ...configWarning !== undefined ? { configWarning } : {}
12387
14373
  });
12388
14374
  }
14375
+ if (command === "upgrade") {
14376
+ if (deps.upgradePort === undefined) {
14377
+ return {
14378
+ exitCode: 1,
14379
+ stdout: upgradeMessages.portNotConfigured
14380
+ };
14381
+ }
14382
+ return await runUpgrade(parsed.positionals, deps.upgradePort, opencodeConfigPaths);
14383
+ }
12389
14384
  if (command === "--version" || command === "-v") {
12390
14385
  return { exitCode: 0, stdout: deps.version };
12391
14386
  }
@@ -12405,7 +14400,7 @@ ${HELP}`
12405
14400
  }
12406
14401
 
12407
14402
  // src/plugin/commandTool.ts
12408
- function commandArgv(action, model) {
14403
+ function commandArgv(action, model, version) {
12409
14404
  switch (action) {
12410
14405
  case "show":
12411
14406
  return ["baseline", "show"];
@@ -12423,11 +14418,15 @@ function commandArgv(action, model) {
12423
14418
  return ["console"];
12424
14419
  case "clear-cache":
12425
14420
  return ["clear-cache"];
14421
+ case "upgrade":
14422
+ return version === undefined ? ["upgrade"] : ["upgrade", "--version", version];
14423
+ case "upgrade-check":
14424
+ return version === undefined ? ["upgrade", "--check"] : ["upgrade", "--check", "--version", version];
12426
14425
  }
12427
14426
  }
12428
14427
  function createCommandTool(deps) {
12429
14428
  return tool({
12430
- description: "openteam runtime commands: baseline (show/set/auto), doctor, report, agents (LLM per agent: local/frontier + subscription) and console (status/URL of the web Console).",
14429
+ description: "openteam runtime commands: baseline (show/set/auto), doctor, report, agents (LLM per agent: local/frontier + subscription), console (status/URL of the web Console), upgrade (update plugin pin) and upgrade-check (show current vs target without changes).",
12431
14430
  args: {
12432
14431
  action: tool.schema.enum([
12433
14432
  "show",
@@ -12437,12 +14436,15 @@ function createCommandTool(deps) {
12437
14436
  "report",
12438
14437
  "agents",
12439
14438
  "console",
12440
- "clear-cache"
14439
+ "clear-cache",
14440
+ "upgrade",
14441
+ "upgrade-check"
12441
14442
  ]).describe("Action to run"),
12442
- model: tool.schema.string().optional().describe("Model provider/model (only for action=set)")
14443
+ model: tool.schema.string().optional().describe("Model provider/model (only for action=set)"),
14444
+ version: tool.schema.string().optional()
12443
14445
  },
12444
14446
  async execute(args) {
12445
- const result = await runCli(commandArgv(args.action, args.model), deps);
14447
+ const result = await runCli(commandArgv(args.action, args.model, args.version), deps);
12446
14448
  return result.stdout;
12447
14449
  }
12448
14450
  });
@@ -12612,36 +14614,36 @@ function formatResponse(response) {
12612
14614
  }
12613
14615
 
12614
14616
  // src/plugin/reportRunSchema.ts
12615
- import { z as z21 } from "zod";
14617
+ import { z as z22 } from "zod";
12616
14618
 
12617
14619
  // src/plugin/graphShadowIngress.ts
12618
- import { z as z20 } from "zod";
14620
+ import { z as z21 } from "zod";
12619
14621
  var LEGACY_EXECUTION_TRACE_VERSION = 1;
12620
- var LegacyExecutionStatusSchema = z20.enum([
14622
+ var LegacyExecutionStatusSchema = z21.enum([
12621
14623
  "completed",
12622
14624
  "failed",
12623
14625
  "cancelled"
12624
14626
  ]);
12625
- var LegacyReviewOutcomeSchema = z20.enum([
14627
+ var LegacyReviewOutcomeSchema = z21.enum([
12626
14628
  "approved",
12627
14629
  "rejected",
12628
14630
  "inconclusive"
12629
14631
  ]);
12630
- var LegacyTraceNodeV1Schema = z20.object({
14632
+ var LegacyTraceNodeV1Schema = z21.object({
12631
14633
  id: NodeIDSchema,
12632
14634
  role: NodeRoleSchema,
12633
- model: z20.string().min(1),
12634
- sessionRef: z20.string().min(1).optional(),
14635
+ model: z21.string().min(1),
14636
+ sessionRef: z21.string().min(1).optional(),
12635
14637
  errorClass: ErrorClassSchema.optional()
12636
14638
  }).strict();
12637
- var LegacyExecutionTraceV1Schema = z20.object({
12638
- version: z20.literal(LEGACY_EXECUTION_TRACE_VERSION),
14639
+ var LegacyExecutionTraceV1Schema = z21.object({
14640
+ version: z21.literal(LEGACY_EXECUTION_TRACE_VERSION),
12639
14641
  runID: NodeIDSchema,
12640
14642
  status: LegacyExecutionStatusSchema,
12641
14643
  outcome: LegacyReviewOutcomeSchema,
12642
- fixes: z20.number().int().nonnegative(),
12643
- nodes: z20.array(LegacyTraceNodeV1Schema).min(1),
12644
- parentSessionRef: z20.string().min(1).optional()
14644
+ fixes: z21.number().int().nonnegative(),
14645
+ nodes: z21.array(LegacyTraceNodeV1Schema).min(1),
14646
+ parentSessionRef: z21.string().min(1).optional()
12645
14647
  }).strict();
12646
14648
  function gateReason2(gate) {
12647
14649
  if (gate === undefined || gate.mode === "off") {
@@ -12712,20 +14714,20 @@ function parseLegacyExecutionTrace(input) {
12712
14714
  }
12713
14715
 
12714
14716
  // src/plugin/reportRunSchema.ts
12715
- var ReportRunNodeSchema = z21.object({
14717
+ var ReportRunNodeSchema = z22.object({
12716
14718
  id: NodeIDSchema,
12717
14719
  role: NodeRoleSchema,
12718
- model: z21.string().min(1),
12719
- ok: z21.boolean(),
12720
- sessionRef: z21.string().min(1).optional(),
14720
+ model: z22.string().min(1),
14721
+ ok: z22.boolean(),
14722
+ sessionRef: z22.string().min(1).optional(),
12721
14723
  errorClass: ErrorClassSchema.optional()
12722
14724
  }).strict();
12723
- var ReportRunPayloadSchema = z21.object({
14725
+ var ReportRunPayloadSchema = z22.object({
12724
14726
  runID: NodeIDSchema,
12725
14727
  status: LegacyExecutionStatusSchema,
12726
- fixes: z21.number().int().nonnegative(),
12727
- nodes: z21.array(ReportRunNodeSchema).min(1),
12728
- parentSessionRef: z21.string().min(1).optional()
14728
+ fixes: z22.number().int().nonnegative(),
14729
+ nodes: z22.array(ReportRunNodeSchema).min(1),
14730
+ parentSessionRef: z22.string().min(1).optional()
12729
14731
  }).strict();
12730
14732
 
12731
14733
  // src/orchestrator/graphShadow.ts
@@ -14026,62 +16028,62 @@ ${lines.join(`
14026
16028
  }
14027
16029
 
14028
16030
  // src/memory/types.ts
14029
- import { z as z22 } from "zod";
16031
+ import { z as z23 } from "zod";
14030
16032
  var SHARED_OWNER_KEY = "*";
14031
- var OwnerKeySchema = z22.string().min(1);
14032
- var MemoryKindSchema = z22.enum(["fact", "preference", "entity"]);
14033
- var MemoryBaseSchema = z22.object({
14034
- id: z22.string().min(1),
16033
+ var OwnerKeySchema = z23.string().min(1);
16034
+ var MemoryKindSchema = z23.enum(["fact", "preference", "entity"]);
16035
+ var MemoryBaseSchema = z23.object({
16036
+ id: z23.string().min(1),
14035
16037
  ownerKey: OwnerKeySchema,
14036
- confidence: z22.number().min(0).max(1),
14037
- validFrom: z22.number().finite(),
14038
- validUntil: z22.number().finite().nullable().default(null),
14039
- createdAt: z22.number().finite(),
14040
- invalidatedAt: z22.number().finite().nullable().default(null),
14041
- sourceHash: z22.string().min(1),
14042
- supersededBy: z22.string().min(1).nullable().default(null)
16038
+ confidence: z23.number().min(0).max(1),
16039
+ validFrom: z23.number().finite(),
16040
+ validUntil: z23.number().finite().nullable().default(null),
16041
+ createdAt: z23.number().finite(),
16042
+ invalidatedAt: z23.number().finite().nullable().default(null),
16043
+ sourceHash: z23.string().min(1),
16044
+ supersededBy: z23.string().min(1).nullable().default(null)
14043
16045
  }).strict();
14044
16046
  var FactSchema = MemoryBaseSchema.extend({
14045
- kind: z22.literal("fact"),
14046
- subject: z22.string().min(1),
14047
- predicate: z22.string().min(1),
14048
- object: z22.string().min(1),
14049
- category: z22.string().min(1).nullable().default(null)
16047
+ kind: z23.literal("fact"),
16048
+ subject: z23.string().min(1),
16049
+ predicate: z23.string().min(1),
16050
+ object: z23.string().min(1),
16051
+ category: z23.string().min(1).nullable().default(null)
14050
16052
  });
14051
16053
  var PreferenceSchema = MemoryBaseSchema.extend({
14052
- kind: z22.literal("preference"),
14053
- category: z22.string().min(1),
14054
- preference: z22.string().min(1),
14055
- context: z22.string().min(1).nullable().default(null),
14056
- lastAccessedAt: z22.number().finite().nullable().default(null),
14057
- accessCount: z22.number().int().min(0).default(0)
16054
+ kind: z23.literal("preference"),
16055
+ category: z23.string().min(1),
16056
+ preference: z23.string().min(1),
16057
+ context: z23.string().min(1).nullable().default(null),
16058
+ lastAccessedAt: z23.number().finite().nullable().default(null),
16059
+ accessCount: z23.number().int().min(0).default(0)
14058
16060
  });
14059
16061
  var EntitySchema = MemoryBaseSchema.extend({
14060
- kind: z22.literal("entity"),
14061
- canonicalName: z22.string().min(1),
14062
- type: z22.string().min(1),
14063
- aliases: z22.array(z22.string().min(1)).default([])
16062
+ kind: z23.literal("entity"),
16063
+ canonicalName: z23.string().min(1),
16064
+ type: z23.string().min(1),
16065
+ aliases: z23.array(z23.string().min(1)).default([])
14064
16066
  });
14065
16067
  var RelationSchema = MemoryBaseSchema.extend({
14066
- kind: z22.literal("relation"),
14067
- from: z22.string().min(1),
14068
- to: z22.string().min(1),
14069
- predicate: z22.string().min(1),
14070
- annotation: z22.string().min(1).nullable().default(null)
16068
+ kind: z23.literal("relation"),
16069
+ from: z23.string().min(1),
16070
+ to: z23.string().min(1),
16071
+ predicate: z23.string().min(1),
16072
+ annotation: z23.string().min(1).nullable().default(null)
14071
16073
  });
14072
- var MemoryRecordSchema = z22.discriminatedUnion("kind", [
16074
+ var MemoryRecordSchema = z23.discriminatedUnion("kind", [
14073
16075
  FactSchema,
14074
16076
  PreferenceSchema,
14075
16077
  EntitySchema,
14076
16078
  RelationSchema
14077
16079
  ]);
14078
- var RecallQuerySchema = z22.object({
16080
+ var RecallQuerySchema = z23.object({
14079
16081
  ownerKey: OwnerKeySchema,
14080
- kinds: z22.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
14081
- asOf: z22.number().finite().nullable().default(null),
14082
- limit: z22.number().int().positive().default(8),
14083
- minSimilarity: z22.number().min(0).max(1).default(0.2),
14084
- includeShared: z22.boolean().default(true)
16082
+ kinds: z23.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
16083
+ asOf: z23.number().finite().nullable().default(null),
16084
+ limit: z23.number().int().positive().default(8),
16085
+ minSimilarity: z23.number().min(0).max(1).default(0.2),
16086
+ includeShared: z23.boolean().default(true)
14085
16087
  }).strict();
14086
16088
 
14087
16089
  // src/memory/rank.ts
@@ -14652,42 +16654,42 @@ function buildMemoryInjector(deps, policy) {
14652
16654
 
14653
16655
  // src/plugin/memoryTool.ts
14654
16656
  import { tool as tool3 } from "@opencode-ai/plugin";
14655
- import { z as z24 } from "zod";
16657
+ import { z as z25 } from "zod";
14656
16658
 
14657
16659
  // src/memory/extract.ts
14658
- import { z as z23 } from "zod";
16660
+ import { z as z24 } from "zod";
14659
16661
  var REDACTED = "[redacted]";
14660
- var RawFactSchema = z23.object({
14661
- subject: z23.string().min(1),
14662
- predicate: z23.string().min(1),
14663
- object: z23.string().min(1),
14664
- category: z23.string().min(1).nullable().optional(),
14665
- confidence: z23.number().min(0).max(1).optional()
16662
+ var RawFactSchema = z24.object({
16663
+ subject: z24.string().min(1),
16664
+ predicate: z24.string().min(1),
16665
+ object: z24.string().min(1),
16666
+ category: z24.string().min(1).nullable().optional(),
16667
+ confidence: z24.number().min(0).max(1).optional()
14666
16668
  });
14667
- var RawPreferenceSchema = z23.object({
14668
- category: z23.string().min(1),
14669
- preference: z23.string().min(1),
14670
- context: z23.string().min(1).nullable().optional(),
14671
- confidence: z23.number().min(0).max(1).optional()
16669
+ var RawPreferenceSchema = z24.object({
16670
+ category: z24.string().min(1),
16671
+ preference: z24.string().min(1),
16672
+ context: z24.string().min(1).nullable().optional(),
16673
+ confidence: z24.number().min(0).max(1).optional()
14672
16674
  });
14673
- var RawEntitySchema = z23.object({
14674
- canonicalName: z23.string().min(1),
14675
- type: z23.string().min(1),
14676
- aliases: z23.array(z23.string().min(1)).optional(),
14677
- confidence: z23.number().min(0).max(1).optional()
16675
+ var RawEntitySchema = z24.object({
16676
+ canonicalName: z24.string().min(1),
16677
+ type: z24.string().min(1),
16678
+ aliases: z24.array(z24.string().min(1)).optional(),
16679
+ confidence: z24.number().min(0).max(1).optional()
14678
16680
  });
14679
- var RawRelationSchema = z23.object({
14680
- from: z23.string().min(1),
14681
- to: z23.string().min(1),
14682
- predicate: z23.string().min(1),
14683
- annotation: z23.string().min(1).nullable().optional(),
14684
- confidence: z23.number().min(0).max(1).optional()
16681
+ var RawRelationSchema = z24.object({
16682
+ from: z24.string().min(1),
16683
+ to: z24.string().min(1),
16684
+ predicate: z24.string().min(1),
16685
+ annotation: z24.string().min(1).nullable().optional(),
16686
+ confidence: z24.number().min(0).max(1).optional()
14685
16687
  }).strict();
14686
- var RawExtractionSchema = z23.object({
14687
- facts: z23.array(RawFactSchema).default([]),
14688
- preferences: z23.array(RawPreferenceSchema).default([]),
14689
- entities: z23.array(RawEntitySchema).default([]),
14690
- relations: z23.array(RawRelationSchema).default([])
16688
+ var RawExtractionSchema = z24.object({
16689
+ facts: z24.array(RawFactSchema).default([]),
16690
+ preferences: z24.array(RawPreferenceSchema).default([]),
16691
+ entities: z24.array(RawEntitySchema).default([]),
16692
+ relations: z24.array(RawRelationSchema).default([])
14691
16693
  });
14692
16694
  var DEFAULT_CONFIDENCE = 0.6;
14693
16695
  var SYSTEM_PROMPT = [
@@ -14941,13 +16943,13 @@ var memoryToolMessages = {
14941
16943
 
14942
16944
  // src/plugin/memoryTool.ts
14943
16945
  var SCRIBE_ROLE_ID = "scribe";
14944
- var MemoryMessageSchema = z24.object({
14945
- role: z24.enum(["system", "user", "assistant"]).describe(memoryToolMessages.messageRoleDescription),
14946
- content: z24.string().min(1).describe(memoryToolMessages.messageContentDescription)
16946
+ var MemoryMessageSchema = z25.object({
16947
+ role: z25.enum(["system", "user", "assistant"]).describe(memoryToolMessages.messageRoleDescription),
16948
+ content: z25.string().min(1).describe(memoryToolMessages.messageContentDescription)
14947
16949
  }).strict();
14948
- var MemoryWritePayloadSchema = z24.object({
14949
- messages: z24.array(MemoryMessageSchema).min(1).describe(memoryToolMessages.messagesDescription),
14950
- roleID: z24.string().min(1).default(SCRIBE_ROLE_ID).describe(memoryToolMessages.roleIDDescription)
16950
+ var MemoryWritePayloadSchema = z25.object({
16951
+ messages: z25.array(MemoryMessageSchema).min(1).describe(memoryToolMessages.messagesDescription),
16952
+ roleID: z25.string().min(1).default(SCRIBE_ROLE_ID).describe(memoryToolMessages.roleIDDescription)
14951
16953
  }).strict();
14952
16954
  function createMemoryTool(deps) {
14953
16955
  return tool3({
@@ -15072,10 +17074,10 @@ function createOrchestratedModelOwnership() {
15072
17074
 
15073
17075
  // src/plugin/orchestrateTool.ts
15074
17076
  import { tool as tool4 } from "@opencode-ai/plugin";
15075
- import { z as z26 } from "zod";
17077
+ import { z as z27 } from "zod";
15076
17078
 
15077
17079
  // src/orchestrator/coordinator.ts
15078
- import { z as z25 } from "zod";
17080
+ import { z as z26 } from "zod";
15079
17081
 
15080
17082
  // src/orchestrator/nodeIteration.ts
15081
17083
  var DEFAULT_MAX_RETRIES = 2;
@@ -15084,7 +17086,7 @@ function nodePassBudget(maxRetries) {
15084
17086
  }
15085
17087
 
15086
17088
  // src/orchestrator/outputContract.ts
15087
- import { createHash as createHash2 } from "node:crypto";
17089
+ import { createHash as createHash3 } from "node:crypto";
15088
17090
  var ROLE_REPORTS_ROOT = ".opencode/openteam-local/reports";
15089
17091
  var REPORT_STATUS_MARKER = "openteam-status";
15090
17092
  var REPORT_STATUS_LINE_PATTERN = /^[ ]{0,3}openteam-status:[ \t]*(.*?)[ \t]*$/i;
@@ -15151,7 +17153,7 @@ var REPORT_SEGMENT_HASH_CHARS = 24;
15151
17153
  var SAFE_REPORT_SEGMENT = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9_-])?$/;
15152
17154
  var WINDOWS_DEVICE_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i;
15153
17155
  function reportSegmentHash(value) {
15154
- return createHash2("sha256").update(value, "utf8").digest("hex").slice(0, REPORT_SEGMENT_HASH_CHARS);
17156
+ return createHash3("sha256").update(value, "utf8").digest("hex").slice(0, REPORT_SEGMENT_HASH_CHARS);
15155
17157
  }
15156
17158
  function reportPathSegment(value) {
15157
17159
  if (value.length <= MAX_REPORT_SEGMENT_CHARS && SAFE_REPORT_SEGMENT.test(value) && value === value.toLowerCase() && !WINDOWS_DEVICE_NAME.test(value)) {
@@ -15543,7 +17545,7 @@ function buildRecord(input, decision2, subsession, deps, decisionID, batchID) {
15543
17545
  }
15544
17546
  return record;
15545
17547
  }
15546
- var TelemetryFailureClassSchema = z25.enum([
17548
+ var TelemetryFailureClassSchema = z26.enum([
15547
17549
  "create-rejected",
15548
17550
  "create-no-id",
15549
17551
  "prompt-rejected",
@@ -16359,7 +18361,7 @@ async function runRoleTasks(inputs, deps, options = {}) {
16359
18361
  var _pluginWarnedRoles = new Set;
16360
18362
  var EMPTY_ROLE_ID_SET = new Set;
16361
18363
  var SAFE_ROLE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
16362
- var RoleIDSchema = z26.string().min(1).superRefine((roleID, ctx) => {
18364
+ var RoleIDSchema = z27.string().min(1).superRefine((roleID, ctx) => {
16363
18365
  if (!SAFE_ROLE_ID.test(roleID)) {
16364
18366
  ctx.addIssue({
16365
18367
  code: "custom",
@@ -16367,16 +18369,16 @@ var RoleIDSchema = z26.string().min(1).superRefine((roleID, ctx) => {
16367
18369
  });
16368
18370
  }
16369
18371
  });
16370
- var RoleAssignmentSchema = z26.object({
18372
+ var RoleAssignmentSchema = z27.object({
16371
18373
  roleID: RoleIDSchema.describe("Role identifier from the roster"),
16372
- prompt: z26.string().min(1).describe("Work prompt for this role"),
16373
- title: z26.string().min(1).optional().describe("Human-readable title"),
16374
- dependsOn: z26.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
18374
+ prompt: z27.string().min(1).describe("Work prompt for this role"),
18375
+ title: z27.string().min(1).optional().describe("Human-readable title"),
18376
+ dependsOn: z27.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
16375
18377
  }).strict();
16376
- var OrchestratePayloadSchema = z26.object({
16377
- assignments: z26.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
16378
- parentSessionID: z26.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
16379
- directory: z26.string().min(1).optional().describe("Working directory override")
18378
+ var OrchestratePayloadSchema = z27.object({
18379
+ assignments: z27.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
18380
+ parentSessionID: z27.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
18381
+ directory: z27.string().min(1).optional().describe("Working directory override")
16380
18382
  }).strict();
16381
18383
  function validateAssignmentDependencies(assignments) {
16382
18384
  const ids = new Set;
@@ -16434,12 +18436,12 @@ function validateAssignmentDependencies(assignments) {
16434
18436
  }
16435
18437
  return;
16436
18438
  }
16437
- function isRecord5(value) {
18439
+ function isRecord7(value) {
16438
18440
  return typeof value === "object" && value !== null && !Array.isArray(value);
16439
18441
  }
16440
18442
  function nestedRecord2(value, key) {
16441
18443
  const nested = value[key];
16442
- return isRecord5(nested) ? nested : undefined;
18444
+ return isRecord7(nested) ? nested : undefined;
16443
18445
  }
16444
18446
  function numberField(records, keys) {
16445
18447
  for (const record of records) {
@@ -16512,7 +18514,7 @@ function retryableFailure(failureClass, statusCode, declared) {
16512
18514
  return failureClass === "prompt-timeout" || failureClass === "prompt-rate-limited" || failureClass === "prompt-connection" || failureClass === "prompt-server" || statusCode !== undefined && statusCode >= 500 && statusCode <= 599;
16513
18515
  }
16514
18516
  function normalizeSdkSessionFailure(error, stage) {
16515
- const root = isRecord5(error) ? error : {};
18517
+ const root = isRecord7(error) ? error : {};
16516
18518
  const data = nestedRecord2(root, "data");
16517
18519
  const nestedError = nestedRecord2(root, "error");
16518
18520
  const records = [
@@ -16531,7 +18533,7 @@ function normalizeSdkSessionFailure(error, stage) {
16531
18533
  };
16532
18534
  }
16533
18535
  function nestedAssistantFailure(data) {
16534
- if (!isRecord5(data)) {
18536
+ if (!isRecord7(data)) {
16535
18537
  return;
16536
18538
  }
16537
18539
  return nestedRecord2(data, "info")?.error;
@@ -17112,7 +19114,7 @@ ${advisory}`;
17112
19114
 
17113
19115
  // src/plugin/registerCastTool.ts
17114
19116
  import { tool as tool5 } from "@opencode-ai/plugin";
17115
- import { z as z27 } from "zod";
19117
+ import { z as z28 } from "zod";
17116
19118
 
17117
19119
  // src/orchestrator/rosterPersistence.ts
17118
19120
  function rosterFence(roster) {
@@ -17147,13 +19149,13 @@ async function persistRosterPreservingProse(storage, roster, path4 = OPENTEAM_RO
17147
19149
  }
17148
19150
 
17149
19151
  // src/plugin/registerCastTool.ts
17150
- var CastEntrySchema = z27.object({
17151
- roleID: z27.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
17152
- agentName: z27.string().min(1).describe("Themed character name assigned to the role")
19152
+ var CastEntrySchema = z28.object({
19153
+ roleID: z28.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
19154
+ agentName: z28.string().min(1).describe("Themed character name assigned to the role")
17153
19155
  }).strict();
17154
- var RegisterCastPayloadSchema = z27.object({
17155
- universe: z27.string().min(1).describe("Casting universe for this roster"),
17156
- entries: z27.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
19156
+ var RegisterCastPayloadSchema = z28.object({
19157
+ universe: z28.string().min(1).describe("Casting universe for this roster"),
19158
+ entries: z28.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
17157
19159
  }).strict();
17158
19160
  function offendingFields(error) {
17159
19161
  const fields = error.issues.flatMap((issue) => {
@@ -17443,7 +19445,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
17443
19445
  }
17444
19446
 
17445
19447
  // src/storage/lifecycle/fsLifecycleJournal.ts
17446
- import { randomUUID as randomUUID2 } from "node:crypto";
19448
+ import { randomUUID as randomUUID3 } from "node:crypto";
17447
19449
  import {
17448
19450
  lstatSync as lstatSync3,
17449
19451
  mkdirSync as mkdirSync3,
@@ -17453,22 +19455,22 @@ import {
17453
19455
  statSync,
17454
19456
  unlinkSync as unlinkSync2
17455
19457
  } from "node:fs";
17456
- import { join as join12, resolve as resolve7 } from "node:path";
19458
+ import { join as join13, resolve as resolve8 } from "node:path";
17457
19459
  import { ZodError } from "zod";
17458
19460
 
17459
19461
  // src/storage/lifecycle/codec.ts
17460
- import { z as z28 } from "zod";
19462
+ import { z as z29 } from "zod";
17461
19463
  var LIFECYCLE_CODEC_VERSION = 1;
17462
19464
  var LIFECYCLE_GENESIS_DIGEST = sha256Hex("openteam/lifecycle-journal/genesis/v1");
17463
19465
  var NUL5 = "\x00";
17464
19466
  var NEWLINE2 = `
17465
19467
  `;
17466
- var frameSchema = z28.object({
17467
- v: z28.literal(LIFECYCLE_CODEC_VERSION),
17468
- seq: z28.number().int().nonnegative(),
17469
- prev: z28.string().min(1),
17470
- sum: z28.string().min(1),
17471
- event: z28.unknown()
19468
+ var frameSchema = z29.object({
19469
+ v: z29.literal(LIFECYCLE_CODEC_VERSION),
19470
+ seq: z29.number().int().nonnegative(),
19471
+ prev: z29.string().min(1),
19472
+ sum: z29.string().min(1),
19473
+ event: z29.unknown()
17472
19474
  }).strict();
17473
19475
  function canonical2(value) {
17474
19476
  if (Array.isArray(value)) {
@@ -17555,7 +19557,7 @@ function decodeLifecycleJournal(text) {
17555
19557
  }
17556
19558
 
17557
19559
  // src/storage/lifecycle/fsLifecycleSupport.ts
17558
- import { randomUUID } from "node:crypto";
19560
+ import { randomUUID as randomUUID2 } from "node:crypto";
17559
19561
  import {
17560
19562
  appendFileSync,
17561
19563
  closeSync as closeSync2,
@@ -17572,7 +19574,7 @@ import {
17572
19574
  unlinkSync,
17573
19575
  writeSync as writeSync2
17574
19576
  } from "node:fs";
17575
- import { basename as basename2, dirname as dirname4, join as join11, relative as relative2, resolve as resolve6 } from "node:path";
19577
+ import { basename as basename3, dirname as dirname5, join as join12, relative as relative3, resolve as resolve7 } from "node:path";
17576
19578
  var SAFE_RUN_ID = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?$/u;
17577
19579
 
17578
19580
  class LifecycleFsError extends Error {
@@ -17596,7 +19598,7 @@ function validateRunID(runID) {
17596
19598
  }
17597
19599
  function resolvedLifecycleRoot(root) {
17598
19600
  try {
17599
- const absolute = resolve6(root);
19601
+ const absolute = resolve7(root);
17600
19602
  mkdirSync2(absolute, { recursive: true });
17601
19603
  const rootStat = lstatSync2(absolute);
17602
19604
  if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
@@ -17612,8 +19614,8 @@ function resolvedLifecycleRoot(root) {
17612
19614
  }
17613
19615
  function directRunPath(root, runID) {
17614
19616
  validateRunID(runID);
17615
- const path4 = join11(root, runID);
17616
- if (dirname4(path4) !== root || basename2(path4) !== runID) {
19617
+ const path4 = join12(root, runID);
19618
+ if (dirname5(path4) !== root || basename3(path4) !== runID) {
17617
19619
  throw new LifecycleFsError("corrupt");
17618
19620
  }
17619
19621
  return path4;
@@ -17638,8 +19640,8 @@ function assertExistingSafeRun(root, runID) {
17638
19640
  } catch {
17639
19641
  throw new LifecycleFsError("unavailable");
17640
19642
  }
17641
- const child = relative2(root, real);
17642
- if (child === "" || child.startsWith("..") || resolve6(root, child) !== real || dirname4(real) !== root) {
19643
+ const child = relative3(root, real);
19644
+ if (child === "" || child.startsWith("..") || resolve7(root, child) !== real || dirname5(real) !== root) {
17643
19645
  throw new LifecycleFsError("corrupt");
17644
19646
  }
17645
19647
  return real;
@@ -17688,7 +19690,7 @@ function appendDurable2(path4, content) {
17688
19690
  }
17689
19691
  }
17690
19692
  function writeDurableAtomically(path4, content) {
17691
- const temporary = join11(dirname4(path4), `.${basename2(path4)}.${randomUUID()}.next`);
19693
+ const temporary = join12(dirname5(path4), `.${basename3(path4)}.${randomUUID2()}.next`);
17692
19694
  try {
17693
19695
  writeDurable2(temporary, content);
17694
19696
  renameSync(temporary, path4);
@@ -17710,8 +19712,8 @@ function readLockContents(path4) {
17710
19712
  }
17711
19713
  }
17712
19714
  function withRunMutationLock(runDir, mutate) {
17713
- const lockPath = join11(runDir, ".mutation-lock");
17714
- const lockToken = randomUUID();
19715
+ const lockPath = join12(runDir, ".mutation-lock");
19716
+ const lockToken = randomUUID2();
17715
19717
  let descriptor;
17716
19718
  try {
17717
19719
  descriptor = openSync2(lockPath, "wx");
@@ -17780,7 +19782,7 @@ function parseJson(text, parse2) {
17780
19782
  }
17781
19783
  }
17782
19784
  function issueWriterToken() {
17783
- return randomUUID2().replaceAll("-", "");
19785
+ return randomUUID3().replaceAll("-", "");
17784
19786
  }
17785
19787
  function createLease(runID, writerToken, input) {
17786
19788
  return parseLifecycleWriterLease({
@@ -17795,12 +19797,12 @@ function createLease(runID, writerToken, input) {
17795
19797
  });
17796
19798
  }
17797
19799
  function readLease2(runDir) {
17798
- const text = readUtf8(join12(runDir, OWNER_FILE2));
19800
+ const text = readUtf8(join13(runDir, OWNER_FILE2));
17799
19801
  return text === undefined ? undefined : parseJson(text, parseLifecycleWriterLease);
17800
19802
  }
17801
19803
  function readRun(runDir, runID) {
17802
- const metadataPath = join12(runDir, METADATA_FILE);
17803
- const eventsPath = join12(runDir, EVENTS_FILE);
19804
+ const metadataPath = join13(runDir, METADATA_FILE);
19805
+ const eventsPath = join13(runDir, EVENTS_FILE);
17804
19806
  assertSafeRegularFile(metadataPath);
17805
19807
  assertSafeRegularFile(eventsPath);
17806
19808
  const metadataText = readUtf8(metadataPath);
@@ -17847,7 +19849,7 @@ function summaryForRun(runDir, runID) {
17847
19849
  }
17848
19850
  function runBytes(runDir) {
17849
19851
  return readdirSync2(runDir).reduce((total, entry) => {
17850
- const path4 = join12(runDir, entry);
19852
+ const path4 = join13(runDir, entry);
17851
19853
  const stats = lstatSync3(path4);
17852
19854
  if (!stats.isFile() || stats.isSymbolicLink()) {
17853
19855
  throw new LifecycleFsError("corrupt");
@@ -17879,7 +19881,7 @@ function canDeleteTerminalRun(runDir, runID, now) {
17879
19881
  return now >= lease.expiresAt;
17880
19882
  }
17881
19883
  var createFsLifecycleJournal = (options) => {
17882
- const configuredRoot = resolve7(options.root);
19884
+ const configuredRoot = resolve8(options.root);
17883
19885
  const now = options.now ?? Date.now;
17884
19886
  const mutateRun = options.withMutationLock ?? withRunMutationLock;
17885
19887
  return {
@@ -17901,7 +19903,7 @@ var createFsLifecycleJournal = (options) => {
17901
19903
  throw error;
17902
19904
  }
17903
19905
  }
17904
- stagingPath = join12(root, `.start-${runID}-${randomUUID2()}`);
19906
+ stagingPath = join13(root, `.start-${runID}-${randomUUID3()}`);
17905
19907
  mkdirSync3(stagingPath);
17906
19908
  const writerToken = issueWriterToken();
17907
19909
  const eventAt = now();
@@ -17921,10 +19923,10 @@ var createFsLifecycleJournal = (options) => {
17921
19923
  executionAuthority: parsedInput.executionAuthority,
17922
19924
  root: parsedInput.root
17923
19925
  });
17924
- writeDurable2(join12(stagingPath, METADATA_FILE), `${JSON.stringify(metadata)}
19926
+ writeDurable2(join13(stagingPath, METADATA_FILE), `${JSON.stringify(metadata)}
17925
19927
  `);
17926
- writeDurable2(join12(stagingPath, EVENTS_FILE), encodeLifecycleJournal([event]));
17927
- writeDurable2(join12(stagingPath, OWNER_FILE2), `${JSON.stringify(lease)}
19928
+ writeDurable2(join13(stagingPath, EVENTS_FILE), encodeLifecycleJournal([event]));
19929
+ writeDurable2(join13(stagingPath, OWNER_FILE2), `${JSON.stringify(lease)}
17928
19930
  `);
17929
19931
  try {
17930
19932
  renameSync2(stagingPath, runPath);
@@ -17966,7 +19968,7 @@ var createFsLifecycleJournal = (options) => {
17966
19968
  }
17967
19969
  const writerToken = issueWriterToken();
17968
19970
  const lease = createLease(runID, writerToken, parsedLeaseInput);
17969
- writeDurableAtomically(join12(runDir, OWNER_FILE2), `${JSON.stringify(lease)}
19971
+ writeDurableAtomically(join13(runDir, OWNER_FILE2), `${JSON.stringify(lease)}
17970
19972
  `);
17971
19973
  return ok({ runID, writerToken });
17972
19974
  });
@@ -18023,7 +20025,7 @@ var createFsLifecycleJournal = (options) => {
18023
20025
  ...parsedBody
18024
20026
  });
18025
20027
  const frame = encodeLifecycleFrame(loaded.decoded.digest, committedEvent);
18026
- appendDurable2(join12(runDir, EVENTS_FILE), `${frame.line}
20028
+ appendDurable2(join13(runDir, EVENTS_FILE), `${frame.line}
18027
20029
  `);
18028
20030
  return ok({ event: committedEvent, head: actualHead + 1 });
18029
20031
  });
@@ -18053,7 +20055,7 @@ var createFsLifecycleJournal = (options) => {
18053
20055
  heartbeatAt: parsed.now,
18054
20056
  expiresAt: parsed.now + parsed.leaseDurationMs
18055
20057
  });
18056
- writeDurableAtomically(join12(runDir, OWNER_FILE2), `${JSON.stringify(lease)}
20058
+ writeDurableAtomically(join13(runDir, OWNER_FILE2), `${JSON.stringify(lease)}
18057
20059
  `);
18058
20060
  return ok({ lease });
18059
20061
  });
@@ -18073,7 +20075,7 @@ var createFsLifecycleJournal = (options) => {
18073
20075
  runID: handle.runID
18074
20076
  }));
18075
20077
  }
18076
- unlinkSync2(join12(runDir, OWNER_FILE2));
20078
+ unlinkSync2(join13(runDir, OWNER_FILE2));
18077
20079
  return ok(undefined);
18078
20080
  });
18079
20081
  } catch (error) {
@@ -18184,14 +20186,14 @@ var createFsLifecycleJournal = (options) => {
18184
20186
  };
18185
20187
 
18186
20188
  // src/storage/lifecycle/fsLifecycleWriterLivenessProbe.ts
18187
- import { join as join13 } from "node:path";
20189
+ import { join as join14 } from "node:path";
18188
20190
  var createFsLifecycleWriterLivenessProbe = (options) => ({
18189
20191
  async observe(runID) {
18190
20192
  const observedAt = options.now();
18191
20193
  try {
18192
20194
  const root = resolvedLifecycleRoot(options.root);
18193
20195
  const runDir = assertExistingSafeRun(root, runID);
18194
- const ownerText = readUtf8(join13(runDir, "owner"));
20196
+ const ownerText = readUtf8(join14(runDir, "owner"));
18195
20197
  if (ownerText === undefined) {
18196
20198
  return { kind: "absent", observedAt };
18197
20199
  }
@@ -18278,111 +20280,6 @@ var createOtlpSpanExporter = (connection, config) => {
18278
20280
  }
18279
20281
  };
18280
20282
  };
18281
- // package.json
18282
- var package_default = {
18283
- name: "@jmanuelcorral/openteam",
18284
- version: "0.21.0",
18285
- packageManager: "bun@1.3.14",
18286
- description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
18287
- license: "MIT",
18288
- author: "Jose Manuel Corral",
18289
- repository: {
18290
- type: "git",
18291
- url: "git+https://github.com/jmanuelcorral/openteam.git"
18292
- },
18293
- homepage: "https://github.com/jmanuelcorral/openteam#readme",
18294
- bugs: {
18295
- url: "https://github.com/jmanuelcorral/openteam/issues"
18296
- },
18297
- keywords: [
18298
- "opencode",
18299
- "opencode-plugin",
18300
- "llm",
18301
- "routing",
18302
- "local-llm",
18303
- "ollama",
18304
- "lm-studio",
18305
- "foundry-local",
18306
- "cost-optimization",
18307
- "multi-agent"
18308
- ],
18309
- engines: {
18310
- bun: ">=1.3",
18311
- node: "^22.22.2 || ^24.15.0 || >=26.0.0"
18312
- },
18313
- type: "module",
18314
- main: "./dist/index.js",
18315
- module: "./dist/index.js",
18316
- types: "./dist/index.d.ts",
18317
- bin: {
18318
- openteam: "./dist/cli.js"
18319
- },
18320
- exports: {
18321
- ".": {
18322
- types: "./dist/index.d.ts",
18323
- import: "./dist/index.js"
18324
- },
18325
- "./package.json": "./package.json"
18326
- },
18327
- files: [
18328
- "dist",
18329
- "README.md",
18330
- "LICENSE",
18331
- "AGENTS.md",
18332
- ".opencode/openteam.example.json",
18333
- ".opencode/command/openteam.md"
18334
- ],
18335
- publishConfig: {
18336
- access: "public",
18337
- registry: "https://registry.npmjs.org/"
18338
- },
18339
- trustedDependencies: [],
18340
- sideEffects: false,
18341
- scripts: {
18342
- prebuild: "bun run clean",
18343
- build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
18344
- "build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
18345
- "build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
18346
- "build:types": "tsc -p tsconfig.build.json",
18347
- clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
18348
- test: "bun test",
18349
- "test:cov": "bun test --coverage",
18350
- "certify:shadow": "bun run scripts/certify-artifact.ts shadow",
18351
- "certify:release": "bun run scripts/certify-artifact.ts release",
18352
- "build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
18353
- "coverage:check": "node scripts/check-coverage.mjs",
18354
- typecheck: "tsc --noEmit",
18355
- lint: "biome check .",
18356
- "format:check": "biome format .",
18357
- "docs:install": "cd docs && bun install --frozen-lockfile --ignore-scripts",
18358
- "docs:dev": "cd docs && bun run docs:dev",
18359
- "docs:build": "cd docs && bun run docs:build",
18360
- "docs:preview": "cd docs && bun run docs:preview",
18361
- prepublishOnly: "bun run build",
18362
- "link:local": "bun run build && npm link",
18363
- "hooks:install": "git config core.hooksPath .githooks"
18364
- },
18365
- dependencies: {
18366
- "@clack/prompts": "1.7.0",
18367
- "@langchain/core": "1.2.9",
18368
- "@langchain/langgraph": "1.4.12",
18369
- "@opencode-ai/plugin": "1.18.19",
18370
- "@opencode-ai/sdk": "1.18.19",
18371
- "@opentelemetry/api": "1.9.1",
18372
- "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
18373
- "@opentelemetry/resources": "2.10.0",
18374
- "@opentelemetry/sdk-trace-base": "2.10.0",
18375
- zod: "4.4.3"
18376
- },
18377
- devDependencies: {
18378
- "@biomejs/biome": "2.5.9",
18379
- "@types/bun": "1.3.14",
18380
- typescript: "7.0.2"
18381
- }
18382
- };
18383
-
18384
- // src/version.ts
18385
- var PACKAGE_VERSION = package_default.version;
18386
20283
 
18387
20284
  // src/index.ts
18388
20285
  var LIFECYCLE_WRITER_IDENTITY = {
@@ -18484,7 +20381,7 @@ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
18484
20381
  var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
18485
20382
  async function readPackagedCertificate(path4) {
18486
20383
  try {
18487
- return await readFile3(new URL(`./certificates/${basename3(path4)}`, import.meta.url), "utf8");
20384
+ return await readFile4(new URL(`./certificates/${basename4(path4)}`, import.meta.url), "utf8");
18488
20385
  } catch {
18489
20386
  return;
18490
20387
  }
@@ -18499,7 +20396,7 @@ async function readAndParseCertificate(storage, path4, parse2, digestExtractor,
18499
20396
  const cert = parse2(raw);
18500
20397
  return { status: "valid", digest: digestExtractor(cert) };
18501
20398
  } catch (error) {
18502
- if (isMissingFile(error)) {
20399
+ if (isMissingFile2(error)) {
18503
20400
  return { status: "absent" };
18504
20401
  }
18505
20402
  if (error instanceof ShadowCertificateError || error instanceof ReleaseCertificateError || error instanceof SoakCertificateError) {
@@ -18616,8 +20513,8 @@ async function readOpencodeVersion(serverUrl, options = {}) {
18616
20513
  }
18617
20514
  var DEFAULT_OPENCODE_VERSION_BACKOFF_MS = [50, 250];
18618
20515
  function defaultSleep(ms) {
18619
- return new Promise((resolve8) => {
18620
- setTimeout(resolve8, ms);
20516
+ return new Promise((resolve9) => {
20517
+ setTimeout(resolve9, ms);
18621
20518
  });
18622
20519
  }
18623
20520
  function createLazyOpencodeVersionReader(serverUrl, options = {}) {
@@ -18745,7 +20642,7 @@ function createEventSink(rawOptions, storage = createFsStorageProvider(process.c
18745
20642
  });
18746
20643
  return sink;
18747
20644
  }
18748
- function isMissingFile(error) {
20645
+ function isMissingFile2(error) {
18749
20646
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
18750
20647
  }
18751
20648
  function configReadFailure(path4) {
@@ -18785,7 +20682,7 @@ async function readRoutingRosterEntries(storage) {
18785
20682
  return content === undefined ? [] : parseRoster(content).entries;
18786
20683
  }
18787
20684
  var auditStoredRosterForDoctor = (roster, configuredRoles) => typeof roster === "string" ? auditRoster(roster, configuredRoles) : auditRoster(roster, configuredRoles);
18788
- function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SESSIONS_DIR, storage = createFsStorageProvider(process.cwd()), fallbackSource = "options", rawOptions, resolveOpencodeVersion) {
20685
+ function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SESSIONS_DIR, storage = createFsStorageProvider(process.cwd()), fallbackSource = "options", rawOptions, resolveOpencodeVersion, workspaceRoot = process.cwd()) {
18789
20686
  return {
18790
20687
  loadConfig: async (path4, resolution = { explicit: false }) => {
18791
20688
  const content = await readCliConfigFromStorage(storage, path4, resolution);
@@ -18868,12 +20765,13 @@ function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SE
18868
20765
  telemetryPath,
18869
20766
  opencodeConfigPaths: OPENCODE_CONFIG_CANDIDATES,
18870
20767
  orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH,
18871
- agentDir: dirname5(ORCHESTRATOR_AGENT_PATH),
20768
+ agentDir: dirname6(ORCHESTRATOR_AGENT_PATH),
18872
20769
  cachePort: realCacheAdapter,
18873
20770
  purge: createPluginPurgeRuntime(sessionsDir),
18874
20771
  loadRosterForDoctor: () => readStoredRosterForDoctor(storage),
18875
20772
  auditRoster: auditStoredRosterForDoctor,
18876
- version: PACKAGE_VERSION
20773
+ version: PACKAGE_VERSION,
20774
+ upgradePort: createFsUpgradePort(workspaceRoot, globalThis.fetch)
18877
20775
  };
18878
20776
  }
18879
20777
  function createPluginPurgeRuntime(sessionsDir) {
@@ -18998,7 +20896,7 @@ var server = async (ctx, rawOptions) => {
18998
20896
  const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions, async () => {
18999
20897
  const version = await getOpencodeVersion();
19000
20898
  return version === "unknown" ? undefined : version;
19001
- });
20899
+ }, workspaceRoot);
19002
20900
  const toolcalls = createToolcallTracker({ sink });
19003
20901
  const announcedSessions = new Set;
19004
20902
  const announceEndpoint = (sessionID) => {
@@ -19140,8 +21038,8 @@ var server = async (ctx, rawOptions) => {
19140
21038
  lifecycleRecorder,
19141
21039
  abortGraceMs: DEFAULT_ABORT_GRACE_MS,
19142
21040
  statusPollMs: DEFAULT_STATUS_POLL_MS,
19143
- delay: (ms) => new Promise((resolve8) => {
19144
- setTimeout(resolve8, ms);
21041
+ delay: (ms) => new Promise((resolve9) => {
21042
+ setTimeout(resolve9, ms);
19145
21043
  }),
19146
21044
  warn: diagnostics.warn("unknown-role")
19147
21045
  });
@@ -19211,7 +21109,7 @@ export {
19211
21109
  logTelemetryError,
19212
21110
  logOpencodeServerUrl,
19213
21111
  logAvailabilityRefreshError,
19214
- isMissingFile,
21112
+ isMissingFile2 as isMissingFile,
19215
21113
  evaluateCutoverGateForBoundary,
19216
21114
  src_default as default,
19217
21115
  createOpencodeVersionReadFailureLogger,