@agent-native/core 0.45.1 → 0.47.1

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 (115) hide show
  1. package/README.md +1 -0
  2. package/dist/agent/production-agent.d.ts +28 -0
  3. package/dist/agent/production-agent.d.ts.map +1 -1
  4. package/dist/agent/production-agent.js +14 -7
  5. package/dist/agent/production-agent.js.map +1 -1
  6. package/dist/cli/skills.d.ts +2 -2
  7. package/dist/cli/skills.d.ts.map +1 -1
  8. package/dist/cli/skills.js +33 -0
  9. package/dist/cli/skills.js.map +1 -1
  10. package/dist/client/components/LiveCursorOverlay.d.ts +46 -0
  11. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -0
  12. package/dist/client/components/LiveCursorOverlay.js +137 -0
  13. package/dist/client/components/LiveCursorOverlay.js.map +1 -0
  14. package/dist/client/components/PresenceBar.d.ts +11 -1
  15. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  16. package/dist/client/components/PresenceBar.js +39 -7
  17. package/dist/client/components/PresenceBar.js.map +1 -1
  18. package/dist/client/components/RemoteSelectionRings.d.ts +43 -0
  19. package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -0
  20. package/dist/client/components/RemoteSelectionRings.js +116 -0
  21. package/dist/client/components/RemoteSelectionRings.js.map +1 -0
  22. package/dist/client/index.d.ts +4 -0
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +5 -0
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/coding-tools/run-code.d.ts +40 -0
  27. package/dist/coding-tools/run-code.d.ts.map +1 -0
  28. package/dist/coding-tools/run-code.js +511 -0
  29. package/dist/coding-tools/run-code.js.map +1 -0
  30. package/dist/collab/awareness.d.ts +25 -0
  31. package/dist/collab/awareness.d.ts.map +1 -1
  32. package/dist/collab/awareness.js +42 -5
  33. package/dist/collab/awareness.js.map +1 -1
  34. package/dist/collab/client.d.ts +19 -1
  35. package/dist/collab/client.d.ts.map +1 -1
  36. package/dist/collab/client.js +362 -57
  37. package/dist/collab/client.js.map +1 -1
  38. package/dist/collab/follow-mode.d.ts +56 -0
  39. package/dist/collab/follow-mode.d.ts.map +1 -0
  40. package/dist/collab/follow-mode.js +54 -0
  41. package/dist/collab/follow-mode.js.map +1 -0
  42. package/dist/collab/index.d.ts +3 -1
  43. package/dist/collab/index.d.ts.map +1 -1
  44. package/dist/collab/index.js +5 -1
  45. package/dist/collab/index.js.map +1 -1
  46. package/dist/collab/presence.d.ts +56 -0
  47. package/dist/collab/presence.d.ts.map +1 -0
  48. package/dist/collab/presence.js +98 -0
  49. package/dist/collab/presence.js.map +1 -0
  50. package/dist/collab/routes.d.ts.map +1 -1
  51. package/dist/collab/routes.js +33 -6
  52. package/dist/collab/routes.js.map +1 -1
  53. package/dist/collab/struct-routes.d.ts.map +1 -1
  54. package/dist/collab/struct-routes.js +24 -4
  55. package/dist/collab/struct-routes.js.map +1 -1
  56. package/dist/collab/ydoc-manager.d.ts +13 -0
  57. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  58. package/dist/collab/ydoc-manager.js +51 -15
  59. package/dist/collab/ydoc-manager.js.map +1 -1
  60. package/dist/extensions/fetch-tool.d.ts.map +1 -1
  61. package/dist/extensions/fetch-tool.js +62 -7
  62. package/dist/extensions/fetch-tool.js.map +1 -1
  63. package/dist/extensions/web-search-tool.d.ts +41 -0
  64. package/dist/extensions/web-search-tool.d.ts.map +1 -0
  65. package/dist/extensions/web-search-tool.js +200 -0
  66. package/dist/extensions/web-search-tool.js.map +1 -0
  67. package/dist/provider-api/custom-registry.d.ts +92 -0
  68. package/dist/provider-api/custom-registry.d.ts.map +1 -0
  69. package/dist/provider-api/custom-registry.js +289 -0
  70. package/dist/provider-api/custom-registry.js.map +1 -0
  71. package/dist/provider-api/index.d.ts +80 -44
  72. package/dist/provider-api/index.d.ts.map +1 -1
  73. package/dist/provider-api/index.js +569 -18
  74. package/dist/provider-api/index.js.map +1 -1
  75. package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
  76. package/dist/secrets/register-framework-secrets.js +36 -3
  77. package/dist/secrets/register-framework-secrets.js.map +1 -1
  78. package/dist/server/agent-chat-plugin.d.ts +36 -0
  79. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  80. package/dist/server/agent-chat-plugin.js +119 -0
  81. package/dist/server/agent-chat-plugin.js.map +1 -1
  82. package/dist/server/collab-plugin.d.ts +6 -0
  83. package/dist/server/collab-plugin.d.ts.map +1 -1
  84. package/dist/server/collab-plugin.js +105 -5
  85. package/dist/server/collab-plugin.js.map +1 -1
  86. package/dist/server/poll-events.d.ts +5 -0
  87. package/dist/server/poll-events.d.ts.map +1 -1
  88. package/dist/server/poll-events.js +27 -4
  89. package/dist/server/poll-events.js.map +1 -1
  90. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +185 -37
  91. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +12 -2
  92. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +185 -37
  93. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +12 -2
  94. package/dist/workspace-files/index.d.ts +4 -0
  95. package/dist/workspace-files/index.d.ts.map +1 -0
  96. package/dist/workspace-files/index.js +4 -0
  97. package/dist/workspace-files/index.js.map +1 -0
  98. package/dist/workspace-files/schema.d.ts +195 -0
  99. package/dist/workspace-files/schema.d.ts.map +1 -0
  100. package/dist/workspace-files/schema.js +48 -0
  101. package/dist/workspace-files/schema.js.map +1 -0
  102. package/dist/workspace-files/store.d.ts +89 -0
  103. package/dist/workspace-files/store.d.ts.map +1 -0
  104. package/dist/workspace-files/store.js +298 -0
  105. package/dist/workspace-files/store.js.map +1 -0
  106. package/dist/workspace-files/tool.d.ts +15 -0
  107. package/dist/workspace-files/tool.d.ts.map +1 -0
  108. package/dist/workspace-files/tool.js +226 -0
  109. package/dist/workspace-files/tool.js.map +1 -0
  110. package/docs/content/real-time-collaboration.md +481 -97
  111. package/package.json +2 -1
  112. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +185 -37
  113. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +12 -2
  114. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +185 -37
  115. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +12 -2
@@ -0,0 +1,226 @@
1
+ /**
2
+ * `workspace-files` agent tool.
3
+ *
4
+ * A single tool with an `action` discriminator covering write, append, read,
5
+ * list, delete, and grep. Files persist across conversations; the agent uses
6
+ * them to stage large intermediate results (fetched pages, per-item memos)
7
+ * and then read back selectively for synthesis.
8
+ *
9
+ * Scope is automatically resolved from the active request context:
10
+ * - org scope when a request orgId is present (shared across users in the org)
11
+ * - user scope otherwise (personal to the requesting user's email)
12
+ */
13
+ import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
14
+ import { writeWorkspaceFile, appendWorkspaceFile, readWorkspaceFile, listWorkspaceFiles, deleteWorkspaceFile, grepWorkspaceFiles, } from "./store.js";
15
+ const MAX_READ_CHARS = 100_000;
16
+ const DEFAULT_READ_CHARS = 40_000;
17
+ /** Resolve scope from the current request context (org-preferred). */
18
+ function resolveScope() {
19
+ const orgId = getRequestOrgId();
20
+ if (orgId)
21
+ return { scope: "org", scopeId: orgId };
22
+ const email = getRequestUserEmail();
23
+ if (email)
24
+ return { scope: "user", scopeId: email };
25
+ return null;
26
+ }
27
+ export function createWorkspaceFilesTool() {
28
+ return {
29
+ "workspace-files": {
30
+ readOnly: false,
31
+ tool: {
32
+ description: [
33
+ "Durable scratch-file storage for the agent. Files persist across conversations and are scoped to the current org/user.",
34
+ "Use this to stage large intermediate results (fetched pages, per-item analysis memos, API payloads) so they don't consume context window, then read them back selectively for synthesis.",
35
+ "",
36
+ "Typical fusion-style workflow:",
37
+ " 1. Fan out: for each item, fetch data and `write` a per-item memo file.",
38
+ " 2. Synthesize: `list` files, then `read` each memo (with offset/maxChars to page large ones).",
39
+ " 3. Optionally `grep` across all memos to find patterns.",
40
+ " 4. `delete` temp files when no longer needed.",
41
+ "",
42
+ "Actions:",
43
+ " write — create or overwrite a file. Max 2 MB per file, 200 MB total.",
44
+ " append — append text to a file (or create if absent).",
45
+ " read — read content, optionally with offset/maxChars for paging large files.",
46
+ " list — list files (with optional path prefix filter) showing name, size, updated.",
47
+ " delete — delete a file by path.",
48
+ " grep — search content across files for a substring or regex.",
49
+ ].join("\n"),
50
+ parameters: {
51
+ type: "object",
52
+ properties: {
53
+ action: {
54
+ type: "string",
55
+ enum: ["write", "append", "read", "list", "delete", "grep"],
56
+ description: "Operation to perform.",
57
+ },
58
+ path: {
59
+ type: "string",
60
+ description: 'File path relative to the scope root, e.g. "analysis/q2-memos/acme.md". Required for write/append/read/delete. Optional for list/grep (acts as prefix filter).',
61
+ },
62
+ content: {
63
+ type: "string",
64
+ description: "Text content to write or append. Required for write/append.",
65
+ },
66
+ contentType: {
67
+ type: "string",
68
+ description: 'MIME type for new files. Default: "text/plain". Use "application/json" for JSON, "text/markdown" for Markdown.',
69
+ },
70
+ offset: {
71
+ type: "number",
72
+ description: "Character offset to start reading from (for paging large files). Default: 0.",
73
+ },
74
+ maxChars: {
75
+ type: "number",
76
+ description: `Maximum characters to return when reading. Default: ${DEFAULT_READ_CHARS}. Max: ${MAX_READ_CHARS}.`,
77
+ },
78
+ pattern: {
79
+ type: "string",
80
+ description: "Search pattern for grep. Required for grep action. A plain substring by default; set useRegex to true for a regex.",
81
+ },
82
+ useRegex: {
83
+ type: "boolean",
84
+ description: "When true, treat `pattern` as a JavaScript regex (case-insensitive). Default: false.",
85
+ },
86
+ },
87
+ required: ["action"],
88
+ },
89
+ },
90
+ run: async (args) => {
91
+ const scope = resolveScope();
92
+ if (!scope) {
93
+ return "Error: workspace-files requires an authenticated request context.";
94
+ }
95
+ const action = String(args.action ?? "").trim();
96
+ try {
97
+ switch (action) {
98
+ case "write": {
99
+ const path = String(args.path ?? "").trim();
100
+ if (!path)
101
+ return "Error: path is required for write.";
102
+ const content = String(args.content ?? "");
103
+ const contentType = String(args.contentType ?? "text/plain");
104
+ const meta = await writeWorkspaceFile(scope, path, content, contentType);
105
+ return JSON.stringify({
106
+ ok: true,
107
+ action: "write",
108
+ path: meta.path,
109
+ sizeBytes: meta.sizeBytes,
110
+ updatedAt: meta.updatedAt,
111
+ });
112
+ }
113
+ case "append": {
114
+ const path = String(args.path ?? "").trim();
115
+ if (!path)
116
+ return "Error: path is required for append.";
117
+ const content = String(args.content ?? "");
118
+ const contentType = String(args.contentType ?? "text/plain");
119
+ const meta = await appendWorkspaceFile(scope, path, content, contentType);
120
+ return JSON.stringify({
121
+ ok: true,
122
+ action: "append",
123
+ path: meta.path,
124
+ sizeBytes: meta.sizeBytes,
125
+ updatedAt: meta.updatedAt,
126
+ });
127
+ }
128
+ case "read": {
129
+ const path = String(args.path ?? "").trim();
130
+ if (!path)
131
+ return "Error: path is required for read.";
132
+ const rawOffset = Number(args.offset);
133
+ const offset = Number.isFinite(rawOffset) && rawOffset > 0 ? rawOffset : 0;
134
+ const rawMax = Number(args.maxChars);
135
+ const maxChars = Number.isFinite(rawMax) && rawMax > 0
136
+ ? Math.min(rawMax, MAX_READ_CHARS)
137
+ : DEFAULT_READ_CHARS;
138
+ const file = await readWorkspaceFile(scope, path, {
139
+ offset,
140
+ maxChars,
141
+ });
142
+ if (!file) {
143
+ return JSON.stringify({
144
+ ok: false,
145
+ error: `File not found: "${path}"`,
146
+ });
147
+ }
148
+ const totalChars = file.sizeBytes; // approx
149
+ const truncated = file.content.length >= maxChars;
150
+ return JSON.stringify({
151
+ ok: true,
152
+ path: file.path,
153
+ contentType: file.contentType,
154
+ sizeBytes: file.sizeBytes,
155
+ updatedAt: file.updatedAt,
156
+ content: file.content,
157
+ ...(truncated
158
+ ? {
159
+ truncated: true,
160
+ nextOffset: offset + file.content.length,
161
+ hint: `File has more content. Call again with offset: ${offset + file.content.length}`,
162
+ }
163
+ : {}),
164
+ });
165
+ }
166
+ case "list": {
167
+ const prefix = args.path ? String(args.path).trim() : undefined;
168
+ const files = await listWorkspaceFiles(scope, prefix || undefined);
169
+ return JSON.stringify({
170
+ ok: true,
171
+ count: files.length,
172
+ files: files.map((f) => ({
173
+ path: f.path,
174
+ sizeBytes: f.sizeBytes,
175
+ contentType: f.contentType,
176
+ updatedAt: f.updatedAt,
177
+ })),
178
+ });
179
+ }
180
+ case "delete": {
181
+ const path = String(args.path ?? "").trim();
182
+ if (!path)
183
+ return "Error: path is required for delete.";
184
+ const deleted = await deleteWorkspaceFile(scope, path);
185
+ return JSON.stringify({
186
+ ok: true,
187
+ deleted,
188
+ path,
189
+ });
190
+ }
191
+ case "grep": {
192
+ const pattern = String(args.pattern ?? "").trim();
193
+ if (!pattern)
194
+ return "Error: pattern is required for grep.";
195
+ const prefix = args.path ? String(args.path).trim() : undefined;
196
+ const useRegex = args.useRegex === true || String(args.useRegex) === "true";
197
+ const matches = await grepWorkspaceFiles(scope, pattern, {
198
+ pathPrefix: prefix || undefined,
199
+ useRegex,
200
+ maxMatchesPerFile: 20,
201
+ maxFiles: 50,
202
+ });
203
+ return JSON.stringify({
204
+ ok: true,
205
+ pattern,
206
+ matchCount: matches.length,
207
+ matches: matches.map((m) => ({
208
+ path: m.path,
209
+ line: m.lineNumber,
210
+ text: m.line,
211
+ })),
212
+ });
213
+ }
214
+ default:
215
+ return `Error: unknown action "${action}". Valid actions: write, append, read, list, delete, grep.`;
216
+ }
217
+ }
218
+ catch (err) {
219
+ const msg = err instanceof Error ? err.message : String(err);
220
+ return JSON.stringify({ ok: false, error: msg });
221
+ }
222
+ },
223
+ },
224
+ };
225
+ }
226
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/workspace-files/tool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `workspace-files` agent tool.\n *\n * A single tool with an `action` discriminator covering write, append, read,\n * list, delete, and grep. Files persist across conversations; the agent uses\n * them to stage large intermediate results (fetched pages, per-item memos)\n * and then read back selectively for synthesis.\n *\n * Scope is automatically resolved from the active request context:\n * - org scope when a request orgId is present (shared across users in the org)\n * - user scope otherwise (personal to the requesting user's email)\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"../server/request-context.js\";\nimport {\n writeWorkspaceFile,\n appendWorkspaceFile,\n readWorkspaceFile,\n listWorkspaceFiles,\n deleteWorkspaceFile,\n grepWorkspaceFiles,\n type WorkspaceFilesScope,\n} from \"./store.js\";\n\nconst MAX_READ_CHARS = 100_000;\nconst DEFAULT_READ_CHARS = 40_000;\n\n/** Resolve scope from the current request context (org-preferred). */\nfunction resolveScope(): WorkspaceFilesScope | null {\n const orgId = getRequestOrgId();\n if (orgId) return { scope: \"org\", scopeId: orgId };\n const email = getRequestUserEmail();\n if (email) return { scope: \"user\", scopeId: email };\n return null;\n}\n\nexport function createWorkspaceFilesTool(): Record<string, ActionEntry> {\n return {\n \"workspace-files\": {\n readOnly: false,\n tool: {\n description: [\n \"Durable scratch-file storage for the agent. Files persist across conversations and are scoped to the current org/user.\",\n \"Use this to stage large intermediate results (fetched pages, per-item analysis memos, API payloads) so they don't consume context window, then read them back selectively for synthesis.\",\n \"\",\n \"Typical fusion-style workflow:\",\n \" 1. Fan out: for each item, fetch data and `write` a per-item memo file.\",\n \" 2. Synthesize: `list` files, then `read` each memo (with offset/maxChars to page large ones).\",\n \" 3. Optionally `grep` across all memos to find patterns.\",\n \" 4. `delete` temp files when no longer needed.\",\n \"\",\n \"Actions:\",\n \" write — create or overwrite a file. Max 2 MB per file, 200 MB total.\",\n \" append — append text to a file (or create if absent).\",\n \" read — read content, optionally with offset/maxChars for paging large files.\",\n \" list — list files (with optional path prefix filter) showing name, size, updated.\",\n \" delete — delete a file by path.\",\n \" grep — search content across files for a substring or regex.\",\n ].join(\"\\n\"),\n parameters: {\n type: \"object\",\n properties: {\n action: {\n type: \"string\",\n enum: [\"write\", \"append\", \"read\", \"list\", \"delete\", \"grep\"],\n description: \"Operation to perform.\",\n },\n path: {\n type: \"string\",\n description:\n 'File path relative to the scope root, e.g. \"analysis/q2-memos/acme.md\". Required for write/append/read/delete. Optional for list/grep (acts as prefix filter).',\n },\n content: {\n type: \"string\",\n description:\n \"Text content to write or append. Required for write/append.\",\n },\n contentType: {\n type: \"string\",\n description:\n 'MIME type for new files. Default: \"text/plain\". Use \"application/json\" for JSON, \"text/markdown\" for Markdown.',\n },\n offset: {\n type: \"number\",\n description:\n \"Character offset to start reading from (for paging large files). Default: 0.\",\n },\n maxChars: {\n type: \"number\",\n description: `Maximum characters to return when reading. Default: ${DEFAULT_READ_CHARS}. Max: ${MAX_READ_CHARS}.`,\n },\n pattern: {\n type: \"string\",\n description:\n \"Search pattern for grep. Required for grep action. A plain substring by default; set useRegex to true for a regex.\",\n },\n useRegex: {\n type: \"boolean\",\n description:\n \"When true, treat `pattern` as a JavaScript regex (case-insensitive). Default: false.\",\n },\n },\n required: [\"action\"],\n },\n },\n\n run: async (args: Record<string, unknown>): Promise<string> => {\n const scope = resolveScope();\n if (!scope) {\n return \"Error: workspace-files requires an authenticated request context.\";\n }\n\n const action = String(args.action ?? \"\").trim();\n\n try {\n switch (action) {\n case \"write\": {\n const path = String(args.path ?? \"\").trim();\n if (!path) return \"Error: path is required for write.\";\n const content = String(args.content ?? \"\");\n const contentType = String(args.contentType ?? \"text/plain\");\n const meta = await writeWorkspaceFile(\n scope,\n path,\n content,\n contentType,\n );\n return JSON.stringify({\n ok: true,\n action: \"write\",\n path: meta.path,\n sizeBytes: meta.sizeBytes,\n updatedAt: meta.updatedAt,\n });\n }\n\n case \"append\": {\n const path = String(args.path ?? \"\").trim();\n if (!path) return \"Error: path is required for append.\";\n const content = String(args.content ?? \"\");\n const contentType = String(args.contentType ?? \"text/plain\");\n const meta = await appendWorkspaceFile(\n scope,\n path,\n content,\n contentType,\n );\n return JSON.stringify({\n ok: true,\n action: \"append\",\n path: meta.path,\n sizeBytes: meta.sizeBytes,\n updatedAt: meta.updatedAt,\n });\n }\n\n case \"read\": {\n const path = String(args.path ?? \"\").trim();\n if (!path) return \"Error: path is required for read.\";\n const rawOffset = Number(args.offset);\n const offset =\n Number.isFinite(rawOffset) && rawOffset > 0 ? rawOffset : 0;\n const rawMax = Number(args.maxChars);\n const maxChars =\n Number.isFinite(rawMax) && rawMax > 0\n ? Math.min(rawMax, MAX_READ_CHARS)\n : DEFAULT_READ_CHARS;\n\n const file = await readWorkspaceFile(scope, path, {\n offset,\n maxChars,\n });\n if (!file) {\n return JSON.stringify({\n ok: false,\n error: `File not found: \"${path}\"`,\n });\n }\n\n const totalChars = file.sizeBytes; // approx\n const truncated = file.content.length >= maxChars;\n return JSON.stringify({\n ok: true,\n path: file.path,\n contentType: file.contentType,\n sizeBytes: file.sizeBytes,\n updatedAt: file.updatedAt,\n content: file.content,\n ...(truncated\n ? {\n truncated: true,\n nextOffset: offset + file.content.length,\n hint: `File has more content. Call again with offset: ${offset + file.content.length}`,\n }\n : {}),\n });\n }\n\n case \"list\": {\n const prefix = args.path ? String(args.path).trim() : undefined;\n const files = await listWorkspaceFiles(\n scope,\n prefix || undefined,\n );\n return JSON.stringify({\n ok: true,\n count: files.length,\n files: files.map((f) => ({\n path: f.path,\n sizeBytes: f.sizeBytes,\n contentType: f.contentType,\n updatedAt: f.updatedAt,\n })),\n });\n }\n\n case \"delete\": {\n const path = String(args.path ?? \"\").trim();\n if (!path) return \"Error: path is required for delete.\";\n const deleted = await deleteWorkspaceFile(scope, path);\n return JSON.stringify({\n ok: true,\n deleted,\n path,\n });\n }\n\n case \"grep\": {\n const pattern = String(args.pattern ?? \"\").trim();\n if (!pattern) return \"Error: pattern is required for grep.\";\n const prefix = args.path ? String(args.path).trim() : undefined;\n const useRegex =\n args.useRegex === true || String(args.useRegex) === \"true\";\n const matches = await grepWorkspaceFiles(scope, pattern, {\n pathPrefix: prefix || undefined,\n useRegex,\n maxMatchesPerFile: 20,\n maxFiles: 50,\n });\n return JSON.stringify({\n ok: true,\n pattern,\n matchCount: matches.length,\n matches: matches.map((m) => ({\n path: m.path,\n line: m.lineNumber,\n text: m.line,\n })),\n });\n }\n\n default:\n return `Error: unknown action \"${action}\". Valid actions: write, append, read, list, delete, grep.`;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return JSON.stringify({ ok: false, error: msg });\n }\n },\n },\n };\n}\n"]}