@scotthuang/agent-knock-knock 0.12.32 → 0.12.34
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/dist/src/callback-cli-adapter.d.ts +2 -1
- package/dist/src/callback-cli-adapter.js +1 -0
- package/dist/src/callback-cli-adapter.js.map +1 -1
- package/dist/src/callback-outbox-policy.d.ts +12 -1
- package/dist/src/callback-outbox-policy.js +51 -0
- package/dist/src/callback-outbox-policy.js.map +1 -1
- package/dist/src/callback-outbox-service.d.ts +19 -0
- package/dist/src/callback-outbox-service.js +63 -0
- package/dist/src/callback-outbox-service.js.map +1 -1
- package/dist/src/openclaw-managed-callback-cli-adapter.d.ts +2 -0
- package/dist/src/openclaw-managed-callback-cli-adapter.js +9 -1
- package/dist/src/openclaw-managed-callback-cli-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-callback-adapter.js +45 -20
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +34 -62
- package/dist/src/openclaw-plugin-schemas.js +28 -64
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +19 -0
- package/dist/src/terminal-agent-bridge.js +61 -11
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-interaction-cli-adapter.js +138 -15
- package/dist/src/terminal-interaction-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-application-service.d.ts +22 -0
- package/dist/src/terminal-monitor-application-service.js +161 -1
- package/dist/src/terminal-monitor-application-service.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.d.ts +37 -1
- package/dist/src/terminal-monitor-cli-adapter.js +88 -0
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.d.ts +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +235 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-questionnaire-adapter.js +66 -15
- package/dist/src/terminal-questionnaire-adapter.js.map +1 -1
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +1 -1
|
@@ -11,13 +11,14 @@ import { readNdjsonLog } from "./transcript.js";
|
|
|
11
11
|
import { createFileLockCliAdapter } from "./file-lock-cli-adapter.js";
|
|
12
12
|
import { cliEnv, cliExit, cliNow, cliNowMs, cliPid, cliRuntimeLog, cliSleepSync } from "./cli-runtime-context.js";
|
|
13
13
|
import { expandHome } from "./cli-command-runtime.js";
|
|
14
|
+
import { validateTerminalInteractionProjection } from "./terminal-interaction-protocol.js";
|
|
14
15
|
import { terminalControlFromTakeover } from "./terminal-runtime-cli-adapter.js";
|
|
15
16
|
import { terminalControlsShareIncarnation } from "./terminal-authority-policy.js";
|
|
16
17
|
import { terminalControlEvidenceMatches } from "./terminal-control-ref.js";
|
|
17
18
|
import { applyTerminalBridgeSubmission, terminalAcceptanceEvidenceForConversation, terminalBridgeEnabled, terminalBridgeRequestFingerprint, terminalBridgeSubmission } from "./terminal-dispatch-receipt.js";
|
|
18
19
|
import { terminalMonitorActivityPersistIntervalMs, terminalMonitorApprovalCandidate, terminalMonitorDeadlineAt, validTerminalMonitorTimestampMs } from "./terminal-monitor-decision-policy.js";
|
|
19
20
|
import { runTerminalMonitor as runTerminalMonitorService } from "./terminal-monitor-application-service.js";
|
|
20
|
-
import { pollTerminalMonitor, presentTerminalMonitor, reconcileMonitorAcceptance, recordMonitorApprovalNotification, repairLaggingAcceptedMonitorAuthority, recoverPreparedMonitorSubmission, terminalMonitorStoreLeaseTimeout, terminalMonitorStoreOperationTimeout } from "./terminal-monitor-cli-adapter.js";
|
|
21
|
+
import { pollTerminalMonitor, presentTerminalMonitor, reconcileMonitorAcceptance, recordMonitorApprovalNotification, recordMonitorInteractionNotification, repairLaggingAcceptedMonitorAuthority, recoverPreparedMonitorSubmission, terminalMonitorStoreLeaseTimeout, terminalMonitorStoreOperationTimeout } from "./terminal-monitor-cli-adapter.js";
|
|
21
22
|
import { terminalMonitorReconciliationEligibility } from "./terminal-monitor-reconciliation-eligibility.js";
|
|
22
23
|
import { reconcileTerminalMonitorStateCandidate } from "./terminal-monitor-state-reconciliation-service.js";
|
|
23
24
|
import { sameCanonicalStatePath } from "./terminal-dispatch-ledger-codec.js";
|
|
@@ -618,6 +619,22 @@ class TerminalMonitorStateCliApplication {
|
|
|
618
619
|
approvalInstructions: terminalBridgeApprovalInstructions,
|
|
619
620
|
approvalCandidate: terminalMonitorApprovalCandidate
|
|
620
621
|
}
|
|
622
|
+
}),
|
|
623
|
+
recordInteractionNotification: (request) => recordMonitorInteractionNotification({
|
|
624
|
+
...request,
|
|
625
|
+
ports: {
|
|
626
|
+
record: (recordRequest) => this.#recordInteractionNotification({
|
|
627
|
+
...recordRequest,
|
|
628
|
+
statePath: input.statePath,
|
|
629
|
+
logPath: input.logPath
|
|
630
|
+
}),
|
|
631
|
+
prepare: (prepareRequest) => this.#dependencies.callbacks.prepareInteractionNotification({
|
|
632
|
+
options: { ...input.options, statePath: input.statePath },
|
|
633
|
+
statePath: input.statePath,
|
|
634
|
+
logPath: input.logPath,
|
|
635
|
+
...prepareRequest
|
|
636
|
+
})
|
|
637
|
+
}
|
|
621
638
|
})
|
|
622
639
|
},
|
|
623
640
|
authority: {
|
|
@@ -1296,6 +1313,215 @@ class TerminalMonitorStateCliApplication {
|
|
|
1296
1313
|
recorded
|
|
1297
1314
|
};
|
|
1298
1315
|
}
|
|
1316
|
+
#recordInteractionNotification(input) {
|
|
1317
|
+
const storeDir = pathsForConversationDir(path.dirname(input.statePath))
|
|
1318
|
+
.storeDir;
|
|
1319
|
+
return withStoreWriterLease(storeDir, () => {
|
|
1320
|
+
const release = this.#stateFileLock.acquire(`${input.statePath}.lock`);
|
|
1321
|
+
try {
|
|
1322
|
+
const conversation = loadState(input.statePath);
|
|
1323
|
+
if (!this.#interactionSnapshotMatches(conversation, input)) {
|
|
1324
|
+
return {
|
|
1325
|
+
conversation,
|
|
1326
|
+
duplicate: false,
|
|
1327
|
+
stale: true,
|
|
1328
|
+
recorded: undefined
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
return this.#recordMatchingInteraction(this.#interactionPersistenceContext(conversation, input));
|
|
1332
|
+
}
|
|
1333
|
+
finally {
|
|
1334
|
+
release();
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
#interactionSnapshotMatches(conversation, input) {
|
|
1339
|
+
const takeover = takeoverFor(conversation);
|
|
1340
|
+
const currentControl = terminalControlFromTakeover(takeover);
|
|
1341
|
+
const projection = input.terminalStatus.interaction_state;
|
|
1342
|
+
return conversation.status === "waiting_for_agent" &&
|
|
1343
|
+
conversation.conversation_id === input.expectedConversation.conversationId &&
|
|
1344
|
+
conversation.status === input.expectedConversation.status &&
|
|
1345
|
+
conversation.updated_at === input.expectedConversation.updatedAt &&
|
|
1346
|
+
takeover?.terminal_bridge === true &&
|
|
1347
|
+
nonBlankString(takeover.terminal_bridge_message_id) ===
|
|
1348
|
+
input.expectedConversation.messageId &&
|
|
1349
|
+
currentControl !== undefined &&
|
|
1350
|
+
terminalControlsShareIncarnation(currentControl, input.terminalControl) &&
|
|
1351
|
+
projection?.interaction_id === input.interactionId &&
|
|
1352
|
+
projection.questions.length === 1 &&
|
|
1353
|
+
projection.questions[0]?.question_id === input.questionId &&
|
|
1354
|
+
input.terminalStatus.interaction_prompt_fingerprint === input.fingerprint;
|
|
1355
|
+
}
|
|
1356
|
+
#interactionPersistenceContext(conversation, input) {
|
|
1357
|
+
const nativeTakeover = { ...takeoverFor(conversation) };
|
|
1358
|
+
const screen = isRecord(input.terminalStatus.screen)
|
|
1359
|
+
? input.terminalStatus.screen
|
|
1360
|
+
: undefined;
|
|
1361
|
+
const interactionScreenDigest = nonBlankString(screen?.digest);
|
|
1362
|
+
const previousNotification = isRecord(nativeTakeover.terminal_bridge_interaction_notification)
|
|
1363
|
+
? nativeTakeover.terminal_bridge_interaction_notification
|
|
1364
|
+
: undefined;
|
|
1365
|
+
const previousInteractionState = validInteractionProjection(previousNotification?.interaction_state);
|
|
1366
|
+
const callbackDelivery = isRecord(conversation.callback_delivery)
|
|
1367
|
+
? conversation.callback_delivery
|
|
1368
|
+
: undefined;
|
|
1369
|
+
const callbackMessage = isRecord(callbackDelivery?.message)
|
|
1370
|
+
? callbackDelivery.message
|
|
1371
|
+
: undefined;
|
|
1372
|
+
const callbackMetadata = isRecord(callbackMessage?.metadata)
|
|
1373
|
+
? callbackMessage.metadata
|
|
1374
|
+
: undefined;
|
|
1375
|
+
const callbackInteractionState = validInteractionProjection(callbackMetadata?.interaction_state);
|
|
1376
|
+
const previousCallbackMessageId = nonBlankString(previousNotification?.callback_message_id);
|
|
1377
|
+
const matchingInteractionOutbox = callbackDelivery?.kind === "interaction_notification" &&
|
|
1378
|
+
previousCallbackMessageId !== undefined &&
|
|
1379
|
+
callbackMessage?.id === previousCallbackMessageId &&
|
|
1380
|
+
callbackMetadata?.source === "terminal_bridge" &&
|
|
1381
|
+
callbackMetadata?.reason === "interaction_required" &&
|
|
1382
|
+
callbackInteractionState !== undefined &&
|
|
1383
|
+
callbackInteractionState.interaction_id ===
|
|
1384
|
+
previousNotification?.interaction_id &&
|
|
1385
|
+
callbackInteractionState.questions.length === 1 &&
|
|
1386
|
+
callbackInteractionState.questions[0]?.question_id ===
|
|
1387
|
+
previousNotification?.question_id &&
|
|
1388
|
+
callbackInteractionState.turn_id === turnIdForConversation(conversation);
|
|
1389
|
+
const deliveryStatus = nonBlankString(callbackDelivery?.status);
|
|
1390
|
+
const deliveryAttempts = Number(callbackDelivery?.attempts ?? 0);
|
|
1391
|
+
const activeOutbox = deliveryStatus === "pending" ||
|
|
1392
|
+
(deliveryStatus === "failed" &&
|
|
1393
|
+
Number.isFinite(deliveryAttempts) &&
|
|
1394
|
+
deliveryAttempts <= this.#dependencies.runtime.callbackRetryLimit);
|
|
1395
|
+
const conflictingActiveOutbox = activeOutbox && !matchingInteractionOutbox;
|
|
1396
|
+
return {
|
|
1397
|
+
input,
|
|
1398
|
+
conversation,
|
|
1399
|
+
nativeTakeover,
|
|
1400
|
+
interactionScreenDigest,
|
|
1401
|
+
previousNotification,
|
|
1402
|
+
previousInteractionState,
|
|
1403
|
+
previousCallbackMessageId,
|
|
1404
|
+
matchingInteractionOutbox,
|
|
1405
|
+
activeOutbox,
|
|
1406
|
+
conflictingActiveOutbox
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
#recordMatchingInteraction(context) {
|
|
1410
|
+
const duplicate = context.previousNotification?.terminal_bridge_message_id ===
|
|
1411
|
+
context.input.expectedConversation.messageId &&
|
|
1412
|
+
context.previousNotification?.interaction_id ===
|
|
1413
|
+
context.input.interactionId &&
|
|
1414
|
+
context.previousNotification?.question_id === context.input.questionId &&
|
|
1415
|
+
context.previousNotification?.prompt_fingerprint ===
|
|
1416
|
+
context.input.fingerprint &&
|
|
1417
|
+
context.previousInteractionState?.interaction_id ===
|
|
1418
|
+
context.input.interactionId &&
|
|
1419
|
+
context.previousInteractionState.questions.length === 1 &&
|
|
1420
|
+
context.previousInteractionState.questions[0]?.question_id ===
|
|
1421
|
+
context.input.questionId;
|
|
1422
|
+
if (context.conflictingActiveOutbox ||
|
|
1423
|
+
(context.activeOutbox && !duplicate)) {
|
|
1424
|
+
return {
|
|
1425
|
+
conversation: context.conversation,
|
|
1426
|
+
duplicate: false,
|
|
1427
|
+
stale: true,
|
|
1428
|
+
deferred: true,
|
|
1429
|
+
recorded: undefined
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
if (!duplicate) {
|
|
1433
|
+
return this.#recordNewInteraction(context);
|
|
1434
|
+
}
|
|
1435
|
+
if (!context.matchingInteractionOutbox) {
|
|
1436
|
+
return this.#recoverInteractionOutbox(context);
|
|
1437
|
+
}
|
|
1438
|
+
return {
|
|
1439
|
+
conversation: context.conversation,
|
|
1440
|
+
duplicate: true,
|
|
1441
|
+
stale: false,
|
|
1442
|
+
recorded: undefined
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
#recoverInteractionOutbox(context) {
|
|
1446
|
+
const messageId = context.previousCallbackMessageId ?? `msg-${randomUUID()}`;
|
|
1447
|
+
const messageTs = nonBlankString(context.previousNotification?.callback_message_ts) ?? nonBlankString(context.previousNotification?.notified_at) ??
|
|
1448
|
+
cliNow().toISOString();
|
|
1449
|
+
const conversation = context.previousCallbackMessageId
|
|
1450
|
+
? context.conversation
|
|
1451
|
+
: {
|
|
1452
|
+
...context.conversation,
|
|
1453
|
+
native_session_takeover: {
|
|
1454
|
+
...context.nativeTakeover,
|
|
1455
|
+
terminal_bridge_interaction_notification: {
|
|
1456
|
+
...context.previousNotification,
|
|
1457
|
+
callback_message_id: messageId,
|
|
1458
|
+
callback_message_ts: messageTs
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
if (!context.previousCallbackMessageId) {
|
|
1463
|
+
saveState(context.input.statePath, conversation);
|
|
1464
|
+
}
|
|
1465
|
+
const recorded = context.input.onRecorded(conversation, {
|
|
1466
|
+
recoverMissingOutbox: true
|
|
1467
|
+
});
|
|
1468
|
+
appendEvent(context.input.logPath, {
|
|
1469
|
+
ts: cliNow().toISOString(),
|
|
1470
|
+
conversation_id: conversation.conversation_id,
|
|
1471
|
+
event: "terminal_bridge_interaction_notification_outbox_recovered",
|
|
1472
|
+
terminal_control: context.input.terminalControl,
|
|
1473
|
+
interaction_id: context.input.interactionId,
|
|
1474
|
+
question_id: context.input.questionId,
|
|
1475
|
+
callback_message_id: messageId
|
|
1476
|
+
});
|
|
1477
|
+
return {
|
|
1478
|
+
conversation: recorded.prepared?.conversation ?? conversation,
|
|
1479
|
+
duplicate: false,
|
|
1480
|
+
recovered: true,
|
|
1481
|
+
stale: false,
|
|
1482
|
+
recorded
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
#recordNewInteraction(context) {
|
|
1486
|
+
const now = cliNow().toISOString();
|
|
1487
|
+
const callbackMessageId = `msg-${randomUUID()}`;
|
|
1488
|
+
const conversation = {
|
|
1489
|
+
...context.conversation,
|
|
1490
|
+
native_session_takeover: {
|
|
1491
|
+
...context.nativeTakeover,
|
|
1492
|
+
terminal_bridge_interaction_notification: {
|
|
1493
|
+
terminal_bridge_message_id: context.input.expectedConversation.messageId,
|
|
1494
|
+
interaction_id: context.input.interactionId,
|
|
1495
|
+
question_id: context.input.questionId,
|
|
1496
|
+
prompt_fingerprint: context.input.fingerprint,
|
|
1497
|
+
screen_digest: context.interactionScreenDigest,
|
|
1498
|
+
notified_at: now,
|
|
1499
|
+
terminal_control: context.input.terminalControl,
|
|
1500
|
+
interaction_state: context.input.terminalStatus.interaction_state,
|
|
1501
|
+
callback_message_id: callbackMessageId,
|
|
1502
|
+
callback_message_ts: now
|
|
1503
|
+
}
|
|
1504
|
+
},
|
|
1505
|
+
updated_at: now
|
|
1506
|
+
};
|
|
1507
|
+
saveState(context.input.statePath, conversation);
|
|
1508
|
+
appendEvent(context.input.logPath, {
|
|
1509
|
+
ts: now,
|
|
1510
|
+
conversation_id: context.conversation.conversation_id,
|
|
1511
|
+
event: "terminal_bridge_interaction_notification_recorded",
|
|
1512
|
+
terminal_control: context.input.terminalControl,
|
|
1513
|
+
interaction_id: context.input.interactionId,
|
|
1514
|
+
question_id: context.input.questionId,
|
|
1515
|
+
screen_digest: context.interactionScreenDigest
|
|
1516
|
+
});
|
|
1517
|
+
const recorded = context.input.onRecorded(conversation);
|
|
1518
|
+
return {
|
|
1519
|
+
conversation: recorded.prepared?.conversation ?? conversation,
|
|
1520
|
+
duplicate: false,
|
|
1521
|
+
stale: false,
|
|
1522
|
+
recorded
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1299
1525
|
#markApprovalPromptCleared(input) {
|
|
1300
1526
|
const storeDir = pathsForConversationDir(path.dirname(input.statePath))
|
|
1301
1527
|
.storeDir;
|
|
@@ -2029,6 +2255,14 @@ function approvalCanBeCleared(conversation, takeover, input) {
|
|
|
2029
2255
|
validTerminalMonitorTimestampMs(takeover.terminal_bridge_approval_resolved_at) !== undefined &&
|
|
2030
2256
|
validTerminalMonitorTimestampMs(takeover.terminal_bridge_last_approval_prompt_cleared_at) === undefined;
|
|
2031
2257
|
}
|
|
2258
|
+
function validInteractionProjection(value) {
|
|
2259
|
+
try {
|
|
2260
|
+
return validateTerminalInteractionProjection(value);
|
|
2261
|
+
}
|
|
2262
|
+
catch {
|
|
2263
|
+
return undefined;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2032
2266
|
function detectorDiagnostic(input, existing, now) {
|
|
2033
2267
|
if (input.limitation && input.fingerprint) {
|
|
2034
2268
|
return {
|