@greppleai/grip 0.0.1 → 0.1.3

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 (197) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +13 -7
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +4 -2
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/embedded-assets.d.ts +3 -0
  7. package/dist/bun/embedded-assets.d.ts.map +1 -0
  8. package/dist/bun/embedded-assets.js +52 -0
  9. package/dist/bun/embedded-assets.js.map +1 -0
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/args.js +1 -0
  12. package/dist/cli/args.js.map +1 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +3 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +17 -2
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/remote-workspace-protocol/contract.d.ts +13 -4
  20. package/dist/core/remote-workspace-protocol/contract.d.ts.map +1 -1
  21. package/dist/core/remote-workspace-protocol/contract.js +17 -1
  22. package/dist/core/remote-workspace-protocol/contract.js.map +1 -1
  23. package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts +16 -0
  24. package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts.map +1 -0
  25. package/dist/core/remote-workspace-protocol/guest-shared-client.js +54 -0
  26. package/dist/core/remote-workspace-protocol/guest-shared-client.js.map +1 -0
  27. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts +23 -0
  28. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts.map +1 -0
  29. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js +112 -0
  30. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js.map +1 -0
  31. package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts +30 -0
  32. package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts.map +1 -0
  33. package/dist/core/remote-workspace-protocol/guest-shared-uplink.js +243 -0
  34. package/dist/core/remote-workspace-protocol/guest-shared-uplink.js.map +1 -0
  35. package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts +3 -0
  36. package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts.map +1 -0
  37. package/dist/core/remote-workspace-protocol/lima-owner-directory.js +31 -0
  38. package/dist/core/remote-workspace-protocol/lima-owner-directory.js.map +1 -0
  39. package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts +9 -0
  40. package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts.map +1 -0
  41. package/dist/core/remote-workspace-protocol/lima-owner-lock.js +89 -0
  42. package/dist/core/remote-workspace-protocol/lima-owner-lock.js.map +1 -0
  43. package/dist/core/remote-workspace-protocol/session.d.ts +2 -0
  44. package/dist/core/remote-workspace-protocol/session.d.ts.map +1 -1
  45. package/dist/core/remote-workspace-protocol/session.js +3 -0
  46. package/dist/core/remote-workspace-protocol/session.js.map +1 -1
  47. package/dist/core/remote-workspace-protocol/shared-transport.d.ts +71 -0
  48. package/dist/core/remote-workspace-protocol/shared-transport.d.ts.map +1 -0
  49. package/dist/core/remote-workspace-protocol/shared-transport.js +68 -0
  50. package/dist/core/remote-workspace-protocol/shared-transport.js.map +1 -0
  51. package/dist/core/sandbox/command.d.ts +2 -4
  52. package/dist/core/sandbox/command.d.ts.map +1 -1
  53. package/dist/core/sandbox/command.js +24 -46
  54. package/dist/core/sandbox/command.js.map +1 -1
  55. package/dist/core/sandbox/config.d.ts +91 -0
  56. package/dist/core/sandbox/config.d.ts.map +1 -0
  57. package/dist/core/sandbox/config.js +88 -0
  58. package/dist/core/sandbox/config.js.map +1 -0
  59. package/dist/core/sandbox/lima-guest-binary.d.ts +11 -0
  60. package/dist/core/sandbox/lima-guest-binary.d.ts.map +1 -0
  61. package/dist/core/sandbox/lima-guest-binary.js +140 -0
  62. package/dist/core/sandbox/lima-guest-binary.js.map +1 -0
  63. package/dist/core/sandbox/lima.d.ts +22 -3
  64. package/dist/core/sandbox/lima.d.ts.map +1 -1
  65. package/dist/core/sandbox/lima.js +434 -42
  66. package/dist/core/sandbox/lima.js.map +1 -1
  67. package/dist/core/sandbox/shared-lima-roots.d.ts +11 -0
  68. package/dist/core/sandbox/shared-lima-roots.d.ts.map +1 -0
  69. package/dist/core/sandbox/shared-lima-roots.js +25 -0
  70. package/dist/core/sandbox/shared-lima-roots.js.map +1 -0
  71. package/dist/core/sandbox/update-shared-guest-binary.d.ts +8 -0
  72. package/dist/core/sandbox/update-shared-guest-binary.d.ts.map +1 -0
  73. package/dist/core/sandbox/update-shared-guest-binary.js +100 -0
  74. package/dist/core/sandbox/update-shared-guest-binary.js.map +1 -0
  75. package/dist/core/settings-manager.d.ts +1 -5
  76. package/dist/core/settings-manager.d.ts.map +1 -1
  77. package/dist/core/settings-manager.js.map +1 -1
  78. package/dist/core/tools/bash.d.ts.map +1 -1
  79. package/dist/core/tools/bash.js +25 -14
  80. package/dist/core/tools/bash.js.map +1 -1
  81. package/dist/core/tools/file-mutation-queue.d.ts +2 -0
  82. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  83. package/dist/core/tools/file-mutation-queue.js +9 -0
  84. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  85. package/dist/core/tools/operations.d.ts +13 -2
  86. package/dist/core/tools/operations.d.ts.map +1 -1
  87. package/dist/core/tools/operations.js +132 -36
  88. package/dist/core/tools/operations.js.map +1 -1
  89. package/dist/daemon/activity-budget.d.ts +10 -0
  90. package/dist/daemon/activity-budget.d.ts.map +1 -0
  91. package/dist/daemon/activity-budget.js +34 -0
  92. package/dist/daemon/activity-budget.js.map +1 -0
  93. package/dist/daemon/config.d.ts +2 -0
  94. package/dist/daemon/config.d.ts.map +1 -1
  95. package/dist/daemon/config.js +15 -0
  96. package/dist/daemon/config.js.map +1 -1
  97. package/dist/daemon/outbound-budget.d.ts +10 -0
  98. package/dist/daemon/outbound-budget.d.ts.map +1 -0
  99. package/dist/daemon/outbound-budget.js +30 -0
  100. package/dist/daemon/outbound-budget.js.map +1 -0
  101. package/dist/daemon/security.d.ts.map +1 -1
  102. package/dist/daemon/security.js +3 -1
  103. package/dist/daemon/security.js.map +1 -1
  104. package/dist/daemon/server.d.ts.map +1 -1
  105. package/dist/daemon/server.js +104 -24
  106. package/dist/daemon/server.js.map +1 -1
  107. package/dist/daemon/shared-connection.d.ts +33 -0
  108. package/dist/daemon/shared-connection.d.ts.map +1 -0
  109. package/dist/daemon/shared-connection.js +271 -0
  110. package/dist/daemon/shared-connection.js.map +1 -0
  111. package/dist/daemon/shared-revision.d.ts +5 -0
  112. package/dist/daemon/shared-revision.d.ts.map +1 -0
  113. package/dist/daemon/shared-revision.js +61 -0
  114. package/dist/daemon/shared-revision.js.map +1 -0
  115. package/dist/daemon/shared-roots.d.ts +9 -0
  116. package/dist/daemon/shared-roots.d.ts.map +1 -0
  117. package/dist/daemon/shared-roots.js +33 -0
  118. package/dist/daemon/shared-roots.js.map +1 -0
  119. package/dist/daemon/workspace-runtime.d.ts +1 -1
  120. package/dist/daemon/workspace-runtime.d.ts.map +1 -1
  121. package/dist/daemon/workspace-runtime.js +42 -10
  122. package/dist/daemon/workspace-runtime.js.map +1 -1
  123. package/dist/main.d.ts.map +1 -1
  124. package/dist/main.js +5 -3
  125. package/dist/main.js.map +1 -1
  126. package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
  127. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/assistant-message.js +13 -3
  129. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/dist/modes/interactive/components/branch-summary-message.d.ts +4 -3
  131. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  132. package/dist/modes/interactive/components/branch-summary-message.js +5 -4
  133. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  134. package/dist/modes/interactive/components/compaction-summary-message.d.ts +4 -3
  135. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  136. package/dist/modes/interactive/components/compaction-summary-message.js +5 -4
  137. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  138. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  139. package/dist/modes/interactive/components/custom-message.js +4 -3
  140. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  141. package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -1
  142. package/dist/modes/interactive/components/hook-execution.js +3 -2
  143. package/dist/modes/interactive/components/hook-execution.js.map +1 -1
  144. package/dist/modes/interactive/components/message-card.d.ts +9 -0
  145. package/dist/modes/interactive/components/message-card.d.ts.map +1 -0
  146. package/dist/modes/interactive/components/message-card.js +24 -0
  147. package/dist/modes/interactive/components/message-card.js.map +1 -0
  148. package/dist/modes/interactive/components/skill-invocation-message.d.ts +4 -3
  149. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  150. package/dist/modes/interactive/components/skill-invocation-message.js +5 -4
  151. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  152. package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
  153. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  154. package/dist/modes/interactive/components/tool-execution.js +27 -21
  155. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  156. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/user-message.js +3 -2
  158. package/dist/modes/interactive/components/user-message.js.map +1 -1
  159. package/dist/modes/interactive/interactive-mode.d.ts +0 -3
  160. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  161. package/dist/modes/interactive/interactive-mode.js +55 -44
  162. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  163. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  164. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  165. package/dist/modes/interactive/theme/theme.js +10 -0
  166. package/dist/modes/interactive/theme/theme.js.map +1 -1
  167. package/dist/utils/clipboard-native.d.ts.map +1 -1
  168. package/dist/utils/clipboard-native.js +1 -1
  169. package/dist/utils/clipboard-native.js.map +1 -1
  170. package/dist/utils/image-resize.d.ts.map +1 -1
  171. package/dist/utils/image-resize.js +5 -0
  172. package/dist/utils/image-resize.js.map +1 -1
  173. package/dist/utils/photon.d.ts.map +1 -1
  174. package/dist/utils/photon.js +1 -0
  175. package/dist/utils/photon.js.map +1 -1
  176. package/docs/daemon.md +6 -4
  177. package/docs/docs.json +1 -1
  178. package/docs/index.md +8 -12
  179. package/docs/packages.md +3 -3
  180. package/docs/quickstart.md +11 -19
  181. package/docs/sandbox.md +40 -117
  182. package/docs/settings.md +12 -11
  183. package/docs/termux.md +1 -18
  184. package/docs/usage.md +2 -2
  185. package/examples/README.md +0 -2
  186. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  187. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  188. package/examples/extensions/sandbox/package.json +1 -1
  189. package/examples/extensions/with-deps/package.json +1 -1
  190. package/npm-shrinkwrap.json +12 -12
  191. package/package.json +5 -6
  192. package/dist/core/sandbox/docker.d.ts +0 -169
  193. package/dist/core/sandbox/docker.d.ts.map +0 -1
  194. package/dist/core/sandbox/docker.js +0 -633
  195. package/dist/core/sandbox/docker.js.map +0 -1
  196. package/examples/sandbox-docker/Dockerfile +0 -274
  197. package/examples/sandbox-docker/README.md +0 -88
@@ -0,0 +1,271 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { VERSION } from "../config.js";
3
+ import { RemoteWorkspaceServerProtocol, } from "../core/remote-workspace-protocol/session.js";
4
+ import { parseSharedWorkspaceClientEnvelope, } from "../core/remote-workspace-protocol/shared-transport.js";
5
+ import { DaemonActivityBudget } from "./activity-budget.js";
6
+ import { withSharedRevisionGuard } from "./shared-revision.js";
7
+ import { createDaemonWorkspaceRuntime } from "./workspace-runtime.js";
8
+ /** One authenticated v2 WebSocket. No state (including request IDs) is shared between channels. */
9
+ export class SharedDaemonConnection {
10
+ channels = new Map();
11
+ channelDisposals = new WeakMap();
12
+ pendingDisposals = new Set();
13
+ usedControlIds = new Set();
14
+ epoch = randomBytes(18).toString("base64url");
15
+ hello = false;
16
+ closed = false;
17
+ queue = Promise.resolve();
18
+ pendingInboundMessages = 0;
19
+ pendingInboundBytes = 0;
20
+ budget;
21
+ configuration;
22
+ roots;
23
+ send;
24
+ closeSocket;
25
+ constructor(configuration, roots, send, closeSocket, budget = new DaemonActivityBudget(configuration)) {
26
+ this.configuration = configuration;
27
+ this.roots = roots;
28
+ this.send = send;
29
+ this.closeSocket = closeSocket;
30
+ this.budget = budget;
31
+ }
32
+ receive(payload) {
33
+ if (this.closed)
34
+ return Promise.resolve();
35
+ const limits = this.configuration.protocolLimits;
36
+ if (payload.byteLength > limits.maxMessageBytes ||
37
+ this.pendingInboundMessages >= limits.maxPendingInboundMessages ||
38
+ this.pendingInboundBytes + payload.byteLength > limits.maxPendingInboundBytes) {
39
+ this.closed = true;
40
+ this.closeSocket(1009, "Shared workspace inbound queue exceeds limits");
41
+ return Promise.resolve();
42
+ }
43
+ // Copy before queuing: WebSocket buffers need not remain stable after the callback returns.
44
+ const queuedPayload = Uint8Array.from(payload);
45
+ this.pendingInboundMessages++;
46
+ this.pendingInboundBytes += queuedPayload.byteLength;
47
+ let complete;
48
+ const finished = new Promise((resolve) => {
49
+ complete = resolve;
50
+ });
51
+ let channelPending = false;
52
+ const settle = () => {
53
+ this.pendingInboundMessages--;
54
+ this.pendingInboundBytes -= queuedPayload.byteLength;
55
+ complete();
56
+ };
57
+ const fail = () => {
58
+ if (!this.closed) {
59
+ this.closed = true;
60
+ this.closeSocket(1002, "Invalid shared workspace frame");
61
+ }
62
+ };
63
+ const scheduled = this.queue.then(() => {
64
+ if (this.closed)
65
+ return;
66
+ const frame = parseSharedWorkspaceClientEnvelope(queuedPayload, limits);
67
+ if (frame.type === "channel") {
68
+ // Validate the channel before letting the control queue advance. Its own
69
+ // protocol preserves per-channel receive order and bounds queued frames.
70
+ if (!this.channels.has(frame.channelId))
71
+ throw new Error("Unknown shared workspace channel");
72
+ channelPending = true;
73
+ void this.dispatch(frame).catch(fail).finally(settle);
74
+ return;
75
+ }
76
+ // Hello, opens, closes and reloads remain serialized in control order.
77
+ return this.dispatch(frame);
78
+ });
79
+ this.queue = scheduled.catch(fail).finally(() => {
80
+ if (!channelPending)
81
+ settle();
82
+ });
83
+ return finished;
84
+ }
85
+ get handshakeComplete() {
86
+ return this.hello;
87
+ }
88
+ async beginDrain() {
89
+ await Promise.allSettled([...this.channels.values()].map((channel) => channel.protocol.beginDrain()));
90
+ }
91
+ createChannelRuntime(root) {
92
+ return createDaemonWorkspaceRuntime({
93
+ ...this.configuration,
94
+ workspaceRoot: root,
95
+ // Operator-wide resource files and the v1 temporary root are not channel-scoped.
96
+ // Keep them available on v1, but never expose them through a child channel.
97
+ temporaryRoot: undefined,
98
+ sandboxInstructionsPath: undefined,
99
+ userAgentsPath: undefined,
100
+ });
101
+ }
102
+ reload() {
103
+ const operation = this.queue.then(async () => {
104
+ if (this.closed)
105
+ throw new Error("Shared workspace connection is closed");
106
+ for (const channel of this.channels.values()) {
107
+ const previous = channel.runtime;
108
+ const previousHandler = channel.handler;
109
+ const candidate = this.createChannelRuntime(channel.workspace.root);
110
+ const candidateHandler = candidate.createHandler(channel.workspace, true);
111
+ channel.runtime = candidate;
112
+ channel.handler = candidateHandler;
113
+ try {
114
+ await channel.protocol.publishCatalog({ ...candidate.catalog, generation: channel.generation + 1 });
115
+ channel.generation++;
116
+ }
117
+ catch (error) {
118
+ channel.runtime = previous;
119
+ channel.handler = previousHandler;
120
+ await candidate.dispose();
121
+ throw error;
122
+ }
123
+ await previous.retire();
124
+ }
125
+ });
126
+ this.queue = operation.catch(() => undefined);
127
+ return operation;
128
+ }
129
+ async dispatch(frame) {
130
+ if (frame.type === "hello") {
131
+ if (this.hello)
132
+ throw new Error("Duplicate v2 hello");
133
+ this.hello = true;
134
+ this.usedControlIds.add(frame.id);
135
+ await this.send({ type: "hello_ack", id: frame.id, version: 2, epoch: this.epoch, daemonVersion: VERSION });
136
+ return;
137
+ }
138
+ if (!this.hello)
139
+ throw new Error("V2 hello required before opening channels");
140
+ if (frame.type === "open") {
141
+ if (this.usedControlIds.has(frame.id))
142
+ throw new Error("Duplicate v2 control ID");
143
+ if (this.usedControlIds.size >= 65_536)
144
+ throw new Error("V2 control ID budget exhausted");
145
+ this.usedControlIds.add(frame.id);
146
+ if (this.channels.size >= Math.min(this.configuration.maxConnections, 32)) {
147
+ await this.send({
148
+ type: "control_error",
149
+ id: frame.id,
150
+ code: "limit_exceeded",
151
+ message: "Channel limit reached",
152
+ });
153
+ return;
154
+ }
155
+ let root;
156
+ try {
157
+ root = await this.roots.bind(frame.root);
158
+ }
159
+ catch {
160
+ await this.send({
161
+ type: "control_error",
162
+ id: frame.id,
163
+ code: "invalid_root",
164
+ message: "Workspace root is not an existing directory under the shared parent",
165
+ });
166
+ return;
167
+ }
168
+ await this.open(frame.id, root);
169
+ return;
170
+ }
171
+ const channel = this.channels.get(frame.channelId);
172
+ if (!channel)
173
+ throw new Error("Unknown shared workspace channel");
174
+ if (frame.type === "close") {
175
+ // Do not wait on a channel's in-flight requests while blocking the socket receive queue.
176
+ this.channels.delete(frame.channelId);
177
+ // Begin cancellation before acknowledging detach; track it for physical shutdown.
178
+ void this.disposeChannel(channel);
179
+ await this.send({ type: "closed", channelId: frame.channelId });
180
+ return;
181
+ }
182
+ await channel.protocol.receive(Buffer.from(JSON.stringify(frame.payload)));
183
+ }
184
+ async open(id, root) {
185
+ const channelId = randomBytes(24).toString("base64url");
186
+ const workspace = Object.freeze({
187
+ id: randomBytes(18).toString("base64url"),
188
+ root,
189
+ pathFlavor: "posix",
190
+ });
191
+ let runtime;
192
+ try {
193
+ runtime = this.createChannelRuntime(root);
194
+ }
195
+ catch {
196
+ // A bad child project configuration is an open failure, not a corrupt
197
+ // physical envelope. Keep already bound sibling channels usable.
198
+ await this.send({
199
+ type: "control_error",
200
+ id,
201
+ code: "invalid_request",
202
+ message: "Unable to initialize shared workspace channel",
203
+ });
204
+ return;
205
+ }
206
+ const handler = runtime.createHandler(workspace, true);
207
+ let channel;
208
+ const protocol = new RemoteWorkspaceServerProtocol({
209
+ send: (payload) => this.channels.has(channelId) && !this.closed
210
+ ? this.send({ type: "channel", channelId, payload })
211
+ : Promise.reject(new Error("Shared workspace channel is closed")),
212
+ close: async (reason) => {
213
+ const channel = this.channels.get(channelId);
214
+ if (channel) {
215
+ this.channels.delete(channelId);
216
+ void this.disposeChannel(channel);
217
+ if (!this.closed)
218
+ await this.send({ type: "closed", channelId, reason });
219
+ }
220
+ },
221
+ }, {
222
+ validateToolArguments: (toolName, value) => channel.handler.validateToolArguments(toolName, value),
223
+ handleRequest: (request, context) => this.budget.run(request.method, async () => {
224
+ const result = await withSharedRevisionGuard(request, workspace.root, channel.runtime.operations, context, () => channel.handler.handleRequest(request, context));
225
+ if (request.method !== "workspace.read")
226
+ return result;
227
+ const read = result;
228
+ return {
229
+ ...read,
230
+ revision: createHash("sha256").update(Buffer.from(read.contentBase64, "base64")).digest("hex"),
231
+ };
232
+ }),
233
+ }, {
234
+ workspace,
235
+ catalog: runtime.catalog,
236
+ capabilities: runtime.capabilities,
237
+ limits: this.configuration.protocolLimits,
238
+ backendLabel: "grip-shared-workspace-daemon",
239
+ });
240
+ channel = { runtime, handler, protocol, workspace, generation: runtime.catalog.generation };
241
+ this.channels.set(channelId, channel);
242
+ try {
243
+ await this.send({ type: "opened", id, channelId });
244
+ }
245
+ catch (error) {
246
+ this.channels.delete(channelId);
247
+ void this.disposeChannel(channel);
248
+ throw error;
249
+ }
250
+ }
251
+ disposeChannel(channel) {
252
+ const existing = this.channelDisposals.get(channel);
253
+ if (existing)
254
+ return existing;
255
+ const disposal = (async () => {
256
+ await channel.protocol.disconnect().catch(() => undefined);
257
+ await channel.runtime.dispose().catch(() => undefined);
258
+ })();
259
+ this.channelDisposals.set(channel, disposal);
260
+ this.pendingDisposals.add(disposal);
261
+ void disposal.then(() => this.pendingDisposals.delete(disposal), () => this.pendingDisposals.delete(disposal));
262
+ return disposal;
263
+ }
264
+ async disconnect() {
265
+ this.closed = true;
266
+ const channels = [...this.channels.values()];
267
+ this.channels.clear();
268
+ await Promise.allSettled([...channels.map((channel) => this.disposeChannel(channel)), ...this.pendingDisposals]);
269
+ }
270
+ }
271
+ //# sourceMappingURL=shared-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-connection.js","sourceRoot":"","sources":["../../src/daemon/shared-connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport { VERSION } from \"../config.ts\";\nimport type {\n\tRemoteWorkspaceIdentity,\n\tRemoteWorkspaceServerMessage,\n} from \"../core/remote-workspace-protocol/contract.ts\";\nimport {\n\ttype RemoteWorkspaceServerHandler,\n\tRemoteWorkspaceServerProtocol,\n} from \"../core/remote-workspace-protocol/session.ts\";\nimport {\n\tparseSharedWorkspaceClientEnvelope,\n\ttype SharedWorkspaceClientEnvelope,\n\ttype SharedWorkspaceServerFrame,\n} from \"../core/remote-workspace-protocol/shared-transport.ts\";\nimport { DaemonActivityBudget } from \"./activity-budget.ts\";\nimport type { DaemonConfiguration } from \"./config.ts\";\nimport { withSharedRevisionGuard } from \"./shared-revision.ts\";\nimport type { SharedWorkspaceRoots } from \"./shared-roots.ts\";\nimport { createDaemonWorkspaceRuntime, type DaemonWorkspaceRuntime } from \"./workspace-runtime.ts\";\n\ninterface Channel {\n\treadonly protocol: RemoteWorkspaceServerProtocol;\n\truntime: DaemonWorkspaceRuntime;\n\thandler: RemoteWorkspaceServerHandler;\n\treadonly workspace: RemoteWorkspaceIdentity;\n\tgeneration: number;\n}\n\n/** One authenticated v2 WebSocket. No state (including request IDs) is shared between channels. */\nexport class SharedDaemonConnection {\n\tprivate readonly channels = new Map<string, Channel>();\n\tprivate readonly channelDisposals = new WeakMap<Channel, Promise<void>>();\n\tprivate readonly pendingDisposals = new Set<Promise<void>>();\n\tprivate readonly usedControlIds = new Set<string>();\n\tprivate readonly epoch = randomBytes(18).toString(\"base64url\");\n\tprivate hello = false;\n\tprivate closed = false;\n\tprivate queue: Promise<void> = Promise.resolve();\n\tprivate pendingInboundMessages = 0;\n\tprivate pendingInboundBytes = 0;\n\tprivate readonly budget: DaemonActivityBudget;\n\n\tprivate readonly configuration: DaemonConfiguration;\n\tprivate readonly roots: SharedWorkspaceRoots;\n\tprivate readonly send: (message: SharedWorkspaceServerFrame) => Promise<void>;\n\tprivate readonly closeSocket: (code: number, message: string) => void;\n\n\tconstructor(\n\t\tconfiguration: DaemonConfiguration,\n\t\troots: SharedWorkspaceRoots,\n\t\tsend: (message: SharedWorkspaceServerFrame) => Promise<void>,\n\t\tcloseSocket: (code: number, message: string) => void,\n\t\tbudget: DaemonActivityBudget = new DaemonActivityBudget(configuration),\n\t) {\n\t\tthis.configuration = configuration;\n\t\tthis.roots = roots;\n\t\tthis.send = send;\n\t\tthis.closeSocket = closeSocket;\n\t\tthis.budget = budget;\n\t}\n\n\treceive(payload: Uint8Array): Promise<void> {\n\t\tif (this.closed) return Promise.resolve();\n\t\tconst limits = this.configuration.protocolLimits;\n\t\tif (\n\t\t\tpayload.byteLength > limits.maxMessageBytes ||\n\t\t\tthis.pendingInboundMessages >= limits.maxPendingInboundMessages ||\n\t\t\tthis.pendingInboundBytes + payload.byteLength > limits.maxPendingInboundBytes\n\t\t) {\n\t\t\tthis.closed = true;\n\t\t\tthis.closeSocket(1009, \"Shared workspace inbound queue exceeds limits\");\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\t// Copy before queuing: WebSocket buffers need not remain stable after the callback returns.\n\t\tconst queuedPayload = Uint8Array.from(payload);\n\t\tthis.pendingInboundMessages++;\n\t\tthis.pendingInboundBytes += queuedPayload.byteLength;\n\t\tlet complete!: () => void;\n\t\tconst finished = new Promise<void>((resolve) => {\n\t\t\tcomplete = resolve;\n\t\t});\n\t\tlet channelPending = false;\n\t\tconst settle = () => {\n\t\t\tthis.pendingInboundMessages--;\n\t\t\tthis.pendingInboundBytes -= queuedPayload.byteLength;\n\t\t\tcomplete();\n\t\t};\n\t\tconst fail = () => {\n\t\t\tif (!this.closed) {\n\t\t\t\tthis.closed = true;\n\t\t\t\tthis.closeSocket(1002, \"Invalid shared workspace frame\");\n\t\t\t}\n\t\t};\n\t\tconst scheduled = this.queue.then(() => {\n\t\t\tif (this.closed) return;\n\t\t\tconst frame = parseSharedWorkspaceClientEnvelope(queuedPayload, limits);\n\t\t\tif (frame.type === \"channel\") {\n\t\t\t\t// Validate the channel before letting the control queue advance. Its own\n\t\t\t\t// protocol preserves per-channel receive order and bounds queued frames.\n\t\t\t\tif (!this.channels.has(frame.channelId)) throw new Error(\"Unknown shared workspace channel\");\n\t\t\t\tchannelPending = true;\n\t\t\t\tvoid this.dispatch(frame).catch(fail).finally(settle);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Hello, opens, closes and reloads remain serialized in control order.\n\t\t\treturn this.dispatch(frame);\n\t\t});\n\t\tthis.queue = scheduled.catch(fail).finally(() => {\n\t\t\tif (!channelPending) settle();\n\t\t});\n\t\treturn finished;\n\t}\n\n\tget handshakeComplete(): boolean {\n\t\treturn this.hello;\n\t}\n\n\tasync beginDrain(): Promise<void> {\n\t\tawait Promise.allSettled([...this.channels.values()].map((channel) => channel.protocol.beginDrain()));\n\t}\n\n\tprivate createChannelRuntime(root: string): DaemonWorkspaceRuntime {\n\t\treturn createDaemonWorkspaceRuntime({\n\t\t\t...this.configuration,\n\t\t\tworkspaceRoot: root,\n\t\t\t// Operator-wide resource files and the v1 temporary root are not channel-scoped.\n\t\t\t// Keep them available on v1, but never expose them through a child channel.\n\t\t\ttemporaryRoot: undefined,\n\t\t\tsandboxInstructionsPath: undefined,\n\t\t\tuserAgentsPath: undefined,\n\t\t});\n\t}\n\n\treload(): Promise<void> {\n\t\tconst operation = this.queue.then(async () => {\n\t\t\tif (this.closed) throw new Error(\"Shared workspace connection is closed\");\n\t\t\tfor (const channel of this.channels.values()) {\n\t\t\t\tconst previous = channel.runtime;\n\t\t\t\tconst previousHandler = channel.handler;\n\t\t\t\tconst candidate = this.createChannelRuntime(channel.workspace.root);\n\t\t\t\tconst candidateHandler = candidate.createHandler(channel.workspace, true);\n\t\t\t\tchannel.runtime = candidate;\n\t\t\t\tchannel.handler = candidateHandler;\n\t\t\t\ttry {\n\t\t\t\t\tawait channel.protocol.publishCatalog({ ...candidate.catalog, generation: channel.generation + 1 });\n\t\t\t\t\tchannel.generation++;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tchannel.runtime = previous;\n\t\t\t\t\tchannel.handler = previousHandler;\n\t\t\t\t\tawait candidate.dispose();\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait previous.retire();\n\t\t\t}\n\t\t});\n\t\tthis.queue = operation.catch(() => undefined);\n\t\treturn operation;\n\t}\n\n\tprivate async dispatch(frame: SharedWorkspaceClientEnvelope): Promise<void> {\n\t\tif (frame.type === \"hello\") {\n\t\t\tif (this.hello) throw new Error(\"Duplicate v2 hello\");\n\t\t\tthis.hello = true;\n\t\t\tthis.usedControlIds.add(frame.id);\n\t\t\tawait this.send({ type: \"hello_ack\", id: frame.id, version: 2, epoch: this.epoch, daemonVersion: VERSION });\n\t\t\treturn;\n\t\t}\n\t\tif (!this.hello) throw new Error(\"V2 hello required before opening channels\");\n\t\tif (frame.type === \"open\") {\n\t\t\tif (this.usedControlIds.has(frame.id)) throw new Error(\"Duplicate v2 control ID\");\n\t\t\tif (this.usedControlIds.size >= 65_536) throw new Error(\"V2 control ID budget exhausted\");\n\t\t\tthis.usedControlIds.add(frame.id);\n\t\t\tif (this.channels.size >= Math.min(this.configuration.maxConnections, 32)) {\n\t\t\t\tawait this.send({\n\t\t\t\t\ttype: \"control_error\",\n\t\t\t\t\tid: frame.id,\n\t\t\t\t\tcode: \"limit_exceeded\",\n\t\t\t\t\tmessage: \"Channel limit reached\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet root: string;\n\t\t\ttry {\n\t\t\t\troot = await this.roots.bind(frame.root);\n\t\t\t} catch {\n\t\t\t\tawait this.send({\n\t\t\t\t\ttype: \"control_error\",\n\t\t\t\t\tid: frame.id,\n\t\t\t\t\tcode: \"invalid_root\",\n\t\t\t\t\tmessage: \"Workspace root is not an existing directory under the shared parent\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.open(frame.id, root);\n\t\t\treturn;\n\t\t}\n\t\tconst channel = this.channels.get(frame.channelId);\n\t\tif (!channel) throw new Error(\"Unknown shared workspace channel\");\n\t\tif (frame.type === \"close\") {\n\t\t\t// Do not wait on a channel's in-flight requests while blocking the socket receive queue.\n\t\t\tthis.channels.delete(frame.channelId);\n\t\t\t// Begin cancellation before acknowledging detach; track it for physical shutdown.\n\t\t\tvoid this.disposeChannel(channel);\n\t\t\tawait this.send({ type: \"closed\", channelId: frame.channelId });\n\t\t\treturn;\n\t\t}\n\t\tawait channel.protocol.receive(Buffer.from(JSON.stringify(frame.payload)));\n\t}\n\n\tprivate async open(id: string, root: string): Promise<void> {\n\t\tconst channelId = randomBytes(24).toString(\"base64url\");\n\t\tconst workspace = Object.freeze({\n\t\t\tid: randomBytes(18).toString(\"base64url\"),\n\t\t\troot,\n\t\t\tpathFlavor: \"posix\" as const,\n\t\t});\n\t\tlet runtime: DaemonWorkspaceRuntime;\n\t\ttry {\n\t\t\truntime = this.createChannelRuntime(root);\n\t\t} catch {\n\t\t\t// A bad child project configuration is an open failure, not a corrupt\n\t\t\t// physical envelope. Keep already bound sibling channels usable.\n\t\t\tawait this.send({\n\t\t\t\ttype: \"control_error\",\n\t\t\t\tid,\n\t\t\t\tcode: \"invalid_request\",\n\t\t\t\tmessage: \"Unable to initialize shared workspace channel\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst handler = runtime.createHandler(workspace, true);\n\t\tlet channel!: Channel;\n\t\tconst protocol = new RemoteWorkspaceServerProtocol(\n\t\t\t{\n\t\t\t\tsend: (payload: RemoteWorkspaceServerMessage) =>\n\t\t\t\t\tthis.channels.has(channelId) && !this.closed\n\t\t\t\t\t\t? this.send({ type: \"channel\", channelId, payload })\n\t\t\t\t\t\t: Promise.reject(new Error(\"Shared workspace channel is closed\")),\n\t\t\t\tclose: async (reason) => {\n\t\t\t\t\tconst channel = this.channels.get(channelId);\n\t\t\t\t\tif (channel) {\n\t\t\t\t\t\tthis.channels.delete(channelId);\n\t\t\t\t\t\tvoid this.disposeChannel(channel);\n\t\t\t\t\t\tif (!this.closed) await this.send({ type: \"closed\", channelId, reason });\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalidateToolArguments: (toolName, value) => channel.handler.validateToolArguments(toolName, value),\n\t\t\t\thandleRequest: (request, context) =>\n\t\t\t\t\tthis.budget.run(request.method, async () => {\n\t\t\t\t\t\tconst result = await withSharedRevisionGuard(\n\t\t\t\t\t\t\trequest,\n\t\t\t\t\t\t\tworkspace.root,\n\t\t\t\t\t\t\tchannel.runtime.operations,\n\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\t() => channel.handler.handleRequest(request, context),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (request.method !== \"workspace.read\") return result;\n\t\t\t\t\t\tconst read = result as { contentBase64: string };\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...read,\n\t\t\t\t\t\t\trevision: createHash(\"sha256\").update(Buffer.from(read.contentBase64, \"base64\")).digest(\"hex\"),\n\t\t\t\t\t\t};\n\t\t\t\t\t}),\n\t\t\t},\n\t\t\t{\n\t\t\t\tworkspace,\n\t\t\t\tcatalog: runtime.catalog,\n\t\t\t\tcapabilities: runtime.capabilities,\n\t\t\t\tlimits: this.configuration.protocolLimits,\n\t\t\t\tbackendLabel: \"grip-shared-workspace-daemon\",\n\t\t\t},\n\t\t);\n\t\tchannel = { runtime, handler, protocol, workspace, generation: runtime.catalog.generation };\n\t\tthis.channels.set(channelId, channel);\n\t\ttry {\n\t\t\tawait this.send({ type: \"opened\", id, channelId });\n\t\t} catch (error) {\n\t\t\tthis.channels.delete(channelId);\n\t\t\tvoid this.disposeChannel(channel);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate disposeChannel(channel: Channel): Promise<void> {\n\t\tconst existing = this.channelDisposals.get(channel);\n\t\tif (existing) return existing;\n\t\tconst disposal = (async () => {\n\t\t\tawait channel.protocol.disconnect().catch(() => undefined);\n\t\t\tawait channel.runtime.dispose().catch(() => undefined);\n\t\t})();\n\t\tthis.channelDisposals.set(channel, disposal);\n\t\tthis.pendingDisposals.add(disposal);\n\t\tvoid disposal.then(\n\t\t\t() => this.pendingDisposals.delete(disposal),\n\t\t\t() => this.pendingDisposals.delete(disposal),\n\t\t);\n\t\treturn disposal;\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tthis.closed = true;\n\t\tconst channels = [...this.channels.values()];\n\t\tthis.channels.clear();\n\t\tawait Promise.allSettled([...channels.map((channel) => this.disposeChannel(channel)), ...this.pendingDisposals]);\n\t}\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { type RemoteWorkspaceServerRequestContext, type RemoteWorkspaceTypedRequest } from "../core/remote-workspace-protocol/session.ts";
2
+ import type { ConfinedDaemonToolOperations } from "./workspace-runtime.ts";
3
+ /** v2-only CAS checked inside the same process-wide per-file queue as edit/write. */
4
+ export declare function withSharedRevisionGuard<T>(request: RemoteWorkspaceTypedRequest, root: string, operations: ConfinedDaemonToolOperations, context: RemoteWorkspaceServerRequestContext, action: () => Promise<T>): Promise<T>;
5
+ //# sourceMappingURL=shared-revision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-revision.d.ts","sourceRoot":"","sources":["../../src/daemon/shared-revision.ts"],"names":[],"mappings":"AAGA,OAAO,EAEN,KAAK,mCAAmC,EACxC,KAAK,2BAA2B,EAChC,MAAM,8CAA8C,CAAC;AAEtD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAmD3E,qFAAqF;AACrF,wBAAgB,uBAAuB,CAAC,CAAC,EACxC,OAAO,EAAE,2BAA2B,EACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,4BAA4B,EACxC,OAAO,EAAE,mCAAmC,EAC5C,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,CAgBZ","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { resolve } from \"node:path\";\nimport type { ExpectedWorkspaceRevision } from \"../core/remote-workspace-protocol/contract.ts\";\nimport {\n\tRemoteWorkspaceRequestError,\n\ttype RemoteWorkspaceServerRequestContext,\n\ttype RemoteWorkspaceTypedRequest,\n} from \"../core/remote-workspace-protocol/session.ts\";\nimport { withFileMutationGuard } from \"../core/tools/file-mutation-queue.ts\";\nimport type { ConfinedDaemonToolOperations } from \"./workspace-runtime.ts\";\n\ntype SharedRevisionErrorCode = \"invalid_request\" | \"conflict\";\n\nfunction revisionError(code: SharedRevisionErrorCode, message: string): RemoteWorkspaceRequestError {\n\treturn new RemoteWorkspaceRequestError({ code, message, executionState: \"not_started\", retryable: false });\n}\nfunction requireActiveMutation(context: RemoteWorkspaceServerRequestContext): void {\n\tif (context.signal.aborted)\n\t\tthrow new RemoteWorkspaceRequestError({\n\t\t\tcode: \"cancelled\",\n\t\t\tmessage: \"Shared mutation was cancelled before starting\",\n\t\t\texecutionState: \"not_started\",\n\t\t\tretryable: false,\n\t\t});\n}\n\nfunction missing(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nasync function checkRevision(\n\tfilePath: string,\n\ttarget: string,\n\trevision: ExpectedWorkspaceRevision,\n\toperations: ConfinedDaemonToolOperations,\n\tcontext: RemoteWorkspaceServerRequestContext,\n): Promise<void> {\n\trequireActiveMutation(context);\n\tif (resolve(filePath) !== target)\n\t\tthrow revisionError(\"invalid_request\", \"Shared mutation path does not match its revision precondition\");\n\tif (\"sha256\" in revision || \"absent\" in revision) {\n\t\tlet actual: Buffer | undefined;\n\t\ttry {\n\t\t\tactual = await operations.readFile(target);\n\t\t} catch (error) {\n\t\t\tif (!missing(error)) throw error;\n\t\t}\n\t\tif (\"sha256\" in revision && (!actual || createHash(\"sha256\").update(actual).digest(\"hex\") !== revision.sha256)) {\n\t\t\tthrow revisionError(\"conflict\", \"Workspace file changed since the expected revision\");\n\t\t}\n\t\tif (\"absent\" in revision && actual) throw revisionError(\"conflict\", \"Workspace file already exists\");\n\t}\n\trequireActiveMutation(context);\n\tcontext.markSideEffectStarted();\n}\n/** v2-only CAS checked inside the same process-wide per-file queue as edit/write. */\nexport function withSharedRevisionGuard<T>(\n\trequest: RemoteWorkspaceTypedRequest,\n\troot: string,\n\toperations: ConfinedDaemonToolOperations,\n\tcontext: RemoteWorkspaceServerRequestContext,\n\taction: () => Promise<T>,\n): Promise<T> {\n\tconst params = request.params as Record<string, unknown>;\n\tconst toolName = request.method === \"tool.invoke\" ? params.toolName : undefined;\n\tif (request.method !== \"workspace.write\" && toolName !== \"edit\" && toolName !== \"write\") return action();\n\tconst args = toolName ? (params.arguments as { path?: unknown }) : params;\n\tif (typeof args?.path !== \"string\" || !args.path)\n\t\tthrow revisionError(\"invalid_request\", \"Shared mutation requires a file path\");\n\tconst revision = params.expectedRevision as ExpectedWorkspaceRevision | undefined;\n\tif (!revision || (toolName === \"edit\" && !(\"sha256\" in revision))) {\n\t\tthrow revisionError(\n\t\t\t\"invalid_request\",\n\t\t\t\"Shared edit requires expected SHA-256; shared write requires expected SHA-256, expected absence or explicit unsafeOverwrite\",\n\t\t);\n\t}\n\tconst target = resolve(root, args.path);\n\treturn withFileMutationGuard((filePath) => checkRevision(filePath, target, revision, operations, context), action);\n}\n"]}
@@ -0,0 +1,61 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { RemoteWorkspaceRequestError, } from "../core/remote-workspace-protocol/session.js";
4
+ import { withFileMutationGuard } from "../core/tools/file-mutation-queue.js";
5
+ function revisionError(code, message) {
6
+ return new RemoteWorkspaceRequestError({ code, message, executionState: "not_started", retryable: false });
7
+ }
8
+ function requireActiveMutation(context) {
9
+ if (context.signal.aborted)
10
+ throw new RemoteWorkspaceRequestError({
11
+ code: "cancelled",
12
+ message: "Shared mutation was cancelled before starting",
13
+ executionState: "not_started",
14
+ retryable: false,
15
+ });
16
+ }
17
+ function missing(error) {
18
+ return (typeof error === "object" &&
19
+ error !== null &&
20
+ "code" in error &&
21
+ (error.code === "ENOENT" || error.code === "ENOTDIR"));
22
+ }
23
+ async function checkRevision(filePath, target, revision, operations, context) {
24
+ requireActiveMutation(context);
25
+ if (resolve(filePath) !== target)
26
+ throw revisionError("invalid_request", "Shared mutation path does not match its revision precondition");
27
+ if ("sha256" in revision || "absent" in revision) {
28
+ let actual;
29
+ try {
30
+ actual = await operations.readFile(target);
31
+ }
32
+ catch (error) {
33
+ if (!missing(error))
34
+ throw error;
35
+ }
36
+ if ("sha256" in revision && (!actual || createHash("sha256").update(actual).digest("hex") !== revision.sha256)) {
37
+ throw revisionError("conflict", "Workspace file changed since the expected revision");
38
+ }
39
+ if ("absent" in revision && actual)
40
+ throw revisionError("conflict", "Workspace file already exists");
41
+ }
42
+ requireActiveMutation(context);
43
+ context.markSideEffectStarted();
44
+ }
45
+ /** v2-only CAS checked inside the same process-wide per-file queue as edit/write. */
46
+ export function withSharedRevisionGuard(request, root, operations, context, action) {
47
+ const params = request.params;
48
+ const toolName = request.method === "tool.invoke" ? params.toolName : undefined;
49
+ if (request.method !== "workspace.write" && toolName !== "edit" && toolName !== "write")
50
+ return action();
51
+ const args = toolName ? params.arguments : params;
52
+ if (typeof args?.path !== "string" || !args.path)
53
+ throw revisionError("invalid_request", "Shared mutation requires a file path");
54
+ const revision = params.expectedRevision;
55
+ if (!revision || (toolName === "edit" && !("sha256" in revision))) {
56
+ throw revisionError("invalid_request", "Shared edit requires expected SHA-256; shared write requires expected SHA-256, expected absence or explicit unsafeOverwrite");
57
+ }
58
+ const target = resolve(root, args.path);
59
+ return withFileMutationGuard((filePath) => checkRevision(filePath, target, revision, operations, context), action);
60
+ }
61
+ //# sourceMappingURL=shared-revision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-revision.js","sourceRoot":"","sources":["../../src/daemon/shared-revision.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { resolve } from \"node:path\";\nimport type { ExpectedWorkspaceRevision } from \"../core/remote-workspace-protocol/contract.ts\";\nimport {\n\tRemoteWorkspaceRequestError,\n\ttype RemoteWorkspaceServerRequestContext,\n\ttype RemoteWorkspaceTypedRequest,\n} from \"../core/remote-workspace-protocol/session.ts\";\nimport { withFileMutationGuard } from \"../core/tools/file-mutation-queue.ts\";\nimport type { ConfinedDaemonToolOperations } from \"./workspace-runtime.ts\";\n\ntype SharedRevisionErrorCode = \"invalid_request\" | \"conflict\";\n\nfunction revisionError(code: SharedRevisionErrorCode, message: string): RemoteWorkspaceRequestError {\n\treturn new RemoteWorkspaceRequestError({ code, message, executionState: \"not_started\", retryable: false });\n}\nfunction requireActiveMutation(context: RemoteWorkspaceServerRequestContext): void {\n\tif (context.signal.aborted)\n\t\tthrow new RemoteWorkspaceRequestError({\n\t\t\tcode: \"cancelled\",\n\t\t\tmessage: \"Shared mutation was cancelled before starting\",\n\t\t\texecutionState: \"not_started\",\n\t\t\tretryable: false,\n\t\t});\n}\n\nfunction missing(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nasync function checkRevision(\n\tfilePath: string,\n\ttarget: string,\n\trevision: ExpectedWorkspaceRevision,\n\toperations: ConfinedDaemonToolOperations,\n\tcontext: RemoteWorkspaceServerRequestContext,\n): Promise<void> {\n\trequireActiveMutation(context);\n\tif (resolve(filePath) !== target)\n\t\tthrow revisionError(\"invalid_request\", \"Shared mutation path does not match its revision precondition\");\n\tif (\"sha256\" in revision || \"absent\" in revision) {\n\t\tlet actual: Buffer | undefined;\n\t\ttry {\n\t\t\tactual = await operations.readFile(target);\n\t\t} catch (error) {\n\t\t\tif (!missing(error)) throw error;\n\t\t}\n\t\tif (\"sha256\" in revision && (!actual || createHash(\"sha256\").update(actual).digest(\"hex\") !== revision.sha256)) {\n\t\t\tthrow revisionError(\"conflict\", \"Workspace file changed since the expected revision\");\n\t\t}\n\t\tif (\"absent\" in revision && actual) throw revisionError(\"conflict\", \"Workspace file already exists\");\n\t}\n\trequireActiveMutation(context);\n\tcontext.markSideEffectStarted();\n}\n/** v2-only CAS checked inside the same process-wide per-file queue as edit/write. */\nexport function withSharedRevisionGuard<T>(\n\trequest: RemoteWorkspaceTypedRequest,\n\troot: string,\n\toperations: ConfinedDaemonToolOperations,\n\tcontext: RemoteWorkspaceServerRequestContext,\n\taction: () => Promise<T>,\n): Promise<T> {\n\tconst params = request.params as Record<string, unknown>;\n\tconst toolName = request.method === \"tool.invoke\" ? params.toolName : undefined;\n\tif (request.method !== \"workspace.write\" && toolName !== \"edit\" && toolName !== \"write\") return action();\n\tconst args = toolName ? (params.arguments as { path?: unknown }) : params;\n\tif (typeof args?.path !== \"string\" || !args.path)\n\t\tthrow revisionError(\"invalid_request\", \"Shared mutation requires a file path\");\n\tconst revision = params.expectedRevision as ExpectedWorkspaceRevision | undefined;\n\tif (!revision || (toolName === \"edit\" && !(\"sha256\" in revision))) {\n\t\tthrow revisionError(\n\t\t\t\"invalid_request\",\n\t\t\t\"Shared edit requires expected SHA-256; shared write requires expected SHA-256, expected absence or explicit unsafeOverwrite\",\n\t\t);\n\t}\n\tconst target = resolve(root, args.path);\n\treturn withFileMutationGuard((filePath) => checkRevision(filePath, target, revision, operations, context), action);\n}\n"]}
@@ -0,0 +1,9 @@
1
+ /** The parent mount is fixed; children can be created at any time. */
2
+ export declare class SharedWorkspaceRoots {
3
+ readonly parent: string;
4
+ private constructor();
5
+ static fromParent(parent: string): Promise<SharedWorkspaceRoots>;
6
+ bind(requested: string): Promise<string>;
7
+ private assertWithin;
8
+ }
9
+ //# sourceMappingURL=shared-roots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-roots.d.ts","sourceRoot":"","sources":["../../src/daemon/shared-roots.ts"],"names":[],"mappings":"AAGA,sEAAsE;AACtE,qBAAa,oBAAoB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,eAEN;IAED,OAAa,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAIrE;IAEK,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS7C;IAED,OAAO,CAAC,YAAY;CAMpB","sourcesContent":["import { realpath, stat } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\n\n/** The parent mount is fixed; children can be created at any time. */\nexport class SharedWorkspaceRoots {\n\treadonly parent: string;\n\tprivate constructor(parent: string) {\n\t\tthis.parent = parent;\n\t}\n\n\tstatic async fromParent(parent: string): Promise<SharedWorkspaceRoots> {\n\t\tconst canonical = await realpath(parent);\n\t\tif (!(await stat(canonical)).isDirectory()) throw new Error(\"Shared workspace parent is not a directory\");\n\t\treturn new SharedWorkspaceRoots(canonical);\n\t}\n\n\tasync bind(requested: string): Promise<string> {\n\t\tif (!isAbsolute(requested) || requested.includes(\"\\0\"))\n\t\t\tthrow new Error(\"Shared workspace root must be an absolute path\");\n\t\tconst candidate = resolve(requested);\n\t\tthis.assertWithin(candidate);\n\t\tconst canonical = await realpath(candidate);\n\t\tthis.assertWithin(canonical);\n\t\tif (!(await stat(canonical)).isDirectory()) throw new Error(\"Shared workspace root is not a directory\");\n\t\treturn canonical;\n\t}\n\n\tprivate assertWithin(path: string): void {\n\t\tconst suffix = relative(this.parent, path);\n\t\tif (suffix === \"..\" || suffix.startsWith(`..${sep}`) || isAbsolute(suffix)) {\n\t\t\tthrow new Error(\"Shared workspace root escapes the parent mount\");\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { realpath, stat } from "node:fs/promises";
2
+ import { isAbsolute, relative, resolve, sep } from "node:path";
3
+ /** The parent mount is fixed; children can be created at any time. */
4
+ export class SharedWorkspaceRoots {
5
+ parent;
6
+ constructor(parent) {
7
+ this.parent = parent;
8
+ }
9
+ static async fromParent(parent) {
10
+ const canonical = await realpath(parent);
11
+ if (!(await stat(canonical)).isDirectory())
12
+ throw new Error("Shared workspace parent is not a directory");
13
+ return new SharedWorkspaceRoots(canonical);
14
+ }
15
+ async bind(requested) {
16
+ if (!isAbsolute(requested) || requested.includes("\0"))
17
+ throw new Error("Shared workspace root must be an absolute path");
18
+ const candidate = resolve(requested);
19
+ this.assertWithin(candidate);
20
+ const canonical = await realpath(candidate);
21
+ this.assertWithin(canonical);
22
+ if (!(await stat(canonical)).isDirectory())
23
+ throw new Error("Shared workspace root is not a directory");
24
+ return canonical;
25
+ }
26
+ assertWithin(path) {
27
+ const suffix = relative(this.parent, path);
28
+ if (suffix === ".." || suffix.startsWith(`..${sep}`) || isAbsolute(suffix)) {
29
+ throw new Error("Shared workspace root escapes the parent mount");
30
+ }
31
+ }
32
+ }
33
+ //# sourceMappingURL=shared-roots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-roots.js","sourceRoot":"","sources":["../../src/daemon/shared-roots.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAE/D,sEAAsE;AACtE,MAAM,OAAO,oBAAoB;IACvB,MAAM,CAAS;IACxB,YAAoB,MAAc,EAAE;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAAA,CACrB;IAED,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAc,EAAiC;QACtE,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC1G,OAAO,IAAI,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAAA,CAC3C;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB,EAAmB;QAC9C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACnE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAC7B,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACxG,OAAO,SAAS,CAAC;IAAA,CACjB;IAEO,YAAY,CAAC,IAAY,EAAQ;QACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5E,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACnE,CAAC;IAAA,CACD;CACD","sourcesContent":["import { realpath, stat } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\n\n/** The parent mount is fixed; children can be created at any time. */\nexport class SharedWorkspaceRoots {\n\treadonly parent: string;\n\tprivate constructor(parent: string) {\n\t\tthis.parent = parent;\n\t}\n\n\tstatic async fromParent(parent: string): Promise<SharedWorkspaceRoots> {\n\t\tconst canonical = await realpath(parent);\n\t\tif (!(await stat(canonical)).isDirectory()) throw new Error(\"Shared workspace parent is not a directory\");\n\t\treturn new SharedWorkspaceRoots(canonical);\n\t}\n\n\tasync bind(requested: string): Promise<string> {\n\t\tif (!isAbsolute(requested) || requested.includes(\"\\0\"))\n\t\t\tthrow new Error(\"Shared workspace root must be an absolute path\");\n\t\tconst candidate = resolve(requested);\n\t\tthis.assertWithin(candidate);\n\t\tconst canonical = await realpath(candidate);\n\t\tthis.assertWithin(canonical);\n\t\tif (!(await stat(canonical)).isDirectory()) throw new Error(\"Shared workspace root is not a directory\");\n\t\treturn canonical;\n\t}\n\n\tprivate assertWithin(path: string): void {\n\t\tconst suffix = relative(this.parent, path);\n\t\tif (suffix === \"..\" || suffix.startsWith(`..${sep}`) || isAbsolute(suffix)) {\n\t\t\tthrow new Error(\"Shared workspace root escapes the parent mount\");\n\t\t}\n\t}\n}\n"]}
@@ -38,7 +38,7 @@ export interface DaemonWorkspaceRuntime {
38
38
  readonly lspRuntime?: LspRuntimeState;
39
39
  readonly catalog: RemoteWorkspaceCatalog;
40
40
  readonly capabilities: readonly RemoteWorkspaceCapability[];
41
- createHandler(workspace: RemoteWorkspaceIdentity): RemoteWorkspaceServerHandler;
41
+ createHandler(workspace: RemoteWorkspaceIdentity, sharedMode?: boolean): RemoteWorkspaceServerHandler;
42
42
  retire(): Promise<void>;
43
43
  dispose(): Promise<void>;
44
44
  }