@mininglamp-oss/octo-runtime 1.1.1 → 1.1.3
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/apps/cli/dist/build-info.json +2 -2
- package/apps/cli/dist/manifest.json +5 -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/package.json +2 -2
- 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/manifest.json +5 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/package.json +23 -22
- package/node_modules/@mininglamp-oss/octo-runtime-cross-service/package.json +1 -1
- 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/invocation-manifest.js +31 -8
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/config-schema.json +1 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/application/im-channel.js +2 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/contracts.d.ts +2 -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/channel.js +5 -4
- 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.d.ts +1 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/normalize.js +5 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/package.json +6 -6
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/package.json +5 -5
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/package.json +2 -2
- 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/package.json +27 -25
- package/packages/octo-runtime-extra-channel-gateway/config-schema.json +1 -1
|
@@ -3,6 +3,7 @@ import { canonicalTerminalStatus } from "@mininglamp-oss/octo-runtime-protocol";
|
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { OpenClawGatewayClient } from "./gateway.js";
|
|
5
5
|
import { resolveOpenClawGateway, localGatewayFailure } from "./local-gateway.js";
|
|
6
|
+
import { resolveGatewayContract, resolveGatewayPermission, gatewayCreateParams, gatewayLegacyPermissionParams, gatewayTerminal, isGatewayRunPending } from "./compatibility.js";
|
|
6
7
|
export const OPENCLAW_PROVIDER_OPTIONS = {
|
|
7
8
|
url: "OPENCLAW_GATEWAY_URL",
|
|
8
9
|
token: "OPENCLAW_GATEWAY_TOKEN",
|
|
@@ -60,10 +61,7 @@ export function buildOpenClawGatewayInvocation(req) {
|
|
|
60
61
|
const createParams = {
|
|
61
62
|
key: sessionKey,
|
|
62
63
|
agentId,
|
|
63
|
-
|
|
64
|
-
idempotencyKey: `${requestId}-session`,
|
|
65
|
-
...(model ? { model } : {}),
|
|
66
|
-
...(thinking ? { thinkingLevel: thinking } : {})
|
|
64
|
+
...(model ? { model } : {})
|
|
67
65
|
};
|
|
68
66
|
const agentParams = {
|
|
69
67
|
message,
|
|
@@ -156,7 +154,7 @@ function normalizeGatewayAgentEvent(payload, state) {
|
|
|
156
154
|
return [];
|
|
157
155
|
state.lastSeq = seq;
|
|
158
156
|
const events = [];
|
|
159
|
-
const stepId = `openclaw:${nonEmptyString(native.runId) ?? "run"}
|
|
157
|
+
const stepId = (state.stepId ??= `openclaw:${nonEmptyString(native.runId) ?? "run"}`);
|
|
160
158
|
const ensureStep = () => {
|
|
161
159
|
if (state.stepStarted)
|
|
162
160
|
return;
|
|
@@ -165,9 +163,13 @@ function normalizeGatewayAgentEvent(payload, state) {
|
|
|
165
163
|
};
|
|
166
164
|
if (stream === "lifecycle") {
|
|
167
165
|
const phase = nonEmptyString(data.phase);
|
|
166
|
+
const terminal = gatewayTerminal(data, "lifecycle");
|
|
168
167
|
if (phase === "start")
|
|
169
168
|
ensureStep();
|
|
170
|
-
if ((phase === "end" || phase === "error") &&
|
|
169
|
+
if ((phase === "end" || phase === "error") &&
|
|
170
|
+
terminal &&
|
|
171
|
+
state.stepStarted &&
|
|
172
|
+
!state.stepFinished) {
|
|
171
173
|
state.stepFinished = true;
|
|
172
174
|
const finishReason = nonEmptyString(data.stopReason);
|
|
173
175
|
events.push({
|
|
@@ -175,7 +177,7 @@ function normalizeGatewayAgentEvent(payload, state) {
|
|
|
175
177
|
type: "model_step.finished",
|
|
176
178
|
payload: {
|
|
177
179
|
stepId,
|
|
178
|
-
status: canonicalTerminalStatus(
|
|
180
|
+
status: canonicalTerminalStatus(terminal),
|
|
179
181
|
...(finishReason ? { finishReason } : {})
|
|
180
182
|
}
|
|
181
183
|
});
|
|
@@ -319,12 +321,9 @@ function terminalState(payload) {
|
|
|
319
321
|
if (native?.stream !== "lifecycle")
|
|
320
322
|
return undefined;
|
|
321
323
|
const data = asRecord(native.data);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (phase === "end")
|
|
326
|
-
return data?.aborted === true ? "cancelled" : "completed";
|
|
327
|
-
return undefined;
|
|
324
|
+
if (data?.phase !== "end" && data?.phase !== "error")
|
|
325
|
+
return undefined;
|
|
326
|
+
return gatewayTerminal(data, "lifecycle");
|
|
328
327
|
}
|
|
329
328
|
function terminalError(payload) {
|
|
330
329
|
return (nonEmptyString(asRecord(asRecord(payload)?.data)?.error) ?? "OpenClaw Gateway agent run failed");
|
|
@@ -334,19 +333,10 @@ async function waitForConfirmedTerminal(client, runId, expected, deadlineAt) {
|
|
|
334
333
|
const waitMs = Math.max(1, Math.min(30_000, deadlineAt - Date.now()));
|
|
335
334
|
const result = asRecord(await client.request("agent.wait", { runId, timeoutMs: waitMs }, { timeoutMs: waitMs + 2_000 }));
|
|
336
335
|
const status = nonEmptyString(result?.status);
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
? "cancelled"
|
|
342
|
-
: status === "ok"
|
|
343
|
-
? "completed"
|
|
344
|
-
: status === "error" || (status === "timeout" && endedAt !== undefined)
|
|
345
|
-
? "failed"
|
|
346
|
-
: undefined;
|
|
347
|
-
if (actual === expected)
|
|
348
|
-
return;
|
|
349
|
-
if (!actual && (status === "pending" || status === "timeout")) {
|
|
336
|
+
const actual = gatewayTerminal(result, "wait");
|
|
337
|
+
if (actual && (expected === undefined || actual === expected))
|
|
338
|
+
return actual;
|
|
339
|
+
if (!actual && (isGatewayRunPending(result) || status === "timeout")) {
|
|
350
340
|
await new Promise((resolve) => setTimeout(resolve, Math.min(250, Math.max(0, deadlineAt - Date.now()))));
|
|
351
341
|
continue;
|
|
352
342
|
}
|
|
@@ -355,14 +345,15 @@ async function waitForConfirmedTerminal(client, runId, expected, deadlineAt) {
|
|
|
355
345
|
? `OpenClaw Gateway confirmed ${actual ?? status ?? "unknown"}, not ${expected}: ${error}`
|
|
356
346
|
: `OpenClaw Gateway confirmed ${actual ?? status ?? "unknown"}, not ${expected}`);
|
|
357
347
|
}
|
|
358
|
-
throw new Error(`OpenClaw Gateway did not confirm ${expected} before the invocation deadline`);
|
|
348
|
+
throw new Error(`OpenClaw Gateway did not confirm ${expected ?? "a terminal state"} before the invocation deadline`);
|
|
359
349
|
}
|
|
360
|
-
function normalizeFinalResponse(value, state) {
|
|
350
|
+
function normalizeFinalResponse(value, state, runId) {
|
|
361
351
|
const response = asRecord(value);
|
|
362
352
|
const result = asRecord(response?.result);
|
|
363
353
|
const meta = asRecord(result?.meta);
|
|
364
354
|
const agentMeta = asRecord(meta?.agentMeta);
|
|
365
355
|
const events = [];
|
|
356
|
+
const stepId = (state.stepId ??= `openclaw:${runId}`);
|
|
366
357
|
const payloads = Array.isArray(result?.payloads) ? result.payloads : [];
|
|
367
358
|
const finalText = payloads
|
|
368
359
|
.map((item) => nonEmptyString(asRecord(item)?.text) ?? "")
|
|
@@ -375,7 +366,7 @@ function normalizeFinalResponse(value, state) {
|
|
|
375
366
|
events.push({
|
|
376
367
|
protocolVersion: 2,
|
|
377
368
|
type: "model_step.started",
|
|
378
|
-
payload: { stepId
|
|
369
|
+
payload: { stepId }
|
|
379
370
|
});
|
|
380
371
|
};
|
|
381
372
|
if (finalText) {
|
|
@@ -412,14 +403,7 @@ function normalizeFinalResponse(value, state) {
|
|
|
412
403
|
});
|
|
413
404
|
if (usageUpdate)
|
|
414
405
|
events.push(usageUpdate);
|
|
415
|
-
const
|
|
416
|
-
const terminal = status === "ok"
|
|
417
|
-
? meta?.aborted === true
|
|
418
|
-
? "cancelled"
|
|
419
|
-
: "completed"
|
|
420
|
-
: status
|
|
421
|
-
? "failed"
|
|
422
|
-
: undefined;
|
|
406
|
+
const terminal = gatewayTerminal(response, "final");
|
|
423
407
|
if (terminal && !state.stepFinished) {
|
|
424
408
|
ensureStep();
|
|
425
409
|
state.stepFinished = true;
|
|
@@ -427,7 +411,7 @@ function normalizeFinalResponse(value, state) {
|
|
|
427
411
|
protocolVersion: 2,
|
|
428
412
|
type: "model_step.finished",
|
|
429
413
|
payload: {
|
|
430
|
-
stepId
|
|
414
|
+
stepId,
|
|
431
415
|
status: canonicalTerminalStatus(terminal === "cancelled" ? "cancelled" : terminal === "failed" ? "failed" : "completed"),
|
|
432
416
|
...(nonEmptyString(meta?.stopReason)
|
|
433
417
|
? { finishReason: nonEmptyString(meta?.stopReason) }
|
|
@@ -443,32 +427,10 @@ function normalizeFinalResponse(value, state) {
|
|
|
443
427
|
: {})
|
|
444
428
|
};
|
|
445
429
|
}
|
|
446
|
-
function assertRuntimeGatewayContract(value) {
|
|
447
|
-
const hello = asRecord(value);
|
|
448
|
-
const features = asRecord(hello?.features);
|
|
449
|
-
const methods = Array.isArray(features?.methods) ? features.methods : [];
|
|
450
|
-
const events = Array.isArray(features?.events) ? features.events : [];
|
|
451
|
-
if (hello?.protocol !== 4)
|
|
452
|
-
throw new Error(`unsupported OpenClaw Gateway protocol: ${String(hello?.protocol)}`);
|
|
453
|
-
const requiredMethods = [
|
|
454
|
-
"sessions.create",
|
|
455
|
-
"sessions.messages.subscribe",
|
|
456
|
-
"agent",
|
|
457
|
-
"agent.wait",
|
|
458
|
-
"sessions.abort"
|
|
459
|
-
];
|
|
460
|
-
requiredMethods.unshift("agents.list");
|
|
461
|
-
for (const method of requiredMethods) {
|
|
462
|
-
if (!methods.includes(method))
|
|
463
|
-
throw new Error(`OpenClaw Gateway does not advertise required method ${method}`);
|
|
464
|
-
}
|
|
465
|
-
if (!events.includes("agent"))
|
|
466
|
-
throw new Error("OpenClaw Gateway does not advertise the agent event stream");
|
|
467
|
-
}
|
|
468
430
|
async function* invokeOpenClawGateway(req, invocation, context, clientFactory, permissions) {
|
|
469
431
|
const client = clientFactory(invocation.gateway);
|
|
470
432
|
const requestAbort = new AbortController();
|
|
471
|
-
let resourceFailure
|
|
433
|
+
let resourceFailure;
|
|
472
434
|
let terminal;
|
|
473
435
|
let closing;
|
|
474
436
|
const stop = (cancel = false) => {
|
|
@@ -477,7 +439,7 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
477
439
|
unsubscribe();
|
|
478
440
|
requestAbort.abort();
|
|
479
441
|
closing = (async () => {
|
|
480
|
-
if (cancel &&
|
|
442
|
+
if (cancel && agentDispatched)
|
|
481
443
|
await client
|
|
482
444
|
.request("sessions.abort", {
|
|
483
445
|
key: providerSessionKey,
|
|
@@ -491,25 +453,74 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
491
453
|
void closing.catch(() => undefined);
|
|
492
454
|
return closing;
|
|
493
455
|
};
|
|
494
|
-
|
|
456
|
+
let cancellation;
|
|
457
|
+
const confirmCancellation = () => {
|
|
458
|
+
cancellation ??= (async () => {
|
|
459
|
+
if (!agentDispatched)
|
|
460
|
+
return;
|
|
461
|
+
let recoveryClient;
|
|
462
|
+
try {
|
|
463
|
+
const abortParams = {
|
|
464
|
+
key: providerSessionKey,
|
|
465
|
+
runId: acceptedRunId,
|
|
466
|
+
agentId: invocation.agentId
|
|
467
|
+
};
|
|
468
|
+
let controlClient = client;
|
|
469
|
+
try {
|
|
470
|
+
await controlClient.request("sessions.abort", abortParams, { timeoutMs: 5_000 });
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
recoveryClient = clientFactory(invocation.gateway);
|
|
474
|
+
resolveGatewayContract(await recoveryClient.connect());
|
|
475
|
+
controlClient = recoveryClient;
|
|
476
|
+
await controlClient.request("sessions.abort", abortParams, { timeoutMs: 5_000 });
|
|
477
|
+
}
|
|
478
|
+
await waitForConfirmedTerminal(controlClient, acceptedRunId, "cancelled", Date.now() + 30_000);
|
|
479
|
+
terminal ??= "cancelled";
|
|
480
|
+
}
|
|
481
|
+
finally {
|
|
482
|
+
await recoveryClient?.close();
|
|
483
|
+
}
|
|
484
|
+
})();
|
|
485
|
+
return cancellation;
|
|
486
|
+
};
|
|
487
|
+
// 桥和执行循环可能同时收到取消;共用一次 abort/对账,确认后才关闭连接。
|
|
488
|
+
permissions.bindTransport(async () => {
|
|
489
|
+
if (req.signal?.aborted && !terminal) {
|
|
490
|
+
try {
|
|
491
|
+
await confirmCancellation();
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// 执行源负责发出取消对账失败;清理桥不重复抛出同一错误。
|
|
495
|
+
}
|
|
496
|
+
finally {
|
|
497
|
+
await stop(false);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
else
|
|
501
|
+
await stop(!terminal);
|
|
502
|
+
});
|
|
495
503
|
const queue = new DriverQueue({
|
|
496
504
|
discardOnFailure: true,
|
|
497
505
|
limits: context?.streamLimits,
|
|
498
506
|
boundary: "openclaw.event_queue",
|
|
499
|
-
onFailure: () => {
|
|
500
|
-
|
|
507
|
+
onFailure: (error) => {
|
|
508
|
+
if (req.signal?.aborted)
|
|
509
|
+
return;
|
|
510
|
+
resourceFailure = error;
|
|
501
511
|
void stop(true);
|
|
502
512
|
}
|
|
503
513
|
});
|
|
504
514
|
const iterator = queue[Symbol.asyncIterator]();
|
|
505
515
|
let acceptedRunId = invocation.requestId;
|
|
516
|
+
let agentDispatched = false;
|
|
506
517
|
let agentAccepted = false;
|
|
507
518
|
const seenApprovals = new Set();
|
|
508
519
|
const activeApprovals = new Map();
|
|
509
520
|
let providerSessionKey = invocation.sessionKey;
|
|
510
521
|
const routedApprovals = new Set();
|
|
511
522
|
const acceptEvent = (event) => {
|
|
512
|
-
if (closing)
|
|
523
|
+
if (closing || terminal)
|
|
513
524
|
return;
|
|
514
525
|
const payload = asRecord(event.payload);
|
|
515
526
|
if (event.event === "agent") {
|
|
@@ -555,32 +566,98 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
555
566
|
messages: new Map(),
|
|
556
567
|
tools: new Set()
|
|
557
568
|
};
|
|
569
|
+
// 原生终态和步骤收尾共用出口;旧等待失败不能覆盖已确认的结果或丢失步骤结束。
|
|
570
|
+
const terminalEvents = (outcome, message) => {
|
|
571
|
+
const events = [];
|
|
572
|
+
if (state.stepStarted && !state.stepFinished) {
|
|
573
|
+
state.stepFinished = true;
|
|
574
|
+
events.push({
|
|
575
|
+
protocolVersion: 2,
|
|
576
|
+
type: "model_step.finished",
|
|
577
|
+
payload: { stepId: state.stepId, status: canonicalTerminalStatus(outcome) }
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
events.push({
|
|
581
|
+
protocolVersion: 2,
|
|
582
|
+
type: "message.update",
|
|
583
|
+
payload: {
|
|
584
|
+
...gatewayMessage(state, "final", state.assistantItemId),
|
|
585
|
+
role: "assistant",
|
|
586
|
+
status: outcome === "cancelled" ? "stopped" : outcome,
|
|
587
|
+
text: state.assistantText,
|
|
588
|
+
channel: "final"
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
if (outcome === "cancelled")
|
|
592
|
+
events.push({
|
|
593
|
+
protocolVersion: 2,
|
|
594
|
+
type: "run.cancelled",
|
|
595
|
+
payload: { reason: String(req.signal?.reason ?? "openclaw_aborted") }
|
|
596
|
+
});
|
|
597
|
+
else if (outcome === "completed")
|
|
598
|
+
events.push({ protocolVersion: 2, type: "run.completed", payload: {} });
|
|
599
|
+
else
|
|
600
|
+
events.push({
|
|
601
|
+
protocolVersion: 2,
|
|
602
|
+
type: "run.failed",
|
|
603
|
+
payload: {
|
|
604
|
+
message: message ?? "OpenClaw Gateway agent run failed",
|
|
605
|
+
adapterType: "openclaw"
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
return events;
|
|
609
|
+
};
|
|
558
610
|
try {
|
|
559
611
|
const hello = await client.connect();
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
612
|
+
const contract = resolveGatewayContract(hello);
|
|
613
|
+
const policy = resolveGatewayPermission(contract, req.approvalMode);
|
|
614
|
+
permissions.options.effectiveApprovalMode = policy.effective;
|
|
615
|
+
// 参数在真实握手之后生成;probe 的版本快照不作为本次执行依据。
|
|
616
|
+
const createParams = gatewayCreateParams(contract, policy, invocation.createParams, invocation.requestId, invocation.agentParams.thinking);
|
|
617
|
+
if (policy.fallbackReason)
|
|
618
|
+
yield {
|
|
619
|
+
protocolVersion: 2,
|
|
620
|
+
type: "run.notice",
|
|
621
|
+
payload: {
|
|
622
|
+
noticeId: `${req.invocationId}:openclaw-permission`,
|
|
623
|
+
code: "provider_diagnostic",
|
|
624
|
+
severity: "warning",
|
|
625
|
+
params: {
|
|
626
|
+
adapter: "openclaw",
|
|
627
|
+
diagnosticId: `openclaw-compat-${randomUUID()}`,
|
|
628
|
+
category: "permission_fallback",
|
|
629
|
+
message: `OpenClaw ${contract.version}: requested ask; using full_access for this invocation (${policy.fallbackReason}).`
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
};
|
|
572
633
|
await assertExistingAgent(client, invocation.agentId, req.signal);
|
|
573
|
-
const created = asRecord(await client.request("sessions.create",
|
|
634
|
+
const created = asRecord(await client.request("sessions.create", createParams, {
|
|
574
635
|
timeoutMs: 30_000,
|
|
575
636
|
signal: req.signal
|
|
576
637
|
}));
|
|
577
|
-
|
|
638
|
+
const createdKey = nonEmptyString(created?.key);
|
|
639
|
+
if (!createdKey)
|
|
640
|
+
throw new Error("OpenClaw Gateway returned no created session key");
|
|
641
|
+
providerSessionKey = createdKey;
|
|
578
642
|
invocation.agentParams.sessionKey = providerSessionKey;
|
|
643
|
+
if (contract.family === "legacy") {
|
|
644
|
+
const patched = asRecord(await client.request("sessions.patch", gatewayLegacyPermissionParams(providerSessionKey), {
|
|
645
|
+
timeoutMs: 10_000,
|
|
646
|
+
signal: req.signal
|
|
647
|
+
}));
|
|
648
|
+
const entry = asRecord(patched?.entry);
|
|
649
|
+
if (patched?.ok !== true ||
|
|
650
|
+
patched.key !== providerSessionKey ||
|
|
651
|
+
entry?.execSecurity !== "full" ||
|
|
652
|
+
entry.execAsk !== "off") {
|
|
653
|
+
throw new Error("OPENCLAW_PERMISSION_UNAVAILABLE: legacy session policy was not confirmed");
|
|
654
|
+
}
|
|
655
|
+
}
|
|
579
656
|
await client.request("sessions.messages.subscribe", {
|
|
580
657
|
key: providerSessionKey,
|
|
581
658
|
agentId: invocation.agentId
|
|
582
659
|
}, { timeoutMs: 10_000, signal: req.signal });
|
|
583
|
-
if (
|
|
660
|
+
if (policy.effective === "ask") {
|
|
584
661
|
const pending = await client.request("exec.approval.list", {}, { timeoutMs: 10_000, signal: req.signal });
|
|
585
662
|
if (!Array.isArray(pending))
|
|
586
663
|
throw new Error("OpenClaw Gateway returned invalid pending approvals");
|
|
@@ -598,6 +675,9 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
598
675
|
}
|
|
599
676
|
}
|
|
600
677
|
};
|
|
678
|
+
if (req.signal?.aborted)
|
|
679
|
+
throw new Error("OpenClaw invocation aborted before dispatch");
|
|
680
|
+
agentDispatched = true;
|
|
601
681
|
const request = client.request("agent", invocation.agentParams, {
|
|
602
682
|
expectFinal: true,
|
|
603
683
|
timeoutMs: null,
|
|
@@ -612,18 +692,46 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
612
692
|
void request.then((result) => queue.push({ done: true, result }), (error) => queue.push({ error: error instanceof Error ? error : new Error(String(error)) }));
|
|
613
693
|
while (!terminal) {
|
|
614
694
|
const next = await iterator.next();
|
|
695
|
+
if (terminal)
|
|
696
|
+
break;
|
|
615
697
|
if (next.done)
|
|
616
698
|
throw new Error("OpenClaw event queue closed before terminal");
|
|
617
699
|
const item = next.value;
|
|
618
700
|
if (item.error)
|
|
619
701
|
throw item.error;
|
|
620
702
|
if (item.done) {
|
|
621
|
-
const reconciled = normalizeFinalResponse(item.result, state);
|
|
703
|
+
const reconciled = normalizeFinalResponse(item.result, state, acceptedRunId);
|
|
704
|
+
terminal = reconciled.terminal;
|
|
705
|
+
// 先保存原生终态,避免消费者在接收收尾事件时取消而覆盖真实结果。
|
|
622
706
|
for (const event of reconciled.events)
|
|
623
707
|
yield event;
|
|
624
|
-
terminal
|
|
708
|
+
if (!terminal) {
|
|
709
|
+
const confirmed = await waitForConfirmedTerminal(client, acceptedRunId, undefined, invocation.reconciliationDeadlineAt);
|
|
710
|
+
if (resourceFailure)
|
|
711
|
+
throw resourceFailure;
|
|
712
|
+
terminal ??= confirmed;
|
|
713
|
+
if (!state.stepFinished) {
|
|
714
|
+
if (!state.stepStarted) {
|
|
715
|
+
state.stepStarted = true;
|
|
716
|
+
yield {
|
|
717
|
+
protocolVersion: 2,
|
|
718
|
+
type: "model_step.started",
|
|
719
|
+
payload: { stepId: state.stepId }
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
state.stepFinished = true;
|
|
723
|
+
yield {
|
|
724
|
+
protocolVersion: 2,
|
|
725
|
+
type: "model_step.finished",
|
|
726
|
+
payload: {
|
|
727
|
+
stepId: state.stepId,
|
|
728
|
+
status: canonicalTerminalStatus(terminal)
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
625
733
|
if (terminal === "failed")
|
|
626
|
-
throw new Error(reconciled.error);
|
|
734
|
+
throw new Error(reconciled.error ?? "OpenClaw Gateway agent run failed");
|
|
627
735
|
break;
|
|
628
736
|
}
|
|
629
737
|
if (item.event?.event === "exec.approval.resolved") {
|
|
@@ -718,151 +826,58 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
718
826
|
const runId = nonEmptyString(native?.runId);
|
|
719
827
|
if (!runId || (runId !== acceptedRunId && runId !== invocation.requestId))
|
|
720
828
|
continue;
|
|
829
|
+
const nativeTerminal = terminalState(item.event.payload);
|
|
830
|
+
if (nativeTerminal) {
|
|
831
|
+
const confirmed = await waitForConfirmedTerminal(client, runId, nativeTerminal, invocation.reconciliationDeadlineAt);
|
|
832
|
+
if (resourceFailure)
|
|
833
|
+
throw resourceFailure;
|
|
834
|
+
// await 期间取消任务可能先完成;较早发出的等待不能覆盖较新的确认。
|
|
835
|
+
terminal ??= confirmed;
|
|
836
|
+
if (terminal !== nativeTerminal)
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
721
839
|
const events = normalizeGatewayAgentEvent(item.event.payload, state);
|
|
722
840
|
for (const event of events)
|
|
723
841
|
yield event;
|
|
724
|
-
terminal
|
|
725
|
-
|
|
726
|
-
await waitForConfirmedTerminal(client, runId, terminal, invocation.reconciliationDeadlineAt);
|
|
727
|
-
if (terminal === "failed")
|
|
728
|
-
throw new Error(terminalError(item.event.payload));
|
|
729
|
-
}
|
|
842
|
+
if (terminal === "failed")
|
|
843
|
+
throw new Error(terminalError(item.event.payload));
|
|
730
844
|
}
|
|
731
|
-
if (
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
type: "message.update",
|
|
735
|
-
payload: {
|
|
736
|
-
...gatewayMessage(state, "final", state.assistantItemId),
|
|
737
|
-
role: "assistant",
|
|
738
|
-
status: "stopped",
|
|
739
|
-
text: state.assistantText,
|
|
740
|
-
channel: "final"
|
|
741
|
-
}
|
|
742
|
-
};
|
|
743
|
-
yield {
|
|
744
|
-
protocolVersion: 2,
|
|
745
|
-
type: "run.cancelled",
|
|
746
|
-
payload: { reason: String(req.signal?.reason ?? "openclaw_aborted") }
|
|
747
|
-
};
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
if (terminal !== "completed")
|
|
845
|
+
if (resourceFailure)
|
|
846
|
+
throw resourceFailure;
|
|
847
|
+
if (terminal !== "completed" && terminal !== "cancelled")
|
|
751
848
|
throw new Error("OpenClaw Gateway stream ended without a terminal lifecycle event");
|
|
752
|
-
yield
|
|
753
|
-
protocolVersion: 2,
|
|
754
|
-
type: "message.update",
|
|
755
|
-
payload: {
|
|
756
|
-
...gatewayMessage(state, "final", state.assistantItemId),
|
|
757
|
-
role: "assistant",
|
|
758
|
-
status: "completed",
|
|
759
|
-
text: state.assistantText,
|
|
760
|
-
channel: "final"
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
yield { protocolVersion: 2, type: "run.completed", payload: {} };
|
|
849
|
+
yield* terminalEvents(terminal);
|
|
764
850
|
}
|
|
765
851
|
catch (error) {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
status: "stopped",
|
|
775
|
-
text: state.assistantText,
|
|
776
|
-
channel: "final"
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
yield {
|
|
780
|
-
protocolVersion: 2,
|
|
781
|
-
type: "run.cancelled",
|
|
782
|
-
payload: { reason: String(req.signal.reason ?? "openclaw_aborted_before_dispatch") }
|
|
783
|
-
};
|
|
852
|
+
let message = resourceFailure?.message ?? (error instanceof Error ? error.message : String(error));
|
|
853
|
+
if (!resourceFailure && terminal) {
|
|
854
|
+
yield* terminalEvents(terminal, message);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (!resourceFailure && req.signal?.aborted) {
|
|
858
|
+
if (!agentDispatched) {
|
|
859
|
+
yield* terminalEvents("cancelled");
|
|
784
860
|
return;
|
|
785
861
|
}
|
|
786
|
-
let recoveryClient;
|
|
787
862
|
try {
|
|
788
|
-
|
|
789
|
-
key: providerSessionKey,
|
|
790
|
-
runId: acceptedRunId,
|
|
791
|
-
agentId: invocation.agentId
|
|
792
|
-
};
|
|
793
|
-
let controlClient = client;
|
|
794
|
-
try {
|
|
795
|
-
await controlClient.request("sessions.abort", abortParams, { timeoutMs: 5_000 });
|
|
796
|
-
}
|
|
797
|
-
catch {
|
|
798
|
-
recoveryClient = clientFactory(invocation.gateway);
|
|
799
|
-
assertRuntimeGatewayContract(await recoveryClient.connect());
|
|
800
|
-
controlClient = recoveryClient;
|
|
801
|
-
await controlClient.request("sessions.abort", abortParams, { timeoutMs: 5_000 });
|
|
802
|
-
}
|
|
803
|
-
await waitForConfirmedTerminal(controlClient, acceptedRunId, "cancelled", Date.now() + 30_000);
|
|
804
|
-
yield {
|
|
805
|
-
protocolVersion: 2,
|
|
806
|
-
type: "message.update",
|
|
807
|
-
payload: {
|
|
808
|
-
...gatewayMessage(state, "final", state.assistantItemId),
|
|
809
|
-
role: "assistant",
|
|
810
|
-
status: "stopped",
|
|
811
|
-
text: state.assistantText,
|
|
812
|
-
channel: "final"
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
yield {
|
|
816
|
-
protocolVersion: 2,
|
|
817
|
-
type: "run.cancelled",
|
|
818
|
-
payload: { reason: String(req.signal.reason ?? "openclaw_aborted") }
|
|
819
|
-
};
|
|
863
|
+
await confirmCancellation();
|
|
820
864
|
}
|
|
821
865
|
catch (abortError) {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
protocolVersion: 2,
|
|
827
|
-
type: "message.update",
|
|
828
|
-
payload: {
|
|
829
|
-
...gatewayMessage(state, "final", state.assistantItemId),
|
|
830
|
-
role: "assistant",
|
|
831
|
-
status: "failed",
|
|
832
|
-
text: state.assistantText,
|
|
833
|
-
channel: "final"
|
|
834
|
-
}
|
|
835
|
-
};
|
|
836
|
-
yield {
|
|
837
|
-
protocolVersion: 2,
|
|
838
|
-
type: "run.failed",
|
|
839
|
-
payload: { message, adapterType: "openclaw" }
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
finally {
|
|
843
|
-
await recoveryClient?.close();
|
|
866
|
+
message =
|
|
867
|
+
abortError instanceof Error
|
|
868
|
+
? abortError.message
|
|
869
|
+
: "OpenClaw Gateway abort confirmation failed";
|
|
844
870
|
}
|
|
871
|
+
yield* terminalEvents(terminal ?? "failed", message);
|
|
845
872
|
return;
|
|
846
873
|
}
|
|
847
|
-
|
|
848
|
-
if (!resourceFailure && agentAccepted && terminal !== "failed" && terminal !== "cancelled") {
|
|
874
|
+
if (!resourceFailure && agentAccepted) {
|
|
849
875
|
const recoveryClient = clientFactory(invocation.gateway);
|
|
850
876
|
try {
|
|
851
|
-
|
|
852
|
-
await waitForConfirmedTerminal(recoveryClient, acceptedRunId,
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
type: "message.update",
|
|
856
|
-
payload: {
|
|
857
|
-
...gatewayMessage(state, "final", state.assistantItemId),
|
|
858
|
-
role: "assistant",
|
|
859
|
-
status: "completed",
|
|
860
|
-
text: state.assistantText,
|
|
861
|
-
channel: "final"
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
yield { protocolVersion: 2, type: "run.completed", payload: {} };
|
|
865
|
-
return;
|
|
877
|
+
resolveGatewayContract(await recoveryClient.connect());
|
|
878
|
+
const confirmed = await waitForConfirmedTerminal(recoveryClient, acceptedRunId, undefined, invocation.reconciliationDeadlineAt);
|
|
879
|
+
terminal ??= confirmed;
|
|
880
|
+
message = `OpenClaw Gateway confirmed a failed run after disconnect: ${message}`;
|
|
866
881
|
}
|
|
867
882
|
catch (recoveryError) {
|
|
868
883
|
const detail = recoveryError instanceof Error ? recoveryError.message : "unknown reconciliation failure";
|
|
@@ -872,18 +887,7 @@ async function* invokeOpenClawGateway(req, invocation, context, clientFactory, p
|
|
|
872
887
|
await recoveryClient.close();
|
|
873
888
|
}
|
|
874
889
|
}
|
|
875
|
-
yield
|
|
876
|
-
protocolVersion: 2,
|
|
877
|
-
type: "message.update",
|
|
878
|
-
payload: {
|
|
879
|
-
...gatewayMessage(state, "final", state.assistantItemId),
|
|
880
|
-
role: "assistant",
|
|
881
|
-
status: "failed",
|
|
882
|
-
text: state.assistantText,
|
|
883
|
-
channel: "final"
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
yield { protocolVersion: 2, type: "run.failed", payload: { message, adapterType: "openclaw" } };
|
|
890
|
+
yield* terminalEvents(resourceFailure ? "failed" : (terminal ?? "failed"), resourceFailure?.message ?? message);
|
|
887
891
|
}
|
|
888
892
|
finally {
|
|
889
893
|
req.signal?.removeEventListener("abort", onAbort);
|
|
@@ -900,7 +904,7 @@ export function createOpenClawAdapter({ clientFactory = (options) => new OpenCla
|
|
|
900
904
|
let client;
|
|
901
905
|
try {
|
|
902
906
|
client = clientFactory(await resolveOpenClawGateway(options?.providerOptions));
|
|
903
|
-
|
|
907
|
+
resolveGatewayContract(await client.connect());
|
|
904
908
|
await client.request("agents.list", {});
|
|
905
909
|
return { available: true, command: "openclaw-gateway" };
|
|
906
910
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mininglamp-oss/octo-runtime-adapter-openclaw",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./dist/index.js",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@mininglamp-oss/octo-runtime-protocol": "1.1.
|
|
9
|
-
"@mininglamp-oss/octo-runtime-adapter-sdk": "1.1.
|
|
8
|
+
"@mininglamp-oss/octo-runtime-protocol": "1.1.3",
|
|
9
|
+
"@mininglamp-oss/octo-runtime-adapter-sdk": "1.1.3",
|
|
10
10
|
"ws": "8.22.0"
|
|
11
11
|
},
|
|
12
12
|
"types": "./dist/index.d.ts",
|