@scotthuang/agent-knock-knock 0.13.18 → 0.14.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 +31 -0
- package/CONTRIBUTING.md +212 -0
- package/README.md +105 -166
- package/connectors/deepseek-harness/LICENSE +21 -0
- package/connectors/deepseek-harness/README.md +406 -0
- package/connectors/pi/LICENSE +21 -0
- package/connectors/pi/README.md +446 -0
- package/dist/src/backend-recovery-semantic.d.ts +16 -0
- package/dist/src/backend-recovery-semantic.js +113 -0
- package/dist/src/backend-recovery-semantic.js.map +1 -0
- package/dist/src/backend-task-recovery.d.ts +103 -0
- package/dist/src/backend-task-recovery.js +142 -0
- package/dist/src/backend-task-recovery.js.map +1 -0
- package/dist/src/callback-transport.d.ts +8 -0
- package/dist/src/callback-transport.js +14 -0
- package/dist/src/callback-transport.js.map +1 -1
- package/dist/src/cli-core.js +18 -8
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/cli-runtime-context.d.ts +5 -0
- package/dist/src/cli-runtime-context.js.map +1 -1
- package/dist/src/codex-native-cli-adapter.d.ts +6 -0
- package/dist/src/codex-native-cli-adapter.js +273 -0
- package/dist/src/codex-native-cli-adapter.js.map +1 -0
- package/dist/src/codex-native-client-interactions.d.ts +6 -0
- package/dist/src/codex-native-client-interactions.js +124 -0
- package/dist/src/codex-native-client-interactions.js.map +1 -0
- package/dist/src/codex-native-client-rpc.d.ts +21 -0
- package/dist/src/codex-native-client-rpc.js +137 -0
- package/dist/src/codex-native-client-rpc.js.map +1 -0
- package/dist/src/codex-native-client.d.ts +42 -0
- package/dist/src/codex-native-client.js +326 -0
- package/dist/src/codex-native-client.js.map +1 -0
- package/dist/src/codex-native-identity.d.ts +6 -0
- package/dist/src/codex-native-identity.js +42 -0
- package/dist/src/codex-native-identity.js.map +1 -0
- package/dist/src/codex-native-monitor.d.ts +4 -0
- package/dist/src/codex-native-monitor.js +92 -0
- package/dist/src/codex-native-monitor.js.map +1 -0
- package/dist/src/codex-native-outbox.d.ts +16 -0
- package/dist/src/codex-native-outbox.js +120 -0
- package/dist/src/codex-native-outbox.js.map +1 -0
- package/dist/src/codex-native-public-projection.d.ts +545 -0
- package/dist/src/codex-native-public-projection.js +182 -0
- package/dist/src/codex-native-public-projection.js.map +1 -0
- package/dist/src/codex-native-record-store.d.ts +27 -0
- package/dist/src/codex-native-record-store.js +111 -0
- package/dist/src/codex-native-record-store.js.map +1 -0
- package/dist/src/codex-native-response-service.d.ts +69 -0
- package/dist/src/codex-native-response-service.js +244 -0
- package/dist/src/codex-native-response-service.js.map +1 -0
- package/dist/src/codex-native-runtime.d.ts +46 -0
- package/dist/src/codex-native-runtime.js +112 -0
- package/dist/src/codex-native-runtime.js.map +1 -0
- package/dist/src/codex-native-semantic.d.ts +17 -0
- package/dist/src/codex-native-semantic.js +175 -0
- package/dist/src/codex-native-semantic.js.map +1 -0
- package/dist/src/codex-native-snapshot.d.ts +19 -0
- package/dist/src/codex-native-snapshot.js +160 -0
- package/dist/src/codex-native-snapshot.js.map +1 -0
- package/dist/src/codex-native-state-store.d.ts +61 -0
- package/dist/src/codex-native-state-store.js +132 -0
- package/dist/src/codex-native-state-store.js.map +1 -0
- package/dist/src/codex-native-task-service.d.ts +71 -0
- package/dist/src/codex-native-task-service.js +502 -0
- package/dist/src/codex-native-task-service.js.map +1 -0
- package/dist/src/codex-native-types.d.ts +92 -0
- package/dist/src/codex-native-types.js +15 -0
- package/dist/src/codex-native-types.js.map +1 -0
- package/dist/src/conversation-list.d.ts +12 -0
- package/dist/src/conversation-list.js +30 -0
- package/dist/src/conversation-list.js.map +1 -0
- package/dist/src/conversation-route-dispatch.d.ts +13 -0
- package/dist/src/conversation-route-dispatch.js +66 -0
- package/dist/src/conversation-route-dispatch.js.map +1 -0
- package/dist/src/conversation-route-legacy.d.ts +18 -0
- package/dist/src/conversation-route-legacy.js +54 -0
- package/dist/src/conversation-route-legacy.js.map +1 -0
- package/dist/src/conversation-route-store.d.ts +51 -0
- package/dist/src/conversation-route-store.js +126 -0
- package/dist/src/conversation-route-store.js.map +1 -0
- package/dist/src/conversation-routing-cli-adapter.d.ts +12 -0
- package/dist/src/conversation-routing-cli-adapter.js +211 -0
- package/dist/src/conversation-routing-cli-adapter.js.map +1 -0
- package/dist/src/conversation-routing-commands.d.ts +11 -0
- package/dist/src/conversation-routing-commands.js +85 -0
- package/dist/src/conversation-routing-commands.js.map +1 -0
- package/dist/src/conversation-routing-home.d.ts +26 -0
- package/dist/src/conversation-routing-home.js +105 -0
- package/dist/src/conversation-routing-home.js.map +1 -0
- package/dist/src/conversation-routing-identity.d.ts +40 -0
- package/dist/src/conversation-routing-identity.js +174 -0
- package/dist/src/conversation-routing-identity.js.map +1 -0
- package/dist/src/conversation-runtime-route-store.d.ts +11 -0
- package/dist/src/conversation-runtime-route-store.js +118 -0
- package/dist/src/conversation-runtime-route-store.js.map +1 -0
- package/dist/src/desktop-async-interactions.d.ts +15 -0
- package/dist/src/desktop-async-interactions.js +88 -0
- package/dist/src/desktop-async-interactions.js.map +1 -0
- package/dist/src/desktop-async-state.d.ts +5 -0
- package/dist/src/desktop-async-state.js +53 -0
- package/dist/src/desktop-async-state.js.map +1 -0
- package/dist/src/desktop-cli-adapter.d.ts +7 -0
- package/dist/src/desktop-cli-adapter.js +322 -0
- package/dist/src/desktop-cli-adapter.js.map +1 -0
- package/dist/src/desktop-cli-controls.d.ts +9 -0
- package/dist/src/desktop-cli-controls.js +45 -0
- package/dist/src/desktop-cli-controls.js.map +1 -0
- package/dist/src/desktop-command-presentation.d.ts +2 -0
- package/dist/src/desktop-command-presentation.js +31 -0
- package/dist/src/desktop-command-presentation.js.map +1 -0
- package/dist/src/desktop-control-semantic.d.ts +8 -0
- package/dist/src/desktop-control-semantic.js +68 -0
- package/dist/src/desktop-control-semantic.js.map +1 -0
- package/dist/src/desktop-identity.d.ts +5 -0
- package/dist/src/desktop-identity.js +40 -0
- package/dist/src/desktop-identity.js.map +1 -0
- package/dist/src/desktop-interaction-projection.d.ts +57 -0
- package/dist/src/desktop-interaction-projection.js +196 -0
- package/dist/src/desktop-interaction-projection.js.map +1 -0
- package/dist/src/desktop-interaction-state.d.ts +3 -0
- package/dist/src/desktop-interaction-state.js +48 -0
- package/dist/src/desktop-interaction-state.js.map +1 -0
- package/dist/src/desktop-ipc-async-answer.d.ts +9 -0
- package/dist/src/desktop-ipc-async-answer.js +71 -0
- package/dist/src/desktop-ipc-async-answer.js.map +1 -0
- package/dist/src/desktop-ipc-client.d.ts +47 -0
- package/dist/src/desktop-ipc-client.js +400 -0
- package/dist/src/desktop-ipc-client.js.map +1 -0
- package/dist/src/desktop-ipc-native-operations.d.ts +11 -0
- package/dist/src/desktop-ipc-native-operations.js +126 -0
- package/dist/src/desktop-ipc-native-operations.js.map +1 -0
- package/dist/src/desktop-ipc-transport.d.ts +13 -0
- package/dist/src/desktop-ipc-transport.js +123 -0
- package/dist/src/desktop-ipc-transport.js.map +1 -0
- package/dist/src/desktop-list.d.ts +25 -0
- package/dist/src/desktop-list.js +55 -0
- package/dist/src/desktop-list.js.map +1 -0
- package/dist/src/desktop-metadata-reader.d.ts +30 -0
- package/dist/src/desktop-metadata-reader.js +182 -0
- package/dist/src/desktop-metadata-reader.js.map +1 -0
- package/dist/src/desktop-monitor.d.ts +4 -0
- package/dist/src/desktop-monitor.js +101 -0
- package/dist/src/desktop-monitor.js.map +1 -0
- package/dist/src/desktop-public-projection.d.ts +6 -0
- package/dist/src/desktop-public-projection.js +134 -0
- package/dist/src/desktop-public-projection.js.map +1 -0
- package/dist/src/desktop-request-interactions.d.ts +14 -0
- package/dist/src/desktop-request-interactions.js +144 -0
- package/dist/src/desktop-request-interactions.js.map +1 -0
- package/dist/src/desktop-response-dispatch.d.ts +14 -0
- package/dist/src/desktop-response-dispatch.js +19 -0
- package/dist/src/desktop-response-dispatch.js.map +1 -0
- package/dist/src/desktop-response-evidence.d.ts +4 -0
- package/dist/src/desktop-response-evidence.js +19 -0
- package/dist/src/desktop-response-evidence.js.map +1 -0
- package/dist/src/desktop-response-service.d.ts +11 -0
- package/dist/src/desktop-response-service.js +188 -0
- package/dist/src/desktop-response-service.js.map +1 -0
- package/dist/src/desktop-response-store.d.ts +35 -0
- package/dist/src/desktop-response-store.js +58 -0
- package/dist/src/desktop-response-store.js.map +1 -0
- package/dist/src/desktop-response-types.d.ts +20 -0
- package/dist/src/desktop-response-types.js +2 -0
- package/dist/src/desktop-response-types.js.map +1 -0
- package/dist/src/desktop-response-value.d.ts +6 -0
- package/dist/src/desktop-response-value.js +31 -0
- package/dist/src/desktop-response-value.js.map +1 -0
- package/dist/src/desktop-runtime.d.ts +153 -0
- package/dist/src/desktop-runtime.js +88 -0
- package/dist/src/desktop-runtime.js.map +1 -0
- package/dist/src/desktop-semantic.d.ts +21 -0
- package/dist/src/desktop-semantic.js +140 -0
- package/dist/src/desktop-semantic.js.map +1 -0
- package/dist/src/desktop-session-catalog.d.ts +68 -0
- package/dist/src/desktop-session-catalog.js +225 -0
- package/dist/src/desktop-session-catalog.js.map +1 -0
- package/dist/src/desktop-session-controls.d.ts +145 -0
- package/dist/src/desktop-session-controls.js +176 -0
- package/dist/src/desktop-session-controls.js.map +1 -0
- package/dist/src/desktop-sidebar-metadata.d.ts +27 -0
- package/dist/src/desktop-sidebar-metadata.js +83 -0
- package/dist/src/desktop-sidebar-metadata.js.map +1 -0
- package/dist/src/desktop-sidebar-projects.d.ts +10 -0
- package/dist/src/desktop-sidebar-projects.js +31 -0
- package/dist/src/desktop-sidebar-projects.js.map +1 -0
- package/dist/src/desktop-sidebar-selection.d.ts +28 -0
- package/dist/src/desktop-sidebar-selection.js +85 -0
- package/dist/src/desktop-sidebar-selection.js.map +1 -0
- package/dist/src/desktop-snapshot-details.d.ts +4 -0
- package/dist/src/desktop-snapshot-details.js +116 -0
- package/dist/src/desktop-snapshot-details.js.map +1 -0
- package/dist/src/desktop-snapshot.d.ts +11 -0
- package/dist/src/desktop-snapshot.js +276 -0
- package/dist/src/desktop-snapshot.js.map +1 -0
- package/dist/src/desktop-state-store.d.ts +72 -0
- package/dist/src/desktop-state-store.js +269 -0
- package/dist/src/desktop-state-store.js.map +1 -0
- package/dist/src/desktop-task-interactions.d.ts +10 -0
- package/dist/src/desktop-task-interactions.js +93 -0
- package/dist/src/desktop-task-interactions.js.map +1 -0
- package/dist/src/desktop-task-recovery.d.ts +38 -0
- package/dist/src/desktop-task-recovery.js +171 -0
- package/dist/src/desktop-task-recovery.js.map +1 -0
- package/dist/src/desktop-task-service.d.ts +54 -0
- package/dist/src/desktop-task-service.js +441 -0
- package/dist/src/desktop-task-service.js.map +1 -0
- package/dist/src/desktop-types.d.ts +281 -0
- package/dist/src/desktop-types.js +13 -0
- package/dist/src/desktop-types.js.map +1 -0
- package/dist/src/host-monitor-reconciliation.js +36 -4
- package/dist/src/host-monitor-reconciliation.js.map +1 -1
- package/dist/src/host-profile-runtime.js +7 -3
- package/dist/src/host-profile-runtime.js.map +1 -1
- package/dist/src/local-codex-cli-adapter.d.ts +7 -0
- package/dist/src/local-codex-cli-adapter.js +13 -0
- package/dist/src/local-codex-cli-adapter.js.map +1 -0
- package/dist/src/openclaw-callback-transport.js +2 -1
- package/dist/src/openclaw-callback-transport.js.map +1 -1
- package/dist/src/semantic-conversation-target.d.ts +4 -0
- package/dist/src/semantic-conversation-target.js +20 -0
- package/dist/src/semantic-conversation-target.js.map +1 -0
- package/dist/src/semantic-permission-control.js +47 -9
- package/dist/src/semantic-permission-control.js.map +1 -1
- package/dist/src/semantic-private-authority-offers.d.ts +1 -1
- package/dist/src/semantic-private-authority-offers.js +3 -1
- package/dist/src/semantic-private-authority-offers.js.map +1 -1
- package/dist/src/semantic-tool-command-helpers.d.ts +6 -0
- package/dist/src/semantic-tool-command-helpers.js +199 -78
- package/dist/src/semantic-tool-command-helpers.js.map +1 -1
- package/dist/src/semantic-tool-descriptions.d.ts +2 -1
- package/dist/src/semantic-tool-descriptions.js +81 -35
- package/dist/src/semantic-tool-descriptions.js.map +1 -1
- package/dist/src/semantic-tool-list-projection.js +66 -2
- package/dist/src/semantic-tool-list-projection.js.map +1 -1
- package/dist/src/semantic-tool-presentation.js +76 -7
- package/dist/src/semantic-tool-presentation.js.map +1 -1
- package/dist/src/semantic-tool-runtime.js +129 -29
- package/dist/src/semantic-tool-runtime.js.map +1 -1
- package/dist/src/semantic-tool-schemas.d.ts +410 -27
- package/dist/src/semantic-tool-schemas.js +176 -88
- package/dist/src/semantic-tool-schemas.js.map +1 -1
- package/dist/src/terminal-action-contracts.js +10 -6
- package/dist/src/terminal-action-contracts.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.d.ts +1 -0
- package/dist/src/terminal-list-cli-adapter.js +6 -3
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-monitor-decision-policy.d.ts +1 -1
- package/dist/src/terminal-permission-control-list-projection.d.ts +1 -1
- package/dist/src/terminal-user-send-intent.d.ts +2 -0
- package/dist/src/terminal-user-send-intent.js +11 -3
- package/dist/src/terminal-user-send-intent.js.map +1 -1
- package/docs/architecture/current-architecture.md +313 -0
- package/docs/architecture/interactive-terminal-response.md +276 -0
- package/docs/architecture/issue-126-final-report.md +248 -0
- package/docs/architecture/orchestration-refactor-history.md +2987 -0
- package/docs/architecture/orchestration-refactor.md +21 -0
- package/docs/backend-task-recovery.md +89 -0
- package/docs/bidirectional-agent-protocol.md +260 -0
- package/docs/codex-cli-native-compatibility.md +246 -0
- package/docs/codex-desktop-compatibility.md +414 -0
- package/docs/openclaw-operations.md +281 -0
- package/docs/openclaw-session-turn-delivery-requirement.md +45 -0
- package/docs/operator-guide.md +543 -0
- package/docs/storage-and-logging.md +91 -0
- package/docs/test-performance-issue-108.md +80 -0
- package/docs/test-performance-issue-120.md +226 -0
- package/docs/testing.md +628 -0
- package/openclaw.plugin.json +7 -3
- package/package.json +6 -13
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +66 -15
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { assertBackendRecoveryFields, assertBackendRecoveryUpdate } from "./backend-task-recovery.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
import { assertRealDirectory, atomicSaveJsonFile, isNodeError, readJsonFileNoFollow } from "./durable-json-file.js";
|
|
6
|
+
import { assertStoreReadable, ensureDir, withStoreWriterLease } from "./store.js";
|
|
7
|
+
import { createCallbackEnvelope, parseCallbackAttemptOutcome, parseCallbackRoute } from "./callback-transport.js";
|
|
8
|
+
import { parseDesktopConversationId } from "./desktop-identity.js";
|
|
9
|
+
import { assertDesktopAsyncInteraction } from "./desktop-async-state.js";
|
|
10
|
+
import { assertDesktopInteraction } from "./desktop-interaction-state.js";
|
|
11
|
+
export const DESKTOP_TASKS_DIRECTORY = "desktop-tasks";
|
|
12
|
+
function assertId(id) {
|
|
13
|
+
if (!/^desktop-watch:[a-zA-Z0-9_-]{8,128}$/.test(id))
|
|
14
|
+
throw new Error("invalid Desktop task id");
|
|
15
|
+
}
|
|
16
|
+
function nonblank(value) {
|
|
17
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
18
|
+
}
|
|
19
|
+
function validTime(value) {
|
|
20
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
21
|
+
}
|
|
22
|
+
function assertPrivate(filePath, directory) {
|
|
23
|
+
const stat = fs.lstatSync(filePath);
|
|
24
|
+
if (stat.isSymbolicLink() || (directory ? !stat.isDirectory() : !stat.isFile()) ||
|
|
25
|
+
(stat.mode & 0o077) !== 0 || (process.getuid && stat.uid !== process.getuid())) {
|
|
26
|
+
throw new Error("Desktop state path must be an owner-private regular " + (directory ? "directory" : "file"));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function assertTaskIdentity(r) {
|
|
30
|
+
assertId(r.id);
|
|
31
|
+
if (r.watch_id !== r.id || !nonblank(r.desktop_id) || !nonblank(r.controller_session) ||
|
|
32
|
+
!r.target || !nonblank(r.target.codexHome) || !path.isAbsolute(r.target.codexHome) ||
|
|
33
|
+
!nonblank(r.target.hostId) || !nonblank(r.target.threadId))
|
|
34
|
+
throw new Error("invalid Desktop task identity");
|
|
35
|
+
if (!isDeepStrictEqual(parseDesktopConversationId(r.desktop_id), r.target))
|
|
36
|
+
throw new Error("Desktop conversation ID does not match native target");
|
|
37
|
+
}
|
|
38
|
+
function assertTaskLifecycle(r) {
|
|
39
|
+
if (!["send", "watch"].includes(r.kind) ||
|
|
40
|
+
!["awaiting_acceptance", "watching", "completed", "failed", "interrupted", "timed_out", "cancelled"].includes(r.status) ||
|
|
41
|
+
!validTime(r.created_at) || !validTime(r.updated_at) || !validTime(r.deadline_at) ||
|
|
42
|
+
!Number.isSafeInteger(r.pending_manual_count) || r.pending_manual_count < 0 ||
|
|
43
|
+
!Array.isArray(r.notifications) || (r.native_turn_id !== undefined && !nonblank(r.native_turn_id)))
|
|
44
|
+
throw new Error("invalid Desktop task state");
|
|
45
|
+
if (["watching", "completed", "interrupted"].includes(r.status) && !r.native_turn_id)
|
|
46
|
+
throw new Error("Desktop task state requires an exact turn anchor");
|
|
47
|
+
}
|
|
48
|
+
function assertSendIntent(r) {
|
|
49
|
+
const i = r.send_intent;
|
|
50
|
+
if (!i || !nonblank(i.message_id) || !nonblank(i.client_user_message_id) || !nonblank(i.text) ||
|
|
51
|
+
!nonblank(i.owner_client_id) || !Array.isArray(i.baseline_turn_ids) ||
|
|
52
|
+
i.baseline_turn_ids.some(id => !nonblank(id)) || !validTime(i.dispatched_at) ||
|
|
53
|
+
!["reserved", "uncertain", "accepted", "not_sent"].includes(i.state) ||
|
|
54
|
+
(i.receipt_turn_id !== undefined && !nonblank(i.receipt_turn_id)) ||
|
|
55
|
+
(i.state === "accepted" && !r.native_turn_id) || (r.native_turn_id && i.state !== "accepted"))
|
|
56
|
+
throw new Error("invalid Desktop send intent");
|
|
57
|
+
}
|
|
58
|
+
function assertNotification(r, n, ids) {
|
|
59
|
+
if (!n || !nonblank(n.id) || ids.has(n.id) ||
|
|
60
|
+
!["ready", "leased", "retry_wait", "accepted", "failed", "uncertain"].includes(n.status) ||
|
|
61
|
+
!Number.isSafeInteger(n.attempts) || n.attempts < 0 || !r.callback_route)
|
|
62
|
+
throw new Error("invalid Desktop outbox");
|
|
63
|
+
ids.add(n.id);
|
|
64
|
+
const envelope = createCallbackEnvelope({ route: r.callback_route, source: n.envelope.source, event: n.envelope.event });
|
|
65
|
+
if (!isDeepStrictEqual(envelope, n.envelope) || envelope.source.kind !== "desktop_watch" ||
|
|
66
|
+
envelope.source.watch_id !== r.id || envelope.source.desktop_id !== r.desktop_id ||
|
|
67
|
+
envelope.event.id !== n.id)
|
|
68
|
+
throw new Error("Desktop callback identity mismatch");
|
|
69
|
+
if (envelope.event.requires_response)
|
|
70
|
+
assertInteractionNotification(r, n);
|
|
71
|
+
assertNotificationDelivery(n);
|
|
72
|
+
}
|
|
73
|
+
function assertInteractionNotification(record, notification) {
|
|
74
|
+
const event = notification.envelope.event;
|
|
75
|
+
const metadata = event.metadata;
|
|
76
|
+
if (event.type !== "desktop_watch.interaction" || !metadata || !nonblank(metadata.interaction_id) ||
|
|
77
|
+
!["respond", "approve"].includes(String(metadata.action)) || metadata.thread_id !== record.target.threadId || metadata.turn_id !== record.native_turn_id ||
|
|
78
|
+
notification.id !== `${record.id}:interaction:${metadata.interaction_id}` || !metadata.interaction || typeof metadata.interaction !== "object") {
|
|
79
|
+
throw new Error("Desktop response notification requires an exact interaction");
|
|
80
|
+
}
|
|
81
|
+
const interaction = metadata.interaction;
|
|
82
|
+
if (!["async_question", "blocking_question", "command_approval", "file_approval"].includes(String(interaction.kind)) || interaction.interaction_id !== metadata.interaction_id ||
|
|
83
|
+
interaction.native_thread_id !== record.target.threadId || interaction.native_turn_id !== record.native_turn_id) {
|
|
84
|
+
throw new Error("Desktop response notification has a different interaction identity");
|
|
85
|
+
}
|
|
86
|
+
if (metadata.action !== (String(interaction.kind).endsWith("approval") ? "approve" : "respond"))
|
|
87
|
+
throw new Error("Desktop notification action does not match its interaction");
|
|
88
|
+
}
|
|
89
|
+
function assertAsyncInteractions(record) {
|
|
90
|
+
if (record.pending_async_interactions === undefined)
|
|
91
|
+
return;
|
|
92
|
+
if (!Array.isArray(record.pending_async_interactions))
|
|
93
|
+
throw new Error("Invalid Desktop pending async questions");
|
|
94
|
+
const ids = new Set();
|
|
95
|
+
for (const interaction of record.pending_async_interactions) {
|
|
96
|
+
assertDesktopAsyncInteraction(interaction, record.target, record.native_turn_id);
|
|
97
|
+
if (!record.native_turn_id || ids.has(interaction.id))
|
|
98
|
+
throw new Error("Desktop async question lacks a unique task anchor");
|
|
99
|
+
ids.add(interaction.id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function assertPendingInteractions(record) {
|
|
103
|
+
if (record.pending_interactions === undefined)
|
|
104
|
+
return;
|
|
105
|
+
if (!Array.isArray(record.pending_interactions))
|
|
106
|
+
throw new Error("Invalid Desktop pending interactions");
|
|
107
|
+
const ids = new Set();
|
|
108
|
+
for (const interaction of record.pending_interactions) {
|
|
109
|
+
assertDesktopInteraction(interaction, record.target, record.native_turn_id);
|
|
110
|
+
if (!record.native_turn_id || ids.has(interaction.id))
|
|
111
|
+
throw new Error("Desktop interaction lacks a unique task anchor");
|
|
112
|
+
ids.add(interaction.id);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function assertNotificationDelivery(n) {
|
|
116
|
+
if (n.retry_budget_until !== undefined && (!Number.isSafeInteger(n.retry_budget_until) || n.retry_budget_until < 1))
|
|
117
|
+
throw new Error("invalid Desktop delivery retry budget");
|
|
118
|
+
if (n.outcome)
|
|
119
|
+
parseCallbackAttemptOutcome(n.outcome);
|
|
120
|
+
if (n.status === "leased" && (!nonblank(n.attempt_id) || !validTime(n.lease_expires_at)))
|
|
121
|
+
throw new Error("invalid Desktop delivery lease");
|
|
122
|
+
if (n.status === "retry_wait" && (!validTime(n.retry_at) || n.outcome?.disposition !== "retryable_failure"))
|
|
123
|
+
throw new Error("invalid Desktop delivery retry");
|
|
124
|
+
}
|
|
125
|
+
export function assertDesktopTaskRecord(value) {
|
|
126
|
+
const r = value;
|
|
127
|
+
if (!r || typeof r !== "object" || r.schema !== "agent-knock-knock/desktop-task" ||
|
|
128
|
+
r.version !== 1 || !Number.isSafeInteger(r.revision) || r.revision < 1)
|
|
129
|
+
throw new Error("invalid Desktop task record");
|
|
130
|
+
assertTaskIdentity(r);
|
|
131
|
+
assertTaskLifecycle(r);
|
|
132
|
+
assertBackendRecoveryFields(r);
|
|
133
|
+
assertAsyncInteractions(r);
|
|
134
|
+
assertPendingInteractions(r);
|
|
135
|
+
if (r.kind === "send")
|
|
136
|
+
assertSendIntent(r);
|
|
137
|
+
else if (r.send_intent !== undefined || !r.native_turn_id)
|
|
138
|
+
throw new Error("invalid Desktop watch anchor");
|
|
139
|
+
if (r.callback_route) {
|
|
140
|
+
parseCallbackRoute(r.callback_route);
|
|
141
|
+
if (r.callback_route.controller_session_id !== r.controller_session || r.callback_route.capabilities?.respond === true)
|
|
142
|
+
throw new Error("Desktop callback must belong to its controller and be notification-only");
|
|
143
|
+
}
|
|
144
|
+
const ids = new Set();
|
|
145
|
+
for (const n of r.notifications)
|
|
146
|
+
assertNotification(r, n, ids);
|
|
147
|
+
}
|
|
148
|
+
export function createDesktopStateStore(storeDir, locks) {
|
|
149
|
+
const root = path.join(storeDir, DESKTOP_TASKS_DIRECTORY);
|
|
150
|
+
const statePath = (id) => { assertId(id); return path.join(root, `${id}.json`); };
|
|
151
|
+
const load = (id) => {
|
|
152
|
+
const filePath = statePath(id);
|
|
153
|
+
if (!fs.existsSync(storeDir))
|
|
154
|
+
return undefined;
|
|
155
|
+
assertStoreReadable(storeDir);
|
|
156
|
+
try {
|
|
157
|
+
assertRealDirectory(root, "Desktop tasks");
|
|
158
|
+
assertPrivate(root, true);
|
|
159
|
+
assertPrivate(filePath, false);
|
|
160
|
+
const record = readJsonFileNoFollow(filePath, "Desktop task");
|
|
161
|
+
assertDesktopTaskRecord(record);
|
|
162
|
+
if (record.id !== id)
|
|
163
|
+
throw new Error("Desktop record filename identity mismatch");
|
|
164
|
+
return record;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (isNodeError(error, "ENOENT"))
|
|
168
|
+
return undefined;
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const scan = (isolate) => {
|
|
173
|
+
const result = { tasks: [], errors: [] };
|
|
174
|
+
if (!fs.existsSync(storeDir))
|
|
175
|
+
return result;
|
|
176
|
+
assertStoreReadable(storeDir);
|
|
177
|
+
if (!fs.existsSync(root))
|
|
178
|
+
return result;
|
|
179
|
+
assertPrivate(root, true);
|
|
180
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
181
|
+
const filePath = path.join(root, entry.name);
|
|
182
|
+
try {
|
|
183
|
+
assertPrivate(filePath, false);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
// Another worker can release its lock or rename a temporary file
|
|
187
|
+
// between readdir and lstat. Do not mistake that normal race for corruption.
|
|
188
|
+
if (isNodeError(error, "ENOENT"))
|
|
189
|
+
continue;
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (/^desktop-watch:[a-zA-Z0-9_-]{8,128}\.json\.lock(?:\.reclaim)?$/.test(entry.name) ||
|
|
193
|
+
/^\.desktop-watch:[a-zA-Z0-9_-]{8,128}\.json\..+\.tmp$/.test(entry.name))
|
|
194
|
+
continue;
|
|
195
|
+
if (!entry.name.endsWith(".json"))
|
|
196
|
+
throw new Error("unknown Desktop state file");
|
|
197
|
+
const id = entry.name.slice(0, -5);
|
|
198
|
+
assertId(id);
|
|
199
|
+
// Security checks are outside content isolation: symlinks and access failures abort the scan.
|
|
200
|
+
let value;
|
|
201
|
+
try {
|
|
202
|
+
value = readJsonFileNoFollow(filePath, "Desktop task");
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (!isolate || !(error instanceof SyntaxError))
|
|
206
|
+
throw error;
|
|
207
|
+
result.errors.push({ id, error_code: "desktop_record_invalid" });
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
assertDesktopTaskRecord(value);
|
|
212
|
+
if (value.id !== id)
|
|
213
|
+
throw new Error("Desktop record filename identity mismatch");
|
|
214
|
+
result.tasks.push(value);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
if (!isolate)
|
|
218
|
+
throw error;
|
|
219
|
+
result.errors.push({ id, error_code: "desktop_record_invalid" });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
result.tasks.sort((a, b) => b.created_at.localeCompare(a.created_at) || a.id.localeCompare(b.id));
|
|
223
|
+
return result;
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
load, list: () => scan(false).tasks, scanForReconciliation: () => scan(true),
|
|
227
|
+
withLock: (id, operation) => withStoreWriterLease(storeDir, () => {
|
|
228
|
+
statePath(id);
|
|
229
|
+
ensureDir(root);
|
|
230
|
+
assertPrivate(root, true);
|
|
231
|
+
const release = locks.acquire(`${statePath(id)}.lock`);
|
|
232
|
+
try {
|
|
233
|
+
return operation();
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
release();
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
save: (record, expectedRevision) => withStoreWriterLease(storeDir, () => {
|
|
240
|
+
const current = load(record.id);
|
|
241
|
+
if ((current?.revision ?? null) !== expectedRevision)
|
|
242
|
+
throw new Error("Desktop task revision conflict");
|
|
243
|
+
if (current) {
|
|
244
|
+
for (const key of ["id", "watch_id", "desktop_id", "target", "controller_session", "kind", "created_at", "callback_route"]) {
|
|
245
|
+
if (!isDeepStrictEqual(current[key], record[key]))
|
|
246
|
+
throw new Error(`Desktop task immutable ${key} changed`);
|
|
247
|
+
}
|
|
248
|
+
assertBackendRecoveryUpdate(current, record);
|
|
249
|
+
if (current.native_turn_id && current.native_turn_id !== record.native_turn_id)
|
|
250
|
+
throw new Error("Desktop exact turn anchor cannot change");
|
|
251
|
+
if (current.send_intent) {
|
|
252
|
+
for (const key of ["message_id", "client_user_message_id", "text", "owner_client_id", "baseline_turn_ids", "dispatched_at"]) {
|
|
253
|
+
if (!isDeepStrictEqual(current.send_intent[key], record.send_intent?.[key]))
|
|
254
|
+
throw new Error("Desktop send intent cannot change");
|
|
255
|
+
}
|
|
256
|
+
if (current.send_intent.receipt_turn_id && current.send_intent.receipt_turn_id !== record.send_intent?.receipt_turn_id)
|
|
257
|
+
throw new Error("Desktop send receipt cannot change");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const saved = { ...record, revision: (expectedRevision ?? 0) + 1 };
|
|
261
|
+
assertDesktopTaskRecord(saved);
|
|
262
|
+
atomicSaveJsonFile(statePath(saved.id), saved, {
|
|
263
|
+
rootLabel: "AKK store", directoryLabel: "Desktop tasks", fileLabel: "Desktop task", ensureDirectory: ensureDir
|
|
264
|
+
});
|
|
265
|
+
return saved;
|
|
266
|
+
})
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=desktop-state-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop-state-store.js","sourceRoot":"","sources":["../../src/desktop-state-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DesktopPendingRequest, DesktopSnapshot } from "./desktop-types.js";
|
|
2
|
+
import type { DesktopTaskRecord } from "./desktop-state-store.js";
|
|
3
|
+
/** Native pending requests and asynchronous questions have distinct completeness evidence. */
|
|
4
|
+
export declare function updateDesktopInteractionAttention(task: DesktopTaskRecord, snapshot: DesktopSnapshot): void;
|
|
5
|
+
/** Stopping observation never implies that the original task or native question stopped. */
|
|
6
|
+
export declare function clearDesktopInteractionAttention(task: DesktopTaskRecord, errorCode: string): void;
|
|
7
|
+
/** Keep unsupported special forms manual; supported requests already have actionable notifications. */
|
|
8
|
+
export declare function desktopManualRequests(task: DesktopTaskRecord, snapshot: DesktopSnapshot): DesktopPendingRequest[];
|
|
9
|
+
/** A request promoted to an actionable interaction must not also deliver an old manual-only notice. */
|
|
10
|
+
export declare function revokeObsoleteDesktopManualAttention(task: DesktopTaskRecord): void;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createCallbackEnvelope } from "./callback-transport.js";
|
|
2
|
+
import { desktopInteractions } from "./desktop-request-interactions.js";
|
|
3
|
+
import { persistDesktopInteraction } from "./desktop-interaction-state.js";
|
|
4
|
+
import { desktopInteractionProjection } from "./desktop-interaction-projection.js";
|
|
5
|
+
function callbackInteraction(task, interaction) {
|
|
6
|
+
const projected = { ...desktopInteractionProjection(interaction, task.desktop_id, task.id) };
|
|
7
|
+
const output = { conversation_id: task.desktop_id, watch_id: task.id };
|
|
8
|
+
for (const key of ["interaction_id", "kind", "status", "native_thread_id", "native_turn_id", "questions",
|
|
9
|
+
"command", "reason", "cwd", "file_changes", "network", "available_decisions", "decisions"])
|
|
10
|
+
if (projected[key] !== undefined)
|
|
11
|
+
output[key] = projected[key];
|
|
12
|
+
return output;
|
|
13
|
+
}
|
|
14
|
+
function interactionBody(task, interaction) {
|
|
15
|
+
const approval = interaction.kind === "command_approval" || interaction.kind === "file_approval";
|
|
16
|
+
const summary = approval ? interaction.command ?? interaction.reason ?? "Review the pending file changes."
|
|
17
|
+
: interaction.questions.map(question => `${question.title}${question.options.length ? `\nOptions: ${question.options.join(" / ")}` : ""}`).join("\n");
|
|
18
|
+
const state = interaction.kind === "async_question" ? "has a question awaiting your reply. The task may continue working."
|
|
19
|
+
: approval ? "is waiting for approval." : "is waiting for an answer before it can continue.";
|
|
20
|
+
return `Desktop task ${task.id} ${state}\n${summary}\n` +
|
|
21
|
+
`Refresh AKK Status for this exact conversation or Watch to confirm that the interaction is still current, then ask the user. ` +
|
|
22
|
+
(approval ? "Use AKK Approve only with the user's decision; do not approve automatically.\n"
|
|
23
|
+
: "Only use AKK Respond after the user supplies an answer; do not choose an answer automatically.\n") +
|
|
24
|
+
JSON.stringify(callbackInteraction(task, interaction));
|
|
25
|
+
}
|
|
26
|
+
/** Native pending requests and asynchronous questions have distinct completeness evidence. */
|
|
27
|
+
export function updateDesktopInteractionAttention(task, snapshot) {
|
|
28
|
+
const turn = snapshot.turns.find(candidate => candidate.turnId === task.native_turn_id);
|
|
29
|
+
// A partial history is missing evidence, not proof that a question was answered.
|
|
30
|
+
if (!turn || turn.status === "unknown") {
|
|
31
|
+
task.observation_error = "desktop_interactions_incomplete";
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const pending = turn.status === "inProgress" ? desktopInteractions(snapshot, task.native_turn_id)
|
|
35
|
+
.filter(interaction => interaction.threadId === task.target.threadId) : [];
|
|
36
|
+
if (turn.status === "inProgress" && !turn.itemsComplete) {
|
|
37
|
+
task.observation_error = "desktop_async_questions_incomplete";
|
|
38
|
+
pending.push(...(task.pending_async_interactions ?? []).filter(previous => !pending.some(current => current.id === previous.id)));
|
|
39
|
+
}
|
|
40
|
+
task.pending_interactions = pending.map(persistDesktopInteraction);
|
|
41
|
+
task.pending_async_interactions = task.pending_interactions.filter((interaction) => interaction.kind === "async_question");
|
|
42
|
+
revokeInteractionNotifications(task, pending, "desktop_interaction_resolved");
|
|
43
|
+
if (!task.callback_route)
|
|
44
|
+
return;
|
|
45
|
+
for (const interaction of pending) {
|
|
46
|
+
const id = `${task.id}:interaction:${interaction.id}`;
|
|
47
|
+
if (task.notifications.some(notification => notification.id === id))
|
|
48
|
+
continue;
|
|
49
|
+
task.notifications.push({ id, attempts: 0, status: "ready", envelope: createCallbackEnvelope({ route: task.callback_route,
|
|
50
|
+
source: { kind: "desktop_watch", watch_id: task.id, desktop_id: task.desktop_id },
|
|
51
|
+
event: { id, type: "desktop_watch.interaction", body: interactionBody(task, interaction), requires_response: true,
|
|
52
|
+
metadata: { watch_id: task.id, desktop_id: task.desktop_id, thread_id: task.target.threadId, turn_id: task.native_turn_id,
|
|
53
|
+
interaction_id: interaction.id, interaction: callbackInteraction(task, interaction),
|
|
54
|
+
action: interaction.kind.endsWith("approval") ? "approve" : "respond" } }
|
|
55
|
+
}) });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function revokeInteractionNotifications(task, pending, errorCode) {
|
|
59
|
+
for (const notification of task.notifications) {
|
|
60
|
+
const interactionId = notification.envelope.event.metadata?.interaction_id;
|
|
61
|
+
if (notification.envelope.event.type === "desktop_watch.interaction" && typeof interactionId === "string" &&
|
|
62
|
+
!pending.some(interaction => interaction.id === interactionId) && ["ready", "retry_wait"].includes(notification.status)) {
|
|
63
|
+
notification.status = "failed";
|
|
64
|
+
notification.outcome = { disposition: "permanent_failure", error_code: errorCode };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Stopping observation never implies that the original task or native question stopped. */
|
|
69
|
+
export function clearDesktopInteractionAttention(task, errorCode) {
|
|
70
|
+
task.pending_async_interactions = [];
|
|
71
|
+
task.pending_interactions = [];
|
|
72
|
+
revokeInteractionNotifications(task, [], errorCode);
|
|
73
|
+
}
|
|
74
|
+
/** Keep unsupported special forms manual; supported requests already have actionable notifications. */
|
|
75
|
+
export function desktopManualRequests(task, snapshot) {
|
|
76
|
+
const turn = snapshot.turns.find(candidate => candidate.turnId === task.native_turn_id);
|
|
77
|
+
if (turn?.status !== "inProgress")
|
|
78
|
+
return [];
|
|
79
|
+
return snapshot.pendingRequests.filter(request => (request.turnId === task.native_turn_id ||
|
|
80
|
+
!request.turnId && snapshot.latestTurnId === task.native_turn_id) && !(task.pending_interactions ?? []).some(interaction => interaction.kind !== "async_question" && interaction.method === request.method && interaction.requestId === request.requestId));
|
|
81
|
+
}
|
|
82
|
+
/** A request promoted to an actionable interaction must not also deliver an old manual-only notice. */
|
|
83
|
+
export function revokeObsoleteDesktopManualAttention(task) {
|
|
84
|
+
const currentId = task.pending_manual_fingerprint ? `${task.id}:manual:${task.pending_manual_fingerprint}` : undefined;
|
|
85
|
+
for (const notification of task.notifications) {
|
|
86
|
+
if (notification.envelope.event.type === "desktop_watch.manual" && notification.id !== currentId &&
|
|
87
|
+
["ready", "retry_wait"].includes(notification.status)) {
|
|
88
|
+
notification.status = "failed";
|
|
89
|
+
notification.outcome = { disposition: "permanent_failure", error_code: "desktop_manual_attention_changed" };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=desktop-task-interactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop-task-interactions.js","sourceRoot":"","sources":["../../src/desktop-task-interactions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { backendObservationStopped } from "./backend-task-recovery.js";
|
|
2
|
+
import type { DesktopNotification, DesktopTaskRecord } from "./desktop-state-store.js";
|
|
3
|
+
import type { DesktopSnapshot, DesktopThreadIdentity } from "./desktop-types.js";
|
|
4
|
+
export declare class DesktopTaskError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly dispatchState?: "not_sent" | undefined;
|
|
7
|
+
constructor(code: string, message: string, dispatchState?: "not_sent" | undefined);
|
|
8
|
+
}
|
|
9
|
+
export interface DesktopRecoveryInput {
|
|
10
|
+
controllerSession: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DesktopRecoveryHost {
|
|
13
|
+
status(id: string): DesktopTaskRecord;
|
|
14
|
+
update(id: string, operation: (task: DesktopTaskRecord) => void): DesktopTaskRecord;
|
|
15
|
+
observe(target: DesktopThreadIdentity): Promise<DesktopSnapshot>;
|
|
16
|
+
applySnapshot(task: DesktopTaskRecord, snapshot: DesktopSnapshot, recover?: boolean): void;
|
|
17
|
+
deliverPending(id: string, notificationId?: string): Promise<void>;
|
|
18
|
+
now(): Date;
|
|
19
|
+
expire(task: DesktopTaskRecord): void;
|
|
20
|
+
}
|
|
21
|
+
export declare const desktopObservationStopped: typeof backendObservationStopped;
|
|
22
|
+
export declare const desktopLive: (task: DesktopTaskRecord) => boolean;
|
|
23
|
+
export declare function desktopUnresolvedSend(task: DesktopTaskRecord): boolean;
|
|
24
|
+
export declare function desktopNotificationObsolete(task: DesktopTaskRecord, notification: DesktopNotification): boolean;
|
|
25
|
+
export declare function suppressDesktopNotifications(task: DesktopTaskRecord, code: string, predicate?: (_notification: DesktopNotification) => boolean): void;
|
|
26
|
+
export declare function createDesktopTaskRecovery(host: DesktopRecoveryHost): {
|
|
27
|
+
close(id: string, input: DesktopRecoveryInput & {
|
|
28
|
+
reason?: string;
|
|
29
|
+
}): DesktopTaskRecord;
|
|
30
|
+
unwatch(id: string, input: DesktopRecoveryInput): DesktopTaskRecord;
|
|
31
|
+
recover(id: string, input: DesktopRecoveryInput): Promise<DesktopTaskRecord>;
|
|
32
|
+
renew(id: string, input: DesktopRecoveryInput & {
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
}): Promise<DesktopTaskRecord>;
|
|
35
|
+
retryCallback(id: string, input: DesktopRecoveryInput & {
|
|
36
|
+
notificationId?: string;
|
|
37
|
+
}): Promise<DesktopTaskRecord>;
|
|
38
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { backendObservationStopped, backendRenewalDeadline, prepareBackendCallbackRetry } from "./backend-task-recovery.js";
|
|
2
|
+
import { clearDesktopInteractionAttention } from "./desktop-task-interactions.js";
|
|
3
|
+
import { desktopInteractions } from "./desktop-request-interactions.js";
|
|
4
|
+
export class DesktopTaskError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
dispatchState;
|
|
7
|
+
constructor(code, message, dispatchState) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.dispatchState = dispatchState;
|
|
11
|
+
this.name = "DesktopTaskError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const desktopObservationStopped = backendObservationStopped;
|
|
15
|
+
export const desktopLive = (task) => task.status === "awaiting_acceptance" || task.status === "watching";
|
|
16
|
+
export function desktopUnresolvedSend(task) {
|
|
17
|
+
return task.kind === "send" && !task.closed_at && !task.native_turn_id &&
|
|
18
|
+
Boolean(task.send_intent && ["reserved", "uncertain"].includes(task.send_intent.state));
|
|
19
|
+
}
|
|
20
|
+
function owner(task, input) {
|
|
21
|
+
if (!input.controllerSession?.trim() || task.controller_session !== input.controllerSession) {
|
|
22
|
+
throw new DesktopTaskError("desktop_controller_mismatch", "Only the owning controller can manage this Desktop task");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function open(task) {
|
|
26
|
+
if (task.closed_at)
|
|
27
|
+
throw new DesktopTaskError("desktop_task_closed", "This Desktop task has been closed");
|
|
28
|
+
}
|
|
29
|
+
export function desktopNotificationObsolete(task, notification) {
|
|
30
|
+
const event = notification.envelope.event;
|
|
31
|
+
if (event.type === "desktop_watch.timed_out") {
|
|
32
|
+
const expected = `${task.id}:timed_out${task.renewal_count ? `:${task.renewal_count}` : ""}`;
|
|
33
|
+
return task.status !== "timed_out" || notification.id !== expected;
|
|
34
|
+
}
|
|
35
|
+
if (event.requires_response)
|
|
36
|
+
return !(task.pending_interactions ?? task.pending_async_interactions ?? [])
|
|
37
|
+
.some(interaction => interaction.id === event.metadata?.interaction_id && interaction.turnId === task.native_turn_id);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
export function suppressDesktopNotifications(task, code, predicate = (_notification) => true) {
|
|
41
|
+
for (const notification of task.notifications)
|
|
42
|
+
if (predicate(notification) && ["ready", "retry_wait", "failed"].includes(notification.status)) {
|
|
43
|
+
notification.status = "failed";
|
|
44
|
+
notification.outcome = { disposition: "permanent_failure", error_code: code };
|
|
45
|
+
delete notification.retry_at;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function exactTurn(task, snapshot) {
|
|
49
|
+
if (snapshot.threadId !== task.target.threadId || !snapshot.ownerClientId || !Number.isSafeInteger(snapshot.revision) || snapshot.revision < 0) {
|
|
50
|
+
throw new DesktopTaskError("desktop_identity_mismatch", "Desktop observation did not match the original thread");
|
|
51
|
+
}
|
|
52
|
+
const matching = snapshot.turns.filter(turn => turn.turnId === task.native_turn_id);
|
|
53
|
+
return matching.length === 1 ? matching[0] : undefined;
|
|
54
|
+
}
|
|
55
|
+
function fresh(task, initial, input) {
|
|
56
|
+
owner(task, input);
|
|
57
|
+
open(task);
|
|
58
|
+
if (task.revision !== initial.revision)
|
|
59
|
+
throw new DesktopTaskError("desktop_task_changed", "Desktop task changed during recovery; refresh Status");
|
|
60
|
+
}
|
|
61
|
+
function timeoutMs(value) {
|
|
62
|
+
const timeout = value ?? 3_600_000;
|
|
63
|
+
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 7 * 24 * 3_600_000)
|
|
64
|
+
throw new DesktopTaskError("invalid_argument", "Desktop Watch timeout must be between 1 millisecond and 7 days");
|
|
65
|
+
return timeout;
|
|
66
|
+
}
|
|
67
|
+
function retryNotification(task, id) {
|
|
68
|
+
const eligible = task.notifications.filter(notification => (!id || notification.id === id) &&
|
|
69
|
+
["failed", "retry_wait"].includes(notification.status) && notification.outcome?.disposition === "retryable_failure");
|
|
70
|
+
if (eligible.length !== 1)
|
|
71
|
+
throw new DesktopTaskError("desktop_callback_not_retryable", "Select one persisted callback with a retryable failure");
|
|
72
|
+
return eligible[0];
|
|
73
|
+
}
|
|
74
|
+
export function createDesktopTaskRecovery(host) {
|
|
75
|
+
const initial = (id, input) => { const task = host.status(id); owner(task, input); open(task); return task; };
|
|
76
|
+
return {
|
|
77
|
+
close(id, input) {
|
|
78
|
+
const task = host.status(id);
|
|
79
|
+
owner(task, input);
|
|
80
|
+
if (task.kind !== "send")
|
|
81
|
+
throw new DesktopTaskError("desktop_close_requires_send", "Observer Watches must use Unwatch");
|
|
82
|
+
if (input.reason !== undefined && !input.reason.trim())
|
|
83
|
+
throw new DesktopTaskError("invalid_argument", "Close reason must be non-empty");
|
|
84
|
+
return host.update(id, current => {
|
|
85
|
+
owner(current, input);
|
|
86
|
+
if (current.closed_at)
|
|
87
|
+
return;
|
|
88
|
+
current.closed_at = host.now().toISOString();
|
|
89
|
+
if (input.reason !== undefined)
|
|
90
|
+
current.close_reason = input.reason;
|
|
91
|
+
clearDesktopInteractionAttention(current, "desktop_task_closed");
|
|
92
|
+
suppressDesktopNotifications(current, "desktop_task_closed");
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
unwatch(id, input) {
|
|
96
|
+
owner(host.status(id), input);
|
|
97
|
+
return host.update(id, task => {
|
|
98
|
+
owner(task, input);
|
|
99
|
+
task.unwatched_at ??= host.now().toISOString();
|
|
100
|
+
clearDesktopInteractionAttention(task, "desktop_watch_unwatched");
|
|
101
|
+
suppressDesktopNotifications(task, "desktop_watch_unwatched");
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
async recover(id, input) {
|
|
105
|
+
const task = initial(id, input);
|
|
106
|
+
const snapshot = await host.observe(task.target);
|
|
107
|
+
return host.update(id, current => {
|
|
108
|
+
fresh(current, task, input);
|
|
109
|
+
host.applySnapshot(current, snapshot, true);
|
|
110
|
+
host.expire(current);
|
|
111
|
+
current.recovered_at = host.now().toISOString();
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
async renew(id, input) {
|
|
115
|
+
const task = initial(id, input);
|
|
116
|
+
const timeout = timeoutMs(input.timeoutMs);
|
|
117
|
+
const snapshot = await host.observe(task.target);
|
|
118
|
+
return host.update(id, current => {
|
|
119
|
+
fresh(current, task, input);
|
|
120
|
+
host.applySnapshot(current, snapshot, true);
|
|
121
|
+
const turn = exactTurn(current, snapshot);
|
|
122
|
+
if (["completed", "failed", "interrupted"].includes(current.status))
|
|
123
|
+
return;
|
|
124
|
+
if (!turn || turn.status !== "inProgress" || snapshot.runtimeStatus !== "active") {
|
|
125
|
+
throw new DesktopTaskError("desktop_exact_task_not_active", "Renew needs fresh active evidence for the original Desktop task");
|
|
126
|
+
}
|
|
127
|
+
current.deadline_at = backendRenewalDeadline(current, host.now(), timeout);
|
|
128
|
+
current.renewed_at = host.now().toISOString();
|
|
129
|
+
current.renewal_count = (current.renewal_count ?? 0) + 1;
|
|
130
|
+
current.status = "watching";
|
|
131
|
+
delete current.unwatched_at;
|
|
132
|
+
suppressDesktopNotifications(current, "desktop_timeout_superseded", notification => notification.envelope.event.type === "desktop_watch.timed_out");
|
|
133
|
+
host.applySnapshot(current, snapshot);
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
async retryCallback(id, input) {
|
|
137
|
+
const task = initial(id, input);
|
|
138
|
+
if (desktopObservationStopped(task))
|
|
139
|
+
throw new DesktopTaskError("desktop_watch_unwatched", "Renew observation before retrying its callback");
|
|
140
|
+
const notification = retryNotification(task, input.notificationId);
|
|
141
|
+
const interactionId = notification.envelope.event.metadata?.interaction_id;
|
|
142
|
+
const snapshot = notification.envelope.event.requires_response ? await host.observe(task.target) : undefined;
|
|
143
|
+
let obsolete = false;
|
|
144
|
+
host.update(id, current => {
|
|
145
|
+
fresh(current, task, input);
|
|
146
|
+
if (desktopObservationStopped(current))
|
|
147
|
+
throw new DesktopTaskError("desktop_watch_unwatched", "This Desktop Watch was stopped");
|
|
148
|
+
const currentNotification = retryNotification(current, notification.id);
|
|
149
|
+
if (snapshot) {
|
|
150
|
+
const turn = exactTurn(current, snapshot);
|
|
151
|
+
if (!turn || turn.status !== "inProgress" || !turn.itemsComplete || !desktopInteractions(snapshot, current.native_turn_id)
|
|
152
|
+
.some(interaction => interaction.id === interactionId && interaction.threadId === current.target.threadId && interaction.turnId === current.native_turn_id)) {
|
|
153
|
+
currentNotification.status = "failed";
|
|
154
|
+
currentNotification.outcome = { disposition: "permanent_failure", error_code: "desktop_interaction_not_pending" };
|
|
155
|
+
delete currentNotification.retry_at;
|
|
156
|
+
obsolete = true;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (snapshot)
|
|
161
|
+
host.applySnapshot(current, snapshot, true);
|
|
162
|
+
prepareBackendCallbackRetry(current, { ...input, notificationId: currentNotification.id }, host.now());
|
|
163
|
+
});
|
|
164
|
+
if (obsolete)
|
|
165
|
+
throw new DesktopTaskError("desktop_interaction_not_pending", "The original Desktop interaction is no longer confirmed pending");
|
|
166
|
+
await host.deliverPending(id, notification.id);
|
|
167
|
+
return host.status(id);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=desktop-task-recovery.js.map
|