@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.
- package/dist/agent/harness/ai-sdk-adapter.d.ts +13 -0
- package/dist/agent/harness/ai-sdk-adapter.d.ts.map +1 -0
- package/dist/agent/harness/ai-sdk-adapter.js +232 -0
- package/dist/agent/harness/ai-sdk-adapter.js.map +1 -0
- package/dist/agent/harness/background.d.ts +15 -0
- package/dist/agent/harness/background.d.ts.map +1 -0
- package/dist/agent/harness/background.js +233 -0
- package/dist/agent/harness/background.js.map +1 -0
- package/dist/agent/harness/builtin.d.ts +2 -0
- package/dist/agent/harness/builtin.d.ts.map +1 -0
- package/dist/agent/harness/builtin.js +24 -0
- package/dist/agent/harness/builtin.js.map +1 -0
- package/dist/agent/harness/index.d.ts +9 -0
- package/dist/agent/harness/index.d.ts.map +1 -0
- package/dist/agent/harness/index.js +8 -0
- package/dist/agent/harness/index.js.map +1 -0
- package/dist/agent/harness/registry.d.ts +15 -0
- package/dist/agent/harness/registry.d.ts.map +1 -0
- package/dist/agent/harness/registry.js +70 -0
- package/dist/agent/harness/registry.js.map +1 -0
- package/dist/agent/harness/runner.d.ts +21 -0
- package/dist/agent/harness/runner.d.ts.map +1 -0
- package/dist/agent/harness/runner.js +86 -0
- package/dist/agent/harness/runner.js.map +1 -0
- package/dist/agent/harness/store.d.ts +46 -0
- package/dist/agent/harness/store.d.ts.map +1 -0
- package/dist/agent/harness/store.js +304 -0
- package/dist/agent/harness/store.js.map +1 -0
- package/dist/agent/harness/translate.d.ts +5 -0
- package/dist/agent/harness/translate.d.ts.map +1 -0
- package/dist/agent/harness/translate.js +120 -0
- package/dist/agent/harness/translate.js.map +1 -0
- package/dist/agent/harness/types.d.ts +127 -0
- package/dist/agent/harness/types.d.ts.map +1 -0
- package/dist/agent/harness/types.js +2 -0
- package/dist/agent/harness/types.js.map +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +2 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +10 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/skills.d.ts +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +8 -1
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe.js +0 -1
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +4 -2
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/code-agents/background-run.d.ts +6 -4
- package/dist/code-agents/background-run.d.ts.map +1 -1
- package/dist/code-agents/background-run.js.map +1 -1
- package/dist/coding-tools/run-code.js +6 -4
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/provider-api/index.d.ts +20 -12
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +157 -37
- package/dist/provider-api/index.js.map +1 -1
- package/dist/provider-api/quota-governor.d.ts +53 -0
- package/dist/provider-api/quota-governor.d.ts.map +1 -0
- package/dist/provider-api/quota-governor.js +395 -0
- package/dist/provider-api/quota-governor.js.map +1 -0
- package/dist/provider-api/staging.d.ts.map +1 -1
- package/dist/provider-api/staging.js +16 -0
- package/dist/provider-api/staging.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +34 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +98 -0
- package/dist/workspace-files/index.d.ts +0 -1
- package/dist/workspace-files/index.d.ts.map +1 -1
- package/dist/workspace-files/index.js +0 -1
- package/dist/workspace-files/index.js.map +1 -1
- package/dist/workspace-files/store.d.ts +14 -9
- package/dist/workspace-files/store.d.ts.map +1 -1
- package/dist/workspace-files/store.js +89 -164
- package/dist/workspace-files/store.js.map +1 -1
- package/dist/workspace-files/tool.d.ts +3 -4
- package/dist/workspace-files/tool.d.ts.map +1 -1
- package/dist/workspace-files/tool.js +8 -7
- package/dist/workspace-files/tool.js.map +1 -1
- package/docs/content/deployment.md +1 -1
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +98 -0
- package/dist/workspace-files/schema.d.ts +0 -195
- package/dist/workspace-files/schema.d.ts.map +0 -1
- package/dist/workspace-files/schema.js +0 -48
- 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
|
|
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
|
|
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
|
-
*
|
|
2
|
+
* Compatibility wrapper for the old `workspace-files` API.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
45
|
-
*
|
|
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
|
|
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
|
-
*
|
|
2
|
+
* Compatibility wrapper for the old `workspace-files` API.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
58
|
-
*
|
|
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
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
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
|
|
146
|
-
|
|
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 =
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
|
200
|
+
function resourceToMeta(resource) {
|
|
289
201
|
return {
|
|
290
|
-
id:
|
|
291
|
-
path:
|
|
292
|
-
contentType:
|
|
293
|
-
sizeBytes:
|
|
294
|
-
createdAt:
|
|
295
|
-
updatedAt:
|
|
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`
|
|
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.
|
|
6
|
-
*
|
|
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
|
|
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"}
|