@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.
- package/README.es.md +11 -8
- package/README.md +11 -8
- package/dist/certificates/graph-release-certificate.json +2 -2
- package/dist/certificates/graph-shadow-certificate.json +2 -2
- package/dist/certificates/opencode-2.0.5/graph-release-certificate.json +45 -0
- package/dist/certificates/opencode-2.0.5/graph-shadow-certificate.json +39 -0
- package/dist/cli/consoleRuntimeState.d.ts.map +1 -1
- package/dist/cli/consoleServe.d.ts +2 -1
- package/dist/cli/consoleServe.d.ts.map +1 -1
- package/dist/cli.js +21871 -12535
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/upgrade.d.ts +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/config/fieldProjection.d.ts +3 -0
- package/dist/config/fieldProjection.d.ts.map +1 -0
- package/dist/config/nativeOpencode.d.ts +5 -0
- package/dist/config/nativeOpencode.d.ts.map +1 -0
- package/dist/console/auth.d.ts +5 -0
- package/dist/console/auth.d.ts.map +1 -0
- package/dist/console/conversation.d.ts +8 -1
- package/dist/console/conversation.d.ts.map +1 -1
- package/dist/console/historyBoundary.d.ts +1 -1
- package/dist/console/historyBoundary.d.ts.map +1 -1
- package/dist/console/nativeConversation.d.ts +7 -0
- package/dist/console/nativeConversation.d.ts.map +1 -0
- package/dist/console/opencodeClient.d.ts +3 -0
- package/dist/console/opencodeClient.d.ts.map +1 -1
- package/dist/console/pty.d.ts +4 -0
- package/dist/console/pty.d.ts.map +1 -1
- package/dist/contract/opencode.d.ts +13 -7
- package/dist/contract/opencode.d.ts.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1150 -207
- package/dist/messages/consoleDetail.d.ts +3 -0
- package/dist/messages/consoleDetail.d.ts.map +1 -1
- package/dist/messages/nativeHost.d.ts +21 -0
- package/dist/messages/nativeHost.d.ts.map +1 -0
- package/dist/plugin/availability.d.ts +1 -0
- package/dist/plugin/availability.d.ts.map +1 -1
- package/dist/plugin/consoleTool.d.ts +1 -0
- package/dist/plugin/consoleTool.d.ts.map +1 -1
- package/dist/plugin/hooks.d.ts +3 -3
- package/dist/plugin/hooks.d.ts.map +1 -1
- package/dist/plugin/modelResidency.d.ts +1 -0
- package/dist/plugin/modelResidency.d.ts.map +1 -1
- package/dist/plugin/nativeHost.d.ts +5 -0
- package/dist/plugin/nativeHost.d.ts.map +1 -0
- package/dist/plugin/nativeHostContext.d.ts +228 -0
- package/dist/plugin/nativeHostContext.d.ts.map +1 -0
- package/dist/plugin/nativeInventory.d.ts +5 -0
- package/dist/plugin/nativeInventory.d.ts.map +1 -0
- package/dist/plugin/nativeResidency.d.ts +30 -0
- package/dist/plugin/nativeResidency.d.ts.map +1 -0
- package/dist/plugin/nativeSessions.d.ts +131 -0
- package/dist/plugin/nativeSessions.d.ts.map +1 -0
- package/dist/plugin/orchestrateTool.d.ts.map +1 -1
- package/dist/plugin/reloadTool.d.ts +3 -2
- package/dist/plugin/reloadTool.d.ts.map +1 -1
- package/dist/plugin/runtimeContext.d.ts +42 -0
- package/dist/plugin/runtimeContext.d.ts.map +1 -0
- package/dist/web/console.d.ts +1 -1
- package/dist/web/console.d.ts.map +1 -1
- package/dist/web/server.d.ts +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/start.d.ts +1 -1
- package/dist/web/start.d.ts.map +1 -1
- 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.
|
|
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
|
|
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 !==
|
|
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
|
|
6779
|
-
const
|
|
6780
|
-
if (
|
|
6809
|
+
for (const key of PLUGIN_KEYS) {
|
|
6810
|
+
const pluginValue = parsed.value[key];
|
|
6811
|
+
if (pluginValue === undefined)
|
|
6781
6812
|
continue;
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
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
|
|
7047
|
-
|
|
7048
|
-
|
|
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 (
|
|
7066
|
-
|
|
7067
|
-
|
|
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 (
|
|
7125
|
+
if (managedCandidates.length === 0) {
|
|
7076
7126
|
continue;
|
|
7077
7127
|
}
|
|
7078
|
-
|
|
7079
|
-
|
|
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
|
|
7118
|
-
if (
|
|
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
|
|
7127
|
-
|
|
7128
|
-
|
|
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:
|
|
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
|
-
|
|
15609
|
-
|
|
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
|
|
15910
|
-
|
|
15911
|
-
|
|
15912
|
-
|
|
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
|
|
20165
|
+
import { z as z32 } from "zod";
|
|
19249
20166
|
|
|
19250
20167
|
// src/orchestrator/coordinator.ts
|
|
19251
|
-
import { z as
|
|
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 =
|
|
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
|
|
20152
|
-
if (seen.has(
|
|
21068
|
+
const key2 = modelKey4(model);
|
|
21069
|
+
if (seen.has(key2))
|
|
20153
21070
|
continue;
|
|
20154
|
-
seen.add(
|
|
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 =
|
|
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 =
|
|
21467
|
+
var RoleAssignmentSchema = z32.object({
|
|
20551
21468
|
roleID: RoleIDSchema.describe("Role identifier from the roster"),
|
|
20552
|
-
prompt:
|
|
20553
|
-
title:
|
|
20554
|
-
dependsOn:
|
|
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 =
|
|
20557
|
-
assignments:
|
|
20558
|
-
parentSessionID:
|
|
20559
|
-
directory:
|
|
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,
|
|
20621
|
-
const nested = value[
|
|
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
|
|
20627
|
-
const value = record[
|
|
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
|
|
20638
|
-
const value = record[
|
|
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
|
|
20650
|
-
const value = record[
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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(([
|
|
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 =
|
|
21525
|
-
roleID:
|
|
21526
|
-
agentName:
|
|
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 =
|
|
21529
|
-
universe:
|
|
21530
|
-
entries:
|
|
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((
|
|
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
|
|
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
|
|
22497
|
+
return label2;
|
|
21579
22498
|
}
|
|
21580
22499
|
const fallbacks = policy.fallbacks.map((fallback) => `${fallback.providerID}/${fallback.modelID}`);
|
|
21581
|
-
return `${
|
|
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
|
|
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 =
|
|
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" ||
|
|
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
|
|
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(
|
|
22905
|
+
started.set(key2(input.sessionID, input.callID), now());
|
|
21987
22906
|
},
|
|
21988
22907
|
after(input) {
|
|
21989
|
-
const mapKey =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
22220
|
-
v:
|
|
22221
|
-
seq:
|
|
22222
|
-
prev:
|
|
22223
|
-
sum:
|
|
22224
|
-
event:
|
|
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(([
|
|
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
|
|
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
|
|
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 =
|
|
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("..") ||
|
|
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)}.${
|
|
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 =
|
|
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
|
|
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 =
|
|
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}-${
|
|
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 [
|
|
23017
|
-
span.setAttribute(
|
|
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
|
-
|
|
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((
|
|
23268
|
-
setTimeout(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
24508
|
+
foundryDiscovery: foundryDiscoveryFromExec(ctx.exec)
|
|
23585
24509
|
});
|
|
23586
24510
|
const cache = createAvailabilityCache({
|
|
23587
24511
|
config,
|
|
23588
24512
|
registry,
|
|
23589
|
-
...ctx.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
23670
|
-
|
|
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:
|
|
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 = (
|
|
23763
|
-
const runtimeId = slotPoolRuntimeID(
|
|
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: (
|
|
23772
|
-
const runtime = runtimeById(
|
|
23773
|
-
return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(
|
|
24701
|
+
capacityFor: (key3) => {
|
|
24702
|
+
const runtime = runtimeById(key3);
|
|
24703
|
+
return runtime === undefined ? unknownPoolCapacity() : slotPoolCapacity(runtime, slotPoolModelID(key3));
|
|
23774
24704
|
},
|
|
23775
|
-
reservedInteractiveFor: (
|
|
23776
|
-
const runtime = runtimeById(
|
|
23777
|
-
return runtime === undefined ? 0 : slotPoolReservedInteractive(runtime, slotPoolModelID(
|
|
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((
|
|
23805
|
-
setTimeout(
|
|
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.
|
|
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.
|
|
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.
|
|
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,
|