@rynfar/meridian 1.63.0 → 1.64.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.md +3 -2
- package/dist/{cli-hdyptm7z.js → cli-4ghkaajs.js} +14507 -4202
- package/dist/cli-pbvm9kc2.js +760 -0
- package/dist/cli.js +70 -11
- package/dist/meridian-v2.js +139 -0
- package/dist/{profileCli-39vshwdn.js → profileCli-ap7eg985.js} +2 -2
- package/dist/proxy/passthroughTools.d.ts.map +1 -1
- package/dist/proxy/setup.d.ts +35 -10
- package/dist/proxy/setup.d.ts.map +1 -1
- package/dist/server.js +4 -4
- package/dist/setup-4v3zg3c0.js +33 -0
- package/package.json +8 -4
- package/plugin/meridian-v2.ts +190 -0
- package/dist/cli-pc0mtjjv.js +0 -167
- package/dist/setup-0x573t61.js +0 -19
- package/dist/{cli-xfbhn15a.js → cli-0ed6j0vk.js} +3 -3
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startProxyServer
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-4ghkaajs.js";
|
|
5
5
|
import"./cli-m0p2bc8v.js";
|
|
6
6
|
import"./cli-sry5aqdj.js";
|
|
7
7
|
import"./cli-8yp89fan.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveClaudeExecutableAsync
|
|
10
|
-
} from "./cli-
|
|
11
|
-
import"./cli-vj9cv18n.js";
|
|
12
|
-
import"./cli-pc0mtjjv.js";
|
|
10
|
+
} from "./cli-0ed6j0vk.js";
|
|
13
11
|
import"./cli-khhjyk04.js";
|
|
12
|
+
import"./cli-vj9cv18n.js";
|
|
13
|
+
import"./cli-pbvm9kc2.js";
|
|
14
14
|
import {
|
|
15
15
|
__require
|
|
16
16
|
} from "./cli-p9swy5t3.js";
|
|
@@ -39,6 +39,11 @@ Commands:
|
|
|
39
39
|
profile Manage Claude account profiles (add, list, switch, remove)
|
|
40
40
|
refresh-token Refresh the Claude Code OAuth token
|
|
41
41
|
|
|
42
|
+
Setup options:
|
|
43
|
+
--v1 Install the OpenCode V1 plugin
|
|
44
|
+
--v2 Install the pinned OpenCode V2 beta plugin
|
|
45
|
+
--opencode-bin <executable> Probe this OpenCode executable
|
|
46
|
+
|
|
42
47
|
Options:
|
|
43
48
|
-v, --version Show version
|
|
44
49
|
-h, --help Show this help
|
|
@@ -55,7 +60,7 @@ See https://github.com/rynfar/meridian for full documentation.`);
|
|
|
55
60
|
process.exit(0);
|
|
56
61
|
}
|
|
57
62
|
if (args[0] === "profile") {
|
|
58
|
-
const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-
|
|
63
|
+
const { profileAdd, profileAddOauthToken, profileList, profileRemove, profileSwitch, profileLogin, profileHelp } = await import("./profileCli-ap7eg985.js");
|
|
59
64
|
const subcommand = args[1];
|
|
60
65
|
const profileId = args[2];
|
|
61
66
|
const headless = args.includes("--headless");
|
|
@@ -80,11 +85,57 @@ if (args[0] === "profile") {
|
|
|
80
85
|
process.exit(0);
|
|
81
86
|
}
|
|
82
87
|
if (args[0] === "setup") {
|
|
83
|
-
const {
|
|
84
|
-
|
|
88
|
+
const {
|
|
89
|
+
detectOpenCodeGeneration,
|
|
90
|
+
DuplicateMeridianConfigError,
|
|
91
|
+
MissingV2PluginError,
|
|
92
|
+
pluginPathForGeneration,
|
|
93
|
+
runSetup,
|
|
94
|
+
SUPPORTED_OPENCODE_V2_VERSION,
|
|
95
|
+
UnparseableConfigError
|
|
96
|
+
} = await import("./setup-4v3zg3c0.js");
|
|
97
|
+
const forceV1 = args.includes("--v1");
|
|
98
|
+
const forceV2 = args.includes("--v2");
|
|
99
|
+
if (forceV1 && forceV2) {
|
|
100
|
+
console.error("Choose only one OpenCode generation: --v1 or --v2");
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const binaryIndex = args.indexOf("--opencode-bin");
|
|
104
|
+
const binary = binaryIndex >= 0 ? args[binaryIndex + 1] : undefined;
|
|
105
|
+
if (binaryIndex >= 0 && (!binary || binary.startsWith("--"))) {
|
|
106
|
+
console.error("--opencode-bin requires an executable path");
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
const environmentBinary = process.env.OPENCODE_BIN;
|
|
110
|
+
const commands = binary ? [binary] : environmentBinary ? [environmentBinary] : forceV2 ? ["opencode2", "opencode"] : undefined;
|
|
111
|
+
const detected = forceV1 ? { generation: "v1" } : detectOpenCodeGeneration(commands);
|
|
112
|
+
if (!forceV1 && (binary || environmentBinary) && !detected.version) {
|
|
113
|
+
console.error(`Could not read a supported OpenCode version from ${binary ?? environmentBinary}.`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
if (forceV2 && detected.generation !== "v2") {
|
|
117
|
+
console.error("Could not find an OpenCode V2 beta. Install the pinned beta or pass --opencode-bin <path>.");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
if (detected.generation === "v2" && detected.version !== SUPPORTED_OPENCODE_V2_VERSION) {
|
|
121
|
+
console.error(`OpenCode V2 ${detected.version ?? "unknown"} is not supported by this Meridian build.`);
|
|
122
|
+
console.error(`Install @opencode-ai/cli@${SUPPORTED_OPENCODE_V2_VERSION}, then re-run meridian setup --v2.`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
let pluginPath;
|
|
126
|
+
try {
|
|
127
|
+
pluginPath = pluginPathForGeneration(import.meta.url, detected.generation);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
if (err instanceof MissingV2PluginError) {
|
|
130
|
+
console.error(`OpenCode V2 plugin bundle is missing: ${err.expectedPath}`);
|
|
131
|
+
console.error("Reinstall Meridian, then re-run meridian setup --v2.");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
85
136
|
let result;
|
|
86
137
|
try {
|
|
87
|
-
result = runSetup(pluginPath);
|
|
138
|
+
result = runSetup(pluginPath, undefined, detected.generation);
|
|
88
139
|
} catch (err) {
|
|
89
140
|
if (err instanceof UnparseableConfigError) {
|
|
90
141
|
console.error(`\x1B[31m✗ Could not parse ${err.configPath}\x1B[0m`);
|
|
@@ -93,16 +144,24 @@ if (args[0] === "setup") {
|
|
|
93
144
|
console.error(` "plugin": ["${pluginPath}"]`);
|
|
94
145
|
process.exit(1);
|
|
95
146
|
}
|
|
147
|
+
if (err instanceof DuplicateMeridianConfigError) {
|
|
148
|
+
console.error("\x1B[31m✗ Meridian is already present in the other OpenCode config file\x1B[0m");
|
|
149
|
+
console.error(` OpenCode loads both ${err.configPath} and ${err.siblingPath}.`);
|
|
150
|
+
console.error(` Remove the Meridian entry from ${err.siblingPath}, then re-run 'meridian setup'.`);
|
|
151
|
+
console.error(" Both files were left untouched.");
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
96
154
|
throw err;
|
|
97
155
|
}
|
|
156
|
+
const generationLabel = detected.generation === "v2" ? "OpenCode V2" : "OpenCode V1";
|
|
98
157
|
if (result.alreadyConfigured) {
|
|
99
|
-
console.log(`\x1B[32m✓ Meridian plugin already configured\x1B[0m`);
|
|
158
|
+
console.log(`\x1B[32m✓ Meridian plugin already configured for ${generationLabel}\x1B[0m`);
|
|
100
159
|
console.log(` ${result.configPath}`);
|
|
101
160
|
} else {
|
|
102
161
|
if (result.removedStale.length > 0) {
|
|
103
162
|
console.log(` Removed ${result.removedStale.length} stale plugin entr${result.removedStale.length === 1 ? "y" : "ies"}`);
|
|
104
163
|
}
|
|
105
|
-
console.log(`\x1B[32m✓ Meridian plugin configured\x1B[0m`);
|
|
164
|
+
console.log(`\x1B[32m✓ Meridian plugin configured for ${generationLabel}\x1B[0m`);
|
|
106
165
|
console.log(` Config: ${result.configPath}`);
|
|
107
166
|
console.log(` Plugin: ${result.pluginPath}`);
|
|
108
167
|
if (!result.created) {
|
|
@@ -146,7 +205,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
|
|
|
146
205
|
return execFile(claudePath, ["auth", "status"], { timeout: 5000 });
|
|
147
206
|
}) {
|
|
148
207
|
try {
|
|
149
|
-
const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-
|
|
208
|
+
const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-4v3zg3c0.js");
|
|
150
209
|
const configPath = findOpencodeConfigPath();
|
|
151
210
|
const { existsSync } = await import("fs");
|
|
152
211
|
if (existsSync(configPath) && !checkPluginConfigured(configPath)) {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import"./cli-p9swy5t3.js";
|
|
2
|
+
|
|
3
|
+
// node_modules/@opencode-ai/plugin/dist/promise/plugin.js
|
|
4
|
+
function define(plugin) {
|
|
5
|
+
return plugin;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// plugin/meridian-v2.ts
|
|
9
|
+
var SUPPORTED_OPENCODE_V2_VERSION = "0.0.0-beta-18314";
|
|
10
|
+
var MERIDIAN_PROVIDERS = new Set(["anthropic", "meridian"]);
|
|
11
|
+
var PARENT_SESSION_ONE_SHOTS = new Set(["title", "summary"]);
|
|
12
|
+
var ATTACHED_COMPACTION_AGENT = "compaction";
|
|
13
|
+
var SESSION_AFFINITY_HEADERS = [
|
|
14
|
+
"x-opencode-session",
|
|
15
|
+
"x-session-affinity",
|
|
16
|
+
"x-session-id",
|
|
17
|
+
"x-parent-session-id"
|
|
18
|
+
];
|
|
19
|
+
var MERIDIAN_CONTROL_HEADERS = [
|
|
20
|
+
"x-meridian-source",
|
|
21
|
+
"x-opencode-agent-name",
|
|
22
|
+
"x-opencode-agent-mode"
|
|
23
|
+
];
|
|
24
|
+
var BUILTIN_AGENT_TRAITS = {
|
|
25
|
+
build: { mode: "primary", hidden: false },
|
|
26
|
+
plan: { mode: "primary", hidden: false },
|
|
27
|
+
general: { mode: "subagent", hidden: false },
|
|
28
|
+
explore: { mode: "subagent", hidden: false },
|
|
29
|
+
title: { mode: "primary", hidden: true },
|
|
30
|
+
summary: { mode: "primary", hidden: true },
|
|
31
|
+
compaction: { mode: "primary", hidden: true }
|
|
32
|
+
};
|
|
33
|
+
function fallbackAgentTraits(agent) {
|
|
34
|
+
return BUILTIN_AGENT_TRAITS[agent] ?? { mode: "primary", hidden: false };
|
|
35
|
+
}
|
|
36
|
+
function deleteHeader(headers, name) {
|
|
37
|
+
if (headers instanceof Headers) {
|
|
38
|
+
headers.delete(name);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const lower = name.toLowerCase();
|
|
42
|
+
for (const key of Object.keys(headers)) {
|
|
43
|
+
if (key.toLowerCase() === lower)
|
|
44
|
+
delete headers[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function setHeader(headers, name, value) {
|
|
48
|
+
deleteHeader(headers, name);
|
|
49
|
+
if (headers instanceof Headers)
|
|
50
|
+
headers.set(name, value);
|
|
51
|
+
else
|
|
52
|
+
headers[name] = value;
|
|
53
|
+
}
|
|
54
|
+
function safeAgentName(agent) {
|
|
55
|
+
return agent.replace(/[^\x20-\x7E]/g, "").trim() || "unknown";
|
|
56
|
+
}
|
|
57
|
+
function shouldDetachFromParentSession(agent, _traits) {
|
|
58
|
+
return PARENT_SESSION_ONE_SHOTS.has(agent);
|
|
59
|
+
}
|
|
60
|
+
function applyMeridianV2Headers(headers, input) {
|
|
61
|
+
for (const name2 of SESSION_AFFINITY_HEADERS)
|
|
62
|
+
deleteHeader(headers, name2);
|
|
63
|
+
for (const name2 of MERIDIAN_CONTROL_HEADERS)
|
|
64
|
+
deleteHeader(headers, name2);
|
|
65
|
+
const name = safeAgentName(input.agent);
|
|
66
|
+
const detached = shouldDetachFromParentSession(input.agent, input.traits);
|
|
67
|
+
if (detached) {
|
|
68
|
+
setHeader(headers, "x-meridian-source", `subagent-${name}`);
|
|
69
|
+
} else {
|
|
70
|
+
setHeader(headers, "x-opencode-session", input.sessionID);
|
|
71
|
+
setHeader(headers, "x-session-affinity", input.sessionID);
|
|
72
|
+
setHeader(headers, "x-session-id", input.sessionID);
|
|
73
|
+
if (input.agent === ATTACHED_COMPACTION_AGENT) {
|
|
74
|
+
setHeader(headers, "x-meridian-source", "subagent-compaction");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const internalMode = PARENT_SESSION_ONE_SHOTS.has(input.agent) ? "subagent" : input.agent === ATTACHED_COMPACTION_AGENT ? "primary" : input.traits.mode;
|
|
78
|
+
setHeader(headers, "x-opencode-agent-name", name);
|
|
79
|
+
setHeader(headers, "x-opencode-agent-mode", internalMode);
|
|
80
|
+
}
|
|
81
|
+
var MeridianV2Plugin = define({
|
|
82
|
+
id: "meridian",
|
|
83
|
+
setup: async (context) => {
|
|
84
|
+
const traitsByAgent = new Map;
|
|
85
|
+
const registered = [];
|
|
86
|
+
const resolveAgentTraits = (agent) => {
|
|
87
|
+
const cached = traitsByAgent.get(agent);
|
|
88
|
+
if (cached && cached.expiresAt > Date.now())
|
|
89
|
+
return cached.pending;
|
|
90
|
+
const pending = Promise.resolve().then(() => context.agent.get({ agentID: agent })).then(({ data }) => ({
|
|
91
|
+
mode: data.mode === "subagent" ? "subagent" : "primary",
|
|
92
|
+
hidden: data.hidden
|
|
93
|
+
})).catch(() => {
|
|
94
|
+
if (traitsByAgent.get(agent)?.pending === pending)
|
|
95
|
+
traitsByAgent.delete(agent);
|
|
96
|
+
return fallbackAgentTraits(agent);
|
|
97
|
+
});
|
|
98
|
+
traitsByAgent.set(agent, { expiresAt: Date.now() + 5000, pending });
|
|
99
|
+
return pending;
|
|
100
|
+
};
|
|
101
|
+
const apply = async (input, headers) => {
|
|
102
|
+
if (!MERIDIAN_PROVIDERS.has(String(input.model.providerID)))
|
|
103
|
+
return;
|
|
104
|
+
const agent = String(input.agent);
|
|
105
|
+
applyMeridianV2Headers(headers, {
|
|
106
|
+
sessionID: String(input.sessionID),
|
|
107
|
+
agent,
|
|
108
|
+
traits: await resolveAgentTraits(agent)
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
try {
|
|
112
|
+
for (const providerID of MERIDIAN_PROVIDERS) {
|
|
113
|
+
registered.push(await context.session.hook("model.request", async (input) => {
|
|
114
|
+
await apply(input, input.headers);
|
|
115
|
+
}, { providerID }));
|
|
116
|
+
registered.push(await context.session.hook("http.request", async (input) => {
|
|
117
|
+
await apply(input, input.request.headers);
|
|
118
|
+
}, { providerID }));
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
await Promise.allSettled(registered.map(({ dispose }) => dispose()));
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
return async () => {
|
|
125
|
+
const results = await Promise.allSettled(registered.map(({ dispose }) => dispose()));
|
|
126
|
+
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
127
|
+
if (failures.length > 0)
|
|
128
|
+
throw new AggregateError(failures, "Failed to dispose Meridian V2 hooks");
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
var meridian_v2_default = MeridianV2Plugin;
|
|
133
|
+
export {
|
|
134
|
+
shouldDetachFromParentSession,
|
|
135
|
+
fallbackAgentTraits,
|
|
136
|
+
meridian_v2_default as default,
|
|
137
|
+
applyMeridianV2Headers,
|
|
138
|
+
SUPPORTED_OPENCODE_V2_VERSION
|
|
139
|
+
};
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
profileLogin,
|
|
14
14
|
profileRemove,
|
|
15
15
|
profileSwitch
|
|
16
|
-
} from "./cli-
|
|
17
|
-
import"./cli-vj9cv18n.js";
|
|
16
|
+
} from "./cli-0ed6j0vk.js";
|
|
18
17
|
import"./cli-khhjyk04.js";
|
|
18
|
+
import"./cli-vj9cv18n.js";
|
|
19
19
|
import"./cli-p9swy5t3.js";
|
|
20
20
|
export {
|
|
21
21
|
profileSwitch,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passthroughTools.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AA0CtE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACjG,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE;;;;
|
|
1
|
+
{"version":3,"file":"passthroughTools.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AA0CtE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACjG,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE;;;;EA8ClC;AAqBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAC9E,MAAM,CASR;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAErE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,YAAY,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,GACtF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgCrC"}
|
package/dist/proxy/setup.d.ts
CHANGED
|
@@ -18,22 +18,46 @@ export declare class UnparseableConfigError extends Error {
|
|
|
18
18
|
readonly configPath: string;
|
|
19
19
|
constructor(configPath: string);
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
export declare class MissingV2PluginError extends Error {
|
|
22
|
+
readonly expectedPath: string;
|
|
23
|
+
constructor(expectedPath: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class DuplicateMeridianConfigError extends Error {
|
|
26
|
+
readonly configPath: string;
|
|
27
|
+
readonly siblingPath: string;
|
|
28
|
+
constructor(configPath: string, siblingPath: string);
|
|
29
|
+
}
|
|
25
30
|
export declare function findOpencodeConfigPath(): string;
|
|
26
31
|
/**
|
|
27
32
|
* Resolve the absolute path to plugin/meridian.ts from any entry point.
|
|
28
33
|
* Works whether called from bin/cli.ts (dev) or dist/cli.js (installed).
|
|
29
34
|
*/
|
|
30
35
|
export declare function findPluginPath(fromUrl: string): string;
|
|
36
|
+
export type OpenCodeGeneration = "v1" | "v2";
|
|
37
|
+
export interface OpenCodeDetection {
|
|
38
|
+
generation: OpenCodeGeneration;
|
|
39
|
+
version?: string;
|
|
40
|
+
command?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare const SUPPORTED_OPENCODE_V2_VERSION = "0.0.0-beta-18314";
|
|
43
|
+
/** Resolve the V2 plugin without selecting stale or incomplete artifacts. */
|
|
44
|
+
export declare function findV2PluginPath(fromUrl: string): string;
|
|
45
|
+
/** Parse the public V1 and beta V2 version formats without guessing. */
|
|
46
|
+
export declare function classifyOpenCodeVersion(output: string): OpenCodeDetection | undefined;
|
|
47
|
+
type VersionProbe = (command: string) => string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Detect the installed OpenCode generation with bounded probes. `opencode`
|
|
50
|
+
* remains first so an existing V1 installation keeps its current behavior;
|
|
51
|
+
* side-by-side beta users can select `opencode2` explicitly.
|
|
52
|
+
*/
|
|
53
|
+
export declare function detectOpenCodeGeneration(commands?: readonly string[], probe?: VersionProbe): OpenCodeDetection;
|
|
54
|
+
export declare function pluginPathForGeneration(fromUrl: string, generation: OpenCodeGeneration): string;
|
|
31
55
|
/**
|
|
32
56
|
* Returns true if the meridian plugin is already configured in the
|
|
33
57
|
* OpenCode global config. Returns false if config doesn't exist or
|
|
34
58
|
* plugin is missing.
|
|
35
59
|
*/
|
|
36
|
-
export declare function checkPluginConfigured(configPath?: string): boolean;
|
|
60
|
+
export declare function checkPluginConfigured(configPath?: string, expectedPluginPath?: string): boolean;
|
|
37
61
|
/** Reset the warned-session memory. Used by tests. */
|
|
38
62
|
export declare function clearPluginlessWarnings(): void;
|
|
39
63
|
/**
|
|
@@ -83,12 +107,13 @@ export interface SetupResult {
|
|
|
83
107
|
created: boolean;
|
|
84
108
|
}
|
|
85
109
|
/**
|
|
86
|
-
* Configure the
|
|
110
|
+
* Configure the Meridian plugin in ~/.config/opencode/opencode.json.
|
|
87
111
|
*
|
|
88
|
-
* -
|
|
89
|
-
* - Removes
|
|
112
|
+
* - Uses `plugin` for V1 and canonical `plugins` for V2
|
|
113
|
+
* - Removes Meridian entries from both fields to prevent duplicate plugin IDs
|
|
90
114
|
* - Adds the current plugin path
|
|
91
|
-
* - Leaves all
|
|
115
|
+
* - Leaves all unrelated plugins and settings untouched
|
|
92
116
|
*/
|
|
93
|
-
export declare function runSetup(pluginPath: string, configPath?: string): SetupResult;
|
|
117
|
+
export declare function runSetup(pluginPath: string, configPath?: string, generation?: OpenCodeGeneration): SetupResult;
|
|
118
|
+
export {};
|
|
94
119
|
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/proxy/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/proxy/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aACnB,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAI/C;AAED,qBAAa,oBAAqB,SAAQ,KAAK;aACjB,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM;CAIjD;AAED,qBAAa,4BAA6B,SAAQ,KAAK;aAEnC,UAAU,EAAE,MAAM;aAClB,WAAW,EAAE,MAAM;gBADnB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM;CAKtC;AA+BD,wBAAgB,sBAAsB,IAAI,MAAM,CAK/C;AASD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAA;AAE5C,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,kBAAkB,CAAA;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,6BAA6B,qBAAqB,CAAA;AAE/D,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBxD;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAYrF;AAED,KAAK,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;AAE3D;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,GAAE,SAAS,MAAM,EAEI,EAC7B,KAAK,GAAE,YASN,GACA,iBAAiB,CAQnB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,MAAM,CAE/F;AA4BD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAgB/F;AAaD,sDAAsD;AACtD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACnD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,mEAAmE;IACnE,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,mEAAmE;IACnE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B,GAAG,MAAM,GAAG,SAAS,CAkBrB;AAMD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,GAAE,kBAAyB,GACpC,WAAW,CA2Eb"}
|
package/dist/server.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
runObserveHook,
|
|
12
12
|
runTransformHook,
|
|
13
13
|
startProxyServer
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-4ghkaajs.js";
|
|
15
15
|
import"./cli-m0p2bc8v.js";
|
|
16
16
|
import"./cli-sry5aqdj.js";
|
|
17
17
|
import"./cli-8yp89fan.js";
|
|
18
|
-
import"./cli-
|
|
19
|
-
import"./cli-vj9cv18n.js";
|
|
20
|
-
import"./cli-pc0mtjjv.js";
|
|
18
|
+
import"./cli-0ed6j0vk.js";
|
|
21
19
|
import"./cli-khhjyk04.js";
|
|
20
|
+
import"./cli-vj9cv18n.js";
|
|
21
|
+
import"./cli-pbvm9kc2.js";
|
|
22
22
|
import"./cli-p9swy5t3.js";
|
|
23
23
|
export {
|
|
24
24
|
startProxyServer,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DuplicateMeridianConfigError,
|
|
3
|
+
MissingV2PluginError,
|
|
4
|
+
SUPPORTED_OPENCODE_V2_VERSION,
|
|
5
|
+
UnparseableConfigError,
|
|
6
|
+
checkPluginConfigured,
|
|
7
|
+
classifyOpenCodeVersion,
|
|
8
|
+
clearPluginlessWarnings,
|
|
9
|
+
detectOpenCodeGeneration,
|
|
10
|
+
findOpencodeConfigPath,
|
|
11
|
+
findPluginPath,
|
|
12
|
+
findV2PluginPath,
|
|
13
|
+
notePluginlessOpenCodeRequest,
|
|
14
|
+
pluginPathForGeneration,
|
|
15
|
+
runSetup
|
|
16
|
+
} from "./cli-pbvm9kc2.js";
|
|
17
|
+
import"./cli-p9swy5t3.js";
|
|
18
|
+
export {
|
|
19
|
+
runSetup,
|
|
20
|
+
pluginPathForGeneration,
|
|
21
|
+
notePluginlessOpenCodeRequest,
|
|
22
|
+
findV2PluginPath,
|
|
23
|
+
findPluginPath,
|
|
24
|
+
findOpencodeConfigPath,
|
|
25
|
+
detectOpenCodeGeneration,
|
|
26
|
+
clearPluginlessWarnings,
|
|
27
|
+
classifyOpenCodeVersion,
|
|
28
|
+
checkPluginConfigured,
|
|
29
|
+
UnparseableConfigError,
|
|
30
|
+
SUPPORTED_OPENCODE_V2_VERSION,
|
|
31
|
+
MissingV2PluginError,
|
|
32
|
+
DuplicateMeridianConfigError
|
|
33
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynfar/meridian",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.64.0",
|
|
4
4
|
"description": "Local Anthropic API powered by your Claude Max subscription. One subscription, every agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/server.js",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "./bin/claude-proxy-supervisor.sh",
|
|
23
23
|
"proxy": "./bin/claude-proxy-supervisor.sh",
|
|
24
|
-
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && bun build bin/cli.ts src/proxy/server.ts --outdir dist --target node --splitting --external @anthropic-ai/claude-agent-sdk --external jsonc-parser --entry-naming \"[name].js\" && tsc -p tsconfig.build.json",
|
|
25
|
-
"postbuild": "node scripts/fix-bun-exports.mjs && node --check dist/cli.js && node --check dist/server.js && node -e \"if(!require('fs').existsSync('dist/proxy/server.d.ts'))process.exit(1)\"",
|
|
24
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && bun build bin/cli.ts src/proxy/server.ts plugin/meridian-v2.ts --outdir dist --target node --splitting --external @anthropic-ai/claude-agent-sdk --external jsonc-parser --entry-naming \"[name].js\" && tsc -p tsconfig.build.json",
|
|
25
|
+
"postbuild": "node scripts/fix-bun-exports.mjs && node --check dist/cli.js && node --check dist/server.js && node --check dist/meridian-v2.js && node -e \"if(!require('fs').existsSync('dist/proxy/server.d.ts'))process.exit(1)\"",
|
|
26
26
|
"postinstall": "node -e \"try{require('child_process').spawnSync(process.execPath,[require.resolve('@anthropic-ai/claude-code/install.cjs')],{stdio:'inherit'})}catch(e){console.error('[meridian] claude-code postinstall skipped:',e.message)}\"",
|
|
27
27
|
"prepublishOnly": "bun run build",
|
|
28
28
|
"test": "bun test --path-ignore-patterns '**/*session-store*' --path-ignore-patterns '**/*proxy-async-ops*' --path-ignore-patterns '**/*models-auth-status*' --path-ignore-patterns '**/*proxy-context-usage-store*' --path-ignore-patterns '**/*proxy-passthrough-thinking*' --path-ignore-patterns '**/*proxy-thinking-setting*' --path-ignore-patterns '**/*session-recovery*' --path-ignore-patterns '**/*models.test*' --path-ignore-patterns '**/*proxy-health-build*' && bun test src/__tests__/proxy-async-ops.test.ts && bun test src/__tests__/proxy-session-store.test.ts && bun test src/__tests__/session-store-pruning.test.ts && bun test src/__tests__/proxy-session-store-locking.test.ts && bun test src/__tests__/proxy-context-usage-store.test.ts && bun test src/__tests__/models-auth-status.test.ts && bun test src/__tests__/proxy-passthrough-thinking.test.ts && bun test src/__tests__/proxy-session-recovery.test.ts && bun test src/__tests__/proxy-thinking-setting.test.ts && bun test src/__tests__/models.test.ts && bun test src/__tests__/proxy-health-build.test.ts",
|
|
@@ -33,12 +33,16 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@anthropic-ai/claude-agent-sdk": "^0.2.117",
|
|
35
35
|
"@anthropic-ai/claude-code": "^2.1.198",
|
|
36
|
+
"cross-spawn": "7.0.6",
|
|
36
37
|
"jsonc-parser": "^3.3.1",
|
|
37
|
-
"libsql": "^0.5.29"
|
|
38
|
+
"libsql": "^0.5.29",
|
|
39
|
+
"zod": "4.4.3"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@hono/node-server": "^1.19.11",
|
|
43
|
+
"@opencode-ai/plugin": "0.0.0-beta-18314",
|
|
41
44
|
"@types/bun": "^1.3.11",
|
|
45
|
+
"@types/cross-spawn": "6.0.6",
|
|
42
46
|
"@types/node": "^22.0.0",
|
|
43
47
|
"bun2nix": "^2.0.8",
|
|
44
48
|
"glob": "^13.0.0",
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meridian OpenCode plugin for the V2 beta line.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode V2 runs hidden title and summary requests in the parent session,
|
|
5
|
+
* often in parallel with the visible first turn. V2's core gives all of those
|
|
6
|
+
* requests the same session-affinity headers. Meridian must detach the hidden
|
|
7
|
+
* one-shots before they reach the proxy or they can advance the visible
|
|
8
|
+
* conversation's durable lineage.
|
|
9
|
+
*
|
|
10
|
+
* The model hook applies the identity as early as V2 permits. The HTTP hook
|
|
11
|
+
* enforces it again at the final request boundary. This removes stale or
|
|
12
|
+
* spoofed control headers regardless of header casing.
|
|
13
|
+
*
|
|
14
|
+
* NOTE: OpenCode-specific. Keep this separate from plugin/meridian.ts: V1
|
|
15
|
+
* expects a default plugin function and V2 expects a Plugin.define() object.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as Plugin from "@opencode-ai/plugin/promise/plugin"
|
|
19
|
+
|
|
20
|
+
/** Exact V2 host used to compile and validate this beta-only integration. */
|
|
21
|
+
export const SUPPORTED_OPENCODE_V2_VERSION = "0.0.0-beta-18314"
|
|
22
|
+
|
|
23
|
+
const MERIDIAN_PROVIDERS = new Set(["anthropic", "meridian"])
|
|
24
|
+
const PARENT_SESSION_ONE_SHOTS = new Set(["title", "summary"])
|
|
25
|
+
const ATTACHED_COMPACTION_AGENT = "compaction"
|
|
26
|
+
|
|
27
|
+
const SESSION_AFFINITY_HEADERS = [
|
|
28
|
+
"x-opencode-session",
|
|
29
|
+
"x-session-affinity",
|
|
30
|
+
"x-session-id",
|
|
31
|
+
"x-parent-session-id",
|
|
32
|
+
] as const
|
|
33
|
+
|
|
34
|
+
const MERIDIAN_CONTROL_HEADERS = [
|
|
35
|
+
"x-meridian-source",
|
|
36
|
+
"x-opencode-agent-name",
|
|
37
|
+
"x-opencode-agent-mode",
|
|
38
|
+
] as const
|
|
39
|
+
|
|
40
|
+
export interface AgentTraits {
|
|
41
|
+
mode: "primary" | "subagent"
|
|
42
|
+
hidden: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const BUILTIN_AGENT_TRAITS: Record<string, AgentTraits> = {
|
|
46
|
+
build: { mode: "primary", hidden: false },
|
|
47
|
+
plan: { mode: "primary", hidden: false },
|
|
48
|
+
general: { mode: "subagent", hidden: false },
|
|
49
|
+
explore: { mode: "subagent", hidden: false },
|
|
50
|
+
// Exact beta-18314 defines all three hidden internal agents as primary.
|
|
51
|
+
title: { mode: "primary", hidden: true },
|
|
52
|
+
summary: { mode: "primary", hidden: true },
|
|
53
|
+
compaction: { mode: "primary", hidden: true },
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function fallbackAgentTraits(agent: string): AgentTraits {
|
|
57
|
+
return BUILTIN_AGENT_TRAITS[agent] ?? { mode: "primary", hidden: false }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type MutableHeaders = Record<string, string> | Headers
|
|
61
|
+
|
|
62
|
+
function deleteHeader(headers: MutableHeaders, name: string): void {
|
|
63
|
+
if (headers instanceof Headers) {
|
|
64
|
+
headers.delete(name)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
const lower = name.toLowerCase()
|
|
68
|
+
for (const key of Object.keys(headers)) {
|
|
69
|
+
if (key.toLowerCase() === lower) delete headers[key]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setHeader(headers: MutableHeaders, name: string, value: string): void {
|
|
74
|
+
deleteHeader(headers, name)
|
|
75
|
+
if (headers instanceof Headers) headers.set(name, value)
|
|
76
|
+
else headers[name] = value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function safeAgentName(agent: string): string {
|
|
80
|
+
return agent.replace(/[^\x20-\x7E]/g, "").trim() || "unknown"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function shouldDetachFromParentSession(agent: string, _traits: AgentTraits): boolean {
|
|
84
|
+
// The exact built-in ID is authoritative. `hidden` is presentation config:
|
|
85
|
+
// making the built-in visible does not stop V2 from running its parent-
|
|
86
|
+
// session title/summary job concurrently with the primary turn.
|
|
87
|
+
return PARENT_SESSION_ONE_SHOTS.has(agent)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Rewrite one V2 request's identity without changing its body or model input.
|
|
92
|
+
* This pure helper is shared by model.request and http.request.
|
|
93
|
+
*/
|
|
94
|
+
export function applyMeridianV2Headers(
|
|
95
|
+
headers: MutableHeaders,
|
|
96
|
+
input: { sessionID: string; agent: string; traits: AgentTraits },
|
|
97
|
+
): void {
|
|
98
|
+
for (const name of SESSION_AFFINITY_HEADERS) deleteHeader(headers, name)
|
|
99
|
+
for (const name of MERIDIAN_CONTROL_HEADERS) deleteHeader(headers, name)
|
|
100
|
+
|
|
101
|
+
const name = safeAgentName(input.agent)
|
|
102
|
+
const detached = shouldDetachFromParentSession(input.agent, input.traits)
|
|
103
|
+
|
|
104
|
+
if (detached) {
|
|
105
|
+
setHeader(headers, "x-meridian-source", `subagent-${name}`)
|
|
106
|
+
} else {
|
|
107
|
+
// Set every V2 affinity spelling from the trusted hook input. Do not retain
|
|
108
|
+
// provider-config values that can bind this request to another session.
|
|
109
|
+
setHeader(headers, "x-opencode-session", input.sessionID)
|
|
110
|
+
setHeader(headers, "x-session-affinity", input.sessionID)
|
|
111
|
+
setHeader(headers, "x-session-id", input.sessionID)
|
|
112
|
+
if (input.agent === ATTACHED_COMPACTION_AGENT) {
|
|
113
|
+
// Source selects the base model tier without making the adapter append
|
|
114
|
+
// `#compaction` to the primary session key.
|
|
115
|
+
setHeader(headers, "x-meridian-source", "subagent-compaction")
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const internalMode = PARENT_SESSION_ONE_SHOTS.has(input.agent)
|
|
120
|
+
? "subagent"
|
|
121
|
+
: input.agent === ATTACHED_COMPACTION_AGENT ? "primary" : input.traits.mode
|
|
122
|
+
setHeader(headers, "x-opencode-agent-name", name)
|
|
123
|
+
setHeader(headers, "x-opencode-agent-mode", internalMode)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const MeridianV2Plugin = Plugin.define({
|
|
127
|
+
id: "meridian",
|
|
128
|
+
setup: async (context) => {
|
|
129
|
+
const traitsByAgent = new Map<string, {
|
|
130
|
+
expiresAt: number
|
|
131
|
+
pending: Promise<AgentTraits>
|
|
132
|
+
}>()
|
|
133
|
+
const registered: Array<{ dispose: () => Promise<void> }> = []
|
|
134
|
+
|
|
135
|
+
const resolveAgentTraits = (agent: string): Promise<AgentTraits> => {
|
|
136
|
+
const cached = traitsByAgent.get(agent)
|
|
137
|
+
if (cached && cached.expiresAt > Date.now()) return cached.pending
|
|
138
|
+
|
|
139
|
+
const pending = Promise.resolve()
|
|
140
|
+
.then(() => context.agent.get({ agentID: agent }))
|
|
141
|
+
.then(({ data }) => ({
|
|
142
|
+
mode: data.mode === "subagent" ? "subagent" as const : "primary" as const,
|
|
143
|
+
hidden: data.hidden,
|
|
144
|
+
}))
|
|
145
|
+
.catch(() => {
|
|
146
|
+
// A transient lookup failure must not permanently promote a custom
|
|
147
|
+
// child to primary. Retry at the final HTTP boundary or next turn.
|
|
148
|
+
if (traitsByAgent.get(agent)?.pending === pending) traitsByAgent.delete(agent)
|
|
149
|
+
return fallbackAgentTraits(agent)
|
|
150
|
+
})
|
|
151
|
+
traitsByAgent.set(agent, { expiresAt: Date.now() + 5_000, pending })
|
|
152
|
+
return pending
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const apply = async (
|
|
156
|
+
input: { sessionID: string; agent: string; model: { providerID: string } },
|
|
157
|
+
headers: MutableHeaders,
|
|
158
|
+
): Promise<void> => {
|
|
159
|
+
if (!MERIDIAN_PROVIDERS.has(String(input.model.providerID))) return
|
|
160
|
+
const agent = String(input.agent)
|
|
161
|
+
applyMeridianV2Headers(headers, {
|
|
162
|
+
sessionID: String(input.sessionID),
|
|
163
|
+
agent,
|
|
164
|
+
traits: await resolveAgentTraits(agent),
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
for (const providerID of MERIDIAN_PROVIDERS) {
|
|
170
|
+
registered.push(await context.session.hook("model.request", async (input) => {
|
|
171
|
+
await apply(input, input.headers)
|
|
172
|
+
}, { providerID }))
|
|
173
|
+
registered.push(await context.session.hook("http.request", async (input) => {
|
|
174
|
+
await apply(input, input.request.headers)
|
|
175
|
+
}, { providerID }))
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
await Promise.allSettled(registered.map(({ dispose }) => dispose()))
|
|
179
|
+
throw error
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return async () => {
|
|
183
|
+
const results = await Promise.allSettled(registered.map(({ dispose }) => dispose()))
|
|
184
|
+
const failures = results.flatMap(result => result.status === "rejected" ? [result.reason] : [])
|
|
185
|
+
if (failures.length > 0) throw new AggregateError(failures, "Failed to dispose Meridian V2 hooks")
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
export default MeridianV2Plugin
|