@scotthuang/agent-knock-knock 0.6.2 → 0.8.0
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 +25 -0
- package/README.md +20 -6
- package/dist/src/cli.js +1150 -508
- package/dist/src/cli.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.js +86 -46
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin.d.ts +1 -0
- package/dist/src/openclaw-plugin.js +175 -172
- package/dist/src/openclaw-plugin.js.map +1 -1
- package/dist/src/session-selector.d.ts +18 -0
- package/dist/src/session-selector.js +18 -9
- package/dist/src/session-selector.js.map +1 -1
- package/dist/src/store.d.ts +50 -0
- package/dist/src/store.js +414 -42
- package/dist/src/store.js.map +1 -1
- package/docs/quickstart-tmux.md +4 -2
- package/openclaw.plugin.json +3 -7
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +12 -10
|
@@ -6,6 +6,7 @@ import { AKK_CALLBACK_METHOD, akkUsageText, buildAkkCommandCliArgs, formatAkkLis
|
|
|
6
6
|
import { attemptAutoApproval } from "./approval-policy.js";
|
|
7
7
|
const CALLBACK_METHOD = AKK_CALLBACK_METHOD;
|
|
8
8
|
const defaultBinPath = fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
9
|
+
const relayPathByApi = new WeakMap();
|
|
9
10
|
const sendParameters = {
|
|
10
11
|
type: "object",
|
|
11
12
|
additionalProperties: false,
|
|
@@ -25,7 +26,7 @@ const sendParameters = {
|
|
|
25
26
|
},
|
|
26
27
|
idleTimeoutMinutes: {
|
|
27
28
|
type: "number",
|
|
28
|
-
description: "Minutes an idle AKK session remains open before
|
|
29
|
+
description: "Minutes an idle AKK session remains open before controlled reconciliation closes its managed-turn record."
|
|
29
30
|
},
|
|
30
31
|
agentTimeoutMinutes: {
|
|
31
32
|
type: "number",
|
|
@@ -52,13 +53,9 @@ const listParameters = {
|
|
|
52
53
|
all: {
|
|
53
54
|
type: "boolean"
|
|
54
55
|
},
|
|
55
|
-
managedOnly: {
|
|
56
|
-
type: "boolean",
|
|
57
|
-
description: "When true, only list AKK-managed turns and skip live tmux terminal discovery."
|
|
58
|
-
},
|
|
59
56
|
noApprovalScan: {
|
|
60
57
|
type: "boolean",
|
|
61
|
-
description: "When true, list
|
|
58
|
+
description: "When true, list live terminals without scanning their panes for approval prompts."
|
|
62
59
|
},
|
|
63
60
|
terminalDebug: {
|
|
64
61
|
type: "boolean",
|
|
@@ -103,7 +100,7 @@ const statusParameters = {
|
|
|
103
100
|
properties: {
|
|
104
101
|
conversation_id: {
|
|
105
102
|
type: "string",
|
|
106
|
-
description: "
|
|
103
|
+
description: "Managed-turn conversation id, or a live terminal id from AKK list such as terminal:v2:tmux:codex:codex-work:0.1:33389."
|
|
107
104
|
},
|
|
108
105
|
idleTimeoutMinutes: {
|
|
109
106
|
type: "number"
|
|
@@ -121,7 +118,7 @@ const cancelParameters = {
|
|
|
121
118
|
properties: {
|
|
122
119
|
conversation_id: {
|
|
123
120
|
type: "string",
|
|
124
|
-
description: "
|
|
121
|
+
description: "Managed-turn conversation id, or a live terminal id from AKK list such as terminal:v2:tmux:codex:codex-work:0.1:33389."
|
|
125
122
|
},
|
|
126
123
|
idleTimeoutMinutes: {
|
|
127
124
|
type: "number"
|
|
@@ -152,7 +149,7 @@ const approveParameters = {
|
|
|
152
149
|
properties: {
|
|
153
150
|
conversation_id: {
|
|
154
151
|
type: "string",
|
|
155
|
-
description: "
|
|
152
|
+
description: "Managed-turn conversation id, or a live terminal id from AKK list such as terminal:v2:tmux:codex:codex-work:0.1:33389."
|
|
156
153
|
},
|
|
157
154
|
expected_approval_fingerprint: {
|
|
158
155
|
type: "string",
|
|
@@ -160,171 +157,175 @@ const approveParameters = {
|
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
159
|
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
api
|
|
160
|
+
function createPlugin(relayPath) {
|
|
161
|
+
return definePluginEntry({
|
|
162
|
+
id: "agent-knock-knock",
|
|
163
|
+
name: "Agent Knock Knock",
|
|
164
|
+
description: "Agent Knock Knock (AKK/akk) lets OpenClaw operate local Codex and Claude Code through shared tmux terminals, with visible monitoring, approvals, callbacks, cancellation, and seamless human takeover.",
|
|
165
|
+
register(api) {
|
|
166
|
+
relayPathByApi.set(api, relayPath);
|
|
167
|
+
api.registerGatewayMethod(CALLBACK_METHOD, async ({ params, respond }) => {
|
|
168
|
+
try {
|
|
169
|
+
const result = await handleCallback(api, params);
|
|
170
|
+
respond(true, result);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
174
|
+
api.logger.warn?.(`agent-knock-knock callback failed: ${message}`);
|
|
175
|
+
respond(false, undefined, {
|
|
176
|
+
code: "AGENT_KNOCK_KNOCK_CALLBACK_FAILED",
|
|
177
|
+
message
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}, { scope: "operator.write" });
|
|
169
181
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
182
|
+
api.registerService?.({
|
|
183
|
+
id: "agent-knock-knock-monitor-reconciliation",
|
|
184
|
+
start() {
|
|
185
|
+
try {
|
|
186
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
187
|
+
const args = ["reconcile-monitors"];
|
|
188
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
189
|
+
const result = runCli(api, args);
|
|
190
|
+
api.logger.info?.("agent-knock-knock monitor reconciliation: " +
|
|
191
|
+
`checked=${result.checked ?? 0} launched=${result.launched ?? 0} ` +
|
|
192
|
+
`already_running=${result.already_running ?? 0} skipped=${result.skipped ?? 0} ` +
|
|
193
|
+
`errors=${result.errors ?? 0}`);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
197
|
+
api.logger.warn?.(`agent-knock-knock monitor reconciliation skipped after startup error: ${message}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
172
201
|
}
|
|
173
202
|
catch (error) {
|
|
174
203
|
const message = error instanceof Error ? error.message : String(error);
|
|
175
|
-
api.logger.warn?.(`agent-knock-knock
|
|
176
|
-
respond(false, undefined, {
|
|
177
|
-
code: "AGENT_KNOCK_KNOCK_CALLBACK_FAILED",
|
|
178
|
-
message
|
|
179
|
-
});
|
|
204
|
+
api.logger.warn?.(`agent-knock-knock monitor reconciliation service was not registered: ${message}`);
|
|
180
205
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
206
|
+
api.registerCommand?.({
|
|
207
|
+
name: "akk",
|
|
208
|
+
description: "Send coding work through existing Codex or Claude Code tmux terminals, inspect managed turns, approve exact prompts, and cancel running work.",
|
|
209
|
+
acceptsArgs: true,
|
|
210
|
+
requireAuth: true,
|
|
211
|
+
nativeProgressMessages: {
|
|
212
|
+
default: "AKK is handling the request..."
|
|
213
|
+
},
|
|
214
|
+
agentPromptGuidance: [
|
|
215
|
+
"Use /akk <task> when exactly one eligible idle coding-agent tmux pane should receive new work. Use /akk codex: <task>, /akk claude: <task>, or another selector returned by /akk list to target an existing pane. AKK never starts a coding agent."
|
|
216
|
+
],
|
|
217
|
+
handler: async (ctx) => handleAkkCommand(api, ctx)
|
|
218
|
+
});
|
|
219
|
+
registerCliTool(api, {
|
|
220
|
+
name: "agent_knock_knock_list",
|
|
221
|
+
description: "List existing Codex and Claude Code tmux panes as the primary terminals[] resources. Each terminal may include managed.current_turn or managed.recent_turn; all=true also includes older managed.history and retained unavailable history. By default, unavailable_managed_turns contains attention-needed records whose pane is unavailable. Use only each row's available_actions and its authoritative prefilled arguments: send starts a new turn on a terminal, while follow_up continues a specific managed turn. AKK revalidates every side effect and never starts a coding agent.",
|
|
222
|
+
parameters: listParameters,
|
|
223
|
+
buildArgs: (params) => {
|
|
224
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
225
|
+
const args = ["list", "--reconcile"];
|
|
226
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
227
|
+
pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
|
|
228
|
+
pushOptional(args, "--agent", stringValue(params.agent));
|
|
229
|
+
pushOptional(args, "--status", stringValue(params.status));
|
|
230
|
+
if (params.all === true) {
|
|
231
|
+
args.push("--all");
|
|
195
232
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
233
|
+
if (params.noApprovalScan === true) {
|
|
234
|
+
args.push("--no-approval-scan");
|
|
235
|
+
}
|
|
236
|
+
if (params.terminalDebug === true) {
|
|
237
|
+
args.push("--terminal-debug");
|
|
199
238
|
}
|
|
239
|
+
return args;
|
|
200
240
|
}
|
|
201
241
|
});
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
acceptsArgs: true,
|
|
211
|
-
requireAuth: true,
|
|
212
|
-
nativeProgressMessages: {
|
|
213
|
-
default: "AKK is handling the request..."
|
|
214
|
-
},
|
|
215
|
-
agentPromptGuidance: [
|
|
216
|
-
"Use /akk <task> when exactly one eligible idle coding-agent tmux pane should receive new work. Use /akk codex: <task>, /akk claude: <task>, or another selector returned by /akk list to target an existing pane. AKK never starts a coding agent."
|
|
217
|
-
],
|
|
218
|
-
handler: async (ctx) => handleAkkCommand(api, ctx)
|
|
219
|
-
});
|
|
220
|
-
registerCliTool(api, {
|
|
221
|
-
name: "agent_knock_knock_list",
|
|
222
|
-
description: "List AKK-managed work and existing Codex or Claude Code tmux terminals. Rows in delegated and terminal_controlled include available_actions with the exact tool name and authoritative prefilled target arguments for safe sending, inspection, approval, cancellation, and recovery. Use only actions present in that snapshot; AKK revalidates them before side effects. AKK never starts a coding agent.",
|
|
223
|
-
parameters: listParameters,
|
|
224
|
-
buildArgs: (params) => {
|
|
225
|
-
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
226
|
-
const args = ["list"];
|
|
227
|
-
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
228
|
-
pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(api.pluginConfig?.idleTimeoutMinutes));
|
|
229
|
-
pushOptional(args, "--agent", stringValue(params.agent));
|
|
230
|
-
pushOptional(args, "--status", stringValue(params.status));
|
|
231
|
-
if (params.all === true) {
|
|
232
|
-
args.push("--all");
|
|
242
|
+
api.registerTool((_toolContext) => ({
|
|
243
|
+
label: "AKK Status",
|
|
244
|
+
name: "agent_knock_knock_status",
|
|
245
|
+
description: "Inspect one AKK-managed turn or existing coding-agent tmux terminal. Returns live state, a bounded terminal screen, and available purpose/context with confidence and limitations. AKK never starts a coding agent.",
|
|
246
|
+
parameters: statusParameters,
|
|
247
|
+
async execute(_toolCallId, params) {
|
|
248
|
+
const result = runCli(api, buildStatusCliArgs(api, isRecord(params) ? params : {}));
|
|
249
|
+
return toolResult(result);
|
|
233
250
|
}
|
|
234
|
-
|
|
235
|
-
|
|
251
|
+
}), { name: "agent_knock_knock_status", optional: true });
|
|
252
|
+
api.registerTool((toolContext) => ({
|
|
253
|
+
label: "AKK Send",
|
|
254
|
+
name: "agent_knock_knock_send",
|
|
255
|
+
description: "Send through an existing Codex or Claude Code tmux terminal. Use a terminal row's send action to start a new managed turn, or a managed turn's follow_up action to continue that exact turn; both call this tool with the prefilled selector. Omit selector only when AKK should require one unique eligible idle pane. 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.",
|
|
256
|
+
parameters: sendParameters,
|
|
257
|
+
async execute(_toolCallId, params) {
|
|
258
|
+
const result = await runSendRequest(api, isRecord(params) ? params : {}, toolContext);
|
|
259
|
+
return toolResult(result);
|
|
236
260
|
}
|
|
237
|
-
|
|
238
|
-
|
|
261
|
+
}), { name: "agent_knock_knock_send", optional: true });
|
|
262
|
+
registerCliTool(api, {
|
|
263
|
+
name: "agent_knock_knock_approve",
|
|
264
|
+
description: "Manually approve the current AKK terminal permission request only after the user reviews and explicitly confirms it. Claude Code uses no Hooks: this manual path accepts only an exact one-time Bash permission screen for the current managed turn, then recaptures its short-lived evidence and process identity before sending Enter. Separately, trusted default-disabled plugin configuration can auto-approve an exact Claude command/workspace match without exposing policy control to the model. Hook-free durable completion is independently verified from the local Claude transcript.",
|
|
265
|
+
parameters: approveParameters,
|
|
266
|
+
buildArgs: (params) => {
|
|
267
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
268
|
+
const args = ["approve", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
269
|
+
pushOptional(args, "--expected-approval-fingerprint", requiredString(params.expected_approval_fingerprint, "expected_approval_fingerprint"));
|
|
270
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
271
|
+
return args;
|
|
239
272
|
}
|
|
240
|
-
|
|
241
|
-
|
|
273
|
+
});
|
|
274
|
+
registerCliTool(api, {
|
|
275
|
+
name: "agent_knock_knock_renew",
|
|
276
|
+
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.",
|
|
277
|
+
parameters: renewParameters,
|
|
278
|
+
buildArgs: (params) => {
|
|
279
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
280
|
+
const args = ["renew", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
281
|
+
pushOptional(args, "--minutes", numberString(params.minutes) ?? numberString(config.agentTimeoutMinutes));
|
|
282
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
283
|
+
return args;
|
|
242
284
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return args;
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
registerCliTool(api, {
|
|
291
|
-
name: "agent_knock_knock_retry_callback",
|
|
292
|
-
description: "Retry a persisted AKK callback that failed before reaching OpenClaw. The original callback message id is reused for idempotent delivery; a completed terminal task remains available for follow-up until idle cleanup.",
|
|
293
|
-
parameters: retryCallbackParameters,
|
|
294
|
-
buildArgs: (params) => {
|
|
295
|
-
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
296
|
-
const args = ["retry-callback", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
297
|
-
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
298
|
-
return args;
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
registerCliTool(api, {
|
|
302
|
-
name: "agent_knock_knock_cancel",
|
|
303
|
-
description: "Interrupt an existing Agent Knock Knock Codex or Claude Code tmux task. Claude sends Escape; Codex uses its declared interrupt key. The shared tmux pane remains open for human takeover.",
|
|
304
|
-
parameters: cancelParameters,
|
|
305
|
-
buildArgs: (params) => {
|
|
306
|
-
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
307
|
-
const args = ["cancel", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
308
|
-
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
309
|
-
pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(config.idleTimeoutMinutes));
|
|
310
|
-
return args;
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
registerCliTool(api, {
|
|
314
|
-
name: "agent_knock_knock_close",
|
|
315
|
-
description: "Close an AKK-managed task record without terminating the shared tmux pane. For an orphaned terminal dispatch only, the user must explicitly request recovery and provide the exact expected_message_id reported by AKK list.",
|
|
316
|
-
parameters: closeParameters,
|
|
317
|
-
buildArgs: (params) => {
|
|
318
|
-
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
319
|
-
const args = ["close", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
320
|
-
pushOptional(args, "--reason", stringValue(params.reason));
|
|
321
|
-
pushOptional(args, "--expected-message-id", stringValue(params.expected_message_id));
|
|
322
|
-
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
323
|
-
return args;
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
});
|
|
285
|
+
});
|
|
286
|
+
registerCliTool(api, {
|
|
287
|
+
name: "agent_knock_knock_retry_callback",
|
|
288
|
+
description: "Retry a persisted AKK callback that failed before reaching OpenClaw. The original callback message id is reused for idempotent delivery; a completed terminal task remains available for follow-up until its idle retention is reconciled.",
|
|
289
|
+
parameters: retryCallbackParameters,
|
|
290
|
+
buildArgs: (params) => {
|
|
291
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
292
|
+
const args = ["retry-callback", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
293
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
294
|
+
return args;
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
registerCliTool(api, {
|
|
298
|
+
name: "agent_knock_knock_cancel",
|
|
299
|
+
description: "Interrupt an existing Agent Knock Knock Codex or Claude Code tmux task. Claude sends Escape; Codex uses its declared interrupt key. The shared tmux pane remains open for human takeover.",
|
|
300
|
+
parameters: cancelParameters,
|
|
301
|
+
buildArgs: (params) => {
|
|
302
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
303
|
+
const args = ["cancel", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
304
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
305
|
+
pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ?? numberString(config.idleTimeoutMinutes));
|
|
306
|
+
return args;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
registerCliTool(api, {
|
|
310
|
+
name: "agent_knock_knock_close",
|
|
311
|
+
description: "Close an AKK-managed turn record without terminating the shared tmux pane. For an orphaned terminal dispatch only, the user must explicitly request recovery and provide the exact expected_message_id reported by AKK list.",
|
|
312
|
+
parameters: closeParameters,
|
|
313
|
+
buildArgs: (params) => {
|
|
314
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
315
|
+
const args = ["close", "--conversation", requiredString(params.conversation_id, "conversation_id")];
|
|
316
|
+
pushOptional(args, "--reason", stringValue(params.reason));
|
|
317
|
+
pushOptional(args, "--expected-message-id", stringValue(params.expected_message_id));
|
|
318
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
319
|
+
return args;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
const plugin = createPlugin(defaultBinPath);
|
|
326
|
+
export function createOpenClawPluginForTest(relayPath) {
|
|
327
|
+
return createPlugin(relayPath);
|
|
328
|
+
}
|
|
328
329
|
export default plugin;
|
|
329
330
|
async function handleAkkCommand(api, ctx) {
|
|
330
331
|
try {
|
|
@@ -409,7 +410,7 @@ function formatDelegateCommandResult(result) {
|
|
|
409
410
|
].join("\n");
|
|
410
411
|
}
|
|
411
412
|
return [
|
|
412
|
-
`AKK
|
|
413
|
+
`AKK sent the task to ${agent} in the shared terminal.`,
|
|
413
414
|
`conversation: ${result.conversation_id ?? "unknown"}`,
|
|
414
415
|
`session: ${result.session ?? "unknown"}`,
|
|
415
416
|
`status: ${result.conversation_status ?? result.status ?? "unknown"}`,
|
|
@@ -572,6 +573,7 @@ function buildStatusCliArgs(api, params) {
|
|
|
572
573
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
573
574
|
const args = [
|
|
574
575
|
"status",
|
|
576
|
+
"--reconcile",
|
|
575
577
|
"--conversation",
|
|
576
578
|
requiredString(params.conversation_id, "conversation_id")
|
|
577
579
|
];
|
|
@@ -676,11 +678,11 @@ async function runDelegate(api, params, toolContext) {
|
|
|
676
678
|
: submissionAborted
|
|
677
679
|
? "submission_aborted"
|
|
678
680
|
: "async_pending",
|
|
679
|
-
|
|
681
|
+
submission_status: submissionUncertain
|
|
680
682
|
? "uncertain"
|
|
681
683
|
: submissionAborted
|
|
682
684
|
? "aborted"
|
|
683
|
-
: "
|
|
685
|
+
: "accepted",
|
|
684
686
|
conversation_id: conversationId,
|
|
685
687
|
conversation_status: parsed.conversation?.status,
|
|
686
688
|
state_path: statePath,
|
|
@@ -752,8 +754,7 @@ function toolLabel(name) {
|
|
|
752
754
|
return `AKK ${action || "Tool"}`;
|
|
753
755
|
}
|
|
754
756
|
function runCli(api, cliArgs, { cwd = process.cwd(), allowNonzeroJson = false } = {}) {
|
|
755
|
-
const
|
|
756
|
-
const binPath = stringValue(config.binPath) ?? defaultBinPath;
|
|
757
|
+
const binPath = relayPathForApi(api);
|
|
757
758
|
const spawned = spawnSync(process.execPath, [binPath, ...cliArgs], {
|
|
758
759
|
encoding: "utf8",
|
|
759
760
|
maxBuffer: 1024 * 1024 * 10,
|
|
@@ -771,8 +772,7 @@ function runCli(api, cliArgs, { cwd = process.cwd(), allowNonzeroJson = false }
|
|
|
771
772
|
return parseJson(spawned.stdout);
|
|
772
773
|
}
|
|
773
774
|
function runCliAsync(api, cliArgs, { cwd = process.cwd(), allowNonzeroJson = false, timeoutMs = 90_000 } = {}) {
|
|
774
|
-
const
|
|
775
|
-
const binPath = stringValue(config.binPath) ?? defaultBinPath;
|
|
775
|
+
const binPath = relayPathForApi(api);
|
|
776
776
|
const maxBuffer = 10 * 1024 * 1024;
|
|
777
777
|
return new Promise((resolve, reject) => {
|
|
778
778
|
const child = spawn(process.execPath, [binPath, ...cliArgs], {
|
|
@@ -843,6 +843,9 @@ function runCliAsync(api, cliArgs, { cwd = process.cwd(), allowNonzeroJson = fal
|
|
|
843
843
|
});
|
|
844
844
|
});
|
|
845
845
|
}
|
|
846
|
+
function relayPathForApi(api) {
|
|
847
|
+
return relayPathByApi.get(api) ?? defaultBinPath;
|
|
848
|
+
}
|
|
846
849
|
async function handleCallback(api, params) {
|
|
847
850
|
if (!isRecord(params)) {
|
|
848
851
|
throw new Error("callback params must be an object");
|
|
@@ -954,8 +957,8 @@ function buildCallbackDeliveryPlan({ sessionKey, conversationId, messageId, mess
|
|
|
954
957
|
sessionKey,
|
|
955
958
|
message: [
|
|
956
959
|
"Continue this OpenClaw product-manager conversation from the Agent Knock Knock callback below.",
|
|
957
|
-
"Treat the callback as a structured message from the
|
|
958
|
-
"Respond in this conversation as OpenClaw product manager. If the callback is question or blocked, make the product decision and answer the
|
|
960
|
+
"Treat the callback as a structured message from the coding agent's managed terminal turn, not as a terminal log, status announcement, or instruction to inspect local state.",
|
|
961
|
+
"Respond in this conversation as OpenClaw product manager. If the callback is question or blocked, make the product decision and answer the coding agent. If it is done, summarize the result to the user.",
|
|
959
962
|
"Do not poll files, processes, sessions, stdout, or stderr. Use only the structured callback payload below.",
|
|
960
963
|
"",
|
|
961
964
|
formatted
|
|
@@ -990,9 +993,9 @@ function formatDoneShortcuts(conversationId) {
|
|
|
990
993
|
"",
|
|
991
994
|
"[AKK convenience commands]",
|
|
992
995
|
"When summarizing this result to the user, include these short next-step commands:",
|
|
993
|
-
"- `AKK list` lists live shared terminals
|
|
996
|
+
"- `AKK list` lists live shared terminals with their current or recent managed turns.",
|
|
994
997
|
"- Use the matching `@short-ref` from `AKK list`, then `AKK @short-ref: <message>` to continue in the same shared terminal.",
|
|
995
|
-
`- \`AKK status ${conversationId}\` shows this
|
|
998
|
+
`- \`AKK status ${conversationId}\` shows this managed turn.`,
|
|
996
999
|
"- AKK never starts or closes the coding agent or tmux pane."
|
|
997
1000
|
].join("\n");
|
|
998
1001
|
}
|