@mininglamp-oss/octo-runtime 1.1.0 → 1.1.2

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 (149) hide show
  1. package/README.md +4 -2
  2. package/apps/cli/README.md +3 -1
  3. package/apps/cli/dist/build-info.json +2 -2
  4. package/apps/cli/dist/extras.d.ts +4 -2
  5. package/apps/cli/dist/extras.js +20 -21
  6. package/apps/cli/dist/help.js +3 -0
  7. package/apps/cli/dist/logs.d.ts +10 -0
  8. package/apps/cli/dist/logs.js +186 -0
  9. package/apps/cli/dist/main.d.ts +1 -0
  10. package/apps/cli/dist/main.js +94 -19
  11. package/apps/cli/dist/{default_adapters.json → manifest.json} +10 -0
  12. package/apps/cli/dist/module-manifest.d.ts +3 -0
  13. package/apps/cli/dist/module-manifest.js +11 -0
  14. package/apps/cli/dist/openclaw.js +30 -6
  15. package/apps/cli/dist/output.js +3 -0
  16. package/apps/cli/dist/playboard.js +10 -8
  17. package/apps/cli/dist/status-output.d.ts +10 -0
  18. package/apps/cli/dist/status-output.js +65 -0
  19. package/apps/cli/dist/worker.d.ts +1 -0
  20. package/apps/cli/dist/worker.js +18 -0
  21. package/dist/public/host.d.ts +1 -0
  22. package/node_modules/@mininglamp-oss/octo-runtime-acp-client/package.json +3 -3
  23. package/node_modules/@mininglamp-oss/octo-runtime-adapter-acp/package.json +4 -4
  24. package/node_modules/@mininglamp-oss/octo-runtime-adapter-antigravity/package.json +3 -3
  25. package/node_modules/@mininglamp-oss/octo-runtime-adapter-claude-code/package.json +3 -3
  26. package/node_modules/@mininglamp-oss/octo-runtime-adapter-codebuddy/package.json +4 -4
  27. package/node_modules/@mininglamp-oss/octo-runtime-adapter-codex/package.json +3 -3
  28. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/adapter.d.ts +54 -0
  29. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/adapter.js +399 -0
  30. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/client.d.ts +14 -0
  31. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/client.js +156 -0
  32. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/index.d.ts +29 -0
  33. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/index.js +273 -0
  34. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/interactions.d.ts +27 -0
  35. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/interactions.js +161 -0
  36. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/package.json +17 -0
  37. package/node_modules/@mininglamp-oss/octo-runtime-adapter-hermes/package.json +2 -2
  38. package/node_modules/@mininglamp-oss/octo-runtime-adapter-kimi-code/package.json +2 -2
  39. package/node_modules/@mininglamp-oss/octo-runtime-adapter-mcode/package.json +2 -2
  40. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/compatibility.d.ts +24 -0
  41. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/compatibility.js +181 -0
  42. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/gateway.d.ts +9 -0
  43. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/gateway.js +24 -8
  44. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/index.js +229 -225
  45. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/package.json +3 -3
  46. package/node_modules/@mininglamp-oss/octo-runtime-adapter-opencode/package.json +4 -4
  47. package/node_modules/@mininglamp-oss/octo-runtime-adapter-qodercli/package.json +2 -2
  48. package/node_modules/@mininglamp-oss/octo-runtime-adapter-qoderclicn/package.json +2 -2
  49. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/approval.d.ts +4 -0
  50. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/approval.js +5 -6
  51. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.d.ts +1 -0
  52. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.js +1 -0
  53. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.d.ts +33 -0
  54. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.js +118 -0
  55. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log-worker.js +7 -5
  56. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.d.ts +3 -0
  57. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.js +45 -10
  58. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.d.ts +11 -0
  59. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.js +70 -0
  60. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/package.json +5 -3
  61. package/node_modules/@mininglamp-oss/octo-runtime-adapter-zcode-gui/package.json +3 -3
  62. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/build-info.json +2 -2
  63. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.d.ts +4 -2
  64. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.js +20 -21
  65. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/help.js +3 -0
  66. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.d.ts +10 -0
  67. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.js +186 -0
  68. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.d.ts +1 -0
  69. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.js +94 -19
  70. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/{default_adapters.json → manifest.json} +10 -0
  71. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.d.ts +3 -0
  72. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.js +11 -0
  73. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/openclaw.js +30 -6
  74. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/output.js +3 -0
  75. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/playboard.js +10 -8
  76. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.d.ts +10 -0
  77. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.js +65 -0
  78. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.d.ts +1 -0
  79. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.js +18 -0
  80. package/node_modules/@mininglamp-oss/octo-runtime-cli/package.json +23 -9
  81. package/node_modules/@mininglamp-oss/octo-runtime-cross-service/package.json +2 -2
  82. package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.d.ts +1 -1
  83. package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.js +13 -9
  84. package/node_modules/@mininglamp-oss/octo-runtime-executor-core/package.json +3 -3
  85. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.d.ts +16 -0
  86. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.js +59 -0
  87. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.d.ts +1 -0
  88. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.js +4 -1
  89. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.d.ts +6 -0
  90. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.js +88 -0
  91. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.d.ts +1 -2
  92. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.js +2 -9
  93. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.d.ts +7 -2
  94. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.js +138 -9
  95. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/runner.js +3 -92
  96. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/serve.js +36 -12
  97. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.d.ts +4 -1
  98. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.js +14 -6
  99. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/service.js +2 -1
  100. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.d.ts +2 -2
  101. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.js +57 -34
  102. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/package.json +8 -16
  103. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
  104. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/application/im-channel.js +18 -18
  105. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/contracts.d.ts +1 -0
  106. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/execution/dispatcher.js +24 -0
  107. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/contexts.js +2 -2
  108. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/mentions.d.ts +5 -2
  109. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/mentions.js +19 -5
  110. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/normalize.js +1 -0
  111. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/package.json +6 -6
  112. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
  113. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/package.json +5 -5
  114. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/config-schema.json +2 -2
  115. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/index.js +1 -1
  116. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.d.ts +4 -1
  117. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.js +54 -4
  118. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/package.json +2 -2
  119. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.d.ts +8 -1
  120. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.js +27 -12
  121. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-service.js +39 -2
  122. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/index.d.ts +1 -0
  123. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/package.json +4 -4
  124. package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +1 -1
  125. package/node_modules/import-meta-resolve/index.d.ts +19 -0
  126. package/node_modules/import-meta-resolve/index.d.ts.map +1 -0
  127. package/node_modules/import-meta-resolve/index.js +47 -0
  128. package/node_modules/import-meta-resolve/lib/errors.d.ts +24 -0
  129. package/node_modules/import-meta-resolve/lib/errors.d.ts.map +1 -0
  130. package/node_modules/import-meta-resolve/lib/errors.js +504 -0
  131. package/node_modules/import-meta-resolve/lib/get-format.d.ts +13 -0
  132. package/node_modules/import-meta-resolve/lib/get-format.d.ts.map +1 -0
  133. package/node_modules/import-meta-resolve/lib/get-format.js +159 -0
  134. package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts +31 -0
  135. package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts.map +1 -0
  136. package/node_modules/import-meta-resolve/lib/package-json-reader.js +177 -0
  137. package/node_modules/import-meta-resolve/lib/resolve.d.ts +29 -0
  138. package/node_modules/import-meta-resolve/lib/resolve.d.ts.map +1 -0
  139. package/node_modules/import-meta-resolve/lib/resolve.js +1301 -0
  140. package/node_modules/import-meta-resolve/lib/utils.d.ts +6 -0
  141. package/node_modules/import-meta-resolve/lib/utils.d.ts.map +1 -0
  142. package/node_modules/import-meta-resolve/lib/utils.js +47 -0
  143. package/node_modules/import-meta-resolve/license +74 -0
  144. package/node_modules/import-meta-resolve/package.json +78 -0
  145. package/node_modules/import-meta-resolve/readme.md +252 -0
  146. package/package.json +30 -26
  147. package/packages/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
  148. package/packages/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
  149. package/packages/octo-runtime-extra-webui/config-schema.json +2 -2
@@ -1,94 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // CLI 使用的独立 worker 入口:只加载显式 manifest 中的 Adapter,注册到唯一 AgentExecutor。
3
- // 通过 stdio NDJSON 为父 Kernel 提供执行;SDK 可用自有代码入口调用 serveWorker,毋须采用此 manifest。
4
- import { readFile } from "node:fs/promises";
5
- import { resolve } from "node:path";
6
- import { resolveAdapterModuleSpecifier } from "./module-specifier.js";
2
+ // 通用清单 worker 不包含产品准备逻辑;SDK 自有 worker 原生 import 后直接调用 serveWorker。
3
+ import { loadManifestAdapters } from "./manifest-adapters.js";
7
4
  import { serveWorker } from "./index.js";
8
- const index = process.argv.indexOf("--manifest");
9
- if (index < 0 || !process.argv[index + 1])
10
- throw new Error("usage: octo-runtime-executor-kernel --manifest <file>");
11
- const manifestPath = resolve(process.argv[index + 1]);
12
- const rawManifest = JSON.parse(await readFile(manifestPath, "utf8"));
13
- const manifest = parseManifest(rawManifest);
14
- const adapters = [];
15
- for (const entry of manifest.adapters) {
16
- const specifier = resolveAdapterModuleSpecifier(entry.module, manifestPath);
17
- const loaded = await import(specifier);
18
- const moduleExports = asRecord(loaded, `adapter module ${entry.module}`);
19
- const exported = moduleExports[entry.exportName];
20
- if (exported === undefined) {
21
- throw new Error(`missing export ${entry.exportName} from ${entry.module}`);
22
- }
23
- // The manifest is a dynamic plugin boundary: validate the factory and adapter
24
- // shape before handing the instance to the host for registration.
25
- const instance = entry.factory ? callFactory(exported, entry.options, entry) : exported;
26
- if (!isHostedAdapter(instance)) {
27
- throw new TypeError(`${entry.module}#${entry.exportName} is not an Octo Runtime adapter`);
28
- }
29
- if (entry.type && instance.metadata.type !== entry.type) {
30
- throw new Error(`manifest type ${entry.type} does not match adapter ${instance.metadata.type}`);
31
- }
32
- adapters.push(instance);
33
- }
34
- // JSON 是 CLI 的装配来源;注册、RPC 和退出清理与 SDK 的代码入口共用。
35
- serveWorker(adapters);
36
- /** Parse and validate the on-disk manifest. Only version 1 is supported; each
37
- * entry names a module, an export, whether that export is a factory, and options. */
38
- function parseManifest(value) {
39
- const record = asRecord(value, "adapter manifest");
40
- if (record.version !== 1)
41
- throw new TypeError("adapter manifest.version must be 1");
42
- if (!Array.isArray(record.adapters))
43
- throw new TypeError("adapter manifest.adapters must be an array");
44
- return {
45
- version: 1,
46
- adapters: record.adapters.map((item, position) => {
47
- const entry = asRecord(item, `adapter manifest entry ${position}`);
48
- const type = optionalString(entry.type);
49
- return {
50
- module: requiredString(entry.module, `adapters[${position}].module`),
51
- exportName: optionalString(entry.export) ?? "default",
52
- factory: entry.factory === true,
53
- options: entry.options === undefined
54
- ? {}
55
- : asRecord(entry.options, `adapters[${position}].options`),
56
- ...(type ? { type } : {})
57
- };
58
- })
59
- };
60
- }
61
- function callFactory(value, options, entry) {
62
- if (typeof value !== "function") {
63
- throw new TypeError(`${entry.module}#${entry.exportName} is marked as a factory but is not callable`);
64
- }
65
- const factory = value;
66
- return factory(options);
67
- }
68
- /** Structural type guard: only `metadata.type`, Agent sources and
69
- * an `invoke` method are required, so any conforming object can be a hosted adapter. */
70
- function isHostedAdapter(value) {
71
- if (!isRecord(value) || !isRecord(value.metadata))
72
- return false;
73
- return (typeof value.metadata.type === "string" &&
74
- Array.isArray(value.metadata.agentSources) &&
75
- value.metadata.agentSources.length > 0 &&
76
- value.metadata.agentSources.every((source) => source === "preset" || source === "provisioned") &&
77
- typeof value.invoke === "function");
78
- }
79
- function asRecord(value, name) {
80
- if (!isRecord(value))
81
- throw new TypeError(`${name} must be an object`);
82
- return value;
83
- }
84
- function isRecord(value) {
85
- return value !== null && typeof value === "object" && !Array.isArray(value);
86
- }
87
- function requiredString(value, name) {
88
- if (typeof value !== "string" || value.length === 0)
89
- throw new TypeError(`${name} must be a non-empty string`);
90
- return value;
91
- }
92
- function optionalString(value) {
93
- return typeof value === "string" && value.length > 0 ? value : undefined;
94
- }
5
+ serveWorker(await loadManifestAdapters());
@@ -28,10 +28,29 @@ export function serveWorker(adapters, options = {}) {
28
28
  logging.kernel("info", "host.ready");
29
29
  // 父 Kernel 断开或停止时等待 Core 取消和清理;需要替换时由 Kernel 监督器重启相同入口。
30
30
  let stopping = false;
31
- const stop = () => {
31
+ let drainedExitCode;
32
+ const stop = (holdForParent = false) => {
32
33
  if (stopping)
33
34
  return;
34
35
  stopping = true;
36
+ // Let the launcher reserve the full bounded drain only for a worker that handles EOF.
37
+ if (holdForParent && process.connected)
38
+ process.send?.({ type: "worker.stopping" }, () => { });
39
+ const finish = async (code) => {
40
+ await logging.close();
41
+ drainedExitCode = code;
42
+ if (holdForParent && process.connected) {
43
+ // Windows 的树清理需要 leader 仍存活。通知只表示排空完成,不代表后代已回收。
44
+ // IPC 保持引用直到 launcher 执行 taskkill;owner 断连仍走下方既有退出收尾。
45
+ process.channel?.ref();
46
+ process.send?.({ type: "worker.drained", cleanup: code === 0 ? "complete" : "incomplete" }, (error) => {
47
+ if (error)
48
+ process.exit(code || 1);
49
+ });
50
+ }
51
+ else
52
+ process.exit(code);
53
+ };
35
54
  void host.shutdown().then(async (result) => {
36
55
  if (result.cleanup === "incomplete") {
37
56
  logging.kernel("error", "host.cleanup_incomplete", {
@@ -39,27 +58,32 @@ export function serveWorker(adapters, options = {}) {
39
58
  unresolved_count: result.unresolvedInvocationIds.length
40
59
  });
41
60
  }
42
- await logging.close();
43
- process.exit(result.cleanup === "complete" ? 0 : 1);
61
+ await finish(result.cleanup === "complete" ? 0 : 1);
44
62
  }, async () => {
45
- await logging.close();
46
- process.exit(1);
63
+ await finish(1);
47
64
  });
48
65
  };
49
- process.once("SIGINT", stop);
50
- process.once("SIGTERM", stop);
51
- process.once("SIGHUP", stop);
52
- process.stdin.once("end", stop);
53
- process.stdin.once("close", stop);
66
+ process.once("SIGINT", () => stop());
67
+ process.once("SIGTERM", () => stop());
68
+ process.once("SIGHUP", () => stop());
69
+ process.stdin.once("end", () => stop(process.platform === "win32"));
70
+ process.stdin.once("close", () => stop(process.platform === "win32"));
54
71
  // 心跳必须由执行进程主事件循环处理;独立线程应答会掩盖执行线程阻塞。
55
72
  process.on("message", (message) => {
56
73
  if (message &&
57
74
  typeof message === "object" &&
58
75
  "type" in message &&
59
76
  message.type === "kernel.ping" &&
77
+ "probe" in message &&
78
+ Number.isSafeInteger(message.probe) &&
60
79
  process.connected) {
61
- process.send?.({ type: "worker.pong" });
80
+ process.send?.({ type: "worker.pong", probe: message.probe });
62
81
  }
63
82
  });
64
- process.once("disconnect", stop);
83
+ process.once("disconnect", () => {
84
+ if (drainedExitCode !== undefined)
85
+ process.exit(drainedExitCode);
86
+ else
87
+ stop();
88
+ });
65
89
  }
@@ -1,9 +1,12 @@
1
1
  import { type AdapterProbeOptions } from "@mininglamp-oss/octo-runtime-adapter-sdk";
2
+ import { type AdapterModuleEntry } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
2
3
  import { type KernelStatus } from "./types.js";
3
4
  export interface KernelServerOptions {
4
5
  endpoint: string;
5
6
  workerEntrypoint: string;
6
7
  manifest?: string;
8
+ /** CLI 装配快照;SDK 原生 worker 无需清单或此字段。 */
9
+ adapterModules?: AdapterModuleEntry[];
7
10
  env?: NodeJS.ProcessEnv;
8
11
  adapterConfigurations?: Record<string, AdapterProbeOptions>;
9
12
  stateDirectory?: string;
@@ -15,5 +18,5 @@ export interface KernelServerOptions {
15
18
  */
16
19
  export declare function startKernelServer(options: KernelServerOptions, signal?: AbortSignal): Promise<{
17
20
  status: () => KernelStatus;
18
- close(): Promise<void>;
21
+ close(until?: number): Promise<void>;
19
22
  }>;
@@ -8,6 +8,7 @@ import { HostRunService } from "./host-run-service.js";
8
8
  import { WorkerSupervisor } from "./supervisor.js";
9
9
  import { acquireEndpoint } from "./endpoint.js";
10
10
  import { InvocationManifest } from "./invocation-manifest.js";
11
+ import { readModuleManifest, resolveAdapterModules } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
11
12
  import { KernelError } from "./types.js";
12
13
  function validateParams(method, params) {
13
14
  const fields = {
@@ -71,9 +72,15 @@ function validateParams(method, params) {
71
72
  * 启动失败也执行回收;close 失败会保留所有权证据,客户端断开不等同于 owner 关闭。
72
73
  */
73
74
  export async function startKernelServer(options, signal) {
75
+ if (options.manifest && options.adapterModules !== undefined)
76
+ throw new TypeError("manifest and adapterModules are mutually exclusive");
77
+ // 文件仅在服务启动时读取一次;worker 自动恢复沿用快照,不因磁盘改动改变加载集合。
78
+ const adapterModules = options.manifest
79
+ ? resolveAdapterModules(await readModuleManifest(options.manifest, { legacy: true }))
80
+ : options.adapterModules;
74
81
  const instance = randomUUID();
75
82
  let manifest;
76
- const worker = new WorkerSupervisor({ entrypoint: options.workerEntrypoint, manifest: options.manifest, env: options.env }, (pid) => manifest?.setWorker(pid, options.workerEntrypoint) ?? Promise.resolve());
83
+ const worker = new WorkerSupervisor({ entrypoint: options.workerEntrypoint, adapterModules, env: options.env }, (pid) => manifest?.setWorker(pid, options.workerEntrypoint) ?? Promise.resolve());
77
84
  const configure = (input) => {
78
85
  const type = contextItem(input.contexts, "octo.execution", "adapter")?.payload.type;
79
86
  return (options.configurations?.(typeof input.runMetadata?.runtimeId === "string" ? input.runMetadata.runtimeId : undefined) ?? options.adapterConfigurations)?.[typeof type === "string" ? type : "codex"];
@@ -312,7 +319,7 @@ export async function startKernelServer(options, signal) {
312
319
  }
313
320
  });
314
321
  });
315
- const teardown = async () => {
322
+ const teardown = async (until = Date.now() + 5000) => {
316
323
  runs.beginDrain();
317
324
  for (const socket of sockets)
318
325
  socket.destroy();
@@ -330,9 +337,10 @@ export async function startKernelServer(options, signal) {
330
337
  if (listening)
331
338
  await attempt(() => new Promise((resolve) => server.close(() => resolve())));
332
339
  // 先终止 worker 才能让失联的源流结束,再清空 Run 缓存;每个资源仍独立尝试回收并汇总失败。
333
- const workerStopped = await attempt(() => worker.close());
340
+ // 内部沿用服务截止时间,为清单和端点收尾预留时间;不重启一份独立关闭预算。
341
+ const workerStopped = await attempt(() => worker.close(until - 200));
334
342
  await attempt(() => runs.close());
335
- // 进程未确认回收时保留身份清单和端点租约,禁止新实例覆盖所有权。
343
+ // An incomplete worker close retains ownership, including an observed executor cleanup failure.
336
344
  if (workerStopped) {
337
345
  await attempt(() => manifest?.close() ?? Promise.resolve());
338
346
  if (listening && process.platform !== "win32")
@@ -385,8 +393,8 @@ export async function startKernelServer(options, signal) {
385
393
  }
386
394
  return {
387
395
  status,
388
- close() {
389
- return (closing ??= teardown());
396
+ close(until) {
397
+ return (closing ??= teardown(until));
390
398
  }
391
399
  };
392
400
  }
@@ -5,6 +5,7 @@ import { beforeDeadline, ServiceInitializationError } from "./lifecycle.js";
5
5
  export function createKernelService(options) {
6
6
  const configuration = {
7
7
  ...options,
8
+ adapterModules: options.adapterModules && structuredClone(options.adapterModules),
8
9
  adapterConfigurations: options.adapterConfigurations && structuredClone(options.adapterConfigurations),
9
10
  env: options.env && { ...options.env }
10
11
  };
@@ -33,7 +34,7 @@ export function createKernelService(options) {
33
34
  await beforeDeadline(starting?.catch(() => undefined) ?? Promise.resolve(), until);
34
35
  if (startupFailure instanceof AggregateError)
35
36
  throw startupFailure;
36
- await beforeDeadline(server?.close() ?? Promise.resolve(), until);
37
+ await beforeDeadline(server?.close(until) ?? Promise.resolve(), until);
37
38
  }
38
39
  catch {
39
40
  errors.push({ resource: "kernel", code: "KERNEL_CLEANUP_INCOMPLETE" });
@@ -1,6 +1,6 @@
1
1
  import { type SpawnWorkerOptions } from "./process.js";
2
2
  import type { KernelStatus } from "./types.js";
3
- /** Kernel 独占 worker 的创建与回收;清理失败时禁止启动替代实例。 */
3
+ /** Kernel exclusively owns worker replacement; incomplete cleanup prevents a second owner. */
4
4
  export declare class WorkerSupervisor {
5
5
  #private;
6
6
  private readonly options;
@@ -9,5 +9,5 @@ export declare class WorkerSupervisor {
9
9
  get status(): KernelStatus["worker"];
10
10
  get client(): import("./rpc.js").WorkerRpcClient;
11
11
  start(signal?: AbortSignal): Promise<void>;
12
- close(): Promise<void>;
12
+ close(until?: number): Promise<void>;
13
13
  }
@@ -1,6 +1,7 @@
1
- import { spawnWorker } from "./process.js";
1
+ import { closeWorker, spawnWorker } from "./process.js";
2
2
  import { KernelError } from "./types.js";
3
- /** Kernel 独占 worker 的创建与回收;清理失败时禁止启动替代实例。 */
3
+ import { WorkerHeartbeat } from "./heartbeat.js";
4
+ /** Kernel exclusively owns worker replacement; incomplete cleanup prevents a second owner. */
4
5
  export class WorkerSupervisor {
5
6
  options;
6
7
  recordWorker;
@@ -11,12 +12,13 @@ export class WorkerSupervisor {
11
12
  #closing = false;
12
13
  #recovery;
13
14
  #timer;
14
- #lastPong = 0;
15
- #pendingPing = false;
16
- #stableSince = 0;
15
+ #heartbeat;
16
+ #activeTime = 0;
17
17
  #attempts = [];
18
+ #slowRecovery = false;
18
19
  #abort = new AbortController();
19
20
  #cleanupFailure;
21
+ #closeUntil;
20
22
  constructor(options, recordWorker) {
21
23
  this.options = options;
22
24
  this.recordWorker = recordWorker;
@@ -63,35 +65,44 @@ export class WorkerSupervisor {
63
65
  throw error;
64
66
  }
65
67
  if (this.#closing) {
66
- await worker.close();
68
+ try {
69
+ await closeWorker(worker, this.#closeUntil ?? Date.now() + 5000, true);
70
+ }
71
+ catch (error) {
72
+ // A late startup still owns resources; failed rollback must block every future replacement.
73
+ throw (this.#cleanupFailure = new AggregateError([error], "WORKER_CLEANUP_INCOMPLETE"));
74
+ }
67
75
  return;
68
76
  }
69
77
  this.#worker = worker;
70
78
  this.#generation++;
71
79
  this.#state = "ready";
72
- this.#stableSince = Date.now();
73
- this.#lastPong = Date.now();
74
- this.#pendingPing = false;
80
+ const heartbeat = new WorkerHeartbeat();
81
+ this.#heartbeat = heartbeat;
82
+ let elapsed = 0;
75
83
  worker.child.on("message", (message) => {
76
84
  if (this.#worker === worker &&
77
85
  message &&
78
86
  typeof message === "object" &&
79
87
  "type" in message &&
80
88
  message.type === "worker.pong") {
81
- this.#lastPong = Date.now();
82
- this.#pendingPing = false;
89
+ heartbeat.pong("probe" in message ? message.probe : undefined);
83
90
  }
84
91
  });
85
92
  worker.child.once("exit", () => this.#failed(worker));
86
93
  worker.child.once("error", () => this.#failed(worker));
87
94
  this.#timer = setInterval(() => {
88
- if (Date.now() - this.#lastPong >= 5000) {
95
+ if (this.#closing || this.#worker !== worker || this.#state !== "ready")
96
+ return;
97
+ const probe = heartbeat.tick();
98
+ this.#activeTime += heartbeat.activeMs - elapsed;
99
+ elapsed = heartbeat.activeMs;
100
+ if (probe.type === "expired") {
89
101
  this.#failed(worker);
90
102
  return;
91
103
  }
92
- if (!this.#pendingPing && worker.child.connected) {
93
- this.#pendingPing = true;
94
- worker.child.send({ type: "kernel.ping" }, (error) => {
104
+ if (probe.type === "ping" && worker.child.connected) {
105
+ worker.child.send({ type: "kernel.ping", probe: probe.id }, (error) => {
95
106
  if (error)
96
107
  this.#failed(worker);
97
108
  });
@@ -106,31 +117,39 @@ export class WorkerSupervisor {
106
117
  return;
107
118
  clearInterval(this.#timer);
108
119
  this.#state = "recovering";
109
- // 先拒绝原 RPC,让 Run 源生成唯一 lost 终态;恢复只允许新 Run。
120
+ // Reject the old RPC first so its source emits one lost terminal. Recovery admits new work only.
110
121
  worker.client.close(new KernelError("WORKER_LOST"));
111
- if (Date.now() - this.#stableSince >= 60_000) {
122
+ if ((this.#heartbeat?.healthyMs ?? 0) >= 60_000) {
112
123
  this.#attempts = [];
113
124
  this.#attempt = 0;
125
+ this.#slowRecovery = false;
114
126
  }
115
- this.#recovery = this.#recover(worker).catch(() => {
116
- this.#state = "exhausted";
127
+ this.#recovery = this.#recover(worker).catch((error) => {
128
+ // Only unconfirmed cleanup or ownership ends recovery; ordinary startup failures stay retryable.
129
+ this.#cleanupFailure ??= new AggregateError([error], "WORKER_RECOVERY_BLOCKED");
130
+ if (!this.#closing)
131
+ this.#state = "exhausted";
117
132
  });
118
133
  }
119
134
  async #recover(worker) {
120
- // 旧 worker 清理成功才允许替换,避免两个 Provider 实例并行持有同一执行资源。
121
- // 只恢复服务可用性,不重放旧 Run;旧流已由 #failed 标为 lost。
122
- await worker.close();
135
+ // Reap the old worker before replacement to prevent concurrent Provider owners.
136
+ // Restore service availability only; #failed already marked old runs lost and never replays them.
137
+ // Preserve baseline recovery for leaders that exited before close began. On Windows this
138
+ // path cannot verify detached descendants; EOF-close checks require a live leader at entry.
139
+ if (worker.child.exitCode !== null || worker.child.signalCode !== null)
140
+ await worker.close();
141
+ else
142
+ await closeWorker(worker, Date.now() + 4800, true);
123
143
  await this.recordWorker?.(undefined);
124
144
  this.#worker = undefined;
125
- for (let attempt = 0; attempt < 3; attempt++) {
126
- if (this.#closing)
127
- return;
128
- this.#attempts = this.#attempts.filter((at) => Date.now() - at < 60_000);
145
+ while (!this.#closing) {
146
+ // Suspend time and clock changes must not replenish a recently exhausted restart budget.
147
+ this.#attempts = this.#attempts.filter((at) => this.#activeTime - at < 60_000);
129
148
  if (this.#attempts.length >= 3)
130
- break;
131
- const delay = [250, 1000, 4000][this.#attempts.length];
132
- this.#attempts.push(Date.now());
133
- this.#attempt++;
149
+ this.#slowRecovery = true;
150
+ // Exhausting the fast budget opens a bounded-rate recovery loop, not a permanent outage.
151
+ // Keep slow recovery across short-lived workers until 60 seconds of health is observed.
152
+ const delay = this.#slowRecovery ? 15_000 : [250, 1000, 4000][this.#attempts.length];
134
153
  await new Promise((resolve) => {
135
154
  const finish = () => {
136
155
  clearTimeout(timer);
@@ -142,6 +161,9 @@ export class WorkerSupervisor {
142
161
  });
143
162
  if (this.#closing)
144
163
  return;
164
+ if (!this.#slowRecovery)
165
+ this.#attempts.push(this.#activeTime);
166
+ this.#attempt = Math.min(Number.MAX_SAFE_INTEGER, this.#attempt + 1);
145
167
  try {
146
168
  await this.#spawn();
147
169
  return;
@@ -149,17 +171,18 @@ export class WorkerSupervisor {
149
171
  catch (error) {
150
172
  if (error instanceof AggregateError)
151
173
  throw error;
152
- /* 普通启动失败已回收;聚合清理错误禁止继续替换。 */
174
+ /* Ordinary startup failures are reaped; aggregate cleanup failures prevent replacement. */
153
175
  }
154
176
  }
155
- this.#state = "exhausted";
156
177
  }
157
- async close() {
178
+ async close(until = Date.now() + 5000) {
158
179
  this.#closing = true;
180
+ this.#closeUntil ??= until;
159
181
  this.#abort.abort();
160
182
  clearInterval(this.#timer);
161
183
  await this.#recovery;
162
- await this.#worker?.close();
184
+ if (this.#worker)
185
+ await closeWorker(this.#worker, this.#closeUntil);
163
186
  if (this.#cleanupFailure)
164
187
  throw this.#cleanupFailure;
165
188
  await this.recordWorker?.(undefined);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mininglamp-oss/octo-runtime-executor-kernel",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -8,27 +8,19 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "import": "./dist/index.js"
10
10
  },
11
+ "./manifest-adapters": {
12
+ "types": "./dist/manifest-adapters.d.ts",
13
+ "import": "./dist/manifest-adapters.js"
14
+ },
11
15
  "./runner": {
12
16
  "types": "./dist/runner.d.ts",
13
17
  "import": "./dist/runner.js"
14
18
  }
15
19
  },
16
20
  "dependencies": {
17
- "@mininglamp-oss/octo-runtime-executor-core": "1.1.0",
18
- "@mininglamp-oss/octo-runtime-adapter-antigravity": "1.1.0",
19
- "@mininglamp-oss/octo-runtime-adapter-claude-code": "1.1.0",
20
- "@mininglamp-oss/octo-runtime-adapter-codebuddy": "1.1.0",
21
- "@mininglamp-oss/octo-runtime-adapter-codex": "1.1.0",
22
- "@mininglamp-oss/octo-runtime-adapter-hermes": "1.1.0",
23
- "@mininglamp-oss/octo-runtime-adapter-kimi-code": "1.1.0",
24
- "@mininglamp-oss/octo-runtime-adapter-mcode": "1.1.0",
25
- "@mininglamp-oss/octo-runtime-adapter-openclaw": "1.1.0",
26
- "@mininglamp-oss/octo-runtime-adapter-opencode": "1.1.0",
27
- "@mininglamp-oss/octo-runtime-adapter-qodercli": "1.1.0",
28
- "@mininglamp-oss/octo-runtime-adapter-qoderclicn": "1.1.0",
29
- "@mininglamp-oss/octo-runtime-adapter-zcode-gui": "1.1.0",
30
- "@mininglamp-oss/octo-runtime-protocol": "1.1.0",
31
- "@mininglamp-oss/octo-runtime-adapter-sdk": "1.1.0"
21
+ "@mininglamp-oss/octo-runtime-executor-core": "1.1.2",
22
+ "@mininglamp-oss/octo-runtime-protocol": "1.1.2",
23
+ "@mininglamp-oss/octo-runtime-adapter-sdk": "1.1.2"
32
24
  },
33
25
  "types": "./dist/index.d.ts",
34
26
  "files": [
@@ -6,12 +6,12 @@
6
6
  "$comment": "声明文件最多 1 MiB,POSIX 权限须为 0600;单 Bot JSON 最多 256 KiB。Bot 按规范化 api_url 与 bot_id 组合判重。路径可访问性、凭据、身份和 Space 授权、重试起点不大于上限等关系仍需运行时检查。Context 内部对象遵循 octo.context-envelope/v3,具体协议约束由现有运行时校验,本文件不重复展开。",
7
7
  "type": "object",
8
8
  "additionalProperties": false,
9
- "required": ["module", "config"],
9
+ "required": ["config"],
10
10
  "properties": {
11
11
  "module": {
12
12
  "type": "string",
13
13
  "const": "@mininglamp-oss/octo-runtime-extra-channel-gateway",
14
- "description": "必填。Octo IM Extra 的固定包入口;宿主须预先安装并确保模块可解析。此 schema 约束包名声明,宿主通过绝对路径加载同一模块时可自行调整该约束。插件实例名及 CLI 配置目录名为 channel-gateway。"
14
+ "description": "可选旧声明。CLI 从 manifest.json 选择模块;省略时使用有效清单,保留时须与清单文字一致。SDK 原生 import 不需要此字段。此 schema 约束固定包名,宿主使用其他公开路径时可自行调整。"
15
15
  },
16
16
  "enabled": {
17
17
  "type": "boolean",
@@ -49,7 +49,6 @@ export class ImRuntime {
49
49
  #busyNotifications = new Map();
50
50
  #busyNotifiedAt = new Map();
51
51
  #retry = 0;
52
- #everOnline = false;
53
52
  #disconnecting;
54
53
  constructor(config, dependencies) {
55
54
  this.dependencies = dependencies;
@@ -110,7 +109,6 @@ export class ImRuntime {
110
109
  this.dependencies.signal?.addEventListener("abort", this.#ownerStopped, { once: true });
111
110
  this.#delivery = new ImDeliveryDispatcher(this.#signal);
112
111
  }
113
- this.#everOnline = false;
114
112
  this.#retry = 0;
115
113
  this.#error = undefined;
116
114
  this.#state = "connecting";
@@ -125,6 +123,9 @@ export class ImRuntime {
125
123
  async #connect(recovering) {
126
124
  if (this.#closed || this.#draining || this.#signal.aborted)
127
125
  return;
126
+ this.#log(recovering ? "channel.retry_started" : "channel.connecting", {
127
+ attempt: this.#retry
128
+ });
128
129
  this.#connectionAbort?.abort();
129
130
  try {
130
131
  await this.#disconnecting;
@@ -134,7 +135,7 @@ export class ImRuntime {
134
135
  this.#state = this.#closed ? "stopped" : "failed";
135
136
  if (!this.#closed)
136
137
  this.#error = "IM_DISPOSE_FAILED";
137
- this.#everOnline = false;
138
+ this.#diagnostic("IM_DISPOSE_FAILED");
138
139
  return;
139
140
  }
140
141
  if (this.#closed || this.#draining || this.#signal.aborted)
@@ -183,7 +184,6 @@ export class ImRuntime {
183
184
  if (disconnected)
184
185
  throw disconnected;
185
186
  starting = false;
186
- this.#everOnline = true;
187
187
  this.#state = "online";
188
188
  this.#log("channel.connected", { state: "online" });
189
189
  this.#error = undefined;
@@ -196,21 +196,19 @@ export class ImRuntime {
196
196
  const failure = connectionFailure(error);
197
197
  this.#connectionAbort.abort();
198
198
  this.#error = failure.message;
199
- const retry = recovering && this.#everOnline && failure.retryable;
199
+ // Offline startup and later disconnects share recovery; only error semantics decide eligibility.
200
+ const retry = failure.retryable;
200
201
  this.#state = retry ? "retrying" : "failed";
201
202
  this.#log("channel.connection_failed", { state: this.#state, code: failure.message });
202
203
  try {
203
204
  await connector.close();
204
205
  }
205
206
  catch {
206
- this.#everOnline = false;
207
207
  if (!this.#closed)
208
208
  this.#state = "failed";
209
209
  this.#diagnostic("IM_DISPOSE_FAILED");
210
210
  return;
211
211
  }
212
- if (!retry)
213
- this.#everOnline = false;
214
212
  if (retry)
215
213
  this.#scheduleReconnect(failure.retryAfterMs);
216
214
  }
@@ -218,12 +216,10 @@ export class ImRuntime {
218
216
  #lostConnection(connector, failure) {
219
217
  this.#log("channel.disconnected", { code: failure.message });
220
218
  this.#error = failure.message;
221
- const retry = this.#everOnline && failure.retryable;
219
+ const retry = failure.retryable;
222
220
  this.#state = retry ? "retrying" : "failed";
223
- if (!retry)
224
- this.#everOnline = false;
225
221
  this.#connectionAbort?.abort();
226
- // 先收回旧连接资源,再计时;停止信号与迟到回调不能建立第二条连接。
222
+ // Reap the old connection before backoff; shutdown and stale callbacks cannot create a second owner.
227
223
  this.#disconnecting = connector.close();
228
224
  void this.#disconnecting.then(() => {
229
225
  if (retry)
@@ -231,7 +227,6 @@ export class ImRuntime {
231
227
  }, () => {
232
228
  if (!this.#closed)
233
229
  this.#state = "failed";
234
- this.#everOnline = false;
235
230
  this.#diagnostic("IM_DISPOSE_FAILED");
236
231
  });
237
232
  }
@@ -242,9 +237,14 @@ export class ImRuntime {
242
237
  this.#state !== "retrying" ||
243
238
  this.#reconnect)
244
239
  return;
245
- const delayMs = Math.max(Math.min(30_000, 5_000 * 2 ** this.#retry), retryAfterMs);
246
- this.#retry = Math.min(3, this.#retry + 1);
247
- // Node 超过 2^31-1 的 timeout 会变为 1ms;长 Retry-After 分段等待,不能绕过限流。
240
+ const delayMs = Math.max([5_000, 10_000, 15_000][Math.min(2, this.#retry)], retryAfterMs);
241
+ this.#retry = Math.min(Number.MAX_SAFE_INTEGER, this.#retry + 1);
242
+ this.#log("channel.retry_scheduled", {
243
+ state: "retrying",
244
+ delay_ms: delayMs,
245
+ attempt: this.#retry
246
+ });
247
+ // Node clamps oversized timers to 1ms. Partition long Retry-After waits without bypassing throttling.
248
248
  const deadline = performance.now() + delayMs;
249
249
  const wake = () => {
250
250
  if (this.#closed || this.#draining || this.#signal.aborted || this.#state !== "retrying")
@@ -744,7 +744,8 @@ export class ImRuntime {
744
744
  ? undefined
745
745
  : await this.dependencies.prepareContexts?.(this.config, turnSignal),
746
746
  initialSession,
747
- mentionGuidance: initialSession || !executionSession.providerSession,
747
+ // 逐轮提醒让升级前的 Provider binding 也收到新 mention 规则,不隐式 reset。
748
+ mentionGuidance: true,
748
749
  ...(gapEnabled
749
750
  ? {
750
751
  historyGap: gapPlan,
@@ -1069,7 +1070,6 @@ export class ImRuntime {
1069
1070
  this.#state = "stopped";
1070
1071
  this.#log("channel.stopped", { state: "stopped" });
1071
1072
  this.#error = undefined;
1072
- this.#everOnline = false;
1073
1073
  this.#retry = 0;
1074
1074
  for (const preparation of this.#preparing.values())
1075
1075
  preparation.abort();
@@ -57,6 +57,7 @@ export interface ImInboundMessage {
57
57
  position?: {
58
58
  messageSeq: number;
59
59
  };
60
+ /** 同代次卡片重试:buildContext 返回 Gateway 已组合的冻结快照,不再采集或追加默认项。 */
60
61
  historyRetry?: boolean;
61
62
  bindHistoryTurn?(token: ImGapTurnToken): void;
62
63
  markRead?(signal: AbortSignal): Promise<void>;