@scotthuang/agent-knock-knock 0.14.4 → 0.14.5

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +1 -0
  3. package/dist/src/claude-native-public-projection.d.ts +2 -2
  4. package/dist/src/cli-core.js +1 -47
  5. package/dist/src/cli-core.js.map +1 -1
  6. package/dist/src/cli-runtime-context.d.ts +3 -0
  7. package/dist/src/cli-runtime-context.js.map +1 -1
  8. package/dist/src/cli-usage.d.ts +1 -0
  9. package/dist/src/cli-usage.js +51 -0
  10. package/dist/src/cli-usage.js.map +1 -0
  11. package/dist/src/desktop-cli-adapter.js +9 -6
  12. package/dist/src/desktop-cli-adapter.js.map +1 -1
  13. package/dist/src/desktop-cli-controls.js +9 -0
  14. package/dist/src/desktop-cli-controls.js.map +1 -1
  15. package/dist/src/desktop-compatibility.d.ts +17 -0
  16. package/dist/src/desktop-compatibility.js +15 -0
  17. package/dist/src/desktop-compatibility.js.map +1 -0
  18. package/dist/src/desktop-creation-cli.d.ts +4 -0
  19. package/dist/src/desktop-creation-cli.js +167 -0
  20. package/dist/src/desktop-creation-cli.js.map +1 -0
  21. package/dist/src/desktop-creation-native.d.ts +70 -0
  22. package/dist/src/desktop-creation-native.js +249 -0
  23. package/dist/src/desktop-creation-native.js.map +1 -0
  24. package/dist/src/desktop-creation-rpc.d.ts +25 -0
  25. package/dist/src/desktop-creation-rpc.js +198 -0
  26. package/dist/src/desktop-creation-rpc.js.map +1 -0
  27. package/dist/src/desktop-creation-semantic.d.ts +13 -0
  28. package/dist/src/desktop-creation-semantic.js +89 -0
  29. package/dist/src/desktop-creation-semantic.js.map +1 -0
  30. package/dist/src/desktop-creation-service.d.ts +27 -0
  31. package/dist/src/desktop-creation-service.js +268 -0
  32. package/dist/src/desktop-creation-service.js.map +1 -0
  33. package/dist/src/desktop-creation-store.d.ts +42 -0
  34. package/dist/src/desktop-creation-store.js +95 -0
  35. package/dist/src/desktop-creation-store.js.map +1 -0
  36. package/dist/src/desktop-interaction-projection.d.ts +2 -1
  37. package/dist/src/desktop-interaction-projection.js +8 -5
  38. package/dist/src/desktop-interaction-projection.js.map +1 -1
  39. package/dist/src/desktop-ipc-client.d.ts +1 -4
  40. package/dist/src/desktop-ipc-client.js +6 -6
  41. package/dist/src/desktop-ipc-client.js.map +1 -1
  42. package/dist/src/desktop-list.d.ts +1 -0
  43. package/dist/src/desktop-list.js +5 -4
  44. package/dist/src/desktop-list.js.map +1 -1
  45. package/dist/src/desktop-public-projection.d.ts +3 -2
  46. package/dist/src/desktop-public-projection.js +23 -18
  47. package/dist/src/desktop-public-projection.js.map +1 -1
  48. package/dist/src/desktop-runtime.js +4 -2
  49. package/dist/src/desktop-runtime.js.map +1 -1
  50. package/dist/src/desktop-semantic.js +2 -0
  51. package/dist/src/desktop-semantic.js.map +1 -1
  52. package/dist/src/desktop-state-store.js +5 -2
  53. package/dist/src/desktop-state-store.js.map +1 -1
  54. package/dist/src/desktop-task-interactions.d.ts +1 -1
  55. package/dist/src/desktop-task-interactions.js +5 -5
  56. package/dist/src/desktop-task-interactions.js.map +1 -1
  57. package/dist/src/desktop-task-service.d.ts +3 -0
  58. package/dist/src/desktop-task-service.js +6 -1
  59. package/dist/src/desktop-task-service.js.map +1 -1
  60. package/dist/src/host-profile-runtime.js +1 -1
  61. package/dist/src/host-profile-runtime.js.map +1 -1
  62. package/dist/src/local-codex-cli-adapter.js +2 -1
  63. package/dist/src/local-codex-cli-adapter.js.map +1 -1
  64. package/dist/src/semantic-tool-descriptions.d.ts +2 -1
  65. package/dist/src/semantic-tool-descriptions.js +8 -1
  66. package/dist/src/semantic-tool-descriptions.js.map +1 -1
  67. package/dist/src/semantic-tool-list-projection.js +1 -1
  68. package/dist/src/semantic-tool-list-projection.js.map +1 -1
  69. package/dist/src/semantic-tool-runtime.js +2 -0
  70. package/dist/src/semantic-tool-runtime.js.map +1 -1
  71. package/dist/src/semantic-tool-schemas.d.ts +52 -1
  72. package/dist/src/semantic-tool-schemas.js +16 -0
  73. package/dist/src/semantic-tool-schemas.js.map +1 -1
  74. package/dist/src/terminal-monitor-decision-policy.d.ts +1 -1
  75. package/docs/codex-desktop-compatibility.md +58 -10
  76. package/docs/desktop-create.md +144 -0
  77. package/openclaw.plugin.json +4 -0
  78. package/package.json +1 -1
  79. package/templates/openclaw-skills/agent-knock-knock/SKILL.md +11 -5
@@ -0,0 +1,25 @@
1
+ import { spawn } from "node:child_process";
2
+ export declare class DesktopCreationRpcError extends Error {
3
+ readonly code: string;
4
+ readonly requestDispatched: boolean;
5
+ readonly method?: string | undefined;
6
+ readonly requestId?: number | undefined;
7
+ readonly rpcCode?: number | undefined;
8
+ constructor(code: string, message: string, requestDispatched: boolean, method?: string | undefined, requestId?: number | undefined, rpcCode?: number | undefined);
9
+ }
10
+ export interface DesktopCreationRpc {
11
+ request(method: string, params: Record<string, unknown>, options?: {
12
+ beforeDispatch?: () => void | Promise<void>;
13
+ }): Promise<unknown>;
14
+ notifyInitialized(): void;
15
+ close(): Promise<void>;
16
+ }
17
+ export interface DesktopCreationRpcOptions {
18
+ binaryPath: string;
19
+ codexHome: string;
20
+ timeoutMs?: number;
21
+ environment?: NodeJS.ProcessEnv;
22
+ spawnProcess?: typeof spawn;
23
+ }
24
+ /** A dedicated, short-lived bundled process. Never attaches to or kills an existing server. */
25
+ export declare function createDesktopCreationRpc(options: DesktopCreationRpcOptions): DesktopCreationRpc;
@@ -0,0 +1,198 @@
1
+ import { spawn } from "node:child_process";
2
+ export class DesktopCreationRpcError extends Error {
3
+ code;
4
+ requestDispatched;
5
+ method;
6
+ requestId;
7
+ rpcCode;
8
+ constructor(code, message, requestDispatched, method, requestId, rpcCode) {
9
+ super(message);
10
+ this.code = code;
11
+ this.requestDispatched = requestDispatched;
12
+ this.method = method;
13
+ this.requestId = requestId;
14
+ this.rpcCode = rpcCode;
15
+ this.name = "DesktopCreationRpcError";
16
+ }
17
+ }
18
+ const METHODS = new Set(["initialize", "project/list", "project/read", "thread/start", "thread/name/set", "thread/read"]);
19
+ const MAX_LINE_BYTES = 4 * 1024 * 1024;
20
+ /** A dedicated, short-lived bundled process. Never attaches to or kills an existing server. */
21
+ export function createDesktopCreationRpc(options) {
22
+ return new DesktopCreationProcess(options);
23
+ }
24
+ class DesktopCreationProcess {
25
+ child;
26
+ timeoutMs;
27
+ pending = new Map();
28
+ sequence = 0;
29
+ buffer = Buffer.alloc(0);
30
+ ended = false;
31
+ closed = false;
32
+ processExited = false;
33
+ spawnFailed = false;
34
+ closePromise;
35
+ resolveExit;
36
+ exited = new Promise(resolve => { this.resolveExit = resolve; });
37
+ constructor(options) {
38
+ this.timeoutMs = options.timeoutMs ?? 30_000;
39
+ if (!Number.isSafeInteger(this.timeoutMs) || this.timeoutMs < 1 || this.timeoutMs > 60_000) {
40
+ throw new DesktopCreationRpcError("invalid_argument", "Invalid Desktop creation request timeout", false);
41
+ }
42
+ this.child = (options.spawnProcess ?? spawn)(options.binaryPath, ["-c", "features.code_mode_host=true", "app-server", "--listen", "stdio://"], {
43
+ cwd: options.codexHome, env: { ...(options.environment ?? process.env), CODEX_HOME: options.codexHome },
44
+ stdio: ["pipe", "pipe", "pipe"]
45
+ });
46
+ this.child.on("error", () => {
47
+ this.fail("process_failed", "Desktop creation process could not run");
48
+ if (this.child.pid === undefined) {
49
+ this.spawnFailed = true;
50
+ this.resolveExit();
51
+ }
52
+ });
53
+ this.child.on("exit", () => { this.processExited = true; this.fail("closed", "Desktop creation process exited"); this.resolveExit(); });
54
+ this.child.stdin.on("error", () => this.fail("write_failed", "Desktop creation input transport failed"));
55
+ this.child.stdout.on("error", () => this.fail("read_failed", "Desktop creation output transport failed"));
56
+ this.child.stderr.resume(); // Diagnostics may contain local paths; never project raw stderr into receipts.
57
+ this.child.stdout.on("data", (chunk) => this.receive(chunk));
58
+ }
59
+ fail(code, message) {
60
+ this.ended = true;
61
+ for (const [id, item] of this.pending) {
62
+ clearTimeout(item.timer);
63
+ item.reject(new DesktopCreationRpcError(code, message, item.dispatched, item.method, id));
64
+ }
65
+ this.pending.clear();
66
+ }
67
+ receive(chunk) {
68
+ if (this.ended)
69
+ return;
70
+ this.buffer = Buffer.concat([this.buffer, chunk]);
71
+ while (this.buffer.includes(10)) {
72
+ const index = this.buffer.indexOf(10), line = this.buffer.subarray(0, index);
73
+ this.buffer = this.buffer.subarray(index + 1);
74
+ if (line.length > MAX_LINE_BYTES) {
75
+ this.fail("invalid_response", "Desktop creation response exceeds its limit");
76
+ return;
77
+ }
78
+ let message;
79
+ try {
80
+ const value = JSON.parse(line.toString("utf8"));
81
+ if (!value || typeof value !== "object" || Array.isArray(value))
82
+ throw new Error();
83
+ message = value;
84
+ }
85
+ catch {
86
+ this.fail("invalid_response", "Desktop creation returned malformed JSON");
87
+ return;
88
+ }
89
+ if (typeof message.method === "string") {
90
+ // Creation never starts a turn or authorizes server tool/approval requests.
91
+ if (message.id !== undefined) {
92
+ this.fail("unexpected_request", "Desktop creation received an unexpected server request");
93
+ return;
94
+ }
95
+ continue;
96
+ }
97
+ this.settle(message);
98
+ }
99
+ if (this.buffer.length > MAX_LINE_BYTES)
100
+ this.fail("invalid_response", "Desktop creation response exceeds its limit");
101
+ }
102
+ settle(message) {
103
+ const item = typeof message.id === "number" ? this.pending.get(message.id) : undefined;
104
+ if (!item)
105
+ return; // A late response never revives or repeats a timed-out mutation.
106
+ const requestId = message.id;
107
+ this.pending.delete(requestId);
108
+ clearTimeout(item.timer);
109
+ if (message.error && typeof message.error === "object") {
110
+ const rpcCode = message.error.code;
111
+ item.reject(new DesktopCreationRpcError("rpc_rejected", `Desktop ${item.method} returned an error`, item.dispatched, item.method, requestId, typeof rpcCode === "number" ? rpcCode : undefined));
112
+ }
113
+ else if (!Object.hasOwn(message, "result")) {
114
+ item.reject(new DesktopCreationRpcError("invalid_response", "Desktop creation response has no result", item.dispatched, item.method, requestId));
115
+ }
116
+ else
117
+ item.resolve(message.result);
118
+ }
119
+ async request(method, params, options) {
120
+ if (!METHODS.has(method))
121
+ throw new DesktopCreationRpcError("invalid_argument", "Desktop creation method is not permitted", false, method);
122
+ if (this.ended || this.closed)
123
+ throw new DesktopCreationRpcError("closed", "Desktop creation connection is closed", false, method);
124
+ // Serialize before the durable barrier, so malformed input cannot leave a false dispatch record.
125
+ const id = ++this.sequence, payload = `${JSON.stringify({ id, method, params })}\n`;
126
+ try {
127
+ await options?.beforeDispatch?.();
128
+ }
129
+ catch {
130
+ throw new DesktopCreationRpcError("dispatch_barrier_failed", "Desktop creation durable dispatch barrier failed", false, method, id);
131
+ }
132
+ if (this.ended || this.closed || this.child.stdin.destroyed || !this.child.stdin.writable) {
133
+ throw new DesktopCreationRpcError("closed", "Desktop creation connection closed before dispatch", false, method, id);
134
+ }
135
+ return new Promise((resolve, reject) => {
136
+ const item = { method, dispatched: false, resolve, reject,
137
+ timer: setTimeout(() => {
138
+ this.pending.delete(id);
139
+ reject(new DesktopCreationRpcError("timeout", "Desktop creation request timed out; no automatic retry", item.dispatched, method, id));
140
+ }, this.timeoutMs) };
141
+ this.pending.set(id, item);
142
+ try {
143
+ // Once write is called, a throwing/erroring pipe may still have submitted bytes.
144
+ item.dispatched = true;
145
+ this.child.stdin.write(payload);
146
+ }
147
+ catch {
148
+ this.pending.delete(id);
149
+ clearTimeout(item.timer);
150
+ reject(new DesktopCreationRpcError("write_failed", "Desktop creation dispatch outcome is unknown", true, method, id));
151
+ }
152
+ });
153
+ }
154
+ notifyInitialized() {
155
+ if (this.ended || this.closed)
156
+ throw new DesktopCreationRpcError("closed", "Desktop creation connection is closed", false);
157
+ this.child.stdin.write(`${JSON.stringify({ method: "initialized" })}\n`);
158
+ }
159
+ close() {
160
+ if (this.closePromise)
161
+ return this.closePromise;
162
+ this.closed = true;
163
+ this.fail("closed", "Desktop creation connection closed");
164
+ this.closePromise = this.closeProcess();
165
+ return this.closePromise;
166
+ }
167
+ async closeProcess() {
168
+ if (this.processExited || this.spawnFailed || this.child.exitCode !== null || this.child.signalCode !== null)
169
+ return;
170
+ const signal = (name) => {
171
+ if (this.processExited || this.spawnFailed)
172
+ return;
173
+ try {
174
+ this.child.kill(name);
175
+ }
176
+ catch { /* Exit must still be independently confirmed below. */ }
177
+ };
178
+ const terminate = setTimeout(() => signal("SIGTERM"), 1000);
179
+ const kill = setTimeout(() => signal("SIGKILL"), 3000);
180
+ let bound;
181
+ const deadline = new Promise((_resolve, reject) => {
182
+ bound = setTimeout(() => reject(new DesktopCreationRpcError("process_exit_unconfirmed", "Desktop creation process exit could not be confirmed; do not hand off its thread", false)), 4000);
183
+ });
184
+ try {
185
+ try {
186
+ this.child.stdin.end();
187
+ }
188
+ catch { /* Retain bounded signal cleanup if the input pipe fails. */ }
189
+ await Promise.race([this.exited, deadline]);
190
+ }
191
+ finally {
192
+ clearTimeout(terminate);
193
+ clearTimeout(kill);
194
+ clearTimeout(bound);
195
+ }
196
+ }
197
+ }
198
+ //# sourceMappingURL=desktop-creation-rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-creation-rpc.js","sourceRoot":"","sources":["../../src/desktop-creation-rpc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SemanticToolCatalogEntry } from "./semantic-tool-catalog.js";
2
+ /** Creation identity belongs to a controller invocation, never a project/cwd guess. */
3
+ export declare function desktopCreationOperationId(params: Record<string, unknown>, context: {
4
+ sessionKey?: unknown;
5
+ sessionId?: unknown;
6
+ }, toolCallId: unknown): string;
7
+ export declare function desktopCreationToolArgs(params: Record<string, unknown>, config: Record<string, unknown>, context: {
8
+ sessionKey?: unknown;
9
+ sessionId?: unknown;
10
+ }, toolCallId: unknown): string[];
11
+ export declare function desktopCreationResultIsError(result: unknown): boolean;
12
+ /** Keep Desktop resource creation separate from the terminal lifecycle catalog. */
13
+ export declare function createDesktopCreationSemanticTool(api: object & Record<string, unknown>): SemanticToolCatalogEntry;
@@ -0,0 +1,89 @@
1
+ import { createHash } from "node:crypto";
2
+ import { requiredString, pushOptional, numberString } from "./semantic-tool-arguments.js";
3
+ import { resolvePluginStoreDir } from "./semantic-tool-command-helpers.js";
4
+ import { isRecord } from "./value-guards.js";
5
+ import { semanticToolDescriptions } from "./semantic-tool-descriptions.js";
6
+ import { desktopCreateParameters } from "./semantic-tool-schemas.js";
7
+ import { runHostAwareCli, withHostBridgeInvocationSignal } from "./semantic-tool-relay.js";
8
+ import { modelFacingErrorMessage, toolResult } from "./semantic-tool-presentation.js";
9
+ const OPERATION_PATTERN = /^desktop-create:[0-9a-f]{64}$/u;
10
+ const CREATION_FIELDS = ["project", "cwd", "title", "request", "agentHardTimeoutMinutes"];
11
+ /** Creation identity belongs to a controller invocation, never a project/cwd guess. */
12
+ export function desktopCreationOperationId(params, context, toolCallId) {
13
+ const sessionKey = requiredString(context.sessionKey, "controller session");
14
+ if (Object.hasOwn(params, "operation_id")) {
15
+ const id = requiredString(params.operation_id, "operation_id");
16
+ if (!OPERATION_PATTERN.test(id))
17
+ throw new Error("Invalid Desktop creation operation_id");
18
+ return id;
19
+ }
20
+ const callId = requiredString(toolCallId, "stable Desktop creation tool call identity");
21
+ const sessionId = typeof context.sessionId === "string" && context.sessionId.trim() ? context.sessionId : null;
22
+ return "desktop-create:" + createHash("sha256").update(JSON.stringify([
23
+ sessionKey, sessionId, "agent_knock_knock_desktop_create", callId
24
+ ])).digest("hex");
25
+ }
26
+ export function desktopCreationToolArgs(params, config, context, toolCallId) {
27
+ if (Object.keys(params).some(key => ![...CREATION_FIELDS, "operation_id"].includes(key))) {
28
+ throw new Error("Desktop creation accepts only project, cwd, title, request and monitor lifetime, or operation_id to continue");
29
+ }
30
+ const resume = Object.hasOwn(params, "operation_id");
31
+ if (resume && CREATION_FIELDS.some(key => Object.hasOwn(params, key))) {
32
+ throw new Error("Continuing Desktop creation accepts only operation_id; its original payload cannot change");
33
+ }
34
+ const args = ["desktop-create", "--operation-id", desktopCreationOperationId(params, context, toolCallId),
35
+ "--openclaw-session", requiredString(context.sessionKey, "controller session")];
36
+ if (resume)
37
+ args.push("--resume-creation");
38
+ else {
39
+ for (const [key, flag] of [["project", "--project"], ["cwd", "--cwd"], ["title", "--title"], ["request", "--message"]]) {
40
+ if (Object.hasOwn(params, key))
41
+ args.push(flag, requiredString(params[key], key));
42
+ }
43
+ if (Object.hasOwn(params, "agentHardTimeoutMinutes") && (typeof params.agentHardTimeoutMinutes !== "number"
44
+ || !Number.isFinite(params.agentHardTimeoutMinutes) || params.agentHardTimeoutMinutes <= 0)) {
45
+ throw new Error("agentHardTimeoutMinutes must be a positive finite number");
46
+ }
47
+ pushOptional(args, "--agent-hard-timeout-minutes", numberString(params.agentHardTimeoutMinutes) ?? numberString(config.agentHardTimeoutMinutes));
48
+ }
49
+ pushOptional(args, "--store-dir", resolvePluginStoreDir(config));
50
+ pushOptional(args, "--codex-home", config.codexHome);
51
+ pushOptional(args, "--openclaw-bin", config.openclawBin);
52
+ return args;
53
+ }
54
+ export function desktopCreationResultIsError(result) {
55
+ return !isRecord(result) || result.source !== "codex_desktop"
56
+ || !["ready", "opening"].includes(String(result.status))
57
+ || (Object.hasOwn(result, "request_state") && result.request_state !== "accepted");
58
+ }
59
+ /** Keep Desktop resource creation separate from the terminal lifecycle catalog. */
60
+ export function createDesktopCreationSemanticTool(api) {
61
+ return {
62
+ label: "AKK Desktop Create",
63
+ name: "agent_knock_knock_desktop_create",
64
+ description: semanticToolDescriptions.desktop_create,
65
+ inputSchema: desktopCreateParameters,
66
+ async execute(toolContext, toolCallId, rawParams, signal) {
67
+ return withHostBridgeInvocationSignal(signal, async () => {
68
+ if (!isRecord(rawParams))
69
+ throw new Error("Desktop creation parameters must be an object");
70
+ const params = rawParams;
71
+ const args = desktopCreationToolArgs(params, isRecord(api.pluginConfig) ? api.pluginConfig : {}, toolContext, toolCallId);
72
+ const operationId = desktopCreationOperationId(params, toolContext, toolCallId);
73
+ try {
74
+ const result = await runHostAwareCli(api, args, { allowNonzeroJson: true });
75
+ return toolResult(result, { normalizeTurnIdentity: false, forceError: desktopCreationResultIsError(result) });
76
+ }
77
+ catch (error) {
78
+ // The child may already have persisted a thread or sent the first task.
79
+ // Keep its exact identity even when the relay loses the receipt.
80
+ return toolResult({ source: "codex_desktop", operation_id: operationId, status: "uncertain", created: null,
81
+ error_code: "desktop_creation_receipt_unavailable", reason: modelFacingErrorMessage(error),
82
+ next_action: { tool: "agent_knock_knock_desktop_create", input: { operation_id: operationId } },
83
+ warning: "Continue this exact operation only; do not create a replacement or resend the task." }, { normalizeTurnIdentity: false, forceError: true });
84
+ }
85
+ });
86
+ }
87
+ };
88
+ }
89
+ //# sourceMappingURL=desktop-creation-semantic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-creation-semantic.js","sourceRoot":"","sources":["../../src/desktop-creation-semantic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type DesktopCreationNative } from "./desktop-creation-native.js";
2
+ import type { DesktopCreationStore, DesktopCreationRecord, DesktopCreationRequest } from "./desktop-creation-store.js";
3
+ import type { DesktopSnapshot, DesktopThreadIdentity } from "./desktop-types.js";
4
+ import type { DesktopTaskService } from "./desktop-task-service.js";
5
+ export interface DesktopCreationServiceDependencies {
6
+ store: DesktopCreationStore;
7
+ connect(codexHome: string): Promise<DesktopCreationNative>;
8
+ defaultDirectory(operationId: string): string;
9
+ open(threadId: string): Promise<void>;
10
+ observe(target: DesktopThreadIdentity): Promise<DesktopSnapshot>;
11
+ tasks: Pick<DesktopTaskService, "send" | "status">;
12
+ now?(): Date;
13
+ sleep?(ms: number): Promise<void>;
14
+ ownerAttempts?: number;
15
+ processId?: number;
16
+ isProcessAlive?(pid: number): boolean;
17
+ }
18
+ export interface DesktopCreationRun {
19
+ operationId: string;
20
+ controllerSession: string;
21
+ input?: DesktopCreationRequest;
22
+ resume?: boolean;
23
+ }
24
+ /** Each invocation owns a durable claim, but no file lock spans native I/O. */
25
+ export declare function createDesktopCreationService(deps: DesktopCreationServiceDependencies): {
26
+ run: (input: DesktopCreationRun) => Promise<DesktopCreationRecord>;
27
+ };
@@ -0,0 +1,268 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { randomUUID } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import { createDesktopConversationId } from "./desktop-identity.js";
5
+ import { DesktopCreationNativeError } from "./desktop-creation-native.js";
6
+ function errorCode(error) {
7
+ const code = error?.code;
8
+ return typeof code === "string" && /^[a-zA-Z0-9_-]{1,80}$/u.test(code) ? code : "desktop_creation_failed";
9
+ }
10
+ function processAlive(pid) {
11
+ try {
12
+ process.kill(pid, 0);
13
+ return true;
14
+ }
15
+ catch (error) {
16
+ return error.code !== "ESRCH";
17
+ }
18
+ }
19
+ function identity(r) {
20
+ if (!r.threadId)
21
+ throw new Error("Desktop creation has no native thread");
22
+ return { codexHome: r.input.codexHome, hostId: "local", threadId: r.threadId };
23
+ }
24
+ const identityErrors = new Set(["creation_identity_mismatch", "thread_target_mismatch", "desktop_identity_mismatch", "process_exit_unconfirmed"]);
25
+ /** Each invocation owns a durable claim, but no file lock spans native I/O. */
26
+ export function createDesktopCreationService(deps) {
27
+ return { run: (input) => new DesktopCreationInvocation(deps, input).run() };
28
+ }
29
+ class DesktopCreationInvocation {
30
+ deps;
31
+ input;
32
+ token = randomUUID();
33
+ r;
34
+ constructor(deps, input) {
35
+ this.deps = deps;
36
+ this.input = input;
37
+ }
38
+ now() { return (this.deps.now?.() ?? new Date()).toISOString(); }
39
+ current() {
40
+ const record = this.deps.store.load(this.r.id);
41
+ if (!record)
42
+ throw new Error("Desktop creation operation not found");
43
+ return record;
44
+ }
45
+ claim() {
46
+ let claimed = false;
47
+ const input = this.input;
48
+ this.r = this.deps.store.update(input.operationId, old => {
49
+ if (old) {
50
+ if (old.controllerSession !== input.controllerSession || (!input.resume && !isDeepStrictEqual(old.input, input.input))
51
+ || (input.resume && input.input !== undefined && !isDeepStrictEqual(old.input, input.input))) {
52
+ throw new Error("Desktop creation operation belongs to different immutable input or controller");
53
+ }
54
+ if (["failed", "uncertain"].includes(old.status)
55
+ || old.worker && (this.deps.isProcessAlive ?? processAlive)(old.worker.pid))
56
+ return old;
57
+ }
58
+ else if (input.resume || !input.input) {
59
+ throw new Error("Desktop creation operation not found; recovery cannot create a new conversation");
60
+ }
61
+ const next = old ?? { schema: "agent-knock-knock/desktop-creation", version: 1, id: input.operationId,
62
+ controllerSession: input.controllerSession, input: input.input, status: "prepared", createdAt: this.now(), updatedAt: this.now() };
63
+ next.worker = { token: this.token, pid: this.deps.processId ?? process.pid, startedAt: this.now() };
64
+ next.updatedAt = this.now();
65
+ claimed = true;
66
+ return next;
67
+ });
68
+ return claimed;
69
+ }
70
+ update(change) {
71
+ return this.deps.store.update(this.r.id, latest => {
72
+ if (!latest || latest.worker?.token !== this.token)
73
+ throw new Error("Desktop creation invocation lost its reservation");
74
+ change(latest);
75
+ latest.updatedAt = this.now();
76
+ return latest;
77
+ });
78
+ }
79
+ recordError(error, status) {
80
+ return this.update(latest => {
81
+ if (latest.status === "uncertain")
82
+ return;
83
+ latest.errorCode = errorCode(error);
84
+ if (identityErrors.has(latest.errorCode))
85
+ latest.status = "uncertain";
86
+ else if (status)
87
+ latest.status = status;
88
+ });
89
+ }
90
+ async run() {
91
+ if (!this.claim())
92
+ return this.r;
93
+ try {
94
+ if (this.r.status === "prepared")
95
+ await this.create();
96
+ else if (this.r.status === "creating" && !this.r.threadId) {
97
+ this.update(latest => { latest.status = "uncertain"; latest.errorCode = "creation_receipt_missing"; });
98
+ }
99
+ else if (["creating", "created", "opening"].includes(this.r.status))
100
+ await this.verifyCreated();
101
+ this.r = this.current();
102
+ if (["created", "opening"].includes(this.r.status) && !this.r.errorCode)
103
+ await this.handoff();
104
+ this.r = this.current();
105
+ if (this.r.status === "ready" && this.r.input.request && !this.r.watchId) {
106
+ try {
107
+ this.update(latest => { latest.requestState = "uncertain"; });
108
+ const task = await this.deps.tasks.send({ target: identity(this.r), desktopId: createDesktopConversationId(identity(this.r)),
109
+ controllerSession: this.r.controllerSession, messageId: this.r.id, text: this.r.input.request,
110
+ timeoutMs: this.r.input.timeoutMs, callbackRoute: this.r.input.callbackRoute, requireEmptyThread: true });
111
+ this.update(latest => {
112
+ latest.watchId = task.id;
113
+ latest.requestState = task.send_intent?.state === "accepted" ? "accepted"
114
+ : task.send_intent?.state === "not_sent" ? "not_sent" : "uncertain";
115
+ delete latest.errorCode;
116
+ });
117
+ }
118
+ catch (error) {
119
+ this.update(latest => {
120
+ latest.errorCode = errorCode(error);
121
+ if (error?.dispatchState === "not_sent")
122
+ latest.requestState = "not_sent";
123
+ });
124
+ }
125
+ }
126
+ }
127
+ finally {
128
+ // If a process dies, its PID remains on disk and the next invocation can reconcile exactly this UUID.
129
+ this.update(latest => { delete latest.worker; });
130
+ }
131
+ return this.current();
132
+ }
133
+ async create() {
134
+ let native, dispatched = false, completed = false;
135
+ try {
136
+ native = await this.deps.connect(this.r.input.codexHome);
137
+ let cwd = this.r.input.cwd, projectId, projectName;
138
+ if (this.r.input.project) {
139
+ const selected = await native.resolveProject({ ...(/^[0-9a-f-]{36}$/iu.test(this.r.input.project)
140
+ ? { projectId: this.r.input.project } : { projectName: this.r.input.project }), ...(cwd ? { cwd } : {}) });
141
+ cwd = selected.cwd;
142
+ projectId = selected.project.id;
143
+ projectName = selected.project.name;
144
+ }
145
+ cwd ??= this.deps.defaultDirectory(this.r.id);
146
+ cwd = fs.realpathSync(cwd);
147
+ const receipt = await native.create({ cwd, projectId, title: this.r.input.title,
148
+ sandbox: "workspace-write", approvalPolicy: "on-request",
149
+ beforeDispatch: () => {
150
+ this.update(latest => {
151
+ if (latest.status !== "prepared")
152
+ throw new Error("Creation already reserved");
153
+ latest.status = "creating";
154
+ latest.cwd = cwd;
155
+ latest.projectId = projectId ?? null;
156
+ latest.projectName = projectName;
157
+ delete latest.errorCode;
158
+ dispatched = true;
159
+ });
160
+ },
161
+ onCreated: receipt => {
162
+ const latest = this.update(record => {
163
+ record.threadId = receipt.threadId;
164
+ record.nativeTitle = receipt.title;
165
+ try {
166
+ validateReceipt(record, receipt);
167
+ }
168
+ catch {
169
+ record.status = "uncertain";
170
+ record.errorCode = "creation_identity_mismatch";
171
+ }
172
+ });
173
+ if (latest.status === "uncertain")
174
+ throw new DesktopCreationNativeError("creation_identity_mismatch", "Created Desktop identity differs", "created", receipt.threadId);
175
+ }
176
+ });
177
+ this.update(latest => { validateReceipt(latest, receipt); latest.threadId = receipt.threadId; latest.nativeTitle = receipt.title; });
178
+ completed = true;
179
+ }
180
+ catch (error) {
181
+ const latest = this.current();
182
+ if (latest.status !== "uncertain")
183
+ this.update(record => {
184
+ const stage = error?.dispatchState;
185
+ record.errorCode = errorCode(error);
186
+ const threadId = error?.threadId;
187
+ if (!record.threadId && typeof threadId === "string" && /^[0-9a-f-]{36}$/iu.test(threadId))
188
+ record.threadId = threadId;
189
+ record.status = identityErrors.has(record.errorCode) ? "uncertain"
190
+ : record.threadId ? "creating" : dispatched && stage !== "not_sent" ? "uncertain" : "failed";
191
+ });
192
+ }
193
+ finally {
194
+ try {
195
+ await native?.close();
196
+ }
197
+ catch (error) {
198
+ completed = false;
199
+ this.recordError(error);
200
+ }
201
+ }
202
+ if (completed)
203
+ this.update(latest => { latest.status = "created"; delete latest.errorCode; });
204
+ }
205
+ async verifyCreated() {
206
+ let native, verified = false;
207
+ try {
208
+ native = await this.deps.connect(this.r.input.codexHome);
209
+ const receipt = await native.read(this.r.threadId);
210
+ validateReceipt(this.r, receipt);
211
+ this.update(latest => { latest.nativeTitle = receipt.title; });
212
+ verified = true;
213
+ }
214
+ catch (error) {
215
+ this.recordError(error);
216
+ }
217
+ finally {
218
+ try {
219
+ await native?.close();
220
+ }
221
+ catch (error) {
222
+ verified = false;
223
+ this.recordError(error);
224
+ }
225
+ }
226
+ if (verified)
227
+ this.update(latest => { latest.status = latest.openedAt ? "opening" : "created"; delete latest.errorCode; });
228
+ }
229
+ async handoff() {
230
+ this.r = this.current();
231
+ if (!this.r.openedAt) {
232
+ this.update(latest => { latest.openedAt = this.now(); latest.status = "opening"; });
233
+ try {
234
+ await this.deps.open(this.r.threadId);
235
+ }
236
+ catch (error) {
237
+ this.recordError(error);
238
+ return;
239
+ }
240
+ }
241
+ for (let attempt = 0; attempt < (this.deps.ownerAttempts ?? 3); attempt++) {
242
+ try {
243
+ const snapshot = await this.deps.observe(identity(this.r));
244
+ if (snapshot.threadId !== this.r.threadId || snapshot.cwd !== undefined && snapshot.cwd !== this.r.cwd) {
245
+ throw Object.assign(new Error("Desktop owner identity is not verified"), { code: "desktop_identity_mismatch" });
246
+ }
247
+ if (!snapshot.ownerClientId || !snapshot.cwd || !snapshot.tailKnown) {
248
+ throw Object.assign(new Error("Desktop owner is still loading"), { code: "desktop_owner_not_ready" });
249
+ }
250
+ this.update(latest => { latest.status = "ready"; delete latest.errorCode; });
251
+ return;
252
+ }
253
+ catch (error) {
254
+ const latest = this.recordError(error);
255
+ if (latest.status === "uncertain")
256
+ return;
257
+ }
258
+ if (attempt + 1 < (this.deps.ownerAttempts ?? 3))
259
+ await (this.deps.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms))))(300);
260
+ }
261
+ }
262
+ }
263
+ function validateReceipt(r, receipt) {
264
+ if (receipt.cwd !== r.cwd || receipt.projectId !== r.projectId || r.threadId && r.threadId !== receipt.threadId) {
265
+ throw new DesktopCreationNativeError("creation_identity_mismatch", "Created Desktop thread identity or project differs", "created", r.threadId);
266
+ }
267
+ }
268
+ //# sourceMappingURL=desktop-creation-service.js.map