@dexto/core 1.8.11 → 1.9.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.
Files changed (113) hide show
  1. package/dist/approval/manager.cjs +37 -325
  2. package/dist/approval/manager.d.ts +10 -109
  3. package/dist/approval/manager.d.ts.map +1 -1
  4. package/dist/approval/manager.js +37 -316
  5. package/dist/approval/schemas.cjs +8 -51
  6. package/dist/approval/schemas.d.ts +5 -179
  7. package/dist/approval/schemas.d.ts.map +1 -1
  8. package/dist/approval/schemas.js +8 -47
  9. package/dist/approval/types.cjs +0 -6
  10. package/dist/approval/types.d.ts +2 -27
  11. package/dist/approval/types.d.ts.map +1 -1
  12. package/dist/approval/types.js +0 -6
  13. package/dist/context/content-clone.cjs +12 -8
  14. package/dist/context/content-clone.d.ts.map +1 -1
  15. package/dist/context/content-clone.js +12 -8
  16. package/dist/errors/DextoRuntimeError.cjs +3 -1
  17. package/dist/errors/DextoRuntimeError.d.ts +5 -3
  18. package/dist/errors/DextoRuntimeError.d.ts.map +1 -1
  19. package/dist/errors/DextoRuntimeError.js +3 -1
  20. package/dist/errors/index.d.ts +1 -1
  21. package/dist/errors/index.d.ts.map +1 -1
  22. package/dist/errors/types.d.ts +1 -0
  23. package/dist/errors/types.d.ts.map +1 -1
  24. package/dist/events/index.d.ts +2 -2
  25. package/dist/events/index.d.ts.map +1 -1
  26. package/dist/llm/executor/provider-error.cjs +25 -4
  27. package/dist/llm/executor/provider-error.d.ts.map +1 -1
  28. package/dist/llm/executor/provider-error.js +25 -4
  29. package/dist/llm/executor/stream-processor.cjs +61 -0
  30. package/dist/llm/executor/stream-processor.d.ts.map +1 -1
  31. package/dist/llm/executor/stream-processor.js +61 -0
  32. package/dist/llm/executor/turn-executor.cjs +93 -26
  33. package/dist/llm/executor/turn-executor.d.ts +2 -0
  34. package/dist/llm/executor/turn-executor.d.ts.map +1 -1
  35. package/dist/llm/executor/turn-executor.js +93 -26
  36. package/dist/llm/formatters/vercel.cjs +66 -0
  37. package/dist/llm/formatters/vercel.d.ts.map +1 -1
  38. package/dist/llm/formatters/vercel.js +66 -0
  39. package/dist/llm/services/factory.cjs +3 -2
  40. package/dist/llm/services/factory.d.ts.map +1 -1
  41. package/dist/llm/services/factory.js +3 -2
  42. package/dist/llm/services/types.d.ts +8 -0
  43. package/dist/llm/services/types.d.ts.map +1 -1
  44. package/dist/llm/services/vercel.cjs +6 -1
  45. package/dist/llm/services/vercel.d.ts +3 -2
  46. package/dist/llm/services/vercel.d.ts.map +1 -1
  47. package/dist/llm/services/vercel.js +6 -1
  48. package/dist/session/chat-session.cjs +3 -0
  49. package/dist/session/chat-session.d.ts +2 -1
  50. package/dist/session/chat-session.d.ts.map +1 -1
  51. package/dist/session/chat-session.js +3 -0
  52. package/dist/session/message-queue.cjs +48 -75
  53. package/dist/session/message-queue.d.ts +1 -2
  54. package/dist/session/message-queue.d.ts.map +1 -1
  55. package/dist/session/message-queue.js +48 -75
  56. package/dist/session/session-manager.cjs +14 -32
  57. package/dist/session/session-manager.d.ts +5 -2
  58. package/dist/session/session-manager.d.ts.map +1 -1
  59. package/dist/session/session-manager.js +14 -32
  60. package/dist/storage/approvals/types.cjs +2 -9
  61. package/dist/storage/approvals/types.d.ts +4 -16
  62. package/dist/storage/approvals/types.d.ts.map +1 -1
  63. package/dist/storage/approvals/types.js +2 -8
  64. package/dist/storage/database/types.d.ts +5 -0
  65. package/dist/storage/database/types.d.ts.map +1 -1
  66. package/dist/storage/index.cjs +0 -2
  67. package/dist/storage/index.d.ts +2 -2
  68. package/dist/storage/index.d.ts.map +1 -1
  69. package/dist/storage/index.js +1 -2
  70. package/dist/storage/message-queue/types.d.ts +14 -6
  71. package/dist/storage/message-queue/types.d.ts.map +1 -1
  72. package/dist/storage/stores/backend.cjs +68 -32
  73. package/dist/storage/stores/backend.d.ts +17 -8
  74. package/dist/storage/stores/backend.d.ts.map +1 -1
  75. package/dist/storage/stores/backend.js +69 -33
  76. package/dist/storage/stores/in-memory.cjs +26 -11
  77. package/dist/storage/stores/in-memory.d.ts.map +1 -1
  78. package/dist/storage/stores/in-memory.js +27 -12
  79. package/dist/test-utils/in-memory-storage.cjs +11 -0
  80. package/dist/test-utils/in-memory-storage.js +11 -0
  81. package/dist/test-utils/session-state-stores.cjs +25 -9
  82. package/dist/test-utils/session-state-stores.js +26 -10
  83. package/dist/tools/approval/tool-approval-policy.cjs +87 -0
  84. package/dist/tools/approval/tool-approval-policy.d.ts +34 -0
  85. package/dist/tools/approval/tool-approval-policy.d.ts.map +1 -0
  86. package/dist/tools/approval/tool-approval-policy.js +64 -0
  87. package/dist/tools/error-codes.cjs +0 -1
  88. package/dist/tools/error-codes.d.ts +0 -1
  89. package/dist/tools/error-codes.d.ts.map +1 -1
  90. package/dist/tools/error-codes.js +0 -1
  91. package/dist/tools/errors.cjs +0 -15
  92. package/dist/tools/errors.d.ts +0 -9
  93. package/dist/tools/errors.d.ts.map +1 -1
  94. package/dist/tools/errors.js +0 -15
  95. package/dist/tools/index.cjs +2 -0
  96. package/dist/tools/index.d.ts +1 -1
  97. package/dist/tools/index.d.ts.map +1 -1
  98. package/dist/tools/index.js +5 -1
  99. package/dist/tools/tool-manager.cjs +94 -312
  100. package/dist/tools/tool-manager.d.ts +4 -19
  101. package/dist/tools/tool-manager.d.ts.map +1 -1
  102. package/dist/tools/tool-manager.js +95 -316
  103. package/dist/tools/types.d.ts +10 -9
  104. package/dist/tools/types.d.ts.map +1 -1
  105. package/dist/utils/service-initializer.cjs +3 -0
  106. package/dist/utils/service-initializer.d.ts +2 -1
  107. package/dist/utils/service-initializer.d.ts.map +1 -1
  108. package/dist/utils/service-initializer.js +3 -0
  109. package/package.json +2 -2
  110. package/dist/tools/pattern-utils.cjs +0 -33
  111. package/dist/tools/pattern-utils.d.ts +0 -20
  112. package/dist/tools/pattern-utils.d.ts.map +0 -1
  113. package/dist/tools/pattern-utils.js +0 -10
@@ -1,44 +1,16 @@
1
1
  import "../chunk-C6A6W6XS.js";
2
- import path from "node:path";
3
- import { realpathSync } from "node:fs";
4
2
  import { isDeepStrictEqual } from "node:util";
5
3
  import { ApprovalType, ApprovalStatus } from "./types.js";
6
4
  import {
7
5
  CommandApprovalResponseSchema,
8
6
  CustomApprovalResponseSchema,
9
- DirectoryAccessResponseSchema,
10
7
  ElicitationResponseSchema,
11
8
  ToolApprovalResponseSchema
12
9
  } from "./schemas.js";
13
10
  import { createApprovalRequest, createDeterministicApprovalId } from "./factory.js";
14
11
  import { DextoLogComponent } from "../logger/v2/types.js";
15
12
  import { ApprovalError } from "./errors.js";
16
- import { patternCovers } from "../tools/pattern-utils.js";
17
13
  const GLOBAL_APPROVAL_SCOPE = "__global__";
18
- function tryRealpathSync(targetPath) {
19
- try {
20
- return realpathSync(targetPath);
21
- } catch {
22
- return null;
23
- }
24
- }
25
- function tryRealpathSyncWithExistingParent(resolvedPath) {
26
- const direct = tryRealpathSync(resolvedPath);
27
- if (direct) return direct;
28
- let currentDir = path.dirname(resolvedPath);
29
- while (true) {
30
- const realDir = tryRealpathSync(currentDir);
31
- if (realDir) {
32
- const suffix = path.relative(currentDir, resolvedPath);
33
- return path.join(realDir, suffix);
34
- }
35
- const parent = path.dirname(currentDir);
36
- if (parent === currentDir) {
37
- return null;
38
- }
39
- currentDir = parent;
40
- }
41
- }
42
14
  class ApprovalManager {
43
15
  constructor(config, logger, approvalStore) {
44
16
  this.approvalStore = approvalStore;
@@ -72,8 +44,7 @@ class ApprovalManager {
72
44
  }
73
45
  createEmptyScopeState() {
74
46
  return {
75
- toolPatterns: /* @__PURE__ */ new Map(),
76
- approvedDirectories: /* @__PURE__ */ new Map()
47
+ approvedKeys: /* @__PURE__ */ new Map()
77
48
  };
78
49
  }
79
50
  getOrCreateScope(scopeKey) {
@@ -120,26 +91,13 @@ class ApprovalManager {
120
91
  }
121
92
  }
122
93
  }
123
- snapshotToolPatterns(scopeKey) {
124
- const snapshot = {};
125
- for (const [toolName, patterns] of this.getScope(scopeKey).toolPatterns) {
126
- snapshot[toolName] = Array.from(patterns);
127
- }
128
- return snapshot;
129
- }
130
- snapshotApprovedDirectories(scopeKey) {
131
- return Array.from(this.getScope(scopeKey).approvedDirectories.entries()).map(
132
- ([path2, type]) => ({
133
- path: path2,
134
- type
135
- })
136
- );
94
+ snapshotApprovedKeys(scopeKey) {
95
+ return Object.fromEntries(this.getScope(scopeKey).approvedKeys.entries());
137
96
  }
138
97
  async persistScope(sessionId) {
139
98
  const scopeKey = this.getScopeKey(sessionId);
140
99
  const state = {
141
- toolPatterns: this.snapshotToolPatterns(scopeKey),
142
- approvedDirectories: this.snapshotApprovedDirectories(scopeKey)
100
+ approvedKeys: this.snapshotApprovedKeys(scopeKey)
143
101
  };
144
102
  await this.approvalStore.saveSessionState({
145
103
  ...this.sessionScope(sessionId),
@@ -148,17 +106,12 @@ class ApprovalManager {
148
106
  }
149
107
  hydrateScope(sessionId, state) {
150
108
  const scopeKey = this.getScopeKey(sessionId);
151
- const toolPatterns = /* @__PURE__ */ new Map();
152
- for (const [toolName, patterns] of Object.entries(state.toolPatterns)) {
153
- toolPatterns.set(toolName, new Set(patterns));
154
- }
155
- const approvedDirectories = /* @__PURE__ */ new Map();
156
- for (const entry of state.approvedDirectories) {
157
- approvedDirectories.set(entry.path, entry.type);
109
+ const approvedKeys = /* @__PURE__ */ new Map();
110
+ for (const [key, type] of Object.entries(state.approvedKeys ?? {})) {
111
+ approvedKeys.set(key, type);
158
112
  }
159
113
  this.scopes.set(scopeKey, {
160
- toolPatterns,
161
- approvedDirectories
114
+ approvedKeys
162
115
  });
163
116
  }
164
117
  async restoreSessionState(sessionId) {
@@ -175,8 +128,7 @@ class ApprovalManager {
175
128
  this.loadedScopes.add(scopeKey);
176
129
  this.logger.debug("Restored persisted approval state", {
177
130
  sessionId: this.getScopeLabel(sessionId),
178
- toolCount: Object.keys(state.toolPatterns).length,
179
- directoryCount: state.approvedDirectories.length
131
+ keyCount: Object.keys(state.approvedKeys ?? {}).length
180
132
  });
181
133
  });
182
134
  }
@@ -192,94 +144,37 @@ class ApprovalManager {
192
144
  await this.approvalStore.deleteSessionState(this.sessionScope(sessionId));
193
145
  });
194
146
  }
195
- // ==================== Pattern Methods ====================
196
- getOrCreateToolPatternSet(toolName, scopeKey) {
197
- const scope = this.getOrCreateScope(scopeKey).toolPatterns;
198
- const existing = scope.get(toolName);
199
- if (existing) return existing;
200
- const created = /* @__PURE__ */ new Set();
201
- scope.set(toolName, created);
202
- return created;
203
- }
204
- /**
205
- * Add an approval pattern for a tool.
206
- */
207
- async addPattern(toolName, pattern, sessionId) {
147
+ // ==================== Generic Approval Key Methods ====================
148
+ async addApprovedKey(key, type = "session", sessionId) {
208
149
  await this.restoreSessionState(sessionId);
209
150
  const scopeKey = this.getScopeKey(sessionId);
210
151
  await this.runWithScopeLock(scopeKey, async () => {
211
- this.getOrCreateToolPatternSet(toolName, scopeKey).add(pattern);
152
+ const approvedKeys = this.getOrCreateScope(scopeKey).approvedKeys;
153
+ const existing = approvedKeys.get(key);
154
+ const effectiveType = type === "session" || existing === "session" ? "session" : "once";
155
+ approvedKeys.set(key, effectiveType);
212
156
  await this.persistScope(sessionId);
213
157
  });
214
- this.logger.debug(
215
- `Added pattern for '${toolName}' in '${this.getScopeLabel(sessionId)}': "${pattern}"`
216
- );
217
158
  }
218
- /**
219
- * Check if a pattern key is covered by any approved pattern for a tool.
220
- *
221
- * Note: This expects a pattern key (e.g. "git push *"), not raw arguments.
222
- * Tools are responsible for generating the key via `tool.approval.patternKey()`.
223
- */
224
- matchesPattern(toolName, patternKey, sessionId) {
225
- const scopeKey = this.getScopeKey(sessionId);
226
- const patterns = this.getScope(scopeKey).toolPatterns.get(toolName);
227
- if (!patterns || patterns.size === 0) return false;
228
- for (const storedPattern of patterns) {
229
- if (patternCovers(storedPattern, patternKey)) {
230
- this.logger.debug(
231
- `Pattern key "${patternKey}" is covered by approved pattern "${storedPattern}" (tool: ${toolName})`
232
- );
233
- return true;
234
- }
235
- }
236
- return false;
159
+ isApprovalKeySessionApproved(key, sessionId) {
160
+ return this.getScope(this.getScopeKey(sessionId)).approvedKeys.get(key) === "session";
237
161
  }
238
- /**
239
- * Clear all patterns for a tool (or all tools when omitted).
240
- */
241
- async clearPatterns(toolName, sessionId) {
162
+ isApprovalKeyApproved(key, sessionId) {
163
+ return this.getScope(this.getScopeKey(sessionId)).approvedKeys.has(key);
164
+ }
165
+ getApprovedKeys(sessionId) {
166
+ return this.getScope(this.getScopeKey(sessionId)).approvedKeys;
167
+ }
168
+ async clearApprovedKeys(sessionId) {
242
169
  await this.restoreSessionState(sessionId);
243
170
  const scopeKey = this.getScopeKey(sessionId);
244
171
  await this.runWithScopeLock(scopeKey, async () => {
245
- const scope = this.getOrCreateScope(scopeKey).toolPatterns;
246
- if (toolName) {
247
- const patterns = scope.get(toolName);
248
- if (!patterns) return;
249
- const count2 = patterns.size;
250
- scope.delete(toolName);
251
- await this.persistScope(sessionId);
252
- if (count2 > 0) {
253
- this.logger.debug(
254
- `Cleared ${count2} pattern(s) for '${toolName}' in '${this.getScopeLabel(sessionId)}'`
255
- );
256
- }
257
- return;
258
- }
259
- const count = Array.from(scope.values()).reduce((sum, set) => sum + set.size, 0);
260
- scope.clear();
172
+ const approvedKeys = this.getOrCreateScope(scopeKey).approvedKeys;
173
+ if (approvedKeys.size === 0) return;
174
+ approvedKeys.clear();
261
175
  await this.persistScope(sessionId);
262
- if (count > 0) {
263
- this.logger.debug(
264
- `Cleared ${count} total tool pattern(s) in '${this.getScopeLabel(sessionId)}'`
265
- );
266
- }
267
176
  });
268
177
  }
269
- /**
270
- * Get patterns for a tool (for debugging/display).
271
- */
272
- getToolPatterns(toolName, sessionId) {
273
- const scopeKey = this.getScopeKey(sessionId);
274
- return this.getScope(scopeKey).toolPatterns.get(toolName) ?? /* @__PURE__ */ new Set();
275
- }
276
- /**
277
- * Get all tool patterns (for debugging/display).
278
- */
279
- getAllToolPatterns(sessionId) {
280
- const scopeKey = this.getScopeKey(sessionId);
281
- return this.getScope(scopeKey).toolPatterns;
282
- }
283
178
  // ==================== Directory Access Methods ====================
284
179
  /**
285
180
  * Resolve a directory path for use as an approval key.
@@ -288,150 +183,8 @@ class ApprovalManager {
288
183
  * continue to work even when other subsystems canonicalize paths via realpath
289
184
  * (e.g. macOS /tmp -> /private/tmp or custom symlinked directories).
290
185
  */
291
- getPathApprovalKeys(targetPath) {
292
- const resolved = path.resolve(targetPath);
293
- const real = tryRealpathSyncWithExistingParent(resolved);
294
- if (real && real !== resolved) {
295
- return [resolved, real];
296
- }
297
- return [resolved];
298
- }
299
- isPathWithinApprovedDirectory(targetPath, sessionId, approvedTypes) {
300
- const scopeKey = this.getScopeKey(sessionId);
301
- const directoryScope = this.getScope(scopeKey).approvedDirectories;
302
- for (const normalized of this.getPathApprovalKeys(targetPath)) {
303
- for (const [approvedDir, type] of directoryScope) {
304
- if (!approvedTypes.has(type)) {
305
- continue;
306
- }
307
- const relative = path.relative(approvedDir, normalized);
308
- if (!relative.startsWith("..") && !path.isAbsolute(relative)) {
309
- this.logger.debug(
310
- `Path "${normalized}" is within approved directory "${approvedDir}" (type: ${type})`
311
- );
312
- return true;
313
- }
314
- }
315
- }
316
- return false;
317
- }
318
- /**
319
- * Initialize the working directory as a session-approved directory.
320
- * This should be called once during setup to ensure the working directory
321
- * never triggers directory access prompts.
322
- *
323
- * @param workingDir The working directory path
324
- */
325
- async initializeWorkingDirectory(workingDir, sessionId) {
326
- await this.addApprovedDirectory(workingDir, "session", sessionId);
327
- }
328
- /**
329
- * Add a directory to the approved list for this session.
330
- * Files within this directory (including subdirectories) will be allowed.
331
- *
332
- * @param directory Absolute path to the directory to approve
333
- * @param type The approval type:
334
- * - 'session': No directory prompt on future accesses, follows tool config
335
- * - 'once': Will prompt again on future accesses, but tool can execute this time
336
- * @example
337
- * ```typescript
338
- * manager.addApprovedDirectory("/external/project", 'session');
339
- * // Now /external/project/src/file.ts is accessible without directory prompt
340
- *
341
- * manager.addApprovedDirectory("/tmp/files", 'once');
342
- * // Tool can access, but will prompt again next time
343
- * ```
344
- */
345
- async addApprovedDirectory(directory, type = "session", sessionId) {
346
- await this.restoreSessionState(sessionId);
347
- const scopeKey = this.getScopeKey(sessionId);
348
- await this.runWithScopeLock(scopeKey, async () => {
349
- const keys = this.getPathApprovalKeys(directory);
350
- const directoryScope = this.getOrCreateScope(scopeKey).approvedDirectories;
351
- const existingTypes = keys.map((key) => directoryScope.get(key)).filter((value) => value !== void 0);
352
- const hasSessionApproval = existingTypes.includes("session");
353
- const effectiveType = type === "session" || hasSessionApproval ? "session" : "once";
354
- for (const key of keys) {
355
- const existing = directoryScope.get(key);
356
- if (existing === "session") {
357
- continue;
358
- }
359
- directoryScope.set(key, effectiveType);
360
- }
361
- await this.persistScope(sessionId);
362
- const resolvedKey = keys[0];
363
- if (effectiveType === "session" && type === "once" && hasSessionApproval) {
364
- this.logger.debug(
365
- `Directory "${resolvedKey}" already approved as 'session', not downgrading to 'once'`
366
- );
367
- return;
368
- }
369
- const realKey = keys.length > 1 ? keys[1] : null;
370
- this.logger.debug(
371
- `Added approved directory in '${this.getScopeLabel(sessionId)}': "${resolvedKey}" (type: ${effectiveType})${realKey ? `, realpath: "${realKey}"` : ""}`
372
- );
373
- });
374
- }
375
186
  /**
376
- * Check if a file path is within any session-approved directory.
377
- * This is used for PROMPTING decisions - only 'session' type directories count.
378
- * Working directory and user session-approved directories return true.
379
- *
380
- * @param filePath The file path to check (can be relative or absolute)
381
- * @returns true if the path is within a session-approved directory
382
- */
383
- isDirectorySessionApproved(filePath, sessionId) {
384
- return this.isPathWithinApprovedDirectory(filePath, sessionId, /* @__PURE__ */ new Set(["session"]));
385
- }
386
- /**
387
- * Check if a file path is within any approved directory (session OR once).
388
- * This is used for EXECUTION decisions - both 'session' and 'once' types count.
389
- * PathValidator uses this to determine if a tool can access the path.
390
- *
391
- * @param filePath The file path to check (can be relative or absolute)
392
- * @returns true if the path is within any approved directory
393
- */
394
- isDirectoryApproved(filePath, sessionId) {
395
- return this.isPathWithinApprovedDirectory(
396
- filePath,
397
- sessionId,
398
- /* @__PURE__ */ new Set(["session", "once"])
399
- );
400
- }
401
- /**
402
- * Clear all approved directories.
403
- * Should be called when session ends.
404
- */
405
- async clearApprovedDirectories(sessionId) {
406
- await this.restoreSessionState(sessionId);
407
- const scopeKey = this.getScopeKey(sessionId);
408
- await this.runWithScopeLock(scopeKey, async () => {
409
- const scope = this.getOrCreateScope(scopeKey).approvedDirectories;
410
- const count = scope.size;
411
- scope.clear();
412
- await this.persistScope(sessionId);
413
- if (count > 0) {
414
- this.logger.debug(
415
- `Cleared ${count} approved directories in '${this.getScopeLabel(sessionId)}'`
416
- );
417
- }
418
- });
419
- }
420
- /**
421
- * Get the current map of approved directories with their types (for debugging/display).
422
- */
423
- getApprovedDirectories(sessionId) {
424
- const scopeKey = this.getScopeKey(sessionId);
425
- return this.getScope(scopeKey).approvedDirectories;
426
- }
427
- /**
428
- * Get just the directory paths that are approved (for debugging/display).
429
- */
430
- getApprovedDirectoryPaths(sessionId) {
431
- return Array.from(this.getApprovedDirectories(sessionId).keys());
432
- }
433
- /**
434
- * Clear all session-scoped approvals (tool patterns and directories).
187
+ * Clear all session-scoped approvals.
435
188
  * Convenience method for clearing all session state at once.
436
189
  */
437
190
  async clearSessionApprovals(sessionId) {
@@ -439,17 +192,12 @@ class ApprovalManager {
439
192
  const scopeKey = this.getScopeKey(sessionId);
440
193
  await this.runWithScopeLock(scopeKey, async () => {
441
194
  const scope = this.getOrCreateScope(scopeKey);
442
- const patternCount = Array.from(scope.toolPatterns.values()).reduce(
443
- (sum, set) => sum + set.size,
444
- 0
445
- );
446
- const directoryCount = scope.approvedDirectories.size;
447
- scope.toolPatterns.clear();
448
- scope.approvedDirectories.clear();
195
+ const keyCount = scope.approvedKeys.size;
196
+ scope.approvedKeys.clear();
449
197
  await this.persistScope(sessionId);
450
- if (patternCount > 0 || directoryCount > 0) {
198
+ if (keyCount > 0) {
451
199
  this.logger.debug(
452
- `Cleared ${patternCount} tool pattern(s) and ${directoryCount} approved director${directoryCount === 1 ? "y" : "ies"} in '${this.getScopeLabel(sessionId)}'`
200
+ `Cleared ${keyCount} approval key(s) in '${this.getScopeLabel(sessionId)}'`
453
201
  );
454
202
  }
455
203
  });
@@ -529,8 +277,6 @@ class ApprovalManager {
529
277
  return ElicitationResponseSchema.parse(response);
530
278
  case ApprovalType.CUSTOM:
531
279
  return CustomApprovalResponseSchema.parse(response);
532
- case ApprovalType.DIRECTORY_ACCESS:
533
- return DirectoryAccessResponseSchema.parse(response);
534
280
  }
535
281
  }
536
282
  getElicitationFormData(response) {
@@ -546,33 +292,6 @@ class ApprovalManager {
546
292
  field: "formData"
547
293
  });
548
294
  }
549
- /**
550
- * Request directory access approval.
551
- * Convenience method for directory access requests.
552
- *
553
- * @example
554
- * ```typescript
555
- * const response = await manager.requestDirectoryAccess({
556
- * path: '/external/project/src/file.ts',
557
- * parentDir: '/external/project',
558
- * operation: 'write',
559
- * toolName: 'write_file',
560
- * sessionId: 'session-123'
561
- * });
562
- * ```
563
- */
564
- async requestDirectoryAccess(metadata) {
565
- const { sessionId, hostRuntime, timeout, ...directoryMetadata } = metadata;
566
- return this.requestApproval(
567
- this.createApprovalDetails(
568
- ApprovalType.DIRECTORY_ACCESS,
569
- directoryMetadata,
570
- sessionId,
571
- hostRuntime,
572
- timeout
573
- )
574
- );
575
- }
576
295
  /**
577
296
  * Request a generic approval
578
297
  */
@@ -824,8 +543,8 @@ class ApprovalManager {
824
543
  }
825
544
  /**
826
545
  * Auto-approve pending requests that match a predicate.
827
- * Used when a pattern is remembered to auto-approve other parallel requests
828
- * that would now match the same pattern.
546
+ * Used when a remembered approval should auto-approve other parallel requests
547
+ * selected by the caller.
829
548
  *
830
549
  * @param predicate Function that returns true for requests that should be auto-approved
831
550
  * @param responseData Optional data to include in the auto-approval response
@@ -834,7 +553,9 @@ class ApprovalManager {
834
553
  autoApprovePendingRequests(predicate, responseData) {
835
554
  const count = this.handler?.autoApprovePending?.(predicate, responseData) ?? 0;
836
555
  if (count > 0) {
837
- this.logger.info(`Auto-approved ${count} pending request(s) due to matching pattern`);
556
+ this.logger.info(
557
+ `Auto-approved ${count} pending request(s) due to remembered approval`
558
+ );
838
559
  }
839
560
  return count;
840
561
  }
@@ -34,10 +34,6 @@ __export(schemas_exports, {
34
34
  CustomApprovalResponseDataSchema: () => CustomApprovalResponseDataSchema,
35
35
  CustomApprovalResponseSchema: () => CustomApprovalResponseSchema,
36
36
  DenialReasonSchema: () => DenialReasonSchema,
37
- DirectoryAccessMetadataSchema: () => DirectoryAccessMetadataSchema,
38
- DirectoryAccessRequestSchema: () => DirectoryAccessRequestSchema,
39
- DirectoryAccessResponseDataSchema: () => DirectoryAccessResponseDataSchema,
40
- DirectoryAccessResponseSchema: () => DirectoryAccessResponseSchema,
41
37
  ElicitationMetadataSchema: () => ElicitationMetadataSchema,
42
38
  ElicitationRequestSchema: () => ElicitationRequestSchema,
43
39
  ElicitationResponseDataSchema: () => ElicitationResponseDataSchema,
@@ -65,14 +61,11 @@ const ToolPresentationSnapshotV1Schema = import_zod.z.custom(
65
61
  message: "Invalid ToolPresentationSnapshotV1"
66
62
  }
67
63
  );
68
- const DirectoryAccessMetadataSchema = import_zod.z.object({
69
- path: import_zod.z.string().describe("Full path being accessed"),
70
- parentDir: import_zod.z.string().describe("Parent directory (what gets approved for session)"),
71
- operation: import_zod.z.enum(["read", "write", "edit"]).describe("Type of file operation"),
72
- toolName: import_zod.z.string().describe("Name of the tool requesting access")
73
- }).strict().describe("Directory access metadata");
74
64
  const ToolApprovalMetadataSchema = import_zod.z.object({
75
65
  toolName: import_zod.z.string().describe("Name of the tool to approve"),
66
+ approvalKey: import_zod.z.string().min(1).optional().describe(
67
+ "Optional opaque key identifying the approval scope. Core stores keys exactly and does not interpret them."
68
+ ),
76
69
  presentationSnapshot: ToolPresentationSnapshotV1Schema.optional().describe(
77
70
  "Optional UI-agnostic presentation snapshot for the tool call. Clients MUST ignore unknown fields."
78
71
  ),
@@ -81,12 +74,6 @@ const ToolApprovalMetadataSchema = import_zod.z.object({
81
74
  description: import_zod.z.string().optional().describe("Description of the tool"),
82
75
  displayPreview: ToolDisplayDataSchema.optional().describe(
83
76
  "Preview display data for approval UI (e.g., diff preview)"
84
- ),
85
- directoryAccess: DirectoryAccessMetadataSchema.optional().describe(
86
- "Optional directory access metadata when the tool targets a path outside config-allowed roots"
87
- ),
88
- suggestedPatterns: import_zod.z.array(import_zod.z.string()).optional().describe(
89
- 'Suggested patterns for session approval. Tools may provide patterns to allow approving a broader subset of future calls (e.g., ["git push *", "git *"]).'
90
77
  )
91
78
  }).strict().describe("Tool approval metadata");
92
79
  const CommandApprovalMetadataSchema = import_zod.z.object({
@@ -127,24 +114,15 @@ const CustomApprovalRequestSchema = BaseApprovalRequestSchema.extend({
127
114
  type: import_zod.z.literal(import_types.ApprovalType.CUSTOM),
128
115
  metadata: CustomApprovalMetadataSchema
129
116
  }).strict();
130
- const DirectoryAccessRequestSchema = BaseApprovalRequestSchema.extend({
131
- type: import_zod.z.literal(import_types.ApprovalType.DIRECTORY_ACCESS),
132
- metadata: DirectoryAccessMetadataSchema
133
- }).strict();
134
117
  const ApprovalRequestSchema = import_zod.z.discriminatedUnion("type", [
135
118
  ToolApprovalRequestSchema,
136
119
  CommandApprovalRequestSchema,
137
120
  ElicitationRequestSchema,
138
- CustomApprovalRequestSchema,
139
- DirectoryAccessRequestSchema
121
+ CustomApprovalRequestSchema
140
122
  ]);
141
123
  const ToolApprovalResponseDataSchema = import_zod.z.object({
142
- rememberChoice: import_zod.z.boolean().optional().describe("Remember this tool for the session (approves ALL uses of this tool)"),
143
- rememberPattern: import_zod.z.string().optional().describe(
144
- 'Remember an approval pattern (e.g., "git *"). Only applicable when the tool provides pattern-based approval support.'
145
- ),
146
- rememberDirectory: import_zod.z.boolean().optional().describe(
147
- "Remember this directory for the session (allows future access without prompting again)"
124
+ rememberChoice: import_zod.z.boolean().optional().describe(
125
+ "Remember this approval scope for the session. If an approval key is present, only matching keys are remembered; otherwise all uses of this tool are approved."
148
126
  )
149
127
  }).strict().describe("Tool approval response data");
150
128
  const CommandApprovalResponseDataSchema = import_zod.z.object({
@@ -155,9 +133,6 @@ const ElicitationResponseDataSchema = import_zod.z.object({
155
133
  formData: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).describe("Form data matching schema")
156
134
  }).strict().describe("Elicitation response data");
157
135
  const CustomApprovalResponseDataSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).describe("Custom response data");
158
- const DirectoryAccessResponseDataSchema = import_zod.z.object({
159
- rememberDirectory: import_zod.z.boolean().optional().describe("Remember this directory for the session (allows all file access within it)")
160
- }).strict().describe("Directory access response data");
161
136
  const BaseApprovalResponseSchema = import_zod.z.object({
162
137
  approvalId: import_zod.z.string().uuid().describe("Must match request approvalId"),
163
138
  status: ApprovalStatusSchema.describe("Approval status"),
@@ -183,15 +158,11 @@ const ElicitationResponseSchema = BaseApprovalResponseSchema.extend({
183
158
  const CustomApprovalResponseSchema = BaseApprovalResponseSchema.extend({
184
159
  data: CustomApprovalResponseDataSchema.optional()
185
160
  }).strict();
186
- const DirectoryAccessResponseSchema = BaseApprovalResponseSchema.extend({
187
- data: DirectoryAccessResponseDataSchema.optional()
188
- }).strict();
189
161
  const ApprovalResponseSchema = import_zod.z.union([
190
162
  ToolApprovalResponseSchema,
191
163
  CommandApprovalResponseSchema,
192
164
  ElicitationResponseSchema,
193
- CustomApprovalResponseSchema,
194
- DirectoryAccessResponseSchema
165
+ CustomApprovalResponseSchema
195
166
  ]);
196
167
  const ApprovalRequestDetailsSchema = import_zod.z.object({
197
168
  type: ApprovalTypeSchema,
@@ -204,8 +175,7 @@ const ApprovalRequestDetailsSchema = import_zod.z.object({
204
175
  ToolApprovalMetadataSchema,
205
176
  CommandApprovalMetadataSchema,
206
177
  ElicitationMetadataSchema,
207
- CustomApprovalMetadataSchema,
208
- DirectoryAccessMetadataSchema
178
+ CustomApprovalMetadataSchema
209
179
  ])
210
180
  }).superRefine((data, ctx) => {
211
181
  if (data.type === import_types.ApprovalType.TOOL_APPROVAL) {
@@ -235,15 +205,6 @@ const ApprovalRequestDetailsSchema = import_zod.z.object({
235
205
  path: ["metadata"]
236
206
  });
237
207
  }
238
- } else if (data.type === import_types.ApprovalType.DIRECTORY_ACCESS) {
239
- const result = DirectoryAccessMetadataSchema.safeParse(data.metadata);
240
- if (!result.success) {
241
- ctx.addIssue({
242
- code: import_zod.z.ZodIssueCode.custom,
243
- message: "Metadata must match DirectoryAccessMetadataSchema for DIRECTORY_ACCESS type",
244
- path: ["metadata"]
245
- });
246
- }
247
208
  } else if (data.type === import_types.ApprovalType.CUSTOM) {
248
209
  const result = CustomApprovalMetadataSchema.safeParse(data.metadata);
249
210
  if (!result.success) {
@@ -273,10 +234,6 @@ const ApprovalRequestDetailsSchema = import_zod.z.object({
273
234
  CustomApprovalResponseDataSchema,
274
235
  CustomApprovalResponseSchema,
275
236
  DenialReasonSchema,
276
- DirectoryAccessMetadataSchema,
277
- DirectoryAccessRequestSchema,
278
- DirectoryAccessResponseDataSchema,
279
- DirectoryAccessResponseSchema,
280
237
  ElicitationMetadataSchema,
281
238
  ElicitationRequestSchema,
282
239
  ElicitationResponseDataSchema,