@scotthuang/agent-knock-knock 0.12.37 → 0.13.1
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 +46 -0
- package/README.md +3 -0
- package/dist/src/agent-session-provider.d.ts +5 -0
- package/dist/src/agent-session-provider.js +10 -1
- package/dist/src/agent-session-provider.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +2 -0
- package/dist/src/callback-outbox-service.js +1 -1
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/callback-transport.d.ts +7 -0
- package/dist/src/callback-transport.js +1 -1
- package/dist/src/callback-transport.js.map +1 -1
- package/dist/src/claude-lifecycle-compatibility.js +49 -2
- package/dist/src/claude-lifecycle-compatibility.js.map +1 -1
- package/dist/src/claude-terminal-agent-adapter.d.ts +1 -1
- package/dist/src/claude-terminal-agent-adapter.js +2 -2
- package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
- package/dist/src/cli-core.js +35 -32
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-store-adapter.d.ts +1 -1
- package/dist/src/codex-store-adapter.js +35 -8
- package/dist/src/codex-store-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.d.ts +20 -0
- package/dist/src/deferred-foreground-authority-cli-adapter.js +216 -8
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/host-adapter.d.ts +1 -1
- package/dist/src/host-adapter.js +1 -1
- package/dist/src/host-bridge-tools.js +2 -2
- package/dist/src/managed-turn-recovery-service.d.ts +8 -1
- package/dist/src/managed-turn-recovery-service.js +55 -13
- package/dist/src/managed-turn-recovery-service.js.map +1 -1
- package/dist/src/mutation-transaction.d.ts +1 -0
- package/dist/src/mutation-transaction.js +19 -15
- package/dist/src/mutation-transaction.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +39 -3
- package/dist/src/native-thread-lifecycle-cli-adapter.js +307 -7
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-callback-adapter.js +17 -7
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js +297 -83
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +57 -0
- package/dist/src/openclaw-plugin-schemas.js +60 -2
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/openclaw-private-authority-offers.d.ts +17 -0
- package/dist/src/openclaw-private-authority-offers.js +75 -3
- package/dist/src/openclaw-private-authority-offers.js.map +1 -1
- package/dist/src/store.d.ts +1 -1
- package/dist/src/store.js +6 -4
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-acceptance-application-service.js +2 -2
- package/dist/src/terminal-acceptance-application-service.js.map +1 -1
- package/dist/src/terminal-acceptance-cli-adapter.d.ts +11 -1
- package/dist/src/terminal-acceptance-cli-adapter.js +290 -16
- package/dist/src/terminal-acceptance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +1 -1
- package/dist/src/terminal-action-projection.js +6 -0
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-adapter.d.ts +9 -0
- package/dist/src/terminal-agent-adapter.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +52 -8
- package/dist/src/terminal-agent-bridge.js +131 -94
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-authority-policy.js +3 -0
- package/dist/src/terminal-authority-policy.js.map +1 -1
- package/dist/src/terminal-binding-authority.d.ts +2 -0
- package/dist/src/terminal-binding-authority.js +8 -0
- package/dist/src/terminal-binding-authority.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +17 -1
- package/dist/src/terminal-command-cli-adapter.js +784 -286
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-delegate-cli-adapter.js +21 -7
- package/dist/src/terminal-delegate-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-application.d.ts +3 -1
- package/dist/src/terminal-dispatch-application.js +13 -5
- package/dist/src/terminal-dispatch-application.js.map +1 -1
- package/dist/src/terminal-dispatch-composition.d.ts +43 -0
- package/dist/src/terminal-dispatch-execution.d.ts +2 -2
- package/dist/src/terminal-dispatch-execution.js +53 -3
- package/dist/src/terminal-dispatch-execution.js.map +1 -1
- package/dist/src/terminal-dispatch-policy.d.ts +20 -0
- package/dist/src/terminal-dispatch-policy.js +33 -1
- package/dist/src/terminal-dispatch-policy.js.map +1 -1
- package/dist/src/terminal-dispatch-presenter.d.ts +45 -0
- package/dist/src/terminal-dispatch-presenter.js +220 -18
- package/dist/src/terminal-dispatch-presenter.js.map +1 -1
- package/dist/src/terminal-dispatch-receipt.d.ts +5 -1
- package/dist/src/terminal-dispatch-receipt.js +26 -3
- package/dist/src/terminal-dispatch-receipt.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js +63 -1
- package/dist/src/terminal-dispatch-recovery-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-recovery-service.d.ts +1 -0
- package/dist/src/terminal-dispatch-recovery-service.js +7 -0
- package/dist/src/terminal-dispatch-recovery-service.js.map +1 -1
- package/dist/src/terminal-identity-authority-cli-adapter.js +103 -4
- package/dist/src/terminal-identity-authority-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-authority.d.ts +48 -1
- package/dist/src/terminal-interaction-authority.js +30 -0
- package/dist/src/terminal-interaction-authority.js.map +1 -1
- package/dist/src/terminal-interaction-cli-adapter.js +9 -0
- package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
- package/dist/src/terminal-interaction-core.d.ts +104 -0
- package/dist/src/terminal-interaction-core.js +407 -0
- package/dist/src/terminal-interaction-core.js.map +1 -0
- package/dist/src/terminal-interaction-protocol.d.ts +89 -0
- package/dist/src/terminal-interaction-protocol.js +199 -0
- package/dist/src/terminal-interaction-protocol.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +5 -0
- package/dist/src/terminal-list-cli-adapter.js +190 -40
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.js +60 -7
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-maintenance-cli-adapter.js +14 -3
- package/dist/src/terminal-maintenance-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +1 -0
- package/dist/src/terminal-monitor-application-service.js +72 -10
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +3 -0
- package/dist/src/terminal-monitor-cli-adapter.js +24 -8
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +15 -2
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.js +15 -1
- package/dist/src/terminal-monitor-state-reconciliation-service.js.map +1 -1
- package/dist/src/terminal-mutation-cli-runtime.d.ts +11 -1
- package/dist/src/terminal-mutation-cli-runtime.js +9 -4
- package/dist/src/terminal-mutation-cli-runtime.js.map +1 -1
- package/dist/src/terminal-questionnaire-adapter.d.ts +3 -1
- package/dist/src/terminal-questionnaire-adapter.js +34 -21
- package/dist/src/terminal-questionnaire-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +1 -7
- package/dist/src/terminal-status-cli-adapter.js +29 -3
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-facts.d.ts +17 -1
- package/dist/src/terminal-status-facts.js +59 -0
- package/dist/src/terminal-status-facts.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +37 -2
- package/dist/src/terminal-submission-acceptance.js +633 -30
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-submission-facts.d.ts +5 -0
- package/dist/src/terminal-submission-facts.js.map +1 -1
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +2 -1
- package/dist/src/terminal-watch-callback-cli-adapter.js +2 -1
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-cli-adapter.d.ts +6 -2
- package/dist/src/terminal-watch-cli-adapter.js +1051 -15
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -1
- package/dist/src/terminal-watch-service.d.ts +15 -1
- package/dist/src/terminal-watch-service.js +169 -13
- package/dist/src/terminal-watch-service.js.map +1 -1
- package/dist/src/terminal-watch-store.d.ts +72 -3
- package/dist/src/terminal-watch-store.js +541 -35
- package/dist/src/terminal-watch-store.js.map +1 -1
- package/docs/host-bridge-profiles.md +2 -2
- package/docs/quickstart-herdr.md +5 -3
- package/docs/quickstart-tmux.md +8 -6
- package/openclaw.plugin.json +8 -0
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +16 -12
|
@@ -6,9 +6,10 @@ import { executorDefinitionForKind } from "./executors.js";
|
|
|
6
6
|
import { isTerminalApprovalDecision } from "./terminal-agent-adapter.js";
|
|
7
7
|
import { isRecord, nonBlankString as stringValue } from "./value-guards.js";
|
|
8
8
|
import { AKK_CALLBACK_METHOD, akkUsageText, buildAkkCommandCliArgs, formatAkkListCommandResult, formatAkkRespondCommandResult, formatAkkTerminalWatchHint, formatAkkThreadsCommandResult, formatAkkThreadTransitionCommandResult, formatAkkUnwatchCommandResult, formatAkkWatchCommandResult, formatAkkWatchStatusCommandResult, isAkkModelFacingDiagnosticField, isAkkModelFacingPrivateAuthorityField, isAkkNativeSubmissionAccepted, isAkkThreadTransitionSuccess, normalizeAkkModelFacingFieldName, parseAkkCommand, resolvePluginStoreDir, sanitizeAkkModelFacingDiagnosticText, sanitizeAkkModelFacingLegacyAuthorityInstructionText } from "./openclaw-plugin-helpers.js";
|
|
9
|
-
import { approveParameters, cancelParameters, closeParameters, listParameters, listResumableThreadsParameters, nativeInspectParameters, newThreadParameters, reconcileBindingParameters, renewParameters, respondInteractionParameters, respondParameters, resumeThreadParameters, retryCallbackParameters, sendParameters, statusParameters, unwatchParameters, watchParameters } from "./openclaw-plugin-schemas.js";
|
|
10
|
-
import { consumeOpenClawPrivateAuthorityOffer, openClawApprovalAuthorityOfferKey, openClawInteractionAuthorityOfferKey, rememberOpenClawPrivateAuthorityOffer } from "./openclaw-private-authority-offers.js";
|
|
11
|
-
import { TERMINAL_INTERACTION_LIMITS,
|
|
9
|
+
import { approveParameters, cancelParameters, closeParameters, identifyAndSendParameters, identifyForegroundParameters, listParameters, listResumableThreadsParameters, nativeInspectParameters, newThreadParameters, reconcileBindingParameters, renewParameters, respondInteractionParameters, respondParameters, resumeThreadParameters, retryCallbackParameters, sendParameters, statusParameters, unwatchParameters, watchParameters } from "./openclaw-plugin-schemas.js";
|
|
10
|
+
import { consumeOpenClawPrivateAuthorityOffer, invalidateOpenClawInteractionAuthorityOffersForSubject, openClawApprovalAuthorityOfferKey, openClawInteractionAuthorityOfferKey, rememberOpenClawPrivateAuthorityOffer } from "./openclaw-private-authority-offers.js";
|
|
11
|
+
import { TERMINAL_INTERACTION_LIMITS, TERMINAL_INTERACTION_SUBJECT_VERSION, terminalInteractionSubjectId, validateAnyTerminalInteractionProjection, validateAnyTerminalInteractionResponse } from "./terminal-interaction-protocol.js";
|
|
12
|
+
import { normalizedTerminalSendResultContract, terminalSendEnterDispatched } from "./terminal-dispatch-presenter.js";
|
|
12
13
|
const MAX_DISPLAYED_RESUME_SNAPSHOTS = 512;
|
|
13
14
|
const OPENCLAW_HANDOFF_AUTHORITY_KIND = "handoff";
|
|
14
15
|
const OPENCLAW_RECONCILE_BINDING_AUTHORITY_KIND = "reconcile_binding";
|
|
@@ -167,6 +168,7 @@ export function registerOpenClawCommands(api, displayedResumeSnapshots) {
|
|
|
167
168
|
return args;
|
|
168
169
|
}
|
|
169
170
|
});
|
|
171
|
+
registerForegroundIdentificationTools(api);
|
|
170
172
|
registerCliTool(api, {
|
|
171
173
|
name: "agent_knock_knock_new_thread",
|
|
172
174
|
description: "Start and verify a clean native coding-agent thread in the exact terminal_id after explicit user intent. A valid unverified agent version remains callable with a compatibility warning. AKK refreshes lifecycle authority privately. Never send /clear as ordinary task text. This creates a new AKK Session but no Turn.",
|
|
@@ -274,7 +276,7 @@ export function registerOpenClawCommands(api, displayedResumeSnapshots) {
|
|
|
274
276
|
async execute(_toolCallId, params, signal) {
|
|
275
277
|
return withHostBridgeInvocationSignal(signal, async () => {
|
|
276
278
|
try {
|
|
277
|
-
const result = await
|
|
279
|
+
const result = await runStatusRequest(api, params, toolContext);
|
|
278
280
|
const rendered = toolResult(result);
|
|
279
281
|
rememberDisplayedApprovalOffer(api, toolContext?.sessionKey, toolContext?.sessionId, result);
|
|
280
282
|
rememberDisplayedInteractionOffer(api, toolContext?.sessionKey, toolContext?.sessionId, result);
|
|
@@ -289,7 +291,7 @@ export function registerOpenClawCommands(api, displayedResumeSnapshots) {
|
|
|
289
291
|
api.registerTool((toolContext) => ({
|
|
290
292
|
label: "AKK Send",
|
|
291
293
|
name: "agent_knock_knock_send",
|
|
292
|
-
description: "Start a new AKK Turn, use one advertised terminal_user_explicit user-priority send, or explicitly recover one current uncertain submission only through its advertised retry_submission action. Ordinary send requires request and may use session_id or terminal_id exactly as advertised. terminal_user_explicit requires one exact live physical terminal/process
|
|
294
|
+
description: "Start a new AKK Turn, use one advertised terminal_user_explicit user-priority send, or explicitly recover one current uncertain submission only through its advertised retry_submission action. Ordinary send requires request and may use session_id or terminal_id exactly as advertised. terminal_user_explicit requires one exact live physical terminal/process, a scanned non-blocked approval state, and no active native questionnaire; parsed working activity, Codex rollout ambiguity, AKK management state, and Codex Composer visibility, stability, or exactness do not veto physical delivery. Codex physical fallback sends C-u once to replace the current Composer, injects the request, waits through the paste window, and dispatches Enter exactly once without a post-text Composer veto; Claude Code remains exact-empty-only. A source-less Codex terminal freezes all current rollout roots before input, then promotes a provisional Session/Turn only when exactly one anchored or newly opened rollout durably accepts the exact request hash; zero matches remain pending and ambiguity becomes uncertain without replay. If managed preparation fails before input, AKK still delivers once as unmanaged work, then best-effort attaches an exact Terminal Watch callback. After exact request acceptance and terminal attribution, a supported questionnaire on that Watch may expose owner-bound response authority through Status and its watch_id; terminal-activity observations and manual_required interactions remain notification-only. Read terminal_input_dispatched, agent_acceptance, management_mode, observation_mode, and capabilities independently. Watch attachment failure never changes a successful Send. Once the mutation sequence begins, an uncertain result must not be automatically retried. Retry submission is the mutually exclusive exact {turn_id} form and cannot change request text or routing. Draft text, composer digests, and opaque freshness authority stay private. A Turn id is never an ordinary-send destination. Managed acceptance is asynchronous: yield and wait for its callback or an explicit status request.",
|
|
293
295
|
parameters: sendParameters,
|
|
294
296
|
async execute(toolCallId, params, signal) {
|
|
295
297
|
return withHostBridgeInvocationSignal(signal, async () => {
|
|
@@ -335,7 +337,7 @@ export function registerOpenClawCommands(api, displayedResumeSnapshots) {
|
|
|
335
337
|
});
|
|
336
338
|
registerCliTool(api, {
|
|
337
339
|
name: "agent_knock_knock_respond_interaction",
|
|
338
|
-
description: "Answer exactly one current native questionnaire step shown by agent_knock_knock_status in this controller conversation. Supply
|
|
340
|
+
description: "Answer exactly one current native questionnaire step shown by agent_knock_knock_status in this controller conversation. Supply exactly one authoritative subject target: turn_id for a managed Turn or watch_id for an interactive Terminal Watch, plus interaction_id and one typed semantic answer. single_select uses selected_option_ids with one advertised option_id; free_text uses text; confirm uses confirm. AKK consumes only that subject's displayed private offer and revalidates the exact discriminator-specific shape, prompt, owner, task attribution, and terminal authority before any input. A displayed expiry is a freshness boundary: after it passes, AKK must prove the same exact live questionnaire again instead of rejecting an otherwise live prompt. Raw keys, menu indexes, rendered labels, fingerprints, versions, and terminal commands are never accepted. An uncertain response must never be retried blindly.",
|
|
339
341
|
parameters: respondInteractionParameters,
|
|
340
342
|
buildArgs: (params, toolContext) => buildPrivateInteractionResponseArgs(api, params, {
|
|
341
343
|
sessionKey: requiredOpenClawSessionKey(toolContext?.sessionKey),
|
|
@@ -407,6 +409,70 @@ export function registerOpenClawCommands(api, displayedResumeSnapshots) {
|
|
|
407
409
|
}
|
|
408
410
|
});
|
|
409
411
|
}
|
|
412
|
+
function registerForegroundIdentificationTools(api) {
|
|
413
|
+
registerCliTool(api, {
|
|
414
|
+
name: "agent_knock_knock_identify_foreground",
|
|
415
|
+
description: "Explicitly identify the foreground Codex native thread in one exact idle terminal by issuing the closed /status probe once. The result is a short-lived diagnostic bound to the current pane, process, cwd, and screen generation; it creates no Session or Turn and grants no later response, approval, lifecycle, or send authority. Ordinary list/status never runs this probe.",
|
|
416
|
+
parameters: identifyForegroundParameters,
|
|
417
|
+
normalizeTurnIdentity: false,
|
|
418
|
+
buildArgs: async (params) => {
|
|
419
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
420
|
+
const terminalId = requiredString(params.terminal_id, "terminal_id");
|
|
421
|
+
const action = await privateTerminalActionArguments(api, terminalId, "agent_knock_knock_identify_foreground", { reconcile: false });
|
|
422
|
+
const args = [
|
|
423
|
+
"identify-foreground",
|
|
424
|
+
"--terminal",
|
|
425
|
+
terminalId,
|
|
426
|
+
"--expected-terminal-token",
|
|
427
|
+
requiredString(action.expected_terminal_token, "current internal foreground-identification terminal authority")
|
|
428
|
+
];
|
|
429
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
430
|
+
pushOptional(args, "--codex-home", stringValue(config.codexHome));
|
|
431
|
+
return args;
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
registerCliTool(api, {
|
|
435
|
+
name: "agent_knock_knock_identify_and_send",
|
|
436
|
+
description: "Explicitly identify the foreground Codex native thread with one closed /status probe, then dispatch one task while retaining the same terminal lock. This is an optional managed-attachment enhancement, not a prerequisite for ordinary human Send. The short-lived status observation never becomes durable identity: only the rollout that uniquely accepts the exact task may own the resulting Session/Turn. If the probe or boundary becomes uncertain, AKK does not send or retry the task.",
|
|
437
|
+
parameters: identifyAndSendParameters,
|
|
438
|
+
isErrorResult: isSubmissionError,
|
|
439
|
+
buildArgs: async (params, toolContext, toolCallId) => {
|
|
440
|
+
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
441
|
+
const terminalId = requiredString(params.terminal_id, "terminal_id");
|
|
442
|
+
const action = await privateTerminalActionArguments(api, terminalId, "agent_knock_knock_identify_and_send", { reconcile: false });
|
|
443
|
+
const openclawSession = stringValue(toolContext?.sessionKey) ?? "agent:main:main";
|
|
444
|
+
const args = [
|
|
445
|
+
"send",
|
|
446
|
+
"--conversation",
|
|
447
|
+
terminalId,
|
|
448
|
+
"--expected-terminal-token",
|
|
449
|
+
requiredString(action.expected_terminal_token, "current internal identify-and-send terminal authority"),
|
|
450
|
+
"--identify-foreground",
|
|
451
|
+
"--message",
|
|
452
|
+
requiredString(params.request, "request"),
|
|
453
|
+
"--background"
|
|
454
|
+
];
|
|
455
|
+
pushOptional(args, "--message-id", terminalMessageIdForToolCall({
|
|
456
|
+
toolCallId,
|
|
457
|
+
sessionKey: openclawSession,
|
|
458
|
+
sessionId: toolContext?.sessionId,
|
|
459
|
+
toolName: "agent_knock_knock_identify_and_send"
|
|
460
|
+
}));
|
|
461
|
+
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
462
|
+
pushOptional(args, "--idle-timeout-minutes", numberString(params.idleTimeoutMinutes) ??
|
|
463
|
+
numberString(config.idleTimeoutMinutes));
|
|
464
|
+
pushOptional(args, "--agent-timeout-minutes", numberString(params.agentTimeoutMinutes) ??
|
|
465
|
+
numberString(config.agentTimeoutMinutes));
|
|
466
|
+
pushOptional(args, "--agent-hard-timeout-minutes", numberString(params.agentHardTimeoutMinutes) ??
|
|
467
|
+
numberString(config.agentHardTimeoutMinutes));
|
|
468
|
+
pushOptional(args, "--openclaw-session", openclawSession);
|
|
469
|
+
pushOptional(args, "--gateway-method", CALLBACK_METHOD);
|
|
470
|
+
pushOptional(args, "--gateway-session", openclawSession);
|
|
471
|
+
pushOptional(args, "--openclaw-bin", stringValue(config.openclawBin));
|
|
472
|
+
return args;
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
}
|
|
410
476
|
function resumeSelectionScope(sessionKey, sessionId) {
|
|
411
477
|
const key = requiredString(sessionKey, "Controller session identity is required for snapshot-bound Resume");
|
|
412
478
|
const incarnation = stringValue(sessionId) ?? null;
|
|
@@ -459,15 +525,11 @@ async function handleAkkCommand(api, ctx, displayedResumeSnapshots) {
|
|
|
459
525
|
}, {
|
|
460
526
|
sessionKey: ctx.sessionKey
|
|
461
527
|
});
|
|
462
|
-
if (result.scope === "terminal_user_explicit") {
|
|
463
|
-
return {
|
|
464
|
-
text: formatSendCommandResult(result),
|
|
465
|
-
isError: sendCommandResultIsError(result)
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
528
|
return {
|
|
469
|
-
text:
|
|
470
|
-
|
|
529
|
+
text: result.scope === "terminal_user_explicit"
|
|
530
|
+
? formatSendCommandResult(result)
|
|
531
|
+
: formatDelegateCommandResult(result),
|
|
532
|
+
isError: sendCommandResultIsError(result)
|
|
471
533
|
};
|
|
472
534
|
}
|
|
473
535
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
@@ -546,13 +608,15 @@ async function handleAkkCommand(api, ctx, displayedResumeSnapshots) {
|
|
|
546
608
|
}
|
|
547
609
|
}
|
|
548
610
|
function sendCommandResultIsError(result) {
|
|
549
|
-
return result
|
|
550
|
-
result.scope === "terminal_user_explicit"
|
|
611
|
+
return isSuccessfulTerminalDispatch(result)
|
|
551
612
|
? false
|
|
552
613
|
: terminalSubmissionReported(result)
|
|
553
614
|
? !isAkkNativeSubmissionAccepted(result)
|
|
554
615
|
: result.status === "delivered_unfenced";
|
|
555
616
|
}
|
|
617
|
+
function isSuccessfulTerminalDispatch(result) {
|
|
618
|
+
return isRecord(result) && terminalSendEnterDispatched(result);
|
|
619
|
+
}
|
|
556
620
|
async function handleAkkLifecycleCommand(api, ctx, parsed, config, displayedResumeSnapshots) {
|
|
557
621
|
const selectionScope = resumeSelectionScope(ctx.sessionKey, ctx.sessionId);
|
|
558
622
|
const snapshotCacheKey = resumeSnapshotCacheKey(ctx.sessionKey, ctx.sessionId, parsed.terminalId);
|
|
@@ -857,48 +921,66 @@ function formatRetryCallbackCommandResult(result) {
|
|
|
857
921
|
`attempts: ${result.conversation?.callback_delivery?.attempts ?? "unknown"}`
|
|
858
922
|
].join("\n");
|
|
859
923
|
}
|
|
860
|
-
function
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const watchCallback = unmanaged &&
|
|
865
|
-
result.callback_expected === true &&
|
|
866
|
-
result.callback_mode === "terminal_watch" &&
|
|
867
|
-
typeof result.watch_id === "string"
|
|
868
|
-
? result.watch_id
|
|
869
|
-
: undefined;
|
|
870
|
-
if (result.delivered !== true) {
|
|
871
|
-
return [
|
|
872
|
-
"AKK already dispatched this managed terminal Send; native acceptance is still pending.",
|
|
873
|
-
`terminal: ${result.terminal_id ?? "unknown"}`,
|
|
874
|
-
`message: ${result.message_id ?? "unknown"}`,
|
|
875
|
-
"delivery: Enter dispatched; do not resend this message id.",
|
|
876
|
-
"next: inspect AKK status or the terminal; wait for the existing managed Turn."
|
|
877
|
-
].join("\n");
|
|
878
|
-
}
|
|
924
|
+
function formatUnconfirmedTerminalUserExplicitSendResult(result) {
|
|
925
|
+
const terminalInputDispatched = result.terminal_input_dispatched === true;
|
|
926
|
+
const enterDispatched = terminalSendEnterDispatched(result);
|
|
927
|
+
if (!enterDispatched) {
|
|
879
928
|
return [
|
|
880
|
-
|
|
881
|
-
?
|
|
882
|
-
|
|
883
|
-
: "AKK delivered the user's request directly to the coding agent."
|
|
884
|
-
: replayed
|
|
885
|
-
? "AKK confirmed this managed terminal Send was already delivered."
|
|
886
|
-
: "AKK delivered the user's terminal Send through managed routing.",
|
|
929
|
+
terminalInputDispatched
|
|
930
|
+
? "AKK started this terminal Send, but could not prove that Enter was dispatched."
|
|
931
|
+
: "AKK stopped this terminal Send before any terminal input was proven.",
|
|
887
932
|
`terminal: ${result.terminal_id ?? "unknown"}`,
|
|
888
933
|
`message: ${result.message_id ?? "unknown"}`,
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
: "callback: unavailable; no managed AKK Turn was created."]
|
|
894
|
-
: []),
|
|
895
|
-
unmanaged
|
|
896
|
-
? watchCallback
|
|
897
|
-
? "next: wait for the Terminal Watch callback; watch-status is the recovery path."
|
|
898
|
-
: "next: refresh AKK list and use Watch to observe the still-running coding-agent task."
|
|
899
|
-
: "next: refresh AKK list; do not resend this message id."
|
|
934
|
+
terminalInputDispatched
|
|
935
|
+
? "delivery: text input may be present; do not resend this message id."
|
|
936
|
+
: "delivery: no terminal input proven.",
|
|
937
|
+
"next: inspect AKK status and the terminal before deciding whether to continue."
|
|
900
938
|
].join("\n");
|
|
901
939
|
}
|
|
940
|
+
return [
|
|
941
|
+
"AKK already dispatched this managed terminal Send; native acceptance is still pending.",
|
|
942
|
+
`terminal: ${result.terminal_id ?? "unknown"}`,
|
|
943
|
+
`message: ${result.message_id ?? "unknown"}`,
|
|
944
|
+
"delivery: Enter dispatched; do not resend this message id.",
|
|
945
|
+
"next: inspect AKK status or the terminal; wait for the existing managed Turn."
|
|
946
|
+
].join("\n");
|
|
947
|
+
}
|
|
948
|
+
function formatTerminalUserExplicitSendResult(result) {
|
|
949
|
+
if (!terminalSendEnterDispatched(result)) {
|
|
950
|
+
return formatUnconfirmedTerminalUserExplicitSendResult(result);
|
|
951
|
+
}
|
|
952
|
+
const unmanaged = result.delivered_unmanaged === true;
|
|
953
|
+
const replayed = result.replayed === true;
|
|
954
|
+
const watchCallback = unmanaged &&
|
|
955
|
+
result.callback_expected === true &&
|
|
956
|
+
result.callback_mode === "terminal_watch" &&
|
|
957
|
+
typeof result.watch_id === "string"
|
|
958
|
+
? result.watch_id
|
|
959
|
+
: undefined;
|
|
960
|
+
return [
|
|
961
|
+
unmanaged
|
|
962
|
+
? replayed
|
|
963
|
+
? "AKK confirmed this direct terminal Send was already delivered."
|
|
964
|
+
: "AKK delivered the user's request directly to the coding agent."
|
|
965
|
+
: replayed
|
|
966
|
+
? "AKK confirmed this managed terminal Send was already delivered."
|
|
967
|
+
: "AKK delivered the user's terminal Send through managed routing.",
|
|
968
|
+
`terminal: ${result.terminal_id ?? "unknown"}`,
|
|
969
|
+
`message: ${result.message_id ?? "unknown"}`,
|
|
970
|
+
`delivery: ${unmanaged ? "unmanaged fallback" : "managed"}`,
|
|
971
|
+
...(unmanaged
|
|
972
|
+
? [watchCallback
|
|
973
|
+
? `callback: Terminal Watch ${watchCallback}; no managed AKK Turn was created.`
|
|
974
|
+
: "callback: unavailable; no managed AKK Turn was created."]
|
|
975
|
+
: []),
|
|
976
|
+
unmanaged
|
|
977
|
+
? watchCallback
|
|
978
|
+
? "next: wait for the Terminal Watch callback; watch-status is the recovery path."
|
|
979
|
+
: "next: refresh AKK list and use Watch to observe the still-running coding-agent task."
|
|
980
|
+
: "next: refresh AKK list; do not resend this message id."
|
|
981
|
+
].join("\n");
|
|
982
|
+
}
|
|
983
|
+
function formatManagedSendCommandResult(result) {
|
|
902
984
|
const conversation = result.conversation ?? {};
|
|
903
985
|
const conversationId = conversation.conversation_id ?? result.conversation_id ?? "unknown";
|
|
904
986
|
const sessionId = conversation.session_id ?? result.session_id ?? conversationId;
|
|
@@ -979,6 +1061,11 @@ function formatSendCommandResult(result) {
|
|
|
979
1061
|
: `launched: ${result.launched === true ? "yes" : "no"}`
|
|
980
1062
|
].join("\n");
|
|
981
1063
|
}
|
|
1064
|
+
function formatSendCommandResult(result) {
|
|
1065
|
+
return result.scope === "terminal_user_explicit"
|
|
1066
|
+
? formatTerminalUserExplicitSendResult(result)
|
|
1067
|
+
: formatManagedSendCommandResult(result);
|
|
1068
|
+
}
|
|
982
1069
|
function formatCancelCommandResult(result) {
|
|
983
1070
|
const conversation = result.conversation ?? {};
|
|
984
1071
|
const { sessionId, turnId } = publicTurnIdentity(result);
|
|
@@ -1066,7 +1153,7 @@ function publicTurnIdentity(result) {
|
|
|
1066
1153
|
"unknown"
|
|
1067
1154
|
};
|
|
1068
1155
|
}
|
|
1069
|
-
function buildStatusCliArgs(api, params) {
|
|
1156
|
+
function buildStatusCliArgs(api, params, toolContext) {
|
|
1070
1157
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
1071
1158
|
if (Object.hasOwn(params, "watch_id")) {
|
|
1072
1159
|
if (Object.hasOwn(params, "turn_id") ||
|
|
@@ -1077,7 +1164,9 @@ function buildStatusCliArgs(api, params) {
|
|
|
1077
1164
|
const watchArgs = [
|
|
1078
1165
|
"watch-status",
|
|
1079
1166
|
"--watch",
|
|
1080
|
-
watchId
|
|
1167
|
+
watchId,
|
|
1168
|
+
"--openclaw-session",
|
|
1169
|
+
requiredOpenClawSessionKey(toolContext?.sessionKey)
|
|
1081
1170
|
];
|
|
1082
1171
|
pushOptional(watchArgs, "--store-dir", resolvePluginStoreDir(config));
|
|
1083
1172
|
return watchArgs;
|
|
@@ -1095,6 +1184,12 @@ function buildStatusCliArgs(api, params) {
|
|
|
1095
1184
|
}
|
|
1096
1185
|
return args;
|
|
1097
1186
|
}
|
|
1187
|
+
async function runStatusRequest(api, params, toolContext) {
|
|
1188
|
+
const statusParams = isRecord(params) ? params : {};
|
|
1189
|
+
const statusArgs = buildStatusCliArgs(api, statusParams, toolContext);
|
|
1190
|
+
invalidateRequestedInteractionOffers(api, toolContext?.sessionKey, toolContext?.sessionId, statusParams);
|
|
1191
|
+
return runHostAwareCli(api, statusArgs);
|
|
1192
|
+
}
|
|
1098
1193
|
function terminalScreenExcerpt(result) {
|
|
1099
1194
|
const screen = result.terminal_screen;
|
|
1100
1195
|
const text = typeof screen === "string"
|
|
@@ -1126,7 +1221,11 @@ async function runSendRequest(api, params, toolContext, messageId) {
|
|
|
1126
1221
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
1127
1222
|
const args = ["send", "--turn", turnId];
|
|
1128
1223
|
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
1129
|
-
|
|
1224
|
+
const result = await runHostAwareCli(api, args);
|
|
1225
|
+
return {
|
|
1226
|
+
...result,
|
|
1227
|
+
...normalizedTerminalSendResultContract(result)
|
|
1228
|
+
};
|
|
1130
1229
|
}
|
|
1131
1230
|
const requestedType = Object.hasOwn(params, "type")
|
|
1132
1231
|
? stringValue(params.type)
|
|
@@ -1160,6 +1259,9 @@ async function runSendRequest(api, params, toolContext, messageId) {
|
|
|
1160
1259
|
const expectedTerminalToken = terminalAction
|
|
1161
1260
|
? requiredString(terminalAction.expected_terminal_token, "current internal terminal send authority")
|
|
1162
1261
|
: undefined;
|
|
1262
|
+
const expectedManagedTerminalToken = terminalAction
|
|
1263
|
+
? stringValue(terminalAction.expected_managed_terminal_token)
|
|
1264
|
+
: undefined;
|
|
1163
1265
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
1164
1266
|
const openclawSession = stringValue(toolContext?.sessionKey) ??
|
|
1165
1267
|
"agent:main:main";
|
|
@@ -1173,6 +1275,7 @@ async function runSendRequest(api, params, toolContext, messageId) {
|
|
|
1173
1275
|
args.push("--conversation", requiredString(terminalId, "terminal_id"));
|
|
1174
1276
|
}
|
|
1175
1277
|
pushOptional(args, "--expected-terminal-token", expectedTerminalToken);
|
|
1278
|
+
pushOptional(args, "--expected-managed-terminal-token", expectedManagedTerminalToken);
|
|
1176
1279
|
args.push("--message", requiredString(params.request, "request"), "--background");
|
|
1177
1280
|
pushOptional(args, "--type", stringValue(params.type));
|
|
1178
1281
|
pushOptional(args, "--message-id", messageId);
|
|
@@ -1187,7 +1290,11 @@ async function runSendRequest(api, params, toolContext, messageId) {
|
|
|
1187
1290
|
pushOptional(args, "--gateway-method", CALLBACK_METHOD);
|
|
1188
1291
|
pushOptional(args, "--gateway-session", openclawSession);
|
|
1189
1292
|
pushOptional(args, "--openclaw-bin", stringValue(config.openclawBin));
|
|
1190
|
-
|
|
1293
|
+
const result = await runHostAwareCli(api, args);
|
|
1294
|
+
return {
|
|
1295
|
+
...result,
|
|
1296
|
+
...normalizedTerminalSendResultContract(result)
|
|
1297
|
+
};
|
|
1191
1298
|
}
|
|
1192
1299
|
function toolResult(result, { submissionErrors = false, normalizeTurnIdentity = true, forceError = false } = {}) {
|
|
1193
1300
|
const normalized = normalizeTurnIdentity ? withTurnIdentity(result) : result;
|
|
@@ -1204,9 +1311,11 @@ function toolResult(result, { submissionErrors = false, normalizeTurnIdentity =
|
|
|
1204
1311
|
...(submissionError || forceError ? { isError: true } : {})
|
|
1205
1312
|
};
|
|
1206
1313
|
}
|
|
1207
|
-
async function privateList(api) {
|
|
1314
|
+
async function privateList(api, options = {}) {
|
|
1208
1315
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
1209
|
-
const args = ["list"
|
|
1316
|
+
const args = ["list"];
|
|
1317
|
+
if (options.reconcile !== false)
|
|
1318
|
+
args.push("--reconcile");
|
|
1210
1319
|
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
1211
1320
|
pushOptional(args, "--idle-timeout-minutes", numberString(config.idleTimeoutMinutes));
|
|
1212
1321
|
return runHostAwareCli(api, args);
|
|
@@ -1222,15 +1331,16 @@ async function privateThreadDiscovery(api, terminalId) {
|
|
|
1222
1331
|
pushOptional(args, "--codex-home", stringValue(config.codexHome));
|
|
1223
1332
|
return runHostAwareCli(api, args);
|
|
1224
1333
|
}
|
|
1225
|
-
async function privateTerminalActionArguments(api, terminalId, tool) {
|
|
1334
|
+
async function privateTerminalActionArguments(api, terminalId, tool, options = {}) {
|
|
1226
1335
|
return privateActionArguments(api, {
|
|
1227
1336
|
tool,
|
|
1228
1337
|
terminalId,
|
|
1338
|
+
reconcile: options.reconcile,
|
|
1229
1339
|
matches: (argumentsValue) => stringValue(argumentsValue.terminal_id) === terminalId
|
|
1230
1340
|
});
|
|
1231
1341
|
}
|
|
1232
1342
|
async function privateActionArguments(api, input) {
|
|
1233
|
-
const result = await privateList(api);
|
|
1343
|
+
const result = await privateList(api, { reconcile: input.reconcile });
|
|
1234
1344
|
const terminals = input.terminalId
|
|
1235
1345
|
? terminalRows(result).filter((terminal) => stringValue(terminal.id) === input.terminalId)
|
|
1236
1346
|
: terminalRows(result);
|
|
@@ -1299,29 +1409,90 @@ function rememberDisplayedInteractionOffer(api, sessionKeyValue, sessionIdValue,
|
|
|
1299
1409
|
const sessionId = stringValue(sessionIdValue);
|
|
1300
1410
|
if (!sessionKey || !sessionId || !isRecord(result))
|
|
1301
1411
|
return;
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
const
|
|
1412
|
+
const displayed = displayedInteractionStatus(result);
|
|
1413
|
+
if (!displayed)
|
|
1414
|
+
return;
|
|
1415
|
+
const expectedSubjectId = stringValue(displayed.expectedSubjectId);
|
|
1416
|
+
if (!expectedSubjectId || expectedSubjectId === "unknown")
|
|
1417
|
+
return;
|
|
1418
|
+
invalidateOpenClawInteractionAuthorityOffersForSubject(api, sessionKey, sessionId, displayed.expectedSubjectKind, expectedSubjectId);
|
|
1419
|
+
const fingerprint = stringValue(displayed.fields.interaction_prompt_fingerprint);
|
|
1306
1420
|
if (!isExactInteractionFingerprint(fingerprint))
|
|
1307
1421
|
return;
|
|
1308
1422
|
let interactionState;
|
|
1309
1423
|
try {
|
|
1310
|
-
interactionState =
|
|
1424
|
+
interactionState = validateAnyTerminalInteractionProjection(displayed.fields.interaction_state);
|
|
1311
1425
|
}
|
|
1312
1426
|
catch {
|
|
1313
1427
|
return;
|
|
1314
1428
|
}
|
|
1429
|
+
const subject = interactionProjectionSubject(interactionState);
|
|
1315
1430
|
if (interactionState.state !== "pending" ||
|
|
1316
1431
|
interactionState.capabilities.respond !== true ||
|
|
1317
|
-
|
|
1432
|
+
subject.kind !== displayed.expectedSubjectKind ||
|
|
1433
|
+
subject.id !== expectedSubjectId ||
|
|
1434
|
+
(interactionState.version === TERMINAL_INTERACTION_SUBJECT_VERSION &&
|
|
1435
|
+
(interactionState.response_authority !== "executable" ||
|
|
1436
|
+
interactionState.prompt_fingerprint !== fingerprint))) {
|
|
1318
1437
|
return;
|
|
1319
1438
|
}
|
|
1320
|
-
rememberOpenClawPrivateAuthorityOffer(api, openClawInteractionAuthorityOfferKey(sessionKey, sessionId,
|
|
1439
|
+
rememberOpenClawPrivateAuthorityOffer(api, openClawInteractionAuthorityOfferKey(sessionKey, sessionId, subject.kind, subject.id, interactionState.interaction_id), {
|
|
1321
1440
|
fingerprint,
|
|
1322
1441
|
interaction_state: interactionState
|
|
1323
1442
|
});
|
|
1324
1443
|
}
|
|
1444
|
+
function invalidateRequestedInteractionOffers(api, sessionKeyValue, sessionIdValue, params) {
|
|
1445
|
+
const sessionKey = stringValue(sessionKeyValue);
|
|
1446
|
+
const sessionId = stringValue(sessionIdValue);
|
|
1447
|
+
if (!sessionKey || !sessionId)
|
|
1448
|
+
return;
|
|
1449
|
+
const watchId = stringValue(params.watch_id);
|
|
1450
|
+
if (watchId) {
|
|
1451
|
+
invalidateOpenClawInteractionAuthorityOffersForSubject(api, sessionKey, sessionId, "terminal_watch", watchId);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
const turnId = stringValue(params.turn_id);
|
|
1455
|
+
if (!turnId)
|
|
1456
|
+
return;
|
|
1457
|
+
invalidateOpenClawInteractionAuthorityOffersForSubject(api, sessionKey, sessionId, "managed_turn", turnId);
|
|
1458
|
+
}
|
|
1459
|
+
function displayedInteractionStatus(result) {
|
|
1460
|
+
const watch = isRecord(result.watch) ? result.watch : undefined;
|
|
1461
|
+
if (watch) {
|
|
1462
|
+
return {
|
|
1463
|
+
fields: watch,
|
|
1464
|
+
expectedSubjectKind: "terminal_watch",
|
|
1465
|
+
expectedSubjectId: stringValue(watch.watch_id)
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
const terminalStatus = isRecord(result.terminal_status)
|
|
1469
|
+
? result.terminal_status
|
|
1470
|
+
: undefined;
|
|
1471
|
+
if (terminalStatus) {
|
|
1472
|
+
return {
|
|
1473
|
+
fields: terminalStatus,
|
|
1474
|
+
expectedSubjectKind: "managed_turn",
|
|
1475
|
+
expectedSubjectId: publicTurnIdentity(result).turnId
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
if (result.interaction_state === undefined)
|
|
1479
|
+
return undefined;
|
|
1480
|
+
const watchId = stringValue(result.watch_id);
|
|
1481
|
+
return {
|
|
1482
|
+
fields: result,
|
|
1483
|
+
expectedSubjectKind: watchId ? "terminal_watch" : "managed_turn",
|
|
1484
|
+
expectedSubjectId: watchId ?? publicTurnIdentity(result).turnId
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
function interactionProjectionSubject(projection) {
|
|
1488
|
+
if (projection.version === TERMINAL_INTERACTION_SUBJECT_VERSION) {
|
|
1489
|
+
return {
|
|
1490
|
+
kind: projection.subject.kind,
|
|
1491
|
+
id: terminalInteractionSubjectId(projection.subject)
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
return { kind: "managed_turn", id: projection.turn_id };
|
|
1495
|
+
}
|
|
1325
1496
|
function approvalTargetFromStatus(result) {
|
|
1326
1497
|
const conversationId = stringValue(result.conversation_id);
|
|
1327
1498
|
if (stringValue(result.source) === "terminal_control" ||
|
|
@@ -1470,26 +1641,28 @@ async function buildPrivateApprovalArgs(api, params, { sessionKey, sessionId })
|
|
|
1470
1641
|
}
|
|
1471
1642
|
function buildPrivateInteractionResponseArgs(api, params, { sessionKey, sessionId }) {
|
|
1472
1643
|
const config = isRecord(api.pluginConfig) ? api.pluginConfig : {};
|
|
1473
|
-
const
|
|
1644
|
+
const requestedSubject = requestedInteractionSubject(params);
|
|
1474
1645
|
const interactionId = requiredTerminalInteractionIdentifier(params.interaction_id, "interaction_id");
|
|
1475
|
-
const offered = consumeOpenClawPrivateAuthorityOffer(api, openClawInteractionAuthorityOfferKey(sessionKey, sessionId,
|
|
1646
|
+
const offered = consumeOpenClawPrivateAuthorityOffer(api, openClawInteractionAuthorityOfferKey(sessionKey, sessionId, requestedSubject.kind, requestedSubject.id, interactionId));
|
|
1476
1647
|
const fingerprint = stringValue(offered?.fingerprint);
|
|
1477
1648
|
if (!isExactInteractionFingerprint(fingerprint) ||
|
|
1478
1649
|
offered?.interaction_state === undefined) {
|
|
1479
1650
|
throw new Error("respond_interaction requires a current pending interaction shown by agent_knock_knock_status in this controller conversation; refresh status, review the current questions, and respond again");
|
|
1480
1651
|
}
|
|
1481
|
-
const projection =
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
//
|
|
1486
|
-
//
|
|
1652
|
+
const projection = validateAnyTerminalInteractionProjection(offered.interaction_state);
|
|
1653
|
+
assertInteractionSubjectMatchesProjection(requestedSubject, projection);
|
|
1654
|
+
const responseInput = interactionResponseForProjection(params, projection);
|
|
1655
|
+
const response = validateAnyTerminalInteractionResponse(responseInput, projection, {
|
|
1656
|
+
// This consumes a still-live, session/incarnation-bound private offer.
|
|
1657
|
+
// The CLI/bridge path always performs exact live terminal recaptures
|
|
1658
|
+
// before it can reserve or dispatch input, so projection expiry is a
|
|
1659
|
+
// recheck trigger rather than proof that the questionnaire disappeared.
|
|
1487
1660
|
allowExpiredForLiveRecapture: true
|
|
1488
1661
|
});
|
|
1489
1662
|
const args = [
|
|
1490
1663
|
"respond-interaction",
|
|
1491
|
-
|
|
1492
|
-
|
|
1664
|
+
requestedSubject.cliOption,
|
|
1665
|
+
requestedSubject.id,
|
|
1493
1666
|
"--interaction",
|
|
1494
1667
|
response.interaction_id,
|
|
1495
1668
|
"--response-json",
|
|
@@ -1504,6 +1677,43 @@ function buildPrivateInteractionResponseArgs(api, params, { sessionKey, sessionI
|
|
|
1504
1677
|
pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
|
|
1505
1678
|
return args;
|
|
1506
1679
|
}
|
|
1680
|
+
function requestedInteractionSubject(params) {
|
|
1681
|
+
const hasTurn = Object.hasOwn(params, "turn_id");
|
|
1682
|
+
const hasWatch = Object.hasOwn(params, "watch_id");
|
|
1683
|
+
if (hasTurn === hasWatch) {
|
|
1684
|
+
throw new Error("respond_interaction requires exactly one of turn_id or watch_id");
|
|
1685
|
+
}
|
|
1686
|
+
return hasTurn
|
|
1687
|
+
? {
|
|
1688
|
+
kind: "managed_turn",
|
|
1689
|
+
id: requiredTerminalInteractionIdentifier(params.turn_id, "turn_id"),
|
|
1690
|
+
cliOption: "--turn"
|
|
1691
|
+
}
|
|
1692
|
+
: {
|
|
1693
|
+
kind: "terminal_watch",
|
|
1694
|
+
id: requiredTerminalInteractionIdentifier(params.watch_id, "watch_id"),
|
|
1695
|
+
cliOption: "--watch"
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
function assertInteractionSubjectMatchesProjection(requested, projection) {
|
|
1699
|
+
const projected = interactionProjectionSubject(projection);
|
|
1700
|
+
if (requested.kind !== projected.kind || requested.id !== projected.id) {
|
|
1701
|
+
throw new Error("respond_interaction target does not match the displayed interaction subject; refresh status and respond to its exact turn_id or watch_id");
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
function interactionResponseForProjection(params, projection) {
|
|
1705
|
+
if (projection.version !== TERMINAL_INTERACTION_SUBJECT_VERSION) {
|
|
1706
|
+
return params;
|
|
1707
|
+
}
|
|
1708
|
+
return {
|
|
1709
|
+
interaction_id: params.interaction_id,
|
|
1710
|
+
subject: projection.subject,
|
|
1711
|
+
...(projection.subject.kind === "managed_turn"
|
|
1712
|
+
? { turn_id: params.turn_id }
|
|
1713
|
+
: {}),
|
|
1714
|
+
answers: params.answers
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1507
1717
|
function isExactApprovalFingerprint(value) {
|
|
1508
1718
|
return typeof value === "string" && /^[a-f0-9]{64}$/u.test(value);
|
|
1509
1719
|
}
|
|
@@ -1577,7 +1787,7 @@ function sanitizeModelFacingValue(value, parentKey, path = []) {
|
|
|
1577
1787
|
}
|
|
1578
1788
|
if (key === "interaction_state") {
|
|
1579
1789
|
try {
|
|
1580
|
-
output[key] =
|
|
1790
|
+
output[key] = sanitizeModelFacingValue(validateAnyTerminalInteractionProjection(item), undefined, [...path, key]);
|
|
1581
1791
|
}
|
|
1582
1792
|
catch {
|
|
1583
1793
|
continue;
|
|
@@ -1641,8 +1851,7 @@ function isSubmissionError(result) {
|
|
|
1641
1851
|
if (!isRecord(result)) {
|
|
1642
1852
|
return false;
|
|
1643
1853
|
}
|
|
1644
|
-
if (result
|
|
1645
|
-
result.scope === "terminal_user_explicit") {
|
|
1854
|
+
if (isSuccessfulTerminalDispatch(result)) {
|
|
1646
1855
|
return false;
|
|
1647
1856
|
}
|
|
1648
1857
|
if (terminalSubmissionReported(result)) {
|
|
@@ -1751,7 +1960,10 @@ async function runDelegate(api, params, toolContext) {
|
|
|
1751
1960
|
// A uniquely delegated user-priority Send may deliberately complete
|
|
1752
1961
|
// without creating a managed Turn. Preserve that terminal receipt for the
|
|
1753
1962
|
// shared Send formatter instead of inventing Session/Turn identity.
|
|
1754
|
-
return
|
|
1963
|
+
return {
|
|
1964
|
+
...parsed,
|
|
1965
|
+
...normalizedTerminalSendResultContract(parsed)
|
|
1966
|
+
};
|
|
1755
1967
|
}
|
|
1756
1968
|
const conversationId = stringValue(parsed.conversation_id) ??
|
|
1757
1969
|
(isRecord(parsed.conversation)
|
|
@@ -1831,6 +2043,7 @@ async function runDelegate(api, params, toolContext) {
|
|
|
1831
2043
|
callback_method: hostBridgePresentationApis.has(api)
|
|
1832
2044
|
? "command_json_v1"
|
|
1833
2045
|
: CALLBACK_METHOD,
|
|
2046
|
+
...normalizedTerminalSendResultContract(parsed),
|
|
1834
2047
|
...(submissionUnfenced
|
|
1835
2048
|
? {
|
|
1836
2049
|
submission_outcome: "submitted",
|
|
@@ -1918,7 +2131,8 @@ function registerCliTool(api, { name, description, parameters, buildArgs, normal
|
|
|
1918
2131
|
try {
|
|
1919
2132
|
const result = await runHostAwareCli(api, await buildArgs(isRecord(params) ? params : {}, toolContext, toolCallId));
|
|
1920
2133
|
const rendered = toolResult(result, {
|
|
1921
|
-
submissionErrors: name === "agent_knock_knock_respond"
|
|
2134
|
+
submissionErrors: name === "agent_knock_knock_respond" ||
|
|
2135
|
+
name === "agent_knock_knock_identify_and_send",
|
|
1922
2136
|
normalizeTurnIdentity,
|
|
1923
2137
|
forceError: typeof isErrorResult === "function" && isErrorResult(result) === true
|
|
1924
2138
|
});
|