@lite-agent/sdk 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ModelProvider, Tool, Middleware, Sandbox, PermissionPolicy, ApprovalHandler, InputHandler, Agent, Message, AgentEvent, RunResult, ToolContext } from '@lite-agent/core';
1
+ import { ModelProvider, Tool, Middleware, Sandbox, Store, Compactor, PermissionPolicy, ApprovalHandler, InputHandler, Agent, Message, AgentEvent, RunResult, ToolContext, SpillStore } from '@lite-agent/core';
2
2
  export * from '@lite-agent/core';
3
3
  import { ZodType } from 'zod';
4
4
 
@@ -15,6 +15,21 @@ interface CreateLiteAgentConfig {
15
15
  maxTokens?: number;
16
16
  use?: Middleware[];
17
17
  sandbox?: Sandbox;
18
+ store?: Store;
19
+ /** Override the global home (default `$LITE_AGENT_HOME` || `~/.lite-agent`). */
20
+ home?: string;
21
+ /** Persist transcripts under the project's sessions dir. Default true. Ignored when `store` is set. */
22
+ sessions?: boolean;
23
+ /** Spill oversized tool_results to disk + register `read_spilled`. Default true. */
24
+ spill?: boolean | {
25
+ budgetBytes?: number;
26
+ };
27
+ /** Proactive compactor. Default deterministic `defaultCompactor`; `false` disables compaction. */
28
+ compactor?: Compactor | false;
29
+ /** Sweep stale spill/session files once at startup. Default true (30 days). */
30
+ cleanup?: boolean | {
31
+ maxAgeDays?: number;
32
+ };
18
33
  permission?: PermissionPolicy;
19
34
  onApproval?: ApprovalHandler;
20
35
  onAskUser?: InputHandler;
@@ -37,6 +52,16 @@ interface QueryOptions {
37
52
  signal?: AbortSignal;
38
53
  sessionId?: string;
39
54
  sandbox?: Sandbox;
55
+ store?: Store;
56
+ compactor?: Compactor | false;
57
+ home?: string;
58
+ sessions?: boolean;
59
+ spill?: boolean | {
60
+ budgetBytes?: number;
61
+ };
62
+ cleanup?: boolean | {
63
+ maxAgeDays?: number;
64
+ };
40
65
  permission?: PermissionPolicy;
41
66
  onApproval?: ApprovalHandler;
42
67
  onAskUser?: InputHandler;
@@ -64,15 +89,58 @@ declare function askUserTool(): Tool;
64
89
  declare function defaultTools(workdir: string): Tool[];
65
90
 
66
91
  declare class SkillLoader {
67
- readonly skillsDir: string;
92
+ readonly dirs: string[];
68
93
  private skills;
69
- constructor(skillsDir: string);
94
+ constructor(dirs: string | string[]);
70
95
  private loadAll;
71
96
  private parse;
72
97
  getDescriptions(): string;
98
+ names(): string[];
73
99
  getContent(name: string): string;
74
100
  }
75
101
 
76
102
  declare function loadSkillTool(loader: SkillLoader): Tool;
77
103
 
78
- export { type CreateLiteAgentConfig, type QueryOptions, SkillLoader, type SystemPromptOptions, askUserTool, bashTool, buildSystemPrompt, createLiteAgent, defaultTools, fileTools, loadSkillTool, makeSafePath, query, todoTool, tool };
104
+ interface JsonlStoreOptions {
105
+ /** Directory holding one `<sessionId>.jsonl` transcript per session. */
106
+ dir: string;
107
+ }
108
+ declare function jsonlStore(opts: JsonlStoreOptions): Store;
109
+
110
+ /** Global home: `$LITE_AGENT_HOME` if set, else `~/.lite-agent`. */
111
+ declare function liteAgentHome(): string;
112
+ /** Stable per absolute project path: first 16 hex of sha1(resolve(workdir)). */
113
+ declare function projectHash(workdir: string): string;
114
+ interface ProjectPaths {
115
+ home: string;
116
+ hash: string;
117
+ spillDir: string;
118
+ sessionsDir: string;
119
+ globalSkillsDir: string;
120
+ projectSkillsDir: string;
121
+ }
122
+ /** Pure: derive every path from `workdir` (+ optional home). No fs side effects. */
123
+ declare function resolveProjectPaths(opts: {
124
+ workdir: string;
125
+ home?: string;
126
+ }): ProjectPaths;
127
+
128
+ /**
129
+ * Delete stale runtime files under <home>/projects/HASH/spill and
130
+ * <home>/projects/HASH/sessions whose mtime is older than maxAgeDays
131
+ * (default 30). Global sweep, synchronous, fully guarded -- a failure
132
+ * here must never block agent startup.
133
+ */
134
+ declare function sweepStale(opts?: {
135
+ home?: string;
136
+ maxAgeDays?: number;
137
+ }): void;
138
+
139
+ interface FileSpillStoreOptions {
140
+ /** Directory holding one `<ref>.txt` blob per spilled tool result. */
141
+ dir: string;
142
+ }
143
+ declare function fileSpillStore(opts: FileSpillStoreOptions): SpillStore;
144
+ declare function readSpilledTool(store: SpillStore): Tool;
145
+
146
+ export { type CreateLiteAgentConfig, type FileSpillStoreOptions, type JsonlStoreOptions, type ProjectPaths, type QueryOptions, SkillLoader, type SystemPromptOptions, askUserTool, bashTool, buildSystemPrompt, createLiteAgent, defaultTools, fileSpillStore, fileTools, jsonlStore, liteAgentHome, loadSkillTool, makeSafePath, projectHash, query, readSpilledTool, resolveProjectPaths, sweepStale, todoTool, tool };
package/dist/index.js CHANGED
@@ -2,7 +2,14 @@
2
2
  export * from "@lite-agent/core";
3
3
 
4
4
  // src/createLiteAgent.ts
5
- import { createAgent, nativeCodec, permission } from "@lite-agent/core";
5
+ import {
6
+ createAgent,
7
+ nativeCodec,
8
+ permission,
9
+ compaction,
10
+ reactiveCompaction,
11
+ defaultCompactor
12
+ } from "@lite-agent/core";
6
13
 
7
14
  // src/tools/bash.ts
8
15
  import { execSync } from "child_process";
@@ -188,15 +195,15 @@ function defaultTools(workdir) {
188
195
  // src/skills/loader.ts
189
196
  import { existsSync, readdirSync, readFileSync as readFileSync2 } from "fs";
190
197
  import { dirname as dirname2, join } from "path";
198
+ import matter from "gray-matter";
191
199
  var SkillLoader = class {
192
- skillsDir;
200
+ dirs;
193
201
  skills = {};
194
- constructor(skillsDir) {
195
- this.skillsDir = skillsDir;
202
+ constructor(dirs) {
203
+ this.dirs = Array.isArray(dirs) ? dirs : [dirs];
196
204
  this.loadAll();
197
205
  }
198
206
  loadAll() {
199
- if (!existsSync(this.skillsDir)) return;
200
207
  const walk = (dir) => {
201
208
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
202
209
  const p = join(dir, entry.name);
@@ -208,27 +215,27 @@ var SkillLoader = class {
208
215
  }
209
216
  }
210
217
  };
211
- walk(this.skillsDir);
218
+ for (const dir of this.dirs) {
219
+ if (existsSync(dir)) walk(dir);
220
+ }
212
221
  }
213
222
  parse(text) {
214
- const m = text.match(/^---\n(.*?)\n---\n(.*)/s);
215
- if (!m) return { meta: {}, body: text };
216
- const meta = {};
217
- for (const line of m[1].trim().split("\n")) {
218
- const i = line.indexOf(":");
219
- if (i > 0) meta[line.slice(0, i).trim()] = line.slice(i + 1).trim();
220
- }
221
- return { meta, body: m[2].trim() };
223
+ const { data, content } = matter(text);
224
+ return { meta: data, body: content.trim() };
222
225
  }
223
226
  getDescriptions() {
224
227
  const names = Object.keys(this.skills);
225
228
  if (!names.length) return "(no skills available)";
226
229
  return names.map((n) => {
227
230
  const s = this.skills[n];
228
- const tags = s.meta.tags ? ` [${s.meta.tags}]` : "";
231
+ const tagList = Array.isArray(s.meta.tags) ? s.meta.tags.join(", ") : s.meta.tags;
232
+ const tags = tagList ? ` [${tagList}]` : "";
229
233
  return ` - ${n}: ${s.meta.description ?? "No description"}${tags}`;
230
234
  }).join("\n");
231
235
  }
236
+ names() {
237
+ return Object.keys(this.skills);
238
+ }
232
239
  getContent(name) {
233
240
  const s = this.skills[name];
234
241
  if (!s) return `Error: Unknown skill '${name}'. Available: ${Object.keys(this.skills).join(", ")}`;
@@ -270,27 +277,144 @@ Available skills:
270
277
  ${opts.skills}`;
271
278
  }
272
279
 
280
+ // src/paths.ts
281
+ import { homedir } from "os";
282
+ import { resolve as resolve2, join as join2 } from "path";
283
+ import { createHash } from "crypto";
284
+ function liteAgentHome() {
285
+ return process.env.LITE_AGENT_HOME || join2(homedir(), ".lite-agent");
286
+ }
287
+ function projectHash(workdir) {
288
+ return createHash("sha1").update(resolve2(workdir)).digest("hex").slice(0, 16);
289
+ }
290
+ function resolveProjectPaths(opts) {
291
+ const home = opts.home ?? liteAgentHome();
292
+ const hash = projectHash(opts.workdir);
293
+ const projectDir = join2(home, "projects", hash);
294
+ return {
295
+ home,
296
+ hash,
297
+ spillDir: join2(projectDir, "spill"),
298
+ sessionsDir: join2(projectDir, "sessions"),
299
+ globalSkillsDir: join2(home, "skills"),
300
+ projectSkillsDir: join2(resolve2(opts.workdir), ".lite-agent", "skills")
301
+ };
302
+ }
303
+
304
+ // src/store.ts
305
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
306
+ import { join as join3 } from "path";
307
+ function jsonlStore(opts) {
308
+ const fileFor = (id) => join3(opts.dir, `${id.replace(/[^a-zA-Z0-9_-]/g, "_")}.jsonl`);
309
+ return {
310
+ async load(id) {
311
+ const file = fileFor(id);
312
+ if (!existsSync2(file)) return null;
313
+ return readFileSync3(file, "utf8").split("\n").filter((line) => line.trim() !== "").map((line) => JSON.parse(line));
314
+ },
315
+ async save(id, messages) {
316
+ mkdirSync2(opts.dir, { recursive: true });
317
+ const body = messages.map((m) => JSON.stringify(m)).join("\n");
318
+ writeFileSync2(fileFor(id), messages.length ? body + "\n" : "");
319
+ }
320
+ };
321
+ }
322
+
323
+ // src/spill.ts
324
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
325
+ import { createHash as createHash2 } from "crypto";
326
+ import { join as join4 } from "path";
327
+ import { z as z6 } from "zod";
328
+ import { defineTool as defineTool6 } from "@lite-agent/core";
329
+ function fileSpillStore(opts) {
330
+ const fileFor = (ref) => join4(opts.dir, `${ref.replace(/[^a-f0-9]/gi, "")}.txt`);
331
+ return {
332
+ put(content) {
333
+ mkdirSync3(opts.dir, { recursive: true });
334
+ const ref = createHash2("sha1").update(content).digest("hex").slice(0, 16);
335
+ writeFileSync3(fileFor(ref), content);
336
+ return ref;
337
+ },
338
+ get(ref) {
339
+ const file = fileFor(ref);
340
+ return existsSync3(file) ? readFileSync4(file, "utf8") : null;
341
+ }
342
+ };
343
+ }
344
+ function readSpilledTool(store) {
345
+ return defineTool6({
346
+ name: "read_spilled",
347
+ description: "Retrieve the full content of a tool result that was moved off-context to save space. Pass the ref shown in its [spilled:<ref>] marker.",
348
+ schema: z6.object({ ref: z6.string() }),
349
+ execute: ({ ref }) => store.get(ref) ?? `No spilled content for ref '${ref}'`
350
+ });
351
+ }
352
+
353
+ // src/cleanup.ts
354
+ import { existsSync as existsSync4, readdirSync as readdirSync2, statSync, rmSync } from "fs";
355
+ import { join as join5 } from "path";
356
+ var DAY_MS = 864e5;
357
+ function sweepStale(opts = {}) {
358
+ const home = opts.home ?? liteAgentHome();
359
+ const cutoff = Date.now() - (opts.maxAgeDays ?? 30) * DAY_MS;
360
+ try {
361
+ const projectsDir = join5(home, "projects");
362
+ if (!existsSync4(projectsDir)) return;
363
+ for (const project of readdirSync2(projectsDir)) {
364
+ for (const sub of ["spill", "sessions"]) {
365
+ const dir = join5(projectsDir, project, sub);
366
+ if (!existsSync4(dir)) continue;
367
+ for (const name of readdirSync2(dir)) {
368
+ const fp = join5(dir, name);
369
+ try {
370
+ if (statSync(fp).mtimeMs < cutoff) rmSync(fp);
371
+ } catch {
372
+ }
373
+ }
374
+ }
375
+ }
376
+ } catch {
377
+ }
378
+ }
379
+
273
380
  // src/createLiteAgent.ts
274
381
  function createLiteAgent(cfg) {
382
+ const paths = resolveProjectPaths({ workdir: cfg.workdir, home: cfg.home });
383
+ if (cfg.cleanup !== false) {
384
+ sweepStale({
385
+ home: paths.home,
386
+ maxAgeDays: typeof cfg.cleanup === "object" ? cfg.cleanup.maxAgeDays : void 0
387
+ });
388
+ }
275
389
  let tools = [...defaultTools(cfg.workdir)];
390
+ const loader = new SkillLoader([
391
+ paths.globalSkillsDir,
392
+ paths.projectSkillsDir,
393
+ ...cfg.skillsDir ? [cfg.skillsDir] : []
394
+ ]);
276
395
  let skills = "(no skills available)";
277
- if (cfg.skillsDir) {
278
- const loader = new SkillLoader(cfg.skillsDir);
396
+ if (loader.names().length > 0) {
279
397
  tools.push(loadSkillTool(loader));
280
398
  skills = loader.getDescriptions();
281
399
  }
400
+ const spillEnabled = cfg.spill !== false;
401
+ const spillStore = spillEnabled ? fileSpillStore({ dir: paths.spillDir }) : void 0;
402
+ if (spillStore) tools.push(readSpilledTool(spillStore));
282
403
  if (cfg.tools) tools.push(...cfg.tools);
283
404
  if (cfg.onAskUser) tools.push(askUserTool());
284
405
  if (cfg.allowedTools)
285
406
  tools = tools.filter((t) => cfg.allowedTools.includes(t.name));
286
407
  if (cfg.disallowedTools)
287
408
  tools = tools.filter((t) => !cfg.disallowedTools.includes(t.name));
288
- const system = cfg.system ?? buildSystemPrompt({
289
- workdir: cfg.workdir,
290
- modelName: cfg.modelName,
291
- skills
409
+ const system = cfg.system ?? buildSystemPrompt({ workdir: cfg.workdir, modelName: cfg.modelName, skills });
410
+ const compactor = cfg.compactor === false ? void 0 : cfg.compactor ?? defaultCompactor({
411
+ spillStore,
412
+ budgetBytes: typeof cfg.spill === "object" ? cfg.spill.budgetBytes : void 0
292
413
  });
414
+ const store = cfg.store ?? (cfg.sessions === false ? void 0 : jsonlStore({ dir: paths.sessionsDir }));
293
415
  const use = [
416
+ // proactive compaction (beforeModel) + reactive overflow net (wrapModelCall)
417
+ ...compactor ? [compaction(compactor), reactiveCompaction()] : [],
294
418
  ...cfg.permission ? [permission(cfg.permission, cfg.onApproval)] : [],
295
419
  ...cfg.use ?? []
296
420
  ];
@@ -304,6 +428,7 @@ function createLiteAgent(cfg) {
304
428
  maxTurns: cfg.maxTurns,
305
429
  maxTokens: cfg.maxTokens,
306
430
  sandbox: cfg.sandbox,
431
+ store,
307
432
  input: cfg.onAskUser
308
433
  });
309
434
  }
@@ -323,6 +448,12 @@ function query(opts) {
323
448
  maxTokens: opts.maxTokens,
324
449
  use: opts.use,
325
450
  sandbox: opts.sandbox,
451
+ store: opts.store,
452
+ compactor: opts.compactor,
453
+ home: opts.home,
454
+ sessions: opts.sessions,
455
+ spill: opts.spill,
456
+ cleanup: opts.cleanup,
326
457
  permission: opts.permission,
327
458
  onApproval: opts.onApproval,
328
459
  onAskUser: opts.onAskUser
@@ -334,9 +465,9 @@ function query(opts) {
334
465
  }
335
466
 
336
467
  // src/tool.ts
337
- import { defineTool as defineTool6 } from "@lite-agent/core";
468
+ import { defineTool as defineTool7 } from "@lite-agent/core";
338
469
  function tool(name, description, schema, handler) {
339
- return defineTool6({ name, description, schema, execute: handler });
470
+ return defineTool7({ name, description, schema, execute: handler });
340
471
  }
341
472
  export {
342
473
  SkillLoader,
@@ -345,10 +476,17 @@ export {
345
476
  buildSystemPrompt,
346
477
  createLiteAgent,
347
478
  defaultTools,
479
+ fileSpillStore,
348
480
  fileTools,
481
+ jsonlStore,
482
+ liteAgentHome,
349
483
  loadSkillTool,
350
484
  makeSafePath,
485
+ projectHash,
351
486
  query,
487
+ readSpilledTool,
488
+ resolveProjectPaths,
489
+ sweepStale,
352
490
  todoTool,
353
491
  tool
354
492
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lite-agent/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Batteries-included agent SDK over @lite-agent/core: tools, skills, system prompt, and query()/createLiteAgent().",
5
5
  "license": "ISC",
6
6
  "type": "module",
@@ -34,19 +34,20 @@
34
34
  "publishConfig": {
35
35
  "access": "public"
36
36
  },
37
- "scripts": {
38
- "build": "tsup src/index.ts --format esm --dts --clean --tsconfig tsconfig.build.json",
39
- "test": "vitest run",
40
- "typecheck": "tsc --noEmit"
41
- },
42
37
  "dependencies": {
43
- "@lite-agent/core": "workspace:*",
44
- "zod": "^4.3.6"
38
+ "gray-matter": "^4.0.3",
39
+ "zod": "^4.3.6",
40
+ "@lite-agent/core": "0.2.0"
45
41
  },
46
42
  "devDependencies": {
47
43
  "@types/node": "^25.5.0",
48
44
  "tsup": "^8.3.0",
49
45
  "typescript": "^6.0.2",
50
46
  "vitest": "^2.1.0"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup src/index.ts --format esm --dts --clean --tsconfig tsconfig.build.json",
50
+ "test": "vitest run",
51
+ "typecheck": "tsc --noEmit"
51
52
  }
52
- }
53
+ }