@agent-native/core 0.49.27 → 0.51.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 (95) hide show
  1. package/dist/agent/harness/ai-sdk-adapter.d.ts +13 -0
  2. package/dist/agent/harness/ai-sdk-adapter.d.ts.map +1 -0
  3. package/dist/agent/harness/ai-sdk-adapter.js +232 -0
  4. package/dist/agent/harness/ai-sdk-adapter.js.map +1 -0
  5. package/dist/agent/harness/background.d.ts +15 -0
  6. package/dist/agent/harness/background.d.ts.map +1 -0
  7. package/dist/agent/harness/background.js +233 -0
  8. package/dist/agent/harness/background.js.map +1 -0
  9. package/dist/agent/harness/builtin.d.ts +2 -0
  10. package/dist/agent/harness/builtin.d.ts.map +1 -0
  11. package/dist/agent/harness/builtin.js +24 -0
  12. package/dist/agent/harness/builtin.js.map +1 -0
  13. package/dist/agent/harness/index.d.ts +9 -0
  14. package/dist/agent/harness/index.d.ts.map +1 -0
  15. package/dist/agent/harness/index.js +8 -0
  16. package/dist/agent/harness/index.js.map +1 -0
  17. package/dist/agent/harness/registry.d.ts +15 -0
  18. package/dist/agent/harness/registry.d.ts.map +1 -0
  19. package/dist/agent/harness/registry.js +70 -0
  20. package/dist/agent/harness/registry.js.map +1 -0
  21. package/dist/agent/harness/runner.d.ts +21 -0
  22. package/dist/agent/harness/runner.d.ts.map +1 -0
  23. package/dist/agent/harness/runner.js +86 -0
  24. package/dist/agent/harness/runner.js.map +1 -0
  25. package/dist/agent/harness/store.d.ts +46 -0
  26. package/dist/agent/harness/store.d.ts.map +1 -0
  27. package/dist/agent/harness/store.js +304 -0
  28. package/dist/agent/harness/store.js.map +1 -0
  29. package/dist/agent/harness/translate.d.ts +5 -0
  30. package/dist/agent/harness/translate.d.ts.map +1 -0
  31. package/dist/agent/harness/translate.js +120 -0
  32. package/dist/agent/harness/translate.js.map +1 -0
  33. package/dist/agent/harness/types.d.ts +127 -0
  34. package/dist/agent/harness/types.d.ts.map +1 -0
  35. package/dist/agent/harness/types.js +2 -0
  36. package/dist/agent/harness/types.js.map +1 -0
  37. package/dist/agent/production-agent.d.ts.map +1 -1
  38. package/dist/agent/production-agent.js +2 -0
  39. package/dist/agent/production-agent.js.map +1 -1
  40. package/dist/cli/create.d.ts.map +1 -1
  41. package/dist/cli/create.js +10 -1
  42. package/dist/cli/create.js.map +1 -1
  43. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  44. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  45. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  46. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  47. package/dist/cli/skills.d.ts +1 -1
  48. package/dist/cli/skills.d.ts.map +1 -1
  49. package/dist/cli/skills.js +8 -1
  50. package/dist/cli/skills.js.map +1 -1
  51. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  52. package/dist/client/blocks/library/wireframe.js +0 -1
  53. package/dist/client/blocks/library/wireframe.js.map +1 -1
  54. package/dist/client/resources/use-resources.d.ts.map +1 -1
  55. package/dist/client/resources/use-resources.js +4 -2
  56. package/dist/client/resources/use-resources.js.map +1 -1
  57. package/dist/code-agents/background-run.d.ts +6 -4
  58. package/dist/code-agents/background-run.d.ts.map +1 -1
  59. package/dist/code-agents/background-run.js.map +1 -1
  60. package/dist/coding-tools/run-code.js +6 -4
  61. package/dist/coding-tools/run-code.js.map +1 -1
  62. package/dist/provider-api/index.d.ts +20 -12
  63. package/dist/provider-api/index.d.ts.map +1 -1
  64. package/dist/provider-api/index.js +157 -37
  65. package/dist/provider-api/index.js.map +1 -1
  66. package/dist/provider-api/quota-governor.d.ts +53 -0
  67. package/dist/provider-api/quota-governor.d.ts.map +1 -0
  68. package/dist/provider-api/quota-governor.js +395 -0
  69. package/dist/provider-api/quota-governor.js.map +1 -0
  70. package/dist/provider-api/staging.d.ts.map +1 -1
  71. package/dist/provider-api/staging.js +16 -0
  72. package/dist/provider-api/staging.js.map +1 -1
  73. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  74. package/dist/server/agent-chat-plugin.js +34 -10
  75. package/dist/server/agent-chat-plugin.js.map +1 -1
  76. package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +98 -0
  77. package/dist/workspace-files/index.d.ts +0 -1
  78. package/dist/workspace-files/index.d.ts.map +1 -1
  79. package/dist/workspace-files/index.js +0 -1
  80. package/dist/workspace-files/index.js.map +1 -1
  81. package/dist/workspace-files/store.d.ts +14 -9
  82. package/dist/workspace-files/store.d.ts.map +1 -1
  83. package/dist/workspace-files/store.js +89 -164
  84. package/dist/workspace-files/store.js.map +1 -1
  85. package/dist/workspace-files/tool.d.ts +3 -4
  86. package/dist/workspace-files/tool.d.ts.map +1 -1
  87. package/dist/workspace-files/tool.js +8 -7
  88. package/dist/workspace-files/tool.js.map +1 -1
  89. package/docs/content/deployment.md +1 -1
  90. package/package.json +2 -1
  91. package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +98 -0
  92. package/dist/workspace-files/schema.d.ts +0 -195
  93. package/dist/workspace-files/schema.d.ts.map +0 -1
  94. package/dist/workspace-files/schema.js +0 -48
  95. package/dist/workspace-files/schema.js.map +0 -1
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: harness-agents
3
+ description: >-
4
+ Add or use full agent harness runtimes like Claude Code, Codex, Pi, Cursor, or Mastra inside Agent Native.
5
+ scope: dev
6
+ ---
7
+
8
+ # Harness Agents
9
+
10
+ ## Rule
11
+
12
+ Full agent harnesses are not `AgentEngine` providers. Use the `AgentHarness`
13
+ substrate in `@agent-native/core/agent/harness`.
14
+
15
+ ## Why
16
+
17
+ `AgentEngine` is for one model round trip beneath `runAgentLoop`. Harnesses like
18
+ Claude Code, Codex, Pi, Cursor, and Mastra own their own loop, workspace,
19
+ native tools, session state, compaction, approval model, and sandbox behavior.
20
+ Putting a harness under `AgentEngine.stream()` double-runs the loop and loses
21
+ session lifecycle semantics.
22
+
23
+ ## How
24
+
25
+ 1. Register or resolve a harness adapter.
26
+
27
+ ```ts
28
+ import {
29
+ registerBuiltinAgentHarnesses,
30
+ resolveAgentHarness,
31
+ } from "@agent-native/core/agent/harness";
32
+
33
+ registerBuiltinAgentHarnesses();
34
+ const harness = resolveAgentHarness("ai-sdk-harness:codex");
35
+ ```
36
+
37
+ 2. Start a turn through the run-manager bridge.
38
+
39
+ ```ts
40
+ import { startAgentHarnessRun } from "@agent-native/core/agent/harness";
41
+
42
+ startAgentHarnessRun({
43
+ runId,
44
+ threadId,
45
+ adapter: harness,
46
+ input: { prompt },
47
+ createSession: {
48
+ sessionId,
49
+ resumeState,
50
+ instructions,
51
+ sandbox,
52
+ permissionMode: "allow-reads",
53
+ },
54
+ ownerEmail,
55
+ orgId,
56
+ });
57
+ ```
58
+
59
+ 3. Persist native session state in SQL.
60
+
61
+ Use `saveAgentHarnessSession`, `updateAgentHarnessSession`, and
62
+ `getLatestAgentHarnessSessionForThread`. The `resumeState` is opaque; Agent
63
+ Native stores it but does not inspect it.
64
+
65
+ 4. Surface runs through background agents.
66
+
67
+ Harness runs are projected into the shared `BackgroundAgentRun` shape with
68
+ `createAgentHarnessBackgroundAgentController()` and are available through the
69
+ existing run routes as `goalId=agent-harness`.
70
+
71
+ ## Adapter Guidance
72
+
73
+ - Keep harness packages optional. Use dynamic imports in adapters and expose an
74
+ install hint through `installPackage`.
75
+ - Use the AI SDK harness adapter as one implementation, not as Agent Native's
76
+ public abstraction.
77
+ - For bridge-backed coding harnesses, require a real sandbox/workspace provider.
78
+ Do not run arbitrary coding agents in the host process by default.
79
+ - Pass only a narrow, intentional set of Agent Native actions as host tools.
80
+ Preserve `defineAction` auth, request context, timeouts, truncation, and
81
+ read-only metadata.
82
+
83
+ ## Don't
84
+
85
+ - Don't add Claude Code, Codex, Cursor, Mastra, or Pi as an `AgentEngine`.
86
+ - Don't replay full Agent Native chat history into a native harness each turn.
87
+ Resume the harness session instead.
88
+ - Don't store resume state in `application_state`; it belongs in the harness
89
+ session SQL table.
90
+ - Don't expose every app action to every harness session by default.
91
+
92
+ ## Related Skills
93
+
94
+ - `adding-a-feature` — feature parity across UI/actions/instructions/state.
95
+ - `delegate-to-agent` — background agents use run-manager infrastructure.
96
+ - `external-agents` — expose openable resources and external-agent surfaces.
97
+ - `storing-data` — durable SQL state and additive schema changes.
98
+
@@ -1,4 +1,3 @@
1
1
  export { writeWorkspaceFile, appendWorkspaceFile, readWorkspaceFile, getWorkspaceFileMeta, listWorkspaceFiles, deleteWorkspaceFile, grepWorkspaceFiles, validatePath, MAX_FILE_BYTES, MAX_SCOPE_BYTES, SAVE_TO_FILE_MAX_BYTES, type WorkspaceFilesScope, type WorkspaceFile, type WorkspaceFileMeta, } from "./store.js";
2
2
  export { createWorkspaceFilesTool } from "./tool.js";
3
- export { WORKSPACE_FILES_CREATE_SQL, WORKSPACE_FILES_INDEX_SQL, } from "./schema.js";
4
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace-files/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace-files/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC"}
@@ -1,4 +1,3 @@
1
1
  export { writeWorkspaceFile, appendWorkspaceFile, readWorkspaceFile, getWorkspaceFileMeta, listWorkspaceFiles, deleteWorkspaceFile, grepWorkspaceFiles, validatePath, MAX_FILE_BYTES, MAX_SCOPE_BYTES, SAVE_TO_FILE_MAX_BYTES, } from "./store.js";
2
2
  export { createWorkspaceFilesTool } from "./tool.js";
3
- export { WORKSPACE_FILES_CREATE_SQL, WORKSPACE_FILES_INDEX_SQL, } from "./schema.js";
4
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace-files/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,sBAAsB,GAIvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,aAAa,CAAC","sourcesContent":["export {\n writeWorkspaceFile,\n appendWorkspaceFile,\n readWorkspaceFile,\n getWorkspaceFileMeta,\n listWorkspaceFiles,\n deleteWorkspaceFile,\n grepWorkspaceFiles,\n validatePath,\n MAX_FILE_BYTES,\n MAX_SCOPE_BYTES,\n SAVE_TO_FILE_MAX_BYTES,\n type WorkspaceFilesScope,\n type WorkspaceFile,\n type WorkspaceFileMeta,\n} from \"./store.js\";\n\nexport { createWorkspaceFilesTool } from \"./tool.js\";\nexport {\n WORKSPACE_FILES_CREATE_SQL,\n WORKSPACE_FILES_INDEX_SQL,\n} from \"./schema.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace-files/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,eAAe,EACf,sBAAsB,GAIvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC","sourcesContent":["export {\n writeWorkspaceFile,\n appendWorkspaceFile,\n readWorkspaceFile,\n getWorkspaceFileMeta,\n listWorkspaceFiles,\n deleteWorkspaceFile,\n grepWorkspaceFiles,\n validatePath,\n MAX_FILE_BYTES,\n MAX_SCOPE_BYTES,\n SAVE_TO_FILE_MAX_BYTES,\n type WorkspaceFilesScope,\n type WorkspaceFile,\n type WorkspaceFileMeta,\n} from \"./store.js\";\n\nexport { createWorkspaceFilesTool } from \"./tool.js\";\n"]}
@@ -1,15 +1,20 @@
1
1
  /**
2
- * Workspace-files store.
2
+ * Compatibility wrapper for the old `workspace-files` API.
3
3
  *
4
- * Provides read/write/append/list/delete/grep operations over the
5
- * `workspace_files` table. All writes enforce per-file (2 MB) and per-scope
6
- * (200 MB) caps. The table is lazily migrated on first use.
4
+ * Storage now goes through the core Resources table so agent files live in the
5
+ * same workspace the user manages in the Resources panel. Paths under
6
+ * `scratch/` are hidden agent scratch; every other path is a normal visible
7
+ * resource in the current personal or organization scope.
7
8
  */
8
- /** Max content size per file (bytes). */
9
+ /** Max content size per file (bytes) for direct workspaceWrite calls. */
9
10
  export declare const MAX_FILE_BYTES: number;
10
- /** Max total content size across all files in one scope (bytes). */
11
+ /**
12
+ * Legacy export retained for API compatibility. The Resources store is the
13
+ * canonical quota surface now, so the compatibility wrapper does not maintain a
14
+ * separate per-scope total.
15
+ */
11
16
  export declare const MAX_SCOPE_BYTES: number;
12
- /** Max content size when saving via saveToFile from provider-api / fetch tool (bytes). */
17
+ /** Max content size when saving via saveToFile from provider-api / fetch tool. */
13
18
  export declare const SAVE_TO_FILE_MAX_BYTES: number;
14
19
  export interface WorkspaceFilesScope {
15
20
  scope: "user" | "org";
@@ -41,8 +46,8 @@ export interface WorkspaceFileMeta {
41
46
  }
42
47
  /**
43
48
  * Write (create or overwrite) a workspace file.
44
- * Enforces per-file (2 MB default; `saveToFile` callers may raise it up to
45
- * 20 MB via `opts.maxFileBytes`) and per-scope (200 MB) caps.
49
+ * Enforces per-file limits for the compatibility API; persistence is handled by
50
+ * Resources. Use `scratch/...` for temporary hidden agent files.
46
51
  */
47
52
  export declare function writeWorkspaceFile(scope: WorkspaceFilesScope, path: string, content: string, contentType?: string, opts?: {
48
53
  maxFileBytes?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/workspace-files/store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,yCAAyC;AACzC,eAAO,MAAM,cAAc,QAAkB,CAAC;AAE9C,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAoB,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAoBvD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWxD;AAMD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,SAAe,EAC1B,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CA4E5B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,WAAW,SAAe,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAQ5B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAkC/B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAmBnC;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA+B9B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IACL,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA+BpE"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/workspace-files/store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiBH,yEAAyE;AACzE,eAAO,MAAM,cAAc,QAAkB,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAAoB,CAAC;AAEjD,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAMvD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAoBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWxD;AAMD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,SAAe,EAC1B,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CA4B5B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,WAAW,SAAe,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAO5B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAiB/B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAMnC;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiB9B;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IACL,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA+BpE"}
@@ -1,33 +1,39 @@
1
1
  /**
2
- * Workspace-files store.
2
+ * Compatibility wrapper for the old `workspace-files` API.
3
3
  *
4
- * Provides read/write/append/list/delete/grep operations over the
5
- * `workspace_files` table. All writes enforce per-file (2 MB) and per-scope
6
- * (200 MB) caps. The table is lazily migrated on first use.
4
+ * Storage now goes through the core Resources table so agent files live in the
5
+ * same workspace the user manages in the Resources panel. Paths under
6
+ * `scratch/` are hidden agent scratch; every other path is a normal visible
7
+ * resource in the current personal or organization scope.
7
8
  */
8
- import crypto from "node:crypto";
9
- import { getDbExec } from "../db/client.js";
10
- import { WORKSPACE_FILES_CREATE_SQL, WORKSPACE_FILES_INDEX_SQL, } from "./schema.js";
9
+ import { SHARED_OWNER, resourceDeleteByPath, resourceGetByPath, resourceList, resourcePut, } from "../resources/store.js";
11
10
  // ---------------------------------------------------------------------------
12
11
  // Constants
13
12
  // ---------------------------------------------------------------------------
14
- /** Max content size per file (bytes). */
13
+ /** Max content size per file (bytes) for direct workspaceWrite calls. */
15
14
  export const MAX_FILE_BYTES = 2 * 1024 * 1024; // 2 MB
16
- /** Max total content size across all files in one scope (bytes). */
15
+ /**
16
+ * Legacy export retained for API compatibility. The Resources store is the
17
+ * canonical quota surface now, so the compatibility wrapper does not maintain a
18
+ * separate per-scope total.
19
+ */
17
20
  export const MAX_SCOPE_BYTES = 200 * 1024 * 1024; // 200 MB
18
- /** Max content size when saving via saveToFile from provider-api / fetch tool (bytes). */
21
+ /** Max content size when saving via saveToFile from provider-api / fetch tool. */
19
22
  export const SAVE_TO_FILE_MAX_BYTES = 20 * 1024 * 1024; // 20 MB
20
- // ---------------------------------------------------------------------------
21
- // Lazy table init
22
- // ---------------------------------------------------------------------------
23
- let _tableReady = false;
24
- async function ensureTable() {
25
- if (_tableReady)
26
- return;
27
- const db = getDbExec();
28
- await db.execute(WORKSPACE_FILES_CREATE_SQL);
29
- await db.execute(WORKSPACE_FILES_INDEX_SQL);
30
- _tableReady = true;
23
+ function ownerForScope(scope) {
24
+ return scope.scope === "org" ? SHARED_OWNER : scope.scopeId;
25
+ }
26
+ function visibilityForPath(path) {
27
+ return path === "scratch" || path.startsWith("scratch/")
28
+ ? "agent_scratch"
29
+ : "workspace";
30
+ }
31
+ function workspaceFileMetadata(scope) {
32
+ return {
33
+ source: "workspace-files",
34
+ scope: scope.scope,
35
+ scopeId: scope.scopeId,
36
+ };
31
37
  }
32
38
  /**
33
39
  * Validate a workspace file path.
@@ -54,8 +60,8 @@ export function validatePath(path) {
54
60
  // ---------------------------------------------------------------------------
55
61
  /**
56
62
  * Write (create or overwrite) a workspace file.
57
- * Enforces per-file (2 MB default; `saveToFile` callers may raise it up to
58
- * 20 MB via `opts.maxFileBytes`) and per-scope (200 MB) caps.
63
+ * Enforces per-file limits for the compatibility API; persistence is handled by
64
+ * Resources. Use `scratch/...` for temporary hidden agent files.
59
65
  */
60
66
  export async function writeWorkspaceFile(scope, path, content, contentType = "text/plain", opts) {
61
67
  const pathErr = validatePath(path);
@@ -66,61 +72,12 @@ export async function writeWorkspaceFile(scope, path, content, contentType = "te
66
72
  if (bytes > maxFileBytes) {
67
73
  throw new Error(`File "${path}" would be ${(bytes / 1024 / 1024).toFixed(2)} MB, which exceeds the ${(maxFileBytes / 1024 / 1024).toFixed(0)} MB per-file limit.`);
68
74
  }
69
- await ensureTable();
70
- const db = getDbExec();
71
- // Check scope total (excluding current file's existing bytes).
72
- const existing = await getWorkspaceFileMeta(scope, path);
73
- const existingBytes = existing?.sizeBytes ?? 0;
74
- const scopeTotal = await getScopeTotalBytes(scope);
75
- const newTotal = scopeTotal - existingBytes + bytes;
76
- if (newTotal > MAX_SCOPE_BYTES) {
77
- throw new Error(`Writing "${path}" would bring the workspace total to ${(newTotal / 1024 / 1024).toFixed(1)} MB, exceeding the 200 MB limit.`);
78
- }
79
- const now = new Date().toISOString();
80
- if (existing) {
81
- await db.execute({
82
- sql: `UPDATE workspace_files SET content = ?, content_type = ?, size_bytes = ?, updated_at = ? WHERE scope = ? AND scope_id = ? AND path = ?`,
83
- args: [
84
- content,
85
- contentType,
86
- bytes,
87
- now,
88
- scope.scope,
89
- scope.scopeId,
90
- path,
91
- ],
92
- });
93
- return {
94
- ...existing,
95
- content: undefined,
96
- contentType,
97
- sizeBytes: bytes,
98
- updatedAt: now,
99
- };
100
- }
101
- const id = crypto.randomUUID();
102
- await db.execute({
103
- sql: `INSERT INTO workspace_files (id, scope, scope_id, path, content, content_type, size_bytes, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
104
- args: [
105
- id,
106
- scope.scope,
107
- scope.scopeId,
108
- path,
109
- content,
110
- contentType,
111
- bytes,
112
- now,
113
- now,
114
- ],
75
+ const resource = await resourcePut(ownerForScope(scope), path, content, contentType, {
76
+ createdBy: "agent",
77
+ visibility: visibilityForPath(path),
78
+ metadata: workspaceFileMetadata(scope),
115
79
  });
116
- return {
117
- id,
118
- path,
119
- contentType,
120
- sizeBytes: bytes,
121
- createdAt: now,
122
- updatedAt: now,
123
- };
80
+ return resourceToMeta(resource);
124
81
  }
125
82
  /**
126
83
  * Append text to an existing workspace file, or create it if it doesn't exist.
@@ -129,8 +86,7 @@ export async function appendWorkspaceFile(scope, path, text, contentType = "text
129
86
  const pathErr = validatePath(path);
130
87
  if (pathErr)
131
88
  throw new Error(`Invalid path: ${pathErr}`);
132
- await ensureTable();
133
- const existing = await readWorkspaceFile(scope, path);
89
+ const existing = await resourceGetByPath(ownerForScope(scope), path);
134
90
  const newContent = existing ? existing.content + text : text;
135
91
  return writeWorkspaceFile(scope, path, newContent, contentType);
136
92
  }
@@ -142,87 +98,43 @@ export async function readWorkspaceFile(scope, path, opts) {
142
98
  const pathErr = validatePath(path);
143
99
  if (pathErr)
144
100
  throw new Error(`Invalid path: ${pathErr}`);
145
- await ensureTable();
146
- const db = getDbExec();
147
- const result = await db.execute({
148
- sql: `SELECT id, scope, scope_id, path, content, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? AND path = ?`,
149
- args: [scope.scope, scope.scopeId, path],
150
- });
151
- const row = result.rows[0];
152
- if (!row)
101
+ const resource = await resourceGetByPath(ownerForScope(scope), path);
102
+ if (!resource)
153
103
  return null;
154
- let content = String(row[4] ?? "");
104
+ let content = resource.content;
155
105
  if (opts?.offset || opts?.maxChars) {
156
106
  const off = opts.offset ?? 0;
157
107
  content = content.slice(off, opts.maxChars !== undefined ? off + opts.maxChars : undefined);
158
108
  }
159
- return {
160
- id: String(row[0]),
161
- scope: String(row[1]),
162
- scopeId: String(row[2]),
163
- path: String(row[3]),
164
- content,
165
- contentType: String(row[5] ?? "text/plain"),
166
- sizeBytes: Number(row[6] ?? 0),
167
- createdAt: String(row[7]),
168
- updatedAt: String(row[8]),
169
- };
109
+ return resourceToFile(resource, scope, content);
170
110
  }
171
111
  /**
172
112
  * Get file metadata without loading content.
173
113
  */
174
114
  export async function getWorkspaceFileMeta(scope, path) {
175
- await ensureTable();
176
- const db = getDbExec();
177
- const result = await db.execute({
178
- sql: `SELECT id, path, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? AND path = ?`,
179
- args: [scope.scope, scope.scopeId, path],
180
- });
181
- const row = result.rows[0];
182
- if (!row)
183
- return null;
184
- return {
185
- id: String(row[0]),
186
- path: String(row[1]),
187
- contentType: String(row[2] ?? "text/plain"),
188
- sizeBytes: Number(row[3] ?? 0),
189
- createdAt: String(row[4]),
190
- updatedAt: String(row[5]),
191
- };
115
+ const pathErr = validatePath(path);
116
+ if (pathErr)
117
+ throw new Error(`Invalid path: ${pathErr}`);
118
+ const resource = await resourceGetByPath(ownerForScope(scope), path);
119
+ return resource ? resourceToMeta(resource) : null;
192
120
  }
193
121
  /**
194
122
  * List workspace files, optionally filtered by path prefix.
195
123
  * Returns metadata only (no content).
196
124
  */
197
125
  export async function listWorkspaceFiles(scope, prefix) {
198
- await ensureTable();
199
- const db = getDbExec();
200
- if (prefix) {
201
- // Allow a trailing slash on list prefixes, but reject traversal and
202
- // other invalid path shapes before they reach the LIKE pattern.
203
- const normalizedPrefix = prefix.endsWith("/")
204
- ? prefix.slice(0, -1)
205
- : prefix;
206
- const pathErr = validatePath(normalizedPrefix);
207
- if (pathErr) {
208
- throw new Error(pathErr);
209
- }
210
- const result = await db.execute({
211
- sql: `SELECT id, path, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? AND (path = ? OR path LIKE ? ESCAPE '\\') ORDER BY path ASC`,
212
- args: [
213
- scope.scope,
214
- scope.scopeId,
215
- normalizedPrefix,
216
- `${normalizedPrefix.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_")}/%`,
217
- ],
218
- });
219
- return result.rows.map(rowToMeta);
220
- }
221
- const result = await db.execute({
222
- sql: `SELECT id, path, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? ORDER BY path ASC`,
223
- args: [scope.scope, scope.scopeId],
126
+ const owner = ownerForScope(scope);
127
+ const normalizedPrefix = normalizePrefix(prefix);
128
+ const resources = await resourceList(owner, normalizedPrefix, {
129
+ includeAgentScratch: true,
224
130
  });
225
- return result.rows.map(rowToMeta);
131
+ const filtered = normalizedPrefix
132
+ ? resources.filter((resource) => resource.path === normalizedPrefix ||
133
+ resource.path.startsWith(`${normalizedPrefix}/`))
134
+ : resources;
135
+ return filtered
136
+ .map(resourceToMeta)
137
+ .sort((a, b) => a.path.localeCompare(b.path));
226
138
  }
227
139
  /**
228
140
  * Delete a workspace file. Returns true if deleted, false if not found.
@@ -231,13 +143,7 @@ export async function deleteWorkspaceFile(scope, path) {
231
143
  const pathErr = validatePath(path);
232
144
  if (pathErr)
233
145
  throw new Error(`Invalid path: ${pathErr}`);
234
- await ensureTable();
235
- const db = getDbExec();
236
- const result = await db.execute({
237
- sql: `DELETE FROM workspace_files WHERE scope = ? AND scope_id = ? AND path = ?`,
238
- args: [scope.scope, scope.scopeId, path],
239
- });
240
- return result.rowsAffected > 0;
146
+ return resourceDeleteByPath(ownerForScope(scope), path);
241
147
  }
242
148
  /**
243
149
  * Search file contents for a substring or regex pattern.
@@ -277,22 +183,41 @@ export async function grepWorkspaceFiles(scope, pattern, opts) {
277
183
  // ---------------------------------------------------------------------------
278
184
  // Internal helpers
279
185
  // ---------------------------------------------------------------------------
280
- async function getScopeTotalBytes(scope) {
281
- const db = getDbExec();
282
- const result = await db.execute({
283
- sql: `SELECT COALESCE(SUM(size_bytes), 0) as total FROM workspace_files WHERE scope = ? AND scope_id = ?`,
284
- args: [scope.scope, scope.scopeId],
285
- });
286
- return Number(result.rows[0]?.[0] ?? 0);
186
+ function normalizePrefix(prefix) {
187
+ if (!prefix)
188
+ return undefined;
189
+ const normalized = prefix.replace(/\/+$/, "");
190
+ if (!normalized)
191
+ return undefined;
192
+ const pathErr = validatePath(normalized);
193
+ if (pathErr)
194
+ throw new Error(pathErr);
195
+ return normalized;
196
+ }
197
+ function isoTime(value) {
198
+ return new Date(value).toISOString();
287
199
  }
288
- function rowToMeta(row) {
200
+ function resourceToMeta(resource) {
289
201
  return {
290
- id: String(row[0]),
291
- path: String(row[1]),
292
- contentType: String(row[2] ?? "text/plain"),
293
- sizeBytes: Number(row[3] ?? 0),
294
- createdAt: String(row[4]),
295
- updatedAt: String(row[5]),
202
+ id: resource.id,
203
+ path: resource.path,
204
+ contentType: resource.mimeType,
205
+ sizeBytes: resource.size,
206
+ createdAt: isoTime(resource.createdAt),
207
+ updatedAt: isoTime(resource.updatedAt),
208
+ };
209
+ }
210
+ function resourceToFile(resource, scope, content) {
211
+ return {
212
+ id: resource.id,
213
+ scope: scope.scope,
214
+ scopeId: scope.scopeId,
215
+ path: resource.path,
216
+ content,
217
+ contentType: resource.mimeType,
218
+ sizeBytes: resource.size,
219
+ createdAt: isoTime(resource.createdAt),
220
+ updatedAt: isoTime(resource.updatedAt),
296
221
  };
297
222
  }
298
223
  //# sourceMappingURL=store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/workspace-files/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workspace-files store.\n *\n * Provides read/write/append/list/delete/grep operations over the\n * `workspace_files` table. All writes enforce per-file (2 MB) and per-scope\n * (200 MB) caps. The table is lazily migrated on first use.\n */\n\nimport crypto from \"node:crypto\";\nimport { getDbExec } from \"../db/client.js\";\nimport {\n WORKSPACE_FILES_CREATE_SQL,\n WORKSPACE_FILES_INDEX_SQL,\n} from \"./schema.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Max content size per file (bytes). */\nexport const MAX_FILE_BYTES = 2 * 1024 * 1024; // 2 MB\n\n/** Max total content size across all files in one scope (bytes). */\nexport const MAX_SCOPE_BYTES = 200 * 1024 * 1024; // 200 MB\n\n/** Max content size when saving via saveToFile from provider-api / fetch tool (bytes). */\nexport const SAVE_TO_FILE_MAX_BYTES = 20 * 1024 * 1024; // 20 MB\n\n// ---------------------------------------------------------------------------\n// Lazy table init\n// ---------------------------------------------------------------------------\n\nlet _tableReady = false;\n\nasync function ensureTable(): Promise<void> {\n if (_tableReady) return;\n const db = getDbExec();\n await db.execute(WORKSPACE_FILES_CREATE_SQL);\n await db.execute(WORKSPACE_FILES_INDEX_SQL);\n _tableReady = true;\n}\n\n// ---------------------------------------------------------------------------\n// Scope helpers\n// ---------------------------------------------------------------------------\n\nexport interface WorkspaceFilesScope {\n scope: \"user\" | \"org\";\n scopeId: string;\n}\n\n/**\n * Validate a workspace file path.\n * - Non-empty, no leading slash, no \"..\" components, no null bytes.\n */\nexport function validatePath(path: string): string | null {\n if (!path || typeof path !== \"string\") return \"path is required\";\n if (path.startsWith(\"/\")) return 'path must not start with \"/\"';\n if (path.includes(\"\\0\")) return \"path must not contain null bytes\";\n const parts = path.split(\"/\");\n for (const part of parts) {\n if (part === \"..\") return 'path must not contain \"..\" components';\n if (part === \"\")\n return 'path must not contain empty segments (\"//\" or trailing \"/\")';\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface WorkspaceFile {\n id: string;\n scope: string;\n scopeId: string;\n path: string;\n content: string;\n contentType: string;\n sizeBytes: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface WorkspaceFileMeta {\n id: string;\n path: string;\n contentType: string;\n sizeBytes: number;\n createdAt: string;\n updatedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Store operations\n// ---------------------------------------------------------------------------\n\n/**\n * Write (create or overwrite) a workspace file.\n * Enforces per-file (2 MB default; `saveToFile` callers may raise it up to\n * 20 MB via `opts.maxFileBytes`) and per-scope (200 MB) caps.\n */\nexport async function writeWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n content: string,\n contentType = \"text/plain\",\n opts?: { maxFileBytes?: number },\n): Promise<WorkspaceFileMeta> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n const maxFileBytes = Math.min(\n opts?.maxFileBytes ?? MAX_FILE_BYTES,\n SAVE_TO_FILE_MAX_BYTES,\n );\n const bytes = Buffer.byteLength(content, \"utf8\");\n if (bytes > maxFileBytes) {\n throw new Error(\n `File \"${path}\" would be ${(bytes / 1024 / 1024).toFixed(2)} MB, which exceeds the ${(maxFileBytes / 1024 / 1024).toFixed(0)} MB per-file limit.`,\n );\n }\n\n await ensureTable();\n const db = getDbExec();\n\n // Check scope total (excluding current file's existing bytes).\n const existing = await getWorkspaceFileMeta(scope, path);\n const existingBytes = existing?.sizeBytes ?? 0;\n const scopeTotal = await getScopeTotalBytes(scope);\n const newTotal = scopeTotal - existingBytes + bytes;\n if (newTotal > MAX_SCOPE_BYTES) {\n throw new Error(\n `Writing \"${path}\" would bring the workspace total to ${(newTotal / 1024 / 1024).toFixed(1)} MB, exceeding the 200 MB limit.`,\n );\n }\n\n const now = new Date().toISOString();\n\n if (existing) {\n await db.execute({\n sql: `UPDATE workspace_files SET content = ?, content_type = ?, size_bytes = ?, updated_at = ? WHERE scope = ? AND scope_id = ? AND path = ?`,\n args: [\n content,\n contentType,\n bytes,\n now,\n scope.scope,\n scope.scopeId,\n path,\n ],\n });\n return {\n ...existing,\n content: undefined as any,\n contentType,\n sizeBytes: bytes,\n updatedAt: now,\n } as unknown as WorkspaceFileMeta;\n }\n\n const id = crypto.randomUUID();\n await db.execute({\n sql: `INSERT INTO workspace_files (id, scope, scope_id, path, content, content_type, size_bytes, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n scope.scope,\n scope.scopeId,\n path,\n content,\n contentType,\n bytes,\n now,\n now,\n ],\n });\n return {\n id,\n path,\n contentType,\n sizeBytes: bytes,\n createdAt: now,\n updatedAt: now,\n };\n}\n\n/**\n * Append text to an existing workspace file, or create it if it doesn't exist.\n */\nexport async function appendWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n text: string,\n contentType = \"text/plain\",\n): Promise<WorkspaceFileMeta> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n await ensureTable();\n const existing = await readWorkspaceFile(scope, path);\n const newContent = existing ? existing.content + text : text;\n return writeWorkspaceFile(scope, path, newContent, contentType);\n}\n\n/**\n * Read a workspace file's content (with optional offset and maxChars for paging).\n * Returns null if the file doesn't exist.\n */\nexport async function readWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n opts?: { offset?: number; maxChars?: number },\n): Promise<WorkspaceFile | null> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n await ensureTable();\n const db = getDbExec();\n const result = await db.execute({\n sql: `SELECT id, scope, scope_id, path, content, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? AND path = ?`,\n args: [scope.scope, scope.scopeId, path],\n });\n\n const row = result.rows[0];\n if (!row) return null;\n\n let content = String(row[4] ?? \"\");\n if (opts?.offset || opts?.maxChars) {\n const off = opts.offset ?? 0;\n content = content.slice(\n off,\n opts.maxChars !== undefined ? off + opts.maxChars : undefined,\n );\n }\n\n return {\n id: String(row[0]),\n scope: String(row[1]),\n scopeId: String(row[2]),\n path: String(row[3]),\n content,\n contentType: String(row[5] ?? \"text/plain\"),\n sizeBytes: Number(row[6] ?? 0),\n createdAt: String(row[7]),\n updatedAt: String(row[8]),\n };\n}\n\n/**\n * Get file metadata without loading content.\n */\nexport async function getWorkspaceFileMeta(\n scope: WorkspaceFilesScope,\n path: string,\n): Promise<WorkspaceFileMeta | null> {\n await ensureTable();\n const db = getDbExec();\n const result = await db.execute({\n sql: `SELECT id, path, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? AND path = ?`,\n args: [scope.scope, scope.scopeId, path],\n });\n\n const row = result.rows[0];\n if (!row) return null;\n\n return {\n id: String(row[0]),\n path: String(row[1]),\n contentType: String(row[2] ?? \"text/plain\"),\n sizeBytes: Number(row[3] ?? 0),\n createdAt: String(row[4]),\n updatedAt: String(row[5]),\n };\n}\n\n/**\n * List workspace files, optionally filtered by path prefix.\n * Returns metadata only (no content).\n */\nexport async function listWorkspaceFiles(\n scope: WorkspaceFilesScope,\n prefix?: string,\n): Promise<WorkspaceFileMeta[]> {\n await ensureTable();\n const db = getDbExec();\n\n if (prefix) {\n // Allow a trailing slash on list prefixes, but reject traversal and\n // other invalid path shapes before they reach the LIKE pattern.\n const normalizedPrefix = prefix.endsWith(\"/\")\n ? prefix.slice(0, -1)\n : prefix;\n const pathErr = validatePath(normalizedPrefix);\n if (pathErr) {\n throw new Error(pathErr);\n }\n const result = await db.execute({\n sql: `SELECT id, path, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? AND (path = ? OR path LIKE ? ESCAPE '\\\\') ORDER BY path ASC`,\n args: [\n scope.scope,\n scope.scopeId,\n normalizedPrefix,\n `${normalizedPrefix.replace(/\\\\/g, \"\\\\\\\\\").replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\")}/%`,\n ],\n });\n return result.rows.map(rowToMeta);\n }\n\n const result = await db.execute({\n sql: `SELECT id, path, content_type, size_bytes, created_at, updated_at FROM workspace_files WHERE scope = ? AND scope_id = ? ORDER BY path ASC`,\n args: [scope.scope, scope.scopeId],\n });\n return result.rows.map(rowToMeta);\n}\n\n/**\n * Delete a workspace file. Returns true if deleted, false if not found.\n */\nexport async function deleteWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n): Promise<boolean> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n await ensureTable();\n const db = getDbExec();\n const result = await db.execute({\n sql: `DELETE FROM workspace_files WHERE scope = ? AND scope_id = ? AND path = ?`,\n args: [scope.scope, scope.scopeId, path],\n });\n return result.rowsAffected > 0;\n}\n\n/**\n * Search file contents for a substring or regex pattern.\n * Returns matching lines with path context.\n */\nexport async function grepWorkspaceFiles(\n scope: WorkspaceFilesScope,\n pattern: string,\n opts?: {\n pathPrefix?: string;\n useRegex?: boolean;\n maxMatchesPerFile?: number;\n maxFiles?: number;\n },\n): Promise<Array<{ path: string; lineNumber: number; line: string }>> {\n const files = await listWorkspaceFiles(scope, opts?.pathPrefix);\n const limited = files.slice(0, opts?.maxFiles ?? 50);\n\n let regex: RegExp;\n try {\n regex = opts?.useRegex\n ? new RegExp(pattern, \"i\")\n : new RegExp(pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"i\");\n } catch {\n throw new Error(`Invalid regex pattern: ${pattern}`);\n }\n\n const results: Array<{ path: string; lineNumber: number; line: string }> = [];\n const maxPerFile = opts?.maxMatchesPerFile ?? 20;\n\n for (const meta of limited) {\n const file = await readWorkspaceFile(scope, meta.path);\n if (!file) continue;\n const lines = file.content.split(\"\\n\");\n let matchCount = 0;\n for (let i = 0; i < lines.length; i++) {\n if (regex.test(lines[i])) {\n results.push({ path: meta.path, lineNumber: i + 1, line: lines[i] });\n matchCount++;\n if (matchCount >= maxPerFile) break;\n }\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function getScopeTotalBytes(scope: WorkspaceFilesScope): Promise<number> {\n const db = getDbExec();\n const result = await db.execute({\n sql: `SELECT COALESCE(SUM(size_bytes), 0) as total FROM workspace_files WHERE scope = ? AND scope_id = ?`,\n args: [scope.scope, scope.scopeId],\n });\n return Number(result.rows[0]?.[0] ?? 0);\n}\n\nfunction rowToMeta(row: any[]): WorkspaceFileMeta {\n return {\n id: String(row[0]),\n path: String(row[1]),\n contentType: String(row[2] ?? \"text/plain\"),\n sizeBytes: Number(row[3] ?? 0),\n createdAt: String(row[4]),\n updatedAt: String(row[5]),\n };\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/workspace-files/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compatibility wrapper for the old `workspace-files` API.\n *\n * Storage now goes through the core Resources table so agent files live in the\n * same workspace the user manages in the Resources panel. Paths under\n * `scratch/` are hidden agent scratch; every other path is a normal visible\n * resource in the current personal or organization scope.\n */\n\nimport {\n SHARED_OWNER,\n resourceDeleteByPath,\n resourceGetByPath,\n resourceList,\n resourcePut,\n type Resource,\n type ResourceMeta,\n type ResourceVisibility,\n} from \"../resources/store.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Max content size per file (bytes) for direct workspaceWrite calls. */\nexport const MAX_FILE_BYTES = 2 * 1024 * 1024; // 2 MB\n\n/**\n * Legacy export retained for API compatibility. The Resources store is the\n * canonical quota surface now, so the compatibility wrapper does not maintain a\n * separate per-scope total.\n */\nexport const MAX_SCOPE_BYTES = 200 * 1024 * 1024; // 200 MB\n\n/** Max content size when saving via saveToFile from provider-api / fetch tool. */\nexport const SAVE_TO_FILE_MAX_BYTES = 20 * 1024 * 1024; // 20 MB\n\n// ---------------------------------------------------------------------------\n// Scope helpers\n// ---------------------------------------------------------------------------\n\nexport interface WorkspaceFilesScope {\n scope: \"user\" | \"org\";\n scopeId: string;\n}\n\nfunction ownerForScope(scope: WorkspaceFilesScope): string {\n return scope.scope === \"org\" ? SHARED_OWNER : scope.scopeId;\n}\n\nfunction visibilityForPath(path: string): ResourceVisibility {\n return path === \"scratch\" || path.startsWith(\"scratch/\")\n ? \"agent_scratch\"\n : \"workspace\";\n}\n\nfunction workspaceFileMetadata(scope: WorkspaceFilesScope) {\n return {\n source: \"workspace-files\",\n scope: scope.scope,\n scopeId: scope.scopeId,\n };\n}\n\n/**\n * Validate a workspace file path.\n * - Non-empty, no leading slash, no \"..\" components, no null bytes.\n */\nexport function validatePath(path: string): string | null {\n if (!path || typeof path !== \"string\") return \"path is required\";\n if (path.startsWith(\"/\")) return 'path must not start with \"/\"';\n if (path.includes(\"\\0\")) return \"path must not contain null bytes\";\n const parts = path.split(\"/\");\n for (const part of parts) {\n if (part === \"..\") return 'path must not contain \"..\" components';\n if (part === \"\")\n return 'path must not contain empty segments (\"//\" or trailing \"/\")';\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface WorkspaceFile {\n id: string;\n scope: string;\n scopeId: string;\n path: string;\n content: string;\n contentType: string;\n sizeBytes: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface WorkspaceFileMeta {\n id: string;\n path: string;\n contentType: string;\n sizeBytes: number;\n createdAt: string;\n updatedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Store operations\n// ---------------------------------------------------------------------------\n\n/**\n * Write (create or overwrite) a workspace file.\n * Enforces per-file limits for the compatibility API; persistence is handled by\n * Resources. Use `scratch/...` for temporary hidden agent files.\n */\nexport async function writeWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n content: string,\n contentType = \"text/plain\",\n opts?: { maxFileBytes?: number },\n): Promise<WorkspaceFileMeta> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n const maxFileBytes = Math.min(\n opts?.maxFileBytes ?? MAX_FILE_BYTES,\n SAVE_TO_FILE_MAX_BYTES,\n );\n const bytes = Buffer.byteLength(content, \"utf8\");\n if (bytes > maxFileBytes) {\n throw new Error(\n `File \"${path}\" would be ${(bytes / 1024 / 1024).toFixed(2)} MB, which exceeds the ${(maxFileBytes / 1024 / 1024).toFixed(0)} MB per-file limit.`,\n );\n }\n\n const resource = await resourcePut(\n ownerForScope(scope),\n path,\n content,\n contentType,\n {\n createdBy: \"agent\",\n visibility: visibilityForPath(path),\n metadata: workspaceFileMetadata(scope),\n },\n );\n\n return resourceToMeta(resource);\n}\n\n/**\n * Append text to an existing workspace file, or create it if it doesn't exist.\n */\nexport async function appendWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n text: string,\n contentType = \"text/plain\",\n): Promise<WorkspaceFileMeta> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n const existing = await resourceGetByPath(ownerForScope(scope), path);\n const newContent = existing ? existing.content + text : text;\n return writeWorkspaceFile(scope, path, newContent, contentType);\n}\n\n/**\n * Read a workspace file's content (with optional offset and maxChars for paging).\n * Returns null if the file doesn't exist.\n */\nexport async function readWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n opts?: { offset?: number; maxChars?: number },\n): Promise<WorkspaceFile | null> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n const resource = await resourceGetByPath(ownerForScope(scope), path);\n if (!resource) return null;\n\n let content = resource.content;\n if (opts?.offset || opts?.maxChars) {\n const off = opts.offset ?? 0;\n content = content.slice(\n off,\n opts.maxChars !== undefined ? off + opts.maxChars : undefined,\n );\n }\n\n return resourceToFile(resource, scope, content);\n}\n\n/**\n * Get file metadata without loading content.\n */\nexport async function getWorkspaceFileMeta(\n scope: WorkspaceFilesScope,\n path: string,\n): Promise<WorkspaceFileMeta | null> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n const resource = await resourceGetByPath(ownerForScope(scope), path);\n return resource ? resourceToMeta(resource) : null;\n}\n\n/**\n * List workspace files, optionally filtered by path prefix.\n * Returns metadata only (no content).\n */\nexport async function listWorkspaceFiles(\n scope: WorkspaceFilesScope,\n prefix?: string,\n): Promise<WorkspaceFileMeta[]> {\n const owner = ownerForScope(scope);\n const normalizedPrefix = normalizePrefix(prefix);\n const resources = await resourceList(owner, normalizedPrefix, {\n includeAgentScratch: true,\n });\n const filtered = normalizedPrefix\n ? resources.filter(\n (resource) =>\n resource.path === normalizedPrefix ||\n resource.path.startsWith(`${normalizedPrefix}/`),\n )\n : resources;\n\n return filtered\n .map(resourceToMeta)\n .sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/**\n * Delete a workspace file. Returns true if deleted, false if not found.\n */\nexport async function deleteWorkspaceFile(\n scope: WorkspaceFilesScope,\n path: string,\n): Promise<boolean> {\n const pathErr = validatePath(path);\n if (pathErr) throw new Error(`Invalid path: ${pathErr}`);\n\n return resourceDeleteByPath(ownerForScope(scope), path);\n}\n\n/**\n * Search file contents for a substring or regex pattern.\n * Returns matching lines with path context.\n */\nexport async function grepWorkspaceFiles(\n scope: WorkspaceFilesScope,\n pattern: string,\n opts?: {\n pathPrefix?: string;\n useRegex?: boolean;\n maxMatchesPerFile?: number;\n maxFiles?: number;\n },\n): Promise<Array<{ path: string; lineNumber: number; line: string }>> {\n const files = await listWorkspaceFiles(scope, opts?.pathPrefix);\n const limited = files.slice(0, opts?.maxFiles ?? 50);\n\n let regex: RegExp;\n try {\n regex = opts?.useRegex\n ? new RegExp(pattern, \"i\")\n : new RegExp(pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"i\");\n } catch {\n throw new Error(`Invalid regex pattern: ${pattern}`);\n }\n\n const results: Array<{ path: string; lineNumber: number; line: string }> = [];\n const maxPerFile = opts?.maxMatchesPerFile ?? 20;\n\n for (const meta of limited) {\n const file = await readWorkspaceFile(scope, meta.path);\n if (!file) continue;\n const lines = file.content.split(\"\\n\");\n let matchCount = 0;\n for (let i = 0; i < lines.length; i++) {\n if (regex.test(lines[i])) {\n results.push({ path: meta.path, lineNumber: i + 1, line: lines[i] });\n matchCount++;\n if (matchCount >= maxPerFile) break;\n }\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction normalizePrefix(prefix?: string): string | undefined {\n if (!prefix) return undefined;\n const normalized = prefix.replace(/\\/+$/, \"\");\n if (!normalized) return undefined;\n const pathErr = validatePath(normalized);\n if (pathErr) throw new Error(pathErr);\n return normalized;\n}\n\nfunction isoTime(value: number): string {\n return new Date(value).toISOString();\n}\n\nfunction resourceToMeta(resource: ResourceMeta): WorkspaceFileMeta {\n return {\n id: resource.id,\n path: resource.path,\n contentType: resource.mimeType,\n sizeBytes: resource.size,\n createdAt: isoTime(resource.createdAt),\n updatedAt: isoTime(resource.updatedAt),\n };\n}\n\nfunction resourceToFile(\n resource: Resource,\n scope: WorkspaceFilesScope,\n content: string,\n): WorkspaceFile {\n return {\n id: resource.id,\n scope: scope.scope,\n scopeId: scope.scopeId,\n path: resource.path,\n content,\n contentType: resource.mimeType,\n sizeBytes: resource.size,\n createdAt: isoTime(resource.createdAt),\n updatedAt: isoTime(resource.updatedAt),\n };\n}\n"]}
@@ -1,10 +1,9 @@
1
1
  /**
2
- * `workspace-files` agent tool.
2
+ * `workspace-files` bridge tool.
3
3
  *
4
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.
5
+ * list, delete, and grep. It is kept for `run-code` workspaceRead/workspaceWrite
6
+ * compatibility and delegates storage to the Resources table.
8
7
  *
9
8
  * Scope is automatically resolved from the active request context:
10
9
  * - org scope when a request orgId is present (shared across users in the org)
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/workspace-files/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA2BhE,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAgOtE"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/workspace-files/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA2BhE,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAkOtE"}