@mininglamp-oss/octo-runtime-client 0.0.0-stage → 1.1.11

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 (87) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +38 -2
  3. package/build-info.json +5 -0
  4. package/dist/async-question-reply.d.ts +30 -0
  5. package/dist/async-question-reply.js +199 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.js +1 -0
  8. package/dist/client.d.ts +10 -0
  9. package/dist/client.js +465 -0
  10. package/dist/index.d.ts +6 -0
  11. package/dist/index.js +6 -0
  12. package/dist/internal.d.ts +4 -0
  13. package/dist/internal.js +5 -0
  14. package/dist/invocation-lifecycle.d.ts +59 -0
  15. package/dist/invocation-lifecycle.js +287 -0
  16. package/dist/notification.d.ts +1 -0
  17. package/dist/notification.js +1 -0
  18. package/dist/protocol.d.ts +1 -0
  19. package/dist/protocol.js +1 -0
  20. package/dist/runtime-path.d.ts +21 -0
  21. package/dist/runtime-path.js +138 -0
  22. package/dist/startup-catalog.d.ts +8 -0
  23. package/dist/startup-catalog.js +65 -0
  24. package/dist/startup-diagnostics.d.ts +82 -0
  25. package/dist/startup-diagnostics.js +371 -0
  26. package/dist/submission-error.d.ts +10 -0
  27. package/dist/submission-error.js +23 -0
  28. package/dist/types.d.ts +72 -0
  29. package/dist/types.js +10 -0
  30. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/actmode.d.ts +4 -0
  31. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/actmode.js +36 -0
  32. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/adapter-metadata.d.ts +7 -0
  33. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/adapter-metadata.js +1 -0
  34. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/browser.d.ts +7 -0
  35. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/browser.js +6 -0
  36. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-catalog.d.ts +11 -0
  37. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-catalog.js +33 -0
  38. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-configuration.d.ts +5 -0
  39. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-configuration.js +58 -0
  40. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-hash.d.ts +2 -0
  41. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-hash.js +5 -0
  42. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context.d.ts +29 -0
  43. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context.js +566 -0
  44. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/execution-plan.d.ts +3 -0
  45. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/execution-plan.js +80 -0
  46. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/experience.d.ts +4 -0
  47. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/experience.js +166 -0
  48. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/failure.d.ts +82 -0
  49. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/failure.js +562 -0
  50. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/generated/protocol-types.d.ts +3022 -0
  51. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/generated/protocol-types.js +6 -0
  52. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/index.d.ts +124 -0
  53. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/index.js +709 -0
  54. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-input.d.ts +129 -0
  55. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-input.js +177 -0
  56. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-size.d.ts +3 -0
  57. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-size.js +11 -0
  58. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/json-size.d.ts +2 -0
  59. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/json-size.js +91 -0
  60. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/model-context.d.ts +3 -0
  61. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/model-context.js +9 -0
  62. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/multimodal.d.ts +15 -0
  63. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/multimodal.js +166 -0
  64. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/notification.d.ts +2 -0
  65. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/notification.js +137 -0
  66. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/questions.d.ts +9 -0
  67. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/questions.js +84 -0
  68. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/run-protocol.d.ts +20 -0
  69. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/run-protocol.js +294 -0
  70. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/runtime-selection.d.ts +3 -0
  71. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/runtime-selection.js +68 -0
  72. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/service-processes.d.ts +5 -0
  73. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/service-processes.js +65 -0
  74. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/stream-limits.d.ts +27 -0
  75. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/stream-limits.js +56 -0
  76. package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +15 -0
  77. package/package.json +55 -4
  78. package/schema/context-configuration-fixtures.json +1219 -0
  79. package/schema/context-fixtures.json +5988 -0
  80. package/schema/kernel-client.behavior.json +56 -0
  81. package/schema/kernel-ipc-fixtures.json +53 -0
  82. package/schema/kernel-ipc.schema.json +538 -0
  83. package/schema/native-invoke-fixtures.json +116 -0
  84. package/schema/protocol.schema.json +4525 -0
  85. package/schema/run-protocol-fixtures.json +761 -0
  86. package/schema/runtime-selection-fixtures.json +663 -0
  87. package/schema/session-name-fixtures.json +191 -0
package/dist/client.js ADDED
@@ -0,0 +1,465 @@
1
+ import net from "node:net";
2
+ import { assertIpcEndpoint } from "./runtime-path.js";
3
+ import { randomUUID } from "node:crypto";
4
+ import { assertHostFrame, assertHostFrameIdentity, assertHostRunReceipt, assertKernelStatus, createInputInvoke, identifyInvokeInput, isHostTerminal, HOST_RPC_MAX_LINE_BYTES, INVOKE_REQUEST_MAX_BYTES } from "@mininglamp-oss/octo-runtime-protocol";
5
+ import { KernelError } from "./types.js";
6
+ import { InvocationRegistry } from "./invocation-lifecycle.js";
7
+ import { markSubmissionRejected } from "./submission-error.js";
8
+ /**
9
+ * 连接本机 socket/Windows Pipe,返回不拥有 Kernel 进程的客户端。
10
+ * 独立应用显式连接已有服务;原生 SDK 服务宿主使用 kernel.client。
11
+ * Kernel/Extra 服务内部也复用本函数构造 RPC 客户端,不要求 SDK 调用方额外 attach。
12
+ * invoke 提供登记、取消和尾流观察;关闭连接或迭代器不会隐式取消已接纳 Run。
13
+ */
14
+ export function attachKernel(options) {
15
+ const endpoint = options.endpoint;
16
+ assertIpcEndpoint(endpoint);
17
+ const sockets = new Set();
18
+ let closed = false;
19
+ const open = async (signal, timeout = 5000) => {
20
+ if (closed)
21
+ throw new KernelError("CLIENT_CLOSED");
22
+ if (sockets.size >= 32)
23
+ throw new KernelError("CLIENT_CAPACITY_EXHAUSTED");
24
+ signal?.throwIfAborted();
25
+ const socket = net.createConnection(endpoint);
26
+ sockets.add(socket);
27
+ socket.once("close", () => sockets.delete(socket));
28
+ socket.on("error", () => { });
29
+ const abort = () => socket.destroy(new KernelError("SUBSCRIPTION_ABORTED"));
30
+ signal?.addEventListener("abort", abort, { once: true });
31
+ socket.once("close", () => signal?.removeEventListener("abort", abort));
32
+ await new Promise((resolve, reject) => {
33
+ const finish = (error) => {
34
+ clearTimeout(timer);
35
+ socket.off("connect", connected);
36
+ socket.off("error", failed);
37
+ socket.off("close", disconnected);
38
+ if (error)
39
+ reject(error);
40
+ else
41
+ resolve();
42
+ };
43
+ const connected = () => finish();
44
+ const failed = (error) => finish(error);
45
+ const disconnected = () => finish(new KernelError(closed ? "CLIENT_CLOSED" : "CONNECTION_CLOSED"));
46
+ const timer = setTimeout(() => {
47
+ socket.destroy();
48
+ finish(new KernelError("KERNEL_UNAVAILABLE"));
49
+ }, Math.min(5000, timeout));
50
+ socket.once("connect", connected);
51
+ socket.once("error", failed);
52
+ socket.once("close", disconnected);
53
+ }).catch(() => {
54
+ socket.destroy();
55
+ throw new KernelError(closed ? "CLIENT_CLOSED" : "KERNEL_UNAVAILABLE");
56
+ });
57
+ return socket;
58
+ };
59
+ const exchange = async (method, params = {}, timeout = 5000, signal, beforeSend, sent) => {
60
+ const until = Date.now() + timeout;
61
+ const socket = await open(signal, timeout);
62
+ const timer = setTimeout(() => socket.destroy(new KernelError("KERNEL_TIMEOUT")), Math.max(1, until - Date.now()));
63
+ const id = randomUUID();
64
+ try {
65
+ signal?.throwIfAborted();
66
+ beforeSend?.();
67
+ send(socket, id, method, params);
68
+ sent?.();
69
+ for await (const value of lines(socket))
70
+ return response(value, id);
71
+ throw new KernelError(closed ? "CLIENT_CLOSED" : "CONNECTION_CLOSED");
72
+ }
73
+ catch (error) {
74
+ if (closed)
75
+ throw new KernelError("CLIENT_CLOSED");
76
+ throw error;
77
+ }
78
+ finally {
79
+ clearTimeout(timer);
80
+ socket.destroy();
81
+ }
82
+ };
83
+ const query = async (key, expectedInstance, deadline = Date.now() + 5000, signal) => {
84
+ const info = (await exchange("agent_run.get", {
85
+ key: lookupForKey(key),
86
+ kernel_instance_id: expectedInstance
87
+ }, remaining(deadline), signal));
88
+ assertHostRunReceipt(receiptFor(info.ref));
89
+ if (expectedInstance !== undefined && info.ref.kernel_instance_id !== expectedInstance)
90
+ throw new KernelError("KERNEL_INSTANCE_CHANGED");
91
+ if (!sameLookup(info.ref, key))
92
+ throw new KernelError("RUN_IDENTITY_MISMATCH");
93
+ if (info.terminal) {
94
+ assertHostFrame(info.terminal);
95
+ assertHostFrameIdentity(info.terminal, receiptFor(info.ref));
96
+ if (!isHostTerminal(info.terminal))
97
+ throw new KernelError("HOST_FRAME_INVALID");
98
+ }
99
+ registry.reconcile(info.ref, info.terminal);
100
+ return info;
101
+ };
102
+ const cancel = async (ref, reason, deadline = Date.now() + 5000, signal) => {
103
+ try {
104
+ const result = (await exchange("agent_run.cancel", { ref, reason }, remaining(deadline), signal));
105
+ if (typeof result?.accepted !== "boolean")
106
+ throw new KernelError("OPERATION_OUTCOME_UNKNOWN");
107
+ return result;
108
+ }
109
+ catch (error) {
110
+ if (error instanceof KernelError &&
111
+ !["CONNECTION_CLOSED", "KERNEL_TIMEOUT", "SUBSCRIPTION_ABORTED"].includes(error.code))
112
+ throw error;
113
+ throw new KernelError("OPERATION_OUTCOME_UNKNOWN");
114
+ }
115
+ };
116
+ const registry = new InvocationRegistry({ get: query, cancel });
117
+ const control = async (method, params) => {
118
+ try {
119
+ await exchange(method, params);
120
+ }
121
+ catch (error) {
122
+ if (error instanceof KernelError &&
123
+ !["CONNECTION_CLOSED", "KERNEL_TIMEOUT"].includes(error.code))
124
+ throw error;
125
+ throw new KernelError("OPERATION_OUTCOME_UNKNOWN");
126
+ }
127
+ };
128
+ const client = {
129
+ topology: "kernel-ipc",
130
+ ownership: "attached",
131
+ invocations: registry,
132
+ invoke: createInputInvoke(async function* (input, environment = {}) {
133
+ if (closed)
134
+ throw new KernelError("CLIENT_CLOSED");
135
+ environment.signal?.throwIfAborted();
136
+ const request = identifyInvokeInput(input);
137
+ const record = registry.begin(lookupFor(request));
138
+ // signal 属于执行控制,不传给 subscribe:取消后仍须读到 Provider 会话更新与真实终态。
139
+ const abort = () => {
140
+ void registry.cancel(record, "caller_aborted").catch(() => undefined);
141
+ };
142
+ environment.signal?.addEventListener("abort", abort, { once: true });
143
+ try {
144
+ let ref;
145
+ try {
146
+ ref = (await submit(request, environment, record)).ref;
147
+ registry.accepted(record, ref);
148
+ }
149
+ catch (error) {
150
+ registry.rejected(record, error);
151
+ throw error;
152
+ }
153
+ // 终态会释放登记,但 yield 后的业务持久化/投递仍由消费者负责,owner 不能据此提前 close。
154
+ let terminal = false;
155
+ for await (const frame of client.subscribe(ref)) {
156
+ terminal = isHostTerminal(frame);
157
+ if (request.protocol_mode === "canonical" &&
158
+ frame.kind === "control" &&
159
+ frame.control.type === "error") {
160
+ const snapshot = await query(ref, ref.kernel_instance_id).catch(() => undefined);
161
+ if (snapshot?.terminal?.kind === "control" &&
162
+ snapshot.terminal.control.type === "state" &&
163
+ snapshot.terminal.control.state === "lost")
164
+ throw new KernelError("RUN_OUTCOME_UNKNOWN");
165
+ }
166
+ yield frame;
167
+ if (terminal &&
168
+ frame.kind === "control" &&
169
+ frame.control.type === "state" &&
170
+ frame.control.state === "lost")
171
+ throw new KernelError("RUN_OUTCOME_UNKNOWN");
172
+ }
173
+ if (!terminal)
174
+ throw new KernelError("HOST_ENDED_WITHOUT_TERMINAL");
175
+ // 调用观察绑定首次控制结果;后续显式 Stop 成功不能抹去原来的 ACK 未知或失败。
176
+ if (record.firstCancelTask)
177
+ await record.firstCancelTask;
178
+ }
179
+ catch (error) {
180
+ registry.interrupted(record, error);
181
+ throw error;
182
+ }
183
+ finally {
184
+ environment.signal?.removeEventListener("abort", abort);
185
+ registry.interrupted(record);
186
+ }
187
+ }),
188
+ cancelInvocation: async (id, reason) => registry.cancel(registry.active(id), reason, { explicit: true }),
189
+ respondInteraction: async (id, response) => client.respond(registry.ref(id), response),
190
+ respondNativeInteraction: async (id, response) => client.respondNative(registry.ref(id), response),
191
+ status: async () => {
192
+ const value = (await exchange("status"));
193
+ if (!value || typeof value !== "object" || typeof value.ipc_version !== "number")
194
+ throw new KernelError("INVALID_RESPONSE");
195
+ // 包版本不参与 wire 兼容判断;submit 复用此既有查询,拒绝后不会发起执行。
196
+ if (value.ipc_version !== 1)
197
+ throw new KernelError("KERNEL_IPC_VERSION_UNSUPPORTED");
198
+ try {
199
+ // 与服务端/doctor 共用快照白名单;仍保持客户端的受控错误与版本拒绝顺序。
200
+ assertKernelStatus(value);
201
+ }
202
+ catch {
203
+ throw new KernelError("INVALID_RESPONSE");
204
+ }
205
+ return value;
206
+ },
207
+ list: async () => (await exchange("adapter.list")),
208
+ get: query,
209
+ submit: (input, environment) => submit(input, environment),
210
+ async *subscribe(ref, afterSeq = 0, signal) {
211
+ // 断线仅续读同一实例、同一流的连续序号;不能把恢复观察变成再次执行 Provider。
212
+ let after = afterSeq;
213
+ let repair = 0;
214
+ let repairUntil;
215
+ while (true) {
216
+ signal?.throwIfAborted();
217
+ if (closed)
218
+ throw new KernelError("CLIENT_CLOSED");
219
+ let socket;
220
+ let timer;
221
+ try {
222
+ if (repairUntil !== undefined && Date.now() >= repairUntil)
223
+ throw new KernelError("KERNEL_UNAVAILABLE");
224
+ socket = await open(signal, repairUntil === undefined ? 5000 : Math.max(1, repairUntil - Date.now()));
225
+ const current = socket;
226
+ timer = setTimeout(() => current.destroy(new KernelError("KERNEL_TIMEOUT")), repairUntil === undefined ? 5000 : Math.max(1, Math.min(5000, repairUntil - Date.now())));
227
+ const id = randomUUID();
228
+ send(socket, id, "agent_run.subscribe", { ref, after_seq: after });
229
+ let acknowledged = false;
230
+ let terminalAt;
231
+ for await (const value of lines(socket)) {
232
+ if (!acknowledged) {
233
+ const info = response(value, id);
234
+ if (info.ref.kernel_instance_id !== ref.kernel_instance_id)
235
+ throw new KernelError("KERNEL_INSTANCE_CHANGED");
236
+ assertHostRunReceipt(receiptFor(info.ref));
237
+ if (!sameLookup(info.ref, ref) ||
238
+ info.ref.stream_id !== ref.stream_id ||
239
+ info.ref.protocol !== ref.protocol)
240
+ throw new KernelError("RUN_IDENTITY_MISMATCH");
241
+ acknowledged = true;
242
+ clearTimeout(timer);
243
+ if (info.terminal) {
244
+ assertHostFrame(info.terminal);
245
+ assertHostFrameIdentity(info.terminal, receiptFor(ref));
246
+ if (!isHostTerminal(info.terminal))
247
+ throw new KernelError("HOST_FRAME_INVALID");
248
+ registry.reconcile(info.ref, info.terminal);
249
+ terminalAt = info.latest_seq;
250
+ }
251
+ if (terminalAt !== undefined && after >= terminalAt)
252
+ return;
253
+ continue;
254
+ }
255
+ const notification = value;
256
+ if (notification.method === "agent_run.stream_error")
257
+ throw new KernelError(notification.params?.code ?? "STREAM_FAILED");
258
+ if (notification.method !== "agent_run.event" ||
259
+ notification.params?.kernel_instance_id !== ref.kernel_instance_id)
260
+ throw new KernelError("HOST_FRAME_INVALID");
261
+ const frame = notification.params.event;
262
+ assertHostFrame(frame);
263
+ assertHostFrameIdentity(frame, receiptFor(ref));
264
+ if (frame.seq !== after + 1 || notification.params.seq !== frame.seq)
265
+ throw new KernelError("STREAM_GAP");
266
+ after = frame.seq;
267
+ registry.observe(ref, frame);
268
+ yield frame;
269
+ if (isHostTerminal(frame))
270
+ return;
271
+ }
272
+ throw new KernelError("CONNECTION_CLOSED");
273
+ }
274
+ catch (error) {
275
+ if (closed)
276
+ throw new KernelError("CLIENT_CLOSED");
277
+ signal?.throwIfAborted();
278
+ if (!(error instanceof KernelError) &&
279
+ error instanceof Error &&
280
+ "code" in error &&
281
+ typeof error.code === "string")
282
+ throw new KernelError(error.code);
283
+ if (error instanceof KernelError &&
284
+ !["CONNECTION_CLOSED", "KERNEL_UNAVAILABLE", "KERNEL_TIMEOUT"].includes(error.code))
285
+ throw error;
286
+ if (repair >= 3)
287
+ throw new KernelError("KERNEL_UNAVAILABLE");
288
+ repairUntil ??= Date.now() + 10_000;
289
+ if (Date.now() >= repairUntil)
290
+ throw new KernelError("KERNEL_UNAVAILABLE");
291
+ await new Promise((resolve) => setTimeout(resolve, [100, 250, 500][repair++]));
292
+ }
293
+ finally {
294
+ clearTimeout(timer);
295
+ socket?.destroy();
296
+ }
297
+ }
298
+ },
299
+ cancel,
300
+ respond: (ref, response) => control("agent_run.respond", { ref, response }),
301
+ respondNative: (ref, response) => control("agent_run.native.respond", { ref, response }),
302
+ close() {
303
+ closed = true;
304
+ registry.close();
305
+ for (const socket of sockets)
306
+ socket.destroy();
307
+ }
308
+ };
309
+ async function submit(input, environment = {}, record) {
310
+ if (closed)
311
+ throw new KernelError("CLIENT_CLOSED");
312
+ environment.signal?.throwIfAborted();
313
+ if (environment.streamLimits !== undefined)
314
+ throw new KernelError("KERNEL_STREAM_LIMITS_UNSUPPORTED");
315
+ const request = identifyInvokeInput(input);
316
+ const requestLine = JSON.stringify({
317
+ jsonrpc: "2.0",
318
+ id: "0".repeat(36),
319
+ method: "agent_run.invoke",
320
+ params: { input: request, executionEnvOverrides: environment.executionEnvOverrides }
321
+ });
322
+ if (Buffer.byteLength(requestLine) + 1 > INVOKE_REQUEST_MAX_BYTES)
323
+ throw new KernelError("INVOKE_REQUEST_TOO_LARGE");
324
+ const instance = (await client.status()).kernel_instance_id;
325
+ environment.signal?.throwIfAborted();
326
+ if (record)
327
+ record.instance = instance;
328
+ let ref;
329
+ let submitted = false;
330
+ try {
331
+ const result = (await exchange("agent_run.invoke", {
332
+ input: request,
333
+ executionEnvOverrides: environment.executionEnvOverrides,
334
+ kernel_instance_id: instance
335
+ }, 30_000,
336
+ // 请求发出后中断等待会让接纳结果不明;先取得/查询 ACK,再按原身份请求取消。
337
+ undefined, () => {
338
+ environment.signal?.throwIfAborted();
339
+ if (record)
340
+ registry.assertSubmission();
341
+ }, () => {
342
+ submitted = true;
343
+ if (record)
344
+ record.submitted = true;
345
+ }));
346
+ assertHostRunReceipt(result.receipt);
347
+ if (result.kernel_instance_id !== instance)
348
+ throw new KernelError("KERNEL_INSTANCE_CHANGED");
349
+ ref = { ...result.receipt, kernel_instance_id: result.kernel_instance_id };
350
+ if (!sameLookup(ref, lookupFor(request)))
351
+ throw new KernelError("RUN_IDENTITY_MISMATCH");
352
+ }
353
+ catch (error) {
354
+ if (!submitted)
355
+ throw error;
356
+ markSubmissionRejected(error, instance);
357
+ if (error instanceof KernelError &&
358
+ !["KERNEL_TIMEOUT", "CONNECTION_CLOSED"].includes(error.code))
359
+ throw error;
360
+ // ACK 丢失后只按原身份查询,不重新提交,避免创建第二次执行。
361
+ const key = lookupFor(request);
362
+ let recovered;
363
+ const until = Date.now() + 10_000;
364
+ for (const delay of [100, 250, 500]) {
365
+ await new Promise((resolve) => setTimeout(resolve, delay));
366
+ if (Date.now() >= until)
367
+ break;
368
+ try {
369
+ recovered = (await query(key, instance, Math.min(until, Date.now() + 5000))).ref;
370
+ break;
371
+ }
372
+ catch (queryError) {
373
+ if (queryError instanceof KernelError &&
374
+ ["KERNEL_INSTANCE_CHANGED", "RUN_IDENTITY_MISMATCH"].includes(queryError.code))
375
+ throw queryError;
376
+ }
377
+ }
378
+ if (!recovered)
379
+ throw new KernelError("ACCEPTANCE_UNKNOWN");
380
+ ref = recovered;
381
+ }
382
+ if (!record && environment.signal?.aborted)
383
+ await client.cancel(ref, "caller_aborted");
384
+ return { ref };
385
+ }
386
+ return client;
387
+ }
388
+ function remaining(deadline) {
389
+ const timeout = deadline - Date.now();
390
+ if (timeout <= 0)
391
+ throw new KernelError("KERNEL_TIMEOUT");
392
+ return timeout;
393
+ }
394
+ function receiptFor(ref) {
395
+ const { kernel_instance_id: _instance, ...receipt } = ref;
396
+ void _instance;
397
+ return receipt;
398
+ }
399
+ function lookupForKey(key) {
400
+ return {
401
+ run_id: key.run_id,
402
+ invocation_id: key.invocation_id,
403
+ agent_session_id: key.agent_session_id,
404
+ session_generation: key.session_generation,
405
+ protocol_mode: key.protocol_mode
406
+ };
407
+ }
408
+ function sameLookup(left, right) {
409
+ return (left.run_id === right.run_id &&
410
+ left.invocation_id === right.invocation_id &&
411
+ left.agent_session_id === right.agent_session_id &&
412
+ left.session_generation === right.session_generation &&
413
+ left.protocol_mode === right.protocol_mode);
414
+ }
415
+ function lookupFor(input) {
416
+ return {
417
+ run_id: input.runId,
418
+ invocation_id: input.invocationId,
419
+ agent_session_id: input.sessionId,
420
+ session_generation: input.sessionGeneration ?? 0,
421
+ protocol_mode: input.protocol_mode ?? "native"
422
+ };
423
+ }
424
+ function send(socket, id, method, params) {
425
+ const line = `${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`;
426
+ if (Buffer.byteLength(line) > INVOKE_REQUEST_MAX_BYTES)
427
+ throw new KernelError("INVOKE_REQUEST_TOO_LARGE");
428
+ socket.write(line);
429
+ }
430
+ function response(value, id) {
431
+ const reply = value;
432
+ if (reply.id !== id)
433
+ throw new KernelError("INVALID_RESPONSE");
434
+ if (reply.error)
435
+ throw new KernelError(typeof reply.error.data?.code === "string" ? reply.error.data.code : "KERNEL_REQUEST_FAILED", reply.error.data);
436
+ return reply.result;
437
+ }
438
+ async function* lines(socket) {
439
+ let pending = Buffer.alloc(0);
440
+ try {
441
+ for await (const chunk of socket) {
442
+ const bytes = chunk;
443
+ pending = pending.length ? Buffer.concat([pending, bytes]) : bytes;
444
+ let offset;
445
+ while ((offset = pending.indexOf(10)) >= 0) {
446
+ if (offset > HOST_RPC_MAX_LINE_BYTES)
447
+ throw new KernelError("HOST_FRAME_TOO_LARGE");
448
+ const line = pending.subarray(0, offset);
449
+ pending = pending.subarray(offset + 1);
450
+ yield JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(line));
451
+ }
452
+ if (pending.length > HOST_RPC_MAX_LINE_BYTES)
453
+ throw new KernelError("HOST_FRAME_TOO_LARGE");
454
+ }
455
+ if (pending.length)
456
+ throw new KernelError("CONNECTION_CLOSED");
457
+ }
458
+ catch (error) {
459
+ if (error instanceof KernelError)
460
+ throw error;
461
+ if (error instanceof Error && /^[A-Z][A-Z0-9_]+$/.test(error.message))
462
+ throw new KernelError(error.message);
463
+ throw new KernelError("CONNECTION_CLOSED");
464
+ }
465
+ }
@@ -0,0 +1,6 @@
1
+ export { attachKernel } from "./client.js";
2
+ export * from "./types.js";
3
+ export { AsyncQuestionReply, type AsyncQuestionReplyResult } from "./async-question-reply.js";
4
+ export { isInvocationNotSubmitted } from "./submission-error.js";
5
+ export { RuntimeHomeError, assertIpcEndpoint } from "./runtime-path.js";
6
+ export type { InvokeInput, InvokeEnvironment, HostFrame, CanonicalEvent, InteractionResponse, NativeInteractionResponse, HostedAdapterMetadata } from "@mininglamp-oss/octo-runtime-protocol";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // 只连接宿主提供的服务;不拥有 Kernel、Adapter、Home 或业务状态。
2
+ export { attachKernel } from "./client.js";
3
+ export * from "./types.js";
4
+ export { AsyncQuestionReply } from "./async-question-reply.js";
5
+ export { isInvocationNotSubmitted } from "./submission-error.js";
6
+ export { RuntimeHomeError, assertIpcEndpoint } from "./runtime-path.js";
@@ -0,0 +1,4 @@
1
+ export * from "./runtime-path.js";
2
+ export * from "./invocation-lifecycle.js";
3
+ export * from "./submission-error.js";
4
+ export * from "./startup-diagnostics.js";
@@ -0,0 +1,5 @@
1
+ // 仅供同版本完整 Runtime 装配;普通应用使用根入口与 /protocol。
2
+ export * from "./runtime-path.js";
3
+ export * from "./invocation-lifecycle.js";
4
+ export * from "./submission-error.js";
5
+ export * from "./startup-diagnostics.js";
@@ -0,0 +1,59 @@
1
+ import { type HostFrame } from "@mininglamp-oss/octo-runtime-protocol";
2
+ import { type KernelInvocationRecord, type KernelInvocationLifecycle, type KernelInvocationDrainResult, type LookupKey, type RunRef, type RunSnapshot } from "./types.js";
3
+ export interface TrackedInvocation {
4
+ key: LookupKey;
5
+ ref?: RunRef;
6
+ instance?: string;
7
+ submitted: boolean;
8
+ phase: KernelInvocationRecord["phase"];
9
+ cancellation: KernelInvocationRecord["cancellation"];
10
+ lastSeq: number;
11
+ observationError?: string;
12
+ reason?: string;
13
+ cancelTask?: Promise<{
14
+ accepted: boolean;
15
+ }>;
16
+ /** 原 invoke 观察首次控制结果;新的显式 Stop 不得覆盖已经发生的控制失败。 */
17
+ firstCancelTask?: Promise<{
18
+ accepted: boolean;
19
+ }>;
20
+ admitted: Promise<void>;
21
+ settleAdmission(): void;
22
+ admissionError?: Error;
23
+ }
24
+ /**
25
+ * 每个 client 独立保存 invoke 的身份、取消和观察状态;执行与断线恢复由 client.ts 负责。
26
+ * 最多登记 128 项,不缓存正文;未知结果继续占用额度,不能通过删除记录伪造排空成功。
27
+ */
28
+ export declare class InvocationRegistry implements KernelInvocationLifecycle {
29
+ #private;
30
+ private readonly transport;
31
+ constructor(transport: {
32
+ get(key: LookupKey, instance: string, deadline: number, signal: AbortSignal): Promise<RunSnapshot>;
33
+ cancel(ref: RunRef, reason: string, deadline: number, signal: AbortSignal): Promise<{
34
+ accepted: boolean;
35
+ }>;
36
+ });
37
+ begin(key: LookupKey): TrackedInvocation;
38
+ assertSubmission(): void;
39
+ accepted(record: TrackedInvocation, ref: RunRef, terminal?: HostFrame): void;
40
+ reconcile(ref: RunRef, terminal?: HostFrame): void;
41
+ rejected(record: TrackedInvocation, error: unknown): void;
42
+ observe(ref: RunRef, frame: HostFrame): void;
43
+ interrupted(record: TrackedInvocation, error?: unknown): void;
44
+ active(id: string): TrackedInvocation;
45
+ ref(id: string): RunRef;
46
+ cancel(record: TrackedInvocation, reason?: string, { explicit }?: {
47
+ explicit?: boolean;
48
+ }): Promise<{
49
+ accepted: boolean;
50
+ }>;
51
+ snapshot(): readonly KernelInvocationRecord[];
52
+ stopAdmission(): void;
53
+ close(): void;
54
+ drain(options: {
55
+ deadline: number;
56
+ reason: string;
57
+ }): Promise<KernelInvocationDrainResult>;
58
+ }
59
+ export declare function errorCode(error: unknown): string;