opencode-anthropic-fix 0.1.31 → 0.2.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 (40) hide show
  1. package/README.md +2 -0
  2. package/cli.mjs +42 -35
  3. package/index.mjs +399 -3591
  4. package/lib/config.mjs +9 -10
  5. package/lib/config.test.mjs +298 -1
  6. package/lib/diagnose.mjs +184 -0
  7. package/lib/diagnose.test.mjs +122 -0
  8. package/lib/env.mjs +19 -0
  9. package/lib/env.test.mjs +25 -0
  10. package/lib/mimicry/cache.mjs +62 -0
  11. package/lib/mimicry/cache.test.mjs +112 -0
  12. package/lib/mimicry/headers.mjs +530 -0
  13. package/lib/mimicry/headers.test.mjs +50 -0
  14. package/lib/mimicry/models.mjs +146 -0
  15. package/lib/mimicry/models.test.mjs +132 -0
  16. package/lib/mimicry/request-body.mjs +684 -0
  17. package/lib/mimicry/request-body.test.mjs +34 -0
  18. package/lib/mimicry/request-helpers.mjs +243 -0
  19. package/lib/mimicry/request-helpers.test.mjs +155 -0
  20. package/lib/mimicry/response-stream.mjs +429 -0
  21. package/lib/mimicry/response-stream.test.mjs +127 -0
  22. package/lib/mimicry/system-prompt.mjs +638 -0
  23. package/lib/mimicry/system-prompt.test.mjs +73 -0
  24. package/lib/oauth.mjs +1 -1
  25. package/lib/oauth.test.mjs +8 -4
  26. package/lib/redact.mjs +147 -0
  27. package/lib/redact.test.mjs +154 -0
  28. package/lib/refresh-lock.test.mjs +52 -0
  29. package/lib/retry/overload-loop.mjs +83 -0
  30. package/lib/retry/overload-loop.test.mjs +107 -0
  31. package/lib/session-metrics.mjs +75 -0
  32. package/lib/session-metrics.test.mjs +67 -0
  33. package/lib/storage.test.mjs +82 -0
  34. package/lib/token-economy/microcompact.mjs +35 -0
  35. package/lib/token-economy/microcompact.test.mjs +28 -0
  36. package/lib/token-economy/transforms.mjs +637 -0
  37. package/lib/token-economy/transforms.test.mjs +192 -0
  38. package/lib/tuning.mjs +70 -0
  39. package/lib/tuning.test.mjs +89 -0
  40. package/package.json +4 -1
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { isFalsyEnv, isTruthyEnv } from "./env.mjs";
4
+
5
+ describe("environment value parsers", () => {
6
+ describe("isTruthyEnv", () => {
7
+ it.each(["1", "true", "yes", " TRUE ", "Yes"])("accepts %j", (value) => {
8
+ expect(isTruthyEnv(value)).toBe(true);
9
+ });
10
+
11
+ it.each([undefined, "", "0", "false", "no", "on", "off", "anything"])("rejects %j", (value) => {
12
+ expect(isTruthyEnv(value)).toBe(false);
13
+ });
14
+ });
15
+
16
+ describe("isFalsyEnv", () => {
17
+ it.each(["0", "false", "no", " FALSE ", "No"])("accepts %j", (value) => {
18
+ expect(isFalsyEnv(value)).toBe(true);
19
+ });
20
+
21
+ it.each([undefined, "", "1", "true", "yes", "on", "off", "anything"])("rejects %j", (value) => {
22
+ expect(isFalsyEnv(value)).toBe(false);
23
+ });
24
+ });
25
+ });
@@ -0,0 +1,62 @@
1
+ import { isTruthyEnv } from "../env.mjs";
2
+
3
+ /**
4
+ * Resolve the prompt-cache TTL for the current request role and overrides.
5
+ *
6
+ * @param {{configuredTtl: string, roleScopedTtl: boolean, isMainForCache: boolean, isSubagent?: boolean, env?: NodeJS.ProcessEnv}} options
7
+ * @returns {string}
8
+ */
9
+ export function resolveCacheTtl({
10
+ configuredTtl,
11
+ roleScopedTtl,
12
+ isMainForCache,
13
+ isSubagent = false,
14
+ env = process.env,
15
+ }) {
16
+ if (isTruthyEnv(env.FORCE_PROMPT_CACHING_5M)) return "5m";
17
+ if (isTruthyEnv(env.ENABLE_PROMPT_CACHING_1H)) return "1h";
18
+ if (roleScopedTtl && isSubagent) return "5m";
19
+ if (roleScopedTtl && !isMainForCache) return "5m";
20
+ return configuredTtl;
21
+ }
22
+
23
+ /**
24
+ * Decide whether the tools cache breakpoint should be placed for this turn.
25
+ *
26
+ * @param {Map<string, number> | null | undefined} stability
27
+ * @returns {boolean}
28
+ */
29
+ export function shouldPlaceToolBreakpoint(stability) {
30
+ if (!stability || stability.size === 0) return true;
31
+ const STABLE_TURNS = 2;
32
+ const systemStability = stability.get("system_prompt");
33
+ const systemIsStable = typeof systemStability === "number" && systemStability >= STABLE_TURNS;
34
+ if (!systemIsStable) return true;
35
+ for (const [source, turns] of stability) {
36
+ if (source.startsWith("tool:") && turns === 0) {
37
+ return false;
38
+ }
39
+ }
40
+ return true;
41
+ }
42
+
43
+ /**
44
+ * Update consecutive-unchanged counters on the supplied stability map.
45
+ *
46
+ * @param {Map<string, string>} current
47
+ * @param {Map<string, string>} previous
48
+ * @param {Map<string, number>} stability
49
+ * @returns {void}
50
+ */
51
+ export function updateBoundaryStability(current, previous, stability) {
52
+ for (const [source, hash] of current) {
53
+ if (previous.get(source) === hash) {
54
+ stability.set(source, (stability.get(source) || 0) + 1);
55
+ } else {
56
+ stability.set(source, 0);
57
+ }
58
+ }
59
+ for (const source of [...stability.keys()]) {
60
+ if (!current.has(source)) stability.delete(source);
61
+ }
62
+ }
@@ -0,0 +1,112 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { resolveCacheTtl, shouldPlaceToolBreakpoint, updateBoundaryStability } from "./cache.mjs";
4
+
5
+ describe("resolveCacheTtl", () => {
6
+ const base = {
7
+ configuredTtl: "1h",
8
+ roleScopedTtl: true,
9
+ isMainForCache: true,
10
+ env: {},
11
+ };
12
+
13
+ it("returns the configured TTL for the main role", () => {
14
+ expect(resolveCacheTtl(base)).toBe("1h");
15
+ });
16
+
17
+ it("downgrades side-query and subagent roles when role scoping is enabled", () => {
18
+ expect(resolveCacheTtl({ ...base, isMainForCache: false })).toBe("5m");
19
+ expect(resolveCacheTtl({ ...base, isSubagent: true })).toBe("5m");
20
+ });
21
+
22
+ it("honors the 5m environment override first", () => {
23
+ expect(
24
+ resolveCacheTtl({
25
+ ...base,
26
+ env: { FORCE_PROMPT_CACHING_5M: "1", ENABLE_PROMPT_CACHING_1H: "1" },
27
+ }),
28
+ ).toBe("5m");
29
+ });
30
+
31
+ it("honors the 1h environment override before role scoping", () => {
32
+ expect(
33
+ resolveCacheTtl({
34
+ ...base,
35
+ isMainForCache: false,
36
+ env: { ENABLE_PROMPT_CACHING_1H: "true" },
37
+ }),
38
+ ).toBe("1h");
39
+ });
40
+
41
+ it("keeps the configured TTL when role scoping is disabled", () => {
42
+ expect(resolveCacheTtl({ ...base, configuredTtl: "5m", roleScopedTtl: false })).toBe("5m");
43
+ });
44
+ });
45
+
46
+ describe("shouldPlaceToolBreakpoint", () => {
47
+ it("places a breakpoint when stability data is unavailable", () => {
48
+ expect(shouldPlaceToolBreakpoint(undefined)).toBe(true);
49
+ expect(shouldPlaceToolBreakpoint(new Map())).toBe(true);
50
+ });
51
+
52
+ it("places a breakpoint below the stable-system boundary", () => {
53
+ expect(
54
+ shouldPlaceToolBreakpoint(
55
+ new Map([
56
+ ["system_prompt", 1],
57
+ ["tool:read", 0],
58
+ ]),
59
+ ),
60
+ ).toBe(true);
61
+ });
62
+
63
+ it("skips a volatile tool breakpoint once the system is stable", () => {
64
+ expect(
65
+ shouldPlaceToolBreakpoint(
66
+ new Map([
67
+ ["system_prompt", 2],
68
+ ["tool:read", 0],
69
+ ]),
70
+ ),
71
+ ).toBe(false);
72
+ });
73
+
74
+ it("places the breakpoint when both system and tools are stable", () => {
75
+ expect(
76
+ shouldPlaceToolBreakpoint(
77
+ new Map([
78
+ ["system_prompt", 2],
79
+ ["tool:read", 1],
80
+ ]),
81
+ ),
82
+ ).toBe(true);
83
+ });
84
+ });
85
+
86
+ describe("updateBoundaryStability", () => {
87
+ it("mutates only the passed stability map across boundary transitions", () => {
88
+ const previous = new Map([
89
+ ["system_prompt", "system-v1"],
90
+ ["tool:read", "read-v1"],
91
+ ["tool:gone", "gone-v1"],
92
+ ]);
93
+ const current = new Map([
94
+ ["system_prompt", "system-v1"],
95
+ ["tool:read", "read-v2"],
96
+ ["tool:new", "new-v1"],
97
+ ]);
98
+ const stability = new Map([
99
+ ["system_prompt", 1],
100
+ ["tool:read", 4],
101
+ ["tool:gone", 3],
102
+ ]);
103
+
104
+ expect(updateBoundaryStability(current, previous, stability)).toBeUndefined();
105
+
106
+ expect([...stability]).toEqual([
107
+ ["system_prompt", 2],
108
+ ["tool:read", 0],
109
+ ["tool:new", 0],
110
+ ]);
111
+ });
112
+ });