@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.
- package/README.md +4 -2
- package/apps/cli/README.md +3 -1
- package/apps/cli/dist/build-info.json +2 -2
- package/apps/cli/dist/extras.d.ts +4 -2
- package/apps/cli/dist/extras.js +20 -21
- package/apps/cli/dist/help.js +3 -0
- package/apps/cli/dist/logs.d.ts +10 -0
- package/apps/cli/dist/logs.js +186 -0
- package/apps/cli/dist/main.d.ts +1 -0
- package/apps/cli/dist/main.js +94 -19
- package/apps/cli/dist/{default_adapters.json → manifest.json} +10 -0
- package/apps/cli/dist/module-manifest.d.ts +3 -0
- package/apps/cli/dist/module-manifest.js +11 -0
- package/apps/cli/dist/openclaw.js +30 -6
- package/apps/cli/dist/output.js +3 -0
- package/apps/cli/dist/playboard.js +10 -8
- package/apps/cli/dist/status-output.d.ts +10 -0
- package/apps/cli/dist/status-output.js +65 -0
- package/apps/cli/dist/worker.d.ts +1 -0
- package/apps/cli/dist/worker.js +18 -0
- package/dist/public/host.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-acp-client/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-acp/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-antigravity/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-claude-code/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-codebuddy/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-codex/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/adapter.d.ts +54 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/adapter.js +399 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/client.d.ts +14 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/client.js +156 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/index.d.ts +29 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/index.js +273 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/interactions.d.ts +27 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/interactions.js +161 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/package.json +17 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-hermes/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-kimi-code/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-mcode/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/compatibility.d.ts +24 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/compatibility.js +181 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/gateway.d.ts +9 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/gateway.js +24 -8
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/index.js +229 -225
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-opencode/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-qodercli/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-qoderclicn/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/approval.d.ts +4 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/approval.js +5 -6
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.js +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.d.ts +33 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.js +118 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log-worker.js +7 -5
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.d.ts +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.js +45 -10
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.d.ts +11 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.js +70 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/package.json +5 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-zcode-gui/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/build-info.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.d.ts +4 -2
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.js +20 -21
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/help.js +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.d.ts +10 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.js +186 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.js +94 -19
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/{default_adapters.json → manifest.json} +10 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.d.ts +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.js +11 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/openclaw.js +30 -6
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/output.js +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/playboard.js +10 -8
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.d.ts +10 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.js +65 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.js +18 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/package.json +23 -9
- package/node_modules/@mininglamp-oss/octo-runtime-cross-service/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.d.ts +1 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.js +13 -9
- package/node_modules/@mininglamp-oss/octo-runtime-executor-core/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.d.ts +16 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.js +59 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.js +4 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.d.ts +6 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.js +88 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.d.ts +1 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.js +2 -9
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.d.ts +7 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.js +138 -9
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/runner.js +3 -92
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/serve.js +36 -12
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.d.ts +4 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.js +14 -6
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/service.js +2 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.d.ts +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.js +57 -34
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/package.json +8 -16
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/application/im-channel.js +18 -18
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/contracts.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/execution/dispatcher.js +24 -0
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/contexts.js +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/mentions.d.ts +5 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/mentions.js +19 -5
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/normalize.js +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/package.json +6 -6
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/package.json +5 -5
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/config-schema.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/index.js +1 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.d.ts +4 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.js +54 -4
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.d.ts +8 -1
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.js +27 -12
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-service.js +39 -2
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/index.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +1 -1
- package/node_modules/import-meta-resolve/index.d.ts +19 -0
- package/node_modules/import-meta-resolve/index.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/index.js +47 -0
- package/node_modules/import-meta-resolve/lib/errors.d.ts +24 -0
- package/node_modules/import-meta-resolve/lib/errors.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/errors.js +504 -0
- package/node_modules/import-meta-resolve/lib/get-format.d.ts +13 -0
- package/node_modules/import-meta-resolve/lib/get-format.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/get-format.js +159 -0
- package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts +31 -0
- package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/package-json-reader.js +177 -0
- package/node_modules/import-meta-resolve/lib/resolve.d.ts +29 -0
- package/node_modules/import-meta-resolve/lib/resolve.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/resolve.js +1301 -0
- package/node_modules/import-meta-resolve/lib/utils.d.ts +6 -0
- package/node_modules/import-meta-resolve/lib/utils.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/utils.js +47 -0
- package/node_modules/import-meta-resolve/license +74 -0
- package/node_modules/import-meta-resolve/package.json +78 -0
- package/node_modules/import-meta-resolve/readme.md +252 -0
- package/package.json +30 -26
- package/packages/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
- package/packages/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
- package/packages/octo-runtime-extra-webui/config-schema.json +2 -2
|
@@ -1,94 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
43
|
-
process.exit(result.cleanup === "complete" ? 0 : 1);
|
|
61
|
+
await finish(result.cleanup === "complete" ? 0 : 1);
|
|
44
62
|
}, async () => {
|
|
45
|
-
await
|
|
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",
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
15
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
this.#
|
|
74
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
93
|
-
|
|
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
|
-
//
|
|
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 (
|
|
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
|
-
|
|
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
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.#
|
|
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
|
-
|
|
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.
|
|
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.
|
|
18
|
-
"@mininglamp-oss/octo-runtime-
|
|
19
|
-
"@mininglamp-oss/octo-runtime-adapter-
|
|
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": ["
|
|
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": "
|
|
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.#
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
246
|
-
this.#retry = Math.min(
|
|
247
|
-
|
|
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
|
-
|
|
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>;
|