Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.live.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ QODER_FORWARD_GLOBAL_PAT=
# Optional PCM/work scenarios (not run by basic connection checks).
QODER_REALTIME_PCM_FILE=
QODER_REALTIME_WORK_PROMPT=

# Forward Drive Beta lifecycle (creates isolated Identities; no model execution).
# Uses QODER_FORWARD_CN_PAT, QODER_FORWARD_GLOBAL_PAT, QODER_SA_CN_KEY,
# and QODER_SA_GLOBAL_KEY. Never point clear at a shared Identity.
QODER_DRIVE_LIVE_ALLOW_WRITE=false
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ existing `0.1.0` release; earlier development prereleases are not listed.

## [Unreleased]

### Added

- Forward Drive Beta: typed directory listing, upload/download signed URLs, entry deletion and Drive clearing, with required Identity scope, independent cursor pagination, service ordering and canonical directory query encoding.
- Template `managed_tool_config.enabled_tools` in create/update requests and responses, including omission, empty-list and null semantics. Runnable Drive examples keep storage requests separate from QCA authentication; opt-in CN/Global lifecycle tests cover PAT, admin SAT and Identity SAT.

## [0.5.0]

### Added
Expand Down
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,56 @@ for await (const event of stream) {

The resumable stream does not query event history, clear an invalid cursor, or deduplicate IDs: delta frames may legitimately share an event ID. It stops after `[DONE]`, `session.status_terminated`, or `session.deleted`. Stop it early with `stream.controller.abort()`, an `AbortSignal` in `RequestOptions`, or `await stream.close()`.

## Forward Drive (Beta)

Drive is a Beta API. It uses `entries` / `next_page_token`, not the ordinary
resource paginator. Pass `identity_id` even with an Identity SAT, preserve the
server order, and reuse a cursor only within the same Identity, directory and
authentication scope. Paths are relative; the SDK preserves slashes when encoding
the list query and leaves path validation to the service.

Upload/download methods return a signed URL, method, headers and expiry. Send the
storage request with a separate HTTP client using only those returned headers;
do not attach QCA credentials or log signed URLs. Replaying an `Idempotency-Key`
does not renew the URL: use a new key or omit it when requesting a fresh URL.

Deleting a nonempty directory requires `recursive=true`; `clear` deletes the
entire selected Identity Drive. Partial failures retain `drive_delete_partial`
and `deleted_count` in the standard API error body. Direct Drive APIs do not
require Template tool configuration.

```ts
const identityID = 'idn_...';
const templateID = 'tmpl_...';
let page = await client.drives.listEntries({ identity_id: identityID, path: 'reports', limit: 20 });
for (const entry of page.entries) console.log(entry.path);
if (page.next_page_token) {
page = await client.drives.listEntries({
identity_id: identityID, path: 'reports', page_token: page.next_page_token,
});
}
// Replace the complete managed-tool list, retaining any other tools you need.
await client.templates.update(templateID, { managed_tool_config: { enabled_tools: ['drive'] } });
```

Run the [file lifecycle example](examples/forward/drive.mjs) with
`node examples/forward/drive.mjs`. It uses the shared PAT/SA configuration and
creates a fresh Identity. A directory has no `size`; a zero-byte file has `size=0`.
For maintainers, `npm run test:live:drive` tests CN/Global × PAT/admin SAT/Identity SAT.

To enable Agent-managed Drive tools, use Template `managed_tool_config.enabled_tools`.
It is separate from `tools[].enabled_tools` and replaces the entire list; an empty
list disables managed tools, omission preserves the setting, and `null` clears it.
Full Agent Drive usage also requires the DeliverArtifacts tool. Internal
`/forward-mcp` routes are invoked by the runtime and are not public SDK methods.

The Drive live suite requires `QODER_DRIVE_LIVE_ALLOW_WRITE=true`,
`QODER_FORWARD_CN_PAT`, `QODER_FORWARD_GLOBAL_PAT`, `QODER_SA_CN_KEY`, and
`QODER_SA_GLOBAL_KEY` in `.env.live` (or `LIVE_ENV_FILE`). It creates isolated
Identities, verifies storage transfers, pagination, deletion, Identity isolation,
and Template configuration, then clears only those new Identities. It does not
run Agent/model tool execution.

## File uploads

File parameters accept a `File`, a `Blob`, a byte array, a `Response`, a `ReadableStream` or an async iterable — including a Node file stream. `toFile` wraps any of those with an explicit filename.
Expand Down
13 changes: 13 additions & 0 deletions docs/api/forward/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [ChannelPairings](classes/ChannelPairings.md)
- [ChannelQRSessions](classes/ChannelQRSessions.md)
- [Channels](classes/Channels.md)
- [Drives](classes/Drives.md)
- [Environments](classes/Environments.md)
- [Files](classes/Files.md)
- [Identities](classes/Identities.md)
Expand Down Expand Up @@ -72,6 +73,17 @@
- [DeletedMemoryStore](interfaces/DeletedMemoryStore.md)
- [DeletedMemoryStoreMount](interfaces/DeletedMemoryStoreMount.md)
- [DeletedSkillVersion](interfaces/DeletedSkillVersion.md)
- [DriveClearParams](interfaces/DriveClearParams.md)
- [DriveClearResponse](interfaces/DriveClearResponse.md)
- [DriveCreateDownloadURLParams](interfaces/DriveCreateDownloadURLParams.md)
- [DriveCreateUploadURLParams](interfaces/DriveCreateUploadURLParams.md)
- [DriveDeleteEntryParams](interfaces/DriveDeleteEntryParams.md)
- [DriveDeleteEntryResponse](interfaces/DriveDeleteEntryResponse.md)
- [DriveDownloadURL](interfaces/DriveDownloadURL.md)
- [DriveEntry](interfaces/DriveEntry.md)
- [DriveListEntriesParams](interfaces/DriveListEntriesParams.md)
- [DriveListEntriesResponse](interfaces/DriveListEntriesResponse.md)
- [DriveUploadURL](interfaces/DriveUploadURL.md)
- [EffectiveConfig](interfaces/EffectiveConfig.md)
- [EffectiveConfigAgent](interfaces/EffectiveConfigAgent.md)
- [EffectiveConfigAgentToolsItem](interfaces/EffectiveConfigAgentToolsItem.md)
Expand Down Expand Up @@ -111,6 +123,7 @@
- [IdentityUsage](interfaces/IdentityUsage.md)
- [ImageSource](interfaces/ImageSource.md)
- [ImageSourceParam](interfaces/ImageSourceParam.md)
- [ManagedToolConfig](interfaces/ManagedToolConfig.md)
- [MCPServer](interfaces/MCPServer.md)
- [MCPServerOverride](interfaces/MCPServerOverride.md)
- [MCPServerOverrideParam](interfaces/MCPServerOverrideParam.md)
Expand Down
155 changes: 155 additions & 0 deletions docs/api/forward/classes/Drives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / Drives

# Class: Drives

Persistent Identity files (Beta). Identity SATs also require identity_id.

## Extends

- `APIResource`

## Constructors

### Constructor

> **new Drives**(`_client`): `Drives`

#### Parameters

##### \_client

[`APIClient`](../../index/classes/APIClient.md)

#### Returns

`Drives`

#### Inherited from

`APIResource.constructor`

## Properties

### \_client

> `protected` `readonly` **\_client**: [`APIClient`](../../index/classes/APIClient.md)

#### Inherited from

`APIResource._client`

## Methods

### clear()

> **clear**(`params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveClearResponse`](../interfaces/DriveClearResponse.md)\>

Clear the selected Identity Drive. All its files are deleted.

#### Parameters

##### params

[`DriveClearParams`](../interfaces/DriveClearParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveClearResponse`](../interfaces/DriveClearResponse.md)\>

***

### createDownloadUrl()

> **createDownloadUrl**(`params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveDownloadURL`](../interfaces/DriveDownloadURL.md)\>

Request a GET URL. Use a new Idempotency-Key or omit it to refresh expiry.

#### Parameters

##### params

[`DriveCreateDownloadURLParams`](../interfaces/DriveCreateDownloadURLParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveDownloadURL`](../interfaces/DriveDownloadURL.md)\>

***

### createUploadUrl()

> **createUploadUrl**(`params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveUploadURL`](../interfaces/DriveUploadURL.md)\>

Request a PUT URL. Only its returned headers belong on the storage request.
Replaying an Idempotency-Key does not renew the URL's expiry.

#### Parameters

##### params

[`DriveCreateUploadURLParams`](../interfaces/DriveCreateUploadURLParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveUploadURL`](../interfaces/DriveUploadURL.md)\>

***

### deleteEntry()

> **deleteEntry**(`params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveDeleteEntryResponse`](../interfaces/DriveDeleteEntryResponse.md)\>

Delete a file or directory. Nonempty directories require recursive: true.
Partial failures preserve error.deleted_count in the API error body.

#### Parameters

##### params

[`DriveDeleteEntryParams`](../interfaces/DriveDeleteEntryParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveDeleteEntryResponse`](../interfaces/DriveDeleteEntryResponse.md)\>

***

### listEntries()

> **listEntries**(`params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveListEntriesResponse`](../interfaces/DriveListEntriesResponse.md)\>

One page in service order. Pass next_page_token unchanged to page_token.

#### Parameters

##### params

[`DriveListEntriesParams`](../interfaces/DriveListEntriesParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`DriveListEntriesResponse`](../interfaces/DriveListEntriesResponse.md)\>
13 changes: 13 additions & 0 deletions docs/api/forward/interfaces/DriveClearParams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / DriveClearParams

# Interface: DriveClearParams

## Properties

### identity\_id

> **identity\_id**: `string`
17 changes: 17 additions & 0 deletions docs/api/forward/interfaces/DriveClearResponse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / DriveClearResponse

# Interface: DriveClearResponse

## Indexable

> \[`key`: `string`\]: `unknown`

## Properties

### deleted\_count

> **deleted\_count**: `number`
19 changes: 19 additions & 0 deletions docs/api/forward/interfaces/DriveCreateDownloadURLParams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / DriveCreateDownloadURLParams

# Interface: DriveCreateDownloadURLParams

## Properties

### identity\_id

> **identity\_id**: `string`

***

### path

> **path**: `string`
27 changes: 27 additions & 0 deletions docs/api/forward/interfaces/DriveCreateUploadURLParams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / DriveCreateUploadURLParams

# Interface: DriveCreateUploadURLParams

## Properties

### content\_type?

> `optional` **content\_type?**: `string`

***

### identity\_id

> **identity\_id**: `string`

Query parameter, not part of the JSON body.

***

### path

> **path**: `string`
25 changes: 25 additions & 0 deletions docs/api/forward/interfaces/DriveDeleteEntryParams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / DriveDeleteEntryParams

# Interface: DriveDeleteEntryParams

## Properties

### identity\_id

> **identity\_id**: `string`

***

### path

> **path**: `string`

***

### recursive?

> `optional` **recursive?**: `boolean`
17 changes: 17 additions & 0 deletions docs/api/forward/interfaces/DriveDeleteEntryResponse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / DriveDeleteEntryResponse

# Interface: DriveDeleteEntryResponse

## Indexable

> \[`key`: `string`\]: `unknown`

## Properties

### deleted\_count

> **deleted\_count**: `number`
Loading
Loading