@agent-api/sdk 1.0.6 → 1.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog — @agent-api/sdk
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Added
6
+
7
+ - Node-only `@agent-api/sdk/local` entrypoint for framework-neutral local app and CLI runtime support.
8
+ - Cross-platform app directory resolution for data, config, cache, logs, and temp files.
9
+ - Root-scoped local file stores with path traversal protection, atomic writes, JSON helpers, recursive listing, and local skill discovery.
10
+ - Local workdir operations for entry search, file delivery, line-range reads and edits, literal grep, and directory summaries.
11
+ - `LocalWorkspace` and `LocalWorkspaceManager` for project roots, default ignore rules, patch previews, snapshots, diffs, and file-watch handles.
12
+ - Typed local errors, `.gitignore` loading, sensitivity classification for likely secret paths, and multi-file line-edit plans with conflict detection and rollback.
13
+ - `createLocalContextPackage()` for bounded, secret-aware local workspace manifests that app integrations can hand to agent workflows.
14
+
15
+ ### Changed
16
+
17
+ - Split the local SDK source behind a small `@agent-api/sdk/local` barrel so runtime and context APIs can grow without a monolithic entrypoint.
18
+
19
+ ## 1.0.8
20
+
21
+ ### Changed
22
+
23
+ - Expanded `reasoning.effort` types to match the Responses-compatible enum: `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`.
24
+
25
+ ## 1.0.7
26
+
27
+ ### Added
28
+
29
+ - Skill artifact archive APIs: `client.skills.exportArchive()` and `client.skills.importArchive()`.
30
+ - Local directory sync helpers for platform skills: `client.skills.pushDirectory()` and `client.skills.pullDirectory()`.
31
+ - Skill branch diff API and file-level `acceptDev()` strategies (`patch` or `mirror`).
32
+
3
33
  ## 1.0.6
4
34
 
5
35
  ### 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.6)
5
+ **Published on npm:** [`@agent-api/sdk`](https://www.npmjs.com/package/@agent-api/sdk) (v1.0.8)
6
6
 
7
7
  ## Install
8
8
 
@@ -60,7 +60,7 @@ src/
60
60
  | `client.presets` | `list` |
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
- | `client.skills` | `list`, `create`, `discover`, `focus`, `createDev`, `updateFile`, `retrieve`, `update`, `archive`, `delete`, `acceptDev`, `discardDev`, `listFiles`, `readFile`, `writeFile`, `deleteFile` |
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
64
 
65
65
  ## Durable Volumes
66
66
 
@@ -80,6 +80,8 @@ const response = await client.agent.create({
80
80
 
81
81
  ## Skills
82
82
 
83
+ `localSkillFromDirectory()` reads `SKILL.md` into the descriptor for initial local-skill auto-focus; later focused reads still use the local skill tool bridge.
84
+
83
85
  ```typescript
84
86
  import { localSkillFromDirectory } from "@agent-api/sdk";
85
87
 
@@ -94,6 +96,93 @@ const response = await client.responses.create({
94
96
  });
95
97
  ```
96
98
 
99
+ ## Local Runtime
100
+
101
+ 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.
102
+
103
+ ```typescript
104
+ import { createLocalRuntime } from "@agent-api/sdk/local";
105
+
106
+ const local = createLocalRuntime({
107
+ appName: "agent-studio",
108
+ });
109
+
110
+ await local.ensure();
111
+
112
+ await local.config.set("settings.json", "baseURL", "https://api.agentsway.dev");
113
+ const baseURL = await local.config.get<string>("settings.json", "baseURL");
114
+
115
+ await local.cache.writeJSON("models.json", [{ id: "openai/gpt-5.5" }]);
116
+ const skills = await local.skills.discover();
117
+ ```
118
+
119
+ The runtime provides:
120
+
121
+ - Cross-platform app directories for data, config, cache, logs, and temp files.
122
+ - Root-scoped file stores with path traversal protection.
123
+ - Atomic text/JSON writes, byte reads/writes, recursive listing, copy, remove, and stat helpers.
124
+ - Local workdir operations inspired by platform volumes: `listEntries`, `searchEntries`, `readFile`, `writeFile`, `deletePath`, `createDirectory`, `readLines`, `patchLines`, `grep`, and `summarize`.
125
+ - First-class local workspaces with default ignore rules, scoped workbench operations, patch previews, snapshots, diffs, file-watch handles, and budgeted context packaging.
126
+ - Typed local errors, `.gitignore` loading, sensitivity classification, and multi-file edit plans with rollback on failure.
127
+ - JSON config helpers for typed app settings.
128
+ - Local skill discovery built on `localSkillFromDirectory()`.
129
+
130
+ 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.
131
+
132
+ ```typescript
133
+ const workspace = local.data.child("workspaces/demo");
134
+
135
+ await workspace.writeText("src/index.ts", "console.log('hello');\n");
136
+
137
+ const matches = await workspace.grep({ pattern: "hello", path: "src" });
138
+ const lines = await workspace.readLines("src/index.ts", { startLine: 1, endLine: 20 });
139
+ await workspace.patchLines("src/index.ts", {
140
+ startLine: 1,
141
+ replacement: "console.log('patched');",
142
+ });
143
+
144
+ const summary = await workspace.summarize();
145
+ ```
146
+
147
+ For project/workspace roots, prefer `local.workspace()` so SDK defaults protect common generated directories such as `.git`, `node_modules`, `dist`, and build caches.
148
+
149
+ ```typescript
150
+ const project = local.workspace("/path/to/project", {
151
+ name: "my-project",
152
+ trusted: true,
153
+ ignore: ["vendor", /^tmp\//],
154
+ });
155
+ await project.loadIgnoreFiles();
156
+
157
+ const before = await project.snapshot();
158
+ const plan = await project.previewEdits([
159
+ {
160
+ path: "src/index.ts",
161
+ startLine: 1,
162
+ endLine: 1,
163
+ replacement: "console.log('patched');",
164
+ },
165
+ ]);
166
+ await project.applyEdits(plan.edits);
167
+ const after = await project.snapshot();
168
+ const diff = project.diff(before, after);
169
+
170
+ const sensitivity = project.classifyPath(".env");
171
+ ```
172
+
173
+ 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.
174
+
175
+ ```typescript
176
+ import { createLocalContextPackage } from "@agent-api/sdk/local";
177
+
178
+ const context = await createLocalContextPackage(project, {
179
+ query: "billing",
180
+ includeSearch: true,
181
+ maxFiles: 80,
182
+ maxBytes: 256 * 1024,
183
+ });
184
+ ```
185
+
97
186
  ## Production features
98
187
 
99
188
  - **Retries:** automatic exponential backoff for network failures, 429, and 5xx (default 2 retries).
@@ -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
+ }