agent-world 0.13.0 → 0.15.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 (263) hide show
  1. package/README.md +90 -17
  2. package/dist/cli/commands.d.ts +7 -1
  3. package/dist/cli/commands.js +27 -10
  4. package/dist/cli/hitl.d.ts +4 -1
  5. package/dist/cli/hitl.js +55 -20
  6. package/dist/cli/index.js +249 -97
  7. package/dist/cli/system-events.d.ts +27 -0
  8. package/dist/cli/system-events.js +63 -0
  9. package/dist/core/activity-tracker.d.ts +26 -0
  10. package/dist/core/activity-tracker.d.ts.map +1 -1
  11. package/dist/core/activity-tracker.js +21 -4
  12. package/dist/core/activity-tracker.js.map +1 -1
  13. package/dist/core/anthropic-direct.d.ts +2 -0
  14. package/dist/core/anthropic-direct.d.ts.map +1 -1
  15. package/dist/core/anthropic-direct.js +43 -1
  16. package/dist/core/anthropic-direct.js.map +1 -1
  17. package/dist/core/chat-constants.d.ts +12 -0
  18. package/dist/core/chat-constants.d.ts.map +1 -1
  19. package/dist/core/chat-constants.js +5 -0
  20. package/dist/core/chat-constants.js.map +1 -1
  21. package/dist/core/create-agent-tool.d.ts +5 -0
  22. package/dist/core/create-agent-tool.d.ts.map +1 -1
  23. package/dist/core/create-agent-tool.js +57 -34
  24. package/dist/core/create-agent-tool.js.map +1 -1
  25. package/dist/core/events/index.d.ts +5 -2
  26. package/dist/core/events/index.d.ts.map +1 -1
  27. package/dist/core/events/index.js +5 -2
  28. package/dist/core/events/index.js.map +1 -1
  29. package/dist/core/events/memory-manager.d.ts +26 -1
  30. package/dist/core/events/memory-manager.d.ts.map +1 -1
  31. package/dist/core/events/memory-manager.js +877 -72
  32. package/dist/core/events/memory-manager.js.map +1 -1
  33. package/dist/core/events/orchestrator.d.ts +8 -0
  34. package/dist/core/events/orchestrator.d.ts.map +1 -1
  35. package/dist/core/events/orchestrator.js +203 -36
  36. package/dist/core/events/orchestrator.js.map +1 -1
  37. package/dist/core/events/persistence.d.ts +21 -14
  38. package/dist/core/events/persistence.d.ts.map +1 -1
  39. package/dist/core/events/persistence.js +100 -35
  40. package/dist/core/events/persistence.js.map +1 -1
  41. package/dist/core/events/publishers.d.ts +13 -7
  42. package/dist/core/events/publishers.d.ts.map +1 -1
  43. package/dist/core/events/publishers.js +53 -37
  44. package/dist/core/events/publishers.js.map +1 -1
  45. package/dist/core/events/subscribers.d.ts +17 -14
  46. package/dist/core/events/subscribers.d.ts.map +1 -1
  47. package/dist/core/events/subscribers.js +61 -148
  48. package/dist/core/events/subscribers.js.map +1 -1
  49. package/dist/core/events/title-scheduler.d.ts +27 -0
  50. package/dist/core/events/title-scheduler.d.ts.map +1 -0
  51. package/dist/core/events/title-scheduler.js +135 -0
  52. package/dist/core/events/title-scheduler.js.map +1 -0
  53. package/dist/core/events/tool-bridge-logging.d.ts +4 -1
  54. package/dist/core/events/tool-bridge-logging.d.ts.map +1 -1
  55. package/dist/core/events/tool-bridge-logging.js +112 -13
  56. package/dist/core/events/tool-bridge-logging.js.map +1 -1
  57. package/dist/core/events-metadata.d.ts.map +1 -1
  58. package/dist/core/events-metadata.js +8 -4
  59. package/dist/core/events-metadata.js.map +1 -1
  60. package/dist/core/export.d.ts +1 -1
  61. package/dist/core/export.d.ts.map +1 -1
  62. package/dist/core/export.js +2 -15
  63. package/dist/core/export.js.map +1 -1
  64. package/dist/core/feature-path-logging.d.ts +50 -0
  65. package/dist/core/feature-path-logging.d.ts.map +1 -0
  66. package/dist/core/feature-path-logging.js +130 -0
  67. package/dist/core/feature-path-logging.js.map +1 -0
  68. package/dist/core/file-tools.d.ts +57 -1
  69. package/dist/core/file-tools.d.ts.map +1 -1
  70. package/dist/core/file-tools.js +329 -29
  71. package/dist/core/file-tools.js.map +1 -1
  72. package/dist/core/google-direct.d.ts +6 -1
  73. package/dist/core/google-direct.d.ts.map +1 -1
  74. package/dist/core/google-direct.js +76 -7
  75. package/dist/core/google-direct.js.map +1 -1
  76. package/dist/core/heartbeat.d.ts +34 -0
  77. package/dist/core/heartbeat.d.ts.map +1 -0
  78. package/dist/core/heartbeat.js +153 -0
  79. package/dist/core/heartbeat.js.map +1 -0
  80. package/dist/core/hitl-tool.d.ts +6 -12
  81. package/dist/core/hitl-tool.d.ts.map +1 -1
  82. package/dist/core/hitl-tool.js +66 -88
  83. package/dist/core/hitl-tool.js.map +1 -1
  84. package/dist/core/hitl.d.ts +61 -4
  85. package/dist/core/hitl.d.ts.map +1 -1
  86. package/dist/core/hitl.js +324 -60
  87. package/dist/core/hitl.js.map +1 -1
  88. package/dist/core/index.d.ts +11 -7
  89. package/dist/core/index.d.ts.map +1 -1
  90. package/dist/core/index.js +10 -6
  91. package/dist/core/index.js.map +1 -1
  92. package/dist/core/llm-manager.d.ts +15 -0
  93. package/dist/core/llm-manager.d.ts.map +1 -1
  94. package/dist/core/llm-manager.js +325 -40
  95. package/dist/core/llm-manager.js.map +1 -1
  96. package/dist/core/load-skill-tool.d.ts +36 -3
  97. package/dist/core/load-skill-tool.d.ts.map +1 -1
  98. package/dist/core/load-skill-tool.js +807 -93
  99. package/dist/core/load-skill-tool.js.map +1 -1
  100. package/dist/core/logger.d.ts +14 -0
  101. package/dist/core/logger.d.ts.map +1 -1
  102. package/dist/core/logger.js +15 -0
  103. package/dist/core/logger.js.map +1 -1
  104. package/dist/core/managers.d.ts +18 -50
  105. package/dist/core/managers.d.ts.map +1 -1
  106. package/dist/core/managers.js +340 -502
  107. package/dist/core/managers.js.map +1 -1
  108. package/dist/core/mcp-server-registry.d.ts +16 -1
  109. package/dist/core/mcp-server-registry.d.ts.map +1 -1
  110. package/dist/core/mcp-server-registry.js +162 -12
  111. package/dist/core/mcp-server-registry.js.map +1 -1
  112. package/dist/core/message-cutoff.d.ts +29 -0
  113. package/dist/core/message-cutoff.d.ts.map +1 -0
  114. package/dist/core/message-cutoff.js +63 -0
  115. package/dist/core/message-cutoff.js.map +1 -0
  116. package/dist/core/message-edit-manager.d.ts +54 -0
  117. package/dist/core/message-edit-manager.d.ts.map +1 -0
  118. package/dist/core/message-edit-manager.js +602 -0
  119. package/dist/core/message-edit-manager.js.map +1 -0
  120. package/dist/core/message-prep.d.ts +2 -0
  121. package/dist/core/message-prep.d.ts.map +1 -1
  122. package/dist/core/message-prep.js +39 -12
  123. package/dist/core/message-prep.js.map +1 -1
  124. package/dist/core/message-processing-control.d.ts +1 -0
  125. package/dist/core/message-processing-control.d.ts.map +1 -1
  126. package/dist/core/message-processing-control.js +23 -6
  127. package/dist/core/message-processing-control.js.map +1 -1
  128. package/dist/core/openai-direct.d.ts +9 -3
  129. package/dist/core/openai-direct.d.ts.map +1 -1
  130. package/dist/core/openai-direct.js +267 -33
  131. package/dist/core/openai-direct.js.map +1 -1
  132. package/dist/core/optional-tracers/opik-runtime.d.ts +32 -0
  133. package/dist/core/optional-tracers/opik-runtime.d.ts.map +1 -0
  134. package/dist/core/optional-tracers/opik-runtime.js +141 -0
  135. package/dist/core/optional-tracers/opik-runtime.js.map +1 -0
  136. package/dist/core/queue-manager.d.ts +84 -0
  137. package/dist/core/queue-manager.d.ts.map +1 -0
  138. package/dist/core/queue-manager.js +814 -0
  139. package/dist/core/queue-manager.js.map +1 -0
  140. package/dist/core/reasoning-controls.d.ts +30 -0
  141. package/dist/core/reasoning-controls.d.ts.map +1 -0
  142. package/dist/core/reasoning-controls.js +118 -0
  143. package/dist/core/reasoning-controls.js.map +1 -0
  144. package/dist/core/reliability-config.d.ts +82 -0
  145. package/dist/core/reliability-config.d.ts.map +1 -0
  146. package/dist/core/reliability-config.js +106 -0
  147. package/dist/core/reliability-config.js.map +1 -0
  148. package/dist/core/reliability-runtime.d.ts +53 -0
  149. package/dist/core/reliability-runtime.d.ts.map +1 -0
  150. package/dist/core/reliability-runtime.js +92 -0
  151. package/dist/core/reliability-runtime.js.map +1 -0
  152. package/dist/core/security/guardrails.d.ts +21 -0
  153. package/dist/core/security/guardrails.d.ts.map +1 -0
  154. package/dist/core/security/guardrails.js +111 -0
  155. package/dist/core/security/guardrails.js.map +1 -0
  156. package/dist/core/send-message-tool.d.ts +79 -0
  157. package/dist/core/send-message-tool.d.ts.map +1 -0
  158. package/dist/core/send-message-tool.js +222 -0
  159. package/dist/core/send-message-tool.js.map +1 -0
  160. package/dist/core/shell-cmd-tool.d.ts +82 -1
  161. package/dist/core/shell-cmd-tool.d.ts.map +1 -1
  162. package/dist/core/shell-cmd-tool.js +854 -42
  163. package/dist/core/shell-cmd-tool.js.map +1 -1
  164. package/dist/core/skill-registry.d.ts +2 -0
  165. package/dist/core/skill-registry.d.ts.map +1 -1
  166. package/dist/core/skill-registry.js +52 -2
  167. package/dist/core/skill-registry.js.map +1 -1
  168. package/dist/core/storage/eventStorage/fileEventStorage.d.ts +5 -0
  169. package/dist/core/storage/eventStorage/fileEventStorage.d.ts.map +1 -1
  170. package/dist/core/storage/eventStorage/fileEventStorage.js +61 -0
  171. package/dist/core/storage/eventStorage/fileEventStorage.js.map +1 -1
  172. package/dist/core/storage/eventStorage/memoryEventStorage.d.ts +5 -0
  173. package/dist/core/storage/eventStorage/memoryEventStorage.d.ts.map +1 -1
  174. package/dist/core/storage/eventStorage/memoryEventStorage.js +34 -0
  175. package/dist/core/storage/eventStorage/memoryEventStorage.js.map +1 -1
  176. package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts +1 -0
  177. package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts.map +1 -1
  178. package/dist/core/storage/eventStorage/sqliteEventStorage.js +19 -2
  179. package/dist/core/storage/eventStorage/sqliteEventStorage.js.map +1 -1
  180. package/dist/core/storage/eventStorage/types.d.ts +6 -0
  181. package/dist/core/storage/eventStorage/types.d.ts.map +1 -1
  182. package/dist/core/storage/eventStorage/types.js +1 -0
  183. package/dist/core/storage/eventStorage/types.js.map +1 -1
  184. package/dist/core/storage/eventStorage/validation.d.ts.map +1 -1
  185. package/dist/core/storage/eventStorage/validation.js +2 -1
  186. package/dist/core/storage/eventStorage/validation.js.map +1 -1
  187. package/dist/core/storage/github-world-import.d.ts +84 -0
  188. package/dist/core/storage/github-world-import.d.ts.map +1 -0
  189. package/dist/core/storage/github-world-import.js +365 -0
  190. package/dist/core/storage/github-world-import.js.map +1 -0
  191. package/dist/core/storage/memory-storage.d.ts +19 -8
  192. package/dist/core/storage/memory-storage.d.ts.map +1 -1
  193. package/dist/core/storage/memory-storage.js +147 -49
  194. package/dist/core/storage/memory-storage.js.map +1 -1
  195. package/dist/core/storage/queue-storage.d.ts +1 -0
  196. package/dist/core/storage/queue-storage.d.ts.map +1 -1
  197. package/dist/core/storage/queue-storage.js +3 -2
  198. package/dist/core/storage/queue-storage.js.map +1 -1
  199. package/dist/core/storage/sqlite-storage.d.ts +14 -9
  200. package/dist/core/storage/sqlite-storage.d.ts.map +1 -1
  201. package/dist/core/storage/sqlite-storage.js +131 -154
  202. package/dist/core/storage/sqlite-storage.js.map +1 -1
  203. package/dist/core/storage/storage-factory.d.ts +3 -0
  204. package/dist/core/storage/storage-factory.d.ts.map +1 -1
  205. package/dist/core/storage/storage-factory.js +175 -89
  206. package/dist/core/storage/storage-factory.js.map +1 -1
  207. package/dist/core/storage/world-storage.d.ts +1 -1
  208. package/dist/core/storage/world-storage.d.ts.map +1 -1
  209. package/dist/core/storage/world-storage.js +5 -1
  210. package/dist/core/storage/world-storage.js.map +1 -1
  211. package/dist/core/storage-init.d.ts +11 -0
  212. package/dist/core/storage-init.d.ts.map +1 -0
  213. package/dist/core/storage-init.js +122 -0
  214. package/dist/core/storage-init.js.map +1 -0
  215. package/dist/core/subscription.d.ts +8 -1
  216. package/dist/core/subscription.d.ts.map +1 -1
  217. package/dist/core/subscription.js +130 -23
  218. package/dist/core/subscription.js.map +1 -1
  219. package/dist/core/tool-approval.d.ts +45 -0
  220. package/dist/core/tool-approval.d.ts.map +1 -0
  221. package/dist/core/tool-approval.js +223 -0
  222. package/dist/core/tool-approval.js.map +1 -0
  223. package/dist/core/tool-execution-envelope.d.ts +87 -0
  224. package/dist/core/tool-execution-envelope.d.ts.map +1 -0
  225. package/dist/core/tool-execution-envelope.js +168 -0
  226. package/dist/core/tool-execution-envelope.js.map +1 -0
  227. package/dist/core/tool-utils.d.ts +7 -2
  228. package/dist/core/tool-utils.d.ts.map +1 -1
  229. package/dist/core/tool-utils.js +81 -17
  230. package/dist/core/tool-utils.js.map +1 -1
  231. package/dist/core/types.d.ts +67 -19
  232. package/dist/core/types.d.ts.map +1 -1
  233. package/dist/core/types.js +3 -0
  234. package/dist/core/types.js.map +1 -1
  235. package/dist/core/utils.d.ts +7 -0
  236. package/dist/core/utils.d.ts.map +1 -1
  237. package/dist/core/utils.js +71 -21
  238. package/dist/core/utils.js.map +1 -1
  239. package/dist/core/web-fetch-tool.d.ts +72 -0
  240. package/dist/core/web-fetch-tool.d.ts.map +1 -0
  241. package/dist/core/web-fetch-tool.js +491 -0
  242. package/dist/core/web-fetch-tool.js.map +1 -0
  243. package/dist/core/world-registry.d.ts +84 -0
  244. package/dist/core/world-registry.d.ts.map +1 -0
  245. package/dist/core/world-registry.js +247 -0
  246. package/dist/core/world-registry.js.map +1 -0
  247. package/dist/public/assets/index-Be-1xtV-.js +104 -0
  248. package/dist/public/assets/index-tsDdiXDU.css +1 -0
  249. package/dist/public/index.html +2 -2
  250. package/dist/public/mcp-sandbox-proxy.html +148 -0
  251. package/dist/server/api.js +260 -18
  252. package/dist/server/error-response.d.ts +27 -0
  253. package/dist/server/error-response.js +77 -0
  254. package/dist/server/index.d.ts +2 -1
  255. package/dist/server/index.js +6 -2
  256. package/dist/server/sse-handler.d.ts +11 -1
  257. package/dist/server/sse-handler.js +194 -34
  258. package/migrations/0015_add_message_queue.sql +36 -0
  259. package/migrations/0016_add_world_heartbeat.sql +13 -0
  260. package/migrations/0017_add_title_provenance.sql +7 -0
  261. package/package.json +31 -10
  262. package/dist/public/assets/index-BW41BxMy.css +0 -1
  263. package/dist/public/assets/index-kO6UJFwK.js +0 -96
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-path-logging.js","sourceRoot":"","sources":["../../core/feature-path-logging.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * File Tools Module - Built-in workspace inspection tools for LLM workflows.
3
3
  *
4
4
  * Features:
5
- * - Exposes built-in `read_file`, `list_files`, and `grep` tool definitions
5
+ * - Exposes built-in `read_file`, `write_file`, `list_files`, and `grep` tool definitions
6
6
  * - Supports bounded line-based file reads with offset and limit
7
7
  * - Provides deterministic directory listing with optional hidden-file support
8
8
  * - Performs recursive text search with plain-text or regex matching
@@ -15,6 +15,13 @@
15
15
  * - Errors are returned as tool-friendly `Error:` strings
16
16
  *
17
17
  * Recent Changes:
18
+ * - 2026-03-12: Added `read` permission level guard to `write_file` — returns a blocked-tool error when world toolPermission is 'read'.
19
+ * - 2026-03-03: Tightened tool output limits: read_file hard-capped at 200 lines, list_files capped at depth 2 and 200 entries, grep capped at 50 hits with configurable context_lines (2–5).
20
+ * - 2026-03-01: Hardened `read_file` against undefined read payloads from mocked fs implementations by coercing to empty content instead of hard-failing.
21
+ * - 2026-03-01: Added read_file fallback that resolves missing relative paths against loaded skill roots (for skill script paths like `scripts/convert.py`).
22
+ * - 2026-03-01: Allowed read-only file tools to traverse lexically in-scope `.agents/skills/*` paths even when symlinks resolve outside the world directory (skill workspace compatibility).
23
+ * - 2026-02-28: Added built-in `write_file` tool with explicit `create`/`overwrite` modes and trusted-scope path enforcement.
24
+ * - 2026-02-21: Added `list_files` output bounding (`maxEntries`) and optional `includePattern` filtering with truncation metadata to reduce continuation token spikes and tool-hop churn on large workspaces.
18
25
  * - 2026-02-19: Aligned file-tool path behavior with `shell_cmd`: `list_files` now defaults to trusted working directory when `path` is omitted, and `read_file` accepts `path` alias.
19
26
  * - 2026-02-16: Switched `list_files` scanning to `fast-glob` with depth control and default ignore rules.
20
27
  * - 2026-02-16: Added optional `recursive` mode to `list_files` for nested directory traversal in a single call.
@@ -27,7 +34,12 @@ type ToolContext = {
27
34
  workingDirectory?: string;
28
35
  world?: {
29
36
  variables?: string;
37
+ [key: string]: unknown;
30
38
  };
39
+ chatId?: string | null;
40
+ toolCallId?: string;
41
+ agentName?: string;
42
+ messages?: Array<Record<string, any>>;
31
43
  };
32
44
  export declare function createReadFileToolDefinition(): {
33
45
  description: string;
@@ -56,6 +68,34 @@ export declare function createReadFileToolDefinition(): {
56
68
  };
57
69
  execute: (args: any, _sequenceId?: string, _parentToolCall?: string, context?: ToolContext) => Promise<string>;
58
70
  };
71
+ export declare function createWriteFileToolDefinition(): {
72
+ description: string;
73
+ parameters: {
74
+ type: string;
75
+ properties: {
76
+ filePath: {
77
+ type: string;
78
+ description: string;
79
+ };
80
+ path: {
81
+ type: string;
82
+ description: string;
83
+ };
84
+ content: {
85
+ type: string;
86
+ description: string;
87
+ };
88
+ mode: {
89
+ type: string;
90
+ enum: string[];
91
+ description: string;
92
+ };
93
+ };
94
+ required: string[];
95
+ additionalProperties: boolean;
96
+ };
97
+ execute: (args: any, _sequenceId?: string, _parentToolCall?: string, context?: ToolContext) => Promise<string>;
98
+ };
59
99
  export declare function createListFilesToolDefinition(): {
60
100
  description: string;
61
101
  parameters: {
@@ -73,6 +113,18 @@ export declare function createListFilesToolDefinition(): {
73
113
  type: string;
74
114
  description: string;
75
115
  };
116
+ maxDepth: {
117
+ type: string;
118
+ description: string;
119
+ };
120
+ includePattern: {
121
+ type: string;
122
+ description: string;
123
+ };
124
+ maxEntries: {
125
+ type: string;
126
+ description: string;
127
+ };
76
128
  };
77
129
  required: never[];
78
130
  additionalProperties: boolean;
@@ -104,6 +156,10 @@ export declare function createGrepToolDefinition(): {
104
156
  type: string;
105
157
  description: string;
106
158
  };
159
+ contextLines: {
160
+ type: string;
161
+ description: string;
162
+ };
107
163
  };
108
164
  required: string[];
109
165
  additionalProperties: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"file-tools.d.ts","sourceRoot":"","sources":["../../core/file-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH,KAAK,WAAW,GAAG;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAoLF,wBAAgB,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;oBA2BlB,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EAmCnG;AAED,wBAAgB,6BAA6B;;;;;;;;;;;;;;;;;;;;;oBAuBnB,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EA4CnG;AAED,wBAAgB,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA+Bd,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EA0DnG"}
1
+ {"version":3,"file":"file-tools.d.ts","sourceRoot":"","sources":["../../core/file-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAaH,KAAK,WAAW,GAAG;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CACvC,CAAC;AAmSF,wBAAgB,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;oBA2BlB,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EA+EnG;AAeD,wBAAgB,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;oBA4BnB,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EAuGnG;AAED,wBAAgB,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAmCnB,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EAsEnG;AAED,wBAAgB,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAmCd,GAAG,gBAAgB,MAAM,oBAAoB,MAAM,YAAY,WAAW;EA4DnG"}
@@ -2,7 +2,7 @@
2
2
  * File Tools Module - Built-in workspace inspection tools for LLM workflows.
3
3
  *
4
4
  * Features:
5
- * - Exposes built-in `read_file`, `list_files`, and `grep` tool definitions
5
+ * - Exposes built-in `read_file`, `write_file`, `list_files`, and `grep` tool definitions
6
6
  * - Supports bounded line-based file reads with offset and limit
7
7
  * - Provides deterministic directory listing with optional hidden-file support
8
8
  * - Performs recursive text search with plain-text or regex matching
@@ -15,6 +15,13 @@
15
15
  * - Errors are returned as tool-friendly `Error:` strings
16
16
  *
17
17
  * Recent Changes:
18
+ * - 2026-03-12: Added `read` permission level guard to `write_file` — returns a blocked-tool error when world toolPermission is 'read'.
19
+ * - 2026-03-03: Tightened tool output limits: read_file hard-capped at 200 lines, list_files capped at depth 2 and 200 entries, grep capped at 50 hits with configurable context_lines (2–5).
20
+ * - 2026-03-01: Hardened `read_file` against undefined read payloads from mocked fs implementations by coercing to empty content instead of hard-failing.
21
+ * - 2026-03-01: Added read_file fallback that resolves missing relative paths against loaded skill roots (for skill script paths like `scripts/convert.py`).
22
+ * - 2026-03-01: Allowed read-only file tools to traverse lexically in-scope `.agents/skills/*` paths even when symlinks resolve outside the world directory (skill workspace compatibility).
23
+ * - 2026-02-28: Added built-in `write_file` tool with explicit `create`/`overwrite` modes and trusted-scope path enforcement.
24
+ * - 2026-02-21: Added `list_files` output bounding (`maxEntries`) and optional `includePattern` filtering with truncation metadata to reduce continuation token spikes and tool-hop churn on large workspaces.
18
25
  * - 2026-02-19: Aligned file-tool path behavior with `shell_cmd`: `list_files` now defaults to trusted working directory when `path` is omitted, and `read_file` accepts `path` alias.
19
26
  * - 2026-02-16: Switched `list_files` scanning to `fast-glob` with depth control and default ignore rules.
20
27
  * - 2026-02-16: Added optional `recursive` mode to `list_files` for nested directory traversal in a single call.
@@ -27,10 +34,19 @@ import { promises as fs } from 'fs';
27
34
  import * as path from 'path';
28
35
  import fg from 'fast-glob';
29
36
  import { resolveTrustedShellWorkingDirectory, validateShellDirectoryRequest, } from './shell-cmd-tool.js';
37
+ import { getSkillSourcePath, getSkills } from './skill-registry.js';
38
+ import { getEnvValueFromText } from './utils.js';
39
+ import { requestToolApproval } from './tool-approval.js';
30
40
  const DEFAULT_READ_LIMIT = 200;
31
- const MAX_READ_LIMIT = 2000;
32
- const DEFAULT_GREP_MAX_RESULTS = 200;
33
- const MAX_GREP_MAX_RESULTS = 2000;
41
+ const MAX_READ_LIMIT = 200;
42
+ const DEFAULT_LIST_MAX_ENTRIES = 200;
43
+ const MAX_LIST_MAX_ENTRIES = 200;
44
+ const DEFAULT_LIST_MAX_DEPTH = 2;
45
+ const MAX_LIST_MAX_DEPTH = 2;
46
+ const DEFAULT_GREP_MAX_RESULTS = 50;
47
+ const MAX_GREP_MAX_RESULTS = 50;
48
+ const DEFAULT_GREP_CONTEXT_LINES = 2;
49
+ const MAX_GREP_CONTEXT_LINES = 5;
34
50
  const MAX_GREP_FILE_BYTES = 1024 * 1024;
35
51
  function clamp(value, min, max) {
36
52
  return Math.max(min, Math.min(max, value));
@@ -48,12 +64,73 @@ function resolveTargetPath(inputPath, trustedWorkingDirectory) {
48
64
  const baseDirectory = trustedWorkingDirectory;
49
65
  return path.resolve(baseDirectory, inputPath);
50
66
  }
51
- function ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory) {
67
+ function isPathWithinRoot(candidatePath, rootPath) {
68
+ const normalizeForComparison = (value) => normalizePath(path.resolve(value)).replace(/\/+/g, '/');
69
+ const normalizedCandidate = normalizeForComparison(candidatePath);
70
+ const normalizedRoot = normalizeForComparison(rootPath);
71
+ return normalizedCandidate === normalizedRoot
72
+ || normalizedCandidate.startsWith(`${normalizedRoot}/`);
73
+ }
74
+ function isMissingFileError(error) {
75
+ if (!error || typeof error !== 'object') {
76
+ return false;
77
+ }
78
+ const errno = error.code;
79
+ if (errno === 'ENOENT') {
80
+ return true;
81
+ }
82
+ const message = String(error.message ?? '').toLowerCase();
83
+ return message.includes('enoent') || message.includes('no such file or directory');
84
+ }
85
+ function resolveSkillRelativeReadCandidates(requestedFilePath) {
86
+ const normalizedRequestedPath = normalizePath(String(requestedFilePath || '').trim());
87
+ if (!normalizedRequestedPath || path.isAbsolute(normalizedRequestedPath)) {
88
+ return [];
89
+ }
90
+ const candidates = [];
91
+ const availableSkills = getSkills();
92
+ for (const skill of availableSkills) {
93
+ const sourcePath = getSkillSourcePath(skill.skill_id);
94
+ if (!sourcePath) {
95
+ continue;
96
+ }
97
+ const skillRoot = path.dirname(sourcePath);
98
+ const candidatePaths = new Set([
99
+ path.resolve(skillRoot, normalizedRequestedPath),
100
+ ]);
101
+ const skillPrefix = `${skill.skill_id}/`;
102
+ if (normalizedRequestedPath.startsWith(skillPrefix)) {
103
+ const withoutSkillPrefix = normalizedRequestedPath.slice(skillPrefix.length);
104
+ if (withoutSkillPrefix) {
105
+ candidatePaths.add(path.resolve(skillRoot, withoutSkillPrefix));
106
+ }
107
+ }
108
+ for (const candidatePath of candidatePaths) {
109
+ if (isPathWithinRoot(candidatePath, skillRoot)) {
110
+ candidates.push(candidatePath);
111
+ }
112
+ }
113
+ }
114
+ return [...new Set(candidates)];
115
+ }
116
+ function ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory, options) {
52
117
  const validation = validateShellDirectoryRequest(resolvedPath, trustedWorkingDirectory);
118
+ if (!validation.valid && options?.allowSkillPathAlias && isWithinSkillAliasPath(resolvedPath, trustedWorkingDirectory)) {
119
+ return;
120
+ }
53
121
  if (!validation.valid) {
54
122
  throw new Error(validation.error);
55
123
  }
56
124
  }
125
+ function isWithinSkillAliasPath(resolvedPath, trustedWorkingDirectory) {
126
+ const candidate = path.resolve(String(resolvedPath || '').trim());
127
+ const trusted = path.resolve(String(trustedWorkingDirectory || '').trim());
128
+ const skillRoot = path.resolve(trusted, '.agents', 'skills');
129
+ const normalizedCandidate = normalizePath(candidate).replace(/\/+/g, '/');
130
+ const normalizedSkillRoot = normalizePath(skillRoot).replace(/\/+/g, '/');
131
+ return normalizedCandidate === normalizedSkillRoot
132
+ || normalizedCandidate.startsWith(`${normalizedSkillRoot}/`);
133
+ }
57
134
  function escapeRegExp(value) {
58
135
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
59
136
  }
@@ -112,10 +189,19 @@ function shouldIncludeFile(relativePath, includePattern) {
112
189
  return true;
113
190
  }
114
191
  return patterns.some((pattern) => {
115
- if (!pattern.includes('*') && !pattern.includes('?')) {
116
- return normalizedRelativePath.includes(normalizePath(pattern));
117
- }
118
- return globToRegExp(pattern).test(normalizedRelativePath);
192
+ const normalizedPattern = normalizePath(pattern);
193
+ const candidatePatterns = normalizedPattern.startsWith('**/')
194
+ ? [normalizedPattern, normalizedPattern.slice(3)]
195
+ : [normalizedPattern];
196
+ return candidatePatterns.some((candidatePattern) => {
197
+ if (!candidatePattern) {
198
+ return false;
199
+ }
200
+ if (!candidatePattern.includes('*') && !candidatePattern.includes('?')) {
201
+ return normalizedRelativePath.includes(candidatePattern);
202
+ }
203
+ return globToRegExp(candidatePattern).test(normalizedRelativePath);
204
+ });
119
205
  });
120
206
  }
121
207
  async function collectFilesRecursively(directoryPath) {
@@ -141,7 +227,7 @@ async function collectFilesRecursively(directoryPath) {
141
227
  return files;
142
228
  }
143
229
  async function searchInFile(options) {
144
- const { filePath, relativePath, matcher, matches, maxResults } = options;
230
+ const { filePath, relativePath, matcher, matches, maxResults, contextLines } = options;
145
231
  const stat = await fs.stat(filePath);
146
232
  if (stat.size > MAX_GREP_FILE_BYTES) {
147
233
  return;
@@ -158,11 +244,23 @@ async function searchInFile(options) {
158
244
  if (!matcher.test(lineContent)) {
159
245
  continue;
160
246
  }
161
- matches.push({
247
+ const entry = {
162
248
  path: normalizePath(relativePath),
163
249
  line: index + 1,
164
250
  content: lineContent,
165
- });
251
+ };
252
+ if (contextLines > 0) {
253
+ const start = Math.max(0, index - contextLines);
254
+ const end = Math.min(lines.length - 1, index + contextLines);
255
+ const surrounding = [];
256
+ for (let ci = start; ci <= end; ci += 1) {
257
+ if (ci !== index) {
258
+ surrounding.push(`${ci + 1}: ${lines[ci] ?? ''}`);
259
+ }
260
+ }
261
+ entry.context = surrounding;
262
+ }
263
+ matches.push(entry);
166
264
  if (matches.length >= maxResults) {
167
265
  return;
168
266
  }
@@ -188,7 +286,7 @@ export function createReadFileToolDefinition() {
188
286
  },
189
287
  limit: {
190
288
  type: 'number',
191
- description: `Maximum number of lines to return (default: ${DEFAULT_READ_LIMIT}, max: ${MAX_READ_LIMIT}).`,
289
+ description: `Maximum number of lines to return (default and cap: ${MAX_READ_LIMIT}).`,
192
290
  },
193
291
  },
194
292
  required: [],
@@ -201,10 +299,50 @@ export function createReadFileToolDefinition() {
201
299
  if (!requestedFilePath) {
202
300
  return 'Error: read_file failed - filePath is required';
203
301
  }
204
- const resolvedPath = resolveTargetPath(requestedFilePath, trustedWorkingDirectory);
205
- ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory);
206
- const rawContent = await fs.readFile(resolvedPath, 'utf8');
207
- const fileContent = toUtf8String(rawContent);
302
+ let resolvedPath = resolveTargetPath(requestedFilePath, trustedWorkingDirectory);
303
+ ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory, { allowSkillPathAlias: true });
304
+ let rawContent;
305
+ try {
306
+ rawContent = await fs.readFile(resolvedPath, 'utf8');
307
+ }
308
+ catch (readError) {
309
+ if (!isMissingFileError(readError)) {
310
+ throw readError;
311
+ }
312
+ const skillReadCandidates = new Set(resolveSkillRelativeReadCandidates(requestedFilePath));
313
+ if (skillReadCandidates.size === 0) {
314
+ const relativeFromTrustedDirectory = normalizePath(path.relative(trustedWorkingDirectory, resolvedPath));
315
+ const isRelativeToTrusted = relativeFromTrustedDirectory
316
+ && relativeFromTrustedDirectory !== '.'
317
+ && !relativeFromTrustedDirectory.startsWith('..')
318
+ && !path.isAbsolute(relativeFromTrustedDirectory);
319
+ if (isRelativeToTrusted) {
320
+ for (const candidatePath of resolveSkillRelativeReadCandidates(relativeFromTrustedDirectory)) {
321
+ skillReadCandidates.add(candidatePath);
322
+ }
323
+ }
324
+ }
325
+ let fallbackReadError = null;
326
+ for (const candidatePath of skillReadCandidates) {
327
+ try {
328
+ ensurePathWithinTrustedDirectory(candidatePath, trustedWorkingDirectory, { allowSkillPathAlias: true });
329
+ rawContent = await fs.readFile(candidatePath, 'utf8');
330
+ resolvedPath = candidatePath;
331
+ fallbackReadError = null;
332
+ break;
333
+ }
334
+ catch (candidateError) {
335
+ fallbackReadError = candidateError;
336
+ if (!isMissingFileError(candidateError)) {
337
+ throw candidateError;
338
+ }
339
+ }
340
+ }
341
+ if (fallbackReadError || rawContent === undefined) {
342
+ throw readError;
343
+ }
344
+ }
345
+ const fileContent = toUtf8String(rawContent ?? '');
208
346
  const lines = fileContent.split(/\r?\n/);
209
347
  const offset = clamp(Number(args.offset ?? 1), 1, Number.MAX_SAFE_INTEGER);
210
348
  const limit = clamp(Number(args.limit ?? DEFAULT_READ_LIMIT), 1, MAX_READ_LIMIT);
@@ -226,9 +364,137 @@ export function createReadFileToolDefinition() {
226
364
  },
227
365
  };
228
366
  }
367
+ function normalizeWriteMode(mode) {
368
+ if (mode === undefined || mode === null || mode === '') {
369
+ return 'overwrite';
370
+ }
371
+ const normalizedMode = String(mode).trim().toLowerCase();
372
+ if (normalizedMode === 'create' || normalizedMode === 'overwrite') {
373
+ return normalizedMode;
374
+ }
375
+ throw new Error(`Invalid mode '${String(mode)}'. Expected 'create' or 'overwrite'.`);
376
+ }
377
+ export function createWriteFileToolDefinition() {
378
+ return {
379
+ description: 'Write text content to a file inside the trusted working-directory scope. Supports explicit create-only and overwrite modes.',
380
+ parameters: {
381
+ type: 'object',
382
+ properties: {
383
+ filePath: {
384
+ type: 'string',
385
+ description: 'Target file path. Relative paths resolve from runtime working directory.',
386
+ },
387
+ path: {
388
+ type: 'string',
389
+ description: 'Alias for filePath.',
390
+ },
391
+ content: {
392
+ type: 'string',
393
+ description: 'UTF-8 text content to write.',
394
+ },
395
+ mode: {
396
+ type: 'string',
397
+ enum: ['create', 'overwrite'],
398
+ description: "Write mode. 'create' fails if the file exists. 'overwrite' replaces existing content (default).",
399
+ },
400
+ },
401
+ required: ['content'],
402
+ additionalProperties: false,
403
+ },
404
+ execute: async (args, _sequenceId, _parentToolCall, context) => {
405
+ try {
406
+ // Check world-level tool permission
407
+ const toolPermission = getEnvValueFromText(context?.world?.variables, 'tool_permission') ?? 'auto';
408
+ if (toolPermission === 'read') {
409
+ return 'Error: write_file is blocked by the current permission level (read).';
410
+ }
411
+ if (toolPermission === 'ask' && context?.world) {
412
+ const approval = await requestToolApproval({
413
+ world: context.world,
414
+ chatId: typeof context.chatId === 'string' ? context.chatId : null,
415
+ toolCallId: context.toolCallId,
416
+ title: 'Approve file write?',
417
+ message: `write_file wants to write to: ${String(args.filePath ?? args.path ?? '').trim() || '(unknown)'}`,
418
+ defaultOptionId: 'deny',
419
+ options: [
420
+ { id: 'approve', label: 'Approve', description: 'Allow this file write.' },
421
+ { id: 'deny', label: 'Deny', description: 'Block this file write.' },
422
+ ],
423
+ approvedOptionIds: ['approve'],
424
+ metadata: { tool: 'write_file', filePath: String(args.filePath ?? args.path ?? '').trim() },
425
+ agentName: context.agentName || null,
426
+ messages: context.messages,
427
+ });
428
+ if (!approval.approved) {
429
+ return `Error: write_file was denied by the user (${approval.reason}).`;
430
+ }
431
+ }
432
+ const trustedWorkingDirectory = getTrustedWorkingDirectory(context);
433
+ const requestedFilePath = String(args.filePath ?? args.path ?? '').trim();
434
+ if (!requestedFilePath) {
435
+ return 'Error: write_file failed - filePath is required';
436
+ }
437
+ if (typeof args.content !== 'string') {
438
+ return 'Error: write_file failed - content must be a string';
439
+ }
440
+ const mode = normalizeWriteMode(args.mode);
441
+ const resolvedPath = resolveTargetPath(requestedFilePath, trustedWorkingDirectory);
442
+ ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory);
443
+ let fileExists = false;
444
+ try {
445
+ const stat = await fs.stat(resolvedPath);
446
+ if (stat.isDirectory()) {
447
+ return 'Error: write_file failed - target path is a directory';
448
+ }
449
+ fileExists = true;
450
+ }
451
+ catch (error) {
452
+ const code = error?.code;
453
+ if (code !== 'ENOENT') {
454
+ throw error;
455
+ }
456
+ }
457
+ if (mode === 'create' && fileExists) {
458
+ return `Error: write_file failed - file already exists: ${resolvedPath}`;
459
+ }
460
+ const parentDirectory = path.dirname(resolvedPath);
461
+ ensurePathWithinTrustedDirectory(parentDirectory, trustedWorkingDirectory);
462
+ await fs.mkdir(parentDirectory, { recursive: true });
463
+ try {
464
+ await fs.writeFile(resolvedPath, args.content, mode === 'create'
465
+ ? { encoding: 'utf8', flag: 'wx' }
466
+ : { encoding: 'utf8', flag: 'w' });
467
+ }
468
+ catch (error) {
469
+ const code = error?.code;
470
+ if (code === 'EEXIST' && mode === 'create') {
471
+ return `Error: write_file failed - file already exists: ${resolvedPath}`;
472
+ }
473
+ throw error;
474
+ }
475
+ const created = !fileExists;
476
+ const bytesWritten = Buffer.byteLength(args.content, 'utf8');
477
+ return JSON.stringify({
478
+ ok: true,
479
+ status: 'success',
480
+ filePath: resolvedPath,
481
+ mode,
482
+ operation: created ? 'created' : 'updated',
483
+ created,
484
+ updated: !created,
485
+ bytesWritten,
486
+ }, null, 2);
487
+ }
488
+ catch (error) {
489
+ const message = error instanceof Error ? error.message : String(error);
490
+ return `Error: write_file failed - ${message}`;
491
+ }
492
+ },
493
+ };
494
+ }
229
495
  export function createListFilesToolDefinition() {
230
496
  return {
231
- description: 'List files and directories available in a directory path for quick workspace exploration. Defaults to trusted working directory when path is omitted. Use recursive=true to include nested entries.',
497
+ description: 'List file and directory names in a directory path for quick workspace exploration. Defaults to trusted working directory when path is omitted. Returns names only. Use maxDepth/includePattern/maxEntries to keep results bounded.',
232
498
  parameters: {
233
499
  type: 'object',
234
500
  properties: {
@@ -242,7 +508,19 @@ export function createListFilesToolDefinition() {
242
508
  },
243
509
  recursive: {
244
510
  type: 'boolean',
245
- description: 'When true, include nested entries recursively (default: false).',
511
+ description: 'When true, include nested entries up to maxDepth (default: false, depth 1).',
512
+ },
513
+ maxDepth: {
514
+ type: 'number',
515
+ description: `Maximum directory depth when recursive (default and cap: ${MAX_LIST_MAX_DEPTH}).`,
516
+ },
517
+ includePattern: {
518
+ type: 'string',
519
+ description: 'Optional glob-like filter (supports comma-separated patterns), for example: **/*.md',
520
+ },
521
+ maxEntries: {
522
+ type: 'number',
523
+ description: `Maximum number of returned entries (default and cap: ${MAX_LIST_MAX_ENTRIES}).`,
246
524
  },
247
525
  },
248
526
  required: [],
@@ -253,28 +531,44 @@ export function createListFilesToolDefinition() {
253
531
  const trustedWorkingDirectory = getTrustedWorkingDirectory(context);
254
532
  const requestedPath = String(args.path ?? '.');
255
533
  const resolvedPath = resolveTargetPath(requestedPath, trustedWorkingDirectory);
256
- ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory);
534
+ ensurePathWithinTrustedDirectory(resolvedPath, trustedWorkingDirectory, { allowSkillPathAlias: true });
257
535
  const includeHidden = Boolean(args.includeHidden ?? true);
258
536
  const recursive = Boolean(args.recursive ?? false);
537
+ const includePattern = String(args.includePattern ?? '').trim();
538
+ const maxDepth = clamp(Number(args.maxDepth ?? (recursive ? DEFAULT_LIST_MAX_DEPTH : 1)), 1, MAX_LIST_MAX_DEPTH);
539
+ const maxEntries = clamp(Number(args.maxEntries ?? DEFAULT_LIST_MAX_ENTRIES), 1, MAX_LIST_MAX_ENTRIES);
259
540
  const items = await fg(['**/*'], {
260
541
  cwd: resolvedPath,
261
- deep: recursive ? Infinity : 1,
542
+ deep: maxDepth,
262
543
  ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**'],
263
544
  onlyFiles: false,
264
545
  markDirectories: true,
265
546
  dot: includeHidden,
266
547
  });
267
- const normalizedItems = items
548
+ const filteredItems = items
268
549
  .map((entry) => normalizePath(entry))
550
+ .filter((entry) => shouldIncludeFile(entry, includePattern))
269
551
  .sort((left, right) => left.localeCompare(right));
552
+ const totalMatchedEntries = filteredItems.length;
553
+ const truncated = totalMatchedEntries > maxEntries;
554
+ const returnedEntries = truncated ? filteredItems.slice(0, maxEntries) : filteredItems;
555
+ const message = totalMatchedEntries === 0
556
+ ? 'No files or directories found in the requested path.'
557
+ : (truncated
558
+ ? `Result truncated to ${maxEntries} entries out of ${totalMatchedEntries}. Narrow with includePattern or path.`
559
+ : undefined);
270
560
  return JSON.stringify({
271
561
  requestedPath,
272
562
  path: resolvedPath,
273
563
  recursive,
274
- total: normalizedItems.length,
275
- entries: normalizedItems,
276
- found: normalizedItems.length > 0,
277
- message: normalizedItems.length > 0 ? undefined : 'No files or directories found in the requested path.',
564
+ includePattern: includePattern || undefined,
565
+ maxEntries,
566
+ total: totalMatchedEntries,
567
+ returned: returnedEntries.length,
568
+ truncated,
569
+ entries: returnedEntries,
570
+ found: totalMatchedEntries > 0,
571
+ message,
278
572
  }, null, 2);
279
573
  }
280
574
  catch (error) {
@@ -289,7 +583,7 @@ export function createListFilesToolDefinition() {
289
583
  }
290
584
  export function createGrepToolDefinition() {
291
585
  return {
292
- description: 'Search text across files to find destinations. Supports plain text or regex queries with optional include filtering.',
586
+ description: 'Search text across files to find destinations. Supports plain text or regex queries with optional include filtering and surrounding context lines.',
293
587
  parameters: {
294
588
  type: 'object',
295
589
  properties: {
@@ -311,7 +605,11 @@ export function createGrepToolDefinition() {
311
605
  },
312
606
  maxResults: {
313
607
  type: 'number',
314
- description: `Maximum matches to return (default: ${DEFAULT_GREP_MAX_RESULTS}, max: ${MAX_GREP_MAX_RESULTS}).`,
608
+ description: `Maximum matches to return (default and cap: ${MAX_GREP_MAX_RESULTS}).`,
609
+ },
610
+ contextLines: {
611
+ type: 'number',
612
+ description: `Number of surrounding context lines per match (default: ${DEFAULT_GREP_CONTEXT_LINES}, range: 0\u2013${MAX_GREP_CONTEXT_LINES}).`,
315
613
  },
316
614
  },
317
615
  required: ['query'],
@@ -328,9 +626,10 @@ export function createGrepToolDefinition() {
328
626
  const directoryPath = args.directoryPath
329
627
  ? resolveTargetPath(String(args.directoryPath), trustedWorkingDirectory)
330
628
  : trustedWorkingDirectory;
331
- ensurePathWithinTrustedDirectory(directoryPath, trustedWorkingDirectory);
629
+ ensurePathWithinTrustedDirectory(directoryPath, trustedWorkingDirectory, { allowSkillPathAlias: true });
332
630
  const includePattern = typeof args.includePattern === 'string' ? args.includePattern : undefined;
333
631
  const maxResults = clamp(Number(args.maxResults ?? DEFAULT_GREP_MAX_RESULTS), 1, MAX_GREP_MAX_RESULTS);
632
+ const contextLines = clamp(Number(args.contextLines ?? DEFAULT_GREP_CONTEXT_LINES), 0, MAX_GREP_CONTEXT_LINES);
334
633
  const matcher = isRegexp ? new RegExp(query, 'i') : new RegExp(escapeRegExp(query), 'i');
335
634
  const allFiles = await collectFilesRecursively(directoryPath);
336
635
  const matches = [];
@@ -345,6 +644,7 @@ export function createGrepToolDefinition() {
345
644
  matcher,
346
645
  matches,
347
646
  maxResults,
647
+ contextLines,
348
648
  });
349
649
  if (matches.length >= maxResults) {
350
650
  break;