@jmanuelcorral/openteam 0.26.0 → 0.27.1

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 (72) hide show
  1. package/README.es.md +11 -8
  2. package/README.md +11 -8
  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/opencodeVersion.d.ts.map +1 -1
  11. package/dist/cli.js +21893 -12535
  12. package/dist/commands/setup.d.ts +2 -0
  13. package/dist/commands/setup.d.ts.map +1 -1
  14. package/dist/commands/upgrade.d.ts +1 -1
  15. package/dist/commands/upgrade.d.ts.map +1 -1
  16. package/dist/config/fieldProjection.d.ts +3 -0
  17. package/dist/config/fieldProjection.d.ts.map +1 -0
  18. package/dist/config/graphFeatureGate.d.ts +3 -3
  19. package/dist/config/nativeOpencode.d.ts +5 -0
  20. package/dist/config/nativeOpencode.d.ts.map +1 -0
  21. package/dist/console/auth.d.ts +5 -0
  22. package/dist/console/auth.d.ts.map +1 -0
  23. package/dist/console/conversation.d.ts +8 -1
  24. package/dist/console/conversation.d.ts.map +1 -1
  25. package/dist/console/historyBoundary.d.ts +1 -1
  26. package/dist/console/historyBoundary.d.ts.map +1 -1
  27. package/dist/console/nativeConversation.d.ts +7 -0
  28. package/dist/console/nativeConversation.d.ts.map +1 -0
  29. package/dist/console/opencodeClient.d.ts +3 -0
  30. package/dist/console/opencodeClient.d.ts.map +1 -1
  31. package/dist/console/pty.d.ts +4 -0
  32. package/dist/console/pty.d.ts.map +1 -1
  33. package/dist/contract/opencode.d.ts +21 -14
  34. package/dist/contract/opencode.d.ts.map +1 -1
  35. package/dist/graph/certificate.d.ts.map +1 -1
  36. package/dist/index.d.ts +10 -5
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1174 -209
  39. package/dist/messages/consoleDetail.d.ts +3 -0
  40. package/dist/messages/consoleDetail.d.ts.map +1 -1
  41. package/dist/messages/nativeHost.d.ts +22 -0
  42. package/dist/messages/nativeHost.d.ts.map +1 -0
  43. package/dist/plugin/availability.d.ts +1 -0
  44. package/dist/plugin/availability.d.ts.map +1 -1
  45. package/dist/plugin/consoleTool.d.ts +1 -0
  46. package/dist/plugin/consoleTool.d.ts.map +1 -1
  47. package/dist/plugin/hooks.d.ts +3 -3
  48. package/dist/plugin/hooks.d.ts.map +1 -1
  49. package/dist/plugin/modelResidency.d.ts +1 -0
  50. package/dist/plugin/modelResidency.d.ts.map +1 -1
  51. package/dist/plugin/nativeHost.d.ts +5 -0
  52. package/dist/plugin/nativeHost.d.ts.map +1 -0
  53. package/dist/plugin/nativeHostContext.d.ts +228 -0
  54. package/dist/plugin/nativeHostContext.d.ts.map +1 -0
  55. package/dist/plugin/nativeInventory.d.ts +5 -0
  56. package/dist/plugin/nativeInventory.d.ts.map +1 -0
  57. package/dist/plugin/nativeResidency.d.ts +30 -0
  58. package/dist/plugin/nativeResidency.d.ts.map +1 -0
  59. package/dist/plugin/nativeSessions.d.ts +131 -0
  60. package/dist/plugin/nativeSessions.d.ts.map +1 -0
  61. package/dist/plugin/orchestrateTool.d.ts.map +1 -1
  62. package/dist/plugin/reloadTool.d.ts +3 -2
  63. package/dist/plugin/reloadTool.d.ts.map +1 -1
  64. package/dist/plugin/runtimeContext.d.ts +42 -0
  65. package/dist/plugin/runtimeContext.d.ts.map +1 -0
  66. package/dist/web/console.d.ts +1 -1
  67. package/dist/web/console.d.ts.map +1 -1
  68. package/dist/web/server.d.ts +1 -1
  69. package/dist/web/server.d.ts.map +1 -1
  70. package/dist/web/start.d.ts +1 -1
  71. package/dist/web/start.d.ts.map +1 -1
  72. 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.",
@@ -5757,7 +5760,7 @@ import { z as z15 } from "zod";
5757
5760
  // package.json
5758
5761
  var package_default = {
5759
5762
  name: "@jmanuelcorral/openteam",
5760
- version: "0.26.0",
5763
+ version: "0.27.1",
5761
5764
  packageManager: "bun@1.3.14",
5762
5765
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
5763
5766
  license: "MIT",
@@ -5817,12 +5820,13 @@ var package_default = {
5817
5820
  scripts: {
5818
5821
  prebuild: "bun run clean",
5819
5822
  build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
5820
- "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",
5821
- "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',
5822
5825
  "build:types": "tsc -p tsconfig.build.json",
5823
5826
  clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
5824
5827
  test: "bun test",
5825
5828
  "test:cov": "bun test --coverage",
5829
+ "verify:opencode-native": "bun scripts/verify-opencode-native.mjs",
5826
5830
  "certify:shadow": "bun run scripts/certify-artifact.ts shadow",
5827
5831
  "certify:release": "bun run scripts/certify-artifact.ts release",
5828
5832
  "build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
@@ -5844,10 +5848,12 @@ var package_default = {
5844
5848
  "@langchain/langgraph": "1.4.12",
5845
5849
  "@opencode-ai/plugin": "1.18.19",
5846
5850
  "@opencode-ai/sdk": "1.18.19",
5851
+ "@opencode/schema": "2.0.3",
5847
5852
  "@opentelemetry/api": "1.9.1",
5848
5853
  "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
5849
5854
  "@opentelemetry/resources": "2.10.0",
5850
5855
  "@opentelemetry/sdk-trace-base": "2.10.0",
5856
+ effect: "4.0.0-rc.112",
5851
5857
  zod: "4.4.3"
5852
5858
  },
5853
5859
  devDependencies: {
@@ -6363,7 +6369,7 @@ var NPM_LATEST_URL = `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/latest`;
6363
6369
  var FETCH_TIMEOUT_MS = 5000;
6364
6370
  var MAX_RESPONSE_BYTES = 64 * 1024;
6365
6371
  var MAX_FETCH_ATTEMPTS = 2;
6366
- var PLUGIN_KEY = "plugin";
6372
+ var PLUGIN_KEYS = ["plugin", "plugins"];
6367
6373
  var PRIVATE_DIRECTORY_MODE = 448;
6368
6374
  var PRIVATE_FILE_MODE = 384;
6369
6375
  var UPGRADE_BACKUP_GITIGNORE_CONTENT = `*
@@ -6417,6 +6423,8 @@ function extractSpecString(entry) {
6417
6423
  if (isTuplePluginEntry(entry)) {
6418
6424
  return entry[0];
6419
6425
  }
6426
+ if (isRecord3(entry) && typeof entry.package === "string")
6427
+ return entry.package;
6420
6428
  return;
6421
6429
  }
6422
6430
  function isLocalFileSpec(spec) {
@@ -6629,7 +6637,7 @@ function readJsoncValueEnd(src, start, limit) {
6629
6637
  }
6630
6638
  return i > start ? i : undefined;
6631
6639
  }
6632
- function findTopLevelPluginArray(raw) {
6640
+ function findTopLevelPluginArray(raw, pluginKey) {
6633
6641
  let found;
6634
6642
  let i = 0;
6635
6643
  let depth = 0;
@@ -6676,7 +6684,7 @@ function findTopLevelPluginArray(raw) {
6676
6684
  return;
6677
6685
  }
6678
6686
  i = token.end;
6679
- if (key !== PLUGIN_KEY || depth !== 1) {
6687
+ if (key !== pluginKey || depth !== 1) {
6680
6688
  continue;
6681
6689
  }
6682
6690
  i = skipTrivia(raw, i);
@@ -6742,6 +6750,33 @@ function findEntrySpecStringSpan(raw, element, entry, expectedSpec) {
6742
6750
  const span2 = { start: element.start, end: token2.end };
6743
6751
  return readJsonStringValue(raw, span2) === expectedSpec ? span2 : undefined;
6744
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
+ }
6745
6780
  if (!isTuplePluginEntry(entry)) {
6746
6781
  return;
6747
6782
  }
@@ -6769,20 +6804,23 @@ function validateEffectivePluginUpdate(path, nextContent, expectedManagedEntries
6769
6804
  if (!parsed.ok) {
6770
6805
  return parsed.error;
6771
6806
  }
6772
- const pluginValue = parsed.value[PLUGIN_KEY];
6773
- if (!Array.isArray(pluginValue)) {
6774
- return upgradeMessages.effectivePluginUpdateMismatch(path);
6775
- }
6776
6807
  let managedEntries = 0;
6777
6808
  let updatedEntries = 0;
6778
- for (const entry of pluginValue) {
6779
- const spec = extractSpecString(entry);
6780
- if (spec === undefined || !isOurSpec(spec)) {
6809
+ for (const key of PLUGIN_KEYS) {
6810
+ const pluginValue = parsed.value[key];
6811
+ if (pluginValue === undefined)
6781
6812
  continue;
6782
- }
6783
- managedEntries += 1;
6784
- if (spec === nextSpec) {
6785
- 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
+ }
6786
6824
  }
6787
6825
  }
6788
6826
  return managedEntries === expectedManagedEntries && updatedEntries === expectedManagedEntries ? undefined : upgradeMessages.effectivePluginUpdateMismatch(path);
@@ -7043,70 +7081,52 @@ async function analyseConfigFiles(paths, port) {
7043
7081
  parseError: parsed.error
7044
7082
  };
7045
7083
  }
7046
- const pluginValue = parsed.value[PLUGIN_KEY];
7047
- if (pluginValue === undefined) {
7048
- continue;
7049
- }
7050
- if (!Array.isArray(pluginValue)) {
7051
- return {
7052
- files,
7053
- foundConfigFile,
7054
- localOpenteamEntries,
7055
- parseError: upgradeMessages.pluginFieldMustBeArray(path)
7056
- };
7057
- }
7058
- const managedCandidates = [];
7059
- for (let index = 0;index < pluginValue.length; index += 1) {
7060
- const entry = pluginValue[index];
7061
- const spec = extractSpecString(entry);
7062
- if (spec === undefined) {
7084
+ const fileReplacements = [];
7085
+ for (const pluginKey of PLUGIN_KEYS) {
7086
+ const pluginValue = parsed.value[pluginKey];
7087
+ if (pluginValue === undefined) {
7063
7088
  continue;
7064
7089
  }
7065
- if (isLocalFileSpec(spec)) {
7066
- const localKind = await port.classifyLocalPluginSpec({
7067
- 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,
7068
7122
  spec
7069
7123
  });
7070
- if (localKind === "ours") {
7071
- localOpenteamEntries.push({ path, spec });
7072
- }
7073
- continue;
7074
7124
  }
7075
- if (!isOurSpec(spec)) {
7125
+ if (managedCandidates.length === 0) {
7076
7126
  continue;
7077
7127
  }
7078
- managedCandidates.push({
7079
- currentVersion: currentPinnedVersion(spec),
7080
- entry,
7081
- index,
7082
- spec
7083
- });
7084
- }
7085
- if (managedCandidates.length === 0) {
7086
- continue;
7087
- }
7088
- const pluginArray = findTopLevelPluginArray(content);
7089
- if (pluginArray === undefined) {
7090
- return {
7091
- files,
7092
- foundConfigFile,
7093
- localOpenteamEntries,
7094
- parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7095
- };
7096
- }
7097
- const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
7098
- if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
7099
- return {
7100
- files,
7101
- foundConfigFile,
7102
- localOpenteamEntries,
7103
- parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7104
- };
7105
- }
7106
- const replacements = [];
7107
- for (const candidate of managedCandidates) {
7108
- const elementSpan = elementSpans[candidate.index];
7109
- if (elementSpan === undefined) {
7128
+ const pluginArray = findTopLevelPluginArray(content, pluginKey);
7129
+ if (pluginArray === undefined) {
7110
7130
  return {
7111
7131
  files,
7112
7132
  foundConfigFile,
@@ -7114,8 +7134,8 @@ async function analyseConfigFiles(paths, port) {
7114
7134
  parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7115
7135
  };
7116
7136
  }
7117
- const specSpan = findEntrySpecStringSpan(content, elementSpan, candidate.entry, candidate.spec);
7118
- if (specSpan === undefined) {
7137
+ const elementSpans = scanTopLevelArrayElements(content, pluginArray.start, pluginArray.end);
7138
+ if (elementSpans === undefined || elementSpans.length !== pluginValue.length) {
7119
7139
  return {
7120
7140
  files,
7121
7141
  foundConfigFile,
@@ -7123,16 +7143,40 @@ async function analyseConfigFiles(paths, port) {
7123
7143
  parseError: upgradeMessages.pluginArrayEditUnavailable(path)
7124
7144
  };
7125
7145
  }
7126
- replacements.push({
7127
- ...specSpan,
7128
- currentVersion: candidate.currentVersion
7129
- });
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);
7130
7172
  }
7173
+ if (fileReplacements.length === 0)
7174
+ continue;
7131
7175
  files.push({
7132
- currentVersions: replacements.map((replacement) => replacement.currentVersion),
7176
+ currentVersions: fileReplacements.map((replacement) => replacement.currentVersion),
7133
7177
  path,
7134
7178
  originalContent: content,
7135
- replacements
7179
+ replacements: fileReplacements
7136
7180
  });
7137
7181
  }
7138
7182
  return {
@@ -7860,15 +7904,14 @@ function mergeOpencodeConfigs(base, override) {
7860
7904
  return result;
7861
7905
  }
7862
7906
 
7863
- // src/graph/certificate.ts
7864
- import { z as z17 } from "zod";
7865
-
7866
7907
  // src/contract/opencode.ts
7867
7908
  var SUPPORTED_OPENCODE_VERSIONS = [
7868
7909
  "1.17.13",
7869
7910
  "1.18.18",
7870
7911
  "1.18.19"
7871
7912
  ];
7913
+ var NATIVE_OPENCODE_VERSION = "2.0.5";
7914
+ var NATIVE_CERTIFICATE_DIRECTORY = `opencode-${NATIVE_OPENCODE_VERSION}`;
7872
7915
  var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
7873
7916
  function parseSemver(version) {
7874
7917
  const normalized = version.trim().replace(/^[=^~v]+/, "");
@@ -7882,10 +7925,31 @@ function parseSemver(version) {
7882
7925
  patch: Number(match[3])
7883
7926
  };
7884
7927
  }
7928
+ function compareSemver2(a, b) {
7929
+ if (a.major !== b.major) {
7930
+ return a.major < b.major ? -1 : 1;
7931
+ }
7932
+ if (a.minor !== b.minor) {
7933
+ return a.minor < b.minor ? -1 : 1;
7934
+ }
7935
+ if (a.patch !== b.patch) {
7936
+ return a.patch < b.patch ? -1 : 1;
7937
+ }
7938
+ return 0;
7939
+ }
7940
+ function isSupportedNativeOpencodeVersion(version) {
7941
+ if (version === undefined || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version))
7942
+ return false;
7943
+ const live = parseSemver(version);
7944
+ const minimum = parseSemver(NATIVE_OPENCODE_VERSION);
7945
+ return Object.values(live).every(Number.isSafeInteger) && live.major === minimum.major && compareSemver2(live, minimum) >= 0;
7946
+ }
7885
7947
  function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
7886
7948
  if (certifiedVersion === liveVersion) {
7887
7949
  return true;
7888
7950
  }
7951
+ if (isSupportedNativeOpencodeVersion(certifiedVersion) && isSupportedNativeOpencodeVersion(liveVersion))
7952
+ return compareSemver2(parseSemver(liveVersion), parseSemver(certifiedVersion)) >= 0;
7889
7953
  let certified;
7890
7954
  let live;
7891
7955
  try {
@@ -7894,12 +7958,38 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
7894
7958
  } catch {
7895
7959
  return false;
7896
7960
  }
7897
- if (certified.major !== live.major || certified.minor !== live.minor) {
7961
+ if (certified.major !== 1 || certified.major !== live.major || certified.minor !== live.minor) {
7898
7962
  return false;
7899
7963
  }
7900
7964
  return live.patch >= certified.patch;
7901
7965
  }
7902
7966
 
7967
+ // src/graph/certificate.ts
7968
+ import { z as z17 } from "zod";
7969
+
7970
+ // src/messages/nativeHost.ts
7971
+ var nativeHostMessages = {
7972
+ invalidNativeConfig: "The existing native opencode plugins/providers configuration cannot be safely merged. Correct its object and plugin declaration shapes before running setup.",
7973
+ 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.`,
7974
+ setupPolicyPreserved: "Setup stopped. Provider-denial rules were preserved and no files were written.",
7975
+ setupPolicyNotice: "Only the explicitly reviewed project rules were removed. Wildcard, scoped, unrelated and global provider denials remain authoritative.",
7976
+ unsupportedVersion: (version) => `openteam does not support native opencode ${version}. The supported stable native range is >=2.0.5 <3.0.0.`,
7977
+ certificateVersionMismatch: (certified, live) => `certified for "${certified}", running "${live}" — evidence is outside the supported host compatibility range`,
7978
+ missingBindings: "The native opencode execution context is unavailable. No child session or privileged operation was started.",
7979
+ missingVersion: "The opencode host did not provide an authoritative version or server endpoint.",
7980
+ workspaceMismatch: "The native opencode session does not belong to this workspace.",
7981
+ noCompletedResponse: "The native opencode session stopped without a completed assistant response.",
7982
+ executionFailed: "The native opencode session failed.",
7983
+ executionInterrupted: "The native opencode session was interrupted.",
7984
+ unknownAgent: "The native opencode session has no available agent.",
7985
+ noAvailableModel: "The native opencode host has no model available in the configured execution domain.",
7986
+ executionDomainBlocked: "The native opencode request is outside the configured execution domain.",
7987
+ transportUnavailable: "This local runtime requires the guarded @ai-sdk/openai-compatible transport. Native and WebSocket transports are not admitted.",
7988
+ missingAdmission: "The native local request has no matching model authorization. No inference was sent.",
7989
+ toolFailed: "The openteam tool failed in the native opencode host.",
7990
+ eventCaptureFailed: "Native opencode event capture failed. The console may be missing activity; reload the plugin after resolving the host error."
7991
+ };
7992
+
7903
7993
  // src/graph/soakLedger.ts
7904
7994
  import { z as z16 } from "zod";
7905
7995
  var Sha256Schema2 = z16.string().regex(/^[0-9a-f]{64}$/);
@@ -8158,7 +8248,7 @@ function parseShadowCertificate(value, opencodeVersion) {
8158
8248
  throw new ShadowCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
8159
8249
  }
8160
8250
  if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
8161
- throw new ShadowCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
8251
+ throw new ShadowCertificateError("opencode-mismatch", nativeHostMessages.certificateVersionMismatch(certificate.opencodeVersion, opencodeVersion));
8162
8252
  }
8163
8253
  return certificate;
8164
8254
  }
@@ -8343,7 +8433,7 @@ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
8343
8433
  throw new ReleaseCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
8344
8434
  }
8345
8435
  if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
8346
- throw new ReleaseCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
8436
+ throw new ReleaseCertificateError("opencode-mismatch", nativeHostMessages.certificateVersionMismatch(certificate.opencodeVersion, opencodeVersion));
8347
8437
  }
8348
8438
  return certificate;
8349
8439
  }
@@ -11967,6 +12057,10 @@ function createAvailabilityCache(options) {
11967
12057
  ref: { ...profile.ref },
11968
12058
  costPer1M: { ...profile.costPer1M }
11969
12059
  })),
12060
+ waitForRefresh: async () => {
12061
+ while (inFlight !== undefined)
12062
+ await inFlight;
12063
+ },
11970
12064
  refresh: () => {
11971
12065
  if (inFlight === undefined) {
11972
12066
  cached = cached.map((model) => requiresModelResidency(options.config, model) ? { ...model, available: false } : model);
@@ -13801,6 +13895,16 @@ function renderMigration(report) {
13801
13895
  // src/commands/purge.ts
13802
13896
  import * as path3 from "node:path";
13803
13897
 
13898
+ // src/config/fieldProjection.ts
13899
+ function only(keys) {
13900
+ return (value) => {
13901
+ if (typeof value !== "object" || value === null || Array.isArray(value))
13902
+ return value;
13903
+ const record = value;
13904
+ return Object.fromEntries(keys.filter((key) => Object.hasOwn(record, key)).map((key) => [key, record[key]]));
13905
+ };
13906
+ }
13907
+
13804
13908
  // src/storage/graph/soakRecorder.ts
13805
13909
  var SOAK_EVIDENCE_DIR = ".opencode/soak";
13806
13910
  var SOAK_RECORDER_BASE_DIR = ".opencode";
@@ -15595,7 +15699,7 @@ function createCommandTool(deps) {
15595
15699
  // src/plugin/consoleTool.ts
15596
15700
  import { spawn as nodeSpawn } from "node:child_process";
15597
15701
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
15598
- import { mkdir as mkdir4, open as open3 } from "node:fs/promises";
15702
+ import { mkdir as mkdir4, open as open3, stat as stat3 } from "node:fs/promises";
15599
15703
  import { dirname as dirname6, join as join13, resolve as resolve8 } from "node:path";
15600
15704
  import { fileURLToPath as fileURLToPath2 } from "node:url";
15601
15705
  import { tool as tool2 } from "@opencode-ai/plugin";
@@ -15605,8 +15709,8 @@ import {
15605
15709
  mkdir as mkdir3,
15606
15710
  open as open2,
15607
15711
  readFile as readFile4,
15608
- unlink,
15609
- writeFile as writeFile2
15712
+ rename as rename2,
15713
+ unlink
15610
15714
  } from "node:fs/promises";
15611
15715
  import { dirname as dirname5, join as join12, resolve as resolve7 } from "node:path";
15612
15716
  import { z as z22 } from "zod";
@@ -15906,10 +16010,17 @@ async function acquireLaunchLockOrWait(workspaceRoot, paths, launchId, deps) {
15906
16010
  currentLock = await readConsoleLaunchLock(paths.launchLockPath);
15907
16011
  } catch (error) {
15908
16012
  if (error instanceof ConsoleRuntimeStateFileError) {
15909
- await removeConsoleLaunchLock(paths.launchLockPath);
15910
- const retry = await acquireConsoleLaunchLock(paths.launchLockPath, attemptLock);
15911
- if (retry !== "busy") {
15912
- return { kind: "acquired", release: retry.release };
16013
+ const info = await stat3(paths.launchLockPath).catch((error2) => {
16014
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
16015
+ return;
16016
+ throw error2;
16017
+ });
16018
+ if (info === undefined || deps.now() - info.mtimeMs > LAUNCH_LOCK_STALE_MS) {
16019
+ await removeConsoleLaunchLock(paths.launchLockPath);
16020
+ const retry = await acquireConsoleLaunchLock(paths.launchLockPath, attemptLock);
16021
+ if (retry !== "busy") {
16022
+ return { kind: "acquired", release: retry.release };
16023
+ }
15913
16024
  }
15914
16025
  } else {
15915
16026
  throw error;
@@ -15982,7 +16093,8 @@ async function spawnDetachedConsole(workspaceRoot, paths, launchId, deps) {
15982
16093
  ], {
15983
16094
  cwd: workspaceRoot,
15984
16095
  detached: true,
15985
- stdio: ["ignore", logHandle.fd, logHandle.fd]
16096
+ stdio: ["ignore", logHandle.fd, logHandle.fd],
16097
+ ...deps.environment === undefined ? {} : { env: deps.environment() }
15986
16098
  });
15987
16099
  child.unref?.();
15988
16100
  return child;
@@ -16017,7 +16129,8 @@ function createConsoleTool(deps = {}) {
16017
16129
  sendSignal,
16018
16130
  sleep,
16019
16131
  resolveCliEntrypoint,
16020
- spawn
16132
+ spawn,
16133
+ ...deps.environment === undefined ? {} : { environment: deps.environment }
16021
16134
  };
16022
16135
  if (args.action === "status") {
16023
16136
  return summaryFromProbe(await readRuntimeProbe(workspaceRoot, {
@@ -16135,10 +16248,7 @@ function createConsoleTool(deps = {}) {
16135
16248
  });
16136
16249
  }
16137
16250
  await removeConsoleRuntimeState(paths.runtimeStatePath);
16138
- const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, {
16139
- resolveCliEntrypoint: runtimeDeps.resolveCliEntrypoint,
16140
- spawn: runtimeDeps.spawn
16141
- });
16251
+ const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, runtimeDeps);
16142
16252
  const probe = await waitForHealthyRuntime(workspaceRoot, START_TIMEOUT_MS, {
16143
16253
  fetch: runtimeDeps.fetch,
16144
16254
  processExists: runtimeDeps.processExists,
@@ -18037,8 +18147,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
18037
18147
  await injectMemoryBestEffort(options.injectMemory, prompt, output);
18038
18148
  return;
18039
18149
  }
18040
- await options.beforeRoute?.();
18041
18150
  const role = await roleForAgentName(config, input.agent ?? output.message.agent, options.resolveAgentRole);
18151
+ await options.beforeRoute?.();
18042
18152
  const derivedSignals = deriveTaskSignals(input, output);
18043
18153
  const signals = role === undefined ? derivedSignals : withoutExplicitOverride(derivedSignals);
18044
18154
  const executionPolicy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
@@ -18092,8 +18202,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
18092
18202
  function createHooks(config, getAvailable, options = {}) {
18093
18203
  const residency = options.modelResidency ?? createModelResidencyGuard(config, { fetch: globalThis.fetch });
18094
18204
  return {
18095
- config: residency.config,
18096
- "chat.params": residency.beforeInference,
18205
+ ...residency.config === undefined ? {} : { config: residency.config },
18206
+ ...residency.beforeInference === undefined ? {} : { "chat.params": residency.beforeInference },
18097
18207
  "chat.message": createChatMessageHook(config, getAvailable, options)
18098
18208
  };
18099
18209
  }
@@ -19243,12 +19353,841 @@ function createOrchestratedModelOwnership() {
19243
19353
  };
19244
19354
  }
19245
19355
 
19356
+ // src/plugin/nativeHost.ts
19357
+ import { AsyncLocalStorage } from "node:async_hooks";
19358
+ import { execFile as execFile3 } from "node:child_process";
19359
+ import { resolve as resolve10 } from "node:path";
19360
+ import { Model as Model2 } from "@opencode/schema/model";
19361
+ import { Session as Session2 } from "@opencode/schema/session";
19362
+ import { Tool } from "@opencode/schema/tool";
19363
+ import { tool as legacyTool } from "@opencode-ai/plugin/tool";
19364
+ import { Clock, Effect, Option, Stream } from "effect";
19365
+ import { z as z30 } from "zod";
19366
+
19367
+ // src/plugin/nativeHostContext.ts
19368
+ import {
19369
+ Context
19370
+ } from "effect";
19371
+
19372
+ class NativeSessions extends Context.Service()("@opencode/Session") {
19373
+ }
19374
+
19375
+ class NativePermissions extends Context.Service()("@opencode/Permission") {
19376
+ }
19377
+
19378
+ class NativeAgents extends Context.Service()("@opencode/Agent") {
19379
+ }
19380
+
19381
+ class NativeLocations extends Context.Service()("@opencode/example/LocationServiceMap") {
19382
+ }
19383
+
19384
+ class NativeServerInfo extends Context.Service()("@opencode/server/ServerInfo") {
19385
+ }
19386
+
19387
+ class NativeServerAuth extends Context.Service()("@opencode/ServerAuthConfig") {
19388
+ }
19389
+
19390
+ // src/plugin/nativeInventory.ts
19391
+ function nativeConfiguredInventory(models, config) {
19392
+ const local = new Set(config.local.runtimes.map((runtime) => runtime.defaultModel.providerID));
19393
+ const enabled = models.filter((model) => model.enabled);
19394
+ const catalog = new Map;
19395
+ for (const model of enabled) {
19396
+ if (local.has(model.providerID))
19397
+ continue;
19398
+ const provider = catalog.get(model.providerID) ?? { models: [] };
19399
+ provider.models.push({
19400
+ id: model.id,
19401
+ name: model.name,
19402
+ limit: model.limit,
19403
+ cost: model.cost.find((tier) => tier.tier === undefined),
19404
+ status: model.status,
19405
+ tool_call: model.capabilities.tools,
19406
+ modalities: { input: model.capabilities.input },
19407
+ reasoning: model.capabilities.output.includes("reasoning")
19408
+ });
19409
+ catalog.set(model.providerID, provider);
19410
+ }
19411
+ return {
19412
+ models: enabled.map((model) => ({
19413
+ providerID: model.providerID,
19414
+ modelID: model.id,
19415
+ apiModelID: model.modelID,
19416
+ ...local.has(model.providerID) ? {} : {
19417
+ supportsTools: model.capabilities.tools,
19418
+ ...model.limit.context > 0 ? { contextWindow: model.limit.context } : {}
19419
+ }
19420
+ })),
19421
+ profiles: normalizeModelsDevCatalog(Object.fromEntries(catalog))
19422
+ };
19423
+ }
19424
+
19425
+ // src/plugin/nativeResidency.ts
19426
+ import { randomUUID as randomUUID2 } from "node:crypto";
19427
+ import { z as z29 } from "zod";
19428
+ var NATIVE_RESIDENCY_PROVIDER_PACKAGE = "aisdk:@ai-sdk/openai-compatible@2.0.75";
19429
+ var NATIVE_ADMISSION_HEADER = "x-openteam-local-admission";
19430
+ var MAX_ADMISSIONS = 2048;
19431
+ var compatiblePackages = new Set([
19432
+ "@ai-sdk/openai-compatible",
19433
+ "aisdk:@ai-sdk/openai-compatible",
19434
+ "@opencode/ai/providers/openai-compatible",
19435
+ NATIVE_RESIDENCY_PROVIDER_PACKAGE
19436
+ ]);
19437
+ function key(ref) {
19438
+ return `${ref.providerID}\x00${ref.modelID}`;
19439
+ }
19440
+ function label(runtime) {
19441
+ return localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade";
19442
+ }
19443
+ function runtimeBase2(runtime) {
19444
+ return localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL) : lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
19445
+ }
19446
+ function createNativeModelResidencyGuard(config, options) {
19447
+ const admissions = new Map;
19448
+ const localTargets = new Map;
19449
+ let disposed = false;
19450
+ const residentRuntimes = config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
19451
+ const globalMode = () => resolveExecutionMode({
19452
+ routerMode: config.router.executionMode,
19453
+ legacyLocalOnly: config.router.localOnly,
19454
+ legacyFrontierOnly: config.router.frontierOnly
19455
+ });
19456
+ function allowed(providerID, mode) {
19457
+ const local = config.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
19458
+ return local.length > 0 ? mode !== "frontier" && local.some((runtime) => runtime.enabled) : mode !== "local";
19459
+ }
19460
+ function protects(providerID, url) {
19461
+ return residentRuntimes.some((runtime) => {
19462
+ if (runtime.defaultModel.providerID === providerID)
19463
+ return true;
19464
+ if (url === undefined)
19465
+ return false;
19466
+ if (!URL.canParse(url))
19467
+ return true;
19468
+ return new URL(url).origin === new URL(runtimeBase2(runtime)).origin;
19469
+ });
19470
+ }
19471
+ function bind(model) {
19472
+ const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === model.providerID);
19473
+ const runtime = runtimes[0];
19474
+ const runtimeLabel = runtime === undefined ? undefined : label(runtime);
19475
+ if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled)
19476
+ throw new ModelResidencyError("destination", runtimeLabel);
19477
+ const baseURL = runtimeBase2(runtime);
19478
+ const base = model.settings?.baseURL;
19479
+ const normalized = typeof base !== "string" ? undefined : localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(base) : lemonadeBaseURL(base);
19480
+ if (normalized !== baseURL)
19481
+ throw new ModelResidencyError("destination", runtimeLabel);
19482
+ if (model.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE || model.transport === "websocket" || model.settings?.fetch !== undefined || model.settings?.queryParams !== undefined || model.settings?.transformRequestBody !== undefined)
19483
+ throw new ModelResidencyError("transport", runtimeLabel);
19484
+ if (!model.modelID || model.modelID !== model.modelID.trim() || model.body?.model !== undefined && model.body.model !== model.modelID)
19485
+ throw new ModelResidencyError("identity", runtimeLabel);
19486
+ return { runtime, model, baseURL };
19487
+ }
19488
+ function guardedFetch(binding, send, authorized = () => true) {
19489
+ const create = localRuntimeKind(binding.runtime) === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
19490
+ return create({
19491
+ ...options,
19492
+ fetch: Object.assign((input, init) => {
19493
+ if (disposed || !authorized())
19494
+ throw new Error(nativeHostMessages.missingAdmission);
19495
+ return send(input, init);
19496
+ }, {
19497
+ preconnect: (...args) => send.preconnect?.(...args)
19498
+ }),
19499
+ healthFetch: options.fetch,
19500
+ baseURL: binding.baseURL,
19501
+ modelIDs: new Set([binding.model.modelID])
19502
+ });
19503
+ }
19504
+ function target(model, send, baseURL) {
19505
+ const headers = new Headers(z29.record(z29.string(), z29.string()).parse(model.headers ?? {}));
19506
+ if (typeof model.settings?.apiKey === "string" && !headers.has("authorization"))
19507
+ headers.set("authorization", `Bearer ${model.settings.apiKey}`);
19508
+ return {
19509
+ baseURL,
19510
+ modelID: model.modelID,
19511
+ fetch: Object.assign(async (input, init) => {
19512
+ if (disposed)
19513
+ throw new Error(nativeHostMessages.missingAdmission);
19514
+ const request = new Request(input, init);
19515
+ for (const [name, value] of headers)
19516
+ if (!request.headers.has(name))
19517
+ request.headers.set(name, value);
19518
+ return send(request);
19519
+ }, {
19520
+ preconnect: (...args) => send.preconnect?.(...args)
19521
+ })
19522
+ };
19523
+ }
19524
+ function observeModels(models) {
19525
+ localTargets.clear();
19526
+ return models.map((model) => {
19527
+ if (!allowed(model.providerID, globalMode()))
19528
+ return { ...model, enabled: false };
19529
+ if (!model.enabled)
19530
+ return model;
19531
+ const ref = { providerID: model.providerID, modelID: model.id };
19532
+ const base = model.settings?.baseURL;
19533
+ if (!protects(model.providerID, typeof base === "string" ? base : undefined)) {
19534
+ if (config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === model.providerID) && typeof base === "string")
19535
+ localTargets.set(key(ref), target(model, options.fetch, base));
19536
+ return model;
19537
+ }
19538
+ try {
19539
+ const binding = bind(model);
19540
+ localTargets.set(key(ref), target(model, guardedFetch(binding, options.fetch), binding.baseURL));
19541
+ return model;
19542
+ } catch (error) {
19543
+ if (!(error instanceof ModelResidencyError) && !(error instanceof z29.ZodError))
19544
+ throw error;
19545
+ (options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(model.providerID, residentRuntimes.find((runtime) => runtime.defaultModel.providerID === model.providerID)?.kind === "llama-swap" ? "llama-swap" : "Lemonade"));
19546
+ return { ...model, enabled: false };
19547
+ }
19548
+ });
19549
+ }
19550
+ return {
19551
+ providers(editor) {
19552
+ for (const record of editor.list())
19553
+ if (!allowed(record.provider.id, globalMode()))
19554
+ editor.remove(record.provider.id);
19555
+ },
19556
+ transform(editor) {
19557
+ for (const model of editor.list()) {
19558
+ const provider = editor.provider.get(model.providerID)?.provider;
19559
+ const base = model.settings?.baseURL ?? provider?.settings?.baseURL;
19560
+ const pkg = model.package ?? provider?.package;
19561
+ if (protects(model.providerID, typeof base === "string" ? base : undefined) && pkg !== undefined && compatiblePackages.has(pkg))
19562
+ editor.update(model.providerID, model.id, (draft) => {
19563
+ draft.package = NATIVE_RESIDENCY_PROVIDER_PACKAGE;
19564
+ });
19565
+ }
19566
+ },
19567
+ observeModels,
19568
+ sdk(event) {
19569
+ if (!protects(event.model.providerID, typeof event.options.baseURL === "string" ? event.options.baseURL : undefined))
19570
+ return;
19571
+ const factory = bind(event.model);
19572
+ if (event.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE.slice("aisdk:".length) || typeof event.options.fetch !== "function")
19573
+ throw new Error(nativeHostMessages.transportUnavailable);
19574
+ const upstream = event.options.fetch;
19575
+ const transport = Object.assign((input, init) => {
19576
+ const request = new Request(input, init);
19577
+ return upstream(request, {
19578
+ signal: request.signal,
19579
+ redirect: "error"
19580
+ });
19581
+ }, {
19582
+ preconnect: (...args) => upstream.preconnect?.(...args)
19583
+ });
19584
+ event.options.fetch = Object.assign(async (input, init) => {
19585
+ let request = new Request(input, init);
19586
+ const token = request.headers.get(NATIVE_ADMISSION_HEADER);
19587
+ const admission = token === null ? undefined : admissions.get(token);
19588
+ if (token === null || admission === undefined || admission.model.providerID !== factory.model.providerID || admission.baseURL !== factory.baseURL)
19589
+ throw new Error(nativeHostMessages.missingAdmission);
19590
+ request.headers.delete(NATIVE_ADMISSION_HEADER);
19591
+ if (factory.model.body !== undefined) {
19592
+ let body;
19593
+ try {
19594
+ body = await request.json();
19595
+ } catch {
19596
+ throw new ModelResidencyError("request", label(factory.runtime));
19597
+ }
19598
+ if (body === null || typeof body !== "object" || Array.isArray(body))
19599
+ throw new ModelResidencyError("request", label(factory.runtime));
19600
+ request = new Request(request, {
19601
+ body: JSON.stringify(mergeConfigInputs(body, factory.model.body))
19602
+ });
19603
+ }
19604
+ return guardedFetch(admission, transport, () => admissions.get(token) === admission)(request);
19605
+ }, {
19606
+ preconnect: (...args) => upstream.preconnect?.(...args)
19607
+ });
19608
+ },
19609
+ async modelRequest(event) {
19610
+ if (disposed)
19611
+ throw new Error(nativeHostMessages.missingAdmission);
19612
+ const roleID = await options.resolveAgentRole?.(event.agent) ?? event.agent;
19613
+ const role = isKnownNonWorkerRole(roleID) ? undefined : getRoleProfile(roleID, config.orchestrator?.roles);
19614
+ const policy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
19615
+ const mode = resolveExecutionMode({
19616
+ routerMode: globalMode(),
19617
+ agentMode: policy?.executionMode
19618
+ });
19619
+ if (!allowed(event.model.providerID, mode))
19620
+ throw new Error(nativeHostMessages.executionDomainBlocked);
19621
+ if (!protects(event.model.providerID, event.baseURL))
19622
+ return;
19623
+ const model = (await options.readModels()).find((candidate) => candidate.providerID === event.model.providerID && candidate.id === event.model.id && candidate.enabled);
19624
+ if (model === undefined)
19625
+ throw new Error(nativeHostMessages.transportUnavailable);
19626
+ const variant = model.variants.find((candidate) => candidate.id === event.model.variant);
19627
+ const effective = variant === undefined ? model : {
19628
+ ...model,
19629
+ settings: { ...model.settings, ...variant.settings },
19630
+ headers: { ...model.headers, ...variant.headers },
19631
+ body: { ...model.body, ...variant.body }
19632
+ };
19633
+ const binding = bind(effective);
19634
+ if (disposed)
19635
+ throw new Error(nativeHostMessages.missingAdmission);
19636
+ const token = (options.newRequestID ?? randomUUID2)();
19637
+ admissions.set(token, {
19638
+ ...binding,
19639
+ sessionID: event.sessionID,
19640
+ kind: event.kind
19641
+ });
19642
+ const oldest = admissions.keys().next().value;
19643
+ if (admissions.size > MAX_ADMISSIONS && oldest !== undefined)
19644
+ admissions.delete(oldest);
19645
+ event.headers[NATIVE_ADMISSION_HEADER] = token;
19646
+ },
19647
+ async http(event) {
19648
+ if (protects(event.model.providerID, event.request.url))
19649
+ throw new Error(nativeHostMessages.transportUnavailable);
19650
+ },
19651
+ websocket(event) {
19652
+ const url = new URL(event.url);
19653
+ if (url.protocol === "ws:")
19654
+ url.protocol = "http:";
19655
+ if (url.protocol === "wss:")
19656
+ url.protocol = "https:";
19657
+ if (protects(event.model.providerID, url.href))
19658
+ throw new Error(nativeHostMessages.transportUnavailable);
19659
+ },
19660
+ finished(sessionID) {
19661
+ for (const [token, admission] of admissions)
19662
+ if (admission.sessionID === sessionID)
19663
+ admissions.delete(token);
19664
+ },
19665
+ dispose() {
19666
+ disposed = true;
19667
+ admissions.clear();
19668
+ localTargets.clear();
19669
+ },
19670
+ localTarget(ref) {
19671
+ const value = localTargets.get(key(ref));
19672
+ if (value === undefined)
19673
+ throw new Error(nativeHostMessages.transportUnavailable);
19674
+ return value;
19675
+ }
19676
+ };
19677
+ }
19678
+
19679
+ // src/plugin/nativeSessions.ts
19680
+ import { resolve as resolve9 } from "node:path";
19681
+ import { Agent } from "@opencode/schema/agent";
19682
+ import { Model } from "@opencode/schema/model";
19683
+ import { Session } from "@opencode/schema/session";
19684
+ import { DateTime } from "effect";
19685
+ function sameNativeWorkspace(actual, expected) {
19686
+ return resolve9(actual.directory) === resolve9(expected.directory) && actual.workspaceID === expected.workspaceID;
19687
+ }
19688
+ function nativeAssistantResponse(sessionID, message) {
19689
+ return {
19690
+ info: {
19691
+ id: message.id,
19692
+ sessionID,
19693
+ role: "assistant",
19694
+ mode: message.agent,
19695
+ providerID: message.model.providerID,
19696
+ modelID: message.model.id,
19697
+ ...message.cost === undefined ? {} : { cost: message.cost },
19698
+ ...message.tokens === undefined ? {} : { tokens: message.tokens },
19699
+ ...message.error === undefined ? {} : { error: message.error },
19700
+ time: {
19701
+ created: DateTime.toEpochMillis(message.time.created),
19702
+ ...message.time.completed === undefined ? {} : { completed: DateTime.toEpochMillis(message.time.completed) }
19703
+ }
19704
+ },
19705
+ parts: message.content.flatMap((part) => part.type === "text" ? [{ type: "text", text: part.text }] : [])
19706
+ };
19707
+ }
19708
+ function createNativeSessionClient(deps) {
19709
+ const directory = resolve9(deps.directory);
19710
+ const location = {
19711
+ directory,
19712
+ ...deps.workspaceID === undefined ? {} : { workspaceID: deps.workspaceID }
19713
+ };
19714
+ function assertDirectory(candidate) {
19715
+ if (candidate !== undefined && resolve9(candidate) !== directory)
19716
+ throw new Error(nativeHostMessages.workspaceMismatch);
19717
+ }
19718
+ async function ownedSession(id, signal) {
19719
+ const info = await deps.run(deps.sessions().get(id), signal === undefined ? undefined : { signal });
19720
+ if (!sameNativeWorkspace(info.location, location))
19721
+ throw new Error(nativeHostMessages.workspaceMismatch);
19722
+ return info;
19723
+ }
19724
+ return {
19725
+ async create(args) {
19726
+ const signal = deps.signal?.();
19727
+ signal?.throwIfAborted();
19728
+ assertDirectory(args.query?.directory);
19729
+ if (args.body?.parentID === undefined)
19730
+ throw new Error(nativeHostMessages.missingBindings);
19731
+ const parentID = Session.ID.make(args.body.parentID);
19732
+ await ownedSession(parentID, signal);
19733
+ const child = await deps.run(deps.sessions().create({
19734
+ parentID,
19735
+ ...args.body.title === undefined ? {} : { title: args.body.title }
19736
+ }));
19737
+ if (!sameNativeWorkspace(child.location, location))
19738
+ throw new Error(nativeHostMessages.workspaceMismatch);
19739
+ if (child.parentID !== parentID)
19740
+ throw new Error(nativeHostMessages.missingBindings);
19741
+ signal?.throwIfAborted();
19742
+ return { data: { id: child.id }, error: undefined };
19743
+ },
19744
+ async prompt(args) {
19745
+ const parentSignal = deps.signal?.();
19746
+ const signal = parentSignal === undefined ? args.signal : args.signal === undefined ? parentSignal : AbortSignal.any([parentSignal, args.signal]);
19747
+ const sessionID = Session.ID.make(args.path.id);
19748
+ await ownedSession(sessionID, signal);
19749
+ const run = (effect) => deps.run(effect, signal === undefined ? undefined : { signal });
19750
+ const sessions = deps.sessions();
19751
+ try {
19752
+ if (args.body.agent !== undefined)
19753
+ await run(sessions.switchAgent({
19754
+ sessionID,
19755
+ agent: Agent.ID.make(args.body.agent)
19756
+ }));
19757
+ if (args.body.model !== undefined)
19758
+ await run(sessions.switchModel({
19759
+ sessionID,
19760
+ model: Model.Ref.parse(`${args.body.model.providerID}/${args.body.model.modelID}`)
19761
+ }));
19762
+ if (args.body.system !== undefined)
19763
+ deps.systemPrompts.set(sessionID, args.body.system);
19764
+ await run(sessions.prompt({
19765
+ sessionID,
19766
+ text: args.body.parts.map((part) => part.text).join(`
19767
+ `)
19768
+ }));
19769
+ await run(sessions.wait(sessionID));
19770
+ const final = await ownedSession(sessionID, signal);
19771
+ const messages = await run(sessions.messages({ sessionID, limit: 100 }));
19772
+ const assistant = [...messages].reverse().find((message) => message.type === "assistant");
19773
+ if (final.outcome === "interrupted")
19774
+ throw new Error(nativeHostMessages.executionInterrupted);
19775
+ if (final.outcome !== "succeeded" || assistant?.error !== undefined)
19776
+ throw new Error(nativeHostMessages.executionFailed);
19777
+ if (assistant?.time.completed === undefined)
19778
+ throw new Error(nativeHostMessages.noCompletedResponse);
19779
+ return {
19780
+ data: nativeAssistantResponse(sessionID, assistant),
19781
+ error: undefined
19782
+ };
19783
+ } finally {
19784
+ if (signal?.aborted)
19785
+ await deps.run(sessions.interrupt(sessionID, { resume: false }));
19786
+ }
19787
+ },
19788
+ async abort(args) {
19789
+ assertDirectory(args.query?.directory);
19790
+ const id = Session.ID.make(args.path.id);
19791
+ await ownedSession(id, args.signal);
19792
+ const options = args.signal === undefined ? undefined : { signal: args.signal };
19793
+ await deps.run(deps.sessions().interrupt(id, { resume: false }), options);
19794
+ const active = await deps.run(deps.sessions().active, options);
19795
+ return { data: !active.has(id), error: undefined };
19796
+ },
19797
+ async status(args) {
19798
+ assertDirectory(args?.query?.directory);
19799
+ const options = args?.signal == null ? undefined : { signal: args.signal };
19800
+ const sessions = deps.sessions();
19801
+ const active = await deps.run(sessions.active, options);
19802
+ const entries = [];
19803
+ for (const id of active) {
19804
+ const info = await deps.run(sessions.get(id), options);
19805
+ if (sameNativeWorkspace(info.location, location))
19806
+ entries.push([id, { type: "busy" }]);
19807
+ }
19808
+ return { data: Object.fromEntries(entries), error: undefined };
19809
+ }
19810
+ };
19811
+ }
19812
+
19813
+ // src/plugin/nativeHost.ts
19814
+ var executionEvent = z30.preprocess(only(["type", "location", "data"]), z30.object({
19815
+ type: z30.enum([
19816
+ "session.created",
19817
+ "session.deleted",
19818
+ "session.execution.started",
19819
+ "session.execution.succeeded",
19820
+ "session.execution.failed",
19821
+ "session.execution.interrupted",
19822
+ "session.step.ended",
19823
+ "session.step.failed"
19824
+ ]),
19825
+ location: z30.preprocess(only(["directory", "workspaceID"]), z30.object({
19826
+ directory: z30.string(),
19827
+ workspaceID: z30.string().optional()
19828
+ }).strict()).optional(),
19829
+ data: z30.preprocess(only(["sessionID", "assistantMessageID"]), z30.object({
19830
+ sessionID: z30.string(),
19831
+ assistantMessageID: z30.string().optional()
19832
+ }).strict())
19833
+ }).strict());
19834
+ var nativeBindings = Effect.fn("openteam.nativeBindings")(function* () {
19835
+ const sessions = yield* Effect.serviceOption(NativeSessions);
19836
+ const permissions = yield* Effect.serviceOption(NativePermissions);
19837
+ const agents = yield* Effect.serviceOption(NativeAgents);
19838
+ const locations = yield* Effect.serviceOption(NativeLocations);
19839
+ if (Option.isNone(sessions) || Option.isNone(permissions) || Option.isNone(agents) || Option.isNone(locations))
19840
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19841
+ const server = yield* Effect.serviceOption(NativeServerInfo);
19842
+ const auth = yield* Effect.serviceOption(NativeServerAuth);
19843
+ return {
19844
+ sessions: sessions.value,
19845
+ permissions: permissions.value,
19846
+ agents: agents.value,
19847
+ locations: locations.value,
19848
+ ...Option.isNone(server) ? {} : { server: server.value },
19849
+ ...Option.isNone(auth) ? {} : { auth: auth.value }
19850
+ };
19851
+ });
19852
+ function nativeExec(directory) {
19853
+ return (command, args) => new Promise((resolveOutput, reject) => {
19854
+ execFile3(command, [...args], { cwd: directory }, (error, stdout, stderr) => {
19855
+ if (error !== null && typeof error.code !== "number") {
19856
+ reject(error);
19857
+ return;
19858
+ }
19859
+ resolveOutput({
19860
+ exitCode: typeof error?.code === "number" ? error.code : 0,
19861
+ stdout,
19862
+ stderr
19863
+ });
19864
+ });
19865
+ });
19866
+ }
19867
+ function legacyModel(ref) {
19868
+ return { providerID: ref.providerID, modelID: ref.id };
19869
+ }
19870
+ function createNativePluginEffect(factory) {
19871
+ return Effect.fn("openteam.native")(function* (ctx) {
19872
+ if (!isSupportedNativeOpencodeVersion(ctx.app.version))
19873
+ return yield* Effect.die(new Error(nativeHostMessages.unsupportedVersion(ctx.app.version)));
19874
+ const version = ctx.app.version;
19875
+ const effectContext = yield* Effect.context();
19876
+ const directory = resolve10(ctx.location.directory);
19877
+ let bindings;
19878
+ const run = (effect, options) => {
19879
+ const current = bindings;
19880
+ 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));
19881
+ return Effect.runPromiseWith(effectContext)(bound, options);
19882
+ };
19883
+ let config;
19884
+ let residency;
19885
+ const systemPrompts = new Map;
19886
+ const toolSignals = new AsyncLocalStorage;
19887
+ const remember = (next) => {
19888
+ bindings = { ...bindings, ...next };
19889
+ };
19890
+ const requiredBindings = () => {
19891
+ if (bindings === undefined)
19892
+ throw new Error(nativeHostMessages.missingBindings);
19893
+ return bindings;
19894
+ };
19895
+ const assertWorkspace = (candidate) => {
19896
+ if (!sameNativeWorkspace(candidate, ctx.location))
19897
+ throw new Error(nativeHostMessages.workspaceMismatch);
19898
+ };
19899
+ const session = createNativeSessionClient({
19900
+ directory,
19901
+ ...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID },
19902
+ sessions: () => requiredBindings().sessions,
19903
+ run,
19904
+ systemPrompts,
19905
+ signal: () => toolSignals.getStore()
19906
+ });
19907
+ const client = {
19908
+ session,
19909
+ app: {
19910
+ async agents() {
19911
+ const result = await run(ctx.agent.list());
19912
+ return {
19913
+ data: result.data.map((agent) => ({ name: agent.id })),
19914
+ error: undefined
19915
+ };
19916
+ },
19917
+ async log(input) {
19918
+ const message = input.body?.message ?? nativeHostMessages.toolFailed;
19919
+ await run(input.body?.level === "error" ? Effect.logError(message) : input.body?.level === "warn" ? Effect.logWarning(message) : Effect.logInfo(message));
19920
+ }
19921
+ },
19922
+ instance: {
19923
+ async dispose(input) {
19924
+ assertWorkspace({
19925
+ ...ctx.location,
19926
+ directory: input.query?.directory ?? directory
19927
+ });
19928
+ await run(requiredBindings().locations.invalidate({
19929
+ directory: ctx.location.directory,
19930
+ ...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID }
19931
+ }), input.signal == null ? undefined : { signal: input.signal });
19932
+ return { data: true, error: undefined };
19933
+ }
19934
+ }
19935
+ };
19936
+ const hooks = yield* Effect.promise(() => factory({
19937
+ directory,
19938
+ worktree: ctx.location.project.directory,
19939
+ client,
19940
+ exec: nativeExec(directory),
19941
+ serverUrl: () => {
19942
+ const url = bindings?.server?.urls().find((value) => {
19943
+ if (!URL.canParse(value))
19944
+ return false;
19945
+ return ["127.0.0.1", "localhost", "[::1]"].includes(new URL(value).hostname);
19946
+ });
19947
+ return url === undefined ? undefined : new URL(url);
19948
+ },
19949
+ opencodeVersion: async () => version,
19950
+ listConfiguredInventory: async () => {
19951
+ if (config === undefined || residency === undefined)
19952
+ throw new Error(nativeHostMessages.missingBindings);
19953
+ const models = residency.observeModels((await run(ctx.model.list())).data);
19954
+ return nativeConfiguredInventory(models, config);
19955
+ },
19956
+ consoleEnvironment: () => {
19957
+ const auth = bindings?.auth;
19958
+ return {
19959
+ ...process.env,
19960
+ BUN_BE_BUN: "1",
19961
+ OPENCODE_SERVER_USERNAME: auth?.username,
19962
+ OPENCODE_SERVER_PASSWORD: auth && Option.isSome(auth.password) ? auth.password.value : undefined
19963
+ };
19964
+ },
19965
+ createResidencyGuard: (runtimeConfig, options) => {
19966
+ config = runtimeConfig;
19967
+ residency = createNativeModelResidencyGuard(runtimeConfig, {
19968
+ ...options,
19969
+ readModels: async () => (await run(ctx.model.list())).data
19970
+ });
19971
+ return residency;
19972
+ }
19973
+ }, ctx.options));
19974
+ const guard = residency;
19975
+ if (guard === undefined)
19976
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19977
+ const chatMessage = hooks["chat.message"];
19978
+ const tools = hooks.tool;
19979
+ if (chatMessage === undefined || tools === undefined)
19980
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19981
+ yield* Effect.addFinalizer(() => Effect.promise(async () => {
19982
+ guard.dispose();
19983
+ systemPrompts.clear();
19984
+ await hooks.dispose?.();
19985
+ }));
19986
+ yield* ctx.provider.transform(guard.providers);
19987
+ yield* ctx.model.transform(guard.transform);
19988
+ yield* ctx.aisdk.hook("sdk", (event) => Effect.sync(() => guard.sdk(event)));
19989
+ yield* ctx.session.hook("model.request", (event) => Effect.promise(() => guard.modelRequest(event)));
19990
+ yield* ctx.session.hook("http.request", (event) => Effect.promise(() => guard.http(event)));
19991
+ yield* ctx.session.hook("experimental.ws.handshake", (event) => Effect.sync(() => guard.websocket(event)));
19992
+ yield* ctx.session.hook("context", (event) => Effect.sync(() => {
19993
+ const system = systemPrompts.get(event.sessionID);
19994
+ if (system !== undefined)
19995
+ event.system.push({ type: "text", text: system });
19996
+ }));
19997
+ yield* ctx.session.hook("prompt", Effect.fn("openteam.nativePrompt")(function* (event) {
19998
+ const currentBindings = yield* nativeBindings();
19999
+ const current = yield* ctx.session.get({ sessionID: event.sessionID }).pipe(Effect.orDie);
20000
+ assertWorkspace(current.location);
20001
+ remember(currentBindings);
20002
+ const agent = yield* currentBindings.agents.resolve(current.agent).pipe(Effect.orDie);
20003
+ if (agent === undefined)
20004
+ return yield* Effect.die(new Error(nativeHostMessages.unknownAgent));
20005
+ const selected = current.model ?? (yield* ctx.model.default().pipe(Effect.orDie)).data ?? (yield* ctx.model.list().pipe(Effect.orDie)).data[0];
20006
+ if (selected === undefined)
20007
+ return yield* Effect.die(new Error(nativeHostMessages.noAvailableModel));
20008
+ const created = yield* Clock.currentTimeMillis;
20009
+ const output = {
20010
+ message: {
20011
+ id: event.messageID,
20012
+ sessionID: event.sessionID,
20013
+ role: "user",
20014
+ time: { created },
20015
+ agent: agent.id,
20016
+ model: legacyModel(selected)
20017
+ },
20018
+ parts: [
20019
+ {
20020
+ id: event.messageID,
20021
+ sessionID: event.sessionID,
20022
+ messageID: event.messageID,
20023
+ type: "text",
20024
+ text: event.prompt.text
20025
+ }
20026
+ ]
20027
+ };
20028
+ yield* Effect.promise(() => chatMessage({
20029
+ sessionID: event.sessionID,
20030
+ messageID: event.messageID,
20031
+ agent: agent.id,
20032
+ ...current.model === undefined ? {} : { model: legacyModel(current.model) }
20033
+ }, output));
20034
+ const routed = output.message.model;
20035
+ yield* ctx.session.switchModel({
20036
+ sessionID: event.sessionID,
20037
+ model: {
20038
+ ...Model2.Ref.parse(`${routed.providerID}/${routed.modelID}`),
20039
+ ...current.model?.providerID === routed.providerID && current.model.id === routed.modelID && current.model.variant !== undefined ? { variant: current.model.variant } : {}
20040
+ }
20041
+ }).pipe(Effect.orDie);
20042
+ const part = output.parts[0];
20043
+ if (part?.type === "text")
20044
+ event.prompt.text = part.text;
20045
+ }));
20046
+ yield* ctx.tool.transform((editor) => {
20047
+ for (const [name, definition] of Object.entries(tools)) {
20048
+ const input = legacyTool.schema.object(definition.args);
20049
+ editor.add({
20050
+ name,
20051
+ description: definition.description,
20052
+ input: legacyTool.schema.toJSONSchema(input, { io: "input" }),
20053
+ options: { codemode: false },
20054
+ execute: Effect.fn("openteam.nativeTool")(function* (_input, toolContext) {
20055
+ const current = requiredBindings();
20056
+ const parent = yield* current.sessions.get(toolContext.sessionID).pipe(Effect.orDie);
20057
+ assertWorkspace(parent.location);
20058
+ remember(current);
20059
+ return yield* Effect.tryPromise(async (signal) => {
20060
+ const progress = [];
20061
+ const context = {
20062
+ sessionID: toolContext.sessionID,
20063
+ messageID: toolContext.messageID,
20064
+ agent: toolContext.agent,
20065
+ directory,
20066
+ worktree: ctx.location.project.directory,
20067
+ abort: signal,
20068
+ metadata: (update) => {
20069
+ progress.push(run(toolContext.progress({
20070
+ ...update.metadata,
20071
+ ...update.title === undefined ? {} : { title: update.title }
20072
+ }), { signal }).then(() => ({}), (error) => ({ error })));
20073
+ },
20074
+ ask: async (request) => {
20075
+ await run(current.permissions.assert({
20076
+ sessionID: toolContext.sessionID,
20077
+ agent: toolContext.agent,
20078
+ action: request.permission,
20079
+ resources: request.patterns,
20080
+ save: request.always,
20081
+ metadata: request.metadata,
20082
+ source: {
20083
+ type: "tool",
20084
+ messageID: toolContext.messageID,
20085
+ id: toolContext.id
20086
+ }
20087
+ }), { signal });
20088
+ }
20089
+ };
20090
+ const result = await toolSignals.run(signal, () => definition.execute(input.parse(_input), context));
20091
+ const failed = (await Promise.all(progress)).find((entry) => entry.error !== undefined);
20092
+ if (failed)
20093
+ throw failed.error;
20094
+ if (typeof result === "string")
20095
+ return { content: result };
20096
+ return {
20097
+ content: [
20098
+ { type: "text", text: result.output },
20099
+ ...(result.attachments ?? []).map((attachment) => ({
20100
+ type: "file",
20101
+ uri: attachment.url,
20102
+ mime: attachment.mime,
20103
+ ...attachment.filename === undefined ? {} : { name: attachment.filename }
20104
+ }))
20105
+ ],
20106
+ metadata: {
20107
+ ...result.metadata,
20108
+ ...result.title === undefined ? {} : { title: result.title }
20109
+ }
20110
+ };
20111
+ }).pipe(Effect.mapError(() => new Tool.Error({ message: nativeHostMessages.toolFailed })));
20112
+ })
20113
+ });
20114
+ }
20115
+ });
20116
+ 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()));
20117
+ yield* ctx.tool.hook("execute.after", (event) => Effect.promise(() => hooks["tool.execute.after"]?.({
20118
+ tool: event.tool,
20119
+ sessionID: event.sessionID,
20120
+ callID: event.id,
20121
+ args: event.input
20122
+ }, { title: event.tool, output: "", metadata: {} }) ?? Promise.resolve()));
20123
+ yield* ctx.event.subscribe().pipe(Stream.runForEach((event) => Effect.tryPromise(async () => {
20124
+ const parsed = executionEvent.safeParse(event);
20125
+ if (!parsed.success)
20126
+ return;
20127
+ const value = parsed.data;
20128
+ if (value.location !== undefined) {
20129
+ if (!sameNativeWorkspace(value.location, ctx.location))
20130
+ return;
20131
+ } else if (value.type !== "session.deleted") {
20132
+ const info = await run(ctx.session.get({
20133
+ sessionID: Session2.ID.make(value.data.sessionID)
20134
+ }));
20135
+ if (!sameNativeWorkspace(info.location, ctx.location))
20136
+ return;
20137
+ } else
20138
+ return;
20139
+ const sessionID = value.data.sessionID;
20140
+ if (value.type === "session.deleted") {
20141
+ guard.finished(sessionID);
20142
+ systemPrompts.delete(sessionID);
20143
+ }
20144
+ let mapped;
20145
+ if (value.type === "session.created" || value.type === "session.deleted") {
20146
+ mapped = {
20147
+ type: value.type,
20148
+ properties: { info: { id: sessionID } }
20149
+ };
20150
+ } else if (value.type === "session.execution.started") {
20151
+ mapped = {
20152
+ type: "session.status",
20153
+ properties: { sessionID, status: { type: "busy" } }
20154
+ };
20155
+ } else if (value.type === "session.step.ended" || value.type === "session.step.failed") {
20156
+ if (bindings === undefined)
20157
+ return;
20158
+ const messages = await run(bindings.sessions.messages({
20159
+ sessionID: Session2.ID.make(sessionID),
20160
+ limit: 100
20161
+ }));
20162
+ const message = messages.find((candidate) => candidate.id === value.data.assistantMessageID);
20163
+ if (message?.type !== "assistant")
20164
+ return;
20165
+ mapped = {
20166
+ type: "message.updated",
20167
+ properties: {
20168
+ info: nativeAssistantResponse(sessionID, message).info
20169
+ }
20170
+ };
20171
+ } else {
20172
+ systemPrompts.delete(sessionID);
20173
+ guard.finished(sessionID);
20174
+ if (value.type !== "session.execution.succeeded")
20175
+ await hooks.event?.({
20176
+ event: { type: "session.error", properties: { sessionID } }
20177
+ });
20178
+ mapped = { type: "session.idle", properties: { sessionID } };
20179
+ }
20180
+ await hooks.event?.({ event: mapped });
20181
+ }).pipe(Effect.catch(() => Effect.logError(nativeHostMessages.eventCaptureFailed)))), Effect.tapCause(() => Effect.logError(nativeHostMessages.eventCaptureFailed)), Effect.forkScoped);
20182
+ });
20183
+ }
20184
+
19246
20185
  // src/plugin/orchestrateTool.ts
19247
20186
  import { tool as tool5 } from "@opencode-ai/plugin";
19248
- import { z as z30 } from "zod";
20187
+ import { z as z32 } from "zod";
19249
20188
 
19250
20189
  // src/orchestrator/coordinator.ts
19251
- import { z as z29 } from "zod";
20190
+ import { z as z31 } from "zod";
19252
20191
 
19253
20192
  // src/orchestrator/nodeIteration.ts
19254
20193
  var DEFAULT_MAX_RETRIES = 2;
@@ -19722,7 +20661,7 @@ function buildRecord(input, decision2, subsession, deps, decisionID, batchID) {
19722
20661
  }
19723
20662
  return record;
19724
20663
  }
19725
- var TelemetryFailureClassSchema = z29.enum([
20664
+ var TelemetryFailureClassSchema = z31.enum([
19726
20665
  "create-rejected",
19727
20666
  "create-no-id",
19728
20667
  "prompt-rejected",
@@ -20148,10 +21087,10 @@ function uniqueModels(models) {
20148
21087
  const seen = new Set;
20149
21088
  const unique = [];
20150
21089
  for (const model of models) {
20151
- const key = modelKey4(model);
20152
- if (seen.has(key))
21090
+ const key2 = modelKey4(model);
21091
+ if (seen.has(key2))
20153
21092
  continue;
20154
- seen.add(key);
21093
+ seen.add(key2);
20155
21094
  unique.push(model);
20156
21095
  }
20157
21096
  return unique;
@@ -20539,7 +21478,7 @@ async function runRoleTasks(inputs, deps, options = {}) {
20539
21478
  var _pluginWarnedRoles = new Set;
20540
21479
  var EMPTY_ROLE_ID_SET = new Set;
20541
21480
  var SAFE_ROLE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
20542
- var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
21481
+ var RoleIDSchema = z32.string().min(1).superRefine((roleID, ctx) => {
20543
21482
  if (!SAFE_ROLE_ID.test(roleID)) {
20544
21483
  ctx.addIssue({
20545
21484
  code: "custom",
@@ -20547,16 +21486,16 @@ var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
20547
21486
  });
20548
21487
  }
20549
21488
  });
20550
- var RoleAssignmentSchema = z30.object({
21489
+ var RoleAssignmentSchema = z32.object({
20551
21490
  roleID: RoleIDSchema.describe("Role identifier from the roster"),
20552
- prompt: z30.string().min(1).describe("Work prompt for this role"),
20553
- title: z30.string().min(1).optional().describe("Human-readable title"),
20554
- dependsOn: z30.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
21491
+ prompt: z32.string().min(1).describe("Work prompt for this role"),
21492
+ title: z32.string().min(1).optional().describe("Human-readable title"),
21493
+ dependsOn: z32.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
20555
21494
  }).strict();
20556
- var OrchestratePayloadSchema = z30.object({
20557
- assignments: z30.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
20558
- parentSessionID: z30.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
20559
- directory: z30.string().min(1).optional().describe("Working directory override")
21495
+ var OrchestratePayloadSchema = z32.object({
21496
+ assignments: z32.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
21497
+ parentSessionID: z32.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
21498
+ directory: z32.string().min(1).optional().describe("Working directory override")
20560
21499
  }).strict();
20561
21500
  function validateAssignmentDependencies(assignments) {
20562
21501
  const ids = new Set;
@@ -20617,14 +21556,14 @@ function validateAssignmentDependencies(assignments) {
20617
21556
  function isRecord7(value) {
20618
21557
  return typeof value === "object" && value !== null && !Array.isArray(value);
20619
21558
  }
20620
- function nestedRecord2(value, key) {
20621
- const nested = value[key];
21559
+ function nestedRecord2(value, key2) {
21560
+ const nested = value[key2];
20622
21561
  return isRecord7(nested) ? nested : undefined;
20623
21562
  }
20624
21563
  function numberField(records, keys) {
20625
21564
  for (const record of records) {
20626
- for (const key of keys) {
20627
- const value = record[key];
21565
+ for (const key2 of keys) {
21566
+ const value = record[key2];
20628
21567
  if (typeof value === "number" && Number.isInteger(value)) {
20629
21568
  return value;
20630
21569
  }
@@ -20634,8 +21573,8 @@ function numberField(records, keys) {
20634
21573
  }
20635
21574
  function booleanField(records, keys) {
20636
21575
  for (const record of records) {
20637
- for (const key of keys) {
20638
- const value = record[key];
21576
+ for (const key2 of keys) {
21577
+ const value = record[key2];
20639
21578
  if (typeof value === "boolean") {
20640
21579
  return value;
20641
21580
  }
@@ -20646,8 +21585,8 @@ function booleanField(records, keys) {
20646
21585
  function boundedErrorTokens(records) {
20647
21586
  const tokens = [];
20648
21587
  for (const record of records) {
20649
- for (const key of ["name", "code", "type", "message"]) {
20650
- const value = record[key];
21588
+ for (const key2 of ["name", "code", "type", "message"]) {
21589
+ const value = record[key2];
20651
21590
  if (typeof value === "string" && value.length <= 100) {
20652
21591
  tokens.push(value.toLowerCase());
20653
21592
  }
@@ -21185,6 +22124,8 @@ function createOrchestrateTool(deps) {
21185
22124
  }
21186
22125
  const parentOverrideIgnored = parsed.data.parentSessionID !== undefined && parsed.data.parentSessionID !== context.sessionID;
21187
22126
  parsed.data.parentSessionID = context.sessionID;
22127
+ if (parsed.data.directory === undefined && context.directory !== undefined)
22128
+ parsed.data.directory = context.directory;
21188
22129
  const depError = validateAssignmentDependencies(parsed.data.assignments);
21189
22130
  if (depError !== undefined) {
21190
22131
  return dependencyValidationError(depError.code, depError.detail);
@@ -21292,7 +22233,7 @@ ${advisory}`;
21292
22233
 
21293
22234
  // src/plugin/registerCastTool.ts
21294
22235
  import { tool as tool6 } from "@opencode-ai/plugin";
21295
- import { z as z31 } from "zod";
22236
+ import { z as z33 } from "zod";
21296
22237
 
21297
22238
  // src/messages/rosterSummary.ts
21298
22239
  var ROSTER_SUMMARY_HEADING = "Registered team model policies";
@@ -21408,12 +22349,12 @@ function createTeamPreparation(deps) {
21408
22349
  OpenTeamConfigSchema.parse(migrateConfig(baseRaw));
21409
22350
  const overlay = overlayBefore === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayBefore);
21410
22351
  const options = configOptionsFromPluginOptions(deps.options);
21411
- const resolve9 = (raw) => {
22352
+ const resolve11 = (raw) => {
21412
22353
  const config = validateConfigInput(TEAM_PREPARATION_CONFIG, mergeConfigInputs(mergeConfigInputs(raw, options), overlay));
21413
22354
  assertEffectiveConfigModelDomains(config);
21414
22355
  return config;
21415
22356
  };
21416
- const current = resolve9(baseRaw);
22357
+ const current = resolve11(baseRaw);
21417
22358
  const live = liveConfig ?? current;
21418
22359
  if (resolveExecutionMode({
21419
22360
  routerMode: current.router.executionMode,
@@ -21435,9 +22376,9 @@ function createTeamPreparation(deps) {
21435
22376
  ]))
21436
22377
  }
21437
22378
  });
21438
- const effective = resolve9(candidate);
22379
+ const effective = resolve11(candidate);
21439
22380
  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)));
21440
- const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key)));
22381
+ const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key2, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key2)));
21441
22382
  const hasAdditions = Object.keys(additions).length > 0;
21442
22383
  const next = hasAdditions ? validateConfigInput(TEAM_PREPARATION_CONFIG, {
21443
22384
  ...live,
@@ -21521,19 +22462,19 @@ async function persistPreparedTeam(storage, files) {
21521
22462
  }
21522
22463
 
21523
22464
  // src/plugin/registerCastTool.ts
21524
- var CastEntrySchema = z31.object({
21525
- roleID: z31.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
21526
- agentName: z31.string().min(1).describe("Themed character name assigned to the role")
22465
+ var CastEntrySchema = z33.object({
22466
+ roleID: z33.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
22467
+ agentName: z33.string().min(1).describe("Themed character name assigned to the role")
21527
22468
  }).strict();
21528
- var RegisterCastPayloadSchema = z31.object({
21529
- universe: z31.string().min(1).describe("Casting universe for this roster"),
21530
- entries: z31.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
22469
+ var RegisterCastPayloadSchema = z33.object({
22470
+ universe: z33.string().min(1).describe("Casting universe for this roster"),
22471
+ entries: z33.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
21531
22472
  }).strict();
21532
22473
  function offendingFields(error) {
21533
22474
  const fields = error.issues.flatMap((issue) => {
21534
22475
  const base = issue.path.map((segment) => String(segment));
21535
22476
  if (issue.code === "unrecognized_keys") {
21536
- return issue.keys.map((key) => [...base, key].join("."));
22477
+ return issue.keys.map((key2) => [...base, key2].join("."));
21537
22478
  }
21538
22479
  return [base.join(".") || "(root)"];
21539
22480
  });
@@ -21573,12 +22514,12 @@ function markdownCell(value) {
21573
22514
  }
21574
22515
  function modelPolicyLabel(policy) {
21575
22516
  const model = policy?.model;
21576
- const label = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
22517
+ const label2 = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
21577
22518
  if (policy?.fallbacks === undefined || policy.fallbacks.length === 0) {
21578
- return label;
22519
+ return label2;
21579
22520
  }
21580
22521
  const fallbacks = policy.fallbacks.map((fallback) => `${fallback.providerID}/${fallback.modelID}`);
21581
- return `${label}; ${rosterOrderedFallbacks(fallbacks)}`;
22522
+ return `${label2}; ${rosterOrderedFallbacks(fallbacks)}`;
21582
22523
  }
21583
22524
  function registeredTeamTable(roster, deps) {
21584
22525
  const rows = roster.entries.map((entry) => {
@@ -21647,7 +22588,7 @@ function createRegisterCastTool(deps) {
21647
22588
  }
21648
22589
 
21649
22590
  // src/plugin/reloadTool.ts
21650
- import { resolve as resolve9 } from "node:path";
22591
+ import { resolve as resolve11 } from "node:path";
21651
22592
  import { tool as tool7 } from "@opencode-ai/plugin/tool";
21652
22593
  var WAIT_MS = 30000;
21653
22594
  var SDK_TIMEOUT_MS = 5000;
@@ -21681,7 +22622,7 @@ function createReloadController(deps) {
21681
22622
  return () => clearTimeout(timer);
21682
22623
  }
21683
22624
  };
21684
- const directory = resolve9(deps.directory);
22625
+ const directory = resolve11(deps.directory);
21685
22626
  let pending;
21686
22627
  let authorizing;
21687
22628
  let closed = false;
@@ -21829,7 +22770,7 @@ function createReloadController(deps) {
21829
22770
  const parsed = schema.safeParse(args);
21830
22771
  if (!parsed.success)
21831
22772
  return reloadMessages.invalid;
21832
- if (context.agent !== "openteam" || resolve9(context.directory) !== directory || context.sessionID.trim().length === 0)
22773
+ if (context.agent !== "openteam" || resolve11(context.directory) !== directory || context.sessionID.trim().length === 0)
21833
22774
  return reloadMessages.denied;
21834
22775
  if (parsed.data.action === "cancel") {
21835
22776
  if ((pending?.sessionID ?? authorizing?.sessionID) !== context.sessionID)
@@ -21975,7 +22916,7 @@ function createSessionTracker() {
21975
22916
  }
21976
22917
 
21977
22918
  // src/plugin/toolcalls.ts
21978
- function key(sessionID, callID) {
22919
+ function key2(sessionID, callID) {
21979
22920
  return `${sessionID}\x00${callID}`;
21980
22921
  }
21981
22922
  function createToolcallTracker(deps) {
@@ -21983,10 +22924,10 @@ function createToolcallTracker(deps) {
21983
22924
  const started = new Map;
21984
22925
  return {
21985
22926
  before(input) {
21986
- started.set(key(input.sessionID, input.callID), now());
22927
+ started.set(key2(input.sessionID, input.callID), now());
21987
22928
  },
21988
22929
  after(input) {
21989
- const mapKey = key(input.sessionID, input.callID);
22930
+ const mapKey = key2(input.sessionID, input.callID);
21990
22931
  const start = started.get(mapKey);
21991
22932
  started.delete(mapKey);
21992
22933
  const end = now();
@@ -22196,7 +23137,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
22196
23137
  }
22197
23138
 
22198
23139
  // src/storage/lifecycle/fsLifecycleJournal.ts
22199
- import { randomUUID as randomUUID3 } from "node:crypto";
23140
+ import { randomUUID as randomUUID4 } from "node:crypto";
22200
23141
  import {
22201
23142
  lstatSync as lstatSync3,
22202
23143
  mkdirSync as mkdirSync3,
@@ -22206,22 +23147,22 @@ import {
22206
23147
  statSync,
22207
23148
  unlinkSync as unlinkSync2
22208
23149
  } from "node:fs";
22209
- import { join as join15, resolve as resolve11 } from "node:path";
23150
+ import { join as join15, resolve as resolve13 } from "node:path";
22210
23151
  import { ZodError } from "zod";
22211
23152
 
22212
23153
  // src/storage/lifecycle/codec.ts
22213
- import { z as z32 } from "zod";
23154
+ import { z as z34 } from "zod";
22214
23155
  var LIFECYCLE_CODEC_VERSION = 1;
22215
23156
  var LIFECYCLE_GENESIS_DIGEST = sha256Hex("openteam/lifecycle-journal/genesis/v1");
22216
23157
  var NUL5 = "\x00";
22217
23158
  var NEWLINE2 = `
22218
23159
  `;
22219
- var frameSchema = z32.object({
22220
- v: z32.literal(LIFECYCLE_CODEC_VERSION),
22221
- seq: z32.number().int().nonnegative(),
22222
- prev: z32.string().min(1),
22223
- sum: z32.string().min(1),
22224
- event: z32.unknown()
23160
+ var frameSchema = z34.object({
23161
+ v: z34.literal(LIFECYCLE_CODEC_VERSION),
23162
+ seq: z34.number().int().nonnegative(),
23163
+ prev: z34.string().min(1),
23164
+ sum: z34.string().min(1),
23165
+ event: z34.unknown()
22225
23166
  }).strict();
22226
23167
  function canonical2(value) {
22227
23168
  if (Array.isArray(value)) {
@@ -22230,7 +23171,7 @@ function canonical2(value) {
22230
23171
  if (value === null || typeof value !== "object") {
22231
23172
  return JSON.stringify(value);
22232
23173
  }
22233
- return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key2, child]) => `${JSON.stringify(key2)}:${canonical2(child)}`).join(",")}}`;
23174
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key3, child]) => `${JSON.stringify(key3)}:${canonical2(child)}`).join(",")}}`;
22234
23175
  }
22235
23176
  function lifecycleFrameDigest(previousDigest, event) {
22236
23177
  return sha256Hex(`${previousDigest}${NUL5}${canonical2(event)}`);
@@ -22308,7 +23249,7 @@ function decodeLifecycleJournal(text) {
22308
23249
  }
22309
23250
 
22310
23251
  // src/storage/lifecycle/fsLifecycleSupport.ts
22311
- import { randomUUID as randomUUID2 } from "node:crypto";
23252
+ import { randomUUID as randomUUID3 } from "node:crypto";
22312
23253
  import {
22313
23254
  appendFileSync,
22314
23255
  closeSync as closeSync2,
@@ -22325,7 +23266,7 @@ import {
22325
23266
  unlinkSync,
22326
23267
  writeSync as writeSync2
22327
23268
  } from "node:fs";
22328
- import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve10 } from "node:path";
23269
+ import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve12 } from "node:path";
22329
23270
  var SAFE_RUN_ID = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?$/u;
22330
23271
 
22331
23272
  class LifecycleFsError extends Error {
@@ -22349,7 +23290,7 @@ function validateRunID(runID) {
22349
23290
  }
22350
23291
  function resolvedLifecycleRoot(root) {
22351
23292
  try {
22352
- const absolute = resolve10(root);
23293
+ const absolute = resolve12(root);
22353
23294
  mkdirSync2(absolute, { recursive: true });
22354
23295
  const rootStat = lstatSync2(absolute);
22355
23296
  if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
@@ -22392,7 +23333,7 @@ function assertExistingSafeRun(root, runID) {
22392
23333
  throw new LifecycleFsError("unavailable");
22393
23334
  }
22394
23335
  const child = relative3(root, real);
22395
- if (child === "" || child.startsWith("..") || resolve10(root, child) !== real || dirname7(real) !== root) {
23336
+ if (child === "" || child.startsWith("..") || resolve12(root, child) !== real || dirname7(real) !== root) {
22396
23337
  throw new LifecycleFsError("corrupt");
22397
23338
  }
22398
23339
  return real;
@@ -22441,7 +23382,7 @@ function appendDurable2(path4, content) {
22441
23382
  }
22442
23383
  }
22443
23384
  function writeDurableAtomically(path4, content) {
22444
- const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID2()}.next`);
23385
+ const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID3()}.next`);
22445
23386
  try {
22446
23387
  writeDurable2(temporary, content);
22447
23388
  renameSync(temporary, path4);
@@ -22464,7 +23405,7 @@ function readLockContents(path4) {
22464
23405
  }
22465
23406
  function withRunMutationLock(runDir, mutate) {
22466
23407
  const lockPath = join14(runDir, ".mutation-lock");
22467
- const lockToken = randomUUID2();
23408
+ const lockToken = randomUUID3();
22468
23409
  let descriptor;
22469
23410
  try {
22470
23411
  descriptor = openSync2(lockPath, "wx");
@@ -22533,7 +23474,7 @@ function parseJson(text, parse2) {
22533
23474
  }
22534
23475
  }
22535
23476
  function issueWriterToken() {
22536
- return randomUUID3().replaceAll("-", "");
23477
+ return randomUUID4().replaceAll("-", "");
22537
23478
  }
22538
23479
  function createLease(runID, writerToken, input) {
22539
23480
  return parseLifecycleWriterLease({
@@ -22632,7 +23573,7 @@ function canDeleteTerminalRun(runDir, runID, now) {
22632
23573
  return now >= lease.expiresAt;
22633
23574
  }
22634
23575
  var createFsLifecycleJournal = (options) => {
22635
- const configuredRoot = resolve11(options.root);
23576
+ const configuredRoot = resolve13(options.root);
22636
23577
  const now = options.now ?? Date.now;
22637
23578
  const mutateRun = options.withMutationLock ?? withRunMutationLock;
22638
23579
  return {
@@ -22654,7 +23595,7 @@ var createFsLifecycleJournal = (options) => {
22654
23595
  throw error;
22655
23596
  }
22656
23597
  }
22657
- stagingPath = join15(root, `.start-${runID}-${randomUUID3()}`);
23598
+ stagingPath = join15(root, `.start-${runID}-${randomUUID4()}`);
22658
23599
  mkdirSync3(stagingPath);
22659
23600
  const writerToken = issueWriterToken();
22660
23601
  const eventAt = now();
@@ -23013,8 +23954,8 @@ async function initSpanEmit(connection, config) {
23013
23954
  const tracer = provider.getTracer(DEFAULT_SERVICE_NAME);
23014
23955
  return (record) => {
23015
23956
  const span = tracer.startSpan(record.name);
23016
- for (const [key2, value] of Object.entries(record.attributes)) {
23017
- span.setAttribute(key2, value);
23957
+ for (const [key3, value] of Object.entries(record.attributes)) {
23958
+ span.setAttribute(key3, value);
23018
23959
  }
23019
23960
  span.end();
23020
23961
  };
@@ -23132,7 +24073,8 @@ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
23132
24073
  var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
23133
24074
  async function readPackagedCertificate(path4) {
23134
24075
  try {
23135
- return await readFile5(new URL(`./certificates/${basename4(path4)}`, import.meta.url), "utf8");
24076
+ const hostDirectory = basename4(dirname8(path4)) === NATIVE_CERTIFICATE_DIRECTORY ? `${NATIVE_CERTIFICATE_DIRECTORY}/` : "";
24077
+ return await readFile5(new URL(`./certificates/${hostDirectory}${basename4(path4)}`, import.meta.url), "utf8");
23136
24078
  } catch {
23137
24079
  return;
23138
24080
  }
@@ -23264,8 +24206,8 @@ async function readOpencodeVersion(serverUrl, options = {}) {
23264
24206
  }
23265
24207
  var DEFAULT_OPENCODE_VERSION_BACKOFF_MS = [50, 250];
23266
24208
  function defaultSleep(ms) {
23267
- return new Promise((resolve12) => {
23268
- setTimeout(resolve12, ms);
24209
+ return new Promise((resolve14) => {
24210
+ setTimeout(resolve14, ms);
23269
24211
  });
23270
24212
  }
23271
24213
  function createLazyOpencodeVersionReader(serverUrl, options = {}) {
@@ -23308,9 +24250,10 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
23308
24250
  };
23309
24251
  }
23310
24252
  async function buildGateInput(config, storage, opencodeVersion, warn, packagedRead = readPackagedCertificate) {
23311
- const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
24253
+ const hostPath = (path4) => isSupportedNativeOpencodeVersion(opencodeVersion) ? `${dirname8(path4)}/${NATIVE_CERTIFICATE_DIRECTORY}/${basename4(path4)}` : path4;
24254
+ const shadow = await readAndParseCertificate(storage, hostPath(SHADOW_CERT_PATH), (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
23312
24255
  const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
23313
- const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
24256
+ const release = await readAndParseCertificate(storage, hostPath(RELEASE_CERT_PATH), (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
23314
24257
  let soakLedgerDigest = "0".repeat(64);
23315
24258
  try {
23316
24259
  const recorderPorts = recorderPortsFromStorage(storage);
@@ -23565,14 +24508,17 @@ async function listConfiguredOpencodeInventory(ctx) {
23565
24508
  profiles: normalizeModelsDevCatalog(Object.fromEntries(providers.map((provider) => [provider.id, provider]))).filter((profile) => profile.costPer1M.inputUSD > 0 && profile.costPer1M.outputUSD > 0)
23566
24509
  };
23567
24510
  }
23568
- var server = async (ctx, rawOptions) => {
24511
+ async function createPluginRuntime(ctx, rawOptions) {
23569
24512
  const now = () => Date.now();
23570
24513
  const diagnostics = createPluginDiagnosticChannel({
23571
24514
  client: ctx.client,
23572
24515
  directory: ctx.directory,
23573
24516
  now
23574
24517
  });
23575
- logOpencodeServerUrl(ctx.serverUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
24518
+ const initialServerUrl = ctx.serverUrl();
24519
+ if (initialServerUrl !== undefined) {
24520
+ logOpencodeServerUrl(initialServerUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
24521
+ }
23576
24522
  const storage = createFsStorageProvider(ctx.directory || ctx.worktree || process.cwd());
23577
24523
  const runtimeConfig = await loadRuntimeOpenTeamConfig(storage, rawOptions);
23578
24524
  const config = runtimeConfig.config;
@@ -23581,20 +24527,19 @@ var server = async (ctx, rawOptions) => {
23581
24527
  const registry = new RuntimeRegistry({
23582
24528
  fetch: globalThis.fetch,
23583
24529
  clock: () => new Date().toISOString(),
23584
- foundryDiscovery: foundryDiscoveryFromExec(createShellExec(ctx.$))
24530
+ foundryDiscovery: foundryDiscoveryFromExec(ctx.exec)
23585
24531
  });
23586
24532
  const cache = createAvailabilityCache({
23587
24533
  config,
23588
24534
  registry,
23589
- ...ctx.client.config?.providers === undefined ? {} : {
23590
- listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
23591
- },
24535
+ ...ctx.listConfiguredInventory === undefined ? {} : { listConfiguredInventory: ctx.listConfiguredInventory },
23592
24536
  onRefreshError: (error) => logAvailabilityRefreshError(error, diagnostics.warn("availability-refresh-failed", { toast: false }))
23593
24537
  });
23594
24538
  let availabilityReady;
23595
- const ensureAvailability = () => {
24539
+ const ensureAvailability = async () => {
23596
24540
  availabilityReady ??= cache.refresh();
23597
- return availabilityReady;
24541
+ await availabilityReady;
24542
+ await cache.waitForRefresh();
23598
24543
  };
23599
24544
  const capabilityProfiles = () => {
23600
24545
  const availableModelKeys = new Set(cache.get().filter((model) => model.available).map((model) => `${model.providerID}\x00${model.modelID}`));
@@ -23623,17 +24568,19 @@ var server = async (ctx, rawOptions) => {
23623
24568
  const injectMemory = await buildMemoryInjectorFromConfig(config, storage, diagnostics.warn("memory-runtime-warning", { toast: false }));
23624
24569
  const sessionTracker = createSessionTracker();
23625
24570
  const orchestratedModelOwnership = createOrchestratedModelOwnership();
23626
- const modelResidency = createModelResidencyGuard(config, {
24571
+ const resolveAgentRole = async (agentName) => {
24572
+ const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
24573
+ return entry?.roleID;
24574
+ };
24575
+ const residencyOptions = {
23627
24576
  fetch: globalThis.fetch,
23628
- ...ctx.client.config?.providers === undefined ? {} : {
24577
+ resolveAgentRole,
24578
+ ...ctx.listConfiguredInventory === undefined ? {} : {
23629
24579
  resolveApiModelID: cache.resolveApiModelID,
23630
24580
  modelInventoryReady: ensureAvailability
23631
24581
  }
23632
- });
23633
- const resolveAgentRole = async (agentName) => {
23634
- const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
23635
- return entry?.roleID;
23636
24582
  };
24583
+ const modelResidency = (ctx.createResidencyGuard ?? createModelResidencyGuard)(config, residencyOptions);
23637
24584
  const hooks = createHooks(config, cache.get, injectMemory === undefined ? {
23638
24585
  sink,
23639
24586
  modelResidency,
@@ -23652,10 +24599,12 @@ var server = async (ctx, rawOptions) => {
23652
24599
  resolveAgentRole,
23653
24600
  onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
23654
24601
  });
23655
- const getOpencodeVersion = createLazyOpencodeVersionReader(ctx.serverUrl, {
24602
+ const getOpencodeVersion = ctx.opencodeVersion ?? (initialServerUrl === undefined ? async () => {
24603
+ throw new Error(nativeHostMessages.missingVersion);
24604
+ } : createLazyOpencodeVersionReader(initialServerUrl, {
23656
24605
  client: ctx.client,
23657
24606
  onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
23658
- });
24607
+ }));
23659
24608
  const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions, async () => {
23660
24609
  const version = await getOpencodeVersion();
23661
24610
  return version === "unknown" ? undefined : version;
@@ -23666,12 +24615,15 @@ var server = async (ctx, rawOptions) => {
23666
24615
  if (sessionID === undefined || announcedSessions.has(sessionID)) {
23667
24616
  return;
23668
24617
  }
23669
- announcedSessions.add(sessionID);
23670
- const endpoint = sessionEndpointEventFrom(sessionID, ctx.serverUrl, now, {
24618
+ const serverUrl = ctx.serverUrl();
24619
+ if (serverUrl === undefined)
24620
+ return;
24621
+ const endpoint = sessionEndpointEventFrom(sessionID, serverUrl, now, {
23671
24622
  directory: ctx.directory,
23672
24623
  worktree: ctx.worktree
23673
24624
  });
23674
24625
  if (endpoint !== undefined) {
24626
+ announcedSessions.add(sessionID);
23675
24627
  sink.emit(endpoint);
23676
24628
  }
23677
24629
  };
@@ -23723,7 +24675,7 @@ var server = async (ctx, rawOptions) => {
23723
24675
  holder: "openteam-plugin",
23724
24676
  lifecycleSource: "plugin",
23725
24677
  lifecycleRecorder,
23726
- exec: createShellExec(ctx.$),
24678
+ exec: ctx.exec,
23727
24679
  repoRoot: ctx.directory
23728
24680
  });
23729
24681
  const graphSurface = createGraphTool({
@@ -23759,8 +24711,8 @@ var server = async (ctx, rawOptions) => {
23759
24711
  rememberReportedSlots(snapshots);
23760
24712
  return reachableRuntimeIds(snapshots);
23761
24713
  };
23762
- const runtimeById = (key2) => {
23763
- const runtimeId = slotPoolRuntimeID(key2);
24714
+ const runtimeById = (key3) => {
24715
+ const runtimeId = slotPoolRuntimeID(key3);
23764
24716
  const runtime = config.local.runtimes.find((candidate) => candidate.id === runtimeId);
23765
24717
  if (runtime === undefined) {
23766
24718
  return;
@@ -23768,13 +24720,13 @@ var server = async (ctx, rawOptions) => {
23768
24720
  return withReportedModelSlots(runtime, reportedSlotsByRuntime.get(runtimeId), localModelAliases(runtime.defaultModel.providerID, cache.get()));
23769
24721
  };
23770
24722
  const runtimeLimiter = createRuntimeConcurrencyLimiter({
23771
- capacityFor: (key2) => {
23772
- const runtime = runtimeById(key2);
23773
- return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key2));
24723
+ capacityFor: (key3) => {
24724
+ const runtime = runtimeById(key3);
24725
+ return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key3));
23774
24726
  },
23775
- reservedInteractiveFor: (key2) => {
23776
- const runtime = runtimeById(key2);
23777
- return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key2));
24727
+ reservedInteractiveFor: (key3) => {
24728
+ const runtime = runtimeById(key3);
24729
+ return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key3));
23778
24730
  },
23779
24731
  now
23780
24732
  });
@@ -23801,8 +24753,8 @@ var server = async (ctx, rawOptions) => {
23801
24753
  lifecycleRecorder,
23802
24754
  abortGraceMs: DEFAULT_ABORT_GRACE_MS,
23803
24755
  statusPollMs: DEFAULT_STATUS_POLL_MS,
23804
- delay: (ms) => new Promise((resolve12) => {
23805
- setTimeout(resolve12, ms);
24756
+ delay: (ms) => new Promise((resolve14) => {
24757
+ setTimeout(resolve14, ms);
23806
24758
  }),
23807
24759
  warn: diagnostics.warn("unknown-role")
23808
24760
  });
@@ -23812,7 +24764,7 @@ var server = async (ctx, rawOptions) => {
23812
24764
  now,
23813
24765
  localRuntimeReachable: async (runtime, signal) => {
23814
24766
  if (localRuntimeKind(runtime) === "llama-swap") {
23815
- if (ctx.client.config?.providers !== undefined)
24767
+ if (ctx.listConfiguredInventory !== undefined)
23816
24768
  await ensureAvailability();
23817
24769
  signal?.throwIfAborted();
23818
24770
  const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
@@ -23826,7 +24778,7 @@ var server = async (ctx, rawOptions) => {
23826
24778
  return true;
23827
24779
  }
23828
24780
  if (localRuntimeKind(runtime) === "lemonade") {
23829
- if (ctx.client.config?.providers !== undefined)
24781
+ if (ctx.listConfiguredInventory !== undefined)
23830
24782
  await ensureAvailability();
23831
24783
  signal?.throwIfAborted();
23832
24784
  const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
@@ -23841,7 +24793,7 @@ var server = async (ctx, rawOptions) => {
23841
24793
  return anyRuntimeReachable(snapshots);
23842
24794
  },
23843
24795
  resolveExtractionTarget: async (model) => {
23844
- if (ctx.client.config?.providers !== undefined)
24796
+ if (ctx.listConfiguredInventory !== undefined)
23845
24797
  await ensureAvailability();
23846
24798
  return modelResidency.localTarget(model);
23847
24799
  },
@@ -23869,6 +24821,7 @@ var server = async (ctx, rawOptions) => {
23869
24821
  "chat.message": async (input, output) => {
23870
24822
  reload.userTurn(input.sessionID);
23871
24823
  await hooks["chat.message"]?.(input, output);
24824
+ announceEndpoint(input.sessionID);
23872
24825
  },
23873
24826
  dispose: async () => {
23874
24827
  reload.dispose();
@@ -23876,7 +24829,7 @@ var server = async (ctx, rawOptions) => {
23876
24829
  },
23877
24830
  tool: {
23878
24831
  openteam: createCommandTool(cliDeps),
23879
- "openteam-console": createConsoleTool(),
24832
+ "openteam-console": createConsoleTool(ctx.consoleEnvironment === undefined ? {} : { environment: ctx.consoleEnvironment }),
23880
24833
  "openteam-graph": graphSurface.definition,
23881
24834
  "openteam-orchestrate": orchestrateTool,
23882
24835
  "openteam-memory": memoryTool,
@@ -23913,10 +24866,21 @@ var server = async (ctx, rawOptions) => {
23913
24866
  }
23914
24867
  }
23915
24868
  };
23916
- };
24869
+ }
24870
+ var server = (ctx, rawOptions) => createPluginRuntime({
24871
+ directory: ctx.directory,
24872
+ worktree: ctx.worktree,
24873
+ client: ctx.client,
24874
+ exec: createShellExec(ctx.$),
24875
+ serverUrl: () => ctx.serverUrl,
24876
+ ...ctx.client.config?.providers === undefined ? {} : {
24877
+ listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
24878
+ }
24879
+ }, rawOptions);
23917
24880
  var plugin = {
23918
24881
  id: "openteam",
23919
- server
24882
+ server,
24883
+ effect: createNativePluginEffect(createPluginRuntime)
23920
24884
  };
23921
24885
  var src_default = plugin;
23922
24886
  export {
@@ -23934,6 +24898,7 @@ export {
23934
24898
  isMissingFile2 as isMissingFile,
23935
24899
  evaluateCutoverGateForBoundary,
23936
24900
  src_default as default,
24901
+ createPluginRuntime,
23937
24902
  createOpencodeVersionReadFailureLogger,
23938
24903
  createLazyOpencodeVersionReader,
23939
24904
  createEventSink,