@a3s-lab/code 2.5.0 → 3.0.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/README.md CHANGED
@@ -52,6 +52,60 @@ Omit `allowedTools` to allow every registered session tool except `program`.
52
52
  Scripts can also be loaded from workspace-relative `.js` or `.mjs` files with
53
53
  `{ path: 'scripts/ptc/search.js' }`.
54
54
 
55
+ ## Workspace Backends And Direct Files
56
+
57
+ The default workspace backend is the local filesystem rooted at the session
58
+ workspace. SDK callers can pass an explicit typed backend through the same
59
+ option surface used by remote, browser, DFS, and container-backed workspaces:
60
+
61
+ ```js
62
+ const { Agent, LocalWorkspaceBackend } = require('@a3s-lab/code')
63
+
64
+ const agent = await Agent.create('agent.acl')
65
+ const session = agent.session('/repo', {
66
+ workspaceBackend: new LocalWorkspaceBackend('/repo'),
67
+ })
68
+
69
+ await session.writeFile('notes.txt', 'one\ntwo\n')
70
+ await session.readFile('notes.txt')
71
+ await session.ls()
72
+ await session.editFile('notes.txt', 'one', 'uno')
73
+ await session.patchFile('notes.txt', '@@ -1,2 +1,2 @@\n uno\n-two\n+dos')
74
+ ```
75
+
76
+ ### S3-compatible object storage
77
+
78
+ `S3WorkspaceBackend` lets built-in file tools (`read`, `write`, `edit`,
79
+ `patch`, `ls`) target any S3-compatible endpoint — AWS S3, MinIO, RustFS,
80
+ Cloudflare R2, Backblaze B2, etc. `bash`, `git`, `grep`, and `glob` are
81
+ automatically hidden from the model because object storage cannot service
82
+ them.
83
+
84
+ ```js
85
+ const { Agent, S3WorkspaceBackend } = require('@a3s-lab/code')
86
+
87
+ const agent = await Agent.create('agent.acl')
88
+ const session = agent.session('s3://workspace/users/u1/sessions/s1', {
89
+ workspaceBackend: new S3WorkspaceBackend({
90
+ endpoint: 'https://minio.local:9000', // omit for AWS S3
91
+ region: 'us-east-1',
92
+ accessKeyId: 'AKIA...',
93
+ secretAccessKey: '...',
94
+ bucket: 'workspace',
95
+ prefix: 'users/u1/sessions/s1',
96
+ forcePathStyle: true, // true for MinIO/RustFS/R2
97
+ }),
98
+ })
99
+
100
+ await session.writeFile('notes/hello.txt', 'one\ntwo\n')
101
+ await session.readFile('notes/hello.txt')
102
+ await session.ls('notes')
103
+ ```
104
+
105
+ S3 has no atomic read-modify-write, so concurrent writers to the same key
106
+ overwrite each other (last-writer-wins). Partition workspaces per session or
107
+ user via the `prefix` field when running multi-tenant.
108
+
55
109
  ## Planning Events
56
110
 
57
111
  Planning is automatic by default. Prefer the explicit tri-state
package/index.d.ts CHANGED
@@ -71,6 +71,14 @@ export interface AgentEvent {
71
71
  verificationSummaryText?: string
72
72
  /** Extra data for events that don't map to standard fields (JSON-encoded) */
73
73
  data?: string
74
+ /**
75
+ * Structured discriminant for tool failures on `tool_end` events
76
+ * (JSON-encoded with a `type` field on the top level, e.g.
77
+ * `{"type":"version_conflict","path":"doc.md","expected":"etag-1","actual":"etag-2"}`).
78
+ * Undefined on success or untyped failure. Streaming consumers parse
79
+ * this to branch on the failure kind without scanning `toolOutput`.
80
+ */
81
+ errorKindJson?: string
74
82
  }
75
83
  export interface VerificationCommand {
76
84
  id: string
@@ -88,7 +96,32 @@ export interface ToolResult {
88
96
  metadataJson?: string
89
97
  /** Convenience JSON view of `metadata.document_runtime` when present. */
90
98
  documentRuntimeJson?: string
99
+ /**
100
+ * Structured discriminant for tool failures, JSON-encoded with a
101
+ * `type` field on the top level — e.g.
102
+ * `{"type":"version_conflict","path":"doc.md","expected":"etag-1","actual":"etag-2"}`.
103
+ * Undefined on success or untyped failure. SDK callers parse it to
104
+ * branch on the failure kind without scanning the `output` string.
105
+ */
106
+ errorKindJson?: string
91
107
  }
108
+
109
+ /**
110
+ * Parsed shape of `ToolResult.errorKindJson` / `AgentEvent.errorKindJson`.
111
+ *
112
+ * Use a discriminated union on the `type` field; new variants may be
113
+ * added in future minor releases — callers should match exhaustively on
114
+ * the kinds they care about and fall through to a default branch for
115
+ * unknown ones.
116
+ */
117
+ export type ToolErrorKind =
118
+ | { type: 'version_conflict'; path: string; expected: string; actual: string | null }
119
+ | { type: 'remote_git_conflict'; code: string; message: string }
120
+ | { type: 'not_found'; path: string }
121
+ | { type: 'invalid_argument'; message: string }
122
+ | { type: 'unsupported'; message: string }
123
+ | { type: 'timeout'; op: string; duration_ms: number }
124
+
92
125
  /** Execution limits for `Session.program`. */
93
126
  export interface ProgramScriptLimits {
94
127
  timeoutMs?: number
@@ -177,6 +210,115 @@ export interface JsSessionStore {
177
210
  export interface JsSecurityProvider {
178
211
  kind: string
179
212
  }
213
+ export interface JsWorkspaceBackend {
214
+ kind: string
215
+ root?: string
216
+ s3?: JsS3BackendConfig
217
+ }
218
+ /**
219
+ * Configuration for an S3-compatible workspace backend.
220
+ *
221
+ * Use this with [`S3WorkspaceBackend`] to point a session's built-in file
222
+ * tools at any S3-compatible endpoint (AWS S3, MinIO, RustFS, R2, etc.).
223
+ * `endpoint` is optional — omit it to use the AWS default. `prefix` is
224
+ * the logical workspace root inside the bucket; every workspace path
225
+ * becomes `<prefix>/<path>` when sent to S3.
226
+ */
227
+ export interface JsS3BackendConfig {
228
+ /**
229
+ * Optional S3 endpoint URL. Omit for AWS S3 (the SDK will compute it
230
+ * from `region`). Set to `https://...` for MinIO / RustFS / R2 / etc.
231
+ */
232
+ endpoint?: string
233
+ /** AWS region. Defaults to `us-east-1` when omitted. */
234
+ region?: string
235
+ /** Static access key. Use `sessionToken` together when STS-issued. */
236
+ accessKeyId: string
237
+ secretAccessKey: string
238
+ sessionToken?: string
239
+ /** Bucket name. */
240
+ bucket: string
241
+ /**
242
+ * Logical workspace prefix inside the bucket (without leading/trailing
243
+ * slashes). Use `""` to make the bucket root the workspace.
244
+ */
245
+ prefix: string
246
+ /** `true` for MinIO / RustFS / most non-AWS endpoints; `false` for AWS S3. */
247
+ forcePathStyle?: boolean
248
+ /**
249
+ * Maximum bytes a single `read` may return. The backend rejects any
250
+ * response with `Content-Length` greater than this without buffering
251
+ * the body. Defaults to 10 MiB on the Rust side when omitted.
252
+ */
253
+ maxReadBytes?: number
254
+ /**
255
+ * Enable degraded `grep` / `glob` against this S3 backend. Off by
256
+ * default — object storage has no native search, so the only viable
257
+ * strategy is `LIST` + `GET` + regex, which can be slow and expensive.
258
+ */
259
+ searchEnabled?: boolean
260
+ /**
261
+ * Upper bound on objects considered per `grep` / `glob` call. Defaults
262
+ * to 500 on the Rust side. Ignored when `searchEnabled` is `false`.
263
+ */
264
+ maxObjectsScanned?: number
265
+ /**
266
+ * Per-object body-size ceiling for `grep` downloads. Larger objects are
267
+ * skipped (debug-traced). Defaults to 1 MiB on the Rust side. Ignored
268
+ * when `searchEnabled` is `false`.
269
+ */
270
+ maxGrepBytesPerObject?: number
271
+ /**
272
+ * Concurrent object downloads during `grep`. Defaults to 8 on the Rust
273
+ * side. Set lower when the gitserver / S3 endpoint rate-limits
274
+ * aggressively; set higher when latency dominates. Ignored when
275
+ * `searchEnabled` is `false`.
276
+ */
277
+ searchConcurrency?: number
278
+ }
279
+ /**
280
+ * Configuration for a `RemoteGitBackend` — an HTTP/JSON client that
281
+ * brings the `git` tool to non-local workspaces (S3, future container /
282
+ * DFS).
283
+ *
284
+ * Pass alongside `workspaceBackend` on a session to attach remote git
285
+ * on top of any filesystem backend.
286
+ */
287
+ export interface JsRemoteGitBackendConfig {
288
+ /**
289
+ * Base URL of the gitserver, no trailing slash. The client builds
290
+ * `{baseUrl}/v1/repos/{repoId}/git/{op}` per the RFC.
291
+ */
292
+ baseUrl: string
293
+ /**
294
+ * Opaque repository identifier, URL-safe. Negotiated out of band
295
+ * with the gitserver operator.
296
+ */
297
+ repoId: string
298
+ /**
299
+ * Bearer token sent as `Authorization: Bearer <token>`. Required in
300
+ * production; omitting it emits a server-side warning and is only safe
301
+ * on a trusted localhost gitserver.
302
+ */
303
+ bearerToken?: string
304
+ /**
305
+ * mTLS client certificate path (PEM). When set together with `clientKeyPem`,
306
+ * the backend reads both files at construction and configures mTLS on the
307
+ * HTTP client. Setting only one of the pair errors at construction.
308
+ */
309
+ clientCertPem?: string
310
+ /**
311
+ * mTLS client private key path (PEM). PKCS#8 format expected for the
312
+ * `rustls-tls` backend. See `clientCertPem`.
313
+ */
314
+ clientKeyPem?: string
315
+ /** Per-call HTTP timeout in milliseconds. Defaults to 30 000. */
316
+ requestTimeoutMs?: number
317
+ /** Client-side cap on `diff` response bytes. Defaults to 1 MiB. */
318
+ maxDiffBytes?: number
319
+ /** Client-side cap on `log` `max_count`. Defaults to 200. */
320
+ maxLogEntries?: number
321
+ }
180
322
  /**
181
323
  * Union type for AHP transport configuration.
182
324
  * Accepts any of: StdioTransport, HttpTransport, WebSocketTransport, UnixSocketTransport.
@@ -339,6 +481,34 @@ export interface SessionOptions {
339
481
  * ```
340
482
  */
341
483
  securityProvider?: JsSecurityProvider
484
+ /**
485
+ * Workspace backend used by built-in tools.
486
+ *
487
+ * Pass `new LocalWorkspaceBackend("/repo")` to explicitly use the local
488
+ * filesystem backend. This option is the SDK surface for future remote,
489
+ * browser, DFS, and container-backed workspace implementations.
490
+ * ```js
491
+ * agent.session('/repo', { workspaceBackend: new LocalWorkspaceBackend('/repo') });
492
+ * ```
493
+ */
494
+ workspaceBackend?: JsWorkspaceBackend
495
+ /**
496
+ * Optional remote git provider. When set, the resulting session attaches
497
+ * a `RemoteGitBackend` on top of `workspaceBackend` so the built-in
498
+ * `git` tool is available even on object-storage workspaces.
499
+ *
500
+ * ```js
501
+ * agent.session('s3://workspace/u1/s1', {
502
+ * workspaceBackend: new S3WorkspaceBackend({ ... }),
503
+ * remoteGit: {
504
+ * baseUrl: 'https://gitserver.internal',
505
+ * repoId: 'u1/s1',
506
+ * bearerToken: token,
507
+ * },
508
+ * });
509
+ * ```
510
+ */
511
+ remoteGit?: JsRemoteGitBackendConfig
342
512
  /**
343
513
  * Custom role/identity prepended before the core agentic prompt.
344
514
  * Example: "You are a senior Python developer specializing in FastAPI."
@@ -698,6 +868,52 @@ export declare class DefaultSecurityProvider {
698
868
  kind: string
699
869
  constructor()
700
870
  }
871
+ /**
872
+ * Local filesystem workspace backend.
873
+ *
874
+ * This is the explicit typed form of the default local workspace behavior.
875
+ * It is useful when callers want to pass workspace backends through the same
876
+ * option surface that remote/browser backends will use.
877
+ *
878
+ * ```js
879
+ * agent.session('/repo', { workspaceBackend: new LocalWorkspaceBackend('/repo') });
880
+ * ```
881
+ */
882
+ export declare class LocalWorkspaceBackend {
883
+ kind: string
884
+ root: string
885
+ /** Create a local filesystem workspace backend rooted at `root`. */
886
+ constructor(root: string)
887
+ }
888
+ /**
889
+ * S3-compatible object-storage workspace backend.
890
+ *
891
+ * Points built-in file tools (`read`, `write`, `edit`, `patch`, `ls`) at an
892
+ * S3-compatible bucket. Works with AWS S3, MinIO, RustFS, Cloudflare R2,
893
+ * Backblaze B2, and other S3-API-compatible services.
894
+ *
895
+ * `bash`, `git`, `grep`, and `glob` are intentionally **not** registered
896
+ * when this backend is in use — object storage cannot service them.
897
+ *
898
+ * ```js
899
+ * const backend = new S3WorkspaceBackend({
900
+ * endpoint: 'https://minio.local:9000',
901
+ * region: 'us-east-1',
902
+ * accessKeyId: 'AKIA...',
903
+ * secretAccessKey: '...',
904
+ * bucket: 'workspace',
905
+ * prefix: 'users/u1/sessions/s1',
906
+ * forcePathStyle: true,
907
+ * });
908
+ * agent.session('s3://workspace/users/u1/sessions/s1', { workspaceBackend: backend });
909
+ * ```
910
+ */
911
+ export declare class S3WorkspaceBackend {
912
+ kind: string
913
+ s3: JsS3BackendConfig
914
+ /** Create an S3-compatible workspace backend. */
915
+ constructor(config: JsS3BackendConfig)
916
+ }
701
917
  /**
702
918
  * Stdio transport for AHP (Agent Harness Protocol).
703
919
  *
@@ -919,6 +1135,14 @@ export declare class Session {
919
1135
  program(options: ProgramScriptOptions): Promise<ToolResult>
920
1136
  /** Read a file from the workspace. */
921
1137
  readFile(path: string): Promise<string>
1138
+ /** Write a file in the workspace. */
1139
+ writeFile(path: string, content: string): Promise<ToolResult>
1140
+ /** List a directory in the workspace. */
1141
+ ls(path?: string | undefined | null): Promise<ToolResult>
1142
+ /** Edit a file by replacing text in the workspace. */
1143
+ editFile(path: string, oldString: string, newString: string, replaceAll?: boolean | undefined | null): Promise<ToolResult>
1144
+ /** Apply a unified diff patch to a workspace file. */
1145
+ patchFile(path: string, diff: string): Promise<ToolResult>
922
1146
  /** Execute a bash command in the workspace. */
923
1147
  bash(command: string): Promise<string>
924
1148
  /** Search for files matching a glob pattern. */
package/index.js CHANGED
@@ -310,7 +310,7 @@ if (!nativeBinding) {
310
310
  throw new Error(`Failed to load native binding`)
311
311
  }
312
312
 
313
- const { formatVerificationSummary, EventStream, FileMemoryStore, FileSessionStore, MemorySessionStore, DefaultSecurityProvider, StdioTransport, HttpTransport, WebSocketTransport, UnixSocketTransport, Agent, Session, builtinSkills, BrowserBackend } = nativeBinding
313
+ const { formatVerificationSummary, EventStream, FileMemoryStore, FileSessionStore, MemorySessionStore, DefaultSecurityProvider, LocalWorkspaceBackend, S3WorkspaceBackend, StdioTransport, HttpTransport, WebSocketTransport, UnixSocketTransport, Agent, Session, builtinSkills, BrowserBackend } = nativeBinding
314
314
 
315
315
  module.exports.formatVerificationSummary = formatVerificationSummary
316
316
  module.exports.EventStream = EventStream
@@ -318,6 +318,8 @@ module.exports.FileMemoryStore = FileMemoryStore
318
318
  module.exports.FileSessionStore = FileSessionStore
319
319
  module.exports.MemorySessionStore = MemorySessionStore
320
320
  module.exports.DefaultSecurityProvider = DefaultSecurityProvider
321
+ module.exports.LocalWorkspaceBackend = LocalWorkspaceBackend
322
+ module.exports.S3WorkspaceBackend = S3WorkspaceBackend
321
323
  module.exports.StdioTransport = StdioTransport
322
324
  module.exports.HttpTransport = HttpTransport
323
325
  module.exports.WebSocketTransport = WebSocketTransport
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3s-lab/code",
3
- "version": "2.5.0",
3
+ "version": "3.0.0",
4
4
  "description": "A3S Code - Native Node.js bindings for the coding-agent runtime",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -40,11 +40,11 @@
40
40
  "test:helpers": "node test-helpers.mjs"
41
41
  },
42
42
  "optionalDependencies": {
43
- "@a3s-lab/code-darwin-arm64": "2.5.0",
44
- "@a3s-lab/code-linux-x64-gnu": "2.5.0",
45
- "@a3s-lab/code-linux-x64-musl": "2.5.0",
46
- "@a3s-lab/code-linux-arm64-gnu": "2.5.0",
47
- "@a3s-lab/code-linux-arm64-musl": "2.5.0",
48
- "@a3s-lab/code-win32-x64-msvc": "2.5.0"
43
+ "@a3s-lab/code-darwin-arm64": "3.0.0",
44
+ "@a3s-lab/code-linux-x64-gnu": "3.0.0",
45
+ "@a3s-lab/code-linux-x64-musl": "3.0.0",
46
+ "@a3s-lab/code-linux-arm64-gnu": "3.0.0",
47
+ "@a3s-lab/code-linux-arm64-musl": "3.0.0",
48
+ "@a3s-lab/code-win32-x64-msvc": "3.0.0"
49
49
  }
50
50
  }