@agent-api/sdk 1.0.8 → 1.1.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
@@ -1,5 +1,29 @@
1
1
  # Changelog — @agent-api/sdk
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Added
6
+
7
+ - Browser/device login helpers for CLI and desktop integrations: `client.auth.startDeviceAuth()`, `client.auth.pollDeviceAuth()`, and `client.auth.waitForDeviceAuth()`.
8
+ - Convenience methods on `AgentAPI`: `startDeviceAuth()`, `pollDeviceAuth()`, and `waitForDeviceAuth()`.
9
+ - `DeviceAuthFlowError` for expired, consumed, or timed-out device login flows.
10
+
11
+ ## 1.1.0
12
+
13
+ ### Added
14
+
15
+ - Node-only `@agent-api/sdk/local` entrypoint for framework-neutral local app and CLI runtime support.
16
+ - Cross-platform app directory resolution for data, config, cache, logs, and temp files.
17
+ - Root-scoped local file stores with path traversal protection, atomic writes, JSON helpers, recursive listing, and local skill discovery.
18
+ - Local workdir operations for entry search, file delivery, line-range reads and edits, literal grep, and directory summaries.
19
+ - `LocalWorkspace` and `LocalWorkspaceManager` for project roots, default ignore rules, patch previews, snapshots, diffs, and file-watch handles.
20
+ - Typed local errors, `.gitignore` loading, sensitivity classification for likely secret paths, and multi-file line-edit plans with conflict detection and rollback.
21
+ - `createLocalContextPackage()` for bounded, secret-aware local workspace manifests that app integrations can hand to agent workflows.
22
+
23
+ ### Changed
24
+
25
+ - Split the local SDK source behind a small `@agent-api/sdk/local` barrel so runtime and context APIs can grow without a monolithic entrypoint.
26
+
3
27
  ## 1.0.8
4
28
 
5
29
  ### Changed
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Production JavaScript/TypeScript SDK for the Managed Agent API.
4
4
 
5
- **Published on npm:** [`@agent-api/sdk`](https://www.npmjs.com/package/@agent-api/sdk) (v1.0.8)
5
+ **Published on npm:** [`@agent-api/sdk`](https://www.npmjs.com/package/@agent-api/sdk) (v1.1.1)
6
6
 
7
7
  ## Install
8
8
 
@@ -46,7 +46,7 @@ src/
46
46
  errors.ts # Typed error hierarchy + retry helpers
47
47
  pagination.ts # Cursor pagination utilities
48
48
  streaming.ts # SSE parser
49
- resources/ # responses, models, presets, tools, volumes, skills
49
+ resources/ # auth, responses, models, presets, tools, volumes, skills
50
50
  types/ # Request/response TypeScript types
51
51
  internal/http.ts # Retries, timeouts, User-Agent
52
52
  ```
@@ -61,6 +61,25 @@ src/
61
61
  | `client.tools` | `list` |
62
62
  | `client.volumes` | `list`, `create`, `retrieve`, `update`, `delete`, `listEntries`, `searchEntries`, `readFile`, `writeFile`, `deletePath`, `reconcileUsage`, `createDirectory`, `downloadArchive`, `summarize`, `readLines`, `patchLines`, `grep` |
63
63
  | `client.skills` | `list`, `create`, `discover`, `focus`, `createDev`, `updateFile`, `retrieve`, `update`, `archive`, `delete`, `diff`, `acceptDev`, `discardDev`, `exportArchive`, `importArchive`, `pushDirectory`, `pullDirectory`, `listFiles`, `readFile`, `writeFile`, `deleteFile` |
64
+ | `client.auth` | `startDeviceAuth`, `pollDeviceAuth`, `waitForDeviceAuth` |
65
+
66
+ ## Browser Device Login
67
+
68
+ CLI and desktop apps can use browser login without handling user passwords or static API keys.
69
+
70
+ ```typescript
71
+ const challenge = await client.auth.startDeviceAuth({ client_name: "Agent CLI" });
72
+ console.log(`Open ${challenge.verification_uri_complete}`);
73
+
74
+ const session = await client.auth.waitForDeviceAuth({
75
+ device_code: challenge.device_code,
76
+ interval_seconds: challenge.interval_seconds,
77
+ });
78
+
79
+ console.log(session.access_token);
80
+ ```
81
+
82
+ The SDK returns URLs and polling helpers only. Opening the browser belongs to the CLI, Electron, Tauri, or native host app.
64
83
 
65
84
  ## Durable Volumes
66
85
 
@@ -96,6 +115,93 @@ const response = await client.responses.create({
96
115
  });
97
116
  ```
98
117
 
118
+ ## Local Runtime
119
+
120
+ Local app integrations can use the Node-only `@agent-api/sdk/local` entrypoint for core filesystem/runtime support. This layer is framework-neutral: it does not depend on Electron, Tauri, native UI kits, or browser APIs.
121
+
122
+ ```typescript
123
+ import { createLocalRuntime } from "@agent-api/sdk/local";
124
+
125
+ const local = createLocalRuntime({
126
+ appName: "agent-studio",
127
+ });
128
+
129
+ await local.ensure();
130
+
131
+ await local.config.set("settings.json", "baseURL", "https://api.agentsway.dev");
132
+ const baseURL = await local.config.get<string>("settings.json", "baseURL");
133
+
134
+ await local.cache.writeJSON("models.json", [{ id: "openai/gpt-5.5" }]);
135
+ const skills = await local.skills.discover();
136
+ ```
137
+
138
+ The runtime provides:
139
+
140
+ - Cross-platform app directories for data, config, cache, logs, and temp files.
141
+ - Root-scoped file stores with path traversal protection.
142
+ - Atomic text/JSON writes, byte reads/writes, recursive listing, copy, remove, and stat helpers.
143
+ - Local workdir operations inspired by platform volumes: `listEntries`, `searchEntries`, `readFile`, `writeFile`, `deletePath`, `createDirectory`, `readLines`, `patchLines`, `grep`, and `summarize`.
144
+ - First-class local workspaces with default ignore rules, scoped workbench operations, patch previews, snapshots, diffs, file-watch handles, and budgeted context packaging.
145
+ - Typed local errors, `.gitignore` loading, sensitivity classification, and multi-file edit plans with rollback on failure.
146
+ - JSON config helpers for typed app settings.
147
+ - Local skill discovery built on `localSkillFromDirectory()`.
148
+
149
+ Keep UI and OS interaction policy in your host framework. Electron, Tauri, Qt, or native apps should call this layer from their trusted local process and expose only the capabilities their UI needs.
150
+
151
+ ```typescript
152
+ const workspace = local.data.child("workspaces/demo");
153
+
154
+ await workspace.writeText("src/index.ts", "console.log('hello');\n");
155
+
156
+ const matches = await workspace.grep({ pattern: "hello", path: "src" });
157
+ const lines = await workspace.readLines("src/index.ts", { startLine: 1, endLine: 20 });
158
+ await workspace.patchLines("src/index.ts", {
159
+ startLine: 1,
160
+ replacement: "console.log('patched');",
161
+ });
162
+
163
+ const summary = await workspace.summarize();
164
+ ```
165
+
166
+ For project/workspace roots, prefer `local.workspace()` so SDK defaults protect common generated directories such as `.git`, `node_modules`, `dist`, and build caches.
167
+
168
+ ```typescript
169
+ const project = local.workspace("/path/to/project", {
170
+ name: "my-project",
171
+ trusted: true,
172
+ ignore: ["vendor", /^tmp\//],
173
+ });
174
+ await project.loadIgnoreFiles();
175
+
176
+ const before = await project.snapshot();
177
+ const plan = await project.previewEdits([
178
+ {
179
+ path: "src/index.ts",
180
+ startLine: 1,
181
+ endLine: 1,
182
+ replacement: "console.log('patched');",
183
+ },
184
+ ]);
185
+ await project.applyEdits(plan.edits);
186
+ const after = await project.snapshot();
187
+ const diff = project.diff(before, after);
188
+
189
+ const sensitivity = project.classifyPath(".env");
190
+ ```
191
+
192
+ Use `createLocalContextPackage()` when a local app needs to prepare bounded workspace context for an agent request. The package includes a manifest, selected file previews, optional search matches, hashes, sensitivity labels, and secret-aware omission by default.
193
+
194
+ ```typescript
195
+ import { createLocalContextPackage } from "@agent-api/sdk/local";
196
+
197
+ const context = await createLocalContextPackage(project, {
198
+ query: "billing",
199
+ includeSearch: true,
200
+ maxFiles: 80,
201
+ maxBytes: 256 * 1024,
202
+ });
203
+ ```
204
+
99
205
  ## Production features
100
206
 
101
207
  - **Retries:** automatic exponential backoff for network failures, 429, and 5xx (default 2 retries).
package/dist/client.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { AuthResource } from "./resources/auth.js";
1
2
  import { ModelsResource } from "./resources/models.js";
2
3
  import { PresetsResource } from "./resources/presets.js";
3
4
  import { ResponsesResource } from "./resources/responses.js";
@@ -23,7 +24,11 @@ export declare class AgentAPI {
23
24
  readonly tools: ToolsResource;
24
25
  readonly volumes: VolumesResource;
25
26
  readonly skills: SkillsResource;
27
+ readonly auth: AuthResource;
26
28
  private readonly http;
27
29
  constructor(options?: ClientOptions);
30
+ startDeviceAuth: (...args: Parameters<AuthResource["startDeviceAuth"]>) => Promise<import("./index.js").DeviceAuthStart>;
31
+ pollDeviceAuth: (...args: Parameters<AuthResource["pollDeviceAuth"]>) => Promise<import("./index.js").DeviceAuthPollResult>;
32
+ waitForDeviceAuth: (...args: Parameters<AuthResource["waitForDeviceAuth"]>) => Promise<import("./index.js").ApprovedDeviceAuth>;
28
33
  }
29
34
  export { VERSION };
package/dist/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { APIConnectionError } from "./errors.js";
2
2
  import { readEnv } from "./internal/env.js";
3
3
  import { HTTPClient } from "./internal/http.js";
4
+ import { AuthResource } from "./resources/auth.js";
4
5
  import { ModelsResource } from "./resources/models.js";
5
6
  import { PresetsResource } from "./resources/presets.js";
6
7
  import { ResponsesResource } from "./resources/responses.js";
@@ -25,6 +26,7 @@ export class AgentAPI {
25
26
  tools;
26
27
  volumes;
27
28
  skills;
29
+ auth;
28
30
  http;
29
31
  constructor(options = {}) {
30
32
  this.apiKey = options.apiKey ?? readEnv("AGENT_API_KEY");
@@ -53,6 +55,10 @@ export class AgentAPI {
53
55
  this.tools = new ToolsResource(this.http);
54
56
  this.volumes = new VolumesResource(this.http);
55
57
  this.skills = new SkillsResource(this.http);
58
+ this.auth = new AuthResource(this.http);
56
59
  }
60
+ startDeviceAuth = (...args) => this.auth.startDeviceAuth(...args);
61
+ pollDeviceAuth = (...args) => this.auth.pollDeviceAuth(...args);
62
+ waitForDeviceAuth = (...args) => this.auth.waitForDeviceAuth(...args);
57
63
  }
58
64
  export { VERSION };
package/dist/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export { functionCallOutputInput, pendingFunctionCalls, runLocalFunctionHandlers
7
7
  export type { LocalFunctionHandler, LocalFunctionHandlers } from "./local-functions.js";
8
8
  export { localSkillFromDirectory, pendingLocalSkillCalls, runLocalSkillHandlers } from "./local-skills.js";
9
9
  export type { LocalSkillDirectoryOptions } from "./local-skills.js";
10
+ export { AuthResource, DeviceAuthFlowError } from "./resources/auth.js";
package/dist/index.js CHANGED
@@ -4,3 +4,4 @@ export { APIError, APIConnectionError, APIStatusError, AuthenticationError, BadR
4
4
  export * from "./types/index.js";
5
5
  export { functionCallOutputInput, pendingFunctionCalls, runLocalFunctionHandlers, } from "./local-functions.js";
6
6
  export { localSkillFromDirectory, pendingLocalSkillCalls, runLocalSkillHandlers } from "./local-skills.js";
7
+ export { AuthResource, DeviceAuthFlowError } from "./resources/auth.js";
@@ -0,0 +1,49 @@
1
+ import { type LocalFileDeliver, type LocalGrepResponse, type LocalPathSensitivity, type LocalPathSensitivityInfo, type LocalSummary, type LocalWorkspace, type LocalWorkspaceSnapshotFile } from "./core.js";
2
+ export interface LocalContextPackageParams {
3
+ path?: string;
4
+ include?: string[];
5
+ exclude?: string[];
6
+ query?: string;
7
+ maxFiles?: number;
8
+ maxBytes?: number;
9
+ maxBytesPerFile?: number;
10
+ previewBytes?: number;
11
+ includeContent?: boolean;
12
+ includeHashes?: boolean;
13
+ includeSummary?: boolean;
14
+ includeSearch?: boolean;
15
+ includeSecrets?: boolean;
16
+ }
17
+ export interface LocalContextFile {
18
+ path: string;
19
+ size: number;
20
+ sha256?: string;
21
+ mime_type?: string;
22
+ sensitivity: LocalPathSensitivity;
23
+ sensitivity_reason?: string;
24
+ content?: string;
25
+ content_base64?: string;
26
+ encoding?: LocalFileDeliver["encoding"];
27
+ truncated?: boolean;
28
+ omitted_reason?: string;
29
+ }
30
+ export interface LocalContextManifest {
31
+ object: "local_context_manifest";
32
+ root: string;
33
+ workspace_name: string;
34
+ generated_at_unix: number;
35
+ base_path: string;
36
+ file_count: number;
37
+ total_bytes: number;
38
+ included_bytes: number;
39
+ truncated: boolean;
40
+ files: LocalContextFile[];
41
+ summary?: LocalSummary;
42
+ search?: LocalGrepResponse;
43
+ }
44
+ export declare function createLocalContextPackage(workspace: LocalWorkspace, params?: LocalContextPackageParams): Promise<LocalContextManifest>;
45
+ export declare function summarizeLocalContextSensitivity(files: Array<Pick<LocalContextFile, "path" | "sensitivity" | "sensitivity_reason">>): {
46
+ highest: LocalPathSensitivity;
47
+ files: LocalPathSensitivityInfo[];
48
+ };
49
+ export type LocalContextSnapshotFile = LocalWorkspaceSnapshotFile;
@@ -0,0 +1,154 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { classifyLocalPathSensitivity, } from "./core.js";
4
+ export async function createLocalContextPackage(workspace, params = {}) {
5
+ const basePath = params.path ?? ".";
6
+ const maxFiles = positiveInt(params.maxFiles, 80);
7
+ const maxBytes = positiveInt(params.maxBytes, 256 * 1024);
8
+ const maxBytesPerFile = positiveInt(params.maxBytesPerFile, 32 * 1024);
9
+ const previewBytes = positiveInt(params.previewBytes, maxBytesPerFile);
10
+ const includeContent = params.includeContent ?? true;
11
+ const includeHashes = params.includeHashes ?? true;
12
+ const includeSummary = params.includeSummary ?? true;
13
+ const includeSearch = Boolean(params.includeSearch && params.query?.trim());
14
+ const includeSecrets = params.includeSecrets ?? false;
15
+ const stats = await workspace.list(basePath, { recursive: true });
16
+ const fileStats = stats
17
+ .filter((item) => item.type === "file")
18
+ .filter((item) => matchesPathFilters(item.path, params.include, params.exclude))
19
+ .sort((a, b) => a.path.localeCompare(b.path));
20
+ const files = [];
21
+ let includedBytes = 0;
22
+ let truncated = fileStats.length > maxFiles;
23
+ for (const item of fileStats.slice(0, maxFiles)) {
24
+ const sensitivity = classifyLocalPathSensitivity(item.path);
25
+ const baseFile = {
26
+ path: item.path,
27
+ size: item.size,
28
+ sensitivity: sensitivity.sensitivity,
29
+ sensitivity_reason: sensitivity.reason,
30
+ };
31
+ if (!includeSecrets && sensitivity.sensitivity === "secret") {
32
+ files.push({ ...baseFile, omitted_reason: "secret_path" });
33
+ continue;
34
+ }
35
+ if (item.size > maxBytesPerFile) {
36
+ files.push({ ...baseFile, omitted_reason: "file_too_large" });
37
+ truncated = true;
38
+ continue;
39
+ }
40
+ if (includedBytes >= maxBytes) {
41
+ files.push({ ...baseFile, omitted_reason: "package_budget_exceeded" });
42
+ truncated = true;
43
+ continue;
44
+ }
45
+ const remaining = maxBytes - includedBytes;
46
+ const readBudget = Math.min(previewBytes, maxBytesPerFile, remaining);
47
+ if (readBudget <= 0) {
48
+ files.push({ ...baseFile, omitted_reason: "package_budget_exceeded" });
49
+ truncated = true;
50
+ continue;
51
+ }
52
+ const delivered = await workspace.readFile(item.path, { maxBytes: readBudget });
53
+ includedBytes += delivered.encoding === "text"
54
+ ? Buffer.byteLength(delivered.content ?? "", "utf8")
55
+ : Buffer.byteLength(delivered.content_base64 ?? "", "base64");
56
+ const packaged = {
57
+ ...baseFile,
58
+ mime_type: delivered.mime_type,
59
+ encoding: delivered.encoding,
60
+ truncated: delivered.truncated || undefined,
61
+ };
62
+ if (includeContent) {
63
+ packaged.content = delivered.content;
64
+ packaged.content_base64 = delivered.content_base64;
65
+ }
66
+ if (includeHashes) {
67
+ packaged.sha256 = await sha256File(workspace, item.path);
68
+ }
69
+ if (delivered.truncated) {
70
+ truncated = true;
71
+ }
72
+ files.push(packaged);
73
+ }
74
+ const summary = includeSummary
75
+ ? await workspace.summarize({
76
+ path: basePath,
77
+ maxFiles,
78
+ previewBytes,
79
+ ignore: params.exclude,
80
+ })
81
+ : undefined;
82
+ const search = includeSearch
83
+ ? await workspace.grep({
84
+ pattern: params.query,
85
+ path: basePath,
86
+ limit: maxFiles,
87
+ maxBytesPerFile,
88
+ ignore: params.exclude,
89
+ })
90
+ : undefined;
91
+ return {
92
+ object: "local_context_manifest",
93
+ root: workspace.root,
94
+ workspace_name: workspace.name,
95
+ generated_at_unix: Math.floor(Date.now() / 1000),
96
+ base_path: basePath,
97
+ file_count: fileStats.length,
98
+ total_bytes: fileStats.reduce((sum, item) => sum + item.size, 0),
99
+ included_bytes: includedBytes,
100
+ truncated,
101
+ files,
102
+ summary,
103
+ search,
104
+ };
105
+ }
106
+ export function summarizeLocalContextSensitivity(files) {
107
+ const order = { normal: 0, sensitive: 1, secret: 2 };
108
+ let highest = "normal";
109
+ const sensitiveFiles = [];
110
+ for (const file of files) {
111
+ if (order[file.sensitivity] > order[highest]) {
112
+ highest = file.sensitivity;
113
+ }
114
+ if (file.sensitivity !== "normal") {
115
+ sensitiveFiles.push({
116
+ path: file.path,
117
+ sensitivity: file.sensitivity,
118
+ reason: file.sensitivity_reason,
119
+ });
120
+ }
121
+ }
122
+ return { highest, files: sensitiveFiles };
123
+ }
124
+ function matchesPathFilters(relativePath, include, exclude) {
125
+ if (include?.length && !include.some((pattern) => pathMatches(relativePath, pattern))) {
126
+ return false;
127
+ }
128
+ if (exclude?.some((pattern) => pathMatches(relativePath, pattern))) {
129
+ return false;
130
+ }
131
+ return true;
132
+ }
133
+ function pathMatches(relativePath, pattern) {
134
+ const clean = pattern.replace(/\\/g, "/").replace(/^\/+/, "");
135
+ if (!clean || clean === ".") {
136
+ return true;
137
+ }
138
+ if (!clean.includes("*")) {
139
+ return relativePath === clean || relativePath.startsWith(`${clean}/`);
140
+ }
141
+ const escaped = clean
142
+ .split("*")
143
+ .map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&"))
144
+ .join(".*");
145
+ return new RegExp(`^${escaped}$`).test(relativePath);
146
+ }
147
+ function positiveInt(value, fallback) {
148
+ const parsed = Number(value);
149
+ return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : fallback;
150
+ }
151
+ async function sha256File(workspace, relativePath) {
152
+ const fullPath = workspace.files.resolvePath(relativePath);
153
+ return createHash("sha256").update(await readFile(fullPath)).digest("hex");
154
+ }