orbit-agent-runtime 0.8.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 (168) hide show
  1. package/CHANGELOG.md +750 -0
  2. package/LICENSE +201 -0
  3. package/README.md +492 -0
  4. package/README.zh-CN.md +389 -0
  5. package/benchmarks/README.md +24 -0
  6. package/benchmarks/gateway.mjs +68 -0
  7. package/benchmarks/pae.mjs +90 -0
  8. package/benchmarks/replay.mjs +51 -0
  9. package/benchmarks/run-all.mjs +24 -0
  10. package/benchmarks/wal.mjs +50 -0
  11. package/bin/orbit.mjs +523 -0
  12. package/dist/.tsbuildinfo +1 -0
  13. package/dist/demo-deepseek.d.ts +2 -0
  14. package/dist/demo-deepseek.d.ts.map +1 -0
  15. package/dist/demo-deepseek.js +66 -0
  16. package/dist/demo-deepseek.js.map +1 -0
  17. package/dist/demo-host.d.ts +2 -0
  18. package/dist/demo-host.d.ts.map +1 -0
  19. package/dist/demo-host.js +98 -0
  20. package/dist/demo-host.js.map +1 -0
  21. package/dist/demo-replay.d.ts +2 -0
  22. package/dist/demo-replay.d.ts.map +1 -0
  23. package/dist/demo-replay.js +68 -0
  24. package/dist/demo-replay.js.map +1 -0
  25. package/dist/src/core/orbitRuntimeHost.d.ts +349 -0
  26. package/dist/src/core/orbitRuntimeHost.d.ts.map +1 -0
  27. package/dist/src/core/orbitRuntimeHost.js +706 -0
  28. package/dist/src/core/orbitRuntimeHost.js.map +1 -0
  29. package/dist/src/index.d.ts +92 -0
  30. package/dist/src/index.d.ts.map +1 -0
  31. package/dist/src/index.js +226 -0
  32. package/dist/src/index.js.map +1 -0
  33. package/dist/test/audit_chain.test.d.ts +2 -0
  34. package/dist/test/audit_chain.test.d.ts.map +1 -0
  35. package/dist/test/audit_chain.test.js +283 -0
  36. package/dist/test/audit_chain.test.js.map +1 -0
  37. package/dist/test/channel_hub.test.d.ts +2 -0
  38. package/dist/test/channel_hub.test.d.ts.map +1 -0
  39. package/dist/test/channel_hub.test.js +66 -0
  40. package/dist/test/channel_hub.test.js.map +1 -0
  41. package/dist/test/cordis_adapter.test.d.ts +2 -0
  42. package/dist/test/cordis_adapter.test.d.ts.map +1 -0
  43. package/dist/test/cordis_adapter.test.js +280 -0
  44. package/dist/test/cordis_adapter.test.js.map +1 -0
  45. package/dist/test/cost_routing.test.d.ts +2 -0
  46. package/dist/test/cost_routing.test.d.ts.map +1 -0
  47. package/dist/test/cost_routing.test.js +31 -0
  48. package/dist/test/cost_routing.test.js.map +1 -0
  49. package/dist/test/domain_transaction.test.d.ts +2 -0
  50. package/dist/test/domain_transaction.test.d.ts.map +1 -0
  51. package/dist/test/domain_transaction.test.js +351 -0
  52. package/dist/test/domain_transaction.test.js.map +1 -0
  53. package/dist/test/file_channel.test.d.ts +2 -0
  54. package/dist/test/file_channel.test.d.ts.map +1 -0
  55. package/dist/test/file_channel.test.js +179 -0
  56. package/dist/test/file_channel.test.js.map +1 -0
  57. package/dist/test/gateway.test.d.ts +2 -0
  58. package/dist/test/gateway.test.d.ts.map +1 -0
  59. package/dist/test/gateway.test.js +548 -0
  60. package/dist/test/gateway.test.js.map +1 -0
  61. package/dist/test/governance_profile.test.d.ts +2 -0
  62. package/dist/test/governance_profile.test.d.ts.map +1 -0
  63. package/dist/test/governance_profile.test.js +214 -0
  64. package/dist/test/governance_profile.test.js.map +1 -0
  65. package/dist/test/governance_schema.test.d.ts +2 -0
  66. package/dist/test/governance_schema.test.d.ts.map +1 -0
  67. package/dist/test/governance_schema.test.js +200 -0
  68. package/dist/test/governance_schema.test.js.map +1 -0
  69. package/dist/test/host_integration.test.d.ts +2 -0
  70. package/dist/test/host_integration.test.d.ts.map +1 -0
  71. package/dist/test/host_integration.test.js +104 -0
  72. package/dist/test/host_integration.test.js.map +1 -0
  73. package/dist/test/host_journal_persistence.test.d.ts +2 -0
  74. package/dist/test/host_journal_persistence.test.d.ts.map +1 -0
  75. package/dist/test/host_journal_persistence.test.js +367 -0
  76. package/dist/test/host_journal_persistence.test.js.map +1 -0
  77. package/dist/test/impact_domain.test.d.ts +2 -0
  78. package/dist/test/impact_domain.test.d.ts.map +1 -0
  79. package/dist/test/impact_domain.test.js +48 -0
  80. package/dist/test/impact_domain.test.js.map +1 -0
  81. package/dist/test/injected_clock.test.d.ts +13 -0
  82. package/dist/test/injected_clock.test.d.ts.map +1 -0
  83. package/dist/test/injected_clock.test.js +200 -0
  84. package/dist/test/injected_clock.test.js.map +1 -0
  85. package/dist/test/isolation_domain.test.d.ts +2 -0
  86. package/dist/test/isolation_domain.test.d.ts.map +1 -0
  87. package/dist/test/isolation_domain.test.js +302 -0
  88. package/dist/test/isolation_domain.test.js.map +1 -0
  89. package/dist/test/journal_wal.test.d.ts +2 -0
  90. package/dist/test/journal_wal.test.d.ts.map +1 -0
  91. package/dist/test/journal_wal.test.js +622 -0
  92. package/dist/test/journal_wal.test.js.map +1 -0
  93. package/dist/test/mcp_adapter.test.d.ts +2 -0
  94. package/dist/test/mcp_adapter.test.d.ts.map +1 -0
  95. package/dist/test/mcp_adapter.test.js +440 -0
  96. package/dist/test/mcp_adapter.test.js.map +1 -0
  97. package/dist/test/openai_compat_channel.test.d.ts +2 -0
  98. package/dist/test/openai_compat_channel.test.d.ts.map +1 -0
  99. package/dist/test/openai_compat_channel.test.js +325 -0
  100. package/dist/test/openai_compat_channel.test.js.map +1 -0
  101. package/dist/test/openapi_adapter.test.d.ts +2 -0
  102. package/dist/test/openapi_adapter.test.d.ts.map +1 -0
  103. package/dist/test/openapi_adapter.test.js +270 -0
  104. package/dist/test/openapi_adapter.test.js.map +1 -0
  105. package/dist/test/orbit_cli.test.d.ts +2 -0
  106. package/dist/test/orbit_cli.test.d.ts.map +1 -0
  107. package/dist/test/orbit_cli.test.js +137 -0
  108. package/dist/test/orbit_cli.test.js.map +1 -0
  109. package/dist/test/pact_verifier.test.d.ts +2 -0
  110. package/dist/test/pact_verifier.test.d.ts.map +1 -0
  111. package/dist/test/pact_verifier.test.js +41 -0
  112. package/dist/test/pact_verifier.test.js.map +1 -0
  113. package/dist/test/pae_adapter.test.d.ts +2 -0
  114. package/dist/test/pae_adapter.test.d.ts.map +1 -0
  115. package/dist/test/pae_adapter.test.js +469 -0
  116. package/dist/test/pae_adapter.test.js.map +1 -0
  117. package/dist/test/plugin_sandbox_guard.test.d.ts +2 -0
  118. package/dist/test/plugin_sandbox_guard.test.d.ts.map +1 -0
  119. package/dist/test/plugin_sandbox_guard.test.js +57 -0
  120. package/dist/test/plugin_sandbox_guard.test.js.map +1 -0
  121. package/dist/test/rate_limit_behavior.test.d.ts +2 -0
  122. package/dist/test/rate_limit_behavior.test.d.ts.map +1 -0
  123. package/dist/test/rate_limit_behavior.test.js +85 -0
  124. package/dist/test/rate_limit_behavior.test.js.map +1 -0
  125. package/dist/test/replay_compat.test.d.ts +2 -0
  126. package/dist/test/replay_compat.test.d.ts.map +1 -0
  127. package/dist/test/replay_compat.test.js +1122 -0
  128. package/dist/test/replay_compat.test.js.map +1 -0
  129. package/dist/test/replay_engine.test.d.ts +2 -0
  130. package/dist/test/replay_engine.test.d.ts.map +1 -0
  131. package/dist/test/replay_engine.test.js +85 -0
  132. package/dist/test/replay_engine.test.js.map +1 -0
  133. package/dist/test/shell_channel.test.d.ts +2 -0
  134. package/dist/test/shell_channel.test.d.ts.map +1 -0
  135. package/dist/test/shell_channel.test.js +173 -0
  136. package/dist/test/shell_channel.test.js.map +1 -0
  137. package/dist/test/teardown_isolation.test.d.ts +10 -0
  138. package/dist/test/teardown_isolation.test.d.ts.map +1 -0
  139. package/dist/test/teardown_isolation.test.js +279 -0
  140. package/dist/test/teardown_isolation.test.js.map +1 -0
  141. package/dist/test/token_budget.test.d.ts +2 -0
  142. package/dist/test/token_budget.test.d.ts.map +1 -0
  143. package/dist/test/token_budget.test.js +113 -0
  144. package/dist/test/token_budget.test.js.map +1 -0
  145. package/dist/test/trace_journal.test.d.ts +2 -0
  146. package/dist/test/trace_journal.test.d.ts.map +1 -0
  147. package/dist/test/trace_journal.test.js +43 -0
  148. package/dist/test/trace_journal.test.js.map +1 -0
  149. package/dist/test/trace_journal_persistence.test.d.ts +2 -0
  150. package/dist/test/trace_journal_persistence.test.d.ts.map +1 -0
  151. package/dist/test/trace_journal_persistence.test.js +175 -0
  152. package/dist/test/trace_journal_persistence.test.js.map +1 -0
  153. package/dist/test/trace_persistence.test.d.ts +2 -0
  154. package/dist/test/trace_persistence.test.d.ts.map +1 -0
  155. package/dist/test/trace_persistence.test.js +216 -0
  156. package/dist/test/trace_persistence.test.js.map +1 -0
  157. package/dist/test/trip_protector.test.d.ts +2 -0
  158. package/dist/test/trip_protector.test.d.ts.map +1 -0
  159. package/dist/test/trip_protector.test.js +42 -0
  160. package/dist/test/trip_protector.test.js.map +1 -0
  161. package/examples/README.md +34 -0
  162. package/examples/cli-record-replay.mjs +74 -0
  163. package/examples/custom-channel.mjs +102 -0
  164. package/examples/js-pae-plugin.mjs +128 -0
  165. package/examples/mcp-adapter.mjs +102 -0
  166. package/package.json +60 -0
  167. package/src/core/orbitRuntimeHost.ts +853 -0
  168. package/src/index.ts +256 -0
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = require("node:test");
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const core_hub_1 = require("@orbit/core-hub");
9
+ /** Minimal fake fetch returning a canned chat completion. */
10
+ function fakeFetchOk(content, calls) {
11
+ return (async (url, init) => {
12
+ calls.push({ url: String(url), init: init });
13
+ return {
14
+ ok: true,
15
+ status: 200,
16
+ statusText: "OK",
17
+ json: async () => ({ choices: [{ message: { role: "assistant", content } }] })
18
+ };
19
+ });
20
+ }
21
+ /** Fake fetch that fails N times with the given status, then succeeds. */
22
+ function fakeFetchRetrySequence(failures, finalContent, calls) {
23
+ let attempt = 0;
24
+ return (async (url, init) => {
25
+ calls.push({ url: String(url), init: init });
26
+ const failure = failures[attempt];
27
+ attempt += 1;
28
+ if (failure) {
29
+ const headers = new Map();
30
+ if (failure.retryAfter !== undefined)
31
+ headers.set("retry-after", failure.retryAfter);
32
+ return {
33
+ ok: false,
34
+ status: failure.status,
35
+ statusText: "ERR",
36
+ headers: { get: (name) => headers.get(name.toLowerCase()) ?? null },
37
+ json: async () => ({ error: { message: `attempt ${attempt} failed` } })
38
+ };
39
+ }
40
+ return {
41
+ ok: true,
42
+ status: 200,
43
+ statusText: "OK",
44
+ json: async () => ({ choices: [{ message: { role: "assistant", content: finalContent } }] })
45
+ };
46
+ });
47
+ }
48
+ function fakeFetchError(status, message) {
49
+ return (async () => ({
50
+ ok: false,
51
+ status,
52
+ statusText: "ERR",
53
+ json: async () => ({ error: { message } })
54
+ }));
55
+ }
56
+ // ------------------------------------------------------------- construction
57
+ (0, node_test_1.test)("DeepSeekChannel: requires an api key field (empty allowed for unauthenticated endpoints)", () => {
58
+ strict_1.default.throws(() => new core_hub_1.DeepSeekChannel({ apiKey: undefined }), /apiKey/);
59
+ });
60
+ // ------------------------------------------------------------- happy path
61
+ (0, node_test_1.test)("DeepSeekChannel: chatRound posts OpenAI-compatible payload and returns content", async () => {
62
+ const calls = [];
63
+ const channel = new core_hub_1.DeepSeekChannel({
64
+ apiKey: "sk-test",
65
+ model: "deepseek-chat",
66
+ fetchImpl: fakeFetchOk("reproducibility is trust", calls)
67
+ });
68
+ const out = await channel.chatRound("why?");
69
+ strict_1.default.equal(out, "reproducibility is trust");
70
+ strict_1.default.equal(calls.length, 1);
71
+ strict_1.default.equal(calls[0].url, "https://api.deepseek.com/chat/completions");
72
+ const headers = calls[0].init.headers;
73
+ strict_1.default.equal(headers.Authorization, "Bearer sk-test");
74
+ const body = JSON.parse(String(calls[0].init.body));
75
+ strict_1.default.equal(body.model, "deepseek-chat");
76
+ strict_1.default.equal(body.messages[0].role, "user");
77
+ strict_1.default.equal(body.stream, false);
78
+ });
79
+ (0, node_test_1.test)("OpenAICompatChannel: works against any compatible endpoint", async () => {
80
+ const calls = [];
81
+ const channel = new core_hub_1.OpenAICompatChannel({
82
+ apiKey: "sk-ollama",
83
+ baseUrl: "http://localhost:11434/v1",
84
+ model: "llama3",
85
+ fetchImpl: fakeFetchOk("local answer", calls)
86
+ });
87
+ const out = await channel.chatRound("hi");
88
+ strict_1.default.equal(out, "local answer");
89
+ strict_1.default.equal(calls[0].url, "http://localhost:11434/v1/chat/completions");
90
+ const body = JSON.parse(String(calls[0].init.body));
91
+ strict_1.default.equal(body.model, "llama3");
92
+ });
93
+ (0, node_test_1.test)("OpenAICompatChannel: requires an api key field (empty allowed for unauthenticated endpoints)", () => {
94
+ strict_1.default.throws(() => new core_hub_1.OpenAICompatChannel({ apiKey: undefined, baseUrl: "https://x" }), /apiKey/);
95
+ // empty key is legal: local endpoints like Ollama need no auth
96
+ const channel = new core_hub_1.OpenAICompatChannel({ apiKey: "", baseUrl: "http://localhost:11434/v1" });
97
+ strict_1.default.ok(channel);
98
+ });
99
+ (0, node_test_1.test)("OpenAICompatChannel: omits Authorization header when key is empty", async () => {
100
+ const calls = [];
101
+ const channel = new core_hub_1.OpenAICompatChannel({
102
+ apiKey: "",
103
+ baseUrl: "http://localhost:11434/v1",
104
+ fetchImpl: fakeFetchOk("local", calls)
105
+ });
106
+ await channel.chatRound("hi");
107
+ const headers = calls[0].init.headers;
108
+ strict_1.default.equal(headers.Authorization, undefined);
109
+ });
110
+ // ----------------------------------------------------- per-call options
111
+ (0, node_test_1.test)("chatRound: opts.messages replaces the single user prompt", async () => {
112
+ const calls = [];
113
+ const channel = new core_hub_1.OpenAICompatChannel({
114
+ apiKey: "sk-test",
115
+ baseUrl: "https://x",
116
+ fetchImpl: fakeFetchOk("ok", calls)
117
+ });
118
+ await channel.chatRound("ignored-when-messages-present", {
119
+ messages: [
120
+ { role: "system", content: "be terse" },
121
+ { role: "user", content: "hello" }
122
+ ]
123
+ });
124
+ const body = JSON.parse(String(calls[0].init.body));
125
+ strict_1.default.deepEqual(body.messages.map((m) => m.role), ["system", "user"]);
126
+ });
127
+ (0, node_test_1.test)("chatRound: per-call seed/temperature/maxTokens/response_format override config", async () => {
128
+ const calls = [];
129
+ const channel = new core_hub_1.OpenAICompatChannel({
130
+ apiKey: "sk-test",
131
+ baseUrl: "https://x",
132
+ temperature: 0.9,
133
+ fetchImpl: fakeFetchOk("ok", calls)
134
+ });
135
+ await channel.chatRound("hi", { temperature: 0, seed: 42, maxTokens: 128, responseFormat: "json" });
136
+ const body = JSON.parse(String(calls[0].init.body));
137
+ strict_1.default.equal(body.temperature, 0);
138
+ strict_1.default.equal(body.seed, 42);
139
+ strict_1.default.equal(body.max_tokens, 128);
140
+ strict_1.default.deepEqual(body.response_format, { type: "json" });
141
+ });
142
+ // ------------------------------------------------------ fault taxonomy
143
+ (0, node_test_1.test)("fault taxonomy: retryable vs non-retryable kinds", () => {
144
+ for (const kind of ["timeout", "network", "rate_limited", "server_error"]) {
145
+ strict_1.default.equal((0, core_hub_1.isRetryableLlmFault)(kind), true, kind);
146
+ }
147
+ for (const kind of ["auth", "bad_request", "not_found", "no_content", "invalid_response"]) {
148
+ strict_1.default.equal((0, core_hub_1.isRetryableLlmFault)(kind), false, kind);
149
+ }
150
+ });
151
+ (0, node_test_1.test)("chatRound: HTTP 401 classifies as non-retryable auth fault", async () => {
152
+ const channel = new core_hub_1.DeepSeekChannel({ apiKey: "sk-bad", fetchImpl: fakeFetchError(401, "invalid key") });
153
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
154
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
155
+ strict_1.default.equal(err.faultKind, "auth");
156
+ strict_1.default.equal(err.httpStatus, 401);
157
+ strict_1.default.equal(err.retryable, false);
158
+ strict_1.default.match(err.message, /LLM API error 401 \(auth\): invalid key/);
159
+ return true;
160
+ });
161
+ });
162
+ (0, node_test_1.test)("chatRound: non-JSON error bodies classify by status", async () => {
163
+ const channel = new core_hub_1.DeepSeekChannel({
164
+ apiKey: "sk-test",
165
+ fetchImpl: (async () => ({
166
+ ok: false,
167
+ status: 502,
168
+ statusText: "Bad Gateway",
169
+ json: async () => {
170
+ throw new SyntaxError("Unexpected token '<'");
171
+ }
172
+ }))
173
+ });
174
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
175
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
176
+ strict_1.default.equal(err.faultKind, "server_error");
177
+ strict_1.default.match(err.message, /502/);
178
+ return true;
179
+ });
180
+ });
181
+ (0, node_test_1.test)("chatRound: network failures classify as retryable network fault", async () => {
182
+ const channel = new core_hub_1.DeepSeekChannel({
183
+ apiKey: "sk-test",
184
+ maxRetries: 1,
185
+ initialRetryDelayMs: 1,
186
+ fetchImpl: (async () => {
187
+ throw new TypeError("fetch failed");
188
+ })
189
+ });
190
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
191
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
192
+ strict_1.default.equal(err.faultKind, "network");
193
+ strict_1.default.equal(err.attempts, 2); // 1 initial + 1 retry
194
+ return true;
195
+ });
196
+ });
197
+ (0, node_test_1.test)("chatRound: rejects empty completions as no_content", async () => {
198
+ const channel = new core_hub_1.DeepSeekChannel({
199
+ apiKey: "sk-test",
200
+ fetchImpl: (async () => ({ ok: true, status: 200, statusText: "OK", json: async () => ({ choices: [] }) }))
201
+ });
202
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
203
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
204
+ strict_1.default.equal(err.faultKind, "no_content");
205
+ return true;
206
+ });
207
+ });
208
+ (0, node_test_1.test)("chatRound: HTTP 200 with error payload classifies as invalid_response", async () => {
209
+ const channel = new core_hub_1.DeepSeekChannel({
210
+ apiKey: "sk-test",
211
+ fetchImpl: (async () => ({
212
+ ok: true,
213
+ status: 200,
214
+ statusText: "OK",
215
+ json: async () => ({ error: { message: "gateway says no" } })
216
+ }))
217
+ });
218
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
219
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
220
+ strict_1.default.equal(err.faultKind, "invalid_response");
221
+ return true;
222
+ });
223
+ });
224
+ (0, node_test_1.test)("chatRound: timeout surfaces a classified timeout fault", async () => {
225
+ const timeoutError = new Error("The operation was aborted due to timeout");
226
+ timeoutError.name = "TimeoutError";
227
+ const channel = new core_hub_1.OpenAICompatChannel({
228
+ apiKey: "sk-test",
229
+ baseUrl: "https://x",
230
+ fetchImpl: (async () => {
231
+ throw timeoutError;
232
+ })
233
+ });
234
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
235
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
236
+ strict_1.default.equal(err.faultKind, "timeout");
237
+ strict_1.default.equal(err.retryable, true);
238
+ return true;
239
+ });
240
+ });
241
+ // ---------------------------------------------------------- retry policy
242
+ (0, node_test_1.test)("retry: 429 twice then 200 succeeds on the third attempt", async () => {
243
+ const calls = [];
244
+ const channel = new core_hub_1.DeepSeekChannel({
245
+ apiKey: "sk-test",
246
+ maxRetries: 2,
247
+ initialRetryDelayMs: 1,
248
+ fetchImpl: fakeFetchRetrySequence([{ status: 429 }, { status: 429 }], "third time lucky", calls)
249
+ });
250
+ const out = await channel.chatRound("hi");
251
+ strict_1.default.equal(out, "third time lucky");
252
+ strict_1.default.equal(calls.length, 3);
253
+ });
254
+ (0, node_test_1.test)("retry: non-retryable auth fault never retries (single fetch call)", async () => {
255
+ const calls = [];
256
+ const channel = new core_hub_1.DeepSeekChannel({
257
+ apiKey: "sk-bad",
258
+ maxRetries: 3,
259
+ initialRetryDelayMs: 1,
260
+ fetchImpl: fakeFetchRetrySequence([{ status: 401 }], "never", calls)
261
+ });
262
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
263
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
264
+ strict_1.default.equal(err.faultKind, "auth");
265
+ return true;
266
+ });
267
+ strict_1.default.equal(calls.length, 1);
268
+ });
269
+ (0, node_test_1.test)("retry: exhausted retries report the attempt count", async () => {
270
+ const calls = [];
271
+ const channel = new core_hub_1.DeepSeekChannel({
272
+ apiKey: "sk-test",
273
+ maxRetries: 1,
274
+ initialRetryDelayMs: 1,
275
+ fetchImpl: fakeFetchRetrySequence([{ status: 500 }, { status: 500 }], "never", calls)
276
+ });
277
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
278
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
279
+ strict_1.default.equal(err.faultKind, "server_error");
280
+ strict_1.default.equal(err.attempts, 2);
281
+ return true;
282
+ });
283
+ strict_1.default.equal(calls.length, 2);
284
+ });
285
+ (0, node_test_1.test)("retry: Retry-After header is parsed onto the fault", async () => {
286
+ const calls = [];
287
+ const channel = new core_hub_1.DeepSeekChannel({
288
+ apiKey: "sk-test",
289
+ maxRetries: 0,
290
+ initialRetryDelayMs: 1,
291
+ fetchImpl: fakeFetchRetrySequence([{ status: 429, retryAfter: "3" }], "never", calls)
292
+ });
293
+ await strict_1.default.rejects(channel.chatRound("hi"), (err) => {
294
+ strict_1.default.ok(err instanceof core_hub_1.LlmChannelFaultError);
295
+ strict_1.default.equal(err.retryAfterSeconds, 3);
296
+ return true;
297
+ });
298
+ });
299
+ (0, node_test_1.test)("retry: backoff is deterministic — no Math.random anywhere in the retry path", async () => {
300
+ const calls = [];
301
+ const channel = new core_hub_1.DeepSeekChannel({
302
+ apiKey: "sk-test",
303
+ maxRetries: 2,
304
+ initialRetryDelayMs: 1,
305
+ fetchImpl: fakeFetchRetrySequence([{ status: 429 }, { status: 500 }], "recovered", calls)
306
+ });
307
+ const originalRandom = Math.random;
308
+ Math.random = () => {
309
+ throw new Error("bare Math.random detected inside a channel (charter violation)");
310
+ };
311
+ try {
312
+ const out = await channel.chatRound("hi");
313
+ strict_1.default.equal(out, "recovered");
314
+ }
315
+ finally {
316
+ Math.random = originalRandom;
317
+ }
318
+ strict_1.default.equal(calls.length, 3);
319
+ });
320
+ // ------------------------------------------------------ replay contract
321
+ (0, node_test_1.test)("DeepSeekChannel: replay contract is stochastic + inject", () => {
322
+ const channel = new core_hub_1.DeepSeekChannel({ apiKey: "sk-test", fetchImpl: fakeFetchOk("x", []) });
323
+ strict_1.default.deepEqual(channel.determinismMeta, { determinism: "stochastic", seedable: true, replayPolicy: "inject" });
324
+ });
325
+ //# sourceMappingURL=openai_compat_channel.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai_compat_channel.test.js","sourceRoot":"","sources":["../../test/openai_compat_channel.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=openapi_adapter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi_adapter.test.d.ts","sourceRoot":"","sources":["../../test/openapi_adapter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = require("node:test");
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const index_1 = require("../src/index");
9
+ /**
10
+ * W17 — OpenAPI adapter suite.
11
+ *
12
+ * Three layers under test, kept separate like the MCP suite:
13
+ *
14
+ * 1. **spec** — pure parsing and request-building, so untrusted-document
15
+ * handling and URL determinism are verifiable without any I/O;
16
+ * 2. **transport** — the real `fetch` path with a stubbed client, so status
17
+ * handling and deadlines are exercised without a live server;
18
+ * 3. **adapter + host** — that a parsed foreign surface is registered, governed,
19
+ * recorded and replayed exactly like a native one, and that the transport is
20
+ * released on teardown.
21
+ */
22
+ const CTX = { traceMarkId: "tm-openapi", maxWaitMs: 1000 };
23
+ // ---------------------------------------------------------------------------
24
+ // Spec — parsing
25
+ // ---------------------------------------------------------------------------
26
+ const SAMPLE_DOC = {
27
+ openapi: "3.0.1",
28
+ info: { title: "Pet Store", version: "1.4.2" },
29
+ servers: [{ url: "https://api.example.com/v1" }],
30
+ paths: {
31
+ "/users/{id}": {
32
+ get: {
33
+ operationId: "getUser",
34
+ summary: "Fetch a user",
35
+ parameters: [{ name: "id", in: "path", required: true }]
36
+ }
37
+ },
38
+ "/users": {
39
+ get: {
40
+ operationId: "listUsers",
41
+ parameters: [
42
+ { name: "limit", in: "query" },
43
+ { name: "offset", in: "query" }
44
+ ]
45
+ },
46
+ post: {
47
+ operationId: "createUser",
48
+ summary: "Create a user",
49
+ requestBody: {}
50
+ }
51
+ },
52
+ "/search": {
53
+ get: {
54
+ parameters: [{ name: "q", in: "query", required: true }]
55
+ }
56
+ }
57
+ }
58
+ };
59
+ (0, node_test_1.test)("openapi spec: parses operations with ids, paths and parameters", () => {
60
+ const parsed = (0, index_1.parseOpenApiDocument)(SAMPLE_DOC);
61
+ const ids = parsed.operations.map((o) => o.id).sort();
62
+ strict_1.default.deepEqual(ids, ["createUser", "getUser", "get_search", "listUsers"]);
63
+ const getUser = parsed.operations.find((o) => o.id === "getUser");
64
+ strict_1.default.equal(getUser.method, "get");
65
+ strict_1.default.equal(getUser.path, "/users/{id}");
66
+ strict_1.default.equal(getUser.parameters[0].name, "id");
67
+ strict_1.default.equal(getUser.parameters[0].required, true);
68
+ strict_1.default.equal(parsed.info.version, "1.4.2");
69
+ });
70
+ (0, node_test_1.test)("openapi spec: synthesizes an operation id when none is given", () => {
71
+ const parsed = (0, index_1.parseOpenApiDocument)({
72
+ openapi: "3.0.0",
73
+ paths: { "/weird/path/{x}": { delete: {} } }
74
+ });
75
+ strict_1.default.equal(parsed.operations.length, 1);
76
+ strict_1.default.equal(parsed.operations[0].id, "delete_weird_path__x");
77
+ });
78
+ (0, node_test_1.test)("openapi spec: rejects a document with no usable version", () => {
79
+ strict_1.default.throws(() => (0, index_1.parseOpenApiDocument)({ paths: {} }), /neither `openapi` nor `swagger`/);
80
+ });
81
+ (0, node_test_1.test)("openapi spec: rejects duplicate operation ids", () => {
82
+ strict_1.default.throws(() => (0, index_1.parseOpenApiDocument)({
83
+ openapi: "3.0.0",
84
+ paths: { "/a": { get: { operationId: "dup" } }, "/b": { post: { operationId: "dup" } } }
85
+ }), /duplicate operation id dup/);
86
+ });
87
+ (0, node_test_1.test)("openapi spec: rejects cookie parameters (kernel never sends ambient creds)", () => {
88
+ strict_1.default.throws(() => (0, index_1.parseOpenApiDocument)({
89
+ openapi: "3.0.0",
90
+ paths: { "/a": { get: { parameters: [{ name: "sid", in: "cookie" }] } } }
91
+ }), /cookie parameter/);
92
+ });
93
+ (0, node_test_1.test)("openapi spec: path-level parameters merge into each operation", () => {
94
+ const parsed = (0, index_1.parseOpenApiDocument)({
95
+ openapi: "3.0.0",
96
+ paths: {
97
+ "/items/{itemId}": {
98
+ parameters: [{ name: "itemId", in: "path", required: true }],
99
+ get: { operationId: "getItem" }
100
+ }
101
+ }
102
+ });
103
+ const op = parsed.operations.find((o) => o.id === "getItem");
104
+ strict_1.default.equal(op.parameters.length, 1);
105
+ strict_1.default.equal(op.parameters[0].name, "itemId");
106
+ });
107
+ // ---------------------------------------------------------------------------
108
+ // Spec — request building
109
+ // ---------------------------------------------------------------------------
110
+ const GET_USER_OP = (0, index_1.parseOpenApiDocument)(SAMPLE_DOC).operations.find((o) => o.id === "getUser");
111
+ const LIST_USERS_OP = (0, index_1.parseOpenApiDocument)(SAMPLE_DOC).operations.find((o) => o.id === "listUsers");
112
+ const CREATE_USER_OP = (0, index_1.parseOpenApiDocument)(SAMPLE_DOC).operations.find((o) => o.id === "createUser");
113
+ const SEARCH_OP = (0, index_1.parseOpenApiDocument)(SAMPLE_DOC).operations.find((o) => o.id === "get_/search");
114
+ (0, node_test_1.test)("openapi spec: substitutes path params and rejects missing ones", () => {
115
+ const req = (0, index_1.buildHttpRequest)(GET_USER_OP, [{ id: "42" }], "https://api.example.com/v1", "getUser", "a1");
116
+ strict_1.default.equal(req.method, "GET");
117
+ strict_1.default.equal(req.url, "https://api.example.com/v1/users/42");
118
+ strict_1.default.throws(() => (0, index_1.buildHttpRequest)(GET_USER_OP, [{}], "https://api.example.com/v1", "getUser", "a1"), /missing required path parameter id/);
119
+ });
120
+ (0, node_test_1.test)("openapi spec: query params serialise in sorted order for digest stability", () => {
121
+ const req = (0, index_1.buildHttpRequest)(LIST_USERS_OP, [{ limit: 10, offset: 5 }], "https://x.com", "listUsers", "a1");
122
+ strict_1.default.match(req.url, /\?limit=10&offset=5$/);
123
+ });
124
+ (0, node_test_1.test)("openapi spec: remaining keys become the JSON body when a body is declared", () => {
125
+ const req = (0, index_1.buildHttpRequest)(CREATE_USER_OP, [{ name: "Ada", email: "a@x.com" }], "https://x.com", "createUser", "a1");
126
+ strict_1.default.equal(req.headers["content-type"], "application/json");
127
+ strict_1.default.deepEqual(JSON.parse(req.body), { name: "Ada", email: "a@x.com" });
128
+ });
129
+ (0, node_test_1.test)("openapi spec: leftover args without a body are a hard error (no silent drop)", () => {
130
+ strict_1.default.throws(() => (0, index_1.buildHttpRequest)(GET_USER_OP, [{ id: "1", surprise: true }], "https://x.com", "getUser", "a1"), /unknown argument\(s\): surprise/);
131
+ });
132
+ (0, node_test_1.test)("openapi spec: positional (non-object) args are rejected", () => {
133
+ strict_1.default.throws(() => (0, index_1.buildHttpRequest)(GET_USER_OP, ["42"], "https://x.com", "getUser", "a1"), /expects a single named-argument object/);
134
+ });
135
+ (0, node_test_1.test)("openapi spec: non-JSON bodies normalise to a string, JSON parses to a value", () => {
136
+ strict_1.default.deepEqual((0, index_1.normaliseHttpResponse)(200, '{"ok":true}'), { value: { ok: true }, degraded: false });
137
+ const raw = (0, index_1.normaliseHttpResponse)(200, "plain text");
138
+ strict_1.default.equal(raw.value, "plain text");
139
+ strict_1.default.equal(raw.degraded, true);
140
+ strict_1.default.deepEqual((0, index_1.normaliseHttpResponse)(204, ""), { value: null, degraded: false });
141
+ });
142
+ (0, node_test_1.test)("openapi spec: success predicate and body tail", () => {
143
+ strict_1.default.equal((0, index_1.isHttpSuccess)(200), true);
144
+ strict_1.default.equal((0, index_1.isHttpSuccess)(404), false);
145
+ strict_1.default.equal((0, index_1.bodyTail)("x".repeat(5000)).endsWith("… (5000 bytes total)"), true);
146
+ });
147
+ // ---------------------------------------------------------------------------
148
+ // Transport — real fetch path with a stubbed client
149
+ // ---------------------------------------------------------------------------
150
+ function fakeFetch(status, body) {
151
+ return async (_url, _init) => ({
152
+ status,
153
+ text: async () => body
154
+ });
155
+ }
156
+ (0, node_test_1.test)("openapi transport: InMemoryHttpTransport round-trips a request", async () => {
157
+ let seen = null;
158
+ const t = new index_1.InMemoryHttpTransport((req) => {
159
+ seen = req;
160
+ return { status: 200, body: JSON.stringify({ echoed: req.url }) };
161
+ });
162
+ const res = await t.send({ method: "GET", url: "https://x.com/a", headers: {} }, 1000);
163
+ strict_1.default.equal(res.status, 200);
164
+ strict_1.default.deepEqual(JSON.parse(res.body), { echoed: "https://x.com/a" });
165
+ strict_1.default.ok(seen !== null);
166
+ await t.close();
167
+ await strict_1.default.rejects(() => t.send({ method: "GET", url: "x", headers: {} }, 1));
168
+ });
169
+ (0, node_test_1.test)("openapi transport: FetchHttpTransport maps status and body through the real path", async () => {
170
+ const t = new index_1.FetchHttpTransport({
171
+ baseUrl: "https://api.example.com",
172
+ fetchImpl: fakeFetch(201, JSON.stringify({ id: 7 }))
173
+ });
174
+ const res = await t.send({ method: "POST", url: "/users", headers: {}, body: "{}" }, 1000);
175
+ strict_1.default.equal(res.status, 201);
176
+ strict_1.default.deepEqual(JSON.parse(res.body), { id: 7 });
177
+ await t.close();
178
+ });
179
+ // ---------------------------------------------------------------------------
180
+ // Adapter + host — registration, governance, recording, release
181
+ // ---------------------------------------------------------------------------
182
+ function makeAdapter(handler, opts = {}) {
183
+ return new index_1.OpenApiPaeAdapter({
184
+ adapterId: opts.id ?? "openapi-petstore",
185
+ document: SAMPLE_DOC,
186
+ transport: new index_1.InMemoryHttpTransport((req) => {
187
+ if (handler)
188
+ return handler(req);
189
+ return { status: 200, body: JSON.stringify({ url: req.url }) };
190
+ }),
191
+ toolNamePrefix: opts.toolNamePrefix
192
+ });
193
+ }
194
+ (0, node_test_1.test)("openapi adapter: rejects missing identity, transport or document", () => {
195
+ strict_1.default.throws(() => new index_1.OpenApiPaeAdapter({ adapterId: "", document: SAMPLE_DOC, transport: new index_1.InMemoryHttpTransport(() => ({ status: 200, body: "" })) }), /requires an adapterId/);
196
+ strict_1.default.throws(() => new index_1.OpenApiPaeAdapter({ adapterId: "x", document: SAMPLE_DOC }), /requires a transport/);
197
+ strict_1.default.throws(() => new index_1.OpenApiPaeAdapter({ adapterId: "x", transport: new index_1.InMemoryHttpTransport(() => ({ status: 200, body: "" })), document: null }), /requires an OpenAPI document/);
198
+ strict_1.default.throws(() => new index_1.OpenApiPaeAdapter({
199
+ adapterId: "x",
200
+ transport: new index_1.InMemoryHttpTransport(() => ({ status: 200, body: "" })),
201
+ document: { openapi: "3.0.0", paths: { "/a": { get: { operationId: "a" } } } }
202
+ }), /needs a baseUrl/);
203
+ });
204
+ (0, node_test_1.test)("openapi adapter: meta reports openapi kind, L2, semver edition, reduced default fidelity", () => {
205
+ const a = makeAdapter();
206
+ strict_1.default.equal(a.meta.kind, "openapi");
207
+ strict_1.default.equal(a.meta.isolation, "L2");
208
+ strict_1.default.equal(a.meta.sourceEdition, "1.4.2");
209
+ const surface = a.describe();
210
+ strict_1.default.equal(surface.length, 4);
211
+ const getUser = surface.find((s) => s.name === "getUser");
212
+ strict_1.default.equal(getUser.fidelity, "reduced");
213
+ strict_1.default.equal(getUser.determinism, index_1.DeterminismLevel.IO_BOUND);
214
+ strict_1.default.equal(getUser.fidelityNote, index_1.OPENAPI_DEFAULT_FIDELITY_NOTE);
215
+ });
216
+ (0, node_test_1.test)("openapi adapter: unknown tool is PaeToolMissingError, HTTP error is PaeRemoteError", async () => {
217
+ const a = makeAdapter();
218
+ await a.setup(CTX);
219
+ await strict_1.default.rejects(() => a.invoke("nope", [], CTX), (e) => e instanceof index_1.PaeToolMissingError);
220
+ const failing = new index_1.OpenApiPaeAdapter({
221
+ adapterId: "openapi-fail",
222
+ document: SAMPLE_DOC,
223
+ baseUrl: "https://x.com",
224
+ transport: new index_1.InMemoryHttpTransport(() => ({ status: 404, body: '{"error":"not found"}' }))
225
+ });
226
+ await failing.setup(CTX);
227
+ await strict_1.default.rejects(() => failing.invoke("getUser", [{ id: "1" }], CTX), (e) => {
228
+ if (!(e instanceof index_1.PaeRemoteError))
229
+ return false;
230
+ return /HTTP 404/.test(e.message);
231
+ });
232
+ });
233
+ (0, node_test_1.test)("openapi adapter: teardown releases the transport", async () => {
234
+ const transport = new index_1.InMemoryHttpTransport(() => ({ status: 200, body: "" }));
235
+ const a = new index_1.OpenApiPaeAdapter({
236
+ adapterId: "openapi-close",
237
+ document: SAMPLE_DOC,
238
+ transport
239
+ });
240
+ await a.setup(CTX);
241
+ strict_1.default.equal(a.isConnected, true);
242
+ await a.teardown();
243
+ strict_1.default.equal(a.isConnected, false);
244
+ strict_1.default.equal(transport.closed, true);
245
+ });
246
+ (0, node_test_1.test)("openapi adapter: two adapters need a toolNamePrefix to avoid collision", async () => {
247
+ const r1 = new index_1.PaeAdapterRegistry();
248
+ const a1 = makeAdapter();
249
+ await a1.setup(CTX);
250
+ r1.register(a1);
251
+ const a2 = makeAdapter(undefined, { toolNamePrefix: "v2_", id: "openapi-petstore-2" });
252
+ await a2.setup(CTX);
253
+ r1.register(a2);
254
+ strict_1.default.equal(r1.listAdapters().length, 2);
255
+ });
256
+ (0, node_test_1.test)("openapi adapter: host routes a call through the gateway like a native tool", async () => {
257
+ const adapter = makeAdapter();
258
+ const host = new index_1.OrbitRuntimeHost();
259
+ await host.connectPaeToolAdapter(adapter);
260
+ const out = await host.capabilityInvoke({
261
+ kind: index_1.ChannelKind.PAE_TOOL,
262
+ pluginId: "openapi-petstore",
263
+ funcName: "getUser",
264
+ args: [{ id: "9" }],
265
+ mode: "record"
266
+ });
267
+ strict_1.default.deepEqual(out, { url: "https://api.example.com/v1/users/9" });
268
+ await host.unregisterPaeToolAdapter("openapi-petstore");
269
+ });
270
+ //# sourceMappingURL=openapi_adapter.test.js.map