@scotthuang/agent-knock-knock 0.10.1 → 0.10.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/CHANGELOG.md +21 -0
- package/README.md +12 -17
- package/dist/src/claude-local-transcript-provider.d.ts +16 -0
- package/dist/src/claude-local-transcript-provider.js +111 -47
- package/dist/src/claude-local-transcript-provider.js.map +1 -1
- package/dist/src/cli.js +3135 -489
- package/dist/src/cli.js.map +1 -1
- package/dist/src/live-lifecycle-smoke.js +7 -2
- package/dist/src/live-lifecycle-smoke.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +1 -0
- package/dist/src/openclaw-plugin-helpers.js +30 -2
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin.d.ts +3 -1
- package/dist/src/openclaw-plugin.js +251 -53
- package/dist/src/openclaw-plugin.js.map +1 -1
- package/dist/src/store.d.ts +6 -0
- package/dist/src/store.js +15 -1
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +31 -2
- package/dist/src/terminal-agent-bridge.js +231 -11
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +118 -0
- package/dist/src/terminal-submission-acceptance.js +785 -0
- package/dist/src/terminal-submission-acceptance.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
4
5
|
import { EXECUTOR_KINDS, executorDefinitionForKind } from "./executors.js";
|
|
5
|
-
import { AKK_CALLBACK_METHOD, akkUsageText, buildAkkCommandCliArgs, formatAkkListCommandResult, formatAkkRespondCommandResult, formatAkkThreadsCommandResult, formatAkkThreadTransitionCommandResult, isAkkThreadTransitionSuccess, parseAkkCommand, resolvePluginStoreDir } from "./openclaw-plugin-helpers.js";
|
|
6
|
+
import { AKK_CALLBACK_METHOD, akkUsageText, buildAkkCommandCliArgs, formatAkkListCommandResult, formatAkkRespondCommandResult, formatAkkThreadsCommandResult, formatAkkThreadTransitionCommandResult, isAkkNativeSubmissionAccepted, isAkkThreadTransitionSuccess, parseAkkCommand, resolvePluginStoreDir } from "./openclaw-plugin-helpers.js";
|
|
6
7
|
import { attemptAutoApproval } from "./approval-policy.js";
|
|
7
8
|
const CALLBACK_METHOD = AKK_CALLBACK_METHOD;
|
|
9
|
+
const MONITOR_SUPERVISOR_INTERVAL_MS = 5_000;
|
|
8
10
|
const defaultBinPath = fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
9
11
|
const relayPathByApi = new WeakMap();
|
|
10
12
|
const sendParameters = {
|
|
@@ -310,7 +312,7 @@ const approveParameters = {
|
|
|
310
312
|
}
|
|
311
313
|
}
|
|
312
314
|
};
|
|
313
|
-
function createPlugin(relayPath) {
|
|
315
|
+
function createPlugin(relayPath, { monitorSupervisorIntervalMs = MONITOR_SUPERVISOR_INTERVAL_MS } = {}) {
|
|
314
316
|
return definePluginEntry({
|
|
315
317
|
id: "agent-knock-knock",
|
|
316
318
|
name: "Agent Knock Knock",
|
|
@@ -332,25 +334,7 @@ function createPlugin(relayPath) {
|
|
|
332
334
|
}
|
|
333
335
|
}, { scope: "operator.write" });
|
|
334
336
|
try {
|
|
335
|
-
api.registerService?.(
|
|
336
|
-
id: "agent-knock-knock-monitor-reconciliation",
|
|
337
|
-
start() {
|
|
338
|
-
try {
|
|
339
|
-
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
340
|
-
const args = ["reconcile-monitors"];
|
|
341
|
-
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
342
|
-
const result = runCli(api, args);
|
|
343
|
-
api.logger.info?.("agent-knock-knock monitor reconciliation: " +
|
|
344
|
-
`checked=${result.checked ?? 0} launched=${result.launched ?? 0} ` +
|
|
345
|
-
`already_running=${result.already_running ?? 0} skipped=${result.skipped ?? 0} ` +
|
|
346
|
-
`errors=${result.errors ?? 0}`);
|
|
347
|
-
}
|
|
348
|
-
catch (error) {
|
|
349
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
350
|
-
api.logger.warn?.(`agent-knock-knock monitor reconciliation skipped after startup error: ${message}`);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
});
|
|
337
|
+
api.registerService?.(createMonitorReconciliationService(api, monitorSupervisorIntervalMs));
|
|
354
338
|
}
|
|
355
339
|
catch (error) {
|
|
356
340
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -465,8 +449,13 @@ function createPlugin(relayPath) {
|
|
|
465
449
|
name: "agent_knock_knock_send",
|
|
466
450
|
description: "Start a new turn in an existing AKK session without clearing the coding agent's native context. Target the exact session_id from list or a prior send; never pass a turn id or discovery selector as session_id. selector is compatibility-only for initial live-terminal discovery: use one explicitly named by the user or prefilled by list, and never infer one. Both fields may be omitted only when AKK should require one unique eligible idle pane. To answer an in-flight question, use agent_knock_knock_respond instead. For ordinary use add only request and omit monitoring timeouts unless the user explicitly asks to change them. timeoutSeconds is unsupported. AKK never starts a coding agent. This is asynchronous: after acceptance, yield and wait for the callback or a later explicit status request.",
|
|
467
451
|
parameters: sendParameters,
|
|
468
|
-
async execute(
|
|
469
|
-
const result = await runSendRequest(api, isRecord(params) ? params : {}, toolContext
|
|
452
|
+
async execute(toolCallId, params) {
|
|
453
|
+
const result = await runSendRequest(api, isRecord(params) ? params : {}, toolContext, terminalMessageIdForToolCall({
|
|
454
|
+
toolCallId,
|
|
455
|
+
sessionKey: toolContext?.sessionKey,
|
|
456
|
+
sessionId: toolContext?.sessionId,
|
|
457
|
+
toolName: "agent_knock_knock_send"
|
|
458
|
+
}));
|
|
470
459
|
return toolResult(result, { submissionErrors: true });
|
|
471
460
|
}
|
|
472
461
|
}), { name: "agent_knock_knock_send", optional: true });
|
|
@@ -474,8 +463,9 @@ function createPlugin(relayPath) {
|
|
|
474
463
|
name: "agent_knock_knock_respond",
|
|
475
464
|
description: "Respond to a question or blocked callback in one exact in-flight AKK turn. This continues that turn and does not create a new turn; use agent_knock_knock_send with session_id for later ordinary work.",
|
|
476
465
|
parameters: respondParameters,
|
|
477
|
-
buildArgs: (params) => {
|
|
466
|
+
buildArgs: (params, toolContext, toolCallId) => {
|
|
478
467
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
468
|
+
const openclawSession = stringValue(toolContext?.sessionKey) ?? "agent:main:main";
|
|
479
469
|
const args = [
|
|
480
470
|
"respond",
|
|
481
471
|
"--turn",
|
|
@@ -483,7 +473,14 @@ function createPlugin(relayPath) {
|
|
|
483
473
|
"--message",
|
|
484
474
|
requiredString(params.request, "request")
|
|
485
475
|
];
|
|
476
|
+
pushOptional(args, "--message-id", terminalMessageIdForToolCall({
|
|
477
|
+
toolCallId,
|
|
478
|
+
sessionKey: openclawSession,
|
|
479
|
+
sessionId: toolContext?.sessionId,
|
|
480
|
+
toolName: "agent_knock_knock_respond"
|
|
481
|
+
}));
|
|
486
482
|
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
483
|
+
pushOptional(args, "--openclaw-session", openclawSession);
|
|
487
484
|
return args;
|
|
488
485
|
}
|
|
489
486
|
});
|
|
@@ -558,9 +555,89 @@ function createPlugin(relayPath) {
|
|
|
558
555
|
}
|
|
559
556
|
});
|
|
560
557
|
}
|
|
558
|
+
function createMonitorReconciliationService(api, configuredIntervalMs) {
|
|
559
|
+
const intervalMs = Number.isFinite(configuredIntervalMs) &&
|
|
560
|
+
configuredIntervalMs > 0
|
|
561
|
+
? Math.max(50, Math.ceil(configuredIntervalMs))
|
|
562
|
+
: MONITOR_SUPERVISOR_INTERVAL_MS;
|
|
563
|
+
let stopped = true;
|
|
564
|
+
let timer;
|
|
565
|
+
let inFlight;
|
|
566
|
+
const reconciliationArgs = (reason) => {
|
|
567
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
568
|
+
const args = ["reconcile-monitors", "--reason", reason];
|
|
569
|
+
if (reason === "monitor_supervision") {
|
|
570
|
+
args.push("--terminal-monitors-only");
|
|
571
|
+
}
|
|
572
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
573
|
+
return args;
|
|
574
|
+
};
|
|
575
|
+
const report = (result, reason) => {
|
|
576
|
+
if (reason === "startup_reconciliation" ||
|
|
577
|
+
Number(result.launched ?? 0) > 0 ||
|
|
578
|
+
Number(result.errors ?? 0) > 0) {
|
|
579
|
+
const label = reason === "startup_reconciliation"
|
|
580
|
+
? "monitor reconciliation"
|
|
581
|
+
: `monitor ${reason}`;
|
|
582
|
+
api.logger.info?.(`agent-knock-knock ${label}: ` +
|
|
583
|
+
`checked=${result.checked ?? 0} launched=${result.launched ?? 0} ` +
|
|
584
|
+
`already_running=${result.already_running ?? 0} skipped=${result.skipped ?? 0} ` +
|
|
585
|
+
`errors=${result.errors ?? 0}`);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
const schedule = () => {
|
|
589
|
+
if (stopped) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
timer = setTimeout(() => {
|
|
593
|
+
timer = undefined;
|
|
594
|
+
inFlight = supervise().finally(() => {
|
|
595
|
+
inFlight = undefined;
|
|
596
|
+
schedule();
|
|
597
|
+
});
|
|
598
|
+
}, intervalMs);
|
|
599
|
+
timer.unref?.();
|
|
600
|
+
};
|
|
601
|
+
const supervise = async () => {
|
|
602
|
+
try {
|
|
603
|
+
const result = await runCliAsync(api, reconciliationArgs("monitor_supervision"));
|
|
604
|
+
report(result, "monitor_supervision");
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
608
|
+
api.logger.warn?.(`agent-knock-knock monitor supervision deferred after error: ${message}`);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
return {
|
|
612
|
+
id: "agent-knock-knock-monitor-reconciliation",
|
|
613
|
+
start() {
|
|
614
|
+
if (!stopped) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
stopped = false;
|
|
618
|
+
try {
|
|
619
|
+
const result = runCli(api, reconciliationArgs("startup_reconciliation"));
|
|
620
|
+
report(result, "startup_reconciliation");
|
|
621
|
+
}
|
|
622
|
+
catch (error) {
|
|
623
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
624
|
+
api.logger.warn?.(`agent-knock-knock monitor reconciliation skipped after startup error: ${message}`);
|
|
625
|
+
}
|
|
626
|
+
schedule();
|
|
627
|
+
},
|
|
628
|
+
async stop() {
|
|
629
|
+
stopped = true;
|
|
630
|
+
if (timer) {
|
|
631
|
+
clearTimeout(timer);
|
|
632
|
+
timer = undefined;
|
|
633
|
+
}
|
|
634
|
+
await inFlight;
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
561
638
|
const plugin = createPlugin(defaultBinPath);
|
|
562
|
-
export function createOpenClawPluginForTest(relayPath) {
|
|
563
|
-
return createPlugin(relayPath);
|
|
639
|
+
export function createOpenClawPluginForTest(relayPath, options = {}) {
|
|
640
|
+
return createPlugin(relayPath, options);
|
|
564
641
|
}
|
|
565
642
|
export default plugin;
|
|
566
643
|
async function handleAkkCommand(api, ctx) {
|
|
@@ -647,7 +724,9 @@ async function handleAkkCommand(api, ctx) {
|
|
|
647
724
|
case "send":
|
|
648
725
|
return {
|
|
649
726
|
text: formatSendCommandResult(result),
|
|
650
|
-
isError:
|
|
727
|
+
isError: terminalSubmissionReported(result)
|
|
728
|
+
? !isAkkNativeSubmissionAccepted(result)
|
|
729
|
+
: result.status === "delivered_unfenced"
|
|
651
730
|
};
|
|
652
731
|
case "respond":
|
|
653
732
|
return formatAkkRespondCommandResult(result);
|
|
@@ -694,11 +773,30 @@ function formatDelegateCommandResult(result) {
|
|
|
694
773
|
].join("\n");
|
|
695
774
|
}
|
|
696
775
|
if (result.status === "submission_aborted") {
|
|
776
|
+
const safeToRetry = result.safe_to_retry === true && result.do_not_retry !== true;
|
|
697
777
|
return [
|
|
698
778
|
`AKK stopped before sending the terminal task to ${agent}.`,
|
|
699
779
|
`session: ${sessionId}`,
|
|
700
780
|
`turn: ${turnId}`,
|
|
701
|
-
|
|
781
|
+
safeToRetry
|
|
782
|
+
? "next: no tmux input was sent and the aborted receipt is durable, so this request may be retried."
|
|
783
|
+
: "next: do not retry automatically; AKK could not prove a durable safe abort, so inspect this Turn and its tmux dispatch ledger."
|
|
784
|
+
].join("\n");
|
|
785
|
+
}
|
|
786
|
+
if (result.status === "submission_pending_acceptance") {
|
|
787
|
+
return [
|
|
788
|
+
`AKK dispatched terminal input to ${agent}, but native acceptance is still pending.`,
|
|
789
|
+
`session: ${sessionId}`,
|
|
790
|
+
`turn: ${turnId}`,
|
|
791
|
+
"next: do not retry or report success; wait for acceptance or inspect the shared tmux pane."
|
|
792
|
+
].join("\n");
|
|
793
|
+
}
|
|
794
|
+
if (result.status === "submission_not_accepted") {
|
|
795
|
+
return [
|
|
796
|
+
`AKK proved that ${agent} did not accept the terminal draft.`,
|
|
797
|
+
`session: ${sessionId}`,
|
|
798
|
+
`turn: ${turnId}`,
|
|
799
|
+
"next: do not retry automatically; inspect the exact draft in the shared tmux pane."
|
|
702
800
|
].join("\n");
|
|
703
801
|
}
|
|
704
802
|
return [
|
|
@@ -843,12 +941,43 @@ function formatSendCommandResult(result) {
|
|
|
843
941
|
].join("\n");
|
|
844
942
|
}
|
|
845
943
|
if (result.submission_outcome === "aborted") {
|
|
944
|
+
const safeToRetry = result.safe_to_retry === true && result.do_not_retry !== true;
|
|
846
945
|
return [
|
|
847
946
|
"AKK terminal submission was aborted before tmux input.",
|
|
848
947
|
`session: ${sessionId}`,
|
|
849
948
|
`turn: ${turnId}`,
|
|
850
949
|
`status: ${result.status ?? status}`,
|
|
851
|
-
|
|
950
|
+
safeToRetry
|
|
951
|
+
? "next: this request was not sent, the aborted receipt is durable, and it may be retried."
|
|
952
|
+
: "next: do not retry automatically; inspect the Turn because a durable safe abort was not proven."
|
|
953
|
+
].join("\n");
|
|
954
|
+
}
|
|
955
|
+
if (result.submission_outcome === "pending_acceptance") {
|
|
956
|
+
return [
|
|
957
|
+
"AKK dispatched the terminal input but native acceptance is still pending.",
|
|
958
|
+
`session: ${sessionId}`,
|
|
959
|
+
`turn: ${turnId}`,
|
|
960
|
+
`status: ${result.status ?? status}`,
|
|
961
|
+
"next: do not retry or report success; wait for acceptance or inspect the shared tmux pane."
|
|
962
|
+
].join("\n");
|
|
963
|
+
}
|
|
964
|
+
if (result.submission_outcome === "not_accepted") {
|
|
965
|
+
return [
|
|
966
|
+
"AKK proved that the agent did not accept the terminal draft.",
|
|
967
|
+
`session: ${sessionId}`,
|
|
968
|
+
`turn: ${turnId}`,
|
|
969
|
+
`status: ${result.status ?? status}`,
|
|
970
|
+
"next: do not retry automatically; inspect the exact draft in the shared tmux pane."
|
|
971
|
+
].join("\n");
|
|
972
|
+
}
|
|
973
|
+
if (terminalSubmissionReported(result) &&
|
|
974
|
+
!isAkkNativeSubmissionAccepted(result)) {
|
|
975
|
+
return [
|
|
976
|
+
"AKK could not verify native agent acceptance for this terminal input.",
|
|
977
|
+
`session: ${sessionId}`,
|
|
978
|
+
`turn: ${turnId}`,
|
|
979
|
+
`status: ${result.status ?? status}`,
|
|
980
|
+
"next: do not retry or report success; inspect the exact Turn receipt and shared tmux pane."
|
|
852
981
|
].join("\n");
|
|
853
982
|
}
|
|
854
983
|
const lines = [
|
|
@@ -977,7 +1106,7 @@ function terminalScreenExcerpt(result) {
|
|
|
977
1106
|
? text
|
|
978
1107
|
: `…${text.slice(-1599)}`;
|
|
979
1108
|
}
|
|
980
|
-
async function runSendRequest(api, params, toolContext) {
|
|
1109
|
+
async function runSendRequest(api, params, toolContext, messageId) {
|
|
981
1110
|
const requestedType = Object.hasOwn(params, "type")
|
|
982
1111
|
? stringValue(params.type)
|
|
983
1112
|
: "task";
|
|
@@ -994,7 +1123,7 @@ async function runSendRequest(api, params, toolContext) {
|
|
|
994
1123
|
? requiredString(params.selector, "selector")
|
|
995
1124
|
: undefined;
|
|
996
1125
|
if (!sessionId && !selector) {
|
|
997
|
-
return runDelegate(api, params, toolContext);
|
|
1126
|
+
return runDelegate(api, { ...params, messageId }, toolContext);
|
|
998
1127
|
}
|
|
999
1128
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
1000
1129
|
const openclawSession = stringValue(toolContext?.sessionKey) ??
|
|
@@ -1010,6 +1139,7 @@ async function runSendRequest(api, params, toolContext) {
|
|
|
1010
1139
|
}
|
|
1011
1140
|
args.push("--message", requiredString(params.request, "request"), "--background");
|
|
1012
1141
|
pushOptional(args, "--type", stringValue(params.type));
|
|
1142
|
+
pushOptional(args, "--message-id", messageId);
|
|
1013
1143
|
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
1014
1144
|
pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ??
|
|
1015
1145
|
numberString(config.idleTimeoutMinutes));
|
|
@@ -1046,14 +1176,24 @@ function isSubmissionError(result) {
|
|
|
1046
1176
|
if (!isRecord(result)) {
|
|
1047
1177
|
return false;
|
|
1048
1178
|
}
|
|
1179
|
+
if (terminalSubmissionReported(result)) {
|
|
1180
|
+
return !isAkkNativeSubmissionAccepted(result);
|
|
1181
|
+
}
|
|
1049
1182
|
return [
|
|
1050
1183
|
"submission_unfenced",
|
|
1051
1184
|
"submission_uncertain",
|
|
1052
|
-
"submission_aborted"
|
|
1185
|
+
"submission_aborted",
|
|
1186
|
+
"submission_pending_acceptance",
|
|
1187
|
+
"submission_not_accepted"
|
|
1053
1188
|
].includes(String(result.status ?? "")) ||
|
|
1054
|
-
["uncertain", "aborted"].includes(String(result.submission_outcome ?? "")) ||
|
|
1189
|
+
["uncertain", "aborted", "pending_acceptance", "not_accepted"].includes(String(result.submission_outcome ?? "")) ||
|
|
1055
1190
|
result.status === "delivered_unfenced";
|
|
1056
1191
|
}
|
|
1192
|
+
function terminalSubmissionReported(result) {
|
|
1193
|
+
return result.submission_outcome !== undefined ||
|
|
1194
|
+
result.delivery_receipt !== undefined ||
|
|
1195
|
+
result.delivered !== undefined;
|
|
1196
|
+
}
|
|
1057
1197
|
function withTurnIdentity(result) {
|
|
1058
1198
|
if (!isRecord(result)) {
|
|
1059
1199
|
return result;
|
|
@@ -1126,6 +1266,7 @@ async function runDelegate(api, params, toolContext) {
|
|
|
1126
1266
|
"--background"
|
|
1127
1267
|
];
|
|
1128
1268
|
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
1269
|
+
pushOptional(args, "--message-id", stringValue(params.messageId));
|
|
1129
1270
|
pushOptional(args, "--openclaw-session", openclawSession);
|
|
1130
1271
|
pushOptional(args, "--gateway-method", CALLBACK_METHOD);
|
|
1131
1272
|
pushOptional(args, "--gateway-session", openclawSession);
|
|
@@ -1159,6 +1300,14 @@ async function runDelegate(api, params, toolContext) {
|
|
|
1159
1300
|
parsedPaths?.logPath;
|
|
1160
1301
|
const submissionUncertain = parsed.submission_outcome === "uncertain";
|
|
1161
1302
|
const submissionAborted = parsed.submission_outcome === "aborted";
|
|
1303
|
+
const submissionAccepted = parsed.submission_outcome === "agent_accepted" &&
|
|
1304
|
+
parsed.delivery_receipt === "agent_accepted" &&
|
|
1305
|
+
parsed.delivered === true;
|
|
1306
|
+
const submissionNotAccepted = parsed.submission_outcome === "not_accepted";
|
|
1307
|
+
const submissionPending = !submissionAccepted &&
|
|
1308
|
+
!submissionUncertain &&
|
|
1309
|
+
!submissionAborted &&
|
|
1310
|
+
!submissionNotAccepted;
|
|
1162
1311
|
const submissionUnfenced = parsed.status === "delivered_unfenced";
|
|
1163
1312
|
const agent = (isRecord(parsedConversation?.executor)
|
|
1164
1313
|
? stringValue(parsedConversation.executor.kind)
|
|
@@ -1171,14 +1320,22 @@ async function runDelegate(api, params, toolContext) {
|
|
|
1171
1320
|
? "submission_uncertain"
|
|
1172
1321
|
: submissionAborted
|
|
1173
1322
|
? "submission_aborted"
|
|
1174
|
-
:
|
|
1323
|
+
: submissionNotAccepted
|
|
1324
|
+
? "submission_not_accepted"
|
|
1325
|
+
: submissionPending
|
|
1326
|
+
? "submission_pending_acceptance"
|
|
1327
|
+
: "async_pending",
|
|
1175
1328
|
submission_status: submissionUnfenced
|
|
1176
1329
|
? "submitted_unfenced"
|
|
1177
1330
|
: submissionUncertain
|
|
1178
1331
|
? "uncertain"
|
|
1179
1332
|
: submissionAborted
|
|
1180
1333
|
? "aborted"
|
|
1181
|
-
:
|
|
1334
|
+
: submissionNotAccepted
|
|
1335
|
+
? "not_accepted"
|
|
1336
|
+
: submissionPending
|
|
1337
|
+
? "pending_acceptance"
|
|
1338
|
+
: "accepted",
|
|
1182
1339
|
conversation_id: conversationId,
|
|
1183
1340
|
session_id: sessionId,
|
|
1184
1341
|
turn_id: turnId,
|
|
@@ -1214,32 +1371,73 @@ async function runDelegate(api, params, toolContext) {
|
|
|
1214
1371
|
note: "AKK could not prove whether tmux accepted Enter. Do not retry automatically; inspect the exact AKK Turn record and shared terminal."
|
|
1215
1372
|
}
|
|
1216
1373
|
: submissionAborted
|
|
1217
|
-
?
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1374
|
+
? parsed.safe_to_retry === true && parsed.do_not_retry !== true
|
|
1375
|
+
? {
|
|
1376
|
+
submission_outcome: "aborted",
|
|
1377
|
+
safe_to_retry: true,
|
|
1378
|
+
do_not_retry: false,
|
|
1379
|
+
reason: parsed.reason,
|
|
1380
|
+
openclaw_next_action: parsed.openclaw_next_action,
|
|
1381
|
+
note: "AKK stopped before sending tmux input and durably proved a safe abort. The request may be retried."
|
|
1382
|
+
}
|
|
1383
|
+
: {
|
|
1384
|
+
submission_outcome: "aborted",
|
|
1385
|
+
safe_to_retry: false,
|
|
1386
|
+
do_not_retry: true,
|
|
1387
|
+
reason: parsed.reason,
|
|
1388
|
+
openclaw_next_action: parsed.openclaw_next_action,
|
|
1389
|
+
note: "AKK could not prove a durable safe abort. Do not retry automatically; inspect the exact Turn and terminal dispatch ledger."
|
|
1390
|
+
}
|
|
1391
|
+
: submissionNotAccepted
|
|
1392
|
+
? {
|
|
1393
|
+
submission_outcome: "not_accepted",
|
|
1394
|
+
do_not_retry: true,
|
|
1395
|
+
reason: parsed.reason,
|
|
1396
|
+
openclaw_next_action: parsed.openclaw_next_action,
|
|
1397
|
+
note: "AKK proved terminal transport but the exact draft is still present in the agent composer. Do not retry automatically; inspect the shared pane."
|
|
1398
|
+
}
|
|
1399
|
+
: submissionPending
|
|
1400
|
+
? {
|
|
1401
|
+
submission_outcome: "pending_acceptance",
|
|
1402
|
+
do_not_retry: true,
|
|
1403
|
+
reason: parsed.reason,
|
|
1404
|
+
openclaw_next_action: parsed.openclaw_next_action,
|
|
1405
|
+
note: "AKK proved only terminal transport and is still waiting for native agent acceptance. Do not retry or report the task as accepted."
|
|
1406
|
+
}
|
|
1407
|
+
: {
|
|
1408
|
+
openclaw_next_action: {
|
|
1409
|
+
action: "yield",
|
|
1410
|
+
reason: "The coding agent is working in the shared tmux terminal. End this OpenClaw turn now and wait for an Agent Knock Knock callback.",
|
|
1411
|
+
do_not: "Do not poll terminal internals while waiting. Further communication must use Agent Knock Knock tools so the same shared terminal remains authoritative.",
|
|
1412
|
+
expected_callback: "The callback will be injected and scheduled into this OpenClaw session by the agent-knock-knock.callback Gateway method."
|
|
1413
|
+
},
|
|
1414
|
+
note: "The task was sent to the shared tmux terminal. OpenClaw should yield now and wait for the scheduled callback turn."
|
|
1415
|
+
})
|
|
1233
1416
|
};
|
|
1234
1417
|
}
|
|
1418
|
+
function terminalMessageIdForToolCall({ toolCallId: toolCallIdValue, sessionKey: sessionKeyValue, sessionId: sessionIdValue, toolName }) {
|
|
1419
|
+
const toolCallId = stringValue(toolCallIdValue);
|
|
1420
|
+
if (!toolCallId) {
|
|
1421
|
+
return undefined;
|
|
1422
|
+
}
|
|
1423
|
+
const sessionKey = stringValue(sessionKeyValue) ?? "agent:main:main";
|
|
1424
|
+
// sessionId is the OpenClaw conversation incarnation. It changes across
|
|
1425
|
+
// /new and /reset even when sessionKey remains stable. Keep a literal null
|
|
1426
|
+
// fallback so retries with the same legacy context remain deterministic.
|
|
1427
|
+
const sessionId = stringValue(sessionIdValue) ?? null;
|
|
1428
|
+
const digest = createHash("sha256")
|
|
1429
|
+
.update(JSON.stringify([sessionKey, sessionId, toolName, toolCallId]))
|
|
1430
|
+
.digest("hex");
|
|
1431
|
+
return `msg-openclaw-${digest}`;
|
|
1432
|
+
}
|
|
1235
1433
|
function registerCliTool(api, { name, description, parameters, buildArgs, normalizeTurnIdentity = true, isErrorResult = (_result) => false }) {
|
|
1236
1434
|
api.registerTool((toolContext) => ({
|
|
1237
1435
|
label: toolLabel(name),
|
|
1238
1436
|
name,
|
|
1239
1437
|
description,
|
|
1240
1438
|
parameters,
|
|
1241
|
-
async execute(
|
|
1242
|
-
const result = runCli(api, buildArgs(isRecord(params) ? params : {}, toolContext));
|
|
1439
|
+
async execute(toolCallId, params) {
|
|
1440
|
+
const result = runCli(api, buildArgs(isRecord(params) ? params : {}, toolContext, toolCallId));
|
|
1243
1441
|
return toolResult(result, {
|
|
1244
1442
|
submissionErrors: name === "agent_knock_knock_respond",
|
|
1245
1443
|
normalizeTurnIdentity,
|