@jmanuelcorral/openteam 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) 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.js +21871 -12535
  11. package/dist/commands/setup.d.ts +2 -0
  12. package/dist/commands/setup.d.ts.map +1 -1
  13. package/dist/commands/upgrade.d.ts +1 -1
  14. package/dist/commands/upgrade.d.ts.map +1 -1
  15. package/dist/config/fieldProjection.d.ts +3 -0
  16. package/dist/config/fieldProjection.d.ts.map +1 -0
  17. package/dist/config/nativeOpencode.d.ts +5 -0
  18. package/dist/config/nativeOpencode.d.ts.map +1 -0
  19. package/dist/console/auth.d.ts +5 -0
  20. package/dist/console/auth.d.ts.map +1 -0
  21. package/dist/console/conversation.d.ts +8 -1
  22. package/dist/console/conversation.d.ts.map +1 -1
  23. package/dist/console/historyBoundary.d.ts +1 -1
  24. package/dist/console/historyBoundary.d.ts.map +1 -1
  25. package/dist/console/nativeConversation.d.ts +7 -0
  26. package/dist/console/nativeConversation.d.ts.map +1 -0
  27. package/dist/console/opencodeClient.d.ts +3 -0
  28. package/dist/console/opencodeClient.d.ts.map +1 -1
  29. package/dist/console/pty.d.ts +4 -0
  30. package/dist/console/pty.d.ts.map +1 -1
  31. package/dist/contract/opencode.d.ts +13 -7
  32. package/dist/contract/opencode.d.ts.map +1 -1
  33. package/dist/index.d.ts +10 -5
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1150 -207
  36. package/dist/messages/consoleDetail.d.ts +3 -0
  37. package/dist/messages/consoleDetail.d.ts.map +1 -1
  38. package/dist/messages/nativeHost.d.ts +21 -0
  39. package/dist/messages/nativeHost.d.ts.map +1 -0
  40. package/dist/plugin/availability.d.ts +1 -0
  41. package/dist/plugin/availability.d.ts.map +1 -1
  42. package/dist/plugin/consoleTool.d.ts +1 -0
  43. package/dist/plugin/consoleTool.d.ts.map +1 -1
  44. package/dist/plugin/hooks.d.ts +3 -3
  45. package/dist/plugin/hooks.d.ts.map +1 -1
  46. package/dist/plugin/modelResidency.d.ts +1 -0
  47. package/dist/plugin/modelResidency.d.ts.map +1 -1
  48. package/dist/plugin/nativeHost.d.ts +5 -0
  49. package/dist/plugin/nativeHost.d.ts.map +1 -0
  50. package/dist/plugin/nativeHostContext.d.ts +228 -0
  51. package/dist/plugin/nativeHostContext.d.ts.map +1 -0
  52. package/dist/plugin/nativeInventory.d.ts +5 -0
  53. package/dist/plugin/nativeInventory.d.ts.map +1 -0
  54. package/dist/plugin/nativeResidency.d.ts +30 -0
  55. package/dist/plugin/nativeResidency.d.ts.map +1 -0
  56. package/dist/plugin/nativeSessions.d.ts +131 -0
  57. package/dist/plugin/nativeSessions.d.ts.map +1 -0
  58. package/dist/plugin/orchestrateTool.d.ts.map +1 -1
  59. package/dist/plugin/reloadTool.d.ts +3 -2
  60. package/dist/plugin/reloadTool.d.ts.map +1 -1
  61. package/dist/plugin/runtimeContext.d.ts +42 -0
  62. package/dist/plugin/runtimeContext.d.ts.map +1 -0
  63. package/dist/web/console.d.ts +1 -1
  64. package/dist/web/console.d.ts.map +1 -1
  65. package/dist/web/server.d.ts +1 -1
  66. package/dist/web/server.d.ts.map +1 -1
  67. package/dist/web/start.d.ts +1 -1
  68. package/dist/web/start.d.ts.map +1 -1
  69. 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.0",
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]+/, "");
@@ -7894,12 +7937,15 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
7894
7937
  } catch {
7895
7938
  return false;
7896
7939
  }
7897
- if (certified.major !== live.major || certified.minor !== live.minor) {
7940
+ if (certified.major !== 1 || certified.major !== live.major || certified.minor !== live.minor) {
7898
7941
  return false;
7899
7942
  }
7900
7943
  return live.patch >= certified.patch;
7901
7944
  }
7902
7945
 
7946
+ // src/graph/certificate.ts
7947
+ import { z as z17 } from "zod";
7948
+
7903
7949
  // src/graph/soakLedger.ts
7904
7950
  import { z as z16 } from "zod";
7905
7951
  var Sha256Schema2 = z16.string().regex(/^[0-9a-f]{64}$/);
@@ -10987,6 +11033,28 @@ function withReportedModelSlots(runtime, reported, aliases) {
10987
11033
  };
10988
11034
  }
10989
11035
 
11036
+ // src/messages/nativeHost.ts
11037
+ var nativeHostMessages = {
11038
+ invalidNativeConfig: "The existing native opencode plugins/providers configuration cannot be safely merged. Correct its object and plugin declaration shapes before running setup.",
11039
+ setupPolicyQuestion: (paths, providers) => `Remove exact project-local provider.use deny rules for ${providers.join(", ")} from ${paths.join(", ")}? Older setup added these rules, but they may also be operator limits. Confirm only if you intend to enable the guarded native transport.`,
11040
+ setupPolicyPreserved: "Setup stopped. Provider-denial rules were preserved and no files were written.",
11041
+ setupPolicyNotice: "Only the explicitly reviewed project rules were removed. Wildcard, scoped, unrelated and global provider denials remain authoritative.",
11042
+ unsupportedVersion: (version) => `openteam has not verified the native plugin contract for opencode ${version}. The supported native host is 2.0.5.`,
11043
+ missingBindings: "The native opencode execution context is unavailable. No child session or privileged operation was started.",
11044
+ missingVersion: "The opencode host did not provide an authoritative version or server endpoint.",
11045
+ workspaceMismatch: "The native opencode session does not belong to this workspace.",
11046
+ noCompletedResponse: "The native opencode session stopped without a completed assistant response.",
11047
+ executionFailed: "The native opencode session failed.",
11048
+ executionInterrupted: "The native opencode session was interrupted.",
11049
+ unknownAgent: "The native opencode session has no available agent.",
11050
+ noAvailableModel: "The native opencode host has no model available in the configured execution domain.",
11051
+ executionDomainBlocked: "The native opencode request is outside the configured execution domain.",
11052
+ transportUnavailable: "This local runtime requires the guarded @ai-sdk/openai-compatible transport. Native and WebSocket transports are not admitted.",
11053
+ missingAdmission: "The native local request has no matching model authorization. No inference was sent.",
11054
+ toolFailed: "The openteam tool failed in the native opencode host.",
11055
+ eventCaptureFailed: "Native opencode event capture failed. The console may be missing activity; reload the plugin after resolving the host error."
11056
+ };
11057
+
10990
11058
  // src/messages/orchestration.ts
10991
11059
  var HOST_SESSION_REQUIRED = "Orchestration requires a nonempty host ToolContext sessionID.";
10992
11060
  var PARENT_OVERRIDE_IGNORED = "parentSessionID override ignored: the trusted host ToolContext session owns this orchestration and its children. Programmatic callers must supply their parent as the ToolContext sessionID.";
@@ -11967,6 +12035,10 @@ function createAvailabilityCache(options) {
11967
12035
  ref: { ...profile.ref },
11968
12036
  costPer1M: { ...profile.costPer1M }
11969
12037
  })),
12038
+ waitForRefresh: async () => {
12039
+ while (inFlight !== undefined)
12040
+ await inFlight;
12041
+ },
11970
12042
  refresh: () => {
11971
12043
  if (inFlight === undefined) {
11972
12044
  cached = cached.map((model) => requiresModelResidency(options.config, model) ? { ...model, available: false } : model);
@@ -13801,6 +13873,16 @@ function renderMigration(report) {
13801
13873
  // src/commands/purge.ts
13802
13874
  import * as path3 from "node:path";
13803
13875
 
13876
+ // src/config/fieldProjection.ts
13877
+ function only(keys) {
13878
+ return (value) => {
13879
+ if (typeof value !== "object" || value === null || Array.isArray(value))
13880
+ return value;
13881
+ const record = value;
13882
+ return Object.fromEntries(keys.filter((key) => Object.hasOwn(record, key)).map((key) => [key, record[key]]));
13883
+ };
13884
+ }
13885
+
13804
13886
  // src/storage/graph/soakRecorder.ts
13805
13887
  var SOAK_EVIDENCE_DIR = ".opencode/soak";
13806
13888
  var SOAK_RECORDER_BASE_DIR = ".opencode";
@@ -15595,7 +15677,7 @@ function createCommandTool(deps) {
15595
15677
  // src/plugin/consoleTool.ts
15596
15678
  import { spawn as nodeSpawn } from "node:child_process";
15597
15679
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
15598
- import { mkdir as mkdir4, open as open3 } from "node:fs/promises";
15680
+ import { mkdir as mkdir4, open as open3, stat as stat3 } from "node:fs/promises";
15599
15681
  import { dirname as dirname6, join as join13, resolve as resolve8 } from "node:path";
15600
15682
  import { fileURLToPath as fileURLToPath2 } from "node:url";
15601
15683
  import { tool as tool2 } from "@opencode-ai/plugin";
@@ -15605,8 +15687,8 @@ import {
15605
15687
  mkdir as mkdir3,
15606
15688
  open as open2,
15607
15689
  readFile as readFile4,
15608
- unlink,
15609
- writeFile as writeFile2
15690
+ rename as rename2,
15691
+ unlink
15610
15692
  } from "node:fs/promises";
15611
15693
  import { dirname as dirname5, join as join12, resolve as resolve7 } from "node:path";
15612
15694
  import { z as z22 } from "zod";
@@ -15906,10 +15988,17 @@ async function acquireLaunchLockOrWait(workspaceRoot, paths, launchId, deps) {
15906
15988
  currentLock = await readConsoleLaunchLock(paths.launchLockPath);
15907
15989
  } catch (error) {
15908
15990
  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 };
15991
+ const info = await stat3(paths.launchLockPath).catch((error2) => {
15992
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
15993
+ return;
15994
+ throw error2;
15995
+ });
15996
+ if (info === undefined || deps.now() - info.mtimeMs > LAUNCH_LOCK_STALE_MS) {
15997
+ await removeConsoleLaunchLock(paths.launchLockPath);
15998
+ const retry = await acquireConsoleLaunchLock(paths.launchLockPath, attemptLock);
15999
+ if (retry !== "busy") {
16000
+ return { kind: "acquired", release: retry.release };
16001
+ }
15913
16002
  }
15914
16003
  } else {
15915
16004
  throw error;
@@ -15982,7 +16071,8 @@ async function spawnDetachedConsole(workspaceRoot, paths, launchId, deps) {
15982
16071
  ], {
15983
16072
  cwd: workspaceRoot,
15984
16073
  detached: true,
15985
- stdio: ["ignore", logHandle.fd, logHandle.fd]
16074
+ stdio: ["ignore", logHandle.fd, logHandle.fd],
16075
+ ...deps.environment === undefined ? {} : { env: deps.environment() }
15986
16076
  });
15987
16077
  child.unref?.();
15988
16078
  return child;
@@ -16017,7 +16107,8 @@ function createConsoleTool(deps = {}) {
16017
16107
  sendSignal,
16018
16108
  sleep,
16019
16109
  resolveCliEntrypoint,
16020
- spawn
16110
+ spawn,
16111
+ ...deps.environment === undefined ? {} : { environment: deps.environment }
16021
16112
  };
16022
16113
  if (args.action === "status") {
16023
16114
  return summaryFromProbe(await readRuntimeProbe(workspaceRoot, {
@@ -16135,10 +16226,7 @@ function createConsoleTool(deps = {}) {
16135
16226
  });
16136
16227
  }
16137
16228
  await removeConsoleRuntimeState(paths.runtimeStatePath);
16138
- const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, {
16139
- resolveCliEntrypoint: runtimeDeps.resolveCliEntrypoint,
16140
- spawn: runtimeDeps.spawn
16141
- });
16229
+ const child = await spawnDetachedConsole(workspaceRoot, paths, launchId, runtimeDeps);
16142
16230
  const probe = await waitForHealthyRuntime(workspaceRoot, START_TIMEOUT_MS, {
16143
16231
  fetch: runtimeDeps.fetch,
16144
16232
  processExists: runtimeDeps.processExists,
@@ -18037,8 +18125,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
18037
18125
  await injectMemoryBestEffort(options.injectMemory, prompt, output);
18038
18126
  return;
18039
18127
  }
18040
- await options.beforeRoute?.();
18041
18128
  const role = await roleForAgentName(config, input.agent ?? output.message.agent, options.resolveAgentRole);
18129
+ await options.beforeRoute?.();
18042
18130
  const derivedSignals = deriveTaskSignals(input, output);
18043
18131
  const signals = role === undefined ? derivedSignals : withoutExplicitOverride(derivedSignals);
18044
18132
  const executionPolicy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
@@ -18092,8 +18180,8 @@ function createChatMessageHook(config, getAvailable, options = {}) {
18092
18180
  function createHooks(config, getAvailable, options = {}) {
18093
18181
  const residency = options.modelResidency ?? createModelResidencyGuard(config, { fetch: globalThis.fetch });
18094
18182
  return {
18095
- config: residency.config,
18096
- "chat.params": residency.beforeInference,
18183
+ ...residency.config === undefined ? {} : { config: residency.config },
18184
+ ...residency.beforeInference === undefined ? {} : { "chat.params": residency.beforeInference },
18097
18185
  "chat.message": createChatMessageHook(config, getAvailable, options)
18098
18186
  };
18099
18187
  }
@@ -19243,12 +19331,841 @@ function createOrchestratedModelOwnership() {
19243
19331
  };
19244
19332
  }
19245
19333
 
19334
+ // src/plugin/nativeHost.ts
19335
+ import { AsyncLocalStorage } from "node:async_hooks";
19336
+ import { execFile as execFile3 } from "node:child_process";
19337
+ import { resolve as resolve10 } from "node:path";
19338
+ import { Model as Model2 } from "@opencode/schema/model";
19339
+ import { Session as Session2 } from "@opencode/schema/session";
19340
+ import { Tool } from "@opencode/schema/tool";
19341
+ import { tool as legacyTool } from "@opencode-ai/plugin/tool";
19342
+ import { Clock, Effect, Option, Stream } from "effect";
19343
+ import { z as z30 } from "zod";
19344
+
19345
+ // src/plugin/nativeHostContext.ts
19346
+ import {
19347
+ Context
19348
+ } from "effect";
19349
+
19350
+ class NativeSessions extends Context.Service()("@opencode/Session") {
19351
+ }
19352
+
19353
+ class NativePermissions extends Context.Service()("@opencode/Permission") {
19354
+ }
19355
+
19356
+ class NativeAgents extends Context.Service()("@opencode/Agent") {
19357
+ }
19358
+
19359
+ class NativeLocations extends Context.Service()("@opencode/example/LocationServiceMap") {
19360
+ }
19361
+
19362
+ class NativeServerInfo extends Context.Service()("@opencode/server/ServerInfo") {
19363
+ }
19364
+
19365
+ class NativeServerAuth extends Context.Service()("@opencode/ServerAuthConfig") {
19366
+ }
19367
+
19368
+ // src/plugin/nativeInventory.ts
19369
+ function nativeConfiguredInventory(models, config) {
19370
+ const local = new Set(config.local.runtimes.map((runtime) => runtime.defaultModel.providerID));
19371
+ const enabled = models.filter((model) => model.enabled);
19372
+ const catalog = new Map;
19373
+ for (const model of enabled) {
19374
+ if (local.has(model.providerID))
19375
+ continue;
19376
+ const provider = catalog.get(model.providerID) ?? { models: [] };
19377
+ provider.models.push({
19378
+ id: model.id,
19379
+ name: model.name,
19380
+ limit: model.limit,
19381
+ cost: model.cost.find((tier) => tier.tier === undefined),
19382
+ status: model.status,
19383
+ tool_call: model.capabilities.tools,
19384
+ modalities: { input: model.capabilities.input },
19385
+ reasoning: model.capabilities.output.includes("reasoning")
19386
+ });
19387
+ catalog.set(model.providerID, provider);
19388
+ }
19389
+ return {
19390
+ models: enabled.map((model) => ({
19391
+ providerID: model.providerID,
19392
+ modelID: model.id,
19393
+ apiModelID: model.modelID,
19394
+ ...local.has(model.providerID) ? {} : {
19395
+ supportsTools: model.capabilities.tools,
19396
+ ...model.limit.context > 0 ? { contextWindow: model.limit.context } : {}
19397
+ }
19398
+ })),
19399
+ profiles: normalizeModelsDevCatalog(Object.fromEntries(catalog))
19400
+ };
19401
+ }
19402
+
19403
+ // src/plugin/nativeResidency.ts
19404
+ import { randomUUID as randomUUID2 } from "node:crypto";
19405
+ import { z as z29 } from "zod";
19406
+ var NATIVE_RESIDENCY_PROVIDER_PACKAGE = "aisdk:@ai-sdk/openai-compatible@2.0.75";
19407
+ var NATIVE_ADMISSION_HEADER = "x-openteam-local-admission";
19408
+ var MAX_ADMISSIONS = 2048;
19409
+ var compatiblePackages = new Set([
19410
+ "@ai-sdk/openai-compatible",
19411
+ "aisdk:@ai-sdk/openai-compatible",
19412
+ "@opencode/ai/providers/openai-compatible",
19413
+ NATIVE_RESIDENCY_PROVIDER_PACKAGE
19414
+ ]);
19415
+ function key(ref) {
19416
+ return `${ref.providerID}\x00${ref.modelID}`;
19417
+ }
19418
+ function label(runtime) {
19419
+ return localRuntimeKind(runtime) === "llama-swap" ? "llama-swap" : "Lemonade";
19420
+ }
19421
+ function runtimeBase2(runtime) {
19422
+ return localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(runtime.baseURL ?? LLAMA_SWAP_DEFAULT_BASE_URL) : lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL);
19423
+ }
19424
+ function createNativeModelResidencyGuard(config, options) {
19425
+ const admissions = new Map;
19426
+ const localTargets = new Map;
19427
+ let disposed = false;
19428
+ const residentRuntimes = config.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime)));
19429
+ const globalMode = () => resolveExecutionMode({
19430
+ routerMode: config.router.executionMode,
19431
+ legacyLocalOnly: config.router.localOnly,
19432
+ legacyFrontierOnly: config.router.frontierOnly
19433
+ });
19434
+ function allowed(providerID, mode) {
19435
+ const local = config.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
19436
+ return local.length > 0 ? mode !== "frontier" && local.some((runtime) => runtime.enabled) : mode !== "local";
19437
+ }
19438
+ function protects(providerID, url) {
19439
+ return residentRuntimes.some((runtime) => {
19440
+ if (runtime.defaultModel.providerID === providerID)
19441
+ return true;
19442
+ if (url === undefined)
19443
+ return false;
19444
+ if (!URL.canParse(url))
19445
+ return true;
19446
+ return new URL(url).origin === new URL(runtimeBase2(runtime)).origin;
19447
+ });
19448
+ }
19449
+ function bind(model) {
19450
+ const runtimes = config.local.runtimes.filter((runtime2) => runtime2.defaultModel.providerID === model.providerID);
19451
+ const runtime = runtimes[0];
19452
+ const runtimeLabel = runtime === undefined ? undefined : label(runtime);
19453
+ if (runtimes.length !== 1 || runtime === undefined || !runtime.enabled)
19454
+ throw new ModelResidencyError("destination", runtimeLabel);
19455
+ const baseURL = runtimeBase2(runtime);
19456
+ const base = model.settings?.baseURL;
19457
+ const normalized = typeof base !== "string" ? undefined : localRuntimeKind(runtime) === "llama-swap" ? llamaSwapBaseURL(base) : lemonadeBaseURL(base);
19458
+ if (normalized !== baseURL)
19459
+ throw new ModelResidencyError("destination", runtimeLabel);
19460
+ if (model.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE || model.transport === "websocket" || model.settings?.fetch !== undefined || model.settings?.queryParams !== undefined || model.settings?.transformRequestBody !== undefined)
19461
+ throw new ModelResidencyError("transport", runtimeLabel);
19462
+ if (!model.modelID || model.modelID !== model.modelID.trim() || model.body?.model !== undefined && model.body.model !== model.modelID)
19463
+ throw new ModelResidencyError("identity", runtimeLabel);
19464
+ return { runtime, model, baseURL };
19465
+ }
19466
+ function guardedFetch(binding, send, authorized = () => true) {
19467
+ const create = localRuntimeKind(binding.runtime) === "llama-swap" ? createLlamaSwapGuardedFetch : createLemonadeGuardedFetch;
19468
+ return create({
19469
+ ...options,
19470
+ fetch: Object.assign((input, init) => {
19471
+ if (disposed || !authorized())
19472
+ throw new Error(nativeHostMessages.missingAdmission);
19473
+ return send(input, init);
19474
+ }, {
19475
+ preconnect: (...args) => send.preconnect?.(...args)
19476
+ }),
19477
+ healthFetch: options.fetch,
19478
+ baseURL: binding.baseURL,
19479
+ modelIDs: new Set([binding.model.modelID])
19480
+ });
19481
+ }
19482
+ function target(model, send, baseURL) {
19483
+ const headers = new Headers(z29.record(z29.string(), z29.string()).parse(model.headers ?? {}));
19484
+ if (typeof model.settings?.apiKey === "string" && !headers.has("authorization"))
19485
+ headers.set("authorization", `Bearer ${model.settings.apiKey}`);
19486
+ return {
19487
+ baseURL,
19488
+ modelID: model.modelID,
19489
+ fetch: Object.assign(async (input, init) => {
19490
+ if (disposed)
19491
+ throw new Error(nativeHostMessages.missingAdmission);
19492
+ const request = new Request(input, init);
19493
+ for (const [name, value] of headers)
19494
+ if (!request.headers.has(name))
19495
+ request.headers.set(name, value);
19496
+ return send(request);
19497
+ }, {
19498
+ preconnect: (...args) => send.preconnect?.(...args)
19499
+ })
19500
+ };
19501
+ }
19502
+ function observeModels(models) {
19503
+ localTargets.clear();
19504
+ return models.map((model) => {
19505
+ if (!allowed(model.providerID, globalMode()))
19506
+ return { ...model, enabled: false };
19507
+ if (!model.enabled)
19508
+ return model;
19509
+ const ref = { providerID: model.providerID, modelID: model.id };
19510
+ const base = model.settings?.baseURL;
19511
+ if (!protects(model.providerID, typeof base === "string" ? base : undefined)) {
19512
+ if (config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === model.providerID) && typeof base === "string")
19513
+ localTargets.set(key(ref), target(model, options.fetch, base));
19514
+ return model;
19515
+ }
19516
+ try {
19517
+ const binding = bind(model);
19518
+ localTargets.set(key(ref), target(model, guardedFetch(binding, options.fetch), binding.baseURL));
19519
+ return model;
19520
+ } catch (error) {
19521
+ if (!(error instanceof ModelResidencyError) && !(error instanceof z29.ZodError))
19522
+ throw error;
19523
+ (options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(model.providerID, residentRuntimes.find((runtime) => runtime.defaultModel.providerID === model.providerID)?.kind === "llama-swap" ? "llama-swap" : "Lemonade"));
19524
+ return { ...model, enabled: false };
19525
+ }
19526
+ });
19527
+ }
19528
+ return {
19529
+ providers(editor) {
19530
+ for (const record of editor.list())
19531
+ if (!allowed(record.provider.id, globalMode()))
19532
+ editor.remove(record.provider.id);
19533
+ },
19534
+ transform(editor) {
19535
+ for (const model of editor.list()) {
19536
+ const provider = editor.provider.get(model.providerID)?.provider;
19537
+ const base = model.settings?.baseURL ?? provider?.settings?.baseURL;
19538
+ const pkg = model.package ?? provider?.package;
19539
+ if (protects(model.providerID, typeof base === "string" ? base : undefined) && pkg !== undefined && compatiblePackages.has(pkg))
19540
+ editor.update(model.providerID, model.id, (draft) => {
19541
+ draft.package = NATIVE_RESIDENCY_PROVIDER_PACKAGE;
19542
+ });
19543
+ }
19544
+ },
19545
+ observeModels,
19546
+ sdk(event) {
19547
+ if (!protects(event.model.providerID, typeof event.options.baseURL === "string" ? event.options.baseURL : undefined))
19548
+ return;
19549
+ const factory = bind(event.model);
19550
+ if (event.package !== NATIVE_RESIDENCY_PROVIDER_PACKAGE.slice("aisdk:".length) || typeof event.options.fetch !== "function")
19551
+ throw new Error(nativeHostMessages.transportUnavailable);
19552
+ const upstream = event.options.fetch;
19553
+ const transport = Object.assign((input, init) => {
19554
+ const request = new Request(input, init);
19555
+ return upstream(request, {
19556
+ signal: request.signal,
19557
+ redirect: "error"
19558
+ });
19559
+ }, {
19560
+ preconnect: (...args) => upstream.preconnect?.(...args)
19561
+ });
19562
+ event.options.fetch = Object.assign(async (input, init) => {
19563
+ let request = new Request(input, init);
19564
+ const token = request.headers.get(NATIVE_ADMISSION_HEADER);
19565
+ const admission = token === null ? undefined : admissions.get(token);
19566
+ if (token === null || admission === undefined || admission.model.providerID !== factory.model.providerID || admission.baseURL !== factory.baseURL)
19567
+ throw new Error(nativeHostMessages.missingAdmission);
19568
+ request.headers.delete(NATIVE_ADMISSION_HEADER);
19569
+ if (factory.model.body !== undefined) {
19570
+ let body;
19571
+ try {
19572
+ body = await request.json();
19573
+ } catch {
19574
+ throw new ModelResidencyError("request", label(factory.runtime));
19575
+ }
19576
+ if (body === null || typeof body !== "object" || Array.isArray(body))
19577
+ throw new ModelResidencyError("request", label(factory.runtime));
19578
+ request = new Request(request, {
19579
+ body: JSON.stringify(mergeConfigInputs(body, factory.model.body))
19580
+ });
19581
+ }
19582
+ return guardedFetch(admission, transport, () => admissions.get(token) === admission)(request);
19583
+ }, {
19584
+ preconnect: (...args) => upstream.preconnect?.(...args)
19585
+ });
19586
+ },
19587
+ async modelRequest(event) {
19588
+ if (disposed)
19589
+ throw new Error(nativeHostMessages.missingAdmission);
19590
+ const roleID = await options.resolveAgentRole?.(event.agent) ?? event.agent;
19591
+ const role = isKnownNonWorkerRole(roleID) ? undefined : getRoleProfile(roleID, config.orchestrator?.roles);
19592
+ const policy = role === undefined ? config.orchestrator?.primary : agentExecutionPolicyForRole(role);
19593
+ const mode = resolveExecutionMode({
19594
+ routerMode: globalMode(),
19595
+ agentMode: policy?.executionMode
19596
+ });
19597
+ if (!allowed(event.model.providerID, mode))
19598
+ throw new Error(nativeHostMessages.executionDomainBlocked);
19599
+ if (!protects(event.model.providerID, event.baseURL))
19600
+ return;
19601
+ const model = (await options.readModels()).find((candidate) => candidate.providerID === event.model.providerID && candidate.id === event.model.id && candidate.enabled);
19602
+ if (model === undefined)
19603
+ throw new Error(nativeHostMessages.transportUnavailable);
19604
+ const variant = model.variants.find((candidate) => candidate.id === event.model.variant);
19605
+ const effective = variant === undefined ? model : {
19606
+ ...model,
19607
+ settings: { ...model.settings, ...variant.settings },
19608
+ headers: { ...model.headers, ...variant.headers },
19609
+ body: { ...model.body, ...variant.body }
19610
+ };
19611
+ const binding = bind(effective);
19612
+ if (disposed)
19613
+ throw new Error(nativeHostMessages.missingAdmission);
19614
+ const token = (options.newRequestID ?? randomUUID2)();
19615
+ admissions.set(token, {
19616
+ ...binding,
19617
+ sessionID: event.sessionID,
19618
+ kind: event.kind
19619
+ });
19620
+ const oldest = admissions.keys().next().value;
19621
+ if (admissions.size > MAX_ADMISSIONS && oldest !== undefined)
19622
+ admissions.delete(oldest);
19623
+ event.headers[NATIVE_ADMISSION_HEADER] = token;
19624
+ },
19625
+ async http(event) {
19626
+ if (protects(event.model.providerID, event.request.url))
19627
+ throw new Error(nativeHostMessages.transportUnavailable);
19628
+ },
19629
+ websocket(event) {
19630
+ const url = new URL(event.url);
19631
+ if (url.protocol === "ws:")
19632
+ url.protocol = "http:";
19633
+ if (url.protocol === "wss:")
19634
+ url.protocol = "https:";
19635
+ if (protects(event.model.providerID, url.href))
19636
+ throw new Error(nativeHostMessages.transportUnavailable);
19637
+ },
19638
+ finished(sessionID) {
19639
+ for (const [token, admission] of admissions)
19640
+ if (admission.sessionID === sessionID)
19641
+ admissions.delete(token);
19642
+ },
19643
+ dispose() {
19644
+ disposed = true;
19645
+ admissions.clear();
19646
+ localTargets.clear();
19647
+ },
19648
+ localTarget(ref) {
19649
+ const value = localTargets.get(key(ref));
19650
+ if (value === undefined)
19651
+ throw new Error(nativeHostMessages.transportUnavailable);
19652
+ return value;
19653
+ }
19654
+ };
19655
+ }
19656
+
19657
+ // src/plugin/nativeSessions.ts
19658
+ import { resolve as resolve9 } from "node:path";
19659
+ import { Agent } from "@opencode/schema/agent";
19660
+ import { Model } from "@opencode/schema/model";
19661
+ import { Session } from "@opencode/schema/session";
19662
+ import { DateTime } from "effect";
19663
+ function sameNativeWorkspace(actual, expected) {
19664
+ return resolve9(actual.directory) === resolve9(expected.directory) && actual.workspaceID === expected.workspaceID;
19665
+ }
19666
+ function nativeAssistantResponse(sessionID, message) {
19667
+ return {
19668
+ info: {
19669
+ id: message.id,
19670
+ sessionID,
19671
+ role: "assistant",
19672
+ mode: message.agent,
19673
+ providerID: message.model.providerID,
19674
+ modelID: message.model.id,
19675
+ ...message.cost === undefined ? {} : { cost: message.cost },
19676
+ ...message.tokens === undefined ? {} : { tokens: message.tokens },
19677
+ ...message.error === undefined ? {} : { error: message.error },
19678
+ time: {
19679
+ created: DateTime.toEpochMillis(message.time.created),
19680
+ ...message.time.completed === undefined ? {} : { completed: DateTime.toEpochMillis(message.time.completed) }
19681
+ }
19682
+ },
19683
+ parts: message.content.flatMap((part) => part.type === "text" ? [{ type: "text", text: part.text }] : [])
19684
+ };
19685
+ }
19686
+ function createNativeSessionClient(deps) {
19687
+ const directory = resolve9(deps.directory);
19688
+ const location = {
19689
+ directory,
19690
+ ...deps.workspaceID === undefined ? {} : { workspaceID: deps.workspaceID }
19691
+ };
19692
+ function assertDirectory(candidate) {
19693
+ if (candidate !== undefined && resolve9(candidate) !== directory)
19694
+ throw new Error(nativeHostMessages.workspaceMismatch);
19695
+ }
19696
+ async function ownedSession(id, signal) {
19697
+ const info = await deps.run(deps.sessions().get(id), signal === undefined ? undefined : { signal });
19698
+ if (!sameNativeWorkspace(info.location, location))
19699
+ throw new Error(nativeHostMessages.workspaceMismatch);
19700
+ return info;
19701
+ }
19702
+ return {
19703
+ async create(args) {
19704
+ const signal = deps.signal?.();
19705
+ signal?.throwIfAborted();
19706
+ assertDirectory(args.query?.directory);
19707
+ if (args.body?.parentID === undefined)
19708
+ throw new Error(nativeHostMessages.missingBindings);
19709
+ const parentID = Session.ID.make(args.body.parentID);
19710
+ await ownedSession(parentID, signal);
19711
+ const child = await deps.run(deps.sessions().create({
19712
+ parentID,
19713
+ ...args.body.title === undefined ? {} : { title: args.body.title }
19714
+ }));
19715
+ if (!sameNativeWorkspace(child.location, location))
19716
+ throw new Error(nativeHostMessages.workspaceMismatch);
19717
+ if (child.parentID !== parentID)
19718
+ throw new Error(nativeHostMessages.missingBindings);
19719
+ signal?.throwIfAborted();
19720
+ return { data: { id: child.id }, error: undefined };
19721
+ },
19722
+ async prompt(args) {
19723
+ const parentSignal = deps.signal?.();
19724
+ const signal = parentSignal === undefined ? args.signal : args.signal === undefined ? parentSignal : AbortSignal.any([parentSignal, args.signal]);
19725
+ const sessionID = Session.ID.make(args.path.id);
19726
+ await ownedSession(sessionID, signal);
19727
+ const run = (effect) => deps.run(effect, signal === undefined ? undefined : { signal });
19728
+ const sessions = deps.sessions();
19729
+ try {
19730
+ if (args.body.agent !== undefined)
19731
+ await run(sessions.switchAgent({
19732
+ sessionID,
19733
+ agent: Agent.ID.make(args.body.agent)
19734
+ }));
19735
+ if (args.body.model !== undefined)
19736
+ await run(sessions.switchModel({
19737
+ sessionID,
19738
+ model: Model.Ref.parse(`${args.body.model.providerID}/${args.body.model.modelID}`)
19739
+ }));
19740
+ if (args.body.system !== undefined)
19741
+ deps.systemPrompts.set(sessionID, args.body.system);
19742
+ await run(sessions.prompt({
19743
+ sessionID,
19744
+ text: args.body.parts.map((part) => part.text).join(`
19745
+ `)
19746
+ }));
19747
+ await run(sessions.wait(sessionID));
19748
+ const final = await ownedSession(sessionID, signal);
19749
+ const messages = await run(sessions.messages({ sessionID, limit: 100 }));
19750
+ const assistant = [...messages].reverse().find((message) => message.type === "assistant");
19751
+ if (final.outcome === "interrupted")
19752
+ throw new Error(nativeHostMessages.executionInterrupted);
19753
+ if (final.outcome !== "succeeded" || assistant?.error !== undefined)
19754
+ throw new Error(nativeHostMessages.executionFailed);
19755
+ if (assistant?.time.completed === undefined)
19756
+ throw new Error(nativeHostMessages.noCompletedResponse);
19757
+ return {
19758
+ data: nativeAssistantResponse(sessionID, assistant),
19759
+ error: undefined
19760
+ };
19761
+ } finally {
19762
+ if (signal?.aborted)
19763
+ await deps.run(sessions.interrupt(sessionID, { resume: false }));
19764
+ }
19765
+ },
19766
+ async abort(args) {
19767
+ assertDirectory(args.query?.directory);
19768
+ const id = Session.ID.make(args.path.id);
19769
+ await ownedSession(id, args.signal);
19770
+ const options = args.signal === undefined ? undefined : { signal: args.signal };
19771
+ await deps.run(deps.sessions().interrupt(id, { resume: false }), options);
19772
+ const active = await deps.run(deps.sessions().active, options);
19773
+ return { data: !active.has(id), error: undefined };
19774
+ },
19775
+ async status(args) {
19776
+ assertDirectory(args?.query?.directory);
19777
+ const options = args?.signal == null ? undefined : { signal: args.signal };
19778
+ const sessions = deps.sessions();
19779
+ const active = await deps.run(sessions.active, options);
19780
+ const entries = [];
19781
+ for (const id of active) {
19782
+ const info = await deps.run(sessions.get(id), options);
19783
+ if (sameNativeWorkspace(info.location, location))
19784
+ entries.push([id, { type: "busy" }]);
19785
+ }
19786
+ return { data: Object.fromEntries(entries), error: undefined };
19787
+ }
19788
+ };
19789
+ }
19790
+
19791
+ // src/plugin/nativeHost.ts
19792
+ var executionEvent = z30.preprocess(only(["type", "location", "data"]), z30.object({
19793
+ type: z30.enum([
19794
+ "session.created",
19795
+ "session.deleted",
19796
+ "session.execution.started",
19797
+ "session.execution.succeeded",
19798
+ "session.execution.failed",
19799
+ "session.execution.interrupted",
19800
+ "session.step.ended",
19801
+ "session.step.failed"
19802
+ ]),
19803
+ location: z30.preprocess(only(["directory", "workspaceID"]), z30.object({
19804
+ directory: z30.string(),
19805
+ workspaceID: z30.string().optional()
19806
+ }).strict()).optional(),
19807
+ data: z30.preprocess(only(["sessionID", "assistantMessageID"]), z30.object({
19808
+ sessionID: z30.string(),
19809
+ assistantMessageID: z30.string().optional()
19810
+ }).strict())
19811
+ }).strict());
19812
+ var nativeBindings = Effect.fn("openteam.nativeBindings")(function* () {
19813
+ const sessions = yield* Effect.serviceOption(NativeSessions);
19814
+ const permissions = yield* Effect.serviceOption(NativePermissions);
19815
+ const agents = yield* Effect.serviceOption(NativeAgents);
19816
+ const locations = yield* Effect.serviceOption(NativeLocations);
19817
+ if (Option.isNone(sessions) || Option.isNone(permissions) || Option.isNone(agents) || Option.isNone(locations))
19818
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19819
+ const server = yield* Effect.serviceOption(NativeServerInfo);
19820
+ const auth = yield* Effect.serviceOption(NativeServerAuth);
19821
+ return {
19822
+ sessions: sessions.value,
19823
+ permissions: permissions.value,
19824
+ agents: agents.value,
19825
+ locations: locations.value,
19826
+ ...Option.isNone(server) ? {} : { server: server.value },
19827
+ ...Option.isNone(auth) ? {} : { auth: auth.value }
19828
+ };
19829
+ });
19830
+ function nativeExec(directory) {
19831
+ return (command, args) => new Promise((resolveOutput, reject) => {
19832
+ execFile3(command, [...args], { cwd: directory }, (error, stdout, stderr) => {
19833
+ if (error !== null && typeof error.code !== "number") {
19834
+ reject(error);
19835
+ return;
19836
+ }
19837
+ resolveOutput({
19838
+ exitCode: typeof error?.code === "number" ? error.code : 0,
19839
+ stdout,
19840
+ stderr
19841
+ });
19842
+ });
19843
+ });
19844
+ }
19845
+ function legacyModel(ref) {
19846
+ return { providerID: ref.providerID, modelID: ref.id };
19847
+ }
19848
+ function createNativePluginEffect(factory) {
19849
+ return Effect.fn("openteam.native")(function* (ctx) {
19850
+ if (ctx.app.version !== NATIVE_OPENCODE_VERSION)
19851
+ return yield* Effect.die(new Error(nativeHostMessages.unsupportedVersion(ctx.app.version)));
19852
+ const version = ctx.app.version;
19853
+ const effectContext = yield* Effect.context();
19854
+ const directory = resolve10(ctx.location.directory);
19855
+ let bindings;
19856
+ const run = (effect, options) => {
19857
+ const current = bindings;
19858
+ const bound = current === undefined ? effect : effect.pipe(Effect.provideService(NativeSessions, current.sessions), Effect.provideService(NativePermissions, current.permissions), Effect.provideService(NativeAgents, current.agents), Effect.provideService(NativeLocations, current.locations));
19859
+ return Effect.runPromiseWith(effectContext)(bound, options);
19860
+ };
19861
+ let config;
19862
+ let residency;
19863
+ const systemPrompts = new Map;
19864
+ const toolSignals = new AsyncLocalStorage;
19865
+ const remember = (next) => {
19866
+ bindings = { ...bindings, ...next };
19867
+ };
19868
+ const requiredBindings = () => {
19869
+ if (bindings === undefined)
19870
+ throw new Error(nativeHostMessages.missingBindings);
19871
+ return bindings;
19872
+ };
19873
+ const assertWorkspace = (candidate) => {
19874
+ if (!sameNativeWorkspace(candidate, ctx.location))
19875
+ throw new Error(nativeHostMessages.workspaceMismatch);
19876
+ };
19877
+ const session = createNativeSessionClient({
19878
+ directory,
19879
+ ...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID },
19880
+ sessions: () => requiredBindings().sessions,
19881
+ run,
19882
+ systemPrompts,
19883
+ signal: () => toolSignals.getStore()
19884
+ });
19885
+ const client = {
19886
+ session,
19887
+ app: {
19888
+ async agents() {
19889
+ const result = await run(ctx.agent.list());
19890
+ return {
19891
+ data: result.data.map((agent) => ({ name: agent.id })),
19892
+ error: undefined
19893
+ };
19894
+ },
19895
+ async log(input) {
19896
+ const message = input.body?.message ?? nativeHostMessages.toolFailed;
19897
+ await run(input.body?.level === "error" ? Effect.logError(message) : input.body?.level === "warn" ? Effect.logWarning(message) : Effect.logInfo(message));
19898
+ }
19899
+ },
19900
+ instance: {
19901
+ async dispose(input) {
19902
+ assertWorkspace({
19903
+ ...ctx.location,
19904
+ directory: input.query?.directory ?? directory
19905
+ });
19906
+ await run(requiredBindings().locations.invalidate({
19907
+ directory: ctx.location.directory,
19908
+ ...ctx.location.workspaceID === undefined ? {} : { workspaceID: ctx.location.workspaceID }
19909
+ }), input.signal == null ? undefined : { signal: input.signal });
19910
+ return { data: true, error: undefined };
19911
+ }
19912
+ }
19913
+ };
19914
+ const hooks = yield* Effect.promise(() => factory({
19915
+ directory,
19916
+ worktree: ctx.location.project.directory,
19917
+ client,
19918
+ exec: nativeExec(directory),
19919
+ serverUrl: () => {
19920
+ const url = bindings?.server?.urls().find((value) => {
19921
+ if (!URL.canParse(value))
19922
+ return false;
19923
+ return ["127.0.0.1", "localhost", "[::1]"].includes(new URL(value).hostname);
19924
+ });
19925
+ return url === undefined ? undefined : new URL(url);
19926
+ },
19927
+ opencodeVersion: async () => version,
19928
+ listConfiguredInventory: async () => {
19929
+ if (config === undefined || residency === undefined)
19930
+ throw new Error(nativeHostMessages.missingBindings);
19931
+ const models = residency.observeModels((await run(ctx.model.list())).data);
19932
+ return nativeConfiguredInventory(models, config);
19933
+ },
19934
+ consoleEnvironment: () => {
19935
+ const auth = bindings?.auth;
19936
+ return {
19937
+ ...process.env,
19938
+ BUN_BE_BUN: "1",
19939
+ OPENCODE_SERVER_USERNAME: auth?.username,
19940
+ OPENCODE_SERVER_PASSWORD: auth && Option.isSome(auth.password) ? auth.password.value : undefined
19941
+ };
19942
+ },
19943
+ createResidencyGuard: (runtimeConfig, options) => {
19944
+ config = runtimeConfig;
19945
+ residency = createNativeModelResidencyGuard(runtimeConfig, {
19946
+ ...options,
19947
+ readModels: async () => (await run(ctx.model.list())).data
19948
+ });
19949
+ return residency;
19950
+ }
19951
+ }, ctx.options));
19952
+ const guard = residency;
19953
+ if (guard === undefined)
19954
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19955
+ const chatMessage = hooks["chat.message"];
19956
+ const tools = hooks.tool;
19957
+ if (chatMessage === undefined || tools === undefined)
19958
+ return yield* Effect.die(new Error(nativeHostMessages.missingBindings));
19959
+ yield* Effect.addFinalizer(() => Effect.promise(async () => {
19960
+ guard.dispose();
19961
+ systemPrompts.clear();
19962
+ await hooks.dispose?.();
19963
+ }));
19964
+ yield* ctx.provider.transform(guard.providers);
19965
+ yield* ctx.model.transform(guard.transform);
19966
+ yield* ctx.aisdk.hook("sdk", (event) => Effect.sync(() => guard.sdk(event)));
19967
+ yield* ctx.session.hook("model.request", (event) => Effect.promise(() => guard.modelRequest(event)));
19968
+ yield* ctx.session.hook("http.request", (event) => Effect.promise(() => guard.http(event)));
19969
+ yield* ctx.session.hook("experimental.ws.handshake", (event) => Effect.sync(() => guard.websocket(event)));
19970
+ yield* ctx.session.hook("context", (event) => Effect.sync(() => {
19971
+ const system = systemPrompts.get(event.sessionID);
19972
+ if (system !== undefined)
19973
+ event.system.push({ type: "text", text: system });
19974
+ }));
19975
+ yield* ctx.session.hook("prompt", Effect.fn("openteam.nativePrompt")(function* (event) {
19976
+ const currentBindings = yield* nativeBindings();
19977
+ const current = yield* ctx.session.get({ sessionID: event.sessionID }).pipe(Effect.orDie);
19978
+ assertWorkspace(current.location);
19979
+ remember(currentBindings);
19980
+ const agent = yield* currentBindings.agents.resolve(current.agent).pipe(Effect.orDie);
19981
+ if (agent === undefined)
19982
+ return yield* Effect.die(new Error(nativeHostMessages.unknownAgent));
19983
+ const selected = current.model ?? (yield* ctx.model.default().pipe(Effect.orDie)).data ?? (yield* ctx.model.list().pipe(Effect.orDie)).data[0];
19984
+ if (selected === undefined)
19985
+ return yield* Effect.die(new Error(nativeHostMessages.noAvailableModel));
19986
+ const created = yield* Clock.currentTimeMillis;
19987
+ const output = {
19988
+ message: {
19989
+ id: event.messageID,
19990
+ sessionID: event.sessionID,
19991
+ role: "user",
19992
+ time: { created },
19993
+ agent: agent.id,
19994
+ model: legacyModel(selected)
19995
+ },
19996
+ parts: [
19997
+ {
19998
+ id: event.messageID,
19999
+ sessionID: event.sessionID,
20000
+ messageID: event.messageID,
20001
+ type: "text",
20002
+ text: event.prompt.text
20003
+ }
20004
+ ]
20005
+ };
20006
+ yield* Effect.promise(() => chatMessage({
20007
+ sessionID: event.sessionID,
20008
+ messageID: event.messageID,
20009
+ agent: agent.id,
20010
+ ...current.model === undefined ? {} : { model: legacyModel(current.model) }
20011
+ }, output));
20012
+ const routed = output.message.model;
20013
+ yield* ctx.session.switchModel({
20014
+ sessionID: event.sessionID,
20015
+ model: {
20016
+ ...Model2.Ref.parse(`${routed.providerID}/${routed.modelID}`),
20017
+ ...current.model?.providerID === routed.providerID && current.model.id === routed.modelID && current.model.variant !== undefined ? { variant: current.model.variant } : {}
20018
+ }
20019
+ }).pipe(Effect.orDie);
20020
+ const part = output.parts[0];
20021
+ if (part?.type === "text")
20022
+ event.prompt.text = part.text;
20023
+ }));
20024
+ yield* ctx.tool.transform((editor) => {
20025
+ for (const [name, definition] of Object.entries(tools)) {
20026
+ const input = legacyTool.schema.object(definition.args);
20027
+ editor.add({
20028
+ name,
20029
+ description: definition.description,
20030
+ input: legacyTool.schema.toJSONSchema(input, { io: "input" }),
20031
+ options: { codemode: false },
20032
+ execute: Effect.fn("openteam.nativeTool")(function* (_input, toolContext) {
20033
+ const current = requiredBindings();
20034
+ const parent = yield* current.sessions.get(toolContext.sessionID).pipe(Effect.orDie);
20035
+ assertWorkspace(parent.location);
20036
+ remember(current);
20037
+ return yield* Effect.tryPromise(async (signal) => {
20038
+ const progress = [];
20039
+ const context = {
20040
+ sessionID: toolContext.sessionID,
20041
+ messageID: toolContext.messageID,
20042
+ agent: toolContext.agent,
20043
+ directory,
20044
+ worktree: ctx.location.project.directory,
20045
+ abort: signal,
20046
+ metadata: (update) => {
20047
+ progress.push(run(toolContext.progress({
20048
+ ...update.metadata,
20049
+ ...update.title === undefined ? {} : { title: update.title }
20050
+ }), { signal }).then(() => ({}), (error) => ({ error })));
20051
+ },
20052
+ ask: async (request) => {
20053
+ await run(current.permissions.assert({
20054
+ sessionID: toolContext.sessionID,
20055
+ agent: toolContext.agent,
20056
+ action: request.permission,
20057
+ resources: request.patterns,
20058
+ save: request.always,
20059
+ metadata: request.metadata,
20060
+ source: {
20061
+ type: "tool",
20062
+ messageID: toolContext.messageID,
20063
+ id: toolContext.id
20064
+ }
20065
+ }), { signal });
20066
+ }
20067
+ };
20068
+ const result = await toolSignals.run(signal, () => definition.execute(input.parse(_input), context));
20069
+ const failed = (await Promise.all(progress)).find((entry) => entry.error !== undefined);
20070
+ if (failed)
20071
+ throw failed.error;
20072
+ if (typeof result === "string")
20073
+ return { content: result };
20074
+ return {
20075
+ content: [
20076
+ { type: "text", text: result.output },
20077
+ ...(result.attachments ?? []).map((attachment) => ({
20078
+ type: "file",
20079
+ uri: attachment.url,
20080
+ mime: attachment.mime,
20081
+ ...attachment.filename === undefined ? {} : { name: attachment.filename }
20082
+ }))
20083
+ ],
20084
+ metadata: {
20085
+ ...result.metadata,
20086
+ ...result.title === undefined ? {} : { title: result.title }
20087
+ }
20088
+ };
20089
+ }).pipe(Effect.mapError(() => new Tool.Error({ message: nativeHostMessages.toolFailed })));
20090
+ })
20091
+ });
20092
+ }
20093
+ });
20094
+ yield* ctx.tool.hook("execute.before", (event) => Effect.promise(() => hooks["tool.execute.before"]?.({ tool: event.tool, sessionID: event.sessionID, callID: event.id }, { args: event.input }) ?? Promise.resolve()));
20095
+ yield* ctx.tool.hook("execute.after", (event) => Effect.promise(() => hooks["tool.execute.after"]?.({
20096
+ tool: event.tool,
20097
+ sessionID: event.sessionID,
20098
+ callID: event.id,
20099
+ args: event.input
20100
+ }, { title: event.tool, output: "", metadata: {} }) ?? Promise.resolve()));
20101
+ yield* ctx.event.subscribe().pipe(Stream.runForEach((event) => Effect.tryPromise(async () => {
20102
+ const parsed = executionEvent.safeParse(event);
20103
+ if (!parsed.success)
20104
+ return;
20105
+ const value = parsed.data;
20106
+ if (value.location !== undefined) {
20107
+ if (!sameNativeWorkspace(value.location, ctx.location))
20108
+ return;
20109
+ } else if (value.type !== "session.deleted") {
20110
+ const info = await run(ctx.session.get({
20111
+ sessionID: Session2.ID.make(value.data.sessionID)
20112
+ }));
20113
+ if (!sameNativeWorkspace(info.location, ctx.location))
20114
+ return;
20115
+ } else
20116
+ return;
20117
+ const sessionID = value.data.sessionID;
20118
+ if (value.type === "session.deleted") {
20119
+ guard.finished(sessionID);
20120
+ systemPrompts.delete(sessionID);
20121
+ }
20122
+ let mapped;
20123
+ if (value.type === "session.created" || value.type === "session.deleted") {
20124
+ mapped = {
20125
+ type: value.type,
20126
+ properties: { info: { id: sessionID } }
20127
+ };
20128
+ } else if (value.type === "session.execution.started") {
20129
+ mapped = {
20130
+ type: "session.status",
20131
+ properties: { sessionID, status: { type: "busy" } }
20132
+ };
20133
+ } else if (value.type === "session.step.ended" || value.type === "session.step.failed") {
20134
+ if (bindings === undefined)
20135
+ return;
20136
+ const messages = await run(bindings.sessions.messages({
20137
+ sessionID: Session2.ID.make(sessionID),
20138
+ limit: 100
20139
+ }));
20140
+ const message = messages.find((candidate) => candidate.id === value.data.assistantMessageID);
20141
+ if (message?.type !== "assistant")
20142
+ return;
20143
+ mapped = {
20144
+ type: "message.updated",
20145
+ properties: {
20146
+ info: nativeAssistantResponse(sessionID, message).info
20147
+ }
20148
+ };
20149
+ } else {
20150
+ systemPrompts.delete(sessionID);
20151
+ guard.finished(sessionID);
20152
+ if (value.type !== "session.execution.succeeded")
20153
+ await hooks.event?.({
20154
+ event: { type: "session.error", properties: { sessionID } }
20155
+ });
20156
+ mapped = { type: "session.idle", properties: { sessionID } };
20157
+ }
20158
+ await hooks.event?.({ event: mapped });
20159
+ }).pipe(Effect.catch(() => Effect.logError(nativeHostMessages.eventCaptureFailed)))), Effect.tapCause(() => Effect.logError(nativeHostMessages.eventCaptureFailed)), Effect.forkScoped);
20160
+ });
20161
+ }
20162
+
19246
20163
  // src/plugin/orchestrateTool.ts
19247
20164
  import { tool as tool5 } from "@opencode-ai/plugin";
19248
- import { z as z30 } from "zod";
20165
+ import { z as z32 } from "zod";
19249
20166
 
19250
20167
  // src/orchestrator/coordinator.ts
19251
- import { z as z29 } from "zod";
20168
+ import { z as z31 } from "zod";
19252
20169
 
19253
20170
  // src/orchestrator/nodeIteration.ts
19254
20171
  var DEFAULT_MAX_RETRIES = 2;
@@ -19722,7 +20639,7 @@ function buildRecord(input, decision2, subsession, deps, decisionID, batchID) {
19722
20639
  }
19723
20640
  return record;
19724
20641
  }
19725
- var TelemetryFailureClassSchema = z29.enum([
20642
+ var TelemetryFailureClassSchema = z31.enum([
19726
20643
  "create-rejected",
19727
20644
  "create-no-id",
19728
20645
  "prompt-rejected",
@@ -20148,10 +21065,10 @@ function uniqueModels(models) {
20148
21065
  const seen = new Set;
20149
21066
  const unique = [];
20150
21067
  for (const model of models) {
20151
- const key = modelKey4(model);
20152
- if (seen.has(key))
21068
+ const key2 = modelKey4(model);
21069
+ if (seen.has(key2))
20153
21070
  continue;
20154
- seen.add(key);
21071
+ seen.add(key2);
20155
21072
  unique.push(model);
20156
21073
  }
20157
21074
  return unique;
@@ -20539,7 +21456,7 @@ async function runRoleTasks(inputs, deps, options = {}) {
20539
21456
  var _pluginWarnedRoles = new Set;
20540
21457
  var EMPTY_ROLE_ID_SET = new Set;
20541
21458
  var SAFE_ROLE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
20542
- var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
21459
+ var RoleIDSchema = z32.string().min(1).superRefine((roleID, ctx) => {
20543
21460
  if (!SAFE_ROLE_ID.test(roleID)) {
20544
21461
  ctx.addIssue({
20545
21462
  code: "custom",
@@ -20547,16 +21464,16 @@ var RoleIDSchema = z30.string().min(1).superRefine((roleID, ctx) => {
20547
21464
  });
20548
21465
  }
20549
21466
  });
20550
- var RoleAssignmentSchema = z30.object({
21467
+ var RoleAssignmentSchema = z32.object({
20551
21468
  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)")
21469
+ prompt: z32.string().min(1).describe("Work prompt for this role"),
21470
+ title: z32.string().min(1).optional().describe("Human-readable title"),
21471
+ dependsOn: z32.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
20555
21472
  }).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")
21473
+ var OrchestratePayloadSchema = z32.object({
21474
+ assignments: z32.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
21475
+ parentSessionID: z32.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
21476
+ directory: z32.string().min(1).optional().describe("Working directory override")
20560
21477
  }).strict();
20561
21478
  function validateAssignmentDependencies(assignments) {
20562
21479
  const ids = new Set;
@@ -20617,14 +21534,14 @@ function validateAssignmentDependencies(assignments) {
20617
21534
  function isRecord7(value) {
20618
21535
  return typeof value === "object" && value !== null && !Array.isArray(value);
20619
21536
  }
20620
- function nestedRecord2(value, key) {
20621
- const nested = value[key];
21537
+ function nestedRecord2(value, key2) {
21538
+ const nested = value[key2];
20622
21539
  return isRecord7(nested) ? nested : undefined;
20623
21540
  }
20624
21541
  function numberField(records, keys) {
20625
21542
  for (const record of records) {
20626
- for (const key of keys) {
20627
- const value = record[key];
21543
+ for (const key2 of keys) {
21544
+ const value = record[key2];
20628
21545
  if (typeof value === "number" && Number.isInteger(value)) {
20629
21546
  return value;
20630
21547
  }
@@ -20634,8 +21551,8 @@ function numberField(records, keys) {
20634
21551
  }
20635
21552
  function booleanField(records, keys) {
20636
21553
  for (const record of records) {
20637
- for (const key of keys) {
20638
- const value = record[key];
21554
+ for (const key2 of keys) {
21555
+ const value = record[key2];
20639
21556
  if (typeof value === "boolean") {
20640
21557
  return value;
20641
21558
  }
@@ -20646,8 +21563,8 @@ function booleanField(records, keys) {
20646
21563
  function boundedErrorTokens(records) {
20647
21564
  const tokens = [];
20648
21565
  for (const record of records) {
20649
- for (const key of ["name", "code", "type", "message"]) {
20650
- const value = record[key];
21566
+ for (const key2 of ["name", "code", "type", "message"]) {
21567
+ const value = record[key2];
20651
21568
  if (typeof value === "string" && value.length <= 100) {
20652
21569
  tokens.push(value.toLowerCase());
20653
21570
  }
@@ -21185,6 +22102,8 @@ function createOrchestrateTool(deps) {
21185
22102
  }
21186
22103
  const parentOverrideIgnored = parsed.data.parentSessionID !== undefined && parsed.data.parentSessionID !== context.sessionID;
21187
22104
  parsed.data.parentSessionID = context.sessionID;
22105
+ if (parsed.data.directory === undefined && context.directory !== undefined)
22106
+ parsed.data.directory = context.directory;
21188
22107
  const depError = validateAssignmentDependencies(parsed.data.assignments);
21189
22108
  if (depError !== undefined) {
21190
22109
  return dependencyValidationError(depError.code, depError.detail);
@@ -21292,7 +22211,7 @@ ${advisory}`;
21292
22211
 
21293
22212
  // src/plugin/registerCastTool.ts
21294
22213
  import { tool as tool6 } from "@opencode-ai/plugin";
21295
- import { z as z31 } from "zod";
22214
+ import { z as z33 } from "zod";
21296
22215
 
21297
22216
  // src/messages/rosterSummary.ts
21298
22217
  var ROSTER_SUMMARY_HEADING = "Registered team model policies";
@@ -21408,12 +22327,12 @@ function createTeamPreparation(deps) {
21408
22327
  OpenTeamConfigSchema.parse(migrateConfig(baseRaw));
21409
22328
  const overlay = overlayBefore === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayBefore);
21410
22329
  const options = configOptionsFromPluginOptions(deps.options);
21411
- const resolve9 = (raw) => {
22330
+ const resolve11 = (raw) => {
21412
22331
  const config = validateConfigInput(TEAM_PREPARATION_CONFIG, mergeConfigInputs(mergeConfigInputs(raw, options), overlay));
21413
22332
  assertEffectiveConfigModelDomains(config);
21414
22333
  return config;
21415
22334
  };
21416
- const current = resolve9(baseRaw);
22335
+ const current = resolve11(baseRaw);
21417
22336
  const live = liveConfig ?? current;
21418
22337
  if (resolveExecutionMode({
21419
22338
  routerMode: current.router.executionMode,
@@ -21435,9 +22354,9 @@ function createTeamPreparation(deps) {
21435
22354
  ]))
21436
22355
  }
21437
22356
  });
21438
- const effective = resolve9(candidate);
22357
+ const effective = resolve11(candidate);
21439
22358
  const requestedProfiles = new Set(roster.entries.filter((entry) => !isKnownNonWorkerRole(entry.roleID) && getRoleProfile(entry.roleID, live.orchestrator?.roles) === undefined).map((entry) => getRoleProfile(entry.roleID, effective.orchestrator?.roles)));
21440
- const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key)));
22359
+ const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key2, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key2)));
21441
22360
  const hasAdditions = Object.keys(additions).length > 0;
21442
22361
  const next = hasAdditions ? validateConfigInput(TEAM_PREPARATION_CONFIG, {
21443
22362
  ...live,
@@ -21521,19 +22440,19 @@ async function persistPreparedTeam(storage, files) {
21521
22440
  }
21522
22441
 
21523
22442
  // 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")
22443
+ var CastEntrySchema = z33.object({
22444
+ roleID: z33.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
22445
+ agentName: z33.string().min(1).describe("Themed character name assigned to the role")
21527
22446
  }).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")
22447
+ var RegisterCastPayloadSchema = z33.object({
22448
+ universe: z33.string().min(1).describe("Casting universe for this roster"),
22449
+ entries: z33.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
21531
22450
  }).strict();
21532
22451
  function offendingFields(error) {
21533
22452
  const fields = error.issues.flatMap((issue) => {
21534
22453
  const base = issue.path.map((segment) => String(segment));
21535
22454
  if (issue.code === "unrecognized_keys") {
21536
- return issue.keys.map((key) => [...base, key].join("."));
22455
+ return issue.keys.map((key2) => [...base, key2].join("."));
21537
22456
  }
21538
22457
  return [base.join(".") || "(root)"];
21539
22458
  });
@@ -21573,12 +22492,12 @@ function markdownCell(value) {
21573
22492
  }
21574
22493
  function modelPolicyLabel(policy) {
21575
22494
  const model = policy?.model;
21576
- const label = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
22495
+ const label2 = model === undefined || model === "auto" ? ROSTER_AUTO_MODEL : `${model.providerID}/${model.modelID}`;
21577
22496
  if (policy?.fallbacks === undefined || policy.fallbacks.length === 0) {
21578
- return label;
22497
+ return label2;
21579
22498
  }
21580
22499
  const fallbacks = policy.fallbacks.map((fallback) => `${fallback.providerID}/${fallback.modelID}`);
21581
- return `${label}; ${rosterOrderedFallbacks(fallbacks)}`;
22500
+ return `${label2}; ${rosterOrderedFallbacks(fallbacks)}`;
21582
22501
  }
21583
22502
  function registeredTeamTable(roster, deps) {
21584
22503
  const rows = roster.entries.map((entry) => {
@@ -21647,7 +22566,7 @@ function createRegisterCastTool(deps) {
21647
22566
  }
21648
22567
 
21649
22568
  // src/plugin/reloadTool.ts
21650
- import { resolve as resolve9 } from "node:path";
22569
+ import { resolve as resolve11 } from "node:path";
21651
22570
  import { tool as tool7 } from "@opencode-ai/plugin/tool";
21652
22571
  var WAIT_MS = 30000;
21653
22572
  var SDK_TIMEOUT_MS = 5000;
@@ -21681,7 +22600,7 @@ function createReloadController(deps) {
21681
22600
  return () => clearTimeout(timer);
21682
22601
  }
21683
22602
  };
21684
- const directory = resolve9(deps.directory);
22603
+ const directory = resolve11(deps.directory);
21685
22604
  let pending;
21686
22605
  let authorizing;
21687
22606
  let closed = false;
@@ -21829,7 +22748,7 @@ function createReloadController(deps) {
21829
22748
  const parsed = schema.safeParse(args);
21830
22749
  if (!parsed.success)
21831
22750
  return reloadMessages.invalid;
21832
- if (context.agent !== "openteam" || resolve9(context.directory) !== directory || context.sessionID.trim().length === 0)
22751
+ if (context.agent !== "openteam" || resolve11(context.directory) !== directory || context.sessionID.trim().length === 0)
21833
22752
  return reloadMessages.denied;
21834
22753
  if (parsed.data.action === "cancel") {
21835
22754
  if ((pending?.sessionID ?? authorizing?.sessionID) !== context.sessionID)
@@ -21975,7 +22894,7 @@ function createSessionTracker() {
21975
22894
  }
21976
22895
 
21977
22896
  // src/plugin/toolcalls.ts
21978
- function key(sessionID, callID) {
22897
+ function key2(sessionID, callID) {
21979
22898
  return `${sessionID}\x00${callID}`;
21980
22899
  }
21981
22900
  function createToolcallTracker(deps) {
@@ -21983,10 +22902,10 @@ function createToolcallTracker(deps) {
21983
22902
  const started = new Map;
21984
22903
  return {
21985
22904
  before(input) {
21986
- started.set(key(input.sessionID, input.callID), now());
22905
+ started.set(key2(input.sessionID, input.callID), now());
21987
22906
  },
21988
22907
  after(input) {
21989
- const mapKey = key(input.sessionID, input.callID);
22908
+ const mapKey = key2(input.sessionID, input.callID);
21990
22909
  const start = started.get(mapKey);
21991
22910
  started.delete(mapKey);
21992
22911
  const end = now();
@@ -22196,7 +23115,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
22196
23115
  }
22197
23116
 
22198
23117
  // src/storage/lifecycle/fsLifecycleJournal.ts
22199
- import { randomUUID as randomUUID3 } from "node:crypto";
23118
+ import { randomUUID as randomUUID4 } from "node:crypto";
22200
23119
  import {
22201
23120
  lstatSync as lstatSync3,
22202
23121
  mkdirSync as mkdirSync3,
@@ -22206,22 +23125,22 @@ import {
22206
23125
  statSync,
22207
23126
  unlinkSync as unlinkSync2
22208
23127
  } from "node:fs";
22209
- import { join as join15, resolve as resolve11 } from "node:path";
23128
+ import { join as join15, resolve as resolve13 } from "node:path";
22210
23129
  import { ZodError } from "zod";
22211
23130
 
22212
23131
  // src/storage/lifecycle/codec.ts
22213
- import { z as z32 } from "zod";
23132
+ import { z as z34 } from "zod";
22214
23133
  var LIFECYCLE_CODEC_VERSION = 1;
22215
23134
  var LIFECYCLE_GENESIS_DIGEST = sha256Hex("openteam/lifecycle-journal/genesis/v1");
22216
23135
  var NUL5 = "\x00";
22217
23136
  var NEWLINE2 = `
22218
23137
  `;
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()
23138
+ var frameSchema = z34.object({
23139
+ v: z34.literal(LIFECYCLE_CODEC_VERSION),
23140
+ seq: z34.number().int().nonnegative(),
23141
+ prev: z34.string().min(1),
23142
+ sum: z34.string().min(1),
23143
+ event: z34.unknown()
22225
23144
  }).strict();
22226
23145
  function canonical2(value) {
22227
23146
  if (Array.isArray(value)) {
@@ -22230,7 +23149,7 @@ function canonical2(value) {
22230
23149
  if (value === null || typeof value !== "object") {
22231
23150
  return JSON.stringify(value);
22232
23151
  }
22233
- return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key2, child]) => `${JSON.stringify(key2)}:${canonical2(child)}`).join(",")}}`;
23152
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key3, child]) => `${JSON.stringify(key3)}:${canonical2(child)}`).join(",")}}`;
22234
23153
  }
22235
23154
  function lifecycleFrameDigest(previousDigest, event) {
22236
23155
  return sha256Hex(`${previousDigest}${NUL5}${canonical2(event)}`);
@@ -22308,7 +23227,7 @@ function decodeLifecycleJournal(text) {
22308
23227
  }
22309
23228
 
22310
23229
  // src/storage/lifecycle/fsLifecycleSupport.ts
22311
- import { randomUUID as randomUUID2 } from "node:crypto";
23230
+ import { randomUUID as randomUUID3 } from "node:crypto";
22312
23231
  import {
22313
23232
  appendFileSync,
22314
23233
  closeSync as closeSync2,
@@ -22325,7 +23244,7 @@ import {
22325
23244
  unlinkSync,
22326
23245
  writeSync as writeSync2
22327
23246
  } from "node:fs";
22328
- import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve10 } from "node:path";
23247
+ import { basename as basename3, dirname as dirname7, join as join14, relative as relative3, resolve as resolve12 } from "node:path";
22329
23248
  var SAFE_RUN_ID = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,126}[A-Za-z0-9])?$/u;
22330
23249
 
22331
23250
  class LifecycleFsError extends Error {
@@ -22349,7 +23268,7 @@ function validateRunID(runID) {
22349
23268
  }
22350
23269
  function resolvedLifecycleRoot(root) {
22351
23270
  try {
22352
- const absolute = resolve10(root);
23271
+ const absolute = resolve12(root);
22353
23272
  mkdirSync2(absolute, { recursive: true });
22354
23273
  const rootStat = lstatSync2(absolute);
22355
23274
  if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
@@ -22392,7 +23311,7 @@ function assertExistingSafeRun(root, runID) {
22392
23311
  throw new LifecycleFsError("unavailable");
22393
23312
  }
22394
23313
  const child = relative3(root, real);
22395
- if (child === "" || child.startsWith("..") || resolve10(root, child) !== real || dirname7(real) !== root) {
23314
+ if (child === "" || child.startsWith("..") || resolve12(root, child) !== real || dirname7(real) !== root) {
22396
23315
  throw new LifecycleFsError("corrupt");
22397
23316
  }
22398
23317
  return real;
@@ -22441,7 +23360,7 @@ function appendDurable2(path4, content) {
22441
23360
  }
22442
23361
  }
22443
23362
  function writeDurableAtomically(path4, content) {
22444
- const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID2()}.next`);
23363
+ const temporary = join14(dirname7(path4), `.${basename3(path4)}.${randomUUID3()}.next`);
22445
23364
  try {
22446
23365
  writeDurable2(temporary, content);
22447
23366
  renameSync(temporary, path4);
@@ -22464,7 +23383,7 @@ function readLockContents(path4) {
22464
23383
  }
22465
23384
  function withRunMutationLock(runDir, mutate) {
22466
23385
  const lockPath = join14(runDir, ".mutation-lock");
22467
- const lockToken = randomUUID2();
23386
+ const lockToken = randomUUID3();
22468
23387
  let descriptor;
22469
23388
  try {
22470
23389
  descriptor = openSync2(lockPath, "wx");
@@ -22533,7 +23452,7 @@ function parseJson(text, parse2) {
22533
23452
  }
22534
23453
  }
22535
23454
  function issueWriterToken() {
22536
- return randomUUID3().replaceAll("-", "");
23455
+ return randomUUID4().replaceAll("-", "");
22537
23456
  }
22538
23457
  function createLease(runID, writerToken, input) {
22539
23458
  return parseLifecycleWriterLease({
@@ -22632,7 +23551,7 @@ function canDeleteTerminalRun(runDir, runID, now) {
22632
23551
  return now >= lease.expiresAt;
22633
23552
  }
22634
23553
  var createFsLifecycleJournal = (options) => {
22635
- const configuredRoot = resolve11(options.root);
23554
+ const configuredRoot = resolve13(options.root);
22636
23555
  const now = options.now ?? Date.now;
22637
23556
  const mutateRun = options.withMutationLock ?? withRunMutationLock;
22638
23557
  return {
@@ -22654,7 +23573,7 @@ var createFsLifecycleJournal = (options) => {
22654
23573
  throw error;
22655
23574
  }
22656
23575
  }
22657
- stagingPath = join15(root, `.start-${runID}-${randomUUID3()}`);
23576
+ stagingPath = join15(root, `.start-${runID}-${randomUUID4()}`);
22658
23577
  mkdirSync3(stagingPath);
22659
23578
  const writerToken = issueWriterToken();
22660
23579
  const eventAt = now();
@@ -23013,8 +23932,8 @@ async function initSpanEmit(connection, config) {
23013
23932
  const tracer = provider.getTracer(DEFAULT_SERVICE_NAME);
23014
23933
  return (record) => {
23015
23934
  const span = tracer.startSpan(record.name);
23016
- for (const [key2, value] of Object.entries(record.attributes)) {
23017
- span.setAttribute(key2, value);
23935
+ for (const [key3, value] of Object.entries(record.attributes)) {
23936
+ span.setAttribute(key3, value);
23018
23937
  }
23019
23938
  span.end();
23020
23939
  };
@@ -23132,7 +24051,8 @@ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
23132
24051
  var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
23133
24052
  async function readPackagedCertificate(path4) {
23134
24053
  try {
23135
- return await readFile5(new URL(`./certificates/${basename4(path4)}`, import.meta.url), "utf8");
24054
+ const hostDirectory = basename4(dirname8(path4)) === NATIVE_CERTIFICATE_DIRECTORY ? `${NATIVE_CERTIFICATE_DIRECTORY}/` : "";
24055
+ return await readFile5(new URL(`./certificates/${hostDirectory}${basename4(path4)}`, import.meta.url), "utf8");
23136
24056
  } catch {
23137
24057
  return;
23138
24058
  }
@@ -23264,8 +24184,8 @@ async function readOpencodeVersion(serverUrl, options = {}) {
23264
24184
  }
23265
24185
  var DEFAULT_OPENCODE_VERSION_BACKOFF_MS = [50, 250];
23266
24186
  function defaultSleep(ms) {
23267
- return new Promise((resolve12) => {
23268
- setTimeout(resolve12, ms);
24187
+ return new Promise((resolve14) => {
24188
+ setTimeout(resolve14, ms);
23269
24189
  });
23270
24190
  }
23271
24191
  function createLazyOpencodeVersionReader(serverUrl, options = {}) {
@@ -23308,9 +24228,10 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
23308
24228
  };
23309
24229
  }
23310
24230
  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);
24231
+ const hostPath = (path4) => opencodeVersion === NATIVE_OPENCODE_VERSION ? `${dirname8(path4)}/${NATIVE_CERTIFICATE_DIRECTORY}/${basename4(path4)}` : path4;
24232
+ const shadow = await readAndParseCertificate(storage, hostPath(SHADOW_CERT_PATH), (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
23312
24233
  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);
24234
+ const release = await readAndParseCertificate(storage, hostPath(RELEASE_CERT_PATH), (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
23314
24235
  let soakLedgerDigest = "0".repeat(64);
23315
24236
  try {
23316
24237
  const recorderPorts = recorderPortsFromStorage(storage);
@@ -23565,14 +24486,17 @@ async function listConfiguredOpencodeInventory(ctx) {
23565
24486
  profiles: normalizeModelsDevCatalog(Object.fromEntries(providers.map((provider) => [provider.id, provider]))).filter((profile) => profile.costPer1M.inputUSD > 0 && profile.costPer1M.outputUSD > 0)
23566
24487
  };
23567
24488
  }
23568
- var server = async (ctx, rawOptions) => {
24489
+ async function createPluginRuntime(ctx, rawOptions) {
23569
24490
  const now = () => Date.now();
23570
24491
  const diagnostics = createPluginDiagnosticChannel({
23571
24492
  client: ctx.client,
23572
24493
  directory: ctx.directory,
23573
24494
  now
23574
24495
  });
23575
- logOpencodeServerUrl(ctx.serverUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
24496
+ const initialServerUrl = ctx.serverUrl();
24497
+ if (initialServerUrl !== undefined) {
24498
+ logOpencodeServerUrl(initialServerUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
24499
+ }
23576
24500
  const storage = createFsStorageProvider(ctx.directory || ctx.worktree || process.cwd());
23577
24501
  const runtimeConfig = await loadRuntimeOpenTeamConfig(storage, rawOptions);
23578
24502
  const config = runtimeConfig.config;
@@ -23581,20 +24505,19 @@ var server = async (ctx, rawOptions) => {
23581
24505
  const registry = new RuntimeRegistry({
23582
24506
  fetch: globalThis.fetch,
23583
24507
  clock: () => new Date().toISOString(),
23584
- foundryDiscovery: foundryDiscoveryFromExec(createShellExec(ctx.$))
24508
+ foundryDiscovery: foundryDiscoveryFromExec(ctx.exec)
23585
24509
  });
23586
24510
  const cache = createAvailabilityCache({
23587
24511
  config,
23588
24512
  registry,
23589
- ...ctx.client.config?.providers === undefined ? {} : {
23590
- listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
23591
- },
24513
+ ...ctx.listConfiguredInventory === undefined ? {} : { listConfiguredInventory: ctx.listConfiguredInventory },
23592
24514
  onRefreshError: (error) => logAvailabilityRefreshError(error, diagnostics.warn("availability-refresh-failed", { toast: false }))
23593
24515
  });
23594
24516
  let availabilityReady;
23595
- const ensureAvailability = () => {
24517
+ const ensureAvailability = async () => {
23596
24518
  availabilityReady ??= cache.refresh();
23597
- return availabilityReady;
24519
+ await availabilityReady;
24520
+ await cache.waitForRefresh();
23598
24521
  };
23599
24522
  const capabilityProfiles = () => {
23600
24523
  const availableModelKeys = new Set(cache.get().filter((model) => model.available).map((model) => `${model.providerID}\x00${model.modelID}`));
@@ -23623,17 +24546,19 @@ var server = async (ctx, rawOptions) => {
23623
24546
  const injectMemory = await buildMemoryInjectorFromConfig(config, storage, diagnostics.warn("memory-runtime-warning", { toast: false }));
23624
24547
  const sessionTracker = createSessionTracker();
23625
24548
  const orchestratedModelOwnership = createOrchestratedModelOwnership();
23626
- const modelResidency = createModelResidencyGuard(config, {
24549
+ const resolveAgentRole = async (agentName) => {
24550
+ const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
24551
+ return entry?.roleID;
24552
+ };
24553
+ const residencyOptions = {
23627
24554
  fetch: globalThis.fetch,
23628
- ...ctx.client.config?.providers === undefined ? {} : {
24555
+ resolveAgentRole,
24556
+ ...ctx.listConfiguredInventory === undefined ? {} : {
23629
24557
  resolveApiModelID: cache.resolveApiModelID,
23630
24558
  modelInventoryReady: ensureAvailability
23631
24559
  }
23632
- });
23633
- const resolveAgentRole = async (agentName) => {
23634
- const entry = (await readRoutingRosterEntries(storage)).find((candidate) => candidate.agentName === agentName);
23635
- return entry?.roleID;
23636
24560
  };
24561
+ const modelResidency = (ctx.createResidencyGuard ?? createModelResidencyGuard)(config, residencyOptions);
23637
24562
  const hooks = createHooks(config, cache.get, injectMemory === undefined ? {
23638
24563
  sink,
23639
24564
  modelResidency,
@@ -23652,10 +24577,12 @@ var server = async (ctx, rawOptions) => {
23652
24577
  resolveAgentRole,
23653
24578
  onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
23654
24579
  });
23655
- const getOpencodeVersion = createLazyOpencodeVersionReader(ctx.serverUrl, {
24580
+ const getOpencodeVersion = ctx.opencodeVersion ?? (initialServerUrl === undefined ? async () => {
24581
+ throw new Error(nativeHostMessages.missingVersion);
24582
+ } : createLazyOpencodeVersionReader(initialServerUrl, {
23656
24583
  client: ctx.client,
23657
24584
  onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
23658
- });
24585
+ }));
23659
24586
  const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions, async () => {
23660
24587
  const version = await getOpencodeVersion();
23661
24588
  return version === "unknown" ? undefined : version;
@@ -23666,12 +24593,15 @@ var server = async (ctx, rawOptions) => {
23666
24593
  if (sessionID === undefined || announcedSessions.has(sessionID)) {
23667
24594
  return;
23668
24595
  }
23669
- announcedSessions.add(sessionID);
23670
- const endpoint = sessionEndpointEventFrom(sessionID, ctx.serverUrl, now, {
24596
+ const serverUrl = ctx.serverUrl();
24597
+ if (serverUrl === undefined)
24598
+ return;
24599
+ const endpoint = sessionEndpointEventFrom(sessionID, serverUrl, now, {
23671
24600
  directory: ctx.directory,
23672
24601
  worktree: ctx.worktree
23673
24602
  });
23674
24603
  if (endpoint !== undefined) {
24604
+ announcedSessions.add(sessionID);
23675
24605
  sink.emit(endpoint);
23676
24606
  }
23677
24607
  };
@@ -23723,7 +24653,7 @@ var server = async (ctx, rawOptions) => {
23723
24653
  holder: "openteam-plugin",
23724
24654
  lifecycleSource: "plugin",
23725
24655
  lifecycleRecorder,
23726
- exec: createShellExec(ctx.$),
24656
+ exec: ctx.exec,
23727
24657
  repoRoot: ctx.directory
23728
24658
  });
23729
24659
  const graphSurface = createGraphTool({
@@ -23759,8 +24689,8 @@ var server = async (ctx, rawOptions) => {
23759
24689
  rememberReportedSlots(snapshots);
23760
24690
  return reachableRuntimeIds(snapshots);
23761
24691
  };
23762
- const runtimeById = (key2) => {
23763
- const runtimeId = slotPoolRuntimeID(key2);
24692
+ const runtimeById = (key3) => {
24693
+ const runtimeId = slotPoolRuntimeID(key3);
23764
24694
  const runtime = config.local.runtimes.find((candidate) => candidate.id === runtimeId);
23765
24695
  if (runtime === undefined) {
23766
24696
  return;
@@ -23768,13 +24698,13 @@ var server = async (ctx, rawOptions) => {
23768
24698
  return withReportedModelSlots(runtime, reportedSlotsByRuntime.get(runtimeId), localModelAliases(runtime.defaultModel.providerID, cache.get()));
23769
24699
  };
23770
24700
  const runtimeLimiter = createRuntimeConcurrencyLimiter({
23771
- capacityFor: (key2) => {
23772
- const runtime = runtimeById(key2);
23773
- return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key2));
24701
+ capacityFor: (key3) => {
24702
+ const runtime = runtimeById(key3);
24703
+ return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key3));
23774
24704
  },
23775
- reservedInteractiveFor: (key2) => {
23776
- const runtime = runtimeById(key2);
23777
- return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key2));
24705
+ reservedInteractiveFor: (key3) => {
24706
+ const runtime = runtimeById(key3);
24707
+ return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(key3));
23778
24708
  },
23779
24709
  now
23780
24710
  });
@@ -23801,8 +24731,8 @@ var server = async (ctx, rawOptions) => {
23801
24731
  lifecycleRecorder,
23802
24732
  abortGraceMs: DEFAULT_ABORT_GRACE_MS,
23803
24733
  statusPollMs: DEFAULT_STATUS_POLL_MS,
23804
- delay: (ms) => new Promise((resolve12) => {
23805
- setTimeout(resolve12, ms);
24734
+ delay: (ms) => new Promise((resolve14) => {
24735
+ setTimeout(resolve14, ms);
23806
24736
  }),
23807
24737
  warn: diagnostics.warn("unknown-role")
23808
24738
  });
@@ -23812,7 +24742,7 @@ var server = async (ctx, rawOptions) => {
23812
24742
  now,
23813
24743
  localRuntimeReachable: async (runtime, signal) => {
23814
24744
  if (localRuntimeKind(runtime) === "llama-swap") {
23815
- if (ctx.client.config?.providers !== undefined)
24745
+ if (ctx.listConfiguredInventory !== undefined)
23816
24746
  await ensureAvailability();
23817
24747
  signal?.throwIfAborted();
23818
24748
  const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
@@ -23826,7 +24756,7 @@ var server = async (ctx, rawOptions) => {
23826
24756
  return true;
23827
24757
  }
23828
24758
  if (localRuntimeKind(runtime) === "lemonade") {
23829
- if (ctx.client.config?.providers !== undefined)
24759
+ if (ctx.listConfiguredInventory !== undefined)
23830
24760
  await ensureAvailability();
23831
24761
  signal?.throwIfAborted();
23832
24762
  const target = modelResidency.localTarget(resolveMemorySemantic(config).extraction.model ?? runtime.defaultModel);
@@ -23841,7 +24771,7 @@ var server = async (ctx, rawOptions) => {
23841
24771
  return anyRuntimeReachable(snapshots);
23842
24772
  },
23843
24773
  resolveExtractionTarget: async (model) => {
23844
- if (ctx.client.config?.providers !== undefined)
24774
+ if (ctx.listConfiguredInventory !== undefined)
23845
24775
  await ensureAvailability();
23846
24776
  return modelResidency.localTarget(model);
23847
24777
  },
@@ -23869,6 +24799,7 @@ var server = async (ctx, rawOptions) => {
23869
24799
  "chat.message": async (input, output) => {
23870
24800
  reload.userTurn(input.sessionID);
23871
24801
  await hooks["chat.message"]?.(input, output);
24802
+ announceEndpoint(input.sessionID);
23872
24803
  },
23873
24804
  dispose: async () => {
23874
24805
  reload.dispose();
@@ -23876,7 +24807,7 @@ var server = async (ctx, rawOptions) => {
23876
24807
  },
23877
24808
  tool: {
23878
24809
  openteam: createCommandTool(cliDeps),
23879
- "openteam-console": createConsoleTool(),
24810
+ "openteam-console": createConsoleTool(ctx.consoleEnvironment === undefined ? {} : { environment: ctx.consoleEnvironment }),
23880
24811
  "openteam-graph": graphSurface.definition,
23881
24812
  "openteam-orchestrate": orchestrateTool,
23882
24813
  "openteam-memory": memoryTool,
@@ -23913,10 +24844,21 @@ var server = async (ctx, rawOptions) => {
23913
24844
  }
23914
24845
  }
23915
24846
  };
23916
- };
24847
+ }
24848
+ var server = (ctx, rawOptions) => createPluginRuntime({
24849
+ directory: ctx.directory,
24850
+ worktree: ctx.worktree,
24851
+ client: ctx.client,
24852
+ exec: createShellExec(ctx.$),
24853
+ serverUrl: () => ctx.serverUrl,
24854
+ ...ctx.client.config?.providers === undefined ? {} : {
24855
+ listConfiguredInventory: () => listConfiguredOpencodeInventory(ctx)
24856
+ }
24857
+ }, rawOptions);
23917
24858
  var plugin = {
23918
24859
  id: "openteam",
23919
- server
24860
+ server,
24861
+ effect: createNativePluginEffect(createPluginRuntime)
23920
24862
  };
23921
24863
  var src_default = plugin;
23922
24864
  export {
@@ -23934,6 +24876,7 @@ export {
23934
24876
  isMissingFile2 as isMissingFile,
23935
24877
  evaluateCutoverGateForBoundary,
23936
24878
  src_default as default,
24879
+ createPluginRuntime,
23937
24880
  createOpencodeVersionReadFailureLogger,
23938
24881
  createLazyOpencodeVersionReader,
23939
24882
  createEventSink,