@scotthuang/agent-knock-knock 0.12.12 → 0.12.13
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 +22 -0
- package/README.md +39 -26
- package/dist/src/approval-policy.d.ts +11 -2
- package/dist/src/approval-policy.js +33 -7
- package/dist/src/approval-policy.js.map +1 -1
- package/dist/src/claude-local-transcript-provider.d.ts +81 -0
- package/dist/src/claude-local-transcript-provider.js +854 -18
- package/dist/src/claude-local-transcript-provider.js.map +1 -1
- package/dist/src/cli-core.js +85 -0
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/deferred-foreground-authority-cli-adapter.js +90 -19
- package/dist/src/deferred-foreground-authority-cli-adapter.js.map +1 -1
- package/dist/src/deferred-foreground-transfer.js +3 -4
- package/dist/src/deferred-foreground-transfer.js.map +1 -1
- package/dist/src/openclaw-callback-transport.d.ts +5 -0
- package/dist/src/openclaw-callback-transport.js +1 -1
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- package/dist/src/openclaw-plugin-callback-adapter.js +96 -14
- package/dist/src/openclaw-plugin-callback-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js +548 -107
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +27 -4
- package/dist/src/openclaw-plugin-helpers.js +309 -44
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +44 -68
- package/dist/src/openclaw-plugin-schemas.js +63 -91
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/openclaw-plugin-supervisor.js +48 -10
- package/dist/src/openclaw-plugin-supervisor.js.map +1 -1
- package/dist/src/openclaw-private-authority-offers.d.ts +22 -0
- package/dist/src/openclaw-private-authority-offers.js +177 -0
- package/dist/src/openclaw-private-authority-offers.js.map +1 -0
- package/dist/src/store.d.ts +1 -0
- package/dist/src/store.js +3 -1
- package/dist/src/store.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +19 -1
- package/dist/src/terminal-action-projection.js +23 -0
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-command-cli-adapter.d.ts +5 -0
- package/dist/src/terminal-command-cli-adapter.js +249 -89
- package/dist/src/terminal-command-cli-adapter.js.map +1 -1
- package/dist/src/terminal-dispatch-repository-cli-adapter.js +305 -79
- package/dist/src/terminal-dispatch-repository-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +31 -7
- package/dist/src/terminal-list-cli-adapter.js +285 -67
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +2 -0
- package/dist/src/terminal-list-renderer.js +116 -41
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-monitor-cli-adapter.js +1 -1
- package/dist/src/terminal-monitor-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-state-cli-adapter.js +6 -15
- package/dist/src/terminal-monitor-state-cli-adapter.js.map +1 -1
- package/dist/src/terminal-status-cli-adapter.d.ts +10 -0
- package/dist/src/terminal-status-cli-adapter.js +89 -7
- package/dist/src/terminal-status-cli-adapter.js.map +1 -1
- package/dist/src/terminal-submission-acceptance.d.ts +60 -1
- package/dist/src/terminal-submission-acceptance.js +560 -0
- package/dist/src/terminal-submission-acceptance.js.map +1 -1
- package/dist/src/terminal-watch-callback-cli-adapter.d.ts +20 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js +55 -0
- package/dist/src/terminal-watch-callback-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-cli-adapter.d.ts +64 -0
- package/dist/src/terminal-watch-cli-adapter.js +508 -0
- package/dist/src/terminal-watch-cli-adapter.js.map +1 -0
- package/dist/src/terminal-watch-service.d.ts +97 -0
- package/dist/src/terminal-watch-service.js +714 -0
- package/dist/src/terminal-watch-service.js.map +1 -0
- package/dist/src/terminal-watch-store.d.ts +149 -0
- package/dist/src/terminal-watch-store.js +861 -0
- package/dist/src/terminal-watch-store.js.map +1 -0
- package/docs/quickstart-herdr.md +8 -0
- package/docs/quickstart-tmux.md +33 -6
- package/openclaw.plugin.json +11 -3
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +57 -39
|
@@ -1630,6 +1630,11 @@ async function runApprove(options) {
|
|
|
1630
1630
|
}
|
|
1631
1631
|
const loaded = loadConversationFromOptions(options);
|
|
1632
1632
|
const { statePath, logPath } = loaded;
|
|
1633
|
+
assertAutoApprovalCallbackRoute({
|
|
1634
|
+
options,
|
|
1635
|
+
conversation: loaded.conversation,
|
|
1636
|
+
statePath
|
|
1637
|
+
});
|
|
1633
1638
|
const conversation = await migrateLegacyTerminalAgentIdentity({
|
|
1634
1639
|
...loaded,
|
|
1635
1640
|
options
|
|
@@ -1641,7 +1646,10 @@ async function runApprove(options) {
|
|
|
1641
1646
|
if (!terminalControl) {
|
|
1642
1647
|
throw new Error(`conversation ${conversation.conversation_id} is not controlled through a terminal`);
|
|
1643
1648
|
}
|
|
1644
|
-
|
|
1649
|
+
const autoApproved = options.autoApproved === true;
|
|
1650
|
+
const callbackAuthority = autoApprovalCallbackAuthorityFromOptions(options);
|
|
1651
|
+
if (!["waiting_for_agent", "waiting_for_openclaw"].includes(conversation.status) &&
|
|
1652
|
+
!(autoApproved && callbackAuthority)) {
|
|
1645
1653
|
throw new Error(`cannot approve ${conversation.conversation_id}; conversation is ${conversation.status}`);
|
|
1646
1654
|
}
|
|
1647
1655
|
const executor = executorForConversation(conversation);
|
|
@@ -1651,7 +1659,6 @@ async function runApprove(options) {
|
|
|
1651
1659
|
const suppliedExpectedFingerprint = stringValue(options.expectedApprovalFingerprint);
|
|
1652
1660
|
const expectedFingerprint = suppliedExpectedFingerprint ??
|
|
1653
1661
|
stringValue(monitoredApproval?.fingerprint);
|
|
1654
|
-
const autoApproved = options.autoApproved === true;
|
|
1655
1662
|
const claudeScreenApproval = executor.kind === "claude";
|
|
1656
1663
|
if (claudeScreenApproval) {
|
|
1657
1664
|
const monitoredState = isRecord(monitoredApproval?.approval_state)
|
|
@@ -1665,6 +1672,7 @@ async function runApprove(options) {
|
|
|
1665
1672
|
const currentMessageId = stringValue(nativeTakeover?.terminal_bridge_message_id);
|
|
1666
1673
|
const approvalResolvedAt = validTimestampMs(nativeTakeover?.terminal_bridge_approval_resolved_at);
|
|
1667
1674
|
if (autoApproved &&
|
|
1675
|
+
callbackAuthority === undefined &&
|
|
1668
1676
|
monitoredApproval === undefined &&
|
|
1669
1677
|
pendingDispatch === undefined &&
|
|
1670
1678
|
conversation.status === "waiting_for_agent" &&
|
|
@@ -1693,63 +1701,68 @@ async function runApprove(options) {
|
|
|
1693
1701
|
});
|
|
1694
1702
|
return;
|
|
1695
1703
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1704
|
+
// A provenance-bound callback whose approval is already absent must reach
|
|
1705
|
+
// the terminal+state lock. Only the persisted callback message and the
|
|
1706
|
+
// locked consumed-approval receipt may classify it as an idempotent replay.
|
|
1707
|
+
if (callbackAuthority === undefined || monitoredApproval !== undefined) {
|
|
1708
|
+
const notifiedAt = validTimestampMs(monitoredApproval?.notified_at);
|
|
1709
|
+
if (conversation.status !== "waiting_for_openclaw" ||
|
|
1710
|
+
monitoredState?.decision_mode !== "keys" ||
|
|
1711
|
+
!stringValue(monitoredApproval?.fingerprint)) {
|
|
1712
|
+
printJson({
|
|
1713
|
+
conversation,
|
|
1714
|
+
approved: false,
|
|
1715
|
+
blocked: true,
|
|
1716
|
+
reason: "Claude screen approval requires a current managed-turn approval notification",
|
|
1717
|
+
terminal_control: terminalControl
|
|
1718
|
+
});
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
if (notifiedAt === undefined ||
|
|
1722
|
+
cliNowMs() - notifiedAt > CLAUDE_SCREEN_APPROVAL_TTL_MS) {
|
|
1723
|
+
printJson({
|
|
1724
|
+
conversation,
|
|
1725
|
+
approved: false,
|
|
1726
|
+
blocked: true,
|
|
1727
|
+
reason: "Claude screen approval expired; inspect and resolve the terminal manually",
|
|
1728
|
+
terminal_control: terminalControl
|
|
1729
|
+
});
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (!suppliedExpectedFingerprint ||
|
|
1733
|
+
expectedFingerprint !== monitoredApproval?.fingerprint) {
|
|
1734
|
+
printJson({
|
|
1735
|
+
conversation,
|
|
1736
|
+
approved: false,
|
|
1737
|
+
blocked: true,
|
|
1738
|
+
reason: "Claude screen approval requires the latest notified fingerprint",
|
|
1739
|
+
terminal_control: terminalControl
|
|
1740
|
+
});
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
if (pendingDispatch?.state === "reserved" &&
|
|
1744
|
+
pendingDispatch.terminal_bridge_message_id ===
|
|
1745
|
+
nativeTakeover?.terminal_bridge_message_id) {
|
|
1746
|
+
printJson({
|
|
1747
|
+
conversation,
|
|
1748
|
+
approved: false,
|
|
1749
|
+
blocked: true,
|
|
1750
|
+
reason: "a previous Claude approval dispatch has an uncertain outcome; inspect and resolve the terminal manually",
|
|
1751
|
+
terminal_control: terminalControl
|
|
1752
|
+
});
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
if (expectedFingerprint ===
|
|
1756
|
+
stringValue(nativeTakeover?.terminal_bridge_last_approval_fingerprint)) {
|
|
1757
|
+
printJson({
|
|
1758
|
+
conversation,
|
|
1759
|
+
approved: false,
|
|
1760
|
+
blocked: true,
|
|
1761
|
+
reason: "Claude screen approval fingerprint was already consumed",
|
|
1762
|
+
terminal_control: terminalControl
|
|
1763
|
+
});
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1753
1766
|
}
|
|
1754
1767
|
}
|
|
1755
1768
|
return runManagedApprovalDispatch({
|
|
@@ -1759,6 +1772,28 @@ async function runApprove(options) {
|
|
|
1759
1772
|
autoApproved, claudeScreenApproval
|
|
1760
1773
|
});
|
|
1761
1774
|
}
|
|
1775
|
+
function approvalPolicyCandidateForInspection({ agent, currentTerminalControl, inspection, fingerprint }) {
|
|
1776
|
+
const evidence = inspection.approval.approvable
|
|
1777
|
+
? inspection.approval.policyEvidence
|
|
1778
|
+
: undefined;
|
|
1779
|
+
return {
|
|
1780
|
+
agent,
|
|
1781
|
+
kind: evidence?.kind ?? inspection.approval.promptKind ?? "unknown",
|
|
1782
|
+
decisionMode: inspection.approval.approvable
|
|
1783
|
+
? inspection.approval.action.mode ?? "keys"
|
|
1784
|
+
: undefined,
|
|
1785
|
+
command: evidence?.command ?? inspection.approval.command,
|
|
1786
|
+
cwd: evidence?.cwd ?? inspection.approval.cwd ?? currentTerminalControl.currentPath,
|
|
1787
|
+
fingerprint: fingerprint ?? "",
|
|
1788
|
+
terminalTarget: currentTerminalControl.target,
|
|
1789
|
+
...(evidence?.source === "claude_transcript"
|
|
1790
|
+
? {
|
|
1791
|
+
evidenceSource: "claude_transcript",
|
|
1792
|
+
evidenceFingerprint: evidence.evidenceFingerprint
|
|
1793
|
+
}
|
|
1794
|
+
: {})
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1762
1797
|
async function runManagedApprovalDispatch({ options, conversation, statePath, logPath, nativeTakeover, terminalControl, executor, monitoredApproval, expectedFingerprint, autoApproved, claudeScreenApproval }) {
|
|
1763
1798
|
const policyRuleId = stringValue(options.policyRuleId);
|
|
1764
1799
|
const policyFingerprint = stringValue(options.policyFingerprint);
|
|
@@ -1766,28 +1801,6 @@ async function runManagedApprovalDispatch({ options, conversation, statePath, lo
|
|
|
1766
1801
|
? parseJsonOption(options.autoApprovalPolicyJson, "--auto-approval-policy-json")
|
|
1767
1802
|
: undefined;
|
|
1768
1803
|
let executorPolicyDecision;
|
|
1769
|
-
const policyCandidateForInspection = ({ agent, currentTerminalControl, inspection, fingerprint }) => {
|
|
1770
|
-
const evidence = inspection.approval.approvable
|
|
1771
|
-
? inspection.approval.policyEvidence
|
|
1772
|
-
: undefined;
|
|
1773
|
-
return {
|
|
1774
|
-
agent,
|
|
1775
|
-
kind: evidence?.kind ?? inspection.approval.promptKind ?? "unknown",
|
|
1776
|
-
decisionMode: inspection.approval.approvable
|
|
1777
|
-
? inspection.approval.action.mode ?? "keys"
|
|
1778
|
-
: undefined,
|
|
1779
|
-
command: evidence?.command ?? inspection.approval.command,
|
|
1780
|
-
cwd: evidence?.cwd ?? inspection.approval.cwd ?? currentTerminalControl.currentPath,
|
|
1781
|
-
fingerprint: fingerprint ?? "",
|
|
1782
|
-
terminalTarget: currentTerminalControl.target,
|
|
1783
|
-
...(evidence?.source === "claude_transcript"
|
|
1784
|
-
? {
|
|
1785
|
-
evidenceSource: "claude_transcript",
|
|
1786
|
-
evidenceFingerprint: evidence.evidenceFingerprint
|
|
1787
|
-
}
|
|
1788
|
-
: {})
|
|
1789
|
-
};
|
|
1790
|
-
};
|
|
1791
1804
|
const releaseTerminalLock = acquireTerminalBridgeSendLock(storeDirFromOptions(options), terminalControl, { timeoutMs: 30000 });
|
|
1792
1805
|
let terminalLockReleased = false;
|
|
1793
1806
|
const releaseApprovalTerminalLock = () => {
|
|
@@ -1817,21 +1830,43 @@ async function runManagedApprovalDispatch({ options, conversation, statePath, lo
|
|
|
1817
1830
|
const currentApproval = isRecord(currentTakeover?.terminal_bridge_approval)
|
|
1818
1831
|
? currentTakeover.terminal_bridge_approval
|
|
1819
1832
|
: undefined;
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1833
|
+
const callbackAuthorityState = assertAutoApprovalCallbackAuthority({
|
|
1834
|
+
options,
|
|
1835
|
+
conversation: currentConversation,
|
|
1836
|
+
statePath,
|
|
1837
|
+
takeover: currentTakeover,
|
|
1838
|
+
approval: currentApproval,
|
|
1839
|
+
expectedFingerprint
|
|
1840
|
+
});
|
|
1841
|
+
if (!currentControl ||
|
|
1823
1842
|
!terminalControlsShareIncarnation(currentControl, terminalControl) ||
|
|
1824
|
-
(
|
|
1825
|
-
|
|
1843
|
+
(callbackAuthorityState !== "already_approved" &&
|
|
1844
|
+
(currentConversation.status !== conversation.status ||
|
|
1845
|
+
currentTakeover?.terminal_bridge_message_id !==
|
|
1846
|
+
nativeTakeover?.terminal_bridge_message_id ||
|
|
1847
|
+
(claudeScreenApproval &&
|
|
1848
|
+
currentApproval?.fingerprint !== monitoredApproval?.fingerprint)))) {
|
|
1826
1849
|
throw new Error("approval state changed while waiting for terminal control; refresh status and retry");
|
|
1827
1850
|
}
|
|
1851
|
+
lockedConversation = currentConversation;
|
|
1852
|
+
if (callbackAuthorityState === "already_approved") {
|
|
1853
|
+
releaseApprovalStateLock();
|
|
1854
|
+
printJson({
|
|
1855
|
+
conversation: lockedConversation,
|
|
1856
|
+
approved: false,
|
|
1857
|
+
already_approved: true,
|
|
1858
|
+
blocked: false,
|
|
1859
|
+
reason: "automatic approval callback was already handled",
|
|
1860
|
+
terminal_control: currentControl
|
|
1861
|
+
});
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1828
1864
|
assertManagedTerminalDispatchOwner({
|
|
1829
1865
|
storeDir: writerStoreDir,
|
|
1830
1866
|
conversation: currentConversation,
|
|
1831
1867
|
terminalControl: currentControl,
|
|
1832
1868
|
action: "approve"
|
|
1833
1869
|
});
|
|
1834
|
-
lockedConversation = currentConversation;
|
|
1835
1870
|
const currentRuntimeIdentity = terminalRuntimeIdentityForConversation(currentConversation, currentControl);
|
|
1836
1871
|
approval = await createTerminalAgentBridge(options).approve(executor.kind, currentControl, {
|
|
1837
1872
|
expectedFingerprint,
|
|
@@ -1847,7 +1882,7 @@ async function runManagedApprovalDispatch({ options, conversation, statePath, lo
|
|
|
1847
1882
|
reason: "automatic approval requires an executor-side policy"
|
|
1848
1883
|
};
|
|
1849
1884
|
}
|
|
1850
|
-
const candidate =
|
|
1885
|
+
const candidate = approvalPolicyCandidateForInspection({
|
|
1851
1886
|
agent,
|
|
1852
1887
|
currentTerminalControl,
|
|
1853
1888
|
inspection,
|
|
@@ -1887,7 +1922,7 @@ async function runManagedApprovalDispatch({ options, conversation, statePath, lo
|
|
|
1887
1922
|
}
|
|
1888
1923
|
const freshPolicyDecision = evaluateApprovalPolicy({
|
|
1889
1924
|
policy: autoApprovalPolicy,
|
|
1890
|
-
candidate:
|
|
1925
|
+
candidate: approvalPolicyCandidateForInspection({
|
|
1891
1926
|
agent: executor.kind,
|
|
1892
1927
|
currentTerminalControl: dispatchControl,
|
|
1893
1928
|
inspection,
|
|
@@ -2147,6 +2182,131 @@ async function runManagedApprovalDispatch({ options, conversation, statePath, lo
|
|
|
2147
2182
|
}
|
|
2148
2183
|
}
|
|
2149
2184
|
}
|
|
2185
|
+
function assertAutoApprovalCallbackAuthority(input) {
|
|
2186
|
+
if (input.options.autoApproved !== true) {
|
|
2187
|
+
return "not_callback";
|
|
2188
|
+
}
|
|
2189
|
+
const expected = autoApprovalCallbackAuthorityFromOptions(input.options);
|
|
2190
|
+
if (!expected) {
|
|
2191
|
+
return "not_callback";
|
|
2192
|
+
}
|
|
2193
|
+
assertAutoApprovalCallbackRoute({
|
|
2194
|
+
options: input.options,
|
|
2195
|
+
conversation: input.conversation,
|
|
2196
|
+
statePath: input.statePath
|
|
2197
|
+
});
|
|
2198
|
+
const delivery = isRecord(input.conversation.callback_delivery)
|
|
2199
|
+
? input.conversation.callback_delivery
|
|
2200
|
+
: undefined;
|
|
2201
|
+
const callbackMessage = isRecord(delivery?.message)
|
|
2202
|
+
? delivery.message
|
|
2203
|
+
: undefined;
|
|
2204
|
+
const callbackMetadata = isRecord(callbackMessage?.metadata)
|
|
2205
|
+
? callbackMessage.metadata
|
|
2206
|
+
: undefined;
|
|
2207
|
+
const callbackCandidate = isRecord(callbackMetadata?.approval_candidate)
|
|
2208
|
+
? callbackMetadata.approval_candidate
|
|
2209
|
+
: undefined;
|
|
2210
|
+
const callbackTerminalStatus = isRecord(callbackMetadata?.terminal_status)
|
|
2211
|
+
? callbackMetadata.terminal_status
|
|
2212
|
+
: undefined;
|
|
2213
|
+
const callbackApprovalState = isRecord(callbackTerminalStatus?.approval_state)
|
|
2214
|
+
? callbackTerminalStatus.approval_state
|
|
2215
|
+
: undefined;
|
|
2216
|
+
const persistedApprovalState = isRecord(input.approval?.approval_state)
|
|
2217
|
+
? input.approval.approval_state
|
|
2218
|
+
: undefined;
|
|
2219
|
+
const callbackFingerprints = [
|
|
2220
|
+
stringValue(callbackMetadata?.approval_fingerprint),
|
|
2221
|
+
stringValue(callbackCandidate?.fingerprint),
|
|
2222
|
+
stringValue(callbackApprovalState?.fingerprint)
|
|
2223
|
+
];
|
|
2224
|
+
const commonAuthorityMismatch = delivery?.kind !== "approval_notification" ||
|
|
2225
|
+
stringValue(callbackMessage?.id) !== expected.messageId ||
|
|
2226
|
+
stringValue(callbackMessage?.conversation_id) !== expected.conversationId ||
|
|
2227
|
+
stringValue(callbackMessage?.session_id) !== expected.sessionId ||
|
|
2228
|
+
stringValue(callbackMessage?.turn_id) !== expected.turnId ||
|
|
2229
|
+
!input.expectedFingerprint ||
|
|
2230
|
+
!/^[a-f0-9]{64}$/u.test(input.expectedFingerprint) ||
|
|
2231
|
+
callbackFingerprints.some((value) => value !== input.expectedFingerprint) ||
|
|
2232
|
+
input.takeover?.terminal_bridge !== true;
|
|
2233
|
+
if (commonAuthorityMismatch) {
|
|
2234
|
+
throw new Error("automatic approval callback no longer matches the locked Turn state; refresh status and retry");
|
|
2235
|
+
}
|
|
2236
|
+
if (input.approval) {
|
|
2237
|
+
const currentFingerprints = [
|
|
2238
|
+
stringValue(input.approval.fingerprint),
|
|
2239
|
+
stringValue(persistedApprovalState?.fingerprint)
|
|
2240
|
+
];
|
|
2241
|
+
if (input.conversation.status !== "waiting_for_openclaw" ||
|
|
2242
|
+
currentFingerprints.some((value) => value !== input.expectedFingerprint) ||
|
|
2243
|
+
stringValue(input.approval.callback_message_id) !== expected.messageId) {
|
|
2244
|
+
throw new Error("automatic approval callback no longer matches the locked Turn state; refresh status and retry");
|
|
2245
|
+
}
|
|
2246
|
+
return "current";
|
|
2247
|
+
}
|
|
2248
|
+
if (input.takeover?.terminal_bridge_approval !== undefined ||
|
|
2249
|
+
input.takeover?.terminal_bridge_approval_dispatch !== undefined ||
|
|
2250
|
+
!isPostApprovalCallbackReplayStatus(input.conversation.status) ||
|
|
2251
|
+
stringValue(input.takeover?.terminal_bridge_message_id) !==
|
|
2252
|
+
expected.messageId ||
|
|
2253
|
+
stringValue(input.takeover?.terminal_bridge_last_approval_message_id) !==
|
|
2254
|
+
expected.messageId ||
|
|
2255
|
+
stringValue(input.takeover?.terminal_bridge_last_approval_fingerprint) !==
|
|
2256
|
+
input.expectedFingerprint ||
|
|
2257
|
+
validTimestampMs(input.takeover?.terminal_bridge_approval_resolved_at) === undefined) {
|
|
2258
|
+
throw new Error("automatic approval callback no longer matches the locked Turn receipt; refresh status and retry");
|
|
2259
|
+
}
|
|
2260
|
+
return "already_approved";
|
|
2261
|
+
}
|
|
2262
|
+
function isPostApprovalCallbackReplayStatus(status) {
|
|
2263
|
+
return [
|
|
2264
|
+
"waiting_for_agent",
|
|
2265
|
+
"running",
|
|
2266
|
+
"idle",
|
|
2267
|
+
"stalled",
|
|
2268
|
+
"callback_pending",
|
|
2269
|
+
"callback_failed",
|
|
2270
|
+
"failed",
|
|
2271
|
+
"closed",
|
|
2272
|
+
"cancelled",
|
|
2273
|
+
"cancelling"
|
|
2274
|
+
].includes(status);
|
|
2275
|
+
}
|
|
2276
|
+
function autoApprovalCallbackAuthorityFromOptions(options) {
|
|
2277
|
+
const candidate = {
|
|
2278
|
+
conversationId: stringValue(options.expectedCallbackConversationId),
|
|
2279
|
+
sessionId: stringValue(options.expectedCallbackSessionId),
|
|
2280
|
+
turnId: stringValue(options.expectedCallbackTurnId),
|
|
2281
|
+
messageId: stringValue(options.expectedCallbackMessageId),
|
|
2282
|
+
openclawSession: stringValue(options.expectedCallbackOpenclawSession)
|
|
2283
|
+
};
|
|
2284
|
+
const values = Object.values(candidate);
|
|
2285
|
+
if (values.every((value) => value === undefined)) {
|
|
2286
|
+
return undefined;
|
|
2287
|
+
}
|
|
2288
|
+
if (values.some((value) => value === undefined)) {
|
|
2289
|
+
throw new Error("automatic approval callback identity is incomplete; no approval key was sent");
|
|
2290
|
+
}
|
|
2291
|
+
return candidate;
|
|
2292
|
+
}
|
|
2293
|
+
function assertAutoApprovalCallbackRoute(input) {
|
|
2294
|
+
if (input.options.autoApproved !== true) {
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
const expected = autoApprovalCallbackAuthorityFromOptions(input.options);
|
|
2298
|
+
if (!expected) {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
const storedOpenClawSession = stringValue(input.conversation.gateway_session ?? input.conversation.openclaw_session);
|
|
2302
|
+
if (input.conversation.conversation_id !== expected.conversationId ||
|
|
2303
|
+
sessionIdForConversation(input.conversation) !== expected.sessionId ||
|
|
2304
|
+
turnIdForConversation(input.conversation) !== expected.turnId ||
|
|
2305
|
+
!sameCanonicalStatePath(input.conversation.state_path, input.statePath) ||
|
|
2306
|
+
storedOpenClawSession !== expected.openclawSession) {
|
|
2307
|
+
throw new Error("automatic approval callback does not match the selected Turn state; no state was changed");
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2150
2310
|
async function runTerminalConversationApprove({ options, terminal }) {
|
|
2151
2311
|
const { conversationId, agent, terminalControl, pid } = terminal;
|
|
2152
2312
|
const storeDir = storeDirFromOptions(options);
|