@jmanuelcorral/openteam 0.25.0 → 0.27.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 (100) hide show
  1. package/README.es.md +39 -13
  2. package/README.md +39 -14
  3. package/dist/certificates/graph-release-certificate.json +2 -2
  4. package/dist/certificates/graph-shadow-certificate.json +2 -2
  5. package/dist/certificates/opencode-2.0.5/graph-release-certificate.json +45 -0
  6. package/dist/certificates/opencode-2.0.5/graph-shadow-certificate.json +39 -0
  7. package/dist/cli/consoleRuntimeState.d.ts.map +1 -1
  8. package/dist/cli/consoleServe.d.ts +2 -1
  9. package/dist/cli/consoleServe.d.ts.map +1 -1
  10. package/dist/cli/setupAdapters.d.ts.map +1 -1
  11. package/dist/cli.js +24534 -14551
  12. package/dist/commands/agents.d.ts.map +1 -1
  13. package/dist/commands/dispatch.d.ts.map +1 -1
  14. package/dist/commands/doctor.d.ts.map +1 -1
  15. package/dist/commands/setup.d.ts +22 -15
  16. package/dist/commands/setup.d.ts.map +1 -1
  17. package/dist/commands/upgrade.d.ts +1 -1
  18. package/dist/commands/upgrade.d.ts.map +1 -1
  19. package/dist/config/fieldProjection.d.ts +3 -0
  20. package/dist/config/fieldProjection.d.ts.map +1 -0
  21. package/dist/config/nativeOpencode.d.ts +5 -0
  22. package/dist/config/nativeOpencode.d.ts.map +1 -0
  23. package/dist/config/schema.d.ts +101 -7
  24. package/dist/config/schema.d.ts.map +1 -1
  25. package/dist/console/auth.d.ts +5 -0
  26. package/dist/console/auth.d.ts.map +1 -0
  27. package/dist/console/conversation.d.ts +8 -1
  28. package/dist/console/conversation.d.ts.map +1 -1
  29. package/dist/console/historyBoundary.d.ts +1 -1
  30. package/dist/console/historyBoundary.d.ts.map +1 -1
  31. package/dist/console/nativeConversation.d.ts +7 -0
  32. package/dist/console/nativeConversation.d.ts.map +1 -0
  33. package/dist/console/opencodeClient.d.ts +3 -0
  34. package/dist/console/opencodeClient.d.ts.map +1 -1
  35. package/dist/console/pty.d.ts +4 -0
  36. package/dist/console/pty.d.ts.map +1 -1
  37. package/dist/contract/opencode.d.ts +13 -7
  38. package/dist/contract/opencode.d.ts.map +1 -1
  39. package/dist/index.d.ts +10 -5
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1796 -327
  42. package/dist/local/lemonadeResidency.d.ts +2 -14
  43. package/dist/local/lemonadeResidency.d.ts.map +1 -1
  44. package/dist/local/llamaSwap.d.ts +5 -0
  45. package/dist/local/llamaSwap.d.ts.map +1 -0
  46. package/dist/local/llamaSwapRequestGuard.d.ts +11 -0
  47. package/dist/local/llamaSwapRequestGuard.d.ts.map +1 -0
  48. package/dist/local/llamaSwapResidency.d.ts +9 -0
  49. package/dist/local/llamaSwapResidency.d.ts.map +1 -0
  50. package/dist/local/modelResidency.d.ts +1 -0
  51. package/dist/local/modelResidency.d.ts.map +1 -1
  52. package/dist/local/registry.d.ts.map +1 -1
  53. package/dist/local/residency.d.ts +16 -0
  54. package/dist/local/residency.d.ts.map +1 -0
  55. package/dist/local/types.d.ts +10 -2
  56. package/dist/local/types.d.ts.map +1 -1
  57. package/dist/messages/consoleDetail.d.ts +3 -0
  58. package/dist/messages/consoleDetail.d.ts.map +1 -1
  59. package/dist/messages/executionSetup.d.ts +22 -3
  60. package/dist/messages/executionSetup.d.ts.map +1 -1
  61. package/dist/messages/memoryRuntime.d.ts +6 -0
  62. package/dist/messages/memoryRuntime.d.ts.map +1 -0
  63. package/dist/messages/memoryTool.d.ts +1 -1
  64. package/dist/messages/memoryTool.d.ts.map +1 -1
  65. package/dist/messages/modelResidency.d.ts +20 -2
  66. package/dist/messages/modelResidency.d.ts.map +1 -1
  67. package/dist/messages/nativeHost.d.ts +21 -0
  68. package/dist/messages/nativeHost.d.ts.map +1 -0
  69. package/dist/plugin/availability.d.ts +1 -0
  70. package/dist/plugin/availability.d.ts.map +1 -1
  71. package/dist/plugin/consoleTool.d.ts +1 -0
  72. package/dist/plugin/consoleTool.d.ts.map +1 -1
  73. package/dist/plugin/hooks.d.ts +3 -3
  74. package/dist/plugin/hooks.d.ts.map +1 -1
  75. package/dist/plugin/memoryTool.d.ts.map +1 -1
  76. package/dist/plugin/modelResidency.d.ts +1 -0
  77. package/dist/plugin/modelResidency.d.ts.map +1 -1
  78. package/dist/plugin/nativeHost.d.ts +5 -0
  79. package/dist/plugin/nativeHost.d.ts.map +1 -0
  80. package/dist/plugin/nativeHostContext.d.ts +228 -0
  81. package/dist/plugin/nativeHostContext.d.ts.map +1 -0
  82. package/dist/plugin/nativeInventory.d.ts +5 -0
  83. package/dist/plugin/nativeInventory.d.ts.map +1 -0
  84. package/dist/plugin/nativeResidency.d.ts +30 -0
  85. package/dist/plugin/nativeResidency.d.ts.map +1 -0
  86. package/dist/plugin/nativeSessions.d.ts +131 -0
  87. package/dist/plugin/nativeSessions.d.ts.map +1 -0
  88. package/dist/plugin/orchestrateTool.d.ts.map +1 -1
  89. package/dist/plugin/reloadTool.d.ts +3 -2
  90. package/dist/plugin/reloadTool.d.ts.map +1 -1
  91. package/dist/plugin/runtimeContext.d.ts +42 -0
  92. package/dist/plugin/runtimeContext.d.ts.map +1 -0
  93. package/dist/storage/index/memoryRuntime.d.ts.map +1 -1
  94. package/dist/web/console.d.ts +1 -1
  95. package/dist/web/console.d.ts.map +1 -1
  96. package/dist/web/server.d.ts +1 -1
  97. package/dist/web/server.d.ts.map +1 -1
  98. package/dist/web/start.d.ts +1 -1
  99. package/dist/web/start.d.ts.map +1 -1
  100. package/package.json +6 -3
package/dist/index.js CHANGED
@@ -784,6 +784,9 @@ function migrateConfig(raw) {
784
784
 
785
785
  // src/messages/consoleDetail.ts
786
786
  var consoleDetailMessages = {
787
+ runtimeStateCleanupFailed: "The Console runtime state could not be published and its staging file could not be removed.",
788
+ requestFailed: "The local opencode request failed.",
789
+ nativeToolFailed: "The native tool execution failed.",
787
790
  follow: "Follow new activity",
788
791
  metadataOnly: "Lifecycle and telemetry stay metadata-only.",
789
792
  logsDescription: "Current recorded conversations load automatically. Lifecycle and telemetry remain metadata-only; credentials are masked.",
@@ -1049,7 +1052,8 @@ var LocalRuntimeKindSchema = z3.enum([
1049
1052
  "ollama",
1050
1053
  "lmstudio",
1051
1054
  "foundry-local",
1052
- "lemonade"
1055
+ "lemonade",
1056
+ "llama-swap"
1053
1057
  ]);
1054
1058
  var LocalRuntimeSchema = z3.object({
1055
1059
  id: z3.string().min(1),
@@ -1069,15 +1073,25 @@ var LocalRuntimeSchema = z3.object({
1069
1073
  ctx.addIssue({
1070
1074
  code: z3.ZodIssueCode.custom,
1071
1075
  path: ["kind"],
1072
- message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade), since the id is " + "not itself a runtime kind."
1076
+ message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade, llama-swap), " + "since the id is not itself a runtime kind."
1073
1077
  });
1074
1078
  }
1079
+ }).transform((runtime) => {
1080
+ const resolvedKind = runtime.kind ?? runtime.id;
1081
+ if (resolvedKind !== "llama-swap" || runtime.maxConcurrency !== undefined) {
1082
+ return runtime;
1083
+ }
1084
+ return { ...runtime, maxConcurrency: 1 };
1075
1085
  });
1076
1086
  function localRuntimeKind(runtime) {
1077
1087
  return runtime.kind ?? runtime.id;
1078
1088
  }
1079
1089
  function effectiveMaxConcurrency(runtime) {
1080
- return runtime.maxConcurrency ?? DEFAULT_LOCAL_MAX_CONCURRENCY;
1090
+ if (runtime.maxConcurrency !== undefined) {
1091
+ return runtime.maxConcurrency;
1092
+ }
1093
+ const kind = runtime.kind ?? (runtime.id !== undefined && LocalRuntimeKindSchema.safeParse(runtime.id).success ? runtime.id : undefined);
1094
+ return kind === "llama-swap" ? 1 : DEFAULT_LOCAL_MAX_CONCURRENCY;
1081
1095
  }
1082
1096
  var MemoryScopeSchema = z3.enum(["project", "user"]);
1083
1097
  var MemoryExtractionModeSchema = z3.enum([
@@ -5746,7 +5760,7 @@ import { z as z15 } from "zod";
5746
5760
  // package.json
5747
5761
  var package_default = {
5748
5762
  name: "@jmanuelcorral/openteam",
5749
- version: "0.25.0",
5763
+ version: "0.27.0",
5750
5764
  packageManager: "bun@1.3.14",
5751
5765
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
5752
5766
  license: "MIT",
@@ -5806,12 +5820,13 @@ var package_default = {
5806
5820
  scripts: {
5807
5821
  prebuild: "bun run clean",
5808
5822
  build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
5809
- "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",
5810
- "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',
5823
+ "build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external @opencode/schema --external effect --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
5824
+ "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 @opencode/schema --external effect --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',
5811
5825
  "build:types": "tsc -p tsconfig.build.json",
5812
5826
  clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
5813
5827
  test: "bun test",
5814
5828
  "test:cov": "bun test --coverage",
5829
+ "verify:opencode-native": "bun scripts/verify-opencode-native.mjs",
5815
5830
  "certify:shadow": "bun run scripts/certify-artifact.ts shadow",
5816
5831
  "certify:release": "bun run scripts/certify-artifact.ts release",
5817
5832
  "build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
@@ -5833,10 +5848,12 @@ var package_default = {
5833
5848
  "@langchain/langgraph": "1.4.12",
5834
5849
  "@opencode-ai/plugin": "1.18.19",
5835
5850
  "@opencode-ai/sdk": "1.18.19",
5851
+ "@opencode/schema": "2.0.3",
5836
5852
  "@opentelemetry/api": "1.9.1",
5837
5853
  "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
5838
5854
  "@opentelemetry/resources": "2.10.0",
5839
5855
  "@opentelemetry/sdk-trace-base": "2.10.0",
5856
+ effect: "4.0.0-rc.112",
5840
5857
  zod: "4.4.3"
5841
5858
  },
5842
5859
  devDependencies: {
@@ -6103,6 +6120,23 @@ var executionSetupMessages = {
6103
6120
  `),
6104
6121
  localOnlyTitle: "Local execution",
6105
6122
  localRequiredError: "local execution requires an enabled local runtime",
6123
+ llamaSwap: {
6124
+ label: "llama-swap",
6125
+ noLoadedModels: "llama-swap has no canonical models verified as ready with no active routing profile. Keep the intended model ready through the operator's existing runtime controls, then rerun setup; manual IDs cannot bypass admission.",
6126
+ modelNotLoaded: (model) => `llama-swap model "${model}" is not positively verified as canonical and ready without an active routing profile. No substitute was selected.`,
6127
+ probeRequired: "llama-swap setup requires a fresh read-only residency probe before writing configuration. Manual model IDs cannot bypass this requirement.",
6128
+ probeFailed: "llama-swap readiness could not be verified. Check the configured /v1/models catalog and origin /running and /api/profiles endpoints; authenticated runtimes are not supported.",
6129
+ endpointMismatch: "llama-swap endpoint/provider mapping differs from the selected runtime. Reconcile the configured origin or /v1 URL and provider; no configuration was written.",
6130
+ modelIdentityUnknown: (model) => `llama-swap model "${model}" has no usable registered physical identity. Fix its model declaration; setup will not guess an alias.`,
6131
+ declaredLimits: "llama-swap context and output limits are operator-declared metadata, not measured per-slot capacities. Missing output metadata uses openteam's client policy default, not a guessed model cap. Unknown context remains 0 and disables automatic compaction. Tool support remains unknown without evidence.",
6132
+ declaredLimitsTitle: "Declared llama-swap limits",
6133
+ outputReduction: (model, previous, ceiling) => `${model}: reducing limit.output from ${previous} to ${ceiling} to respect the declared runtime ceiling. Smaller operator limits are preserved.`,
6134
+ outputReductionTitle: "Declared output ceiling",
6135
+ outputChanged: (model, ceiling) => `${model}: fresh runtime metadata declares an output ceiling of ${ceiling} below the prepared configuration. Rerun setup to review the updated limit; no configuration was written.`
6136
+ },
6137
+ residencyV2Title: "Resident runtime transport compatibility",
6138
+ residencyV2Restriction: (providers) => `Project policies disable opencode's separate native/core V2 transport for resident-only providers: ${providers.join(", ")}. Guarded V1 provider transport remains supported; this does not ban the public SDK v2 client library. Higher-precedence policies can override project restrictions. Readiness preflight is not an atomic no-autoload guarantee.`,
6139
+ retainedResidencyProviderUnprotected: (providerID) => `Cannot safely retain resident-only provider "${providerID}": the effective configuration must retain its matching runtime, disabled when deselected. Reconcile the runtime/provider configuration; no configuration was written.`,
6106
6140
  lemonadeDetectedHint: (loaded, advertised) => `detected - ${loaded} confirmed loaded of ${advertised} advertised text model(s)`,
6107
6141
  lemonadeNoLoadedModels: "Lemonade has no text models verified as loaded and ready. Keep the intended model active in Lemonade and rerun setup; inactive or unknown models cannot be selected or entered manually.",
6108
6142
  lemonadeModelNotLoaded: (model) => `Lemonade model "${model}" is not positively verified as loaded and ready. Keep that exact text model active and rerun setup; no substitute was selected and no implicit loading is allowed.`,
@@ -6117,11 +6151,11 @@ var executionSetupMessages = {
6117
6151
  lemonadeV2InvalidPolicies: "Cannot safely preserve experimental.policies: experimental must be an object and policies must be an array of provider.use statements with a string resource and allow/deny effect. Fix the project configuration before rerunning setup.",
6118
6152
  lemonadeV2Title: "Lemonade transport compatibility",
6119
6153
  lemonadeV2Restriction: (providers) => `Project policies disable native V2 Lemonade access for: ${providers.join(", ")}. Ordinary guarded V1 configuration remains supported; V2 is not an openteam-hook execution path. Higher-precedence or global policies can override this project restriction; it is not a server-atomic no-autoload guarantee.`,
6120
- lemonadeInventory: (instanceID, providerID, models) => {
6154
+ lemonadeInventory: (instanceID, providerID, models, runtime = "Lemonade") => {
6121
6155
  const inventory = models.map((model) => `${model.modelID} (${model.loaded === true ? "loaded" : model.loaded === false ? "inactive" : "residency unknown"})`).join(", ");
6122
- return `Lemonade inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
6156
+ return `${runtime} inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
6123
6157
  },
6124
- lemonadeAgentResidency: (agent, model, state) => `Lemonade readiness: ${agent} (${model}): ${state === "not-loaded" ? "inactive (not ready)" : "residency unknown"}; advertised inventory is not loaded readiness. Keep this exact model active and verify residency before use; requests without verified readiness are blocked.`,
6158
+ lemonadeAgentResidency: (agent, model, state, runtime = "Lemonade") => `${runtime} readiness: ${agent} (${model}): ${state === "not-loaded" ? "inactive (not ready)" : "residency unknown"}; advertised inventory is not loaded readiness. Keep this exact model active and verify residency before use; requests without verified readiness are blocked.`,
6125
6159
  mixedPrimaryQuestion: "Primary coordinator model",
6126
6160
  mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
6127
6161
  mixedPrimaryFrontier: "Choose a frontier model",
@@ -6225,6 +6259,8 @@ var executionPolicyMessages = {
6225
6259
  primaryLocalMismatchRemedy: (sourceFile) => ` remedy: re-run \`openteam setup\` to regenerate ${sourceFile} and opencode.json from the same primary policy, then re-run \`openteam doctor\`.`
6226
6260
  };
6227
6261
  var doctorMessages = {
6262
+ defaultRuntimeSlots: (slots) => ` · ${slots} slot(s) (default)`,
6263
+ llamaSwapConcurrency: " llama-swap does not report per-model slots; openteam uses the configured runtime-wide concurrency limit. No per-model capacity was inferred.",
6228
6264
  localModelLimits: {
6229
6265
  section: " local model limits:",
6230
6266
  healthy: " ✓ enabled local provider models declare valid output and context limits in opencode.json.",
@@ -6241,7 +6277,7 @@ var doctorMessages = {
6241
6277
  invalidInput: "invalid limit.input (expected a positive integer token count)",
6242
6278
  outputExceedsContext: "limit.output is greater than or equal to limit.context, so the configured context leaves no usable input window",
6243
6279
  outputExceedsInput: "limit.input is less than or equal to the reserved output budget, so compaction would have no usable input threshold",
6244
- outputRemedy: " remedy: re-run `openteam setup` to write openteam's 8 192-token local output default, then re-run `openteam doctor`.",
6280
+ outputRemedy: " remedy: re-run `openteam setup` to write openteam's local output policy (at most 8 192 tokens, narrowed by declared runtime ceilings and smaller context/input budgets), then re-run `openteam doctor`.",
6245
6281
  unconfiguredLimitRemedy: " remedy: the optional limit block may remain absent; to configure explicit limits, manually add a complete limit object in opencode.json with positive output and non-negative context (0 means unknown), because setup fills an absent limit block only for models the runtime currently discovers. Then re-run `openteam doctor`.",
6246
6282
  detectedContextRemedy: " remedy: re-run `openteam setup` while the runtime is reachable to copy the detected usable context budget, then re-run `openteam doctor`.",
6247
6283
  unknownContextRemedy: " remedy: re-run `openteam setup` to write the required context:0 sentinel; automatic compaction remains disabled until a positive context is configured.",
@@ -6333,7 +6369,7 @@ var NPM_LATEST_URL = `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/latest`;
6333
6369
  var FETCH_TIMEOUT_MS = 5000;
6334
6370
  var MAX_RESPONSE_BYTES = 64 * 1024;
6335
6371
  var MAX_FETCH_ATTEMPTS = 2;
6336
- var PLUGIN_KEY = "plugin";
6372
+ var PLUGIN_KEYS = ["plugin", "plugins"];
6337
6373
  var PRIVATE_DIRECTORY_MODE = 448;
6338
6374
  var PRIVATE_FILE_MODE = 384;
6339
6375
  var UPGRADE_BACKUP_GITIGNORE_CONTENT = `*
@@ -6387,6 +6423,8 @@ function extractSpecString(entry) {
6387
6423
  if (isTuplePluginEntry(entry)) {
6388
6424
  return entry[0];
6389
6425
  }
6426
+ if (isRecord3(entry) && typeof entry.package === "string")
6427
+ return entry.package;
6390
6428
  return;
6391
6429
  }
6392
6430
  function isLocalFileSpec(spec) {
@@ -6599,7 +6637,7 @@ function readJsoncValueEnd(src, start, limit) {
6599
6637
  }
6600
6638
  return i > start ? i : undefined;
6601
6639
  }
6602
- function findTopLevelPluginArray(raw) {
6640
+ function findTopLevelPluginArray(raw, pluginKey) {
6603
6641
  let found;
6604
6642
  let i = 0;
6605
6643
  let depth = 0;
@@ -6646,7 +6684,7 @@ function findTopLevelPluginArray(raw) {
6646
6684
  return;
6647
6685
  }
6648
6686
  i = token.end;
6649
- if (key !== PLUGIN_KEY || depth !== 1) {
6687
+ if (key !== pluginKey || depth !== 1) {
6650
6688
  continue;
6651
6689
  }
6652
6690
  i = skipTrivia(raw, i);
@@ -6712,6 +6750,33 @@ function findEntrySpecStringSpan(raw, element, entry, expectedSpec) {
6712
6750
  const span2 = { start: element.start, end: token2.end };
6713
6751
  return readJsonStringValue(raw, span2) === expectedSpec ? span2 : undefined;
6714
6752
  }
6753
+ if (isRecord3(entry) && typeof entry.package === "string") {
6754
+ let found;
6755
+ let i = skipTrivia(raw, element.start + 1, element.end);
6756
+ while (i < element.end && raw[i] !== "}") {
6757
+ const key = readStringToken(raw, i, element.end);
6758
+ if (key === undefined)
6759
+ return;
6760
+ const name = readJsonStringValue(raw, { start: i, end: key.end });
6761
+ i = skipTrivia(raw, key.end, element.end);
6762
+ if (raw[i] !== ":")
6763
+ return;
6764
+ i = skipTrivia(raw, i + 1, element.end);
6765
+ const end = readJsoncValueEnd(raw, i, element.end);
6766
+ if (end === undefined)
6767
+ return;
6768
+ if (name === "package") {
6769
+ const span2 = { start: i, end };
6770
+ found = readJsonStringValue(raw, span2) === expectedSpec ? span2 : undefined;
6771
+ }
6772
+ i = skipTrivia(raw, end, element.end);
6773
+ if (raw[i] === ",")
6774
+ i = skipTrivia(raw, i + 1, element.end);
6775
+ else if (raw[i] !== "}")
6776
+ return;
6777
+ }
6778
+ return found;
6779
+ }
6715
6780
  if (!isTuplePluginEntry(entry)) {
6716
6781
  return;
6717
6782
  }
@@ -6739,20 +6804,23 @@ function validateEffectivePluginUpdate(path, nextContent, expectedManagedEntries
6739
6804
  if (!parsed.ok) {
6740
6805
  return parsed.error;
6741
6806
  }
6742
- const pluginValue = parsed.value[PLUGIN_KEY];
6743
- if (!Array.isArray(pluginValue)) {
6744
- return upgradeMessages.effectivePluginUpdateMismatch(path);
6745
- }
6746
6807
  let managedEntries = 0;
6747
6808
  let updatedEntries = 0;
6748
- for (const entry of pluginValue) {
6749
- const spec = extractSpecString(entry);
6750
- if (spec === undefined || !isOurSpec(spec)) {
6809
+ for (const key of PLUGIN_KEYS) {
6810
+ const pluginValue = parsed.value[key];
6811
+ if (pluginValue === undefined)
6751
6812
  continue;
6752
- }
6753
- managedEntries += 1;
6754
- if (spec === nextSpec) {
6755
- updatedEntries += 1;
6813
+ if (!Array.isArray(pluginValue))
6814
+ return upgradeMessages.effectivePluginUpdateMismatch(path);
6815
+ for (const entry of pluginValue) {
6816
+ const spec = extractSpecString(entry);
6817
+ if (spec === undefined || !isOurSpec(spec)) {
6818
+ continue;
6819
+ }
6820
+ managedEntries += 1;
6821
+ if (spec === nextSpec) {
6822
+ updatedEntries += 1;
6823
+ }
6756
6824
  }
6757
6825
  }
6758
6826
  return managedEntries === expectedManagedEntries && updatedEntries === expectedManagedEntries ? undefined : upgradeMessages.effectivePluginUpdateMismatch(path);
@@ -7013,70 +7081,52 @@ async function analyseConfigFiles(paths, port) {
7013
7081
  parseError: parsed.error
7014
7082
  };
7015
7083
  }
7016
- const pluginValue = parsed.value[PLUGIN_KEY];
7017
- if (pluginValue === undefined) {
7018
- continue;
7019
- }
7020
- if (!Array.isArray(pluginValue)) {
7021
- return {
7022
- files,
7023
- foundConfigFile,
7024
- localOpenteamEntries,
7025
- parseError: upgradeMessages.pluginFieldMustBeArray(path)
7026
- };
7027
- }
7028
- const managedCandidates = [];
7029
- for (let index = 0;index < pluginValue.length; index += 1) {
7030
- const entry = pluginValue[index];
7031
- const spec = extractSpecString(entry);
7032
- if (spec === undefined) {
7084
+ const fileReplacements = [];
7085
+ for (const pluginKey of PLUGIN_KEYS) {
7086
+ const pluginValue = parsed.value[pluginKey];
7087
+ if (pluginValue === undefined) {
7033
7088
  continue;
7034
7089
  }
7035
- if (isLocalFileSpec(spec)) {
7036
- const localKind = await port.classifyLocalPluginSpec({
7037
- configPath: path,
7090
+ if (!Array.isArray(pluginValue)) {
7091
+ return {
7092
+ files,
7093
+ foundConfigFile,
7094
+ localOpenteamEntries,
7095
+ parseError: upgradeMessages.pluginFieldMustBeArray(path)
7096
+ };
7097
+ }
7098
+ const managedCandidates = [];
7099
+ for (let index = 0;index < pluginValue.length; index += 1) {
7100
+ const entry = pluginValue[index];
7101
+ const spec = extractSpecString(entry);
7102
+ if (spec === undefined) {
7103
+ continue;
7104
+ }
7105
+ if (isLocalFileSpec(spec)) {
7106
+ const localKind = await port.classifyLocalPluginSpec({
7107
+ configPath: path,
7108
+ spec
7109
+ });
7110
+ if (localKind === "ours") {
7111
+ localOpenteamEntries.push({ path, spec });
7112
+ }
7113
+ continue;
7114
+ }
7115
+ if (!isOurSpec(spec)) {
7116
+ continue;
7117
+ }
7118
+ managedCandidates.push({
7119
+ currentVersion: currentPinnedVersion(spec),
7120
+ entry,
7121
+ index,
7038
7122
  spec
7039
7123
  });
7040
- if (localKind === "ours") {
7041
- localOpenteamEntries.push({ path, spec });
7042
- }
7043
- continue;
7044
7124
  }
7045
- if (!isOurSpec(spec)) {
7125
+ if (managedCandidates.length === 0) {
7046
7126
  continue;
7047
7127
  }
7048
- managedCandidates.push({
7049
- currentVersion: currentPinnedVersion(spec),
7050
- entry,
7051
- index,
7052
- spec
7053
- });
7054
- }
7055
- if (managedCandidates.length === 0) {
7056
- continue;
7057
- }
7058
- const pluginArray = findTopLevelPluginArray(content);
7059
- if (pluginArray === undefined) {
7060
- return {
7061
- files,
7062
- foundConfigFile,
7063
- localOpenteamEntries,
7064
- parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7065
- };
7066
- }
7067
- const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
7068
- if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
7069
- return {
7070
- files,
7071
- foundConfigFile,
7072
- localOpenteamEntries,
7073
- parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7074
- };
7075
- }
7076
- const replacements = [];
7077
- for (const candidate of managedCandidates) {
7078
- const elementSpan = elementSpans[candidate.index];
7079
- if (elementSpan === undefined) {
7128
+ const pluginArray = findTopLevelPluginArray(content, pluginKey);
7129
+ if (pluginArray === undefined) {
7080
7130
  return {
7081
7131
  files,
7082
7132
  foundConfigFile,
@@ -7084,8 +7134,8 @@ async function analyseConfigFiles(paths, port) {
7084
7134
  parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7085
7135
  };
7086
7136
  }
7087
- const specSpan = findEntrySpecStringSpan(content, elementSpan, candidate.entry, candidate.spec);
7088
- if (specSpan === undefined) {
7137
+ const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
7138
+ if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
7089
7139
  return {
7090
7140
  files,
7091
7141
  foundConfigFile,
@@ -7093,16 +7143,40 @@ async function analyseConfigFiles(paths, port) {
7093
7143
  parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7094
7144
  };
7095
7145
  }
7096
- replacements.push({
7097
- ...specSpan,
7098
- currentVersion: candidate.currentVersion
7099
- });
7146
+ const replacements = [];
7147
+ for (const candidate of managedCandidates) {
7148
+ const elementSpan = elementSpans[candidate.index];
7149
+ if (elementSpan === undefined) {
7150
+ return {
7151
+ files,
7152
+ foundConfigFile,
7153
+ localOpenteamEntries,
7154
+ parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7155
+ };
7156
+ }
7157
+ const specSpan = findEntrySpecStringSpan(content, elementSpan, candidate.entry, candidate.spec);
7158
+ if (specSpan === undefined) {
7159
+ return {
7160
+ files,
7161
+ foundConfigFile,
7162
+ localOpenteamEntries,
7163
+ parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7164
+ };
7165
+ }
7166
+ replacements.push({
7167
+ ...specSpan,
7168
+ currentVersion: candidate.currentVersion
7169
+ });
7170
+ }
7171
+ fileReplacements.push(...replacements);
7100
7172
  }
7173
+ if (fileReplacements.length === 0)
7174
+ continue;
7101
7175
  files.push({
7102
- currentVersions: replacements.map((replacement) => replacement.currentVersion),
7176
+ currentVersions: fileReplacements.map((replacement) => replacement.currentVersion),
7103
7177
  path,
7104
7178
  originalContent: content,
7105
- replacements
7179
+ replacements: fileReplacements
7106
7180
  });
7107
7181
  }
7108
7182
  return {
@@ -7830,15 +7904,14 @@ function mergeOpencodeConfigs(base, override) {
7830
7904
  return result;
7831
7905
  }
7832
7906
 
7833
- // src/graph/certificate.ts
7834
- import { z as z17 } from "zod";
7835
-
7836
7907
  // src/contract/opencode.ts
7837
7908
  var SUPPORTED_OPENCODE_VERSIONS = [
7838
7909
  "1.17.13",
7839
7910
  "1.18.18",
7840
7911
  "1.18.19"
7841
7912
  ];
7913
+ var NATIVE_OPENCODE_VERSION = "2.0.5";
7914
+ var NATIVE_CERTIFICATE_DIRECTORY = `opencode-${NATIVE_OPENCODE_VERSION}`;
7842
7915
  var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
7843
7916
  function parseSemver(version) {
7844
7917
  const normalized = version.trim().replace(/^[=^~v]+/, "");
@@ -7864,12 +7937,15 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
7864
7937
  } catch {
7865
7938
  return false;
7866
7939
  }
7867
- if (certified.major !== live.major || certified.minor !== live.minor) {
7940
+ if (certified.major !== 1 || certified.major !== live.major || certified.minor !== live.minor) {
7868
7941
  return false;
7869
7942
  }
7870
7943
  return live.patch >= certified.patch;
7871
7944
  }
7872
7945
 
7946
+ // src/graph/certificate.ts
7947
+ import { z as z17 } from "zod";
7948
+
7873
7949
  // src/graph/soakLedger.ts
7874
7950
  import { z as z16 } from "zod";
7875
7951
  var Sha256Schema2 = z16.string().regex(/^[0-9a-f]{64}$/);
@@ -9953,14 +10029,12 @@ function ensureFoundryV1BaseURL(endpoint) {
9953
10029
  }
9954
10030
  }
9955
10031
 
9956
- // src/local/lemonadeResidency.ts
9957
- import { z as z19 } from "zod";
9958
-
9959
10032
  // src/messages/modelResidency.ts
9960
10033
  var reasons = {
9961
10034
  unknown: "fresh residency metadata is unavailable, malformed, or ambiguous",
9962
10035
  inactive: "the requested model is not verified as resident and serving",
9963
10036
  identity: "the requested model identity can be rewritten or is ambiguous",
10037
+ "authentication-unsupported": "authentication is required but unsupported by this integration",
9964
10038
  destination: "the request destination does not match its configured runtime",
9965
10039
  transport: "this provider execution path cannot enforce the residency guard",
9966
10040
  request: "this is not a supported, explicit model inference request",
@@ -9969,22 +10043,328 @@ var reasons = {
9969
10043
  configuration: "the residency guard configuration is invalid"
9970
10044
  };
9971
10045
  var modelResidencyMessages = {
9972
- blocked: (reason) => `[openteam] Lemonade model request blocked: ${reasons[reason]}.`,
9973
- providerDisabled: (providerID) => `[openteam] Lemonade provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
9974
- extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider."
10046
+ blocked: (reason, runtimeLabel = "Lemonade", detail) => `[openteam] ${runtimeLabel} model request blocked: ${reasons[reason]}${detail === undefined ? "" : ` (${detail})`}.`,
10047
+ providerDisabled: (providerID, runtimeLabel = "Lemonade") => `[openteam] ${runtimeLabel} provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
10048
+ extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider.",
10049
+ extractionRuntimeUnavailableFor: (runtimeLabel) => `[openteam] Memory extraction requires one enabled ${runtimeLabel} local runtime matching the configured extraction provider.`
10050
+ };
10051
+ var llamaSwapResidencyMessages = {
10052
+ catalogModelDetail: (id, detail) => `catalog model "${id}" ${detail}`,
10053
+ invalidCatalogField: (field) => `declares an invalid ${field}`,
10054
+ catalogEntryMustExposeStringId: "catalog entries must expose a string id",
10055
+ catalogModelIdsMustBeNonEmptyTrimmedStrings: "catalog model ids must be non-empty, trimmed strings",
10056
+ expectedDataArray: (path2) => `GET ${path2} must return an object with a data array`,
10057
+ duplicateCanonicalModelId: (modelID, path2) => `duplicate canonical model id "${modelID}" in ${path2}`,
10058
+ httpFailure: (path2, status) => `GET ${path2} failed with HTTP ${status}`,
10059
+ unverifiableResponse: (path2) => `GET ${path2} failed before a verifiable response`,
10060
+ expectedProfilesArray: (path2) => `GET ${path2} must return an object with a profiles array`,
10061
+ activeProfileRewrite: (profile) => `active profile "${profile}" can rewrite canonical model routing`,
10062
+ invalidActiveProfileValue: (path2) => `GET ${path2} reported an invalid active profile value`,
10063
+ expectedRunningArray: (path2) => `GET ${path2} must return an object with a running array`,
10064
+ malformedRunningEvidence: (path2) => `GET ${path2} returned malformed model/state evidence`,
10065
+ duplicateRunningEvidence: (path2, modelID) => `GET ${path2} returned duplicate evidence for "${modelID}"`
9975
10066
  };
9976
10067
 
9977
- // src/local/lemonadeResidency.ts
10068
+ // src/local/residency.ts
9978
10069
  class ModelResidencyError extends Error {
9979
10070
  code;
9980
10071
  statusCode = 400;
9981
10072
  isRetryable = false;
9982
- constructor(code) {
9983
- super(modelResidencyMessages.blocked(code));
10073
+ constructor(code, runtimeLabel = "Lemonade", detail) {
10074
+ super(modelResidencyMessages.blocked(code, runtimeLabel, detail));
9984
10075
  this.code = code;
9985
10076
  this.name = "ModelResidencyError";
9986
10077
  }
9987
10078
  }
10079
+ async function withResidencyDeadline(operation, options) {
10080
+ const timeoutMs = options.timeoutMs ?? 5000;
10081
+ const runtimeLabel = options.runtimeLabel;
10082
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
10083
+ throw new ModelResidencyError("configuration", runtimeLabel);
10084
+ }
10085
+ const controller = new AbortController;
10086
+ const cancel = () => controller.abort(new ModelResidencyError("cancelled", runtimeLabel));
10087
+ if (options.signal?.aborted)
10088
+ throw new ModelResidencyError("cancelled", runtimeLabel);
10089
+ options.signal?.addEventListener("abort", cancel, { once: true });
10090
+ const schedule = options.scheduleTimeout ?? ((callback, ms) => {
10091
+ const timer = setTimeout(callback, ms);
10092
+ return () => clearTimeout(timer);
10093
+ });
10094
+ let onAbort = () => {};
10095
+ const aborted = new Promise((_, reject) => {
10096
+ onAbort = () => reject(controller.signal.reason);
10097
+ controller.signal.addEventListener("abort", onAbort, { once: true });
10098
+ });
10099
+ const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout", runtimeLabel)), timeoutMs);
10100
+ try {
10101
+ controller.signal.throwIfAborted();
10102
+ return await Promise.race([operation(controller.signal), aborted]);
10103
+ } finally {
10104
+ clearTimer();
10105
+ options.signal?.removeEventListener("abort", cancel);
10106
+ controller.signal.removeEventListener("abort", onAbort);
10107
+ controller.abort();
10108
+ }
10109
+ }
10110
+
10111
+ // src/local/llamaSwapResidency.ts
10112
+ function isObject2(value) {
10113
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10114
+ }
10115
+ function readPositiveSafeInteger(value) {
10116
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
10117
+ }
10118
+ function readSupportsTools2(model) {
10119
+ const direct = readBoolean2(model, [
10120
+ "tool_call",
10121
+ "tool_calls",
10122
+ "toolCalling",
10123
+ "supportsToolCalling",
10124
+ "supports_tools",
10125
+ "supportsTools"
10126
+ ]);
10127
+ if (direct !== undefined) {
10128
+ return direct;
10129
+ }
10130
+ const capabilities = readCapabilityBoolean2(model.capabilities);
10131
+ if (capabilities !== undefined) {
10132
+ return capabilities;
10133
+ }
10134
+ const features = readCapabilityBoolean2(model.features);
10135
+ if (features !== undefined) {
10136
+ return features;
10137
+ }
10138
+ const labels = readCapabilityBoolean2(model.labels);
10139
+ if (labels !== undefined) {
10140
+ return labels;
10141
+ }
10142
+ return "unknown";
10143
+ }
10144
+ function readCapabilityBoolean2(value) {
10145
+ if (Array.isArray(value)) {
10146
+ if (value.some((entry) => typeof entry === "string" && ["tools", "tool_call", "tool-calling", "function_calling"].includes(entry))) {
10147
+ return true;
10148
+ }
10149
+ return;
10150
+ }
10151
+ if (!isObject2(value)) {
10152
+ return;
10153
+ }
10154
+ return readBoolean2(value, [
10155
+ "tools",
10156
+ "tool_call",
10157
+ "tool_calls",
10158
+ "toolCalling",
10159
+ "function_calling",
10160
+ "supportsToolCalling"
10161
+ ]);
10162
+ }
10163
+ function readBoolean2(value, keys) {
10164
+ for (const key of keys) {
10165
+ if (typeof value[key] === "boolean") {
10166
+ return value[key];
10167
+ }
10168
+ }
10169
+ return;
10170
+ }
10171
+ function modelDetail(id, detail) {
10172
+ return llamaSwapResidencyMessages.catalogModelDetail(id, detail);
10173
+ }
10174
+ function llamaSwapBaseURL(value) {
10175
+ let url;
10176
+ try {
10177
+ url = new URL(value);
10178
+ } catch {
10179
+ throw new ModelResidencyError("destination", "llama-swap");
10180
+ }
10181
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") {
10182
+ throw new ModelResidencyError("destination", "llama-swap");
10183
+ }
10184
+ if (url.pathname === "/" || url.pathname === "") {
10185
+ return `${url.origin}/v1`;
10186
+ }
10187
+ if (url.pathname === "/v1" || url.pathname === "/v1/") {
10188
+ return `${url.origin}/v1`;
10189
+ }
10190
+ throw new ModelResidencyError("destination", "llama-swap");
10191
+ }
10192
+ function parseCanonicalType(entry, id) {
10193
+ if (!isObject2(entry.meta) || !isObject2(entry.meta.llamaswap)) {
10194
+ return;
10195
+ }
10196
+ const type = entry.meta.llamaswap.type;
10197
+ if (type === undefined) {
10198
+ return;
10199
+ }
10200
+ if (typeof type !== "string" || type.length === 0 || type !== type.trim()) {
10201
+ throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.type")));
10202
+ }
10203
+ return type === "model" ? "model" : undefined;
10204
+ }
10205
+ function readContextCandidates(entry) {
10206
+ const values = [
10207
+ readPositiveSafeInteger(entry.context_length),
10208
+ readPositiveSafeInteger(entry.context_window),
10209
+ isObject2(entry.meta) ? readPositiveSafeInteger(entry.meta.n_ctx) : undefined
10210
+ ];
10211
+ return values.filter((value) => value !== undefined);
10212
+ }
10213
+ function readDeclaredMaxOutput(entry, id) {
10214
+ if (!isObject2(entry.meta) || !isObject2(entry.meta.llamaswap)) {
10215
+ return;
10216
+ }
10217
+ if (!Object.hasOwn(entry.meta.llamaswap, "max_output_tokens")) {
10218
+ return;
10219
+ }
10220
+ const maxOutputTokens = readPositiveSafeInteger(entry.meta.llamaswap.max_output_tokens);
10221
+ if (maxOutputTokens === undefined) {
10222
+ throw new ModelResidencyError("unknown", "llama-swap", modelDetail(id, llamaSwapResidencyMessages.invalidCatalogField("meta.llamaswap.max_output_tokens")));
10223
+ }
10224
+ return maxOutputTokens;
10225
+ }
10226
+ function parseCatalogModel(value) {
10227
+ if (!isObject2(value) || typeof value.id !== "string") {
10228
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogEntryMustExposeStringId);
10229
+ }
10230
+ const id = value.id;
10231
+ if (id.length === 0 || id !== id.trim()) {
10232
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.catalogModelIdsMustBeNonEmptyTrimmedStrings);
10233
+ }
10234
+ if (parseCanonicalType(value, id) !== "model") {
10235
+ return;
10236
+ }
10237
+ const model = {
10238
+ modelID: id,
10239
+ supportsTools: readSupportsTools2(value)
10240
+ };
10241
+ const contexts = readContextCandidates(value);
10242
+ if (contexts.length > 0) {
10243
+ model.contextWindow = Math.min(...contexts);
10244
+ model.contextWindowProvenance = "declared";
10245
+ }
10246
+ const maxOutputTokens = readDeclaredMaxOutput(value, id);
10247
+ if (maxOutputTokens !== undefined) {
10248
+ model.maxOutputTokens = maxOutputTokens;
10249
+ model.maxOutputTokensProvenance = "declared";
10250
+ }
10251
+ return model;
10252
+ }
10253
+ function parseCatalog(payload) {
10254
+ if (!isObject2(payload) || !Array.isArray(payload.data)) {
10255
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedDataArray("/v1/models"));
10256
+ }
10257
+ const seen = new Set;
10258
+ const models = [];
10259
+ for (const entry of payload.data) {
10260
+ const model = parseCatalogModel(entry);
10261
+ if (model === undefined) {
10262
+ continue;
10263
+ }
10264
+ if (seen.has(model.modelID)) {
10265
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateCanonicalModelId(model.modelID, "/v1/models"));
10266
+ }
10267
+ seen.add(model.modelID);
10268
+ models.push(model);
10269
+ }
10270
+ return models;
10271
+ }
10272
+ async function metadata(url, fetch, signal, headers) {
10273
+ signal.throwIfAborted();
10274
+ try {
10275
+ const response = await fetch(url, {
10276
+ method: "GET",
10277
+ headers,
10278
+ signal,
10279
+ redirect: "error",
10280
+ cache: "no-store"
10281
+ });
10282
+ signal.throwIfAborted();
10283
+ if (response.status === 401 || response.status === 403) {
10284
+ throw new ModelResidencyError("authentication-unsupported", "llama-swap");
10285
+ }
10286
+ if (!response.ok || response.redirected || response.url !== "" && response.url !== url) {
10287
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.httpFailure(new URL(url).pathname, response.status));
10288
+ }
10289
+ return await response.json();
10290
+ } catch (error) {
10291
+ signal.throwIfAborted();
10292
+ if (error instanceof ModelResidencyError) {
10293
+ throw error;
10294
+ }
10295
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.unverifiableResponse(new URL(url).pathname));
10296
+ }
10297
+ }
10298
+ function readProfiles(payload) {
10299
+ if (!isObject2(payload) || !Array.isArray(payload.profiles)) {
10300
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedProfilesArray("/api/profiles"));
10301
+ }
10302
+ if (payload.active === null) {
10303
+ return;
10304
+ }
10305
+ if (typeof payload.active === "string" && payload.active.length > 0 && payload.active === payload.active.trim()) {
10306
+ throw new ModelResidencyError("identity", "llama-swap", llamaSwapResidencyMessages.activeProfileRewrite(payload.active));
10307
+ }
10308
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.invalidActiveProfileValue("/api/profiles"));
10309
+ }
10310
+ function readRunning(payload, modelIDs) {
10311
+ if (!isObject2(payload) || !Array.isArray(payload.running)) {
10312
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.expectedRunningArray("/running"));
10313
+ }
10314
+ const result = new Map;
10315
+ for (const modelID of modelIDs) {
10316
+ result.set(modelID, { loaded: false });
10317
+ }
10318
+ const seen = new Set;
10319
+ for (const entry of payload.running) {
10320
+ if (!isObject2(entry) || typeof entry.model !== "string" || entry.model.length === 0 || entry.model !== entry.model.trim() || typeof entry.state !== "string" || entry.state.length === 0 || entry.state !== entry.state.trim()) {
10321
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.malformedRunningEvidence("/running"));
10322
+ }
10323
+ if (seen.has(entry.model)) {
10324
+ throw new ModelResidencyError("unknown", "llama-swap", llamaSwapResidencyMessages.duplicateRunningEvidence("/running", entry.model));
10325
+ }
10326
+ seen.add(entry.model);
10327
+ if (!modelIDs.has(entry.model)) {
10328
+ continue;
10329
+ }
10330
+ result.set(entry.model, { loaded: entry.state === "ready" });
10331
+ }
10332
+ return result;
10333
+ }
10334
+ function requestHeaders(headers) {
10335
+ const result = new Headers(headers);
10336
+ result.delete("content-type");
10337
+ result.delete("content-length");
10338
+ result.set("accept", "application/json");
10339
+ result.set("cache-control", "no-cache, no-store");
10340
+ result.delete("if-none-match");
10341
+ result.delete("if-modified-since");
10342
+ return result;
10343
+ }
10344
+ function llamaSwapResidencyOptions(options) {
10345
+ return { ...options, runtimeLabel: "llama-swap" };
10346
+ }
10347
+ async function readLlamaSwapCatalog(baseURL, fetch, options = {}) {
10348
+ const base = llamaSwapBaseURL(baseURL);
10349
+ const headers = requestHeaders(options.headers);
10350
+ return withResidencyDeadline(async (signal) => parseCatalog(await metadata(`${base}/models`, fetch, signal, headers)), llamaSwapResidencyOptions(options));
10351
+ }
10352
+ async function readLlamaSwapResidency(baseURL, fetch, options = {}) {
10353
+ const base = llamaSwapBaseURL(baseURL);
10354
+ const origin = new URL(base).origin;
10355
+ const headers = requestHeaders(options.headers);
10356
+ return withResidencyDeadline(async (signal) => {
10357
+ const models = await readLlamaSwapCatalog(base, fetch, {
10358
+ ...llamaSwapResidencyOptions(options),
10359
+ signal
10360
+ });
10361
+ readProfiles(await metadata(`${origin}/api/profiles`, fetch, signal, headers));
10362
+ return readRunning(await metadata(`${origin}/running`, fetch, signal, headers), new Set(models.map((model) => model.modelID)));
10363
+ }, llamaSwapResidencyOptions(options));
10364
+ }
10365
+
10366
+ // src/local/lemonadeResidency.ts
10367
+ import { z as z19 } from "zod";
9988
10368
  var modelName = z19.string().min(1).refine((name) => name === name.trim());
9989
10369
  var LoadedModelSchema = z19.object({
9990
10370
  model_name: modelName,
@@ -10121,37 +10501,7 @@ function lemonadeBaseURL(value) {
10121
10501
  }
10122
10502
  return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
10123
10503
  }
10124
- async function withResidencyDeadline(operation, options) {
10125
- const timeoutMs = options.timeoutMs ?? 5000;
10126
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
10127
- throw new ModelResidencyError("configuration");
10128
- }
10129
- const controller = new AbortController;
10130
- const cancel = () => controller.abort(new ModelResidencyError("cancelled"));
10131
- if (options.signal?.aborted)
10132
- throw new ModelResidencyError("cancelled");
10133
- options.signal?.addEventListener("abort", cancel, { once: true });
10134
- const schedule = options.scheduleTimeout ?? ((callback, ms) => {
10135
- const timer = setTimeout(callback, ms);
10136
- return () => clearTimeout(timer);
10137
- });
10138
- let onAbort = () => {};
10139
- const aborted = new Promise((_, reject) => {
10140
- onAbort = () => reject(controller.signal.reason);
10141
- controller.signal.addEventListener("abort", onAbort, { once: true });
10142
- });
10143
- const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout")), timeoutMs);
10144
- try {
10145
- controller.signal.throwIfAborted();
10146
- return await Promise.race([operation(controller.signal), aborted]);
10147
- } finally {
10148
- clearTimer();
10149
- options.signal?.removeEventListener("abort", cancel);
10150
- controller.signal.removeEventListener("abort", onAbort);
10151
- controller.abort();
10152
- }
10153
- }
10154
- async function metadata(url, fetch, signal, headers) {
10504
+ async function metadata2(url, fetch, signal, headers) {
10155
10505
  signal.throwIfAborted();
10156
10506
  let payload;
10157
10507
  try {
@@ -10184,8 +10534,8 @@ async function readLemonadeResidency(baseURL, fetch, options = {}) {
10184
10534
  headers.delete("if-none-match");
10185
10535
  headers.delete("if-modified-since");
10186
10536
  return withResidencyDeadline(async (signal) => {
10187
- const aliases = await metadata(`${new URL(base).origin}/internal/aliases`, fetch, signal, headers);
10188
- const health = await metadata(`${base}/health`, fetch, signal, headers);
10537
+ const aliases = await metadata2(`${new URL(base).origin}/internal/aliases`, fetch, signal, headers);
10538
+ const health = await metadata2(`${base}/health`, fetch, signal, headers);
10189
10539
  return parseResidency(health, aliases);
10190
10540
  }, options);
10191
10541
  }
@@ -10256,11 +10606,11 @@ async function listLemonadeModels(baseURL, fetch) {
10256
10606
  }
10257
10607
  function readNonTextModelIDs(payload) {
10258
10608
  const ids = new Set;
10259
- if (!isObject2(payload) || !Array.isArray(payload.data)) {
10609
+ if (!isObject3(payload) || !Array.isArray(payload.data)) {
10260
10610
  return ids;
10261
10611
  }
10262
10612
  for (const entry of payload.data) {
10263
- if (!isObject2(entry) || typeof entry.id !== "string") {
10613
+ if (!isObject3(entry) || typeof entry.id !== "string") {
10264
10614
  continue;
10265
10615
  }
10266
10616
  if (isNonTextModel(entry)) {
@@ -10298,7 +10648,7 @@ function readLoadedContextWindow(entry) {
10298
10648
  }
10299
10649
  function slotsFromLlamacppArgs(entry) {
10300
10650
  const options = entry.recipe_options;
10301
- if (!isObject2(options) || typeof options.llamacpp_args !== "string") {
10651
+ if (!isObject3(options) || typeof options.llamacpp_args !== "string") {
10302
10652
  return;
10303
10653
  }
10304
10654
  const match = PARALLEL_ARG.exec(options.llamacpp_args);
@@ -10334,7 +10684,7 @@ function slotsFromLaunchCommand(entry) {
10334
10684
  }
10335
10685
  function contextWindowFromRecipeOptions(entry) {
10336
10686
  const options = entry.recipe_options;
10337
- if (!isObject2(options) || !hasLlamacppEvidence(entry)) {
10687
+ if (!isObject3(options) || !hasLlamacppEvidence(entry)) {
10338
10688
  return;
10339
10689
  }
10340
10690
  const direct = positiveIntegerValue(options.ctx_size);
@@ -10394,7 +10744,7 @@ function hasLlamacppEvidence(entry) {
10394
10744
  return true;
10395
10745
  }
10396
10746
  const options = entry.recipe_options;
10397
- if (isObject2(options) && typeof options.llamacpp_args === "string") {
10747
+ if (isObject3(options) && typeof options.llamacpp_args === "string") {
10398
10748
  return true;
10399
10749
  }
10400
10750
  return Array.isArray(entry.launch_command) && launchCommandLooksLikeLlamacpp(entry.launch_command);
@@ -10450,10 +10800,57 @@ function enrichWithLoadState(models, loaded) {
10450
10800
  };
10451
10801
  });
10452
10802
  }
10453
- function isObject2(value) {
10803
+ function isObject3(value) {
10454
10804
  return typeof value === "object" && value !== null && !Array.isArray(value);
10455
10805
  }
10456
10806
 
10807
+ // src/local/llamaSwap.ts
10808
+ var LLAMA_SWAP_DEFAULT_BASE_URL = "http://localhost:8080/v1";
10809
+ function withLoadedState(models, residency) {
10810
+ return models.map((model) => {
10811
+ const loaded = residency.get(model.modelID)?.loaded;
10812
+ return loaded === undefined ? model : { ...model, loaded };
10813
+ });
10814
+ }
10815
+ function createLlamaSwapAdapter() {
10816
+ const listModels = async (options) => {
10817
+ const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
10818
+ const models = await readLlamaSwapCatalog(baseURL, options.fetch);
10819
+ try {
10820
+ return withLoadedState(models, await readLlamaSwapResidency(baseURL, options.fetch));
10821
+ } catch (error) {
10822
+ if (error instanceof ModelResidencyError && (error.code === "identity" || error.code === "unknown")) {
10823
+ return models;
10824
+ }
10825
+ throw error;
10826
+ }
10827
+ };
10828
+ return {
10829
+ id: "llama-swap",
10830
+ defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
10831
+ listModels,
10832
+ async probe(options) {
10833
+ const baseURL = llamaSwapBaseURL(options.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL);
10834
+ try {
10835
+ return {
10836
+ id: "llama-swap",
10837
+ baseURL,
10838
+ reachable: true,
10839
+ models: await listModels({ ...options, baseURL }),
10840
+ probedAt: options.probedAt
10841
+ };
10842
+ } catch (error) {
10843
+ return unavailableSnapshot({
10844
+ id: "llama-swap",
10845
+ baseURL,
10846
+ probedAt: options.probedAt,
10847
+ error
10848
+ });
10849
+ }
10850
+ }
10851
+ };
10852
+ }
10853
+
10457
10854
  // src/local/lmstudio.ts
10458
10855
  var LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
10459
10856
  function createLMStudioAdapter() {
@@ -10506,6 +10903,7 @@ class RuntimeRegistry {
10506
10903
  lmstudio: createLMStudioAdapter(),
10507
10904
  "foundry-local": createFoundryLocalAdapter(),
10508
10905
  lemonade: createLemonadeAdapter(),
10906
+ "llama-swap": createLlamaSwapAdapter(),
10509
10907
  ...options.adapters
10510
10908
  };
10511
10909
  this.clock = options.clock;
@@ -10635,6 +11033,28 @@ function withReportedModelSlots(runtime, reported, aliases) {
10635
11033
  };
10636
11034
  }
10637
11035
 
11036
+ // src/messages/nativeHost.ts
11037
+ var nativeHostMessages = {
11038
+ invalidNativeConfig: "The existing native opencode plugins/providers configuration cannot be safely merged. Correct its object and plugin declaration shapes before running setup.",
11039
+ setupPolicyQuestion: (paths, providers) => `Remove exact project-local provider.use deny rules for ${providers.join(", ")} from ${paths.join(", ")}? Older setup added these rules, but they may also be operator limits. Confirm only if you intend to enable the guarded native transport.`,
11040
+ setupPolicyPreserved: "Setup stopped. Provider-denial rules were preserved and no files were written.",
11041
+ setupPolicyNotice: "Only the explicitly reviewed project rules were removed. Wildcard, scoped, unrelated and global provider denials remain authoritative.",
11042
+ unsupportedVersion: (version) => `openteam has not verified the native plugin contract for opencode ${version}. The supported native host is 2.0.5.`,
11043
+ missingBindings: "The native opencode execution context is unavailable. No child session or privileged operation was started.",
11044
+ missingVersion: "The opencode host did not provide an authoritative version or server endpoint.",
11045
+ workspaceMismatch: "The native opencode session does not belong to this workspace.",
11046
+ noCompletedResponse: "The native opencode session stopped without a completed assistant response.",
11047
+ executionFailed: "The native opencode session failed.",
11048
+ executionInterrupted: "The native opencode session was interrupted.",
11049
+ unknownAgent: "The native opencode session has no available agent.",
11050
+ noAvailableModel: "The native opencode host has no model available in the configured execution domain.",
11051
+ executionDomainBlocked: "The native opencode request is outside the configured execution domain.",
11052
+ transportUnavailable: "This local runtime requires the guarded @ai-sdk/openai-compatible transport. Native and WebSocket transports are not admitted.",
11053
+ missingAdmission: "The native local request has no matching model authorization. No inference was sent.",
11054
+ toolFailed: "The openteam tool failed in the native opencode host.",
11055
+ eventCaptureFailed: "Native opencode event capture failed. The console may be missing activity; reload the plugin after resolving the host error."
11056
+ };
11057
+
10638
11058
  // src/messages/orchestration.ts
10639
11059
  var HOST_SESSION_REQUIRED = "Orchestration requires a nonempty host ToolContext sessionID.";
10640
11060
  var PARENT_OVERRIDE_IGNORED = "parentSessionID override ignored: the trusted host ToolContext session owns this orchestration and its children. Programmatic callers must supply their parent as the ToolContext sessionID.";
@@ -10972,7 +11392,7 @@ var TRUNCATED_REASON_SUFFIX = "…[truncated]";
10972
11392
  var MAX_SERIALIZED_ARRAY_ITEMS = 20;
10973
11393
  var MAX_SERIALIZED_OBJECT_PROPERTIES = 25;
10974
11394
  var MAX_SERIALIZED_DEPTH = 4;
10975
- function isObject3(value) {
11395
+ function isObject4(value) {
10976
11396
  return typeof value === "object" && value !== null;
10977
11397
  }
10978
11398
  function isError(value) {
@@ -11002,7 +11422,7 @@ function stringProperty(value, key) {
11002
11422
  }
11003
11423
  function nestedMessage(value, key) {
11004
11424
  const container = safeGet(value, key);
11005
- if (!isObject3(container)) {
11425
+ if (!isObject4(container)) {
11006
11426
  return;
11007
11427
  }
11008
11428
  const message = stringProperty(container, "message");
@@ -11086,7 +11506,7 @@ function errorReason(error) {
11086
11506
  return message;
11087
11507
  }
11088
11508
  }
11089
- if (isObject3(error)) {
11509
+ if (isObject4(error)) {
11090
11510
  const message = stringProperty(error, "message");
11091
11511
  if (message !== undefined) {
11092
11512
  return message;
@@ -11104,7 +11524,7 @@ function errorReason(error) {
11104
11524
  return safeToString(error);
11105
11525
  }
11106
11526
  function isNormalizedSessionFailure(value) {
11107
- return isObject3(value) && safeGet(value, "kind") === "normalized-session-failure" && typeof safeGet(value, "failureClass") === "string" && typeof safeGet(value, "retryable") === "boolean";
11527
+ return isObject4(value) && safeGet(value, "kind") === "normalized-session-failure" && typeof safeGet(value, "failureClass") === "string" && typeof safeGet(value, "retryable") === "boolean";
11108
11528
  }
11109
11529
  function createArgs(req) {
11110
11530
  const body = {};
@@ -11231,7 +11651,7 @@ async function confirmSessionStopped(client, sessionID, directory, deps, deadlin
11231
11651
  return { confirmed: false, cause: "status-error" };
11232
11652
  }
11233
11653
  const statuses = statusOutcome.value;
11234
- if (!isObject3(statuses)) {
11654
+ if (!isObject4(statuses)) {
11235
11655
  return { confirmed: false, cause: "status-error" };
11236
11656
  }
11237
11657
  if (safeGet(statuses, "error") !== undefined) {
@@ -11381,8 +11801,11 @@ async function runSubsession(client, req, deps) {
11381
11801
  }
11382
11802
 
11383
11803
  // src/local/modelResidency.ts
11804
+ function runtimeRequiresModelResidency(kind) {
11805
+ return kind === "lemonade" || kind === "llama-swap";
11806
+ }
11384
11807
  function hasRequiredModelResidency(kind, model) {
11385
- return kind !== "lemonade" || model?.loaded === true;
11808
+ return !runtimeRequiresModelResidency(kind) || model?.loaded === true;
11386
11809
  }
11387
11810
 
11388
11811
  // src/plugin/availability.ts
@@ -11391,9 +11814,9 @@ function modelKey2(model) {
11391
11814
  }
11392
11815
  function availabilityMetadata({
11393
11816
  apiModelID: _apiModelID,
11394
- ...metadata2
11817
+ ...metadata3
11395
11818
  }) {
11396
- return metadata2;
11819
+ return metadata3;
11397
11820
  }
11398
11821
  function compareAvailableModels(left, right) {
11399
11822
  const kindOrder = Number(left.kind === "frontier") - Number(right.kind === "frontier");
@@ -11413,7 +11836,7 @@ function isConfiguredLocalModel(config, model) {
11413
11836
  return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID);
11414
11837
  }
11415
11838
  function requiresModelResidency(config, model) {
11416
- return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID && !hasRequiredModelResidency(localRuntimeKind(runtime), undefined));
11839
+ return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === model.providerID && runtimeRequiresModelResidency(localRuntimeKind(runtime)));
11417
11840
  }
11418
11841
  function configuredFrontierProviderIDs(config) {
11419
11842
  const providerIDs = new Set([config.baseline.hardDefault.providerID]);
@@ -11612,6 +12035,10 @@ function createAvailabilityCache(options) {
11612
12035
  ref: { ...profile.ref },
11613
12036
  costPer1M: { ...profile.costPer1M }
11614
12037
  })),
12038
+ waitForRefresh: async () => {
12039
+ while (inFlight !== undefined)
12040
+ await inFlight;
12041
+ },
11615
12042
  refresh: () => {
11616
12043
  if (inFlight === undefined) {
11617
12044
  cached = cached.map((model) => requiresModelResidency(options.config, model) ? { ...model, available: false } : model);
@@ -12039,6 +12466,7 @@ var PROVIDER_LABELS = {
12039
12466
  ollama: "Ollama",
12040
12467
  lmstudio: "LM Studio",
12041
12468
  lemonade: "Lemonade Server",
12469
+ "llama-swap": executionSetupMessages.llamaSwap.label,
12042
12470
  "foundry-local": "Foundry Local"
12043
12471
  };
12044
12472
  function providerLabel(providerID) {
@@ -12260,7 +12688,8 @@ var KNOWN_LOCAL_PROVIDER_IDS = new Set([
12260
12688
  "ollama",
12261
12689
  "lmstudio",
12262
12690
  "foundry-local",
12263
- "lemonade"
12691
+ "lemonade",
12692
+ "llama-swap"
12264
12693
  ]);
12265
12694
  var KNOWN_FRONTIER_PROVIDER_IDS = new Set([
12266
12695
  "github-copilot",
@@ -12606,7 +13035,7 @@ function runtimeLine(snapshot, runtime) {
12606
13035
  const mark = snapshot.reachable ? "✓" : "✗";
12607
13036
  const detail = snapshot.reachable ? `${snapshot.models.length} model(s)` : snapshot.error ?? "unreachable";
12608
13037
  const declared = runtime?.maxConcurrency;
12609
- const slots = declared === undefined ? ` · ${DEFAULT_LOCAL_MAX_CONCURRENCY} slot(s) (default)` : ` · ${declared} slot(s) (declared)`;
13038
+ const slots = declared === undefined ? doctorMessages.defaultRuntimeSlots(effectiveMaxConcurrency(runtime ?? {})) : ` · ${declared} slot(s) (declared)`;
12610
13039
  return ` ${mark} ${snapshot.id.padEnd(14)} ${snapshot.baseURL || "(no baseURL)"} — ${detail}${slots}`;
12611
13040
  }
12612
13041
  function runtimeModelSlotsLine(runtime, snapshot) {
@@ -12618,6 +13047,8 @@ function runtimeModelSlotsLine(runtime, snapshot) {
12618
13047
  if (runtime === undefined || snapshot === undefined || !snapshot.reachable || snapshot.models.length === 0) {
12619
13048
  return [];
12620
13049
  }
13050
+ if (localRuntimeKind(runtime) === "llama-swap")
13051
+ return [doctorMessages.llamaSwapConcurrency];
12621
13052
  const reported = snapshot.models.flatMap((model) => model.slots === undefined ? [] : [`${model.modelID} ${model.slots} slot(s)`]);
12622
13053
  if (reported.length > 0) {
12623
13054
  return [
@@ -13021,7 +13452,7 @@ function renderDoctor(input) {
13021
13452
  continue;
13022
13453
  }
13023
13454
  const runtime = enabledRuntimes.find((r) => r.id === snapshot.id);
13024
- const cap = runtime?.maxConcurrency ?? DEFAULT_LOCAL_MAX_CONCURRENCY;
13455
+ const cap = effectiveMaxConcurrency(runtime ?? {});
13025
13456
  lines.push(` ⚠ ${snapshot.id} is reached over the network: its ${cap}-slot cap is enforced per openteam process, so`, " two processes at once (an opencode session plus 'openteam console') can together exceed it.");
13026
13457
  }
13027
13458
  lines.push(` telemetry: ${input.telemetryPath} — ${input.telemetryRecords} record(s)`);
@@ -13442,6 +13873,16 @@ function renderMigration(report) {
13442
13873
  // src/commands/purge.ts
13443
13874
  import * as path3 from "node:path";
13444
13875
 
13876
+ // src/config/fieldProjection.ts
13877
+ function only(keys) {
13878
+ return (value) => {
13879
+ if (typeof value !== "object" || value === null || Array.isArray(value))
13880
+ return value;
13881
+ const record = value;
13882
+ return Object.fromEntries(keys.filter((key) => Object.hasOwn(record, key)).map((key) => [key, record[key]]));
13883
+ };
13884
+ }
13885
+
13445
13886
  // src/storage/graph/soakRecorder.ts
13446
13887
  var SOAK_EVIDENCE_DIR = ".opencode/soak";
13447
13888
  var SOAK_RECORDER_BASE_DIR = ".opencode";
@@ -13633,6 +14074,38 @@ function missingGeneratedStateRules(gitignore) {
13633
14074
  const present = new Set(activeGitignoreRules(gitignore));
13634
14075
  return OPENCODE_GITIGNORE_GENERATED_STATE_RULES.filter((rule) => !present.has(rule));
13635
14076
  }
14077
+ var KNOWN_RUNTIMES = [
14078
+ {
14079
+ id: "ollama",
14080
+ label: "Ollama",
14081
+ defaultBaseURL: "http://localhost:11434/v1",
14082
+ fallbackModelID: "qwen3:8b"
14083
+ },
14084
+ {
14085
+ id: "lmstudio",
14086
+ label: "LM Studio",
14087
+ defaultBaseURL: "http://localhost:1234/v1",
14088
+ fallbackModelID: "qwen2.5-coder"
14089
+ },
14090
+ {
14091
+ id: "lemonade",
14092
+ label: "Lemonade Server",
14093
+ defaultBaseURL: LEMONADE_DEFAULT_BASE_URL,
14094
+ fallbackModelID: "Qwen3-Coder-30B-A3B-Instruct-GGUF"
14095
+ },
14096
+ {
14097
+ id: "llama-swap",
14098
+ label: executionSetupMessages.llamaSwap.label,
14099
+ defaultBaseURL: LLAMA_SWAP_DEFAULT_BASE_URL,
14100
+ fallbackModelID: ""
14101
+ },
14102
+ {
14103
+ id: "foundry-local",
14104
+ label: "Foundry Local",
14105
+ fallbackModelID: "Phi-4-mini-instruct",
14106
+ dynamicPort: true
14107
+ }
14108
+ ];
13636
14109
 
13637
14110
  // src/commands/purge.ts
13638
14111
  var PURGE_CATEGORY_IDS = [
@@ -14661,7 +15134,7 @@ function liveModelsFromSnapshots(snapshots, config) {
14661
15134
  map.set(runtime.defaultModel.providerID, {
14662
15135
  reachable: snapshot.reachable,
14663
15136
  modelIDs: new Set(snapshot.models.filter((model) => hasRequiredModelResidency(kind, model)).map((model) => model.modelID)),
14664
- ...kind === "lemonade" ? {
15137
+ ...runtimeRequiresModelResidency(kind) ? {
14665
15138
  residency: new Map(snapshot.models.map((model) => [model.modelID, model.loaded]))
14666
15139
  } : {}
14667
15140
  });
@@ -14991,9 +15464,9 @@ async function runCli(argv, deps) {
14991
15464
  const inventoryNotes = [];
14992
15465
  const readySnapshots = snapshots.map((snapshot) => {
14993
15466
  const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
14994
- if (runtime === undefined || localRuntimeKind(runtime) !== "lemonade")
15467
+ if (runtime === undefined || !runtimeRequiresModelResidency(localRuntimeKind(runtime)))
14995
15468
  return snapshot;
14996
- inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models));
15469
+ inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models, localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade"));
14997
15470
  return {
14998
15471
  ...snapshot,
14999
15472
  models: snapshot.models.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model))
@@ -15004,7 +15477,7 @@ async function runCli(argv, deps) {
15004
15477
  if (agent.model === undefined || live !== "not-loaded" && live !== "residency-unknown")
15005
15478
  return [];
15006
15479
  return [
15007
- executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live)
15480
+ executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live, config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === agent.model?.providerID && localRuntimeKind(runtime) === "llama-swap") ? "llama-swap" : "Lemonade")
15008
15481
  ];
15009
15482
  });
15010
15483
  return {
@@ -15204,7 +15677,7 @@ function createCommandTool(deps) {
15204
15677
  // src/plugin/consoleTool.ts
15205
15678
  import { spawn as nodeSpawn } from "node:child_process";
15206
15679
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
15207
- import { mkdir as mkdir4, open as open3 } from "node:fs/promises";
15680
+ import { mkdir as mkdir4, open as open3, stat as stat3 } from "node:fs/promises";
15208
15681
  import { dirname as dirname6, join as join13, resolve as resolve8 } from "node:path";
15209
15682
  import { fileURLToPath as fileURLToPath2 } from "node:url";
15210
15683
  import { tool as tool2 } from "@opencode-ai/plugin";
@@ -15214,8 +15687,8 @@ import {
15214
15687
  mkdir as mkdir3,
15215
15688
  open as open2,
15216
15689
  readFile as readFile4,
15217
- unlink,
15218
- writeFile as writeFile2
15690
+ rename as rename2,
15691
+ unlink
15219
15692
  } from "node:fs/promises";
15220
15693
  import { dirname as dirname5, join as join12, resolve as resolve7 } from "node:path";
15221
15694
  import { z as z22 } from "zod";
@@ -15515,10 +15988,17 @@ async function acquireLaunchLockOrWait(workspaceRoot, paths, launchId, deps) {
15515
15988
  currentLock = await readConsoleLaunchLock(paths.launchLockPath);
15516
15989
  } catch (error) {
15517
15990
  if (error instanceof ConsoleRuntimeStateFileError) {
15518
- await removeConsoleLaunchLock(paths.launchLockPath);
15519
- const retry = await acquireConsoleLaunchLock(paths.launchLockPath, attemptLock);
15520
- if (retry !== "busy") {
15521
- return { kind: "acquired", release: retry.release };
15991
+ const info = await stat3(paths.launchLockPath).catch((error2) => {
15992
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
15993
+ return;
15994
+ throw error2;
15995
+ });
15996
+ if (info === undefined || deps.now() - info.mtimeMs > LAUNCH_LOCK_STALE_MS) {
15997
+ await removeConsoleLaunchLock(paths.launchLockPath);
15998
+ const retry = await acquireConsoleLaunchLock(paths.launchLockPath, attemptLock);
15999
+ if (retry !== "busy") {
16000
+ return { kind: "acquired", release: retry.release };
16001
+ }
15522
16002
  }
15523
16003
  } else {
15524
16004
  throw error;
@@ -15591,7 +16071,8 @@ async function spawnDetachedConsole(workspaceRoot, paths, launchId, deps) {
15591
16071
  ], {
15592
16072
  cwd: workspaceRoot,
15593
16073
  detached: true,
15594
- stdio: ["ignore", logHandle.fd, logHandle.fd]
16074
+ stdio: ["ignore", logHandle.fd, logHandle.fd],
16075
+ ...deps.environment === undefined ? {} : { env: deps.environment() }
15595
16076
  });
15596
16077
  child.unref?.();
15597
16078
  return child;
@@ -15626,7 +16107,8 @@ function createConsoleTool(deps = {}) {
15626
16107
  sendSignal,
15627
16108
  sleep,
15628
16109
  resolveCliEntrypoint,
15629
- spawn
16110
+ spawn,
16111
+ ...deps.environment === undefined ? {} : { environment: deps.environment }
15630
16112
  };
15631
16113
  if (args.action === "status") {
15632
16114
  return summaryFromProbe(await readRuntimeProbe(workspaceRoot, {
@@ -15744,10 +16226,7 @@ function createConsoleTool(deps = {}) {
15744
16226
  });
15745
16227
  }
15746
16228
  await removeConsoleRuntimeState(paths.runtimeStatePath);
15747
- const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, {
15748
- resolveCliEntrypoint: runtimeDeps.resolveCliEntrypoint,
15749
- spawn: runtimeDeps.spawn
15750
- });
16229
+ const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, runtimeDeps);
15751
16230
  const probe = await waitForHealthyRuntime(workspaceRoot, START_TIMEOUT_MS, {
15752
16231
  fetch: runtimeDeps.fetch,
15753
16232
  processExists: runtimeDeps.processExists,
@@ -17234,6 +17713,84 @@ function createLemonadeGuardedFetch(options) {
17234
17713
  });
17235
17714
  }
17236
17715
 
17716
+ // src/local/llamaSwapRequestGuard.ts
17717
+ var inferencePaths2 = new Set([
17718
+ "/chat/completions",
17719
+ "/completions",
17720
+ "/embeddings",
17721
+ "/responses"
17722
+ ]);
17723
+ async function validatedModelID(payload, modelIDs) {
17724
+ if (typeof payload !== "object" || payload === null || !("model" in payload) || typeof payload.model !== "string" || payload.model.length === 0 || payload.model !== payload.model.trim()) {
17725
+ throw new ModelResidencyError("request", "llama-swap");
17726
+ }
17727
+ if (modelIDs === undefined) {
17728
+ return payload.model;
17729
+ }
17730
+ const ids = typeof modelIDs === "function" ? await modelIDs() : modelIDs;
17731
+ if (ids.has(payload.model)) {
17732
+ return payload.model;
17733
+ }
17734
+ throw new ModelResidencyError("identity", "llama-swap");
17735
+ }
17736
+ function createLlamaSwapGuardedFetch(options) {
17737
+ const base = llamaSwapBaseURL(options.baseURL);
17738
+ const upstream = options.fetch;
17739
+ const healthFetch = options.healthFetch ?? upstream;
17740
+ return Object.assign(async (input, init) => {
17741
+ const request = new Request(input, init);
17742
+ const url = new URL(request.url);
17743
+ if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !request.url.startsWith(`${base}/`)) {
17744
+ throw new ModelResidencyError("destination", "llama-swap");
17745
+ }
17746
+ const path4 = request.url.slice(base.length);
17747
+ const metadataOnly = request.method === "GET" && /^\/models(?:\/[^/]+)?$/.test(path4);
17748
+ if (!metadataOnly && (request.method !== "POST" || !inferencePaths2.has(path4))) {
17749
+ throw new ModelResidencyError("request", "llama-swap");
17750
+ }
17751
+ if (!metadataOnly) {
17752
+ await withResidencyDeadline(async (signal) => {
17753
+ signal.throwIfAborted();
17754
+ let payload;
17755
+ try {
17756
+ payload = await request.clone().json();
17757
+ } catch {
17758
+ signal.throwIfAborted();
17759
+ throw new ModelResidencyError("request", "llama-swap");
17760
+ }
17761
+ signal.throwIfAborted();
17762
+ const model = await validatedModelID(payload, options.modelIDs);
17763
+ const residency = await readLlamaSwapResidency(base, healthFetch, {
17764
+ ...options,
17765
+ signal,
17766
+ headers: request.headers
17767
+ });
17768
+ signal.throwIfAborted();
17769
+ if (!residency.has(model)) {
17770
+ throw new ModelResidencyError("identity", "llama-swap");
17771
+ }
17772
+ if (residency.get(model)?.loaded !== true) {
17773
+ throw new ModelResidencyError("inactive", "llama-swap");
17774
+ }
17775
+ }, { ...options, signal: request.signal, runtimeLabel: "llama-swap" });
17776
+ }
17777
+ if (request.signal.aborted) {
17778
+ throw new ModelResidencyError("cancelled", "llama-swap");
17779
+ }
17780
+ const {
17781
+ body: _body,
17782
+ headers: _headers,
17783
+ method: _method,
17784
+ signal: _signal,
17785
+ redirect: _redirect,
17786
+ ...transportOptions
17787
+ } = init ?? {};
17788
+ return upstream(request, { ...transportOptions, redirect: "error" });
17789
+ }, {
17790
+ preconnect: (...args) => upstream.preconnect?.(...args)
17791
+ });
17792
+ }
17793
+
17237
17794
  // src/plugin/modelResidency.ts
17238
17795
  function isFetch(value) {
17239
17796
  return typeof value === "function";
@@ -17273,20 +17830,29 @@ function providerRoutes(provider) {
17273
17830
  npm: model?.provider?.npm ?? provider.npm
17274
17831
  }));
17275
17832
  }
17276
- function wireID(key, model) {
17833
+ function wireID(key, model, runtimeLabel = "Lemonade") {
17277
17834
  const id = model?.id ?? key;
17278
17835
  if (id.length === 0 || id !== id.trim())
17279
- throw new ModelResidencyError("identity");
17836
+ throw new ModelResidencyError("identity", runtimeLabel);
17280
17837
  return id;
17281
17838
  }
17282
17839
  function nativeEligibleID(id) {
17283
17840
  return id === "openai" || id === "anthropic" || id.startsWith("opencode");
17284
17841
  }
17842
+ function normalizedBase(kind, baseURL) {
17843
+ return kind === "llama-swap" ? llamaSwapBaseURL(baseURL) : lemonadeBaseURL(baseURL);
17844
+ }
17845
+ function runtimeBase(runtime) {
17846
+ return runtime.baseURL ?? (localRuntimeKind(runtime) === "llama-swap" ? LLAMA_SWAP_DEFAULT_BASE_URL : LEMONADE_DEFAULT_BASE_URL);
17847
+ }
17848
+ function residencyRuntimeLabel(config, providerID) {
17849
+ return config.local.runtimes.some((runtime) => runtime.defaultModel.providerID === providerID && localRuntimeKind(runtime) === "llama-swap") ? "llama-swap" : "Lemonade";
17850
+ }
17285
17851
  function configuredRuntime(config, providerID) {
17286
17852
  const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === providerID);
17287
17853
  const runtime = runtimes[0];
17288
17854
  if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled) {
17289
- throw new ModelResidencyError("destination");
17855
+ throw new ModelResidencyError("destination", residencyRuntimeLabel(config, providerID));
17290
17856
  }
17291
17857
  return runtime;
17292
17858
  }
@@ -17295,8 +17861,9 @@ function createModelResidencyGuard(config, options) {
17295
17861
  const blocked = new Set;
17296
17862
  let hostConfig;
17297
17863
  const readEnv = options.readEnv ?? ((name) => process.env[name]);
17298
- const lemonadeRuntimes = () => config.local.runtimes.filter((runtime) => localRuntimeKind(runtime) === "lemonade");
17299
- const protects = (providerID, baseURL) => lemonadeRuntimes().some((runtime) => {
17864
+ const failure = (code, providerID) => new ModelResidencyError(code, residencyRuntimeLabel(config, providerID));
17865
+ const residentRuntimes = () => config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
17866
+ const protects = (providerID, baseURL) => residentRuntimes().some((runtime) => {
17300
17867
  if (runtime.defaultModel.providerID === providerID)
17301
17868
  return true;
17302
17869
  if (baseURL === undefined)
@@ -17304,13 +17871,13 @@ function createModelResidencyGuard(config, options) {
17304
17871
  const binding = endpointBinding(baseURL, readEnv);
17305
17872
  if (binding.ambiguous)
17306
17873
  return true;
17307
- return binding.origin !== undefined && binding.origin === origin(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
17874
+ return binding.origin !== undefined && binding.origin === origin(runtimeBase(runtime));
17308
17875
  });
17309
17876
  const resolvedWireID = (model, declaredID) => {
17310
17877
  const id = options.resolveApiModelID === undefined ? declaredID : options.resolveApiModelID(model);
17311
17878
  if (id === undefined)
17312
- throw new ModelResidencyError("identity");
17313
- return wireID(id, undefined);
17879
+ throw failure("identity", model.providerID);
17880
+ return wireID(id, undefined, residencyRuntimeLabel(config, model.providerID));
17314
17881
  };
17315
17882
  const configHook = async (host) => {
17316
17883
  hostConfig = host;
@@ -17330,21 +17897,22 @@ function createModelResidencyGuard(config, options) {
17330
17897
  ...new Set([...host.disabled_providers ?? [], providerID])
17331
17898
  ];
17332
17899
  const reject = Object.assign(async () => {
17333
- throw new ModelResidencyError("transport");
17900
+ throw failure("transport", providerID);
17334
17901
  }, { preconnect: () => {} });
17335
17902
  provider.options = { ...provider.options, fetch: reject };
17336
17903
  try {
17337
17904
  const runtime = configuredRuntime(config, providerID);
17338
- const baseURL = lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
17339
- if (localRuntimeKind(runtime) !== "lemonade" || routes.some((route) => route.baseURL === undefined || lemonadeBaseURL(route.baseURL) !== baseURL))
17340
- throw new ModelResidencyError("destination");
17905
+ const kind = localRuntimeKind(runtime);
17906
+ const baseURL = normalizedBase(kind, runtimeBase(runtime));
17907
+ if (!runtimeRequiresModelResidency(kind) || routes.some((route) => route.baseURL === undefined || normalizedBase(kind, route.baseURL) !== baseURL))
17908
+ throw failure("destination", providerID);
17341
17909
  if (routes.some((route) => route.npm !== "@ai-sdk/openai-compatible") || nativeEligibleID(providerID) || !isFetch(originalFetch))
17342
- throw new ModelResidencyError("transport");
17910
+ throw failure("transport", providerID);
17343
17911
  const modelKeys = new Map;
17344
17912
  for (const [key, model] of Object.entries(provider.models ?? {})) {
17345
- if (model.options?.fetch !== undefined || model.options?.baseURL !== undefined && (typeof model.options.baseURL !== "string" || lemonadeBaseURL(model.options.baseURL) !== baseURL))
17346
- throw new ModelResidencyError("transport");
17347
- modelKeys.set(key, wireID(key, model));
17913
+ if (model.options?.fetch !== undefined || model.options?.baseURL !== undefined && (typeof model.options.baseURL !== "string" || normalizedBase(kind, model.options.baseURL) !== baseURL))
17914
+ throw failure("transport", providerID);
17915
+ modelKeys.set(key, wireID(key, model, residencyRuntimeLabel(config, providerID)));
17348
17916
  }
17349
17917
  if (!modelKeys.has(runtime.defaultModel.modelID))
17350
17918
  modelKeys.set(runtime.defaultModel.modelID, undefined);
@@ -17353,11 +17921,12 @@ function createModelResidencyGuard(config, options) {
17353
17921
  for (const [modelID2, declaredID] of modelKeys) {
17354
17922
  const id = options.resolveApiModelID === undefined ? declaredID : options.resolveApiModelID({ providerID, modelID: modelID2 });
17355
17923
  if (id !== undefined)
17356
- ids.add(wireID(id, undefined));
17924
+ ids.add(wireID(id, undefined, residencyRuntimeLabel(config, providerID)));
17357
17925
  }
17358
17926
  return ids;
17359
17927
  };
17360
- const fetch = createLemonadeGuardedFetch({
17928
+ const guardFetch = kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
17929
+ const fetch = guardFetch({
17361
17930
  ...options,
17362
17931
  baseURL,
17363
17932
  modelIDs: async () => {
@@ -17368,7 +17937,10 @@ function createModelResidencyGuard(config, options) {
17368
17937
  healthFetch: options.fetch
17369
17938
  });
17370
17939
  provider.options.fetch = fetch;
17940
+ if (kind === "llama-swap")
17941
+ provider.options.baseURL = baseURL;
17371
17942
  guarded.set(providerID, {
17943
+ kind,
17372
17944
  baseURL,
17373
17945
  fetch,
17374
17946
  originalFetch,
@@ -17382,7 +17954,7 @@ function createModelResidencyGuard(config, options) {
17382
17954
  } catch (error) {
17383
17955
  if (!(error instanceof ModelResidencyError))
17384
17956
  throw error;
17385
- (options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(providerID));
17957
+ (options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(providerID, residencyRuntimeLabel(config, providerID)));
17386
17958
  }
17387
17959
  }
17388
17960
  };
@@ -17392,48 +17964,52 @@ function createModelResidencyGuard(config, options) {
17392
17964
  return;
17393
17965
  const provider = guarded.get(input.model.providerID);
17394
17966
  if (provider === undefined || blocked.has(input.model.providerID) || input.provider.options.fetch !== provider.fetch || input.model.api.npm !== "@ai-sdk/openai-compatible" || nativeEligibleID(input.model.providerID))
17395
- throw new ModelResidencyError("transport");
17396
- if (lemonadeBaseURL(base) !== provider.baseURL) {
17397
- throw new ModelResidencyError("destination");
17967
+ throw failure("transport", input.model.providerID);
17968
+ if (normalizedBase(provider.kind, base) !== provider.baseURL) {
17969
+ throw failure("destination", input.model.providerID);
17398
17970
  }
17971
+ if (input.model.options.fetch !== undefined || input.model.options.baseURL !== undefined && (typeof input.model.options.baseURL !== "string" || normalizedBase(provider.kind, input.model.options.baseURL) !== provider.baseURL))
17972
+ throw failure("transport", input.model.providerID);
17399
17973
  const id = resolvedWireID({ providerID: input.model.providerID, modelID: input.model.id }, provider.modelKeys.get(input.model.id));
17400
17974
  if (id !== input.model.api.id)
17401
- throw new ModelResidencyError("identity");
17975
+ throw failure("identity", input.model.providerID);
17402
17976
  if (!provider.modelKeys.has(input.model.id))
17403
17977
  provider.modelKeys.set(input.model.id, undefined);
17404
17978
  };
17405
17979
  const localTarget = (model) => {
17406
17980
  const runtime = configuredRuntime(config, model.providerID);
17981
+ const kind = localRuntimeKind(runtime);
17982
+ const needsResidency = runtimeRequiresModelResidency(kind);
17407
17983
  if (hostConfig?.disabled_providers?.includes(model.providerID)) {
17408
- throw new ModelResidencyError("transport");
17984
+ throw failure("transport", model.providerID);
17409
17985
  }
17410
17986
  if (runtime.baseURL === undefined)
17411
- throw new ModelResidencyError("destination");
17987
+ throw failure("destination", model.providerID);
17412
17988
  const provider = hostConfig?.provider?.[model.providerID];
17413
17989
  const entry = Object.hasOwn(provider?.models ?? {}, model.modelID) ? provider?.models?.[model.modelID] : undefined;
17414
17990
  const configuredURL = provider === undefined ? undefined : providerBase(provider, entry);
17415
- if (configuredURL !== undefined && new URL(configuredURL).href.replace(/\/$/, "") !== new URL(runtime.baseURL).href.replace(/\/$/, "")) {
17416
- throw new ModelResidencyError("destination");
17991
+ if (configuredURL !== undefined && (needsResidency ? normalizedBase(kind, configuredURL) !== normalizedBase(kind, runtime.baseURL) : new URL(configuredURL).href.replace(/\/$/, "") !== new URL(runtime.baseURL).href.replace(/\/$/, ""))) {
17992
+ throw failure("destination", model.providerID);
17417
17993
  }
17418
- const modelID2 = resolvedWireID(model, entry === undefined && localRuntimeKind(runtime) === "lemonade" ? undefined : wireID(model.modelID, entry));
17994
+ const modelID2 = resolvedWireID(model, entry === undefined && needsResidency ? undefined : wireID(model.modelID, entry, residencyRuntimeLabel(config, model.providerID)));
17419
17995
  let fetch = options.fetch;
17420
- if (localRuntimeKind(runtime) === "lemonade") {
17996
+ if (needsResidency) {
17421
17997
  const installed = guarded.get(model.providerID);
17422
17998
  if (installed === undefined || blocked.has(model.providerID)) {
17423
- throw new ModelResidencyError("transport");
17999
+ throw failure("transport", model.providerID);
17424
18000
  }
17425
- if (installed.baseURL !== lemonadeBaseURL(runtime.baseURL)) {
17426
- throw new ModelResidencyError("destination");
18001
+ if (installed.baseURL !== normalizedBase(kind, runtime.baseURL)) {
18002
+ throw failure("destination", model.providerID);
17427
18003
  }
17428
18004
  if (!installed.modelKeys.has(model.modelID))
17429
18005
  installed.modelKeys.set(model.modelID, undefined);
17430
18006
  if (!installed.modelIDs().has(modelID2))
17431
- throw new ModelResidencyError("identity");
18007
+ throw failure("identity", model.providerID);
17432
18008
  fetch = installed.fetch;
17433
18009
  }
17434
18010
  const configuredHeaders = z25.record(z25.string(), z25.string()).safeParse(provider?.options?.headers ?? {});
17435
18011
  if (!configuredHeaders.success)
17436
- throw new ModelResidencyError("configuration");
18012
+ throw failure("configuration", model.providerID);
17437
18013
  const headers = new Headers(configuredHeaders.data);
17438
18014
  if (typeof provider?.options?.apiKey === "string" && !headers.has("authorization")) {
17439
18015
  headers.set("authorization", `Bearer ${provider.options.apiKey}`);
@@ -17442,7 +18018,7 @@ function createModelResidencyGuard(config, options) {
17442
18018
  headers.set(name, value);
17443
18019
  const upstream = fetch;
17444
18020
  return {
17445
- baseURL: runtime.baseURL,
18021
+ baseURL: needsResidency ? normalizedBase(kind, runtime.baseURL) : runtime.baseURL,
17446
18022
  modelID: modelID2,
17447
18023
  fetch: [...headers].length === 0 ? fetch : Object.assign(async (input, init) => {
17448
18024
  const request = new Request(input, init);
@@ -17549,8 +18125,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
17549
18125
  await injectMemoryBestEffort(options.injectMemory, prompt, output);
17550
18126
  return;
17551
18127
  }
17552
- await options.beforeRoute?.();
17553
18128
  const role = await roleForAgentName(config, input.agent ?? output.message.agent, options.resolveAgentRole);
18129
+ await options.beforeRoute?.();
17554
18130
  const derivedSignals = deriveTaskSignals(input, output);
17555
18131
  const signals = role === undefined ? derivedSignals : withoutExplicitOverride(derivedSignals);
17556
18132
  const executionPolicy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
@@ -17604,8 +18180,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
17604
18180
  function createHooks(config, getAvailable, options = {}) {
17605
18181
  const residency = options.modelResidency ?? createModelResidencyGuard(config, { fetch: globalThis.fetch });
17606
18182
  return {
17607
- config: residency.config,
17608
- "chat.params": residency.beforeInference,
18183
+ ...residency.config === undefined ? {} : { config: residency.config },
18184
+ ...residency.beforeInference === undefined ? {} : { "chat.params": residency.beforeInference },
17609
18185
  "chat.message": createChatMessageHook(config, getAvailable, options)
17610
18186
  };
17611
18187
  }
@@ -18565,7 +19141,7 @@ var memoryToolMessages = {
18565
19141
  scribeOnly: (actualRoleID) => `openteam memory: refused — memory records may only be written by the "scribe" role, not "${actualRoleID}".`,
18566
19142
  disabled: "openteam memory: semantic memory is disabled; nothing was recorded.",
18567
19143
  frontierDomainRefusal: 'openteam memory: refused — memory extraction requires local model execution, but router.executionMode is "frontier". Set router.executionMode to "local" or "mixed" and configure a reachable local runtime.',
18568
- localRuntimeUnavailable: "openteam memory: memory extraction requires a local runtime that is reachable and will not fall back to a frontier provider. Configure Ollama, LM Studio, Lemonade, or Foundry Local and run `openteam doctor`.",
19144
+ localRuntimeUnavailable: "openteam memory: memory extraction requires a local runtime that is reachable and will not fall back to a frontier provider. Configure Ollama, LM Studio, Lemonade, llama-swap, or Foundry Local and run `openteam doctor`.",
18569
19145
  localRuntimeBaseURLMissing: "openteam memory: refused — no local runtime with a base URL is configured and enabled; memory extraction is local-only.",
18570
19146
  extractionFailure: (reason) => `openteam memory: extraction failed; nothing was recorded (${reason}).`,
18571
19147
  noRecords: "openteam memory: no records were extracted; nothing was recorded.",
@@ -18635,14 +19211,16 @@ function createMemoryTool(deps) {
18635
19211
  if (runtime?.baseURL === undefined || runtime.baseURL.length === 0) {
18636
19212
  return memoryToolMessages.localRuntimeBaseURLMissing;
18637
19213
  }
18638
- const baseURL = runtime.baseURL;
19214
+ const kind = localRuntimeKind(runtime);
19215
+ const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
18639
19216
  const model = configuredModel ?? runtime.defaultModel;
18640
19217
  const selectedRuntime = runtime;
18641
19218
  const deadline = {
18642
19219
  ...deps.residencyOptions,
19220
+ ...kind === "llama-swap" ? { runtimeLabel: "llama-swap" } : {},
18643
19221
  ...signal === undefined ? {} : { signal }
18644
19222
  };
18645
- const reachable = localRuntimeKind(runtime) === "lemonade" ? await withResidencyDeadline((signal2) => deps.localRuntimeReachable(selectedRuntime, signal2), deadline) : await deps.localRuntimeReachable(runtime);
19223
+ const reachable = runtimeRequiresModelResidency(kind) ? await withResidencyDeadline((signal2) => deps.localRuntimeReachable(selectedRuntime, signal2), deadline) : await deps.localRuntimeReachable(runtime);
18646
19224
  throwIfAborted();
18647
19225
  if (!reachable)
18648
19226
  return memoryToolMessages.localRuntimeUnavailable;
@@ -18650,7 +19228,7 @@ function createMemoryTool(deps) {
18650
19228
  const target = resolveTarget ? await withResidencyDeadline(async () => resolveTarget(model), deadline) : {
18651
19229
  baseURL,
18652
19230
  modelID: model.modelID,
18653
- fetch: localRuntimeKind(runtime) === "lemonade" ? createLemonadeGuardedFetch({
19231
+ fetch: runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
18654
19232
  baseURL,
18655
19233
  fetch: deps.fetch,
18656
19234
  modelIDs: new Set([model.modelID]),
@@ -18753,12 +19331,841 @@ function createOrchestratedModelOwnership() {
18753
19331
  };
18754
19332
  }
18755
19333
 
19334
+ // src/plugin/nativeHost.ts
19335
+ import { AsyncLocalStorage } from "node:async_hooks";
19336
+ import { execFile as execFile3 } from "node:child_process";
19337
+ import { resolve as resolve10 } from "node:path";
19338
+ import { Model as Model2 } from "@opencode/schema/model";
19339
+ import { Session as Session2 } from "@opencode/schema/session";
19340
+ import { Tool } from "@opencode/schema/tool";
19341
+ import { tool as legacyTool } from "@opencode-ai/plugin/tool";
19342
+ import { Clock, Effect, Option, Stream } from "effect";
19343
+ import { z as z30 } from "zod";
19344
+
19345
+ // src/plugin/nativeHostContext.ts
19346
+ import {
19347
+ Context
19348
+ } from "effect";
19349
+
19350
+ class NativeSessions extends Context.Service()("@opencode/Session") {
19351
+ }
19352
+
19353
+ class NativePermissions extends Context.Service()("@opencode/Permission") {
19354
+ }
19355
+
19356
+ class NativeAgents extends Context.Service()("@opencode/Agent") {
19357
+ }
19358
+
19359
+ class NativeLocations extends Context.Service()("@opencode/example/LocationServiceMap") {
19360
+ }
19361
+
19362
+ class NativeServerInfo extends Context.Service()("@opencode/server/ServerInfo") {
19363
+ }
19364
+
19365
+ class NativeServerAuth extends Context.Service()("@opencode/ServerAuthConfig") {
19366
+ }
19367
+
19368
+ // src/plugin/nativeInventory.ts
19369
+ function nativeConfiguredInventory(models, config) {
19370
+ const local = new Set(config.local.runtimes.map((runtime) => runtime.defaultModel.providerID));
19371
+ const enabled = models.filter((model) => model.enabled);
19372
+ const catalog = new Map;
19373
+ for (const model of enabled) {
19374
+ if (local.has(model.providerID))
19375
+ continue;
19376
+ const provider = catalog.get(model.providerID) ?? { models: [] };
19377
+ provider.models.push({
19378
+ id: model.id,
19379
+ name: model.name,
19380
+ limit: model.limit,
19381
+ cost: model.cost.find((tier) => tier.tier === undefined),
19382
+ status: model.status,
19383
+ tool_call: model.capabilities.tools,
19384
+ modalities: { input: model.capabilities.input },
19385
+ reasoning: model.capabilities.output.includes("reasoning")
19386
+ });
19387
+ catalog.set(model.providerID, provider);
19388
+ }
19389
+ return {
19390
+ models: enabled.map((model) => ({
19391
+ providerID: model.providerID,
19392
+ modelID: model.id,
19393
+ apiModelID: model.modelID,
19394
+ ...local.has(model.providerID) ? {} : {
19395
+ supportsTools: model.capabilities.tools,
19396
+ ...model.limit.context > 0 ? { contextWindow: model.limit.context } : {}
19397
+ }
19398
+ })),
19399
+ profiles: normalizeModelsDevCatalog(Object.fromEntries(catalog))
19400
+ };
19401
+ }
19402
+
19403
+ // src/plugin/nativeResidency.ts
19404
+ import { randomUUID as randomUUID2 } from "node:crypto";
19405
+ import { z as z29 } from "zod";
19406
+ var NATIVE_RESIDENCY_PROVIDER_PACKAGE = "aisdk:@ai-sdk/openai-compatible@2.0.75";
19407
+ var NATIVE_ADMISSION_HEADER = "x-openteam-local-admission";
19408
+ var MAX_ADMISSIONS = 2048;
19409
+ var compatiblePackages = new Set([
19410
+ "@ai-sdk/openai-compatible",
19411
+ "aisdk:@ai-sdk/openai-compatible",
19412
+ "@opencode/ai/providers/openai-compatible",
19413
+ NATIVE_RESIDENCY_PROVIDER_PACKAGE
19414
+ ]);
19415
+ function key(ref) {
19416
+ return `${ref.providerID}\x00${ref.modelID}`;
19417
+ }
19418
+ function label(runtime) {
19419
+ return localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade";
19420
+ }
19421
+ function runtimeBase2(runtime) {
19422
+ return localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL) : lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
19423
+ }
19424
+ function createNativeModelResidencyGuard(config, options) {
19425
+ const admissions = new Map;
19426
+ const localTargets = new Map;
19427
+ let disposed = false;
19428
+ const residentRuntimes = config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
19429
+ const globalMode = () => resolveExecutionMode({
19430
+ routerMode: config.router.executionMode,
19431
+ legacyLocalOnly: config.router.localOnly,
19432
+ legacyFrontierOnly: config.router.frontierOnly
19433
+ });
19434
+ function allowed(providerID, mode) {
19435
+ const local = config.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
19436
+ return local.length > 0 ? mode !== "frontier" && local.some((runtime) => runtime.enabled) : mode !== "local";
19437
+ }
19438
+ function protects(providerID, url) {
19439
+ return residentRuntimes.some((runtime) => {
19440
+ if (runtime.defaultModel.providerID === providerID)
19441
+ return true;
19442
+ if (url === undefined)
19443
+ return false;
19444
+ if (!URL.canParse(url))
19445
+ return true;
19446
+ return new URL(url).origin === new URL(runtimeBase2(runtime)).origin;
19447
+ });
19448
+ }
19449
+ function bind(model) {
19450
+ const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === model.providerID);
19451
+ const runtime = runtimes[0];
19452
+ const runtimeLabel = runtime === undefined ? undefined : label(runtime);
19453
+ if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled)
19454
+ throw new ModelResidencyError("destination", runtimeLabel);
19455
+ const baseURL = runtimeBase2(runtime);
19456
+ const base = model.settings?.baseURL;
19457
+ const normalized = typeof base !== "string" ? undefined : localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(base) : lemonadeBaseURL(base);
19458
+ if (normalized !== baseURL)
19459
+ throw new ModelResidencyError("destination", runtimeLabel);
19460
+ if (model.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE || model.transport === "websocket" || model.settings?.fetch !== undefined || model.settings?.queryParams !== undefined || model.settings?.transformRequestBody !== undefined)
19461
+ throw new ModelResidencyError("transport", runtimeLabel);
19462
+ if (!model.modelID || model.modelID !== model.modelID.trim() || model.body?.model !== undefined && model.body.model !== model.modelID)
19463
+ throw new ModelResidencyError("identity", runtimeLabel);
19464
+ return { runtime, model, baseURL };
19465
+ }
19466
+ function guardedFetch(binding, send, authorized = () => true) {
19467
+ const create = localRuntimeKind(binding.runtime) === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
19468
+ return create({
19469
+ ...options,
19470
+ fetch: Object.assign((input, init) => {
19471
+ if (disposed || !authorized())
19472
+ throw new Error(nativeHostMessages.missingAdmission);
19473
+ return send(input, init);
19474
+ }, {
19475
+ preconnect: (...args) => send.preconnect?.(...args)
19476
+ }),
19477
+ healthFetch: options.fetch,
19478
+ baseURL: binding.baseURL,
19479
+ modelIDs: new Set([binding.model.modelID])
19480
+ });
19481
+ }
19482
+ function target(model, send, baseURL) {
19483
+ const headers = new Headers(z29.record(z29.string(), z29.string()).parse(model.headers ?? {}));
19484
+ if (typeof model.settings?.apiKey === "string" && !headers.has("authorization"))
19485
+ headers.set("authorization", `Bearer ${model.settings.apiKey}`);
19486
+ return {
19487
+ baseURL,
19488
+ modelID: model.modelID,
19489
+ fetch: Object.assign(async (input, init) => {
19490
+ if (disposed)
19491
+ throw new Error(nativeHostMessages.missingAdmission);
19492
+ const request = new Request(input, init);
19493
+ for (const [name, value] of headers)
19494
+ if (!request.headers.has(name))
19495
+ request.headers.set(name, value);
19496
+ return send(request);
19497
+ }, {
19498
+ preconnect: (...args) => send.preconnect?.(...args)
19499
+ })
19500
+ };
19501
+ }
19502
+ function observeModels(models) {
19503
+ localTargets.clear();
19504
+ return models.map((model) => {
19505
+ if (!allowed(model.providerID, globalMode()))
19506
+ return { ...model, enabled: false };
19507
+ if (!model.enabled)
19508
+ return model;
19509
+ const ref = { providerID: model.providerID, modelID: model.id };
19510
+ const base = model.settings?.baseURL;
19511
+ if (!protects(model.providerID, typeof base === "string" ? base : undefined)) {
19512
+ if (config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === model.providerID) && typeof base === "string")
19513
+ localTargets.set(key(ref), target(model, options.fetch, base));
19514
+ return model;
19515
+ }
19516
+ try {
19517
+ const binding = bind(model);
19518
+ localTargets.set(key(ref), target(model, guardedFetch(binding, options.fetch), binding.baseURL));
19519
+ return model;
19520
+ } catch (error) {
19521
+ if (!(error instanceof ModelResidencyError) && !(error instanceof z29.ZodError))
19522
+ throw error;
19523
+ (options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(model.providerID, residentRuntimes.find((runtime) => runtime.defaultModel.providerID === model.providerID)?.kind === "llama-swap" ? "llama-swap" : "Lemonade"));
19524
+ return { ...model, enabled: false };
19525
+ }
19526
+ });
19527
+ }
19528
+ return {
19529
+ providers(editor) {
19530
+ for (const record of editor.list())
19531
+ if (!allowed(record.provider.id, globalMode()))
19532
+ editor.remove(record.provider.id);
19533
+ },
19534
+ transform(editor) {
19535
+ for (const model of editor.list()) {
19536
+ const provider = editor.provider.get(model.providerID)?.provider;
19537
+ const base = model.settings?.baseURL ?? provider?.settings?.baseURL;
19538
+ const pkg = model.package ?? provider?.package;
19539
+ if (protects(model.providerID, typeof base === "string" ? base : undefined) && pkg !== undefined && compatiblePackages.has(pkg))
19540
+ editor.update(model.providerID, model.id, (draft) => {
19541
+ draft.package = NATIVE_RESIDENCY_PROVIDER_PACKAGE;
19542
+ });
19543
+ }
19544
+ },
19545
+ observeModels,
19546
+ sdk(event) {
19547
+ if (!protects(event.model.providerID, typeof event.options.baseURL === "string" ? event.options.baseURL : undefined))
19548
+ return;
19549
+ const factory = bind(event.model);
19550
+ if (event.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE.slice("aisdk:".length) || typeof event.options.fetch !== "function")
19551
+ throw new Error(nativeHostMessages.transportUnavailable);
19552
+ const upstream = event.options.fetch;
19553
+ const transport = Object.assign((input, init) => {
19554
+ const request = new Request(input, init);
19555
+ return upstream(request, {
19556
+ signal: request.signal,
19557
+ redirect: "error"
19558
+ });
19559
+ }, {
19560
+ preconnect: (...args) => upstream.preconnect?.(...args)
19561
+ });
19562
+ event.options.fetch = Object.assign(async (input, init) => {
19563
+ let request = new Request(input, init);
19564
+ const token = request.headers.get(NATIVE_ADMISSION_HEADER);
19565
+ const admission = token === null ? undefined : admissions.get(token);
19566
+ if (token === null || admission === undefined || admission.model.providerID !== factory.model.providerID || admission.baseURL !== factory.baseURL)
19567
+ throw new Error(nativeHostMessages.missingAdmission);
19568
+ request.headers.delete(NATIVE_ADMISSION_HEADER);
19569
+ if (factory.model.body !== undefined) {
19570
+ let body;
19571
+ try {
19572
+ body = await request.json();
19573
+ } catch {
19574
+ throw new ModelResidencyError("request", label(factory.runtime));
19575
+ }
19576
+ if (body === null || typeof body !== "object" || Array.isArray(body))
19577
+ throw new ModelResidencyError("request", label(factory.runtime));
19578
+ request = new Request(request, {
19579
+ body: JSON.stringify(mergeConfigInputs(body, factory.model.body))
19580
+ });
19581
+ }
19582
+ return guardedFetch(admission, transport, () => admissions.get(token) === admission)(request);
19583
+ }, {
19584
+ preconnect: (...args) => upstream.preconnect?.(...args)
19585
+ });
19586
+ },
19587
+ async modelRequest(event) {
19588
+ if (disposed)
19589
+ throw new Error(nativeHostMessages.missingAdmission);
19590
+ const roleID = await options.resolveAgentRole?.(event.agent) ?? event.agent;
19591
+ const role = isKnownNonWorkerRole(roleID) ? undefined : getRoleProfile(roleID, config.orchestrator?.roles);
19592
+ const policy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
19593
+ const mode = resolveExecutionMode({
19594
+ routerMode: globalMode(),
19595
+ agentMode: policy?.executionMode
19596
+ });
19597
+ if (!allowed(event.model.providerID, mode))
19598
+ throw new Error(nativeHostMessages.executionDomainBlocked);
19599
+ if (!protects(event.model.providerID, event.baseURL))
19600
+ return;
19601
+ const model = (await options.readModels()).find((candidate) => candidate.providerID === event.model.providerID && candidate.id === event.model.id && candidate.enabled);
19602
+ if (model === undefined)
19603
+ throw new Error(nativeHostMessages.transportUnavailable);
19604
+ const variant = model.variants.find((candidate) => candidate.id === event.model.variant);
19605
+ const effective = variant === undefined ? model : {
19606
+ ...model,
19607
+ settings: { ...model.settings, ...variant.settings },
19608
+ headers: { ...model.headers, ...variant.headers },
19609
+ body: { ...model.body, ...variant.body }
19610
+ };
19611
+ const binding = bind(effective);
19612
+ if (disposed)
19613
+ throw new Error(nativeHostMessages.missingAdmission);
19614
+ const token = (options.newRequestID ?? randomUUID2)();
19615
+ admissions.set(token, {
19616
+ ...binding,
19617
+ sessionID: event.sessionID,
19618
+ kind: event.kind
19619
+ });
19620
+ const oldest = admissions.keys().next().value;
19621
+ if (admissions.size > MAX_ADMISSIONS && oldest !== undefined)
19622
+ admissions.delete(oldest);
19623
+ event.headers[NATIVE_ADMISSION_HEADER] = token;
19624
+ },
19625
+ async http(event) {
19626
+ if (protects(event.model.providerID, event.request.url))
19627
+ throw new Error(nativeHostMessages.transportUnavailable);
19628
+ },
19629
+ websocket(event) {
19630
+ const url = new URL(event.url);
19631
+ if (url.protocol === "ws:")
19632
+ url.protocol = "http:";
19633
+ if (url.protocol === "wss:")
19634
+ url.protocol = "https:";
19635
+ if (protects(event.model.providerID, url.href))
19636
+ throw new Error(nativeHostMessages.transportUnavailable);
19637
+ },
19638
+ finished(sessionID) {
19639
+ for (const [token, admission] of admissions)
19640
+ if (admission.sessionID === sessionID)
19641
+ admissions.delete(token);
19642
+ },
19643
+ dispose() {
19644
+ disposed = true;
19645
+ admissions.clear();
19646
+ localTargets.clear();
19647
+ },
19648
+ localTarget(ref) {
19649
+ const value = localTargets.get(key(ref));
19650
+ if (value === undefined)
19651
+ throw new Error(nativeHostMessages.transportUnavailable);
19652
+ return value;
19653
+ }
19654
+ };
19655
+ }
19656
+
19657
+ // src/plugin/nativeSessions.ts
19658
+ import { resolve as resolve9 } from "node:path";
19659
+ import { Agent } from "@opencode/schema/agent";
19660
+ import { Model } from "@opencode/schema/model";
19661
+ import { Session } from "@opencode/schema/session";
19662
+ import { DateTime } from "effect";
19663
+ function sameNativeWorkspace(actual, expected) {
19664
+ return resolve9(actual.directory) === resolve9(expected.directory) && actual.workspaceID === expected.workspaceID;
19665
+ }
19666
+ function nativeAssistantResponse(sessionID, message) {
19667
+ return {
19668
+ info: {
19669
+ id: message.id,
19670
+ sessionID,
19671
+ role: "assistant",
19672
+ mode: message.agent,
19673
+ providerID: message.model.providerID,
19674
+ modelID: message.model.id,
19675
+ ...message.cost === undefined ? {} : { cost: message.cost },
19676
+ ...message.tokens === undefined ? {} : { tokens: message.tokens },
19677
+ ...message.error === undefined ? {} : { error: message.error },
19678
+ time: {
19679
+ created: DateTime.toEpochMillis(message.time.created),
19680
+ ...message.time.completed === undefined ? {} : { completed: DateTime.toEpochMillis(message.time.completed) }
19681
+ }
19682
+ },
19683
+ parts: message.content.flatMap((part) => part.type === "text" ? [{ type: "text", text: part.text }] : [])
19684
+ };
19685
+ }
19686
+ function createNativeSessionClient(deps) {
19687
+ const directory = resolve9(deps.directory);
19688
+ const location = {
19689
+ directory,
19690
+ ...deps.workspaceID === undefined ? {} : { workspaceID: deps.workspaceID }
19691
+ };
19692
+ function assertDirectory(candidate) {
19693
+ if (candidate !== undefined && resolve9(candidate) !== directory)
19694
+ throw new Error(nativeHostMessages.workspaceMismatch);
19695
+ }
19696
+ async function ownedSession(id, signal) {
19697
+ const info = await deps.run(deps.sessions().get(id), signal === undefined ? undefined : { signal });
19698
+ if (!sameNativeWorkspace(info.location, location))
19699
+ throw new Error(nativeHostMessages.workspaceMismatch);
19700
+ return info;
19701
+ }
19702
+ return {
19703
+ async create(args) {
19704
+ const signal = deps.signal?.();
19705
+ signal?.throwIfAborted();
19706
+ assertDirectory(args.query?.directory);
19707
+ if (args.body?.parentID === undefined)
19708
+ throw new Error(nativeHostMessages.missingBindings);
19709
+ const parentID = Session.ID.make(args.body.parentID);
19710
+ await ownedSession(parentID, signal);
19711
+ const child = await deps.run(deps.sessions().create({
19712
+ parentID,
19713
+ ...args.body.title === undefined ? {} : { title: args.body.title }
19714
+ }));
19715
+ if (!sameNativeWorkspace(child.location, location))
19716
+ throw new Error(nativeHostMessages.workspaceMismatch);
19717
+ if (child.parentID !== parentID)
19718
+ throw new Error(nativeHostMessages.missingBindings);
19719
+ signal?.throwIfAborted();
19720
+ return { data: { id: child.id }, error: undefined };
19721
+ },
19722
+ async prompt(args) {
19723
+ const parentSignal = deps.signal?.();
19724
+ const signal = parentSignal === undefined ? args.signal : args.signal === undefined ? parentSignal : AbortSignal.any([parentSignal, args.signal]);
19725
+ const sessionID = Session.ID.make(args.path.id);
19726
+ await ownedSession(sessionID, signal);
19727
+ const run = (effect) => deps.run(effect, signal === undefined ? undefined : { signal });
19728
+ const sessions = deps.sessions();
19729
+ try {
19730
+ if (args.body.agent !== undefined)
19731
+ await run(sessions.switchAgent({
19732
+ sessionID,
19733
+ agent: Agent.ID.make(args.body.agent)
19734
+ }));
19735
+ if (args.body.model !== undefined)
19736
+ await run(sessions.switchModel({
19737
+ sessionID,
19738
+ model: Model.Ref.parse(`${args.body.model.providerID}/${args.body.model.modelID}`)
19739
+ }));
19740
+ if (args.body.system !== undefined)
19741
+ deps.systemPrompts.set(sessionID, args.body.system);
19742
+ await run(sessions.prompt({
19743
+ sessionID,
19744
+ text: args.body.parts.map((part) => part.text).join(`
19745
+ `)
19746
+ }));
19747
+ await run(sessions.wait(sessionID));
19748
+ const final = await ownedSession(sessionID, signal);
19749
+ const messages = await run(sessions.messages({ sessionID, limit: 100 }));
19750
+ const assistant = [...messages].reverse().find((message) => message.type === "assistant");
19751
+ if (final.outcome === "interrupted")
19752
+ throw new Error(nativeHostMessages.executionInterrupted);
19753
+ if (final.outcome !== "succeeded" || assistant?.error !== undefined)
19754
+ throw new Error(nativeHostMessages.executionFailed);
19755
+ if (assistant?.time.completed === undefined)
19756
+ throw new Error(nativeHostMessages.noCompletedResponse);
19757
+ return {
19758
+ data: nativeAssistantResponse(sessionID, assistant),
19759
+ error: undefined
19760
+ };
19761
+ } finally {
19762
+ if (signal?.aborted)
19763
+ await deps.run(sessions.interrupt(sessionID, { resume: false }));
19764
+ }
19765
+ },
19766
+ async abort(args) {
19767
+ assertDirectory(args.query?.directory);
19768
+ const id = Session.ID.make(args.path.id);
19769
+ await ownedSession(id, args.signal);
19770
+ const options = args.signal === undefined ? undefined : { signal: args.signal };
19771
+ await deps.run(deps.sessions().interrupt(id, { resume: false }), options);
19772
+ const active = await deps.run(deps.sessions().active, options);
19773
+ return { data: !active.has(id), error: undefined };
19774
+ },
19775
+ async status(args) {
19776
+ assertDirectory(args?.query?.directory);
19777
+ const options = args?.signal == null ? undefined : { signal: args.signal };
19778
+ const sessions = deps.sessions();
19779
+ const active = await deps.run(sessions.active, options);
19780
+ const entries = [];
19781
+ for (const id of active) {
19782
+ const info = await deps.run(sessions.get(id), options);
19783
+ if (sameNativeWorkspace(info.location, location))
19784
+ entries.push([id, { type: "busy" }]);
19785
+ }
19786
+ return { data: Object.fromEntries(entries), error: undefined };
19787
+ }
19788
+ };
19789
+ }
19790
+
19791
+ // src/plugin/nativeHost.ts
19792
+ var executionEvent = z30.preprocess(only(["type", "location", "data"]), z30.object({
19793
+ type: z30.enum([
19794
+ "session.created",
19795
+ "session.deleted",
19796
+ "session.execution.started",
19797
+ "session.execution.succeeded",
19798
+ "session.execution.failed",
19799
+ "session.execution.interrupted",
19800
+ "session.step.ended",
19801
+ "session.step.failed"
19802
+ ]),
19803
+ location: z30.preprocess(only(["directory", "workspaceID"]), z30.object({
19804
+ directory: z30.string(),
19805
+ workspaceID: z30.string().optional()
19806
+ }).strict()).optional(),
19807
+ data: z30.preprocess(only(["sessionID", "assistantMessageID"]), z30.object({
19808
+ sessionID: z30.string(),
19809
+ assistantMessageID: z30.string().optional()
19810
+ }).strict())
19811
+ }).strict());
19812
+ var nativeBindings = Effect.fn("openteam.nativeBindings")(function* () {
19813
+ const sessions = yield* Effect.serviceOption(NativeSessions);
19814
+ const permissions = yield* Effect.serviceOption(NativePermissions);
19815
+ const agents = yield* Effect.serviceOption(NativeAgents);
19816
+ const locations = yield* Effect.serviceOption(NativeLocations);
19817
+ if (Option.isNone(sessions) || Option.isNone(permissions) || Option.isNone(agents) || Option.isNone(locations))
19818
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19819
+ const server = yield* Effect.serviceOption(NativeServerInfo);
19820
+ const auth = yield* Effect.serviceOption(NativeServerAuth);
19821
+ return {
19822
+ sessions: sessions.value,
19823
+ permissions: permissions.value,
19824
+ agents: agents.value,
19825
+ locations: locations.value,
19826
+ ...Option.isNone(server) ? {} : { server: server.value },
19827
+ ...Option.isNone(auth) ? {} : { auth: auth.value }
19828
+ };
19829
+ });
19830
+ function nativeExec(directory) {
19831
+ return (command, args) => new Promise((resolveOutput, reject) => {
19832
+ execFile3(command, [...args], { cwd: directory }, (error, stdout, stderr) => {
19833
+ if (error !== null && typeof error.code !== "number") {
19834
+ reject(error);
19835
+ return;
19836
+ }
19837
+ resolveOutput({
19838
+ exitCode: typeof error?.code === "number" ? error.code : 0,
19839
+ stdout,
19840
+ stderr
19841
+ });
19842
+ });
19843
+ });
19844
+ }
19845
+ function legacyModel(ref) {
19846
+ return { providerID: ref.providerID, modelID: ref.id };
19847
+ }
19848
+ function createNativePluginEffect(factory) {
19849
+ return Effect.fn("openteam.native")(function* (ctx) {
19850
+ if (ctx.app.version !== NATIVE_OPENCODE_VERSION)
19851
+ return yield* Effect.die(new Error(nativeHostMessages.unsupportedVersion(ctx.app.version)));
19852
+ const version = ctx.app.version;
19853
+ const effectContext = yield* Effect.context();
19854
+ const directory = resolve10(ctx.location.directory);
19855
+ let bindings;
19856
+ const run = (effect, options) => {
19857
+ const current = bindings;
19858
+ const bound = current === undefined ? effect : effect.pipe(Effect.provideService(NativeSessions, current.sessions), Effect.provideService(NativePermissions, current.permissions), Effect.provideService(NativeAgents, current.agents), Effect.provideService(NativeLocations, current.locations));
19859
+ return Effect.runPromiseWith(effectContext)(bound, options);
19860
+ };
19861
+ let config;
19862
+ let residency;
19863
+ const systemPrompts = new Map;
19864
+ const toolSignals = new AsyncLocalStorage;
19865
+ const remember = (next) => {
19866
+ bindings = { ...bindings, ...next };
19867
+ };
19868
+ const requiredBindings = () => {
19869
+ if (bindings === undefined)
19870
+ throw new Error(nativeHostMessages.missingBindings);
19871
+ return bindings;
19872
+ };
19873
+ const assertWorkspace = (candidate) => {
19874
+ if (!sameNativeWorkspace(candidate, ctx.location))
19875
+ throw new Error(nativeHostMessages.workspaceMismatch);
19876
+ };
19877
+ const session = createNativeSessionClient({
19878
+ directory,
19879
+ ...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID },
19880
+ sessions: () => requiredBindings().sessions,
19881
+ run,
19882
+ systemPrompts,
19883
+ signal: () => toolSignals.getStore()
19884
+ });
19885
+ const client = {
19886
+ session,
19887
+ app: {
19888
+ async agents() {
19889
+ const result = await run(ctx.agent.list());
19890
+ return {
19891
+ data: result.data.map((agent) => ({ name: agent.id })),
19892
+ error: undefined
19893
+ };
19894
+ },
19895
+ async log(input) {
19896
+ const message = input.body?.message ?? nativeHostMessages.toolFailed;
19897
+ await run(input.body?.level === "error" ? Effect.logError(message) : input.body?.level === "warn" ? Effect.logWarning(message) : Effect.logInfo(message));
19898
+ }
19899
+ },
19900
+ instance: {
19901
+ async dispose(input) {
19902
+ assertWorkspace({
19903
+ ...ctx.location,
19904
+ directory: input.query?.directory ?? directory
19905
+ });
19906
+ await run(requiredBindings().locations.invalidate({
19907
+ directory: ctx.location.directory,
19908
+ ...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID }
19909
+ }), input.signal == null ? undefined : { signal: input.signal });
19910
+ return { data: true, error: undefined };
19911
+ }
19912
+ }
19913
+ };
19914
+ const hooks = yield* Effect.promise(() => factory({
19915
+ directory,
19916
+ worktree: ctx.location.project.directory,
19917
+ client,
19918
+ exec: nativeExec(directory),
19919
+ serverUrl: () => {
19920
+ const url = bindings?.server?.urls().find((value) => {
19921
+ if (!URL.canParse(value))
19922
+ return false;
19923
+ return ["127.0.0.1", "localhost", "[::1]"].includes(new URL(value).hostname);
19924
+ });
19925
+ return url === undefined ? undefined : new URL(url);
19926
+ },
19927
+ opencodeVersion: async () => version,
19928
+ listConfiguredInventory: async () => {
19929
+ if (config === undefined || residency === undefined)
19930
+ throw new Error(nativeHostMessages.missingBindings);
19931
+ const models = residency.observeModels((await run(ctx.model.list())).data);
19932
+ return nativeConfiguredInventory(models, config);
19933
+ },
19934
+ consoleEnvironment: () => {
19935
+ const auth = bindings?.auth;
19936
+ return {
19937
+ ...process.env,
19938
+ BUN_BE_BUN: "1",
19939
+ OPENCODE_SERVER_USERNAME: auth?.username,
19940
+ OPENCODE_SERVER_PASSWORD: auth && Option.isSome(auth.password) ? auth.password.value : undefined
19941
+ };
19942
+ },
19943
+ createResidencyGuard: (runtimeConfig, options) => {
19944
+ config = runtimeConfig;
19945
+ residency = createNativeModelResidencyGuard(runtimeConfig, {
19946
+ ...options,
19947
+ readModels: async () => (await run(ctx.model.list())).data
19948
+ });
19949
+ return residency;
19950
+ }
19951
+ }, ctx.options));
19952
+ const guard = residency;
19953
+ if (guard === undefined)
19954
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19955
+ const chatMessage = hooks["chat.message"];
19956
+ const tools = hooks.tool;
19957
+ if (chatMessage === undefined || tools === undefined)
19958
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19959
+ yield* Effect.addFinalizer(() => Effect.promise(async () => {
19960
+ guard.dispose();
19961
+ systemPrompts.clear();
19962
+ await hooks.dispose?.();
19963
+ }));
19964
+ yield* ctx.provider.transform(guard.providers);
19965
+ yield* ctx.model.transform(guard.transform);
19966
+ yield* ctx.aisdk.hook("sdk", (event) => Effect.sync(() => guard.sdk(event)));
19967
+ yield* ctx.session.hook("model.request", (event) => Effect.promise(() => guard.modelRequest(event)));
19968
+ yield* ctx.session.hook("http.request", (event) => Effect.promise(() => guard.http(event)));
19969
+ yield* ctx.session.hook("experimental.ws.handshake", (event) => Effect.sync(() => guard.websocket(event)));
19970
+ yield* ctx.session.hook("context", (event) => Effect.sync(() => {
19971
+ const system = systemPrompts.get(event.sessionID);
19972
+ if (system !== undefined)
19973
+ event.system.push({ type: "text", text: system });
19974
+ }));
19975
+ yield* ctx.session.hook("prompt", Effect.fn("openteam.nativePrompt")(function* (event) {
19976
+ const currentBindings = yield* nativeBindings();
19977
+ const current = yield* ctx.session.get({ sessionID: event.sessionID }).pipe(Effect.orDie);
19978
+ assertWorkspace(current.location);
19979
+ remember(currentBindings);
19980
+ const agent = yield* currentBindings.agents.resolve(current.agent).pipe(Effect.orDie);
19981
+ if (agent === undefined)
19982
+ return yield* Effect.die(new Error(nativeHostMessages.unknownAgent));
19983
+ const selected = current.model ?? (yield* ctx.model.default().pipe(Effect.orDie)).data ?? (yield* ctx.model.list().pipe(Effect.orDie)).data[0];
19984
+ if (selected === undefined)
19985
+ return yield* Effect.die(new Error(nativeHostMessages.noAvailableModel));
19986
+ const created = yield* Clock.currentTimeMillis;
19987
+ const output = {
19988
+ message: {
19989
+ id: event.messageID,
19990
+ sessionID: event.sessionID,
19991
+ role: "user",
19992
+ time: { created },
19993
+ agent: agent.id,
19994
+ model: legacyModel(selected)
19995
+ },
19996
+ parts: [
19997
+ {
19998
+ id: event.messageID,
19999
+ sessionID: event.sessionID,
20000
+ messageID: event.messageID,
20001
+ type: "text",
20002
+ text: event.prompt.text
20003
+ }
20004
+ ]
20005
+ };
20006
+ yield* Effect.promise(() => chatMessage({
20007
+ sessionID: event.sessionID,
20008
+ messageID: event.messageID,
20009
+ agent: agent.id,
20010
+ ...current.model === undefined ? {} : { model: legacyModel(current.model) }
20011
+ }, output));
20012
+ const routed = output.message.model;
20013
+ yield* ctx.session.switchModel({
20014
+ sessionID: event.sessionID,
20015
+ model: {
20016
+ ...Model2.Ref.parse(`${routed.providerID}/${routed.modelID}`),
20017
+ ...current.model?.providerID === routed.providerID && current.model.id === routed.modelID && current.model.variant !== undefined ? { variant: current.model.variant } : {}
20018
+ }
20019
+ }).pipe(Effect.orDie);
20020
+ const part = output.parts[0];
20021
+ if (part?.type === "text")
20022
+ event.prompt.text = part.text;
20023
+ }));
20024
+ yield* ctx.tool.transform((editor) => {
20025
+ for (const [name, definition] of Object.entries(tools)) {
20026
+ const input = legacyTool.schema.object(definition.args);
20027
+ editor.add({
20028
+ name,
20029
+ description: definition.description,
20030
+ input: legacyTool.schema.toJSONSchema(input, { io: "input" }),
20031
+ options: { codemode: false },
20032
+ execute: Effect.fn("openteam.nativeTool")(function* (_input, toolContext) {
20033
+ const current = requiredBindings();
20034
+ const parent = yield* current.sessions.get(toolContext.sessionID).pipe(Effect.orDie);
20035
+ assertWorkspace(parent.location);
20036
+ remember(current);
20037
+ return yield* Effect.tryPromise(async (signal) => {
20038
+ const progress = [];
20039
+ const context = {
20040
+ sessionID: toolContext.sessionID,
20041
+ messageID: toolContext.messageID,
20042
+ agent: toolContext.agent,
20043
+ directory,
20044
+ worktree: ctx.location.project.directory,
20045
+ abort: signal,
20046
+ metadata: (update) => {
20047
+ progress.push(run(toolContext.progress({
20048
+ ...update.metadata,
20049
+ ...update.title === undefined ? {} : { title: update.title }
20050
+ }), { signal }).then(() => ({}), (error) => ({ error })));
20051
+ },
20052
+ ask: async (request) => {
20053
+ await run(current.permissions.assert({
20054
+ sessionID: toolContext.sessionID,
20055
+ agent: toolContext.agent,
20056
+ action: request.permission,
20057
+ resources: request.patterns,
20058
+ save: request.always,
20059
+ metadata: request.metadata,
20060
+ source: {
20061
+ type: "tool",
20062
+ messageID: toolContext.messageID,
20063
+ id: toolContext.id
20064
+ }
20065
+ }), { signal });
20066
+ }
20067
+ };
20068
+ const result = await toolSignals.run(signal, () => definition.execute(input.parse(_input), context));
20069
+ const failed = (await Promise.all(progress)).find((entry) => entry.error !== undefined);
20070
+ if (failed)
20071
+ throw failed.error;
20072
+ if (typeof result === "string")
20073
+ return { content: result };
20074
+ return {
20075
+ content: [
20076
+ { type: "text", text: result.output },
20077
+ ...(result.attachments ?? []).map((attachment) => ({
20078
+ type: "file",
20079
+ uri: attachment.url,
20080
+ mime: attachment.mime,
20081
+ ...attachment.filename === undefined ? {} : { name: attachment.filename }
20082
+ }))
20083
+ ],
20084
+ metadata: {
20085
+ ...result.metadata,
20086
+ ...result.title === undefined ? {} : { title: result.title }
20087
+ }
20088
+ };
20089
+ }).pipe(Effect.mapError(() => new Tool.Error({ message: nativeHostMessages.toolFailed })));
20090
+ })
20091
+ });
20092
+ }
20093
+ });
20094
+ yield* ctx.tool.hook("execute.before", (event) => Effect.promise(() => hooks["tool.execute.before"]?.({ tool: event.tool, sessionID: event.sessionID, callID: event.id }, { args: event.input }) ?? Promise.resolve()));
20095
+ yield* ctx.tool.hook("execute.after", (event) => Effect.promise(() => hooks["tool.execute.after"]?.({
20096
+ tool: event.tool,
20097
+ sessionID: event.sessionID,
20098
+ callID: event.id,
20099
+ args: event.input
20100
+ }, { title: event.tool, output: "", metadata: {} }) ?? Promise.resolve()));
20101
+ yield* ctx.event.subscribe().pipe(Stream.runForEach((event) => Effect.tryPromise(async () => {
20102
+ const parsed = executionEvent.safeParse(event);
20103
+ if (!parsed.success)
20104
+ return;
20105
+ const value = parsed.data;
20106
+ if (value.location !== undefined) {
20107
+ if (!sameNativeWorkspace(value.location, ctx.location))
20108
+ return;
20109
+ } else if (value.type !== "session.deleted") {
20110
+ const info = await run(ctx.session.get({
20111
+ sessionID: Session2.ID.make(value.data.sessionID)
20112
+ }));
20113
+ if (!sameNativeWorkspace(info.location, ctx.location))
20114
+ return;
20115
+ } else
20116
+ return;
20117
+ const sessionID = value.data.sessionID;
20118
+ if (value.type === "session.deleted") {
20119
+ guard.finished(sessionID);
20120
+ systemPrompts.delete(sessionID);
20121
+ }
20122
+ let mapped;
20123
+ if (value.type === "session.created" || value.type === "session.deleted") {
20124
+ mapped = {
20125
+ type: value.type,
20126
+ properties: { info: { id: sessionID } }
20127
+ };
20128
+ } else if (value.type === "session.execution.started") {
20129
+ mapped = {
20130
+ type: "session.status",
20131
+ properties: { sessionID, status: { type: "busy" } }
20132
+ };
20133
+ } else if (value.type === "session.step.ended" || value.type === "session.step.failed") {
20134
+ if (bindings === undefined)
20135
+ return;
20136
+ const messages = await run(bindings.sessions.messages({
20137
+ sessionID: Session2.ID.make(sessionID),
20138
+ limit: 100
20139
+ }));
20140
+ const message = messages.find((candidate) => candidate.id === value.data.assistantMessageID);
20141
+ if (message?.type !== "assistant")
20142
+ return;
20143
+ mapped = {
20144
+ type: "message.updated",
20145
+ properties: {
20146
+ info: nativeAssistantResponse(sessionID, message).info
20147
+ }
20148
+ };
20149
+ } else {
20150
+ systemPrompts.delete(sessionID);
20151
+ guard.finished(sessionID);
20152
+ if (value.type !== "session.execution.succeeded")
20153
+ await hooks.event?.({
20154
+ event: { type: "session.error", properties: { sessionID } }
20155
+ });
20156
+ mapped = { type: "session.idle", properties: { sessionID } };
20157
+ }
20158
+ await hooks.event?.({ event: mapped });
20159
+ }).pipe(Effect.catch(() => Effect.logError(nativeHostMessages.eventCaptureFailed)))), Effect.tapCause(() => Effect.logError(nativeHostMessages.eventCaptureFailed)), Effect.forkScoped);
20160
+ });
20161
+ }
20162
+
18756
20163
  // src/plugin/orchestrateTool.ts
18757
20164
  import { tool as tool5 } from "@opencode-ai/plugin";
18758
- import { z as z30 } from "zod";
20165
+ import { z as z32 } from "zod";
18759
20166
 
18760
20167
  // src/orchestrator/coordinator.ts
18761
- import { z as z29 } from "zod";
20168
+ import { z as z31 } from "zod";
18762
20169
 
18763
20170
  // src/orchestrator/nodeIteration.ts
18764
20171
  var DEFAULT_MAX_RETRIES = 2;
@@ -19232,7 +20639,7 @@ function buildRecord(input, decision2, subsession, deps, decisionID, batchID) {
19232
20639
  }
19233
20640
  return record;
19234
20641
  }
19235
- var TelemetryFailureClassSchema = z29.enum([
20642
+ var TelemetryFailureClassSchema = z31.enum([
19236
20643
  "create-rejected",
19237
20644
  "create-no-id",
19238
20645
  "prompt-rejected",
@@ -19658,10 +21065,10 @@ function uniqueModels(models) {
19658
21065
  const seen = new Set;
19659
21066
  const unique = [];
19660
21067
  for (const model of models) {
19661
- const key = modelKey4(model);
19662
- if (seen.has(key))
21068
+ const key2 = modelKey4(model);
21069
+ if (seen.has(key2))
19663
21070
  continue;
19664
- seen.add(key);
21071
+ seen.add(key2);
19665
21072
  unique.push(model);
19666
21073
  }
19667
21074
  return unique;
@@ -20049,7 +21456,7 @@ async function runRoleTasks(inputs, deps, options = {}) {
20049
21456
  var _pluginWarnedRoles = new Set;
20050
21457
  var EMPTY_ROLE_ID_SET = new Set;
20051
21458
  var SAFE_ROLE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
20052
- var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
21459
+ var RoleIDSchema = z32.string().min(1).superRefine((roleID, ctx) => {
20053
21460
  if (!SAFE_ROLE_ID.test(roleID)) {
20054
21461
  ctx.addIssue({
20055
21462
  code: "custom",
@@ -20057,16 +21464,16 @@ var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
20057
21464
  });
20058
21465
  }
20059
21466
  });
20060
- var RoleAssignmentSchema = z30.object({
21467
+ var RoleAssignmentSchema = z32.object({
20061
21468
  roleID: RoleIDSchema.describe("Role identifier from the roster"),
20062
- prompt: z30.string().min(1).describe("Work prompt for this role"),
20063
- title: z30.string().min(1).optional().describe("Human-readable title"),
20064
- dependsOn: z30.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
21469
+ prompt: z32.string().min(1).describe("Work prompt for this role"),
21470
+ title: z32.string().min(1).optional().describe("Human-readable title"),
21471
+ dependsOn: z32.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
20065
21472
  }).strict();
20066
- var OrchestratePayloadSchema = z30.object({
20067
- assignments: z30.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
20068
- parentSessionID: z30.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
20069
- directory: z30.string().min(1).optional().describe("Working directory override")
21473
+ var OrchestratePayloadSchema = z32.object({
21474
+ assignments: z32.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
21475
+ parentSessionID: z32.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
21476
+ directory: z32.string().min(1).optional().describe("Working directory override")
20070
21477
  }).strict();
20071
21478
  function validateAssignmentDependencies(assignments) {
20072
21479
  const ids = new Set;
@@ -20127,14 +21534,14 @@ function validateAssignmentDependencies(assignments) {
20127
21534
  function isRecord7(value) {
20128
21535
  return typeof value === "object" && value !== null && !Array.isArray(value);
20129
21536
  }
20130
- function nestedRecord2(value, key) {
20131
- const nested = value[key];
21537
+ function nestedRecord2(value, key2) {
21538
+ const nested = value[key2];
20132
21539
  return isRecord7(nested) ? nested : undefined;
20133
21540
  }
20134
21541
  function numberField(records, keys) {
20135
21542
  for (const record of records) {
20136
- for (const key of keys) {
20137
- const value = record[key];
21543
+ for (const key2 of keys) {
21544
+ const value = record[key2];
20138
21545
  if (typeof value === "number" && Number.isInteger(value)) {
20139
21546
  return value;
20140
21547
  }
@@ -20144,8 +21551,8 @@ function numberField(records, keys) {
20144
21551
  }
20145
21552
  function booleanField(records, keys) {
20146
21553
  for (const record of records) {
20147
- for (const key of keys) {
20148
- const value = record[key];
21554
+ for (const key2 of keys) {
21555
+ const value = record[key2];
20149
21556
  if (typeof value === "boolean") {
20150
21557
  return value;
20151
21558
  }
@@ -20156,8 +21563,8 @@ function booleanField(records, keys) {
20156
21563
  function boundedErrorTokens(records) {
20157
21564
  const tokens = [];
20158
21565
  for (const record of records) {
20159
- for (const key of ["name", "code", "type", "message"]) {
20160
- const value = record[key];
21566
+ for (const key2 of ["name", "code", "type", "message"]) {
21567
+ const value = record[key2];
20161
21568
  if (typeof value === "string" && value.length <= 100) {
20162
21569
  tokens.push(value.toLowerCase());
20163
21570
  }
@@ -20695,6 +22102,8 @@ function createOrchestrateTool(deps) {
20695
22102
  }
20696
22103
  const parentOverrideIgnored = parsed.data.parentSessionID !== undefined && parsed.data.parentSessionID !== context.sessionID;
20697
22104
  parsed.data.parentSessionID = context.sessionID;
22105
+ if (parsed.data.directory === undefined && context.directory !== undefined)
22106
+ parsed.data.directory = context.directory;
20698
22107
  const depError = validateAssignmentDependencies(parsed.data.assignments);
20699
22108
  if (depError !== undefined) {
20700
22109
  return dependencyValidationError(depError.code, depError.detail);
@@ -20802,7 +22211,7 @@ ${advisory}`;
20802
22211
 
20803
22212
  // src/plugin/registerCastTool.ts
20804
22213
  import { tool as tool6 } from "@opencode-ai/plugin";
20805
- import { z as z31 } from "zod";
22214
+ import { z as z33 } from "zod";
20806
22215
 
20807
22216
  // src/messages/rosterSummary.ts
20808
22217
  var ROSTER_SUMMARY_HEADING = "Registered team model policies";
@@ -20918,12 +22327,12 @@ function createTeamPreparation(deps) {
20918
22327
  OpenTeamConfigSchema.parse(migrateConfig(baseRaw));
20919
22328
  const overlay = overlayBefore === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayBefore);
20920
22329
  const options = configOptionsFromPluginOptions(deps.options);
20921
- const resolve9 = (raw) => {
22330
+ const resolve11 = (raw) => {
20922
22331
  const config = validateConfigInput(TEAM_PREPARATION_CONFIG, mergeConfigInputs(mergeConfigInputs(raw, options), overlay));
20923
22332
  assertEffectiveConfigModelDomains(config);
20924
22333
  return config;
20925
22334
  };
20926
- const current = resolve9(baseRaw);
22335
+ const current = resolve11(baseRaw);
20927
22336
  const live = liveConfig ?? current;
20928
22337
  if (resolveExecutionMode({
20929
22338
  routerMode: current.router.executionMode,
@@ -20945,9 +22354,9 @@ function createTeamPreparation(deps) {
20945
22354
  ]))
20946
22355
  }
20947
22356
  });
20948
- const effective = resolve9(candidate);
22357
+ const effective = resolve11(candidate);
20949
22358
  const requestedProfiles = new Set(roster.entries.filter((entry) => !isKnownNonWorkerRole(entry.roleID) && getRoleProfile(entry.roleID, live.orchestrator?.roles) === undefined).map((entry) => getRoleProfile(entry.roleID, effective.orchestrator?.roles)));
20950
- const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key)));
22359
+ const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key2, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key2)));
20951
22360
  const hasAdditions = Object.keys(additions).length > 0;
20952
22361
  const next = hasAdditions ? validateConfigInput(TEAM_PREPARATION_CONFIG, {
20953
22362
  ...live,
@@ -21031,19 +22440,19 @@ async function persistPreparedTeam(storage, files) {
21031
22440
  }
21032
22441
 
21033
22442
  // src/plugin/registerCastTool.ts
21034
- var CastEntrySchema = z31.object({
21035
- roleID: z31.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
21036
- agentName: z31.string().min(1).describe("Themed character name assigned to the role")
22443
+ var CastEntrySchema = z33.object({
22444
+ roleID: z33.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
22445
+ agentName: z33.string().min(1).describe("Themed character name assigned to the role")
21037
22446
  }).strict();
21038
- var RegisterCastPayloadSchema = z31.object({
21039
- universe: z31.string().min(1).describe("Casting universe for this roster"),
21040
- entries: z31.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
22447
+ var RegisterCastPayloadSchema = z33.object({
22448
+ universe: z33.string().min(1).describe("Casting universe for this roster"),
22449
+ entries: z33.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
21041
22450
  }).strict();
21042
22451
  function offendingFields(error) {
21043
22452
  const fields = error.issues.flatMap((issue) => {
21044
22453
  const base = issue.path.map((segment) => String(segment));
21045
22454
  if (issue.code === "unrecognized_keys") {
21046
- return issue.keys.map((key) => [...base, key].join("."));
22455
+ return issue.keys.map((key2) => [...base, key2].join("."));
21047
22456
  }
21048
22457
  return [base.join(".") || "(root)"];
21049
22458
  });
@@ -21083,12 +22492,12 @@ function markdownCell(value) {
21083
22492
  }
21084
22493
  function modelPolicyLabel(policy) {
21085
22494
  const model = policy?.model;
21086
- const label = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
22495
+ const label2 = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
21087
22496
  if (policy?.fallbacks === undefined || policy.fallbacks.length === 0) {
21088
- return label;
22497
+ return label2;
21089
22498
  }
21090
22499
  const fallbacks = policy.fallbacks.map((fallback) => `${fallback.providerID}/${fallback.modelID}`);
21091
- return `${label}; ${rosterOrderedFallbacks(fallbacks)}`;
22500
+ return `${label2}; ${rosterOrderedFallbacks(fallbacks)}`;
21092
22501
  }
21093
22502
  function registeredTeamTable(roster, deps) {
21094
22503
  const rows = roster.entries.map((entry) => {
@@ -21157,7 +22566,7 @@ function createRegisterCastTool(deps) {
21157
22566
  }
21158
22567
 
21159
22568
  // src/plugin/reloadTool.ts
21160
- import { resolve as resolve9 } from "node:path";
22569
+ import { resolve as resolve11 } from "node:path";
21161
22570
  import { tool as tool7 } from "@opencode-ai/plugin/tool";
21162
22571
  var WAIT_MS = 30000;
21163
22572
  var SDK_TIMEOUT_MS = 5000;
@@ -21191,7 +22600,7 @@ function createReloadController(deps) {
21191
22600
  return () => clearTimeout(timer);
21192
22601
  }
21193
22602
  };
21194
- const directory = resolve9(deps.directory);
22603
+ const directory = resolve11(deps.directory);
21195
22604
  let pending;
21196
22605
  let authorizing;
21197
22606
  let closed = false;
@@ -21339,7 +22748,7 @@ function createReloadController(deps) {
21339
22748
  const parsed = schema.safeParse(args);
21340
22749
  if (!parsed.success)
21341
22750
  return reloadMessages.invalid;
21342
- if (context.agent !== "openteam" || resolve9(context.directory) !== directory || context.sessionID.trim().length === 0)
22751
+ if (context.agent !== "openteam" || resolve11(context.directory) !== directory || context.sessionID.trim().length === 0)
21343
22752
  return reloadMessages.denied;
21344
22753
  if (parsed.data.action === "cancel") {
21345
22754
  if ((pending?.sessionID ?? authorizing?.sessionID) !== context.sessionID)
@@ -21485,7 +22894,7 @@ function createSessionTracker() {
21485
22894
  }
21486
22895
 
21487
22896
  // src/plugin/toolcalls.ts
21488
- function key(sessionID, callID) {
22897
+ function key2(sessionID, callID) {
21489
22898
  return `${sessionID}\x00${callID}`;
21490
22899
  }
21491
22900
  function createToolcallTracker(deps) {
@@ -21493,10 +22902,10 @@ function createToolcallTracker(deps) {
21493
22902
  const started = new Map;
21494
22903
  return {
21495
22904
  before(input) {
21496
- started.set(key(input.sessionID, input.callID), now());
22905
+ started.set(key2(input.sessionID, input.callID), now());
21497
22906
  },
21498
22907
  after(input) {
21499
- const mapKey = key(input.sessionID, input.callID);
22908
+ const mapKey = key2(input.sessionID, input.callID);
21500
22909
  const start = started.get(mapKey);
21501
22910
  started.delete(mapKey);
21502
22911
  const end = now();
@@ -21569,6 +22978,13 @@ function createLocalEmbedder(request, deps) {
21569
22978
  return (texts) => embedTexts({ ...request, texts }, deps);
21570
22979
  }
21571
22980
 
22981
+ // src/messages/memoryRuntime.ts
22982
+ var memoryRuntimeMessages = {
22983
+ providerUnavailable: "[openteam] Semantic memory requires exactly one enabled local runtime matching the configured embeddings provider. No embedding request was sent.",
22984
+ unavailable: "[openteam] Semantic memory initialization failed; recall and memory injection are unavailable.",
22985
+ rejectedLines: (count) => `[openteam] memory log rejected ${count} line(s); semantic memory may be incomplete.`
22986
+ };
22987
+
21572
22988
  // src/storage/index/bunSqlite.ts
21573
22989
  async function createBunSqliteDatabase(path4) {
21574
22990
  const mod = await import("bun:sqlite");
@@ -21652,7 +23068,14 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
21652
23068
  if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
21653
23069
  return;
21654
23070
  }
21655
- const runtime = config.local.runtimes.find((rt) => rt.enabled && rt.baseURL !== undefined);
23071
+ const warn = deps.warn ?? console.warn;
23072
+ const configuredModel = semantic.embeddings.model;
23073
+ const candidates = configuredModel == null ? config.local.runtimes.filter((rt) => rt.enabled && rt.baseURL !== undefined) : config.local.runtimes.filter((rt) => rt.defaultModel.providerID === configuredModel.providerID);
23074
+ const runtime = candidates[0];
23075
+ if (configuredModel != null && (candidates.length !== 1 || runtime?.enabled !== true)) {
23076
+ warn(memoryRuntimeMessages.providerUnavailable);
23077
+ return;
23078
+ }
21656
23079
  if (runtime?.baseURL === undefined) {
21657
23080
  return;
21658
23081
  }
@@ -21661,15 +23084,22 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
21661
23084
  const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
21662
23085
  const fetchImpl = deps.fetch ?? globalThis.fetch;
21663
23086
  const now = deps.now ?? (() => Date.now());
21664
- const warn = deps.warn ?? console.warn;
23087
+ const kind = localRuntimeKind(runtime);
21665
23088
  try {
23089
+ const baseURL = kind === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL) : runtime.baseURL;
23090
+ const guardedFetch = runtimeRequiresModelResidency(kind) ? (kind === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch)({
23091
+ baseURL,
23092
+ fetch: fetchImpl,
23093
+ healthFetch: fetchImpl,
23094
+ modelIDs: new Set([modelID2])
23095
+ }) : fetchImpl;
21666
23096
  const db = await openDatabase(semantic.indexPath);
21667
23097
  const index = createMemoryIndex(db, warn);
21668
23098
  const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
21669
23099
  if (rebuildResult.rejectedLines > 0) {
21670
- warn(`[openteam] memory log rejected ${rebuildResult.rejectedLines} line(s); semantic memory may be incomplete.`);
23100
+ warn(memoryRuntimeMessages.rejectedLines(rebuildResult.rejectedLines));
21671
23101
  }
21672
- const embedder = makeEmbedder({ baseURL: runtime.baseURL, modelID: modelID2 }, { fetch: fetchImpl });
23102
+ const embedder = makeEmbedder({ baseURL, modelID: modelID2 }, { fetch: guardedFetch });
21673
23103
  await ensureMemoryEmbeddings({ index, embedder, model: modelID2, warn });
21674
23104
  return {
21675
23105
  index,
@@ -21679,12 +23109,13 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
21679
23109
  semantic
21680
23110
  };
21681
23111
  } catch {
23112
+ warn(memoryRuntimeMessages.unavailable);
21682
23113
  return;
21683
23114
  }
21684
23115
  }
21685
23116
 
21686
23117
  // src/storage/lifecycle/fsLifecycleJournal.ts
21687
- import { randomUUID as randomUUID3 } from "node:crypto";
23118
+ import { randomUUID as randomUUID4 } from "node:crypto";
21688
23119
  import {
21689
23120
  lstatSync as lstatSync3,
21690
23121
  mkdirSync as mkdirSync3,
@@ -21694,22 +23125,22 @@ import {
21694
23125
  statSync,
21695
23126
  unlinkSync as unlinkSync2
21696
23127
  } from "node:fs";
21697
- import { join as join15, resolve as resolve11 } from "node:path";
23128
+ import { join as join15, resolve as resolve13 } from "node:path";
21698
23129
  import { ZodError } from "zod";
21699
23130
 
21700
23131
  // src/storage/lifecycle/codec.ts
21701
- import { z as z32 } from "zod";
23132
+ import { z as z34 } from "zod";
21702
23133
  var LIFECYCLE_CODEC_VERSION = 1;
21703
23134
  var LIFECYCLE_GENESIS_DIGEST = sha256Hex("openteam/lifecycle-journal/genesis/v1");
21704
23135
  var NUL5 = "\x00";
21705
23136
  var NEWLINE2 = `
21706
23137
  `;
21707
- var frameSchema = z32.object({
21708
- v: z32.literal(LIFECYCLE_CODEC_VERSION),
21709
- seq: z32.number().int().nonnegative(),
21710
- prev: z32.string().min(1),
21711
- sum: z32.string().min(1),
21712
- event: z32.unknown()
23138
+ var frameSchema = z34.object({
23139
+ v: z34.literal(LIFECYCLE_CODEC_VERSION),
23140
+ seq: z34.number().int().nonnegative(),
23141
+ prev: z34.string().min(1),
23142
+ sum: z34.string().min(1),
23143
+ event: z34.unknown()
21713
23144
  }).strict();
21714
23145
  function canonical2(value) {
21715
23146
  if (Array.isArray(value)) {
@@ -21718,7 +23149,7 @@ function canonical2(value) {
21718
23149
  if (value === null || typeof value !== "object") {
21719
23150
  return JSON.stringify(value);
21720
23151
  }
21721
- return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key2, child]) => `${JSON.stringify(key2)}:${canonical2(child)}`).join(",")}}`;
23152
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key3, child]) => `${JSON.stringify(key3)}:${canonical2(child)}`).join(",")}}`;
21722
23153
  }
21723
23154
  function lifecycleFrameDigest(previousDigest, event) {
21724
23155
  return sha256Hex(`${previousDigest}${NUL5}${canonical2(event)}`);
@@ -21796,7 +23227,7 @@ function decodeLifecycleJournal(text) {
21796
23227
  }
21797
23228
 
21798
23229
  // src/storage/lifecycle/fsLifecycleSupport.ts
21799
- import { randomUUID as randomUUID2 } from "node:crypto";
23230
+ import { randomUUID as randomUUID3 } from "node:crypto";
21800
23231
  import {
21801
23232
  appendFileSync,
21802
23233
  closeSync as closeSync2,
@@ -21813,7 +23244,7 @@ import {
21813
23244
  unlinkSync,
21814
23245
  writeSync as writeSync2
21815
23246
  } from "node:fs";
21816
- import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve10 } from "node:path";
23247
+ import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve12 } from "node:path";
21817
23248
  var SAFE_RUN_ID = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?$/u;
21818
23249
 
21819
23250
  class LifecycleFsError extends Error {
@@ -21837,7 +23268,7 @@ function validateRunID(runID) {
21837
23268
  }
21838
23269
  function resolvedLifecycleRoot(root) {
21839
23270
  try {
21840
- const absolute = resolve10(root);
23271
+ const absolute = resolve12(root);
21841
23272
  mkdirSync2(absolute, { recursive: true });
21842
23273
  const rootStat = lstatSync2(absolute);
21843
23274
  if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
@@ -21880,7 +23311,7 @@ function assertExistingSafeRun(root, runID) {
21880
23311
  throw new LifecycleFsError("unavailable");
21881
23312
  }
21882
23313
  const child = relative3(root, real);
21883
- if (child === "" || child.startsWith("..") || resolve10(root, child) !== real || dirname7(real) !== root) {
23314
+ if (child === "" || child.startsWith("..") || resolve12(root, child) !== real || dirname7(real) !== root) {
21884
23315
  throw new LifecycleFsError("corrupt");
21885
23316
  }
21886
23317
  return real;
@@ -21929,7 +23360,7 @@ function appendDurable2(path4, content) {
21929
23360
  }
21930
23361
  }
21931
23362
  function writeDurableAtomically(path4, content) {
21932
- const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID2()}.next`);
23363
+ const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID3()}.next`);
21933
23364
  try {
21934
23365
  writeDurable2(temporary, content);
21935
23366
  renameSync(temporary, path4);
@@ -21952,7 +23383,7 @@ function readLockContents(path4) {
21952
23383
  }
21953
23384
  function withRunMutationLock(runDir, mutate) {
21954
23385
  const lockPath = join14(runDir, ".mutation-lock");
21955
- const lockToken = randomUUID2();
23386
+ const lockToken = randomUUID3();
21956
23387
  let descriptor;
21957
23388
  try {
21958
23389
  descriptor = openSync2(lockPath, "wx");
@@ -22021,7 +23452,7 @@ function parseJson(text, parse2) {
22021
23452
  }
22022
23453
  }
22023
23454
  function issueWriterToken() {
22024
- return randomUUID3().replaceAll("-", "");
23455
+ return randomUUID4().replaceAll("-", "");
22025
23456
  }
22026
23457
  function createLease(runID, writerToken, input) {
22027
23458
  return parseLifecycleWriterLease({
@@ -22049,8 +23480,8 @@ function readRun(runDir, runID) {
22049
23480
  if (metadataText === undefined || eventsText === undefined) {
22050
23481
  throw new LifecycleFsError("corrupt");
22051
23482
  }
22052
- const metadata2 = parseJson(metadataText, parseLifecycleRunMetadata);
22053
- if (metadata2.runID !== runID) {
23483
+ const metadata3 = parseJson(metadataText, parseLifecycleRunMetadata);
23484
+ if (metadata3.runID !== runID) {
22054
23485
  throw new LifecycleFsError("corrupt");
22055
23486
  }
22056
23487
  let decoded;
@@ -22065,10 +23496,10 @@ function readRun(runDir, runID) {
22065
23496
  }
22066
23497
  }
22067
23498
  const first = decoded.events[0];
22068
- if (first === undefined || first.type !== "run.started" || first.source !== metadata2.source || first.executionAuthority !== metadata2.executionAuthority || first.root.operationID !== metadata2.root.operationID || first.root.taskID !== metadata2.root.taskID) {
23499
+ if (first === undefined || first.type !== "run.started" || first.source !== metadata3.source || first.executionAuthority !== metadata3.executionAuthority || first.root.operationID !== metadata3.root.operationID || first.root.taskID !== metadata3.root.taskID) {
22069
23500
  throw new LifecycleFsError("corrupt");
22070
23501
  }
22071
- return { metadata: metadata2, decoded };
23502
+ return { metadata: metadata3, decoded };
22072
23503
  }
22073
23504
  function terminalOutcome(events) {
22074
23505
  const last = events.at(-1);
@@ -22120,7 +23551,7 @@ function canDeleteTerminalRun(runDir, runID, now) {
22120
23551
  return now >= lease.expiresAt;
22121
23552
  }
22122
23553
  var createFsLifecycleJournal = (options) => {
22123
- const configuredRoot = resolve11(options.root);
23554
+ const configuredRoot = resolve13(options.root);
22124
23555
  const now = options.now ?? Date.now;
22125
23556
  const mutateRun = options.withMutationLock ?? withRunMutationLock;
22126
23557
  return {
@@ -22142,11 +23573,11 @@ var createFsLifecycleJournal = (options) => {
22142
23573
  throw error;
22143
23574
  }
22144
23575
  }
22145
- stagingPath = join15(root, `.start-${runID}-${randomUUID3()}`);
23576
+ stagingPath = join15(root, `.start-${runID}-${randomUUID4()}`);
22146
23577
  mkdirSync3(stagingPath);
22147
23578
  const writerToken = issueWriterToken();
22148
23579
  const eventAt = now();
22149
- const metadata2 = parseLifecycleRunMetadata({
23580
+ const metadata3 = parseLifecycleRunMetadata({
22150
23581
  ...parsedInput,
22151
23582
  version: 1,
22152
23583
  createdAt: eventAt
@@ -22162,7 +23593,7 @@ var createFsLifecycleJournal = (options) => {
22162
23593
  executionAuthority: parsedInput.executionAuthority,
22163
23594
  root: parsedInput.root
22164
23595
  });
22165
- writeDurable2(join15(stagingPath, METADATA_FILE), `${JSON.stringify(metadata2)}
23596
+ writeDurable2(join15(stagingPath, METADATA_FILE), `${JSON.stringify(metadata3)}
22166
23597
  `);
22167
23598
  writeDurable2(join15(stagingPath, EVENTS_FILE), encodeLifecycleJournal([event]));
22168
23599
  writeDurable2(join15(stagingPath, OWNER_FILE2), `${JSON.stringify(lease)}
@@ -22178,7 +23609,7 @@ var createFsLifecycleJournal = (options) => {
22178
23609
  stagingPath = undefined;
22179
23610
  return ok({
22180
23611
  handle: { runID, writerToken },
22181
- metadata: metadata2,
23612
+ metadata: metadata3,
22182
23613
  event,
22183
23614
  lease,
22184
23615
  head: 1
@@ -22501,8 +23932,8 @@ async function initSpanEmit(connection, config) {
22501
23932
  const tracer = provider.getTracer(DEFAULT_SERVICE_NAME);
22502
23933
  return (record) => {
22503
23934
  const span = tracer.startSpan(record.name);
22504
- for (const [key2, value] of Object.entries(record.attributes)) {
22505
- span.setAttribute(key2, value);
23935
+ for (const [key3, value] of Object.entries(record.attributes)) {
23936
+ span.setAttribute(key3, value);
22506
23937
  }
22507
23938
  span.end();
22508
23939
  };
@@ -22620,7 +24051,8 @@ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
22620
24051
  var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
22621
24052
  async function readPackagedCertificate(path4) {
22622
24053
  try {
22623
- return await readFile5(new URL(`./certificates/${basename4(path4)}`, import.meta.url), "utf8");
24054
+ const hostDirectory = basename4(dirname8(path4)) === NATIVE_CERTIFICATE_DIRECTORY ? `${NATIVE_CERTIFICATE_DIRECTORY}/` : "";
24055
+ return await readFile5(new URL(`./certificates/${hostDirectory}${basename4(path4)}`, import.meta.url), "utf8");
22624
24056
  } catch {
22625
24057
  return;
22626
24058
  }
@@ -22752,8 +24184,8 @@ async function readOpencodeVersion(serverUrl, options = {}) {
22752
24184
  }
22753
24185
  var DEFAULT_OPENCODE_VERSION_BACKOFF_MS = [50, 250];
22754
24186
  function defaultSleep(ms) {
22755
- return new Promise((resolve12) => {
22756
- setTimeout(resolve12, ms);
24187
+ return new Promise((resolve14) => {
24188
+ setTimeout(resolve14, ms);
22757
24189
  });
22758
24190
  }
22759
24191
  function createLazyOpencodeVersionReader(serverUrl, options = {}) {
@@ -22796,9 +24228,10 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
22796
24228
  };
22797
24229
  }
22798
24230
  async function buildGateInput(config, storage, opencodeVersion, warn, packagedRead = readPackagedCertificate) {
22799
- const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
24231
+ const hostPath = (path4) => opencodeVersion === NATIVE_OPENCODE_VERSION ? `${dirname8(path4)}/${NATIVE_CERTIFICATE_DIRECTORY}/${basename4(path4)}` : path4;
24232
+ const shadow = await readAndParseCertificate(storage, hostPath(SHADOW_CERT_PATH), (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
22800
24233
  const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
22801
- const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
24234
+ const release = await readAndParseCertificate(storage, hostPath(RELEASE_CERT_PATH), (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
22802
24235
  let soakLedgerDigest = "0".repeat(64);
22803
24236
  try {
22804
24237
  const recorderPorts = recorderPortsFromStorage(storage);
@@ -23053,14 +24486,17 @@ async function listConfiguredOpencodeInventory(ctx) {
23053
24486
  profiles: normalizeModelsDevCatalog(Object.fromEntries(providers.map((provider) => [provider.id, provider]))).filter((profile) => profile.costPer1M.inputUSD > 0 && profile.costPer1M.outputUSD > 0)
23054
24487
  };
23055
24488
  }
23056
- var server = async (ctx, rawOptions) => {
24489
+ async function createPluginRuntime(ctx, rawOptions) {
23057
24490
  const now = () => Date.now();
23058
24491
  const diagnostics = createPluginDiagnosticChannel({
23059
24492
  client: ctx.client,
23060
24493
  directory: ctx.directory,
23061
24494
  now
23062
24495
  });
23063
- logOpencodeServerUrl(ctx.serverUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
24496
+ const initialServerUrl = ctx.serverUrl();
24497
+ if (initialServerUrl !== undefined) {
24498
+ logOpencodeServerUrl(initialServerUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
24499
+ }
23064
24500
  const storage = createFsStorageProvider(ctx.directory || ctx.worktree || process.cwd());
23065
24501
  const runtimeConfig = await loadRuntimeOpenTeamConfig(storage, rawOptions);
23066
24502
  const config = runtimeConfig.config;
@@ -23069,20 +24505,19 @@ var server = async (ctx, rawOptions) => {
23069
24505
  const registry = new RuntimeRegistry({
23070
24506
  fetch: globalThis.fetch,
23071
24507
  clock: () => new Date().toISOString(),
23072
- foundryDiscovery: foundryDiscoveryFromExec(createShellExec(ctx.$))
24508
+ foundryDiscovery: foundryDiscoveryFromExec(ctx.exec)
23073
24509
  });
23074
24510
  const cache = createAvailabilityCache({
23075
24511
  config,
23076
24512
  registry,
23077
- ...ctx.client.config?.providers === undefined ? {} : {
23078
- listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
23079
- },
24513
+ ...ctx.listConfiguredInventory === undefined ? {} : { listConfiguredInventory: ctx.listConfiguredInventory },
23080
24514
  onRefreshError: (error) => logAvailabilityRefreshError(error, diagnostics.warn("availability-refresh-failed", { toast: false }))
23081
24515
  });
23082
24516
  let availabilityReady;
23083
- const ensureAvailability = () => {
24517
+ const ensureAvailability = async () => {
23084
24518
  availabilityReady ??= cache.refresh();
23085
- return availabilityReady;
24519
+ await availabilityReady;
24520
+ await cache.waitForRefresh();
23086
24521
  };
23087
24522
  const capabilityProfiles = () => {
23088
24523
  const availableModelKeys = new Set(cache.get().filter((model) => model.available).map((model) => `${model.providerID}\x00${model.modelID}`));
@@ -23111,17 +24546,19 @@ var server = async (ctx, rawOptions) => {
23111
24546
  const injectMemory = await buildMemoryInjectorFromConfig(config, storage, diagnostics.warn("memory-runtime-warning", { toast: false }));
23112
24547
  const sessionTracker = createSessionTracker();
23113
24548
  const orchestratedModelOwnership = createOrchestratedModelOwnership();
23114
- const modelResidency = createModelResidencyGuard(config, {
24549
+ const resolveAgentRole = async (agentName) => {
24550
+ const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
24551
+ return entry?.roleID;
24552
+ };
24553
+ const residencyOptions = {
23115
24554
  fetch: globalThis.fetch,
23116
- ...ctx.client.config?.providers === undefined ? {} : {
24555
+ resolveAgentRole,
24556
+ ...ctx.listConfiguredInventory === undefined ? {} : {
23117
24557
  resolveApiModelID: cache.resolveApiModelID,
23118
24558
  modelInventoryReady: ensureAvailability
23119
24559
  }
23120
- });
23121
- const resolveAgentRole = async (agentName) => {
23122
- const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
23123
- return entry?.roleID;
23124
24560
  };
24561
+ const modelResidency = (ctx.createResidencyGuard ?? createModelResidencyGuard)(config, residencyOptions);
23125
24562
  const hooks = createHooks(config, cache.get, injectMemory === undefined ? {
23126
24563
  sink,
23127
24564
  modelResidency,
@@ -23140,10 +24577,12 @@ var server = async (ctx, rawOptions) => {
23140
24577
  resolveAgentRole,
23141
24578
  onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
23142
24579
  });
23143
- const getOpencodeVersion = createLazyOpencodeVersionReader(ctx.serverUrl, {
24580
+ const getOpencodeVersion = ctx.opencodeVersion ?? (initialServerUrl === undefined ? async () => {
24581
+ throw new Error(nativeHostMessages.missingVersion);
24582
+ } : createLazyOpencodeVersionReader(initialServerUrl, {
23144
24583
  client: ctx.client,
23145
24584
  onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
23146
- });
24585
+ }));
23147
24586
  const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions, async () => {
23148
24587
  const version = await getOpencodeVersion();
23149
24588
  return version === "unknown" ? undefined : version;
@@ -23154,12 +24593,15 @@ var server = async (ctx, rawOptions) => {
23154
24593
  if (sessionID === undefined || announcedSessions.has(sessionID)) {
23155
24594
  return;
23156
24595
  }
23157
- announcedSessions.add(sessionID);
23158
- const endpoint = sessionEndpointEventFrom(sessionID, ctx.serverUrl, now, {
24596
+ const serverUrl = ctx.serverUrl();
24597
+ if (serverUrl === undefined)
24598
+ return;
24599
+ const endpoint = sessionEndpointEventFrom(sessionID, serverUrl, now, {
23159
24600
  directory: ctx.directory,
23160
24601
  worktree: ctx.worktree
23161
24602
  });
23162
24603
  if (endpoint !== undefined) {
24604
+ announcedSessions.add(sessionID);
23163
24605
  sink.emit(endpoint);
23164
24606
  }
23165
24607
  };
@@ -23211,7 +24653,7 @@ var server = async (ctx, rawOptions) => {
23211
24653
  holder: "openteam-plugin",
23212
24654
  lifecycleSource: "plugin",
23213
24655
  lifecycleRecorder,
23214
- exec: createShellExec(ctx.$),
24656
+ exec: ctx.exec,
23215
24657
  repoRoot: ctx.directory
23216
24658
  });
23217
24659
  const graphSurface = createGraphTool({
@@ -23247,8 +24689,8 @@ var server = async (ctx, rawOptions) => {
23247
24689
  rememberReportedSlots(snapshots);
23248
24690
  return reachableRuntimeIds(snapshots);
23249
24691
  };
23250
- const runtimeById = (key2) => {
23251
- const runtimeId = slotPoolRuntimeID(key2);
24692
+ const runtimeById = (key3) => {
24693
+ const runtimeId = slotPoolRuntimeID(key3);
23252
24694
  const runtime = config.local.runtimes.find((candidate) => candidate.id === runtimeId);
23253
24695
  if (runtime === undefined) {
23254
24696
  return;
@@ -23256,13 +24698,13 @@ var server = async (ctx, rawOptions) => {
23256
24698
  return withReportedModelSlots(runtime, reportedSlotsByRuntime.get(runtimeId), localModelAliases(runtime.defaultModel.providerID, cache.get()));
23257
24699
  };
23258
24700
  const runtimeLimiter = createRuntimeConcurrencyLimiter({
23259
- capacityFor: (key2) => {
23260
- const runtime = runtimeById(key2);
23261
- return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key2));
24701
+ capacityFor: (key3) => {
24702
+ const runtime = runtimeById(key3);
24703
+ return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key3));
23262
24704
  },
23263
- reservedInteractiveFor: (key2) => {
23264
- const runtime = runtimeById(key2);
23265
- return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key2));
24705
+ reservedInteractiveFor: (key3) => {
24706
+ const runtime = runtimeById(key3);
24707
+ return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key3));
23266
24708
  },
23267
24709
  now
23268
24710
  });
@@ -23289,8 +24731,8 @@ var server = async (ctx, rawOptions) => {
23289
24731
  lifecycleRecorder,
23290
24732
  abortGraceMs: DEFAULT_ABORT_GRACE_MS,
23291
24733
  statusPollMs: DEFAULT_STATUS_POLL_MS,
23292
- delay: (ms) => new Promise((resolve12) => {
23293
- setTimeout(resolve12, ms);
24734
+ delay: (ms) => new Promise((resolve14) => {
24735
+ setTimeout(resolve14, ms);
23294
24736
  }),
23295
24737
  warn: diagnostics.warn("unknown-role")
23296
24738
  });
@@ -23299,8 +24741,22 @@ var server = async (ctx, rawOptions) => {
23299
24741
  storage,
23300
24742
  now,
23301
24743
  localRuntimeReachable: async (runtime, signal) => {
24744
+ if (localRuntimeKind(runtime) === "llama-swap") {
24745
+ if (ctx.listConfiguredInventory !== undefined)
24746
+ await ensureAvailability();
24747
+ signal?.throwIfAborted();
24748
+ const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
24749
+ const residency = await readLlamaSwapResidency(target.baseURL, globalThis.fetch, {
24750
+ ...signal === undefined ? {} : { signal }
24751
+ });
24752
+ if (!residency.has(target.modelID))
24753
+ throw new ModelResidencyError("identity", "llama-swap");
24754
+ if (residency.get(target.modelID)?.loaded !== true)
24755
+ throw new ModelResidencyError("inactive", "llama-swap");
24756
+ return true;
24757
+ }
23302
24758
  if (localRuntimeKind(runtime) === "lemonade") {
23303
- if (ctx.client.config?.providers !== undefined)
24759
+ if (ctx.listConfiguredInventory !== undefined)
23304
24760
  await ensureAvailability();
23305
24761
  signal?.throwIfAborted();
23306
24762
  const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
@@ -23315,7 +24771,7 @@ var server = async (ctx, rawOptions) => {
23315
24771
  return anyRuntimeReachable(snapshots);
23316
24772
  },
23317
24773
  resolveExtractionTarget: async (model) => {
23318
- if (ctx.client.config?.providers !== undefined)
24774
+ if (ctx.listConfiguredInventory !== undefined)
23319
24775
  await ensureAvailability();
23320
24776
  return modelResidency.localTarget(model);
23321
24777
  },
@@ -23343,6 +24799,7 @@ var server = async (ctx, rawOptions) => {
23343
24799
  "chat.message": async (input, output) => {
23344
24800
  reload.userTurn(input.sessionID);
23345
24801
  await hooks["chat.message"]?.(input, output);
24802
+ announceEndpoint(input.sessionID);
23346
24803
  },
23347
24804
  dispose: async () => {
23348
24805
  reload.dispose();
@@ -23350,7 +24807,7 @@ var server = async (ctx, rawOptions) => {
23350
24807
  },
23351
24808
  tool: {
23352
24809
  openteam: createCommandTool(cliDeps),
23353
- "openteam-console": createConsoleTool(),
24810
+ "openteam-console": createConsoleTool(ctx.consoleEnvironment === undefined ? {} : { environment: ctx.consoleEnvironment }),
23354
24811
  "openteam-graph": graphSurface.definition,
23355
24812
  "openteam-orchestrate": orchestrateTool,
23356
24813
  "openteam-memory": memoryTool,
@@ -23387,10 +24844,21 @@ var server = async (ctx, rawOptions) => {
23387
24844
  }
23388
24845
  }
23389
24846
  };
23390
- };
24847
+ }
24848
+ var server = (ctx, rawOptions) => createPluginRuntime({
24849
+ directory: ctx.directory,
24850
+ worktree: ctx.worktree,
24851
+ client: ctx.client,
24852
+ exec: createShellExec(ctx.$),
24853
+ serverUrl: () => ctx.serverUrl,
24854
+ ...ctx.client.config?.providers === undefined ? {} : {
24855
+ listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
24856
+ }
24857
+ }, rawOptions);
23391
24858
  var plugin = {
23392
24859
  id: "openteam",
23393
- server
24860
+ server,
24861
+ effect: createNativePluginEffect(createPluginRuntime)
23394
24862
  };
23395
24863
  var src_default = plugin;
23396
24864
  export {
@@ -23408,6 +24876,7 @@ export {
23408
24876
  isMissingFile2 as isMissingFile,
23409
24877
  evaluateCutoverGateForBoundary,
23410
24878
  src_default as default,
24879
+ createPluginRuntime,
23411
24880
  createOpencodeVersionReadFailureLogger,
23412
24881
  createLazyOpencodeVersionReader,
23413
24882
  createEventSink,