@rynfar/meridian 1.68.0 → 1.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +1 -0
  2. package/dist/{cli-0zhb8ss4.js → cli-198xnjcn.js} +1033 -525
  3. package/dist/{cli-sh8bzdwe.js → cli-zdbv40d5.js} +26 -7
  4. package/dist/cli.js +4 -4
  5. package/dist/meridian/index.js +252 -0
  6. package/dist/meridian/package.json +6 -0
  7. package/dist/meridian-v2/index.js +20384 -41
  8. package/dist/meridian-v2.js +20382 -45
  9. package/dist/proxy/adapter.d.ts +16 -0
  10. package/dist/proxy/adapter.d.ts.map +1 -1
  11. package/dist/proxy/adapters/claudecode.d.ts +23 -0
  12. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  13. package/dist/proxy/adapters/detect.d.ts.map +1 -1
  14. package/dist/proxy/adapters/passthrough.d.ts.map +1 -1
  15. package/dist/proxy/adapters/polytoken.d.ts +36 -0
  16. package/dist/proxy/adapters/polytoken.d.ts.map +1 -0
  17. package/dist/proxy/errors.d.ts +2 -0
  18. package/dist/proxy/errors.d.ts.map +1 -1
  19. package/dist/proxy/openaiResponses.d.ts +106 -3
  20. package/dist/proxy/openaiResponses.d.ts.map +1 -1
  21. package/dist/proxy/passthroughEarlyStop.d.ts +3 -3
  22. package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
  23. package/dist/proxy/passthroughTools.d.ts +63 -2
  24. package/dist/proxy/passthroughTools.d.ts.map +1 -1
  25. package/dist/proxy/query.d.ts +3 -0
  26. package/dist/proxy/query.d.ts.map +1 -1
  27. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  28. package/dist/proxy/server.d.ts.map +1 -1
  29. package/dist/proxy/session/cache.d.ts +20 -3
  30. package/dist/proxy/session/cache.d.ts.map +1 -1
  31. package/dist/proxy/session/lineage.d.ts +56 -0
  32. package/dist/proxy/session/lineage.d.ts.map +1 -1
  33. package/dist/proxy/session/processIncarnation.d.ts +55 -0
  34. package/dist/proxy/session/processIncarnation.d.ts.map +1 -1
  35. package/dist/proxy/setup.d.ts +12 -2
  36. package/dist/proxy/setup.d.ts.map +1 -1
  37. package/dist/proxy/transforms/codex.d.ts +12 -0
  38. package/dist/proxy/transforms/codex.d.ts.map +1 -1
  39. package/dist/proxy/transforms/polytoken.d.ts +18 -0
  40. package/dist/proxy/transforms/polytoken.d.ts.map +1 -0
  41. package/dist/proxy/transforms/registry.d.ts.map +1 -1
  42. package/dist/server.js +2 -2
  43. package/dist/{setup-knvctar4.js → setup-b3ymd9z8.js} +3 -1
  44. package/dist/telemetry/index.d.ts.map +1 -1
  45. package/package.json +4 -4
  46. package/plugin/meridian/index.js +1 -0
  47. package/plugin/meridian/package.json +6 -0
  48. package/plugin/meridian-v2.ts +223 -0
  49. package/plugin/meridian.ts +1 -1
@@ -720,6 +720,15 @@ class UnparseableConfigError extends Error {
720
720
  }
721
721
  }
722
722
 
723
+ class MissingV1PluginError extends Error {
724
+ expectedPath;
725
+ constructor(expectedPath) {
726
+ super(`OpenCode V1 plugin bundle not found at ${expectedPath}`);
727
+ this.expectedPath = expectedPath;
728
+ this.name = "MissingV1PluginError";
729
+ }
730
+ }
731
+
723
732
  class MissingV2PluginError extends Error {
724
733
  expectedPath;
725
734
  constructor(expectedPath) {
@@ -771,14 +780,24 @@ function siblingOpencodeConfigPath(configPath) {
771
780
  return;
772
781
  }
773
782
  function findPluginPath(fromUrl) {
774
- const dir = dirname2(fileURLToPath(fromUrl));
775
- return join2(dir, "..", "plugin", "meridian.ts");
783
+ const entryPath = fileURLToPath(fromUrl);
784
+ const dir = dirname2(entryPath);
785
+ if (entryPath.endsWith(".ts")) {
786
+ const sourcePlugin = join2(dir, "..", "plugin", "meridian");
787
+ if (hasPluginPackageEntry(sourcePlugin))
788
+ return sourcePlugin;
789
+ throw new MissingV1PluginError(sourcePlugin);
790
+ }
791
+ const bundledPlugin = join2(dir, "meridian");
792
+ if (hasPluginPackageEntry(bundledPlugin))
793
+ return bundledPlugin;
794
+ throw new MissingV1PluginError(bundledPlugin);
776
795
  }
777
796
  var SUPPORTED_OPENCODE_V2_VERSIONS = new Set([
778
797
  "0.0.0-beta-18314",
779
798
  "0.0.0-beta-18866"
780
799
  ]);
781
- function hasV2PluginEntry(path) {
800
+ function hasPluginPackageEntry(path) {
782
801
  try {
783
802
  if (!statSync(join2(path, "index.js"), { throwIfNoEntry: false })?.isFile())
784
803
  return false;
@@ -796,12 +815,12 @@ function findV2PluginPath(fromUrl) {
796
815
  const dir = dirname2(entryPath);
797
816
  if (entryPath.endsWith(".ts")) {
798
817
  const sourcePlugin = join2(dir, "..", "plugin", "meridian-v2");
799
- if (hasV2PluginEntry(sourcePlugin))
818
+ if (hasPluginPackageEntry(sourcePlugin))
800
819
  return sourcePlugin;
801
820
  throw new MissingV2PluginError(sourcePlugin);
802
821
  }
803
822
  const bundledPlugin = join2(dir, "meridian-v2");
804
- if (hasV2PluginEntry(bundledPlugin))
823
+ if (hasPluginPackageEntry(bundledPlugin))
805
824
  return bundledPlugin;
806
825
  throw new MissingV2PluginError(bundledPlugin);
807
826
  }
@@ -856,7 +875,7 @@ function isMeridianEntry(entry) {
856
875
  const packageName = pluginEntryPackage(entry);
857
876
  if (!packageName)
858
877
  return false;
859
- return STALE_PATTERNS.some((pattern) => packageName.includes(pattern)) || packageName.includes("meridian.ts") || packageName.includes("meridian-v2.") || packageName.endsWith("/meridian-v2") || packageName.includes("@rynfar/meridian");
878
+ return STALE_PATTERNS.some((pattern) => packageName.includes(pattern)) || packageName.includes("meridian.ts") || packageName.includes("meridian-v2.") || packageName.endsWith("/meridian-v2") || /[\\/]meridian$/.test(packageName) || packageName.includes("@rynfar/meridian");
860
879
  }
861
880
  function checkPluginConfigured(configPath, expectedPluginPath) {
862
881
  const selectedPath = configPath ?? findOpencodeConfigPath();
@@ -952,4 +971,4 @@ function runSetup(pluginPath, configPath, generation = "v1") {
952
971
  return { configPath: path, pluginPath, alreadyConfigured, removedStale, created: false };
953
972
  }
954
973
 
955
- export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSIONS, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
974
+ export { LRUMap, PRIORITY_ATTESTATION_HEADER, verifyPriorityAttestation, init_priorityAttestation, UnparseableConfigError, MissingV1PluginError, MissingV2PluginError, DuplicateMeridianConfigError, findOpencodeConfigPath, findPluginPath, SUPPORTED_OPENCODE_V2_VERSIONS, findV2PluginPath, classifyOpenCodeVersion, detectOpenCodeGeneration, pluginPathForGeneration, checkPluginConfigured, clearPluginlessWarnings, notePluginlessOpenCodeRequest, runSetup };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-0zhb8ss4.js";
4
+ } from "./cli-198xnjcn.js";
5
5
  import"./cli-5jxyma6z.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-8yp89fan.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./cli-9e5cxp89.js";
11
11
  import"./cli-khhjyk04.js";
12
12
  import"./cli-vj9cv18n.js";
13
- import"./cli-sh8bzdwe.js";
13
+ import"./cli-zdbv40d5.js";
14
14
  import {
15
15
  __require
16
16
  } from "./cli-p9swy5t3.js";
@@ -93,7 +93,7 @@ if (args[0] === "setup") {
93
93
  runSetup,
94
94
  SUPPORTED_OPENCODE_V2_VERSIONS,
95
95
  UnparseableConfigError
96
- } = await import("./setup-knvctar4.js");
96
+ } = await import("./setup-b3ymd9z8.js");
97
97
  const forceV1 = args.includes("--v1");
98
98
  const forceV2 = args.includes("--v2");
99
99
  if (forceV1 && forceV2) {
@@ -205,7 +205,7 @@ async function runCli(start = startProxyServer, runAuthCheck = async () => {
205
205
  return execFile(claudePath, ["auth", "status"], { timeout: 5000 });
206
206
  }) {
207
207
  try {
208
- const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-knvctar4.js");
208
+ const { findOpencodeConfigPath, checkPluginConfigured, findPluginPath } = await import("./setup-b3ymd9z8.js");
209
209
  const configPath = findOpencodeConfigPath();
210
210
  const { existsSync } = await import("fs");
211
211
  if (existsSync(configPath) && !checkPluginConfigured(configPath)) {
@@ -0,0 +1,252 @@
1
+ // plugin/priority-attestation.ts
2
+ import { createHash, createHmac } from "node:crypto";
3
+ import { readFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ var PRIORITY_ATTESTATION_HEADER = "x-meridian-opencode-turn";
7
+ var PRIORITY_ATTESTATION_KEY_ENV = "MERIDIAN_OPENCODE_ATTESTATION_KEY";
8
+ var PRIORITY_ATTESTATION_KEY_FILE = "opencode-turn.key";
9
+ var TOKEN_PREFIX = "v1";
10
+ var MAC_DOMAIN = "meridian.opencode.turn.v1\x00";
11
+ var TURN_DOMAIN = "meridian.opencode.human.v1\x00";
12
+ var MAX_HEADER_BYTES = 768;
13
+ var MAX_PAYLOAD_BYTES = 384;
14
+ var TURN_DIGEST_PATTERN = /^[A-Za-z0-9_-]{43}$/;
15
+ var SAFE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
16
+ function configDirectory() {
17
+ return process.env.MERIDIAN_CONFIG_DIR ?? join(homedir(), ".config", "meridian");
18
+ }
19
+ function priorityAttestationKeyPath() {
20
+ return join(configDirectory(), PRIORITY_ATTESTATION_KEY_FILE);
21
+ }
22
+ function isSafeAgentId(value) {
23
+ return value.length >= 1 && value.length <= 64 && value.trim() === value && /^[\x20-\x7E]+$/.test(value);
24
+ }
25
+ function decodeCanonicalBase64Url(raw) {
26
+ if (!/^[A-Za-z0-9_-]+$/.test(raw))
27
+ return;
28
+ const decoded = Buffer.from(raw, "base64url");
29
+ return decoded.toString("base64url") === raw ? decoded : undefined;
30
+ }
31
+ function decodePriorityAttestationKey(raw) {
32
+ if (raw === undefined)
33
+ return;
34
+ const normalized = raw.trim();
35
+ const decoded = decodeCanonicalBase64Url(normalized);
36
+ return decoded?.length === 32 ? decoded : undefined;
37
+ }
38
+ function loadPriorityAttestationKey() {
39
+ const fromEnv = process.env[PRIORITY_ATTESTATION_KEY_ENV];
40
+ if (fromEnv !== undefined)
41
+ return decodePriorityAttestationKey(fromEnv);
42
+ try {
43
+ return decodePriorityAttestationKey(readFileSync(priorityAttestationKeyPath(), "utf8"));
44
+ } catch {
45
+ return;
46
+ }
47
+ }
48
+ function computePriorityTurnDigest(input) {
49
+ if (!SAFE_ID_PATTERN.test(input.sessionId) || !SAFE_ID_PATTERN.test(input.humanMessageId)) {
50
+ return;
51
+ }
52
+ return createHash("sha256").update(TURN_DOMAIN).update(input.generation).update("\x00").update(input.sessionId).update("\x00").update(input.humanMessageId).digest("base64url");
53
+ }
54
+ function createPriorityAttestation(input, key = loadPriorityAttestationKey() ?? Buffer.alloc(0)) {
55
+ if (key.length !== 32)
56
+ return;
57
+ if (!SAFE_ID_PATTERN.test(input.sessionId) || !isSafeAgentId(input.agentId))
58
+ return;
59
+ const turnDigest = computePriorityTurnDigest(input);
60
+ if (!turnDigest || !TURN_DIGEST_PATTERN.test(turnDigest))
61
+ return;
62
+ const issuedAt = input.issuedAt;
63
+ if (!Number.isSafeInteger(issuedAt) || issuedAt < 0)
64
+ return;
65
+ const payload = JSON.stringify({
66
+ v: 1,
67
+ g: input.generation,
68
+ s: input.sessionId,
69
+ a: input.agentId,
70
+ t: turnDigest,
71
+ iat: issuedAt
72
+ });
73
+ if (Buffer.byteLength(payload) > MAX_PAYLOAD_BYTES)
74
+ return;
75
+ const encodedPayload = Buffer.from(payload).toString("base64url");
76
+ const mac = createHmac("sha256", key).update(MAC_DOMAIN).update(payload).digest("base64url");
77
+ const token = `${TOKEN_PREFIX}.${encodedPayload}.${mac}`;
78
+ return Buffer.byteLength(token) <= MAX_HEADER_BYTES ? token : undefined;
79
+ }
80
+ function deleteHeader(headers, name) {
81
+ if (headers instanceof Headers) {
82
+ headers.delete(name);
83
+ return;
84
+ }
85
+ const lower = name.toLowerCase();
86
+ for (const key of Object.keys(headers)) {
87
+ if (key.toLowerCase() === lower)
88
+ delete headers[key];
89
+ }
90
+ }
91
+ function getHeader(headers, name) {
92
+ if (headers instanceof Headers)
93
+ return headers.get(name) ?? undefined;
94
+ const lower = name.toLowerCase();
95
+ for (const [key, value] of Object.entries(headers)) {
96
+ if (key.toLowerCase() === lower)
97
+ return value;
98
+ }
99
+ return;
100
+ }
101
+ function setHeader(headers, name, value) {
102
+ deleteHeader(headers, name);
103
+ if (headers instanceof Headers)
104
+ headers.set(name, value);
105
+ else
106
+ headers[name] = value;
107
+ }
108
+
109
+ // plugin/meridian.ts
110
+ var BUILTIN_AGENT_MODES = {
111
+ build: "primary",
112
+ plan: "primary",
113
+ general: "subagent",
114
+ explore: "subagent",
115
+ title: "subagent",
116
+ summary: "subagent",
117
+ compaction: "subagent"
118
+ };
119
+ var INTERNAL_AGENT_IDS = new Set(["title", "summary", "compaction"]);
120
+ var ROOT_SESSION_CACHE_MAX = 256;
121
+ var ROOT_SESSION_CACHE_TTL_MS = 5000;
122
+ var LOOKUP_TIMEOUT_MS = 250;
123
+ function isRecord(value) {
124
+ return typeof value === "object" && value !== null && !Array.isArray(value);
125
+ }
126
+ async function withTimeout(pending, timeoutMs) {
127
+ let timer;
128
+ const timedOut = new Promise((resolve) => {
129
+ timer = setTimeout(() => resolve(undefined), timeoutMs);
130
+ timer.unref?.();
131
+ });
132
+ try {
133
+ return await Promise.race([pending, timedOut]);
134
+ } finally {
135
+ if (timer)
136
+ clearTimeout(timer);
137
+ }
138
+ }
139
+ var MeridianPlugin = async (pluginInput) => {
140
+ let configAgents = {};
141
+ const rootSessionCache = new Map;
142
+ const resolve = (agent) => {
143
+ if (typeof agent === "object" && agent !== null) {
144
+ return { name: agent.name ?? "unknown", mode: agent.mode ?? "primary" };
145
+ }
146
+ const name = String(agent);
147
+ return { name, mode: configAgents[name]?.mode ?? BUILTIN_AGENT_MODES[name] ?? "primary" };
148
+ };
149
+ const isStrictVisiblePrimary = (agent) => {
150
+ const name = typeof agent === "string" ? agent : agent.name;
151
+ if (!name || INTERNAL_AGENT_IDS.has(name))
152
+ return false;
153
+ if (typeof agent === "object") {
154
+ return agent.mode === "primary" && agent.hidden !== true;
155
+ }
156
+ const configured = configAgents[name];
157
+ if (configured)
158
+ return configured.mode === "primary" && configured.hidden !== true;
159
+ return BUILTIN_AGENT_MODES[name] === "primary";
160
+ };
161
+ const isRootSession = async (sessionID) => {
162
+ const cached = rootSessionCache.get(sessionID);
163
+ if (cached && cached.expiresAt > Date.now())
164
+ return cached.pending;
165
+ const pending = (async () => {
166
+ if (!isRecord(pluginInput) || !isRecord(pluginInput.client))
167
+ return false;
168
+ const sessionApi = pluginInput.client.session;
169
+ if (!isRecord(sessionApi) || typeof sessionApi.get !== "function")
170
+ return false;
171
+ const controller = new AbortController;
172
+ try {
173
+ const lookup = Promise.resolve(Reflect.apply(sessionApi.get, sessionApi, [
174
+ { path: { id: sessionID }, signal: controller.signal }
175
+ ]));
176
+ const result = await withTimeout(lookup, LOOKUP_TIMEOUT_MS);
177
+ if (!isRecord(result))
178
+ return false;
179
+ const data = isRecord(result.data) ? result.data : result;
180
+ return data.id === sessionID && data.parentID === undefined && data.fork === undefined;
181
+ } catch {
182
+ return false;
183
+ } finally {
184
+ controller.abort();
185
+ }
186
+ })();
187
+ rootSessionCache.delete(sessionID);
188
+ rootSessionCache.set(sessionID, { expiresAt: Date.now() + ROOT_SESSION_CACHE_TTL_MS, pending });
189
+ while (rootSessionCache.size > ROOT_SESSION_CACHE_MAX) {
190
+ const oldest = rootSessionCache.keys().next().value;
191
+ if (oldest === undefined)
192
+ break;
193
+ rootSessionCache.delete(oldest);
194
+ }
195
+ const eligible = await pending;
196
+ if (!eligible && rootSessionCache.get(sessionID)?.pending === pending)
197
+ rootSessionCache.delete(sessionID);
198
+ return eligible;
199
+ };
200
+ return {
201
+ config: (cfg) => {
202
+ const next = {};
203
+ for (const [name, def] of Object.entries(cfg?.agent ?? {})) {
204
+ if (!def)
205
+ continue;
206
+ next[name] = {
207
+ ...typeof def.mode === "string" ? { mode: def.mode } : {},
208
+ ...typeof def.hidden === "boolean" ? { hidden: def.hidden } : {}
209
+ };
210
+ }
211
+ configAgents = next;
212
+ },
213
+ "chat.headers": async (incoming, output) => {
214
+ deleteHeader(output.headers, PRIORITY_ATTESTATION_HEADER);
215
+ if (incoming.model.providerID !== "anthropic")
216
+ return;
217
+ setHeader(output.headers, "x-opencode-session", incoming.sessionID);
218
+ setHeader(output.headers, "x-opencode-request", incoming.message.id);
219
+ const { name, mode } = resolve(incoming.agent);
220
+ const safeName = name.replace(/[^\x20-\x7E]/g, "").trim() || "unknown";
221
+ setHeader(output.headers, "x-opencode-agent-mode", mode === "subagent" ? "subagent" : "primary");
222
+ setHeader(output.headers, "x-opencode-agent-name", safeName);
223
+ if (getHeader(output.headers, "x-meridian-profile") !== undefined)
224
+ return;
225
+ if (safeName !== name || !isStrictVisiblePrimary(incoming.agent))
226
+ return;
227
+ const createdAt = incoming.message.time?.created;
228
+ if (typeof createdAt !== "number" || !Number.isSafeInteger(createdAt) || createdAt < 0)
229
+ return;
230
+ const issuedAt = Math.floor(createdAt / 1000);
231
+ if (incoming.message.sessionID !== undefined && incoming.message.sessionID !== incoming.sessionID)
232
+ return;
233
+ if (!await isRootSession(incoming.sessionID))
234
+ return;
235
+ if (!isStrictVisiblePrimary(incoming.agent))
236
+ return;
237
+ const token = createPriorityAttestation({
238
+ generation: "oc1",
239
+ sessionId: incoming.sessionID,
240
+ agentId: safeName,
241
+ humanMessageId: incoming.message.id,
242
+ issuedAt
243
+ });
244
+ if (token)
245
+ setHeader(output.headers, PRIORITY_ATTESTATION_HEADER, token);
246
+ }
247
+ };
248
+ };
249
+ var meridian_default = MeridianPlugin;
250
+ export {
251
+ meridian_default as default
252
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@rynfar/meridian-opencode-v1-plugin",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "./index.js"
6
+ }