@lotics/cli 0.49.0 → 0.50.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.
@@ -26,6 +26,22 @@ export type AppQueryDeclaration = {
26
26
  ast: unknown;
27
27
  params?: Record<string, unknown>;
28
28
  };
29
+ /**
30
+ * Manifest declaration for one streaming agent:
31
+ * `"alias": { instructions, tool_names, model_id, inputs?, outputs? }`
32
+ *
33
+ * A read-only reflection of the live App row (`apps.agents`, owned by
34
+ * `set_app_agent`), refreshed by `lotics app pull` and used only to codegen
35
+ * `useAgentRun` typings. `inputs`/`outputs` drive the typed call site; the rest
36
+ * is carried for fidelity.
37
+ */
38
+ export type AppAgentDeclaration = {
39
+ instructions?: string;
40
+ tool_names?: string[];
41
+ model_id?: string;
42
+ inputs?: Record<string, unknown>;
43
+ outputs?: Record<string, unknown>;
44
+ };
29
45
  /**
30
46
  * Stamp the post-extraction manifest with server-authoritative meta. Called
31
47
  * by `appPull` after the source archive lands on disk.
@@ -45,6 +61,7 @@ export declare function stampPulledManifest(projectDir: string, args: {
45
61
  version_number: number;
46
62
  workflows: Record<string, AppWorkflowDeclaration>;
47
63
  queries: Record<string, AppQueryDeclaration>;
64
+ agents: Record<string, AppAgentDeclaration>;
48
65
  }): void;
49
66
  /**
50
67
  * `lotics app create <name> [path]`
@@ -18,6 +18,7 @@ import { tmpdir } from "node:os";
18
18
  import { buildStarterTemplate } from "./starter_template.js";
19
19
  import { startDevServer, openBrowser } from "./dev/server.js";
20
20
  import { generateAppWorkflowsDts } from "./generate_app_workflows_dts.js";
21
+ import { generateAppAgentsDts } from "./generate_app_agents_dts.js";
21
22
  import { generateAppQueriesDts } from "./generate_app_queries_dts.js";
22
23
  /**
23
24
  * Resolve the latest published version of a package from the npm registry.
@@ -92,6 +93,7 @@ function readAppMeta(projectDir) {
92
93
  version_number: pkg.lotics.version_number ?? null,
93
94
  workflows: pkg.lotics.workflows ?? {},
94
95
  queries: pkg.lotics.queries ?? {},
96
+ agents: pkg.lotics.agents ?? {},
95
97
  capabilities: pkg.lotics.capabilities,
96
98
  };
97
99
  }
@@ -113,6 +115,7 @@ function writeAppDts(projectDir, manifest) {
113
115
  fs.mkdirSync(dotLotics, { recursive: true });
114
116
  fs.writeFileSync(path.join(dotLotics, "app_workflows.d.ts"), generateAppWorkflowsDts(manifest.workflows));
115
117
  fs.writeFileSync(path.join(dotLotics, "app_queries.d.ts"), generateAppQueriesDts(manifest.queries));
118
+ fs.writeFileSync(path.join(dotLotics, "app_agents.d.ts"), generateAppAgentsDts(manifest.agents));
116
119
  }
117
120
  /**
118
121
  * Stamp the post-extraction manifest with server-authoritative meta. Called
@@ -134,8 +137,9 @@ export function stampPulledManifest(projectDir, args) {
134
137
  version_number: args.version_number,
135
138
  workflows: args.workflows,
136
139
  queries: args.queries,
140
+ agents: args.agents,
137
141
  });
138
- writeAppDts(projectDir, { workflows: args.workflows, queries: args.queries });
142
+ writeAppDts(projectDir, { workflows: args.workflows, queries: args.queries, agents: args.agents });
139
143
  }
140
144
  async function downloadToFile(url, destPath) {
141
145
  const response = await fetch(url);
@@ -281,6 +285,7 @@ export async function appPull(client, args) {
281
285
  version_number: version.version,
282
286
  workflows: app.workflows ?? {},
283
287
  queries: app.queries ?? {},
288
+ agents: app.agents ?? {},
284
289
  });
285
290
  console.error(`Installing npm dependencies...`);
286
291
  await runNpm(["install"], targetPath);
@@ -355,7 +360,7 @@ export async function appDeploy(client, args) {
355
360
  // Regenerate AppWorkflows typing before the build picks up source. Keeps
356
361
  // .lotics/app_workflows.d.ts in sync with the manifest's workflows map
357
362
  // every time the developer ships.
358
- writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries });
363
+ writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries, agents: meta.agents });
359
364
  // Build locally so the server doesn't need a build sandbox in v1.
360
365
  console.error("Building...");
361
366
  await runNpm(["run", "build"], projectDir);
@@ -469,7 +474,7 @@ export async function appDev(client, args) {
469
474
  // Regenerate AppWorkflows typing before Vite spins up so the dev typecheck
470
475
  // sees the current shape. Doesn't watch for manifest changes mid-session —
471
476
  // re-running `lotics app dev` after editing the manifest is the loop.
472
- writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries });
477
+ writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries, agents: meta.agents });
473
478
  // Sanity: confirm the app exists in the workspace the CLI is auth'd into.
474
479
  // Surfaces a clear error if the project's app_id has been deleted or the
475
480
  // CLI is pointed at the wrong workspace.
@@ -57,6 +57,7 @@ describe("stampPulledManifest", () => {
57
57
  renamed: { workflow_id: "wfl_renamed_on_server" },
58
58
  },
59
59
  queries: {},
60
+ agents: {},
60
61
  });
61
62
  const stamped = readStampedManifest();
62
63
  expect(stamped.app_id).toBe("app_live");
@@ -82,6 +83,7 @@ describe("stampPulledManifest", () => {
82
83
  version_number: 2,
83
84
  workflows: {},
84
85
  queries: {},
86
+ agents: {},
85
87
  });
86
88
  const stamped = readStampedManifest();
87
89
  expect(stamped.workflows).toEqual({});
@@ -97,6 +99,7 @@ describe("stampPulledManifest", () => {
97
99
  only_live: { workflow_id: "wfl_only_live" },
98
100
  },
99
101
  queries: {},
102
+ agents: {},
100
103
  });
101
104
  const dtsPath = path.join(workDir, ".lotics", "app_workflows.d.ts");
102
105
  expect(fs.existsSync(dtsPath)).toBe(true);
package/dist/client.d.ts CHANGED
@@ -130,6 +130,19 @@ export declare class LoticsClient {
130
130
  ast: unknown;
131
131
  params?: Record<string, unknown>;
132
132
  }> | null;
133
+ /**
134
+ * Live alias → agent declaration map from `apps.agents`. Source of truth for
135
+ * `lotics app pull` — supersedes the source archive so `set_app_agent`
136
+ * authoring survives the pull. Null/undefined on apps with no declared
137
+ * agents. Drives `useAgentRun` typing.
138
+ */
139
+ agents?: Record<string, {
140
+ instructions?: string;
141
+ tool_names?: string[];
142
+ model_id?: string;
143
+ inputs?: Record<string, unknown>;
144
+ outputs?: Record<string, unknown>;
145
+ }> | null;
133
146
  }>;
134
147
  createApp(body: {
135
148
  name: string;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Codegen: emit `.lotics/app_agents.d.ts` from the app's manifest.
3
+ *
4
+ * Mirrors `generate_app_workflows_dts` — agents reuse the exact same typed
5
+ * input/output vocabulary, so the `inputsToType` / `objectFieldsToType` mappers
6
+ * are shared. The augmentation adds a typed entry to `AppAgents` for every alias
7
+ * declared in `package.json#lotics.agents`:
8
+ *
9
+ * - No declared `inputs` → `alias: Record<string, unknown>` (untyped payload)
10
+ * - Declared `inputs` → `alias: { …declared shape… }`
11
+ * - Declared `outputs` → an `AppAgentResults[alias]` entry → typed `output`
12
+ *
13
+ * Pure function. Same inputs → same output. Idempotent.
14
+ */
15
+ import type { AppAgentDeclaration } from "./app_commands.js";
16
+ export declare function generateAppAgentsDts(agents: Record<string, AppAgentDeclaration> | undefined): string;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Codegen: emit `.lotics/app_agents.d.ts` from the app's manifest.
3
+ *
4
+ * Mirrors `generate_app_workflows_dts` — agents reuse the exact same typed
5
+ * input/output vocabulary, so the `inputsToType` / `objectFieldsToType` mappers
6
+ * are shared. The augmentation adds a typed entry to `AppAgents` for every alias
7
+ * declared in `package.json#lotics.agents`:
8
+ *
9
+ * - No declared `inputs` → `alias: Record<string, unknown>` (untyped payload)
10
+ * - Declared `inputs` → `alias: { …declared shape… }`
11
+ * - Declared `outputs` → an `AppAgentResults[alias]` entry → typed `output`
12
+ *
13
+ * Pure function. Same inputs → same output. Idempotent.
14
+ */
15
+ import { inputsToType, objectFieldsToType, isValidIdentifier } from "./generate_app_workflows_dts.js";
16
+ const HEADER = `// Auto-generated by 'lotics app pull/dev/deploy'.
17
+ // DO NOT EDIT — regenerated from package.json#lotics.agents.
18
+ //
19
+ // This file gives \`useAgentRun("alias")\` typed input + output at call sites by
20
+ // augmenting the @lotics/app-sdk \`AppAgents\` / \`AppAgentResults\` interfaces.
21
+
22
+ import "@lotics/app-sdk";
23
+ `;
24
+ export function generateAppAgentsDts(agents) {
25
+ const entries = Object.entries(agents ?? {});
26
+ if (entries.length === 0) {
27
+ return `${HEADER}
28
+ // No agents declared in package.json#lotics.agents.
29
+ // Add an entry to enable typed useAgentRun<"alias"> at call sites.
30
+ declare module "@lotics/app-sdk" {
31
+ interface AppAgents {}
32
+ }
33
+ `;
34
+ }
35
+ // Sort alphabetically for deterministic output across regenerations.
36
+ entries.sort(([a], [b]) => a.localeCompare(b));
37
+ const inputLines = [];
38
+ const resultLines = [];
39
+ for (const [alias, declaration] of entries) {
40
+ const valueType = declaration.inputs
41
+ ? inputsToType(declaration.inputs)
42
+ : "Record<string, unknown>";
43
+ const aliasKey = isValidIdentifier(alias) ? alias : JSON.stringify(alias);
44
+ inputLines.push(` ${aliasKey}: ${valueType};`);
45
+ if (declaration.outputs) {
46
+ resultLines.push(` ${aliasKey}: ${objectFieldsToType(declaration.outputs)};`);
47
+ }
48
+ }
49
+ const resultsBlock = resultLines.length > 0
50
+ ? `
51
+ interface AppAgentResults {
52
+ ${resultLines.join("\n")}
53
+ }`
54
+ : "";
55
+ return `${HEADER}
56
+ declare module "@lotics/app-sdk" {
57
+ interface AppAgents {
58
+ ${inputLines.join("\n")}
59
+ }${resultsBlock}
60
+ }
61
+ `;
62
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { generateAppAgentsDts } from "./generate_app_agents_dts.js";
3
+ describe("generateAppAgentsDts", () => {
4
+ it("emits an empty AppAgents interface when no agents are declared", () => {
5
+ const out = generateAppAgentsDts(undefined);
6
+ expect(out).toContain("interface AppAgents {}");
7
+ expect(out).not.toContain("interface AppAgentResults");
8
+ });
9
+ it("types the input payload from declared inputs", () => {
10
+ const out = generateAppAgentsDts({
11
+ recognize: { inputs: { photo: { type: "file" }, prompt: { type: "text", required: false } } },
12
+ });
13
+ expect(out).toContain("recognize: {");
14
+ expect(out).toContain("photo: string;");
15
+ expect(out).toContain("prompt?: string;");
16
+ });
17
+ it("leaves an inputs-less agent as an untyped payload", () => {
18
+ const out = generateAppAgentsDts({ summarize: {} });
19
+ expect(out).toContain("summarize: Record<string, unknown>;");
20
+ });
21
+ it("emits AppAgentResults from declared outputs, including nested objects", () => {
22
+ const out = generateAppAgentsDts({
23
+ recognize: {
24
+ inputs: { photo: { type: "file" } },
25
+ outputs: {
26
+ style: { type: "text" },
27
+ dimensions: {
28
+ type: "object",
29
+ fields: { width_mm: { type: "number" }, height_mm: { type: "number" } },
30
+ },
31
+ confidence: { type: "number" },
32
+ },
33
+ },
34
+ });
35
+ expect(out).toContain("interface AppAgentResults {");
36
+ expect(out).toContain("style: string");
37
+ expect(out).toContain("width_mm: number");
38
+ expect(out).toContain("confidence: number");
39
+ });
40
+ it("is deterministic and sorts aliases", () => {
41
+ const a = generateAppAgentsDts({ zebra: {}, alpha: {} });
42
+ expect(a.indexOf("alpha:")).toBeLessThan(a.indexOf("zebra:"));
43
+ expect(generateAppAgentsDts({ zebra: {}, alpha: {} })).toBe(a);
44
+ });
45
+ });
package/dist/src/cli.js CHANGED
@@ -31358,8 +31358,52 @@ function isValidIdentifier(name) {
31358
31358
  return IDENTIFIER_REGEX.test(name);
31359
31359
  }
31360
31360
 
31361
- // src/generate_app_queries_dts.ts
31361
+ // src/generate_app_agents_dts.ts
31362
31362
  var HEADER2 = `// Auto-generated by 'lotics app pull/dev/deploy'.
31363
+ // DO NOT EDIT \u2014 regenerated from package.json#lotics.agents.
31364
+ //
31365
+ // This file gives \`useAgentRun("alias")\` typed input + output at call sites by
31366
+ // augmenting the @lotics/app-sdk \`AppAgents\` / \`AppAgentResults\` interfaces.
31367
+
31368
+ import "@lotics/app-sdk";
31369
+ `;
31370
+ function generateAppAgentsDts(agents) {
31371
+ const entries = Object.entries(agents ?? {});
31372
+ if (entries.length === 0) {
31373
+ return `${HEADER2}
31374
+ // No agents declared in package.json#lotics.agents.
31375
+ // Add an entry to enable typed useAgentRun<"alias"> at call sites.
31376
+ declare module "@lotics/app-sdk" {
31377
+ interface AppAgents {}
31378
+ }
31379
+ `;
31380
+ }
31381
+ entries.sort(([a], [b]) => a.localeCompare(b));
31382
+ const inputLines = [];
31383
+ const resultLines = [];
31384
+ for (const [alias, declaration] of entries) {
31385
+ const valueType = declaration.inputs ? inputsToType(declaration.inputs) : "Record<string, unknown>";
31386
+ const aliasKey = isValidIdentifier(alias) ? alias : JSON.stringify(alias);
31387
+ inputLines.push(` ${aliasKey}: ${valueType};`);
31388
+ if (declaration.outputs) {
31389
+ resultLines.push(` ${aliasKey}: ${objectFieldsToType(declaration.outputs)};`);
31390
+ }
31391
+ }
31392
+ const resultsBlock = resultLines.length > 0 ? `
31393
+ interface AppAgentResults {
31394
+ ${resultLines.join("\n")}
31395
+ }` : "";
31396
+ return `${HEADER2}
31397
+ declare module "@lotics/app-sdk" {
31398
+ interface AppAgents {
31399
+ ${inputLines.join("\n")}
31400
+ }${resultsBlock}
31401
+ }
31402
+ `;
31403
+ }
31404
+
31405
+ // src/generate_app_queries_dts.ts
31406
+ var HEADER3 = `// Auto-generated by 'lotics app pull/dev/deploy'.
31363
31407
  // DO NOT EDIT \u2014 regenerated from package.json#lotics.queries.
31364
31408
  //
31365
31409
  // This file gives \`useQuery("alias", params)\` typed params at call sites by
@@ -31370,7 +31414,7 @@ import "@lotics/app-sdk";
31370
31414
  function generateAppQueriesDts(queries) {
31371
31415
  const entries = Object.entries(queries ?? {});
31372
31416
  if (entries.length === 0) {
31373
- return `${HEADER2}
31417
+ return `${HEADER3}
31374
31418
  // No queries declared in package.json#lotics.queries.
31375
31419
  // Add an entry to enable typed useQuery("alias", params) at call sites.
31376
31420
  declare module "@lotics/app-sdk" {
@@ -31385,7 +31429,7 @@ declare module "@lotics/app-sdk" {
31385
31429
  const aliasKey = isValidIdentifier(alias) ? alias : JSON.stringify(alias);
31386
31430
  lines.push(` ${aliasKey}: ${valueType};`);
31387
31431
  }
31388
- return `${HEADER2}
31432
+ return `${HEADER3}
31389
31433
  declare module "@lotics/app-sdk" {
31390
31434
  interface AppQueries {
31391
31435
  ${lines.join("\n")}
@@ -31453,6 +31497,7 @@ function readAppMeta(projectDir) {
31453
31497
  version_number: pkg2.lotics.version_number ?? null,
31454
31498
  workflows: pkg2.lotics.workflows ?? {},
31455
31499
  queries: pkg2.lotics.queries ?? {},
31500
+ agents: pkg2.lotics.agents ?? {},
31456
31501
  capabilities: pkg2.lotics.capabilities
31457
31502
  };
31458
31503
  }
@@ -31473,6 +31518,10 @@ function writeAppDts(projectDir, manifest) {
31473
31518
  path4.join(dotLotics, "app_queries.d.ts"),
31474
31519
  generateAppQueriesDts(manifest.queries)
31475
31520
  );
31521
+ fs3.writeFileSync(
31522
+ path4.join(dotLotics, "app_agents.d.ts"),
31523
+ generateAppAgentsDts(manifest.agents)
31524
+ );
31476
31525
  }
31477
31526
  function stampPulledManifest(projectDir, args) {
31478
31527
  writeAppMeta(projectDir, {
@@ -31481,9 +31530,10 @@ function stampPulledManifest(projectDir, args) {
31481
31530
  current_version_id: args.current_version_id,
31482
31531
  version_number: args.version_number,
31483
31532
  workflows: args.workflows,
31484
- queries: args.queries
31533
+ queries: args.queries,
31534
+ agents: args.agents
31485
31535
  });
31486
- writeAppDts(projectDir, { workflows: args.workflows, queries: args.queries });
31536
+ writeAppDts(projectDir, { workflows: args.workflows, queries: args.queries, agents: args.agents });
31487
31537
  }
31488
31538
  async function downloadToFile(url, destPath) {
31489
31539
  const response = await fetch(url);
@@ -31572,7 +31622,8 @@ async function appPull(client, args) {
31572
31622
  current_version_id: app.current_version_id,
31573
31623
  version_number: version.version,
31574
31624
  workflows: app.workflows ?? {},
31575
- queries: app.queries ?? {}
31625
+ queries: app.queries ?? {},
31626
+ agents: app.agents ?? {}
31576
31627
  });
31577
31628
  console.error(`Installing npm dependencies...`);
31578
31629
  await runNpm(["install"], targetPath);
@@ -31619,7 +31670,7 @@ async function appDeploy(client, args) {
31619
31670
  Add to package.json#lotics.capabilities: ${block}`
31620
31671
  );
31621
31672
  }
31622
- writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries });
31673
+ writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries, agents: meta.agents });
31623
31674
  console.error("Building...");
31624
31675
  await runNpm(["run", "build"], projectDir);
31625
31676
  const distDir = path4.join(projectDir, "dist");
@@ -31710,7 +31761,7 @@ async function warnIfUnbranded(client, appId) {
31710
31761
  async function appDev(client, args) {
31711
31762
  const projectDir = path4.resolve(args.projectDir ?? process.cwd());
31712
31763
  const meta = readAppMeta(projectDir);
31713
- writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries });
31764
+ writeAppDts(projectDir, { workflows: meta.workflows, queries: meta.queries, agents: meta.agents });
31714
31765
  const app = await client.getApp(meta.app_id);
31715
31766
  const handle = await startDevServer({
31716
31767
  projectDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {