@robota-sdk/agent-sdk 3.0.0-beta.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.
@@ -0,0 +1,521 @@
1
+ // src/types.ts
2
+ import { TRUST_TO_MODE } from "@robota-sdk/agent-core";
3
+
4
+ // src/session.ts
5
+ import { Session } from "@robota-sdk/agent-sessions";
6
+
7
+ // src/session-store.ts
8
+ import { SessionStore } from "@robota-sdk/agent-sessions";
9
+
10
+ // src/config/config-loader.ts
11
+ import { readFileSync, existsSync } from "fs";
12
+ import { join } from "path";
13
+
14
+ // src/config/config-types.ts
15
+ import { z } from "zod";
16
+ var ProviderSchema = z.object({
17
+ name: z.string().optional(),
18
+ model: z.string().optional(),
19
+ apiKey: z.string().optional()
20
+ });
21
+ var PermissionsSchema = z.object({
22
+ /** Patterns that are always approved without prompting */
23
+ allow: z.array(z.string()).optional(),
24
+ /** Patterns that are always denied */
25
+ deny: z.array(z.string()).optional()
26
+ });
27
+ var EnvSchema = z.record(z.string()).optional();
28
+ var HookDefinitionSchema = z.object({
29
+ type: z.literal("command"),
30
+ command: z.string()
31
+ });
32
+ var HookGroupSchema = z.object({
33
+ matcher: z.string(),
34
+ hooks: z.array(HookDefinitionSchema)
35
+ });
36
+ var HooksSchema = z.object({
37
+ PreToolUse: z.array(HookGroupSchema).optional(),
38
+ PostToolUse: z.array(HookGroupSchema).optional(),
39
+ SessionStart: z.array(HookGroupSchema).optional(),
40
+ Stop: z.array(HookGroupSchema).optional()
41
+ }).optional();
42
+ var SettingsSchema = z.object({
43
+ /** Trust level used when no --permission-mode flag is given */
44
+ defaultTrustLevel: z.enum(["safe", "moderate", "full"]).optional(),
45
+ provider: ProviderSchema.optional(),
46
+ permissions: PermissionsSchema.optional(),
47
+ env: EnvSchema,
48
+ hooks: HooksSchema
49
+ });
50
+
51
+ // src/config/config-loader.ts
52
+ function getHomeDir() {
53
+ return process.env.HOME ?? process.env.USERPROFILE ?? "/";
54
+ }
55
+ var DEFAULTS = {
56
+ defaultTrustLevel: "moderate",
57
+ provider: {
58
+ name: "anthropic",
59
+ model: "claude-opus-4-5",
60
+ apiKey: void 0
61
+ },
62
+ permissions: {
63
+ allow: [],
64
+ deny: []
65
+ },
66
+ env: {}
67
+ };
68
+ function readJsonFile(filePath) {
69
+ if (!existsSync(filePath)) {
70
+ return void 0;
71
+ }
72
+ const raw = readFileSync(filePath, "utf-8");
73
+ return JSON.parse(raw);
74
+ }
75
+ function resolveEnvRef(value) {
76
+ const ENV_PREFIX = "$ENV:";
77
+ if (value.startsWith(ENV_PREFIX)) {
78
+ const varName = value.slice(ENV_PREFIX.length);
79
+ return process.env[varName] ?? value;
80
+ }
81
+ return value;
82
+ }
83
+ function resolveEnvRefs(settings) {
84
+ if (settings.provider?.apiKey !== void 0) {
85
+ return {
86
+ ...settings,
87
+ provider: {
88
+ ...settings.provider,
89
+ apiKey: resolveEnvRef(settings.provider.apiKey)
90
+ }
91
+ };
92
+ }
93
+ return settings;
94
+ }
95
+ function mergeSettings(layers) {
96
+ return layers.reduce((merged, layer) => {
97
+ return {
98
+ ...merged,
99
+ ...layer,
100
+ provider: merged.provider !== void 0 || layer.provider !== void 0 ? { ...merged.provider, ...layer.provider } : void 0,
101
+ permissions: merged.permissions !== void 0 || layer.permissions !== void 0 ? {
102
+ allow: layer.permissions?.allow ?? merged.permissions?.allow,
103
+ deny: layer.permissions?.deny ?? merged.permissions?.deny
104
+ } : void 0,
105
+ env: {
106
+ ...merged.env ?? {},
107
+ ...layer.env ?? {}
108
+ }
109
+ };
110
+ }, {});
111
+ }
112
+ function toResolvedConfig(merged) {
113
+ return {
114
+ defaultTrustLevel: merged.defaultTrustLevel ?? DEFAULTS.defaultTrustLevel,
115
+ provider: {
116
+ name: merged.provider?.name ?? DEFAULTS.provider.name,
117
+ model: merged.provider?.model ?? DEFAULTS.provider.model,
118
+ apiKey: merged.provider?.apiKey ?? DEFAULTS.provider.apiKey
119
+ },
120
+ permissions: {
121
+ allow: merged.permissions?.allow ?? DEFAULTS.permissions.allow,
122
+ deny: merged.permissions?.deny ?? DEFAULTS.permissions.deny
123
+ },
124
+ env: merged.env ?? DEFAULTS.env,
125
+ hooks: merged.hooks ?? void 0
126
+ };
127
+ }
128
+ async function loadConfig(cwd) {
129
+ const userSettingsPath = join(getHomeDir(), ".robota", "settings.json");
130
+ const projectSettingsPath = join(cwd, ".robota", "settings.json");
131
+ const localSettingsPath = join(cwd, ".robota", "settings.local.json");
132
+ const rawLayers = [
133
+ readJsonFile(userSettingsPath),
134
+ readJsonFile(projectSettingsPath),
135
+ readJsonFile(localSettingsPath)
136
+ ].filter((v) => v !== void 0);
137
+ const parsedLayers = rawLayers.map((raw, index) => {
138
+ const result = SettingsSchema.safeParse(raw);
139
+ if (!result.success) {
140
+ const paths = [userSettingsPath, projectSettingsPath, localSettingsPath].filter(
141
+ (_, i) => rawLayers[i] !== void 0
142
+ );
143
+ throw new Error(`Invalid settings in ${paths[index] ?? "unknown"}: ${result.error.message}`);
144
+ }
145
+ return resolveEnvRefs(result.data);
146
+ });
147
+ const merged = mergeSettings(parsedLayers);
148
+ return toResolvedConfig(merged);
149
+ }
150
+
151
+ // src/context/context-loader.ts
152
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
153
+ import { join as join2, dirname, resolve } from "path";
154
+ var AGENTS_FILENAME = "AGENTS.md";
155
+ var CLAUDE_FILENAME = "CLAUDE.md";
156
+ function collectFilesWalkingUp(startDir, filename) {
157
+ const found = [];
158
+ let current = resolve(startDir);
159
+ let atRoot = false;
160
+ while (!atRoot) {
161
+ const candidate = join2(current, filename);
162
+ if (existsSync2(candidate)) {
163
+ found.push(candidate);
164
+ }
165
+ const parent = dirname(current);
166
+ atRoot = parent === current;
167
+ if (!atRoot) {
168
+ current = parent;
169
+ }
170
+ }
171
+ return found.reverse();
172
+ }
173
+ function extractCompactInstructions(content) {
174
+ const lines = content.split("\n");
175
+ let capturing = false;
176
+ let headingLevel = 0;
177
+ const captured = [];
178
+ for (const line of lines) {
179
+ const headingMatch = /^(#{1,6})\s+/.exec(line);
180
+ if (headingMatch) {
181
+ if (capturing) {
182
+ if (headingMatch[1].length <= headingLevel) break;
183
+ }
184
+ if (/compact\s+instructions/i.test(line)) {
185
+ capturing = true;
186
+ headingLevel = headingMatch[1].length;
187
+ continue;
188
+ }
189
+ }
190
+ if (capturing) {
191
+ captured.push(line);
192
+ }
193
+ }
194
+ const result = captured.join("\n").trim();
195
+ return result || void 0;
196
+ }
197
+ async function loadContext(cwd) {
198
+ const agentsPaths = collectFilesWalkingUp(cwd, AGENTS_FILENAME);
199
+ const claudePaths = collectFilesWalkingUp(cwd, CLAUDE_FILENAME);
200
+ const agentsMd = agentsPaths.map((p) => readFileSync2(p, "utf-8")).join("\n\n");
201
+ const claudeMd = claudePaths.map((p) => readFileSync2(p, "utf-8")).join("\n\n");
202
+ const compactInstructions = extractCompactInstructions(claudeMd);
203
+ return { agentsMd, claudeMd, compactInstructions };
204
+ }
205
+
206
+ // src/context/project-detector.ts
207
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
208
+ import { join as join3 } from "path";
209
+ function tryReadJson(filePath) {
210
+ if (!existsSync3(filePath)) return void 0;
211
+ try {
212
+ return JSON.parse(readFileSync3(filePath, "utf-8"));
213
+ } catch {
214
+ return void 0;
215
+ }
216
+ }
217
+ function detectPackageManager(cwd) {
218
+ if (existsSync3(join3(cwd, "pnpm-workspace.yaml")) || existsSync3(join3(cwd, "pnpm-lock.yaml"))) {
219
+ return "pnpm";
220
+ }
221
+ if (existsSync3(join3(cwd, "yarn.lock"))) {
222
+ return "yarn";
223
+ }
224
+ if (existsSync3(join3(cwd, "bun.lockb"))) {
225
+ return "bun";
226
+ }
227
+ if (existsSync3(join3(cwd, "package-lock.json"))) {
228
+ return "npm";
229
+ }
230
+ return void 0;
231
+ }
232
+ async function detectProject(cwd) {
233
+ const pkgJsonPath = join3(cwd, "package.json");
234
+ const tsconfigPath = join3(cwd, "tsconfig.json");
235
+ const pyprojectPath = join3(cwd, "pyproject.toml");
236
+ const cargoPath = join3(cwd, "Cargo.toml");
237
+ const goModPath = join3(cwd, "go.mod");
238
+ if (existsSync3(pkgJsonPath)) {
239
+ const pkgJson = tryReadJson(pkgJsonPath);
240
+ const language = existsSync3(tsconfigPath) ? "typescript" : "javascript";
241
+ const packageManager = detectPackageManager(cwd);
242
+ return {
243
+ type: "node",
244
+ name: pkgJson?.name,
245
+ packageManager,
246
+ language
247
+ };
248
+ }
249
+ if (existsSync3(pyprojectPath) || existsSync3(join3(cwd, "setup.py"))) {
250
+ return {
251
+ type: "python",
252
+ language: "python"
253
+ };
254
+ }
255
+ if (existsSync3(cargoPath)) {
256
+ return {
257
+ type: "rust",
258
+ language: "rust"
259
+ };
260
+ }
261
+ if (existsSync3(goModPath)) {
262
+ return {
263
+ type: "go",
264
+ language: "go"
265
+ };
266
+ }
267
+ return {
268
+ type: "unknown",
269
+ language: "unknown"
270
+ };
271
+ }
272
+
273
+ // src/context/system-prompt-builder.ts
274
+ var TRUST_LEVEL_DESCRIPTIONS = {
275
+ safe: "safe (read-only / plan mode \u2014 only read-access tools are available)",
276
+ moderate: "moderate (default mode \u2014 write and bash tools require approval)",
277
+ full: "full (acceptEdits mode \u2014 file writes are auto-approved; bash requires approval)"
278
+ };
279
+ function buildProjectSection(info) {
280
+ const lines = ["## Current Project"];
281
+ if (info.name !== void 0) {
282
+ lines.push(`- **Name:** ${info.name}`);
283
+ }
284
+ if (info.type !== "unknown") {
285
+ lines.push(`- **Type:** ${info.type}`);
286
+ }
287
+ if (info.language !== "unknown") {
288
+ lines.push(`- **Language:** ${info.language}`);
289
+ }
290
+ if (info.packageManager !== void 0) {
291
+ lines.push(`- **Package manager:** ${info.packageManager}`);
292
+ }
293
+ return lines.join("\n");
294
+ }
295
+ function buildToolsSection(descriptions) {
296
+ if (descriptions.length === 0) {
297
+ return "";
298
+ }
299
+ const lines = ["## Available Tools", ...descriptions.map((d) => `- ${d}`)];
300
+ return lines.join("\n");
301
+ }
302
+ function buildSystemPrompt(params) {
303
+ const { agentsMd, claudeMd, toolDescriptions, trustLevel, projectInfo } = params;
304
+ const sections = [];
305
+ sections.push(
306
+ [
307
+ "## Role",
308
+ "You are an AI coding assistant with access to tools that let you read and modify code.",
309
+ "You help developers understand, write, and improve their codebase.",
310
+ "Always be precise, follow existing code conventions, and prefer minimal changes."
311
+ ].join("\n")
312
+ );
313
+ sections.push(buildProjectSection(projectInfo));
314
+ sections.push(
315
+ [
316
+ "## Permission Mode",
317
+ `Your current trust level is **${TRUST_LEVEL_DESCRIPTIONS[trustLevel]}**.`
318
+ ].join("\n")
319
+ );
320
+ if (agentsMd.trim().length > 0) {
321
+ sections.push(["## Agent Instructions", agentsMd].join("\n"));
322
+ }
323
+ if (claudeMd.trim().length > 0) {
324
+ sections.push(["## Project Notes", claudeMd].join("\n"));
325
+ }
326
+ sections.push(
327
+ [
328
+ "## Web Search",
329
+ "You have access to web search. When the user asks to search, look up, or find current/latest information,",
330
+ "you MUST use the web_search tool. Do NOT answer from training data when the user explicitly asks to search.",
331
+ "Always prefer web search for: news, latest versions, current events, live documentation."
332
+ ].join("\n")
333
+ );
334
+ const toolsSection = buildToolsSection(toolDescriptions);
335
+ if (toolsSection.length > 0) {
336
+ sections.push(toolsSection);
337
+ }
338
+ return sections.join("\n\n");
339
+ }
340
+
341
+ // src/query.ts
342
+ import { Session as Session2 } from "@robota-sdk/agent-sessions";
343
+
344
+ // src/permissions/permission-prompt.ts
345
+ import chalk from "chalk";
346
+ var PERMISSION_OPTIONS = ["Allow", "Deny"];
347
+ var ALLOW_INDEX = 0;
348
+ function formatArgs(toolArgs) {
349
+ const entries = Object.entries(toolArgs);
350
+ if (entries.length === 0) {
351
+ return "(no arguments)";
352
+ }
353
+ return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
354
+ }
355
+ async function promptForApproval(terminal, toolName, toolArgs) {
356
+ terminal.writeLine("");
357
+ terminal.writeLine(chalk.yellow(`[Permission Required] Tool: ${toolName}`));
358
+ terminal.writeLine(chalk.dim(` ${formatArgs(toolArgs)}`));
359
+ terminal.writeLine("");
360
+ const selected = await terminal.select(PERMISSION_OPTIONS, ALLOW_INDEX);
361
+ return selected === ALLOW_INDEX;
362
+ }
363
+
364
+ // src/query.ts
365
+ async function query(prompt, options) {
366
+ const cwd = options?.cwd ?? process.cwd();
367
+ const [config, context, projectInfo] = await Promise.all([
368
+ loadConfig(cwd),
369
+ loadContext(cwd),
370
+ detectProject(cwd)
371
+ ]);
372
+ const noopTerminal = {
373
+ write: () => {
374
+ },
375
+ writeLine: () => {
376
+ },
377
+ writeMarkdown: () => {
378
+ },
379
+ writeError: () => {
380
+ },
381
+ prompt: () => Promise.resolve(""),
382
+ select: () => Promise.resolve(0),
383
+ spinner: () => ({ stop: () => {
384
+ }, update: () => {
385
+ } })
386
+ };
387
+ const session = new Session2({
388
+ config,
389
+ context,
390
+ terminal: noopTerminal,
391
+ projectInfo,
392
+ permissionMode: options?.permissionMode ?? "bypassPermissions",
393
+ maxTurns: options?.maxTurns,
394
+ provider: options?.provider,
395
+ permissionHandler: options?.permissionHandler,
396
+ onTextDelta: options?.onTextDelta,
397
+ onCompact: options?.onCompact,
398
+ compactInstructions: context.compactInstructions,
399
+ systemPromptBuilder: buildSystemPrompt,
400
+ promptForApproval
401
+ });
402
+ return session.run(prompt);
403
+ }
404
+
405
+ // src/permissions/permission-gate.ts
406
+ import { evaluatePermission } from "@robota-sdk/agent-core";
407
+
408
+ // src/hooks/hook-runner.ts
409
+ import { runHooks } from "@robota-sdk/agent-core";
410
+
411
+ // src/tools/bash-tool.ts
412
+ import { bashTool } from "@robota-sdk/agent-tools";
413
+
414
+ // src/tools/read-tool.ts
415
+ import { readTool } from "@robota-sdk/agent-tools";
416
+
417
+ // src/tools/write-tool.ts
418
+ import { writeTool } from "@robota-sdk/agent-tools";
419
+
420
+ // src/tools/edit-tool.ts
421
+ import { editTool } from "@robota-sdk/agent-tools";
422
+
423
+ // src/tools/glob-tool.ts
424
+ import { globTool } from "@robota-sdk/agent-tools";
425
+
426
+ // src/tools/grep-tool.ts
427
+ import { grepTool } from "@robota-sdk/agent-tools";
428
+
429
+ // src/tools/agent-tool.ts
430
+ import { z as z2 } from "zod";
431
+ import { createZodFunctionTool } from "@robota-sdk/agent-tools";
432
+ import { Session as Session3 } from "@robota-sdk/agent-sessions";
433
+ function asZodSchema(schema) {
434
+ return schema;
435
+ }
436
+ var AgentSchema = z2.object({
437
+ prompt: z2.string().describe("Task description for the sub-agent"),
438
+ description: z2.string().optional().describe("Short description of what the sub-agent will do (3-5 words)")
439
+ });
440
+ var agentToolDeps;
441
+ function setAgentToolDeps(deps) {
442
+ agentToolDeps = deps;
443
+ }
444
+ async function runAgent(args) {
445
+ if (!agentToolDeps) {
446
+ const result = {
447
+ success: false,
448
+ output: "",
449
+ error: "Agent tool not initialized \u2014 missing dependencies"
450
+ };
451
+ return JSON.stringify(result);
452
+ }
453
+ const subSession = new Session3({
454
+ config: agentToolDeps.config,
455
+ context: agentToolDeps.context,
456
+ projectInfo: agentToolDeps.projectInfo,
457
+ // No terminal needed — sub-agents don't prompt for permissions
458
+ terminal: {
459
+ write: () => {
460
+ },
461
+ writeLine: () => {
462
+ },
463
+ writeMarkdown: () => {
464
+ },
465
+ writeError: () => {
466
+ },
467
+ prompt: () => Promise.resolve(""),
468
+ select: () => Promise.resolve(0),
469
+ spinner: () => ({ stop: () => {
470
+ }, update: () => {
471
+ } })
472
+ },
473
+ // Sub-agents bypass permissions — they inherit parent's trust
474
+ permissionMode: "bypassPermissions"
475
+ });
476
+ try {
477
+ const response = await subSession.run(args.prompt);
478
+ const result = {
479
+ success: true,
480
+ output: response
481
+ };
482
+ return JSON.stringify(result);
483
+ } catch (err) {
484
+ const message = err instanceof Error ? err.message : String(err);
485
+ const result = {
486
+ success: false,
487
+ output: "",
488
+ error: `Sub-agent error: ${message}`
489
+ };
490
+ return JSON.stringify(result);
491
+ }
492
+ }
493
+ var agentTool = createZodFunctionTool(
494
+ "Agent",
495
+ "Spawn a sub-agent with isolated context to handle a task. The sub-agent has its own conversation history and can use all tools.",
496
+ asZodSchema(AgentSchema),
497
+ async (params) => {
498
+ return runAgent(params);
499
+ }
500
+ );
501
+ export {
502
+ Session,
503
+ SessionStore,
504
+ TRUST_TO_MODE,
505
+ agentTool,
506
+ bashTool,
507
+ buildSystemPrompt,
508
+ detectProject,
509
+ editTool,
510
+ evaluatePermission,
511
+ globTool,
512
+ grepTool,
513
+ loadConfig,
514
+ loadContext,
515
+ promptForApproval,
516
+ query,
517
+ readTool,
518
+ runHooks,
519
+ setAgentToolDeps,
520
+ writeTool
521
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@robota-sdk/agent-sdk",
3
+ "version": "3.0.0-beta.1",
4
+ "description": "Programmatic SDK for building AI agents with Robota — provides Session, query(), built-in tools, permissions, hooks, and context loading",
5
+ "type": "module",
6
+ "main": "dist/node/index.js",
7
+ "types": "dist/node/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/node/index.d.ts",
11
+ "node": {
12
+ "import": "./dist/node/index.js",
13
+ "require": "./dist/node/index.cjs"
14
+ },
15
+ "default": {
16
+ "import": "./dist/node/index.js",
17
+ "require": "./dist/node/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "dependencies": {
25
+ "chalk": "^5.3.0",
26
+ "zod": "^3.24.0",
27
+ "@robota-sdk/agent-provider-anthropic": "3.0.0-beta.1",
28
+ "@robota-sdk/agent-core": "3.0.0-beta.1",
29
+ "@robota-sdk/agent-tools": "3.0.0-beta.1",
30
+ "@robota-sdk/agent-sessions": "3.0.0-beta.1"
31
+ },
32
+ "devDependencies": {
33
+ "rimraf": "^5.0.5",
34
+ "tsup": "^8.0.1",
35
+ "typescript": "^5.3.3",
36
+ "vitest": "^1.6.1"
37
+ },
38
+ "license": "MIT",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "build": "tsup src/index.ts --format esm,cjs --dts --out-dir dist/node --clean",
44
+ "test": "vitest run --passWithNoTests",
45
+ "typecheck": "tsc --noEmit",
46
+ "lint": "eslint src/ --ext .ts",
47
+ "clean": "rimraf dist"
48
+ }
49
+ }