@zoowork-ai/sdk 0.10.2 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to `@zoowork-ai/sdk` (formerly `@zooclaw-agents/sdk`). Dates are the
4
4
  day the behaviour was verified, not the day it was written.
5
5
 
6
+ ## 0.11.1 — 2026-10-04
7
+
8
+ - `uploadFile` no longer runs `chown` on the uploaded file or on directories it creates. The
9
+ sandbox workspace mount does not enforce ownership, and a staging check showed the Agent's
10
+ edit and write tools modify uploaded files without it. Existing calls remain compatible.
11
+
12
+ ## 0.11.0 — 2026-10-04
13
+
14
+ - Add `uploadFile(agentId, path, content)`, which copies a file into an Agent's sandbox through
15
+ `exec` and verifies its SHA-256 there. Verified against staging on 2026-10-04 with files up
16
+ to 3 MiB.
17
+ - Deprecate `getWorkspaceFile`, `writeWorkspaceFile` and `getWorkspaceFileContent`. The hosted
18
+ service does not enable the workspace Files routes, so these calls fail with HTTP 501 or 502
19
+ ([ecap #4006](https://github.com/SerendipityOneInc/ecap-workspace/issues/4006)). The methods
20
+ remain callable.
21
+
6
22
  ## 0.10.2 — 2026-10-04
7
23
 
8
24
  - Accept `project` in `uploadSkill` for named Project keys.
package/README.md CHANGED
@@ -165,6 +165,20 @@ await zc.wake(agent.agent_id, { text: 'Review the pending deployment.' }) // at
165
165
  const { exit_code, stdout } = await zc.exec(agent.agent_id, ['bash', '-lc', 'pwd'])
166
166
  ```
167
167
 
168
+ ## Send a file to an Agent
169
+
170
+ ```ts
171
+ import { readFile } from 'node:fs/promises'
172
+
173
+ const file = await zc.uploadFile(agent.agent_id, 'input/report.pdf', await readFile('report.pdf'))
174
+ // file.path === '/workspace/input/report.pdf'; tell the Agent to read that path.
175
+ ```
176
+
177
+ `uploadFile` copies bytes or a string into the Agent's sandbox and verifies the SHA-256 there
178
+ before the file appears at its path. A relative path resolves against `/workspace`. It sends the
179
+ content through `exec` about 72 KB per call, so it suits files up to a few megabytes, and it
180
+ needs an agent-scope sandbox.
181
+
168
182
  - **Schedules outlive their agent.** `stopAgent` and `deleteAgent` leave them running; list and
169
183
  delete them yourself. Also available: `getSchedule`, `updateSchedule`, `triggerSchedule`,
170
184
  `listScheduleRuns`.
@@ -418,9 +432,11 @@ const output = await zc.getRunOutput(agentId, sessionId, runId)
418
432
  const approvals = await zc.listApprovalPage(agentId, { sessionId })
419
433
  ```
420
434
 
421
- Direct workspace Files and the database viewer are not supported production workflows.
422
- Supply text in Session messages, ask the Agent to create and publish Artifacts, and download
423
- those through the Artifact API. The Agent can use `agent_db` and return query results in its
435
+ The workspace Files methods (`getWorkspaceFile`, `writeWorkspaceFile`,
436
+ `getWorkspaceFileContent`) are deprecated: the hosted service does not enable those routes.
437
+ Send a file to an Agent with `uploadFile`, and retrieve results by asking the Agent to publish
438
+ Artifacts and downloading those through the Artifact API. The database viewer is not a
439
+ supported production workflow. The Agent can use `agent_db` and return query results in its
424
440
  reply. Method presence is not production availability. Usage stays within the current key scope. Page helpers retain `next_cursor` and `has_more`; replay cursors
425
441
  verbatim. `getApproval` and `getCustomToolCall` read terminal as well as pending actions.
426
442
 
package/dist/client.d.ts CHANGED
@@ -1321,6 +1321,15 @@ export interface ExecResult {
1321
1321
  stdout: string;
1322
1322
  stderr: string;
1323
1323
  }
1324
+ /** What {@link ZooworkClient.uploadFile} wrote. */
1325
+ export interface UploadFileResult {
1326
+ /** Absolute path of the file in the sandbox. */
1327
+ path: string;
1328
+ /** Bytes written. */
1329
+ size: number;
1330
+ /** Lowercase hex SHA-256 of the content, verified inside the sandbox. */
1331
+ sha256: string;
1332
+ }
1324
1333
  export interface WakeResult {
1325
1334
  mode: 'now' | 'next-heartbeat' | string;
1326
1335
  /** The reminder was written to the pending queue. */
@@ -1855,6 +1864,21 @@ export interface ZooworkClient extends DeveloperApi {
1855
1864
  * agent's tool path.
1856
1865
  */
1857
1866
  exec(agentId: string, args: string[]): Promise<ExecResult>;
1867
+ /**
1868
+ * Copy a file into the agent's sandbox so the Agent can read it by path.
1869
+ *
1870
+ * `path` is absolute, or relative to `/workspace`; parent directories are created. A string is
1871
+ * written as UTF-8. The content travels through {@link ZooworkClient.exec} in base64 chunks,
1872
+ * lands in a temporary file beside the target, is checked against its SHA-256 inside the
1873
+ * sandbox, and only then moves into place, so a failed upload leaves nothing at `path`.
1874
+ *
1875
+ * Each chunk is one `exec` call carrying about 72 KB, so this suits files up to a few
1876
+ * megabytes. It has exec's requirements: an agent-scope sandbox and a rendered config.
1877
+ *
1878
+ * Rejects with `ZooworkError` when a call fails, and with `Error` when a sandbox command exits
1879
+ * non-zero or the checksum does not match.
1880
+ */
1881
+ uploadFile(agentId: string, path: string, content: Blob | ArrayBuffer | Uint8Array | string): Promise<UploadFileResult>;
1858
1882
  /**
1859
1883
  * Environments visible to your org, 1-based `page`. Note that the platform DEFAULT
1860
1884
  * Environment — the one a fresh agent is pinned to — is not in here and is not fetchable: the
package/dist/client.js CHANGED
@@ -294,6 +294,49 @@ export function createZooworkClient(cfg = {}) {
294
294
  throw httpError(res, await res.text());
295
295
  return new Uint8Array(await res.arrayBuffer());
296
296
  };
297
+ const exec = (agentId, args) => json(`${agents(agentId)}/exec`, { method: 'POST', body: JSON.stringify({ args }) });
298
+ /**
299
+ * Linux caps one argv string at 128 KiB. 72,000 bytes is 96,000 base64 characters, which
300
+ * leaves room under that cap and is a multiple of 3, so every chunk decodes on its own.
301
+ */
302
+ const UPLOAD_CHUNK_BYTES = 72_000;
303
+ const uploadFile = async (agentId, path, content) => {
304
+ if (path === '')
305
+ throw new Error('uploadFile: path is required');
306
+ const data = typeof content === 'string' ? new TextEncoder().encode(content)
307
+ : content instanceof Blob ? new Uint8Array(await content.arrayBuffer())
308
+ : content instanceof ArrayBuffer ? new Uint8Array(content)
309
+ : content;
310
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', data));
311
+ const sha256 = Array.from(digest, (byte) => byte.toString(16).padStart(2, '0')).join('');
312
+ // `bash -c SCRIPT NAME ARG…`: values arrive as "$1", "$2", … and are never parsed as shell.
313
+ const sh = async (script, ...args) => {
314
+ const res = await exec(agentId, ['bash', '-c', script, 'upload', ...args]);
315
+ if (res.exit_code !== 0) {
316
+ throw new Error(`uploadFile: sandbox command exited ${res.exit_code}: ${res.stderr.trim().slice(0, 500)}`);
317
+ }
318
+ return res;
319
+ };
320
+ const partial = `${path}.upload-${crypto.randomUUID()}`;
321
+ try {
322
+ await sh('mkdir -p "$(dirname "$1")" && : > "$1"', partial);
323
+ for (let offset = 0; offset < data.length; offset += UPLOAD_CHUNK_BYTES) {
324
+ let binary = '';
325
+ for (const byte of data.subarray(offset, offset + UPLOAD_CHUNK_BYTES))
326
+ binary += String.fromCharCode(byte);
327
+ await sh('printf %s "$2" | base64 -d >> "$1"', partial, btoa(binary));
328
+ }
329
+ const moved = await sh('actual=$(sha256sum "$1" | cut -d" " -f1); '
330
+ + 'if [ "$actual" != "$3" ]; then echo "checksum mismatch: got $actual" >&2; exit 3; fi; '
331
+ + 'mv -f "$1" "$2" && realpath "$2"', partial, path, sha256);
332
+ return { path: moved.stdout.trim(), size: data.length, sha256 };
333
+ }
334
+ catch (error) {
335
+ // Best effort: the original failure is what the caller needs to see.
336
+ await exec(agentId, ['rm', '-f', partial]).catch(() => undefined);
337
+ throw error;
338
+ }
339
+ };
297
340
  const client = {
298
341
  ...createDeveloperApi(json, bytes, artifactSelectors),
299
342
  listModels: async () => {
@@ -758,7 +801,8 @@ export function createZooworkClient(cfg = {}) {
758
801
  return data.runs ?? [];
759
802
  },
760
803
  wake: (agentId, input) => json(`${agents(agentId)}/wake`, { method: 'POST', body: JSON.stringify(input) }),
761
- exec: (agentId, args) => json(`${agents(agentId)}/exec`, { method: 'POST', body: JSON.stringify({ args }) }),
804
+ exec,
805
+ uploadFile,
762
806
  listEnvironments: async (opts = {}) => {
763
807
  const data = await json(`/environments${query({ page: opts.page })}`);
764
808
  return data.environments ?? [];
@@ -157,10 +157,22 @@ export interface WebhookBatchRedeliveryInput {
157
157
  limit?: number;
158
158
  }
159
159
  export interface DeveloperApi {
160
+ /**
161
+ * @deprecated The hosted service does not enable the workspace Files routes; this call
162
+ * fails with HTTP 501 or 502. Ask the Agent to publish an Artifact and download that.
163
+ */
160
164
  getWorkspaceFile(agentId: string, path: string, opts?: {
161
165
  showHidden?: boolean;
162
166
  }): Promise<WorkspaceFile>;
167
+ /**
168
+ * @deprecated The hosted service does not enable the workspace Files routes; this call
169
+ * fails with HTTP 501 or 502. Use `uploadFile` to send a file to an Agent.
170
+ */
163
171
  writeWorkspaceFile(agentId: string, path: string, content: string): Promise<ApiObject>;
172
+ /**
173
+ * @deprecated The hosted service does not enable the workspace Files routes; this call
174
+ * fails with HTTP 501 or 502. Ask the Agent to publish an Artifact and download that.
175
+ */
164
176
  getWorkspaceFileContent(agentId: string, path: string, opts?: {
165
177
  download?: boolean;
166
178
  }): Promise<Uint8Array>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { createZooworkClient, DEFAULT_BASE_URL, ZooworkError, type ZooworkClient, type ZooworkConfig, type ZooworkAuth, type Ownership, type ModelInfo, type AgentResource, type AgentRecord, type AgentListParams, type AgentPage, type AgentPagePromise, type AgentStatus, type AgentSkill, type AgentChannel, type AgentChannelCapabilitySync, type AgentChannelCapabilities, type FeishuChannelProviderStatus, type FeishuDocumentsCapability, type ChannelPlatform, type AddChannelPlatform, type GuidedSetupPlatform, type AddChannelInput, type UpdateChannelInput, type ChannelSetupInput, type ChannelSetupSession, type ChannelPollResult, type FeishuSetupInput, type FeishuSetupSession, type FeishuPollResult, type McpContextConfig, type McpServerDeclaration, type McpToolPermission, type McpToolPermissionOverride, type CustomToolDeclaration, type CustomToolResultImageMimeType, type CustomToolResultContent, type CustomToolResultEvent, type SkillRecord, type SkillVersionRecord, type SessionRecord, type SessionListPageOptions, type SessionListPage, type SessionHistoryEntry, type SessionEvent, type SessionEventPage, type OutboundEvent, type PostEventReceipt, type ApprovalDecision, type ApprovalRecord, type CustomToolCallStatus, type CustomToolCallRecord, type ArtifactPage, type ArtifactRecord, type ArtifactStatus, type OutcomeConfig, type OutcomeEvaluator, type SystemPromptDeclaration, type SystemPromptInfo, type SystemPromptPreview, type SystemPromptPreviewInput, type SystemPromptUpgrade, type ScheduleSpec, type SchedulePayload, type ScheduleInput, type ScheduleUpdate, type ScheduleRecord, type ScheduleRun, type WakeResult, type ExecResult, type EnvironmentConfig, type EnvironmentResource, type EnvironmentRecord, type EnvironmentVersionRecord, } from './client.js';
1
+ export { createZooworkClient, DEFAULT_BASE_URL, ZooworkError, type ZooworkClient, type ZooworkConfig, type ZooworkAuth, type Ownership, type ModelInfo, type AgentResource, type AgentRecord, type AgentListParams, type AgentPage, type AgentPagePromise, type AgentStatus, type AgentSkill, type AgentChannel, type AgentChannelCapabilitySync, type AgentChannelCapabilities, type FeishuChannelProviderStatus, type FeishuDocumentsCapability, type ChannelPlatform, type AddChannelPlatform, type GuidedSetupPlatform, type AddChannelInput, type UpdateChannelInput, type ChannelSetupInput, type ChannelSetupSession, type ChannelPollResult, type FeishuSetupInput, type FeishuSetupSession, type FeishuPollResult, type McpContextConfig, type McpServerDeclaration, type McpToolPermission, type McpToolPermissionOverride, type CustomToolDeclaration, type CustomToolResultImageMimeType, type CustomToolResultContent, type CustomToolResultEvent, type SkillRecord, type SkillVersionRecord, type SessionRecord, type SessionListPageOptions, type SessionListPage, type SessionHistoryEntry, type SessionEvent, type SessionEventPage, type OutboundEvent, type PostEventReceipt, type ApprovalDecision, type ApprovalRecord, type CustomToolCallStatus, type CustomToolCallRecord, type ArtifactPage, type ArtifactRecord, type ArtifactStatus, type OutcomeConfig, type OutcomeEvaluator, type SystemPromptDeclaration, type SystemPromptInfo, type SystemPromptPreview, type SystemPromptPreviewInput, type SystemPromptUpgrade, type ScheduleSpec, type SchedulePayload, type ScheduleInput, type ScheduleUpdate, type ScheduleRecord, type ScheduleRun, type WakeResult, type ExecResult, type UploadFileResult, type EnvironmentConfig, type EnvironmentResource, type EnvironmentRecord, type EnvironmentVersionRecord, } from './client.js';
2
2
  export { SESSION_EVENT_TYPES, type SessionEventType, PUBLIC_INPUT_EVENT_TYPES, type PublicInputEventType, normalizeEvent, isRunFinished, runOutcome, messageText, assistantText, thinkingText, customToolUse, type CustomToolUse, toolCall, type ToolCall, } from './events.js';
3
3
  export { parseSSE, type SSEMessage } from './sse.js';
4
4
  export { WEBHOOK_EVENT_TYPES, WEBHOOK_SCHEDULE_CONFIG_EVENT_TYPES, type WebhookEventType, isKnownWebhookEventType, knownWebhookEvent, verifyWebhookSignature, unwrapWebhook, signWebhook, ZooworkWebhookError, type WebhookErrorCode, WEBHOOK_ID_HEADER, WEBHOOK_TIMESTAMP_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_SECRET_ENV, WEBHOOK_TOLERANCE_SECONDS, WEBHOOK_DEFAULT_MAX_BODY_BYTES, type WebhookEvent, type WebhookEventFor, type KnownWebhookEvent, type WebhookEventData, type WebhookEventDataByType, type WebhookEventAttribution, type WebhookScheduleRunRef, type WebhookWaitingOnRef, type WebhookRunEventData, type WebhookRunStartedData, type WebhookRunFinishedData, type WebhookRunYieldedData, type WebhookApprovalEventData, type WebhookApprovalRequestedData, type WebhookApprovalResolvedData, type WebhookCustomToolEventData, type WebhookCustomToolRequestedData, type WebhookCustomToolResolvedData, type WebhookOutcomeEvaluatedData, type WebhookSessionCreatedData, type WebhookSessionArchivedData, type WebhookSessionDeletedData, type WebhookScheduleFireData, type WebhookScheduleDispatchedData, type WebhookScheduleDispatchFailedData, type WebhookScheduleSkippedData, type WebhookScheduleFinishedData, type WebhookScheduleConfigData, type WebhookTestData, type WebhookHeaders, type WebhookHeaderSource, type WebhookSignatureHeaders, type VerifyWebhookInput, type VerifiedWebhook, type SignWebhookInput, } from './webhooks.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zoowork-ai/sdk",
3
- "version": "0.10.2",
3
+ "version": "0.11.1",
4
4
  "description": "TypeScript SDK for the ZooWork Managed Agents API (Developer Preview)",
5
5
  "keywords": [
6
6
  "zoowork",