@scotthuang/agent-knock-knock 0.2.46 → 0.2.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +93 -55
  3. package/dist/src/approval-policy.d.ts +3 -1
  4. package/dist/src/approval-policy.js +18 -4
  5. package/dist/src/approval-policy.js.map +1 -1
  6. package/dist/src/claude-hook-installer.d.ts +65 -0
  7. package/dist/src/claude-hook-installer.js +410 -0
  8. package/dist/src/claude-hook-installer.js.map +1 -0
  9. package/dist/src/claude-hook-protocol.d.ts +89 -0
  10. package/dist/src/claude-hook-protocol.js +243 -0
  11. package/dist/src/claude-hook-protocol.js.map +1 -0
  12. package/dist/src/claude-hook-store.d.ts +247 -0
  13. package/dist/src/claude-hook-store.js +1075 -0
  14. package/dist/src/claude-hook-store.js.map +1 -0
  15. package/dist/src/claude-terminal-agent-adapter.d.ts +47 -0
  16. package/dist/src/claude-terminal-agent-adapter.js +935 -0
  17. package/dist/src/claude-terminal-agent-adapter.js.map +1 -0
  18. package/dist/src/cli.js +2065 -316
  19. package/dist/src/cli.js.map +1 -1
  20. package/dist/src/openclaw-plugin-helpers.d.ts +49 -0
  21. package/dist/src/openclaw-plugin-helpers.js +246 -0
  22. package/dist/src/openclaw-plugin-helpers.js.map +1 -0
  23. package/dist/src/openclaw-plugin.js +98 -275
  24. package/dist/src/openclaw-plugin.js.map +1 -1
  25. package/dist/src/runtime-log.js +54 -2
  26. package/dist/src/runtime-log.js.map +1 -1
  27. package/dist/src/store.js +432 -12
  28. package/dist/src/store.js.map +1 -1
  29. package/dist/src/terminal-agent-adapter.d.ts +37 -0
  30. package/dist/src/terminal-agent-adapter.js.map +1 -1
  31. package/dist/src/terminal-agent-bridge.d.ts +51 -4
  32. package/dist/src/terminal-agent-bridge.js +326 -43
  33. package/dist/src/terminal-agent-bridge.js.map +1 -1
  34. package/dist/src/terminal-agent-registry.js +3 -1
  35. package/dist/src/terminal-agent-registry.js.map +1 -1
  36. package/dist/src/terminal-control-provider.d.ts +3 -1
  37. package/dist/src/terminal-control-provider.js +41 -37
  38. package/dist/src/terminal-control-provider.js.map +1 -1
  39. package/dist/src/terminal-process-source.d.ts +12 -3
  40. package/dist/src/terminal-process-source.js +37 -6
  41. package/dist/src/terminal-process-source.js.map +1 -1
  42. package/openclaw.plugin.json +5 -5
  43. package/package.json +10 -5
  44. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +19 -30
@@ -2,9 +2,10 @@ import { spawnSync } from "node:child_process";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
5
- import { EXECUTOR_KINDS, executorDefinitionForAlias, executorDefinitionForKind, parseLeadingExecutorAlias } from "./executors.js";
5
+ import { EXECUTOR_KINDS, executorDefinitionForKind, parseLeadingExecutorAlias } from "./executors.js";
6
+ import { AKK_CALLBACK_METHOD, akkUsageText, buildAkkCommandCliArgs, formatAkkListCommandResult, parseAkkCommand, resolvePluginStoreDir, resolveConversationOverrides } from "./openclaw-plugin-helpers.js";
6
7
  import { attemptAutoApproval } from "./approval-policy.js";
7
- const CALLBACK_METHOD = "agent-knock-knock.callback";
8
+ const CALLBACK_METHOD = AKK_CALLBACK_METHOD;
8
9
  const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
9
10
  const defaultBinPath = path.join(pluginRoot, "dist", "src", "cli.js");
10
11
  const delegateParameters = {
@@ -23,11 +24,7 @@ const delegateParameters = {
23
24
  },
24
25
  workspace: {
25
26
  type: "string",
26
- description: "Workspace path for Claude Code. Defaults to plugin config or the current process directory."
27
- },
28
- storeDir: {
29
- type: "string",
30
- description: "Conversation store directory. Defaults to <workspace>/.agent-knock-knock/conversations."
27
+ description: "Workspace path for the selected coding agent. Defaults to plugin config or the Gateway process directory."
31
28
  },
32
29
  claudeSession: {
33
30
  type: "string",
@@ -117,9 +114,6 @@ const listParameters = {
117
114
  type: "boolean",
118
115
  description: "When true, include tmux terminal discovery diagnostics for debugging Gateway environment issues."
119
116
  },
120
- storeDir: {
121
- type: "string"
122
- },
123
117
  idleTimeoutMinutes: {
124
118
  type: "number"
125
119
  }
@@ -138,9 +132,6 @@ const renewParameters = {
138
132
  type: "number",
139
133
  exclusiveMinimum: 0,
140
134
  description: "New terminal inactivity timeout in minutes."
141
- },
142
- storeDir: {
143
- type: "string"
144
135
  }
145
136
  }
146
137
  };
@@ -152,9 +143,6 @@ const retryCallbackParameters = {
152
143
  conversation_id: {
153
144
  type: "string",
154
145
  description: "AKK-managed conversation whose persisted callback delivery is pending or failed."
155
- },
156
- storeDir: {
157
- type: "string"
158
146
  }
159
147
  }
160
148
  };
@@ -167,9 +155,6 @@ const statusParameters = {
167
155
  type: "string",
168
156
  description: "AKK-managed conversation id, or a terminal-controlled id from AKK list such as terminal:v2:tmux:codex:codex-work:0.1:33389."
169
157
  },
170
- storeDir: {
171
- type: "string"
172
- },
173
158
  idleTimeoutMinutes: {
174
159
  type: "number"
175
160
  },
@@ -191,9 +176,6 @@ const describeParameters = {
191
176
  type: "string",
192
177
  description: "AKK-managed conversation id, native Codex id, or terminal-controlled id from AKK list. Use this when the user asks what a listed AKK/Codex session is about."
193
178
  },
194
- storeDir: {
195
- type: "string"
196
- },
197
179
  idleTimeoutMinutes: {
198
180
  type: "number"
199
181
  },
@@ -230,9 +212,6 @@ const sendParameters = {
230
212
  model: {
231
213
  type: "string"
232
214
  },
233
- storeDir: {
234
- type: "string"
235
- },
236
215
  idleTimeoutMinutes: {
237
216
  type: "number"
238
217
  },
@@ -259,9 +238,6 @@ const cancelParameters = {
259
238
  allProxy: {
260
239
  type: "string"
261
240
  },
262
- storeDir: {
263
- type: "string"
264
- },
265
241
  idleTimeoutMinutes: {
266
242
  type: "number"
267
243
  }
@@ -284,9 +260,6 @@ const recoveryParameters = {
284
260
  model: {
285
261
  type: "string"
286
262
  },
287
- storeDir: {
288
- type: "string"
289
- },
290
263
  idleTimeoutMinutes: {
291
264
  type: "number"
292
265
  }
@@ -302,9 +275,6 @@ const closeParameters = {
302
275
  },
303
276
  reason: {
304
277
  type: "string"
305
- },
306
- storeDir: {
307
- type: "string"
308
278
  }
309
279
  }
310
280
  };
@@ -359,10 +329,6 @@ const agentTakeoverParameters = {
359
329
  type: "string",
360
330
  description: "Required when strategy=fork and createConversation=true. OpenClaw-approved summary of the bounded source context to inject into the new forked AKK session."
361
331
  },
362
- storeDir: {
363
- type: "string",
364
- description: "Conversation store directory. Defaults to plugin config or <workspace>/.agent-knock-knock/conversations."
365
- },
366
332
  openclawSession: {
367
333
  type: "string",
368
334
  description: "OpenClaw session label recorded in the created AKK conversation."
@@ -397,9 +363,6 @@ const approveParameters = {
397
363
  expected_approval_fingerprint: {
398
364
  type: "string",
399
365
  description: "Exact approval fingerprint returned by the latest status or approval-required callback. The prompt is captured again and must still match before keys are sent."
400
- },
401
- storeDir: {
402
- type: "string"
403
366
  }
404
367
  }
405
368
  };
@@ -422,6 +385,31 @@ export default definePluginEntry({
422
385
  });
423
386
  }
424
387
  }, { scope: "operator.write" });
388
+ try {
389
+ api.registerService?.({
390
+ id: "agent-knock-knock-monitor-reconciliation",
391
+ start() {
392
+ try {
393
+ const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
394
+ const args = ["reconcile-monitors"];
395
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
396
+ const result = runCli(api, args);
397
+ api.logger.info?.("agent-knock-knock monitor reconciliation: " +
398
+ `checked=${result.checked ?? 0} launched=${result.launched ?? 0} ` +
399
+ `already_running=${result.already_running ?? 0} skipped=${result.skipped ?? 0} ` +
400
+ `errors=${result.errors ?? 0}`);
401
+ }
402
+ catch (error) {
403
+ const message = error instanceof Error ? error.message : String(error);
404
+ api.logger.warn?.(`agent-knock-knock monitor reconciliation skipped after startup error: ${message}`);
405
+ }
406
+ }
407
+ });
408
+ }
409
+ catch (error) {
410
+ const message = error instanceof Error ? error.message : String(error);
411
+ api.logger.warn?.(`agent-knock-knock monitor reconciliation service was not registered: ${message}`);
412
+ }
425
413
  api.registerCommand?.({
426
414
  name: "akk",
427
415
  description: "Delegate coding work to Agent Knock Knock, list tasks, send follow-ups, cancel running work, and close tasks.",
@@ -453,11 +441,11 @@ export default definePluginEntry({
453
441
  }), { name: "agent_knock_knock_delegate", optional: true });
454
442
  registerCliTool(api, {
455
443
  name: "agent_knock_knock_list",
456
- description: "List Agent Knock Knock work and local active coding-agent sessions. Use this for AKK list, akk list, current AKK tasks, native Codex active work, terminal-controlled Codex work, or asking which Codex/Claude/Cursor sessions are open. The result separates delegated AKK tasks, native active processes, and terminal-controlled sessions; terminal-controlled entries include approval state when available.",
444
+ description: "List Agent Knock Knock work and local active coding-agent sessions. Use this for AKK list, current AKK tasks, native Codex work, terminal-controlled Codex or Claude Code work, or asking which coding-agent sessions are open. The result separates delegated tasks, native processes, and terminal-controlled sessions; terminal entries include approval state when available.",
457
445
  parameters: listParameters,
458
446
  buildArgs: (params) => {
459
447
  const args = ["list"];
460
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
448
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(isRecord(api.pluginConfig) ? api.pluginConfig : {}));
461
449
  pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
462
450
  pushOptional(args, "--agent", stringValue(params.agent));
463
451
  pushOptional(args, "--status", stringValue(params.status));
@@ -482,7 +470,7 @@ export default definePluginEntry({
482
470
  parameters: statusParameters,
483
471
  buildArgs: (params) => {
484
472
  const args = ["status", "--conversation", requiredString(params.conversation_id, "conversation_id")];
485
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
473
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(isRecord(api.pluginConfig) ? api.pluginConfig : {}));
486
474
  pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
487
475
  if (params.trace === true) {
488
476
  args.push("--trace");
@@ -492,11 +480,11 @@ export default definePluginEntry({
492
480
  });
493
481
  registerCliTool(api, {
494
482
  name: "agent_knock_knock_describe",
495
- description: "Describe what a listed Agent Knock Knock or local Codex session is about. Use this when the user asks what a session/task/drawing is doing, what the original work was, or wants a reminder of a session's content. It summarizes AKK history when available, otherwise Codex rollout history, cwd-based inferred history, or terminal screen fallback with confidence and limitations.",
483
+ description: "Describe what a listed Agent Knock Knock or local coding-agent session is about. It summarizes AKK history when available, otherwise uses supported durable agent context or a conservative terminal-screen fallback with confidence and limitations.",
496
484
  parameters: describeParameters,
497
485
  buildArgs: (params) => {
498
486
  const args = ["describe", "--conversation", requiredString(params.conversation_id, "conversation_id")];
499
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
487
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(isRecord(api.pluginConfig) ? api.pluginConfig : {}));
500
488
  pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
501
489
  pushOptional(args, "--max-messages", numberString(params.maxMessages));
502
490
  pushOptional(args, "--max-commands", numberString(params.maxCommands));
@@ -506,7 +494,7 @@ export default definePluginEntry({
506
494
  });
507
495
  registerCliTool(api, {
508
496
  name: "agent_knock_knock_send",
509
- description: "Send a message, follow-up, or new task to an existing open Agent Knock Knock coding-agent session or terminal-controlled session from AKK list. Use this when the user says to send/tell/ask/forward/add/continue work in a listed session, a listed Codex terminal, a tmux target such as my-work:0.1, or the one from AKK list. Do not start a new delegate for those requests. This is an asynchronous handoff: after the message is accepted, end the OpenClaw turn and wait for an Agent Knock Knock callback or a later explicit status request. Do not poll terminal output or wait for the coding agent's final result in the same OpenClaw turn.",
497
+ description: "Send a message, follow-up, or new task to an existing open Agent Knock Knock or terminal-controlled coding-agent session from AKK list, including Codex or Claude Code in tmux. Do not start a new delegate for those requests. This is asynchronous: after acceptance, end the OpenClaw turn and wait for the AKK callback or a later explicit status request.",
510
498
  parameters: sendParameters,
511
499
  buildArgs: (params, toolContext) => {
512
500
  const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
@@ -522,15 +510,14 @@ export default definePluginEntry({
522
510
  "--background"
523
511
  ];
524
512
  pushOptional(args, "--type", stringValue(params.type));
525
- pushOptional(args, "--all-proxy", stringValue(params.allProxy) ?? stringValue(config.codexAllProxy) ?? stringValue(config.allProxy));
526
- pushOptional(args, "--model", stringValue(params.model) ?? stringValue(config.codexModel) ?? stringValue(config.model));
527
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(config.storeDir));
513
+ const overrides = resolveConversationOverrides(params, config);
514
+ pushOptional(args, "--all-proxy", overrides.allProxy);
515
+ pushOptional(args, "--model", overrides.model);
516
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
528
517
  pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(config.idleTimeoutMinutes));
529
518
  pushOptional(args, "--agent-timeout-minutes", numberString(params.agentTimeoutMinutes) ?? numberString(config.agentTimeoutMinutes));
530
519
  pushOptional(args, "--agent-hard-timeout-minutes", numberString(params.agentHardTimeoutMinutes) ?? numberString(config.agentHardTimeoutMinutes));
531
520
  pushOptional(args, "--openclaw-session", openclawSession);
532
- pushOptional(args, "--gateway-url", stringValue(config.gatewayUrl));
533
- pushOptional(args, "--token", stringValue(config.gatewayToken));
534
521
  pushOptional(args, "--gateway-method", CALLBACK_METHOD);
535
522
  pushOptional(args, "--gateway-session", openclawSession);
536
523
  pushOptional(args, "--openclaw-bin", stringValue(config.openclawBin));
@@ -542,24 +529,24 @@ export default definePluginEntry({
542
529
  });
543
530
  registerCliTool(api, {
544
531
  name: "agent_knock_knock_approve",
545
- description: "Approve the current visible terminal approval prompt for an AKK terminal-controlled session. Use only after showing the user the detected prompt and receiving explicit approval. This sends the primary approve shortcut to the controlled terminal pane.",
532
+ description: "Approve the current AKK terminal permission request after showing it to the user and receiving explicit approval. Claude Code uses a revalidated structured one-time Hook decision; supported screen fallbacks use the detected primary shortcut and are never eligible for Claude auto-approval.",
546
533
  parameters: approveParameters,
547
534
  buildArgs: (params) => {
548
535
  const args = ["approve", "--conversation", requiredString(params.conversation_id, "conversation_id")];
549
536
  pushOptional(args, "--expected-approval-fingerprint", requiredString(params.expected_approval_fingerprint, "expected_approval_fingerprint"));
550
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
537
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(isRecord(api.pluginConfig) ? api.pluginConfig : {}));
551
538
  return args;
552
539
  }
553
540
  });
554
541
  registerCliTool(api, {
555
542
  name: "agent_knock_knock_renew",
556
- description: "Renew monitoring for a stalled AKK-managed terminal bridge task without sending text or keys to Codex. Use this when the user wants a still-live long-running terminal task to keep monitoring after an inactivity stall.",
543
+ description: "Renew monitoring for a stalled AKK-managed terminal bridge task without sending text or keys to the coding agent. Use this when the user wants a still-live long-running terminal task to keep monitoring after an inactivity stall.",
557
544
  parameters: renewParameters,
558
545
  buildArgs: (params) => {
559
546
  const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
560
547
  const args = ["renew", "--conversation", requiredString(params.conversation_id, "conversation_id")];
561
548
  pushOptional(args, "--minutes", numberString(params.minutes) ?? numberString(config.agentTimeoutMinutes));
562
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(config.storeDir));
549
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
563
550
  return args;
564
551
  }
565
552
  });
@@ -570,19 +557,21 @@ export default definePluginEntry({
570
557
  buildArgs: (params) => {
571
558
  const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
572
559
  const args = ["retry-callback", "--conversation", requiredString(params.conversation_id, "conversation_id")];
573
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(config.storeDir));
560
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
574
561
  return args;
575
562
  }
576
563
  });
577
564
  registerCliTool(api, {
578
565
  name: "agent_knock_knock_cancel",
579
- description: "Request cancellation for an existing Agent Knock Knock Codex, Claude, or Cursor session. Delegated sessions use cooperative ACPX cancellation. Terminal-controlled sessions send Control-C to the controlled terminal pane. This does not close the AKK session; use close when the session should no longer be reused.",
566
+ description: "Cancel an existing Agent Knock Knock Codex, Claude, or Cursor task. Delegated sessions use cooperative ACPX cancellation. Terminal-controlled Claude denies a pending structured permission or sends Escape; other adapters use their declared interrupt keys. The underlying tmux pane remains open.",
580
567
  parameters: cancelParameters,
581
568
  buildArgs: (params) => {
569
+ const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
570
+ const overrides = resolveConversationOverrides(params, config);
582
571
  const args = ["cancel", "--conversation", requiredString(params.conversation_id, "conversation_id")];
583
- pushOptional(args, "--all-proxy", stringValue(params.allProxy) ?? stringValue(api.pluginConfig?.codexAllProxy) ?? stringValue(api.pluginConfig?.allProxy));
584
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
585
- pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
572
+ pushOptional(args, "--all-proxy", overrides.allProxy);
573
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
574
+ pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(config.idleTimeoutMinutes));
586
575
  return args;
587
576
  }
588
577
  });
@@ -599,7 +588,7 @@ export default definePluginEntry({
599
588
  buildArgs: (params) => {
600
589
  const args = ["close", "--conversation", requiredString(params.conversation_id, "conversation_id")];
601
590
  pushOptional(args, "--reason", stringValue(params.reason));
602
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
591
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(isRecord(api.pluginConfig) ? api.pluginConfig : {}));
603
592
  return args;
604
593
  }
605
594
  });
@@ -639,10 +628,8 @@ export default definePluginEntry({
639
628
  pushOptional(args, "--terminal-target", stringValue(params.terminalTarget));
640
629
  pushOptional(args, "--request", stringValue(params.request));
641
630
  pushOptional(args, "--fork-summary", stringValue(params.forkSummary));
642
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(config.storeDir));
631
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
643
632
  pushOptional(args, "--openclaw-session", openclawSession);
644
- pushOptional(args, "--gateway-url", stringValue(config.gatewayUrl));
645
- pushOptional(args, "--token", stringValue(config.gatewayToken));
646
633
  pushOptional(args, "--gateway-method", CALLBACK_METHOD);
647
634
  pushOptional(args, "--gateway-session", openclawSession);
648
635
  pushOptional(args, "--openclaw-bin", stringValue(config.openclawBin));
@@ -674,88 +661,34 @@ async function handleAkkCommand(api, ctx) {
674
661
  });
675
662
  return { text: formatDelegateCommandResult(result) };
676
663
  }
677
- if (parsed.action === "list") {
678
- const result = runCli(api, ["list"]);
679
- return { text: formatListCommandResult(result) };
680
- }
681
- if (parsed.action === "status") {
682
- const result = runCli(api, ["status", "--conversation", parsed.conversationId]);
683
- return { text: formatStatusCommandResult(result) };
684
- }
685
- if (parsed.action === "describe") {
686
- const result = runCli(api, ["describe", "--conversation", parsed.conversationId]);
687
- return { text: formatDescribeCommandResult(result) };
688
- }
689
- if (parsed.action === "send") {
690
- const args = [
691
- "send",
692
- "--conversation",
693
- parsed.conversationId,
694
- "--message",
695
- parsed.message,
696
- "--background"
697
- ];
698
- const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
699
- pushOptional(args, "--all-proxy", stringValue(config.codexAllProxy) ?? stringValue(config.allProxy));
700
- pushOptional(args, "--model", stringValue(config.codexModel) ?? stringValue(config.model));
701
- pushOptional(args, "--idle-timeout-minutes", numberString(config.idleTimeoutMinutes));
702
- pushOptional(args, "--agent-timeout-minutes", numberString(config.agentTimeoutMinutes));
703
- pushOptional(args, "--agent-hard-timeout-minutes", numberString(config.agentHardTimeoutMinutes));
704
- const result = runCli(api, args);
705
- return { text: formatSendCommandResult(result) };
706
- }
707
- if (parsed.action === "renew") {
708
- const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
709
- const args = ["renew", "--conversation", parsed.conversationId];
710
- pushOptional(args, "--minutes", parsed.minutes ?? numberString(config.agentTimeoutMinutes));
711
- pushOptional(args, "--store-dir", stringValue(config.storeDir));
712
- const result = runCli(api, args);
713
- return { text: formatRenewCommandResult(result) };
714
- }
715
- if (parsed.action === "retry-callback" || parsed.action === "retry") {
716
- const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
717
- const args = ["retry-callback", "--conversation", parsed.conversationId];
718
- pushOptional(args, "--store-dir", stringValue(config.storeDir));
719
- const result = runCli(api, args);
720
- return { text: formatRetryCallbackCommandResult(result) };
721
- }
722
- if (parsed.action === "cancel") {
723
- const args = [
724
- "cancel",
725
- "--conversation",
726
- parsed.conversationId
727
- ];
728
- const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
729
- pushOptional(args, "--all-proxy", stringValue(config.codexAllProxy) ?? stringValue(config.allProxy));
730
- pushOptional(args, "--idle-timeout-minutes", numberString(config.idleTimeoutMinutes));
731
- const result = runCli(api, args);
732
- return { text: formatCancelCommandResult(result) };
733
- }
734
- if (parsed.action === "recover") {
735
- const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
736
- const args = [
737
- "recover",
738
- "--conversation",
739
- parsed.conversationId,
740
- "--background"
741
- ];
742
- pushOptional(args, "--all-proxy", stringValue(config.codexAllProxy) ?? stringValue(config.allProxy));
743
- pushOptional(args, "--model", stringValue(config.codexModel) ?? stringValue(config.model));
744
- pushOptional(args, "--idle-timeout-minutes", numberString(config.idleTimeoutMinutes));
745
- const result = runCli(api, args);
746
- return { text: formatRecoveryCommandResult(result) };
664
+ const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
665
+ const args = buildAkkCommandCliArgs(parsed, config, {
666
+ sessionKey: ctx.sessionKey
667
+ });
668
+ if (!args) {
669
+ return { text: akkUsageText(), isError: true };
747
670
  }
748
- if (parsed.action === "close") {
749
- const result = runCli(api, [
750
- "close",
751
- "--conversation",
752
- parsed.conversationId,
753
- "--reason",
754
- parsed.reason
755
- ]);
756
- return { text: formatCloseCommandResult(result) };
671
+ const result = runCli(api, args);
672
+ switch (parsed.action) {
673
+ case "list":
674
+ return { text: formatAkkListCommandResult(result) };
675
+ case "status":
676
+ return { text: formatStatusCommandResult(result) };
677
+ case "describe":
678
+ return { text: formatDescribeCommandResult(result) };
679
+ case "send":
680
+ return { text: formatSendCommandResult(result) };
681
+ case "renew":
682
+ return { text: formatRenewCommandResult(result) };
683
+ case "retry-callback":
684
+ return { text: formatRetryCallbackCommandResult(result) };
685
+ case "cancel":
686
+ return { text: formatCancelCommandResult(result) };
687
+ case "recover":
688
+ return { text: formatRecoveryCommandResult(result) };
689
+ case "close":
690
+ return { text: formatCloseCommandResult(result) };
757
691
  }
758
- return { text: akkUsageText(), isError: true };
759
692
  }
760
693
  catch (error) {
761
694
  const message = error instanceof Error ? error.message : String(error);
@@ -765,109 +698,14 @@ async function handleAkkCommand(api, ctx) {
765
698
  };
766
699
  }
767
700
  }
768
- function parseAkkCommand(args) {
769
- const input = String(args ?? "").trim();
770
- if (!input || input === "help" || input === "-h" || input === "--help") {
771
- return { action: "help" };
772
- }
773
- const { token, rest } = takeToken(input);
774
- const action = String(token).toLowerCase();
775
- if (action === "list" || action === "ls" || action === "tasks") {
776
- return { action: "list" };
777
- }
778
- if (action === "status" || action === "show") {
779
- const { token: conversationId } = takeRequiredToken(rest, "Usage: /akk status <conversation-id>");
780
- return { action: "status", conversationId };
781
- }
782
- if (action === "describe" || action === "summary" || action === "about") {
783
- const { token: conversationId } = takeRequiredToken(rest, "Usage: /akk describe <conversation-id>");
784
- return { action: "describe", conversationId };
785
- }
786
- if (action === "send" || action === "reply") {
787
- const { token: conversationId, rest: message } = takeRequiredToken(rest, "Usage: /akk send <conversation-id> <message>");
788
- const body = message.trim();
789
- if (!body) {
790
- throw new Error("Usage: /akk send <conversation-id> <message>");
791
- }
792
- return { action: "send", conversationId, message: body };
793
- }
794
- if (action === "cancel" || action === "stop") {
795
- const { token: conversationId } = takeRequiredToken(rest, "Usage: /akk cancel <conversation-id>");
796
- return { action: "cancel", conversationId };
797
- }
798
- if (action === "renew") {
799
- const { token: conversationId, rest: minutesInput } = takeRequiredToken(rest, "Usage: /akk renew <conversation-id> [minutes]");
800
- const minutes = minutesInput.trim();
801
- if (minutes && (!Number.isFinite(Number(minutes)) || Number(minutes) <= 0)) {
802
- throw new Error("Usage: /akk renew <conversation-id> [positive-minutes]");
803
- }
804
- return { action: "renew", conversationId, minutes: minutes || undefined };
805
- }
806
- if (action === "retry-callback" || action === "retry") {
807
- const { token: conversationId } = takeRequiredToken(rest, "Usage: /akk retry-callback <conversation-id>");
808
- return { action: "retry-callback", conversationId };
809
- }
810
- if (action === "recover") {
811
- const { token: conversationId } = takeRequiredToken(rest, "Usage: /akk recover <conversation-id>");
812
- return { action: "recover", conversationId };
813
- }
814
- if (action === "close" || action === "done") {
815
- const { token: conversationId, rest: reason } = takeRequiredToken(rest, "Usage: /akk close <conversation-id> [reason]");
816
- return { action: "close", conversationId, reason: reason.trim() || "Closed from /akk command" };
817
- }
818
- const executorDefinition = executorDefinitionForAlias(action);
819
- if (executorDefinition) {
820
- const request = rest.trim();
821
- if (!request) {
822
- throw new Error(`Usage: /akk ${executorDefinition.kind} <task>`);
823
- }
824
- return { action: "delegate", agent: executorDefinition.kind, request };
825
- }
826
- return { action: "delegate", agent: "codex", request: input };
827
- }
828
- function takeRequiredToken(input, usage) {
829
- const parsed = takeToken(input);
830
- if (!parsed.token) {
831
- throw new Error(usage);
832
- }
833
- return parsed;
834
- }
835
- function takeToken(input) {
836
- const value = String(input ?? "").trimStart();
837
- const match = value.match(/^(\S+)(?:\s+([\s\S]*))?$/);
838
- if (!match) {
839
- return { token: "", rest: "" };
840
- }
841
- return {
842
- token: match[1],
843
- rest: match[2] ?? ""
844
- };
845
- }
846
- function akkUsageText() {
847
- return [
848
- "AKK usage:",
849
- "/akk <task>",
850
- "/akk codex <task>",
851
- "/akk claude <task>",
852
- "/akk list",
853
- "/akk status <conversation-id>",
854
- "/akk describe <conversation-id>",
855
- "/akk send <conversation-id> <message>",
856
- "/akk cancel <conversation-id>",
857
- "/akk renew <conversation-id> [minutes]",
858
- "/akk retry-callback <conversation-id>",
859
- "/akk recover <conversation-id>",
860
- "/akk close <conversation-id> [reason]"
861
- ].join("\n");
862
- }
863
701
  function formatDelegateCommandResult(result) {
864
702
  const agent = executorDisplayName(result.agent);
865
703
  return [
866
- `AKK 已交给 ${agent}。`,
704
+ `AKK delegated the task to ${agent}.`,
867
705
  `conversation: ${result.conversation_id ?? "unknown"}`,
868
706
  `session: ${result.session ?? "unknown"}`,
869
707
  `status: ${result.conversation_status ?? result.status ?? "unknown"}`,
870
- "结果会通过 OpenClaw 回调返回当前会话。"
708
+ "The result will return to this OpenClaw session through the callback."
871
709
  ].join("\n");
872
710
  }
873
711
  function executorDisplayName(kind) {
@@ -878,23 +716,15 @@ function executorDisplayName(kind) {
878
716
  return String(kind ?? "agent");
879
717
  }
880
718
  }
881
- function formatListCommandResult(result) {
882
- const tasks = Array.isArray(result.tasks) ? result.tasks : [];
883
- if (!tasks.length) {
884
- return "AKK 当前没有打开的会话。";
885
- }
886
- return [
887
- `AKK open sessions (${tasks.length}):`,
888
- ...tasks.slice(0, 20).map(formatTaskLine)
889
- ].join("\n");
890
- }
891
719
  function formatStatusCommandResult(result) {
892
- const summary = result.summary ?? result.conversation ?? {};
720
+ const summary = result.summary ?? result.conversation ?? result ?? {};
721
+ const terminalStatus = isRecord(result.terminal_status) ? result.terminal_status : {};
722
+ const terminalControl = isRecord(result.terminal_control) ? result.terminal_control : {};
893
723
  const lines = [
894
- `AKK status: ${summary.conversation_id ?? "unknown"}`,
895
- `agent: ${summary.agent ?? summary.executor?.kind ?? "unknown"}`,
896
- `status: ${summary.status ?? "unknown"}`,
897
- `session: ${summary.session ?? summary.executor?.session ?? "unknown"}`
724
+ `AKK status: ${summary.conversation_id ?? result.conversation_id ?? "unknown"}`,
725
+ `agent: ${summary.agent ?? summary.executor?.kind ?? terminalStatus.agent ?? "unknown"}`,
726
+ `status: ${summary.status ?? terminalStatus.activity_state ?? "unknown"}`,
727
+ `session: ${summary.session ?? summary.executor?.session ?? terminalControl.target ?? "unknown"}`
898
728
  ];
899
729
  if (summary.request) {
900
730
  lines.push(`request: ${truncateText(summary.request, 180)}`);
@@ -953,12 +783,13 @@ function formatSendCommandResult(result) {
953
783
  }
954
784
  function formatCancelCommandResult(result) {
955
785
  const conversation = result.conversation ?? {};
786
+ const terminalControl = isRecord(result.terminal_control) ? result.terminal_control : {};
956
787
  return [
957
788
  "AKK cancel requested.",
958
- `conversation: ${conversation.conversation_id ?? "unknown"}`,
789
+ `conversation: ${conversation.conversation_id ?? result.conversation_id ?? "unknown"}`,
959
790
  `agent: ${result.executor?.kind ?? conversation.executor?.kind ?? "unknown"}`,
960
- `session: ${result.executor?.session ?? conversation.executor?.session ?? "unknown"}`,
961
- `status: ${conversation.status ?? "unknown"}`
791
+ `session: ${result.executor?.session ?? conversation.executor?.session ?? terminalControl.target ?? "unknown"}`,
792
+ `status: ${conversation.status ?? (result.cancel_requested === true ? "cancel requested" : "not cancelled")}`
962
793
  ].join("\n");
963
794
  }
964
795
  function formatRecoveryCommandResult(result) {
@@ -979,14 +810,6 @@ function formatCloseCommandResult(result) {
979
810
  `status: ${conversation.status ?? "unknown"}`
980
811
  ].join("\n");
981
812
  }
982
- function formatTaskLine(task) {
983
- return [
984
- task.conversation_id ?? "unknown",
985
- task.agent ?? task.executor?.kind ?? "agent",
986
- task.status ?? "unknown",
987
- truncateText(task.request ?? "", 90)
988
- ].filter(Boolean).join(" | ");
989
- }
990
813
  function runDelegate(api, params, toolContext) {
991
814
  const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
992
815
  const binPath = stringValue(config.binPath) ?? defaultBinPath;
@@ -1022,13 +845,11 @@ function runDelegate(api, params, toolContext) {
1022
845
  workspace,
1023
846
  "--background"
1024
847
  ];
1025
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(config.storeDir));
848
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
1026
849
  pushOptional(args, "--session", agentSession);
1027
850
  pushOptional(args, "--all-proxy", allProxy);
1028
851
  pushOptional(args, "--model", model);
1029
852
  pushOptional(args, "--openclaw-session", openclawSession);
1030
- pushOptional(args, "--gateway-url", stringValue(config.gatewayUrl));
1031
- pushOptional(args, "--token", stringValue(config.gatewayToken));
1032
853
  pushOptional(args, "--gateway-method", CALLBACK_METHOD);
1033
854
  pushOptional(args, "--gateway-session", openclawSession);
1034
855
  pushOptional(args, "--openclaw-bin", stringValue(config.openclawBin));
@@ -1096,12 +917,14 @@ function registerCliTool(api, { name, description, parameters, buildArgs }) {
1096
917
  }), { name, optional: true });
1097
918
  }
1098
919
  function buildRecoveryArgs(api, command, params) {
920
+ const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
921
+ const overrides = resolveConversationOverrides(params, config);
1099
922
  const args = [command, "--conversation", requiredString(params.conversation_id, "conversation_id"), "--background"];
1100
923
  pushOptional(args, "--session", stringValue(params.session));
1101
- pushOptional(args, "--all-proxy", stringValue(params.allProxy) ?? stringValue(api.pluginConfig?.codexAllProxy) ?? stringValue(api.pluginConfig?.allProxy));
1102
- pushOptional(args, "--model", stringValue(params.model) ?? stringValue(api.pluginConfig?.codexModel) ?? stringValue(api.pluginConfig?.model));
1103
- pushOptional(args, "--store-dir", stringValue(params.storeDir) ?? stringValue(api.pluginConfig?.storeDir));
1104
- pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
924
+ pushOptional(args, "--all-proxy", overrides.allProxy);
925
+ pushOptional(args, "--model", overrides.model);
926
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
927
+ pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(config.idleTimeoutMinutes));
1105
928
  return args;
1106
929
  }
1107
930
  function runCli(api, cliArgs, { cwd = process.cwd() } = {}) {