@cerebro-labs/agentnava-sdk 0.47.1 → 0.51.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.
@@ -1 +1 @@
1
- {"version":3,"file":"load-agent-dir.js","sourceRoot":"","sources":["../src/load-agent-dir.ts"],"names":[],"mappings":"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","sourcesContent":["// `loadAgentDir` reads a Claude-Code-style folder and returns a SpecInput\n// suitable for `an.specs.push(...)`. Layout it expects:\n//\n// <path>/\n// ├── AGENT.md # required: core instructions (CLAUDE.md equivalent)\n// ├── skills/<name>/SKILL.md # optional, repeated: loaded as workflows\n// ├── subagents/<name>.md # optional, repeated (advanced)\n// ├── workflows/<name>.md # optional, repeated\n// ├── commands/<name>.md # IGNORED (warned): specs carry no commands\n// ├── http-tools.json # optional, declarative HTTP tool array\n// └── agent.json # optional, capabilities (tools, sandbox, role)\n//\n// Files inside `skills/` use the Claude Code convention: one folder per skill,\n// containing `SKILL.md` with YAML frontmatter. The folder name is the skill name.\n// Each one loads as a WORKFLOW: a spec is instructions plus workflows and has no\n// skills collection (PR #873). `skills/` is read only so a Claude Code agent\n// directory drops in unchanged.\n// `subagents/` and `workflows/` are flat directories of `<name>.md`. A\n// `commands/` directory is ignored with a warning: specs no longer carry them.\n//\n// Every workflow, from either folder, is read with `parseWorkflow`, so its\n// frontmatter `when:` (or a SKILL.md's `description:`) becomes the `when` the\n// platform requires. The file or folder name stays the workflow's name.\n//\n// `agent.json` is what makes a folder self-describing: it carries the spec-level\n// capabilities (`tools`, `sandbox`, `role`) alongside the markdown, so an agent\n// directory can be copied between workspaces and still be the same agent. Values\n// passed in `opts` win over the file, so a caller can still override in code.\n//\n// Knowledge base files are NOT loaded by this helper. Use `an.knowledge.upload`\n// for those. Everything else in the bundle is markdown.\n\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Filesystem, HttpTool, McpServer, SpecFile, SpecInput } from './types.ts';\nimport { parseWorkflow, workflowSlug } from './workflows.ts';\n\n// Local aliases, so the body of this file reads the way it was written against\n// the retired package's names. Nothing outside this module uses them.\ntype HttpToolDef = HttpTool;\ntype McpServerSpec = McpServer;\ntype AgentRole = string;\ntype SpecSandbox = Filesystem;\ntype SpecToolGroup = string;\ntype WidgetDef = { name: string; [k: string]: unknown };\n\nexport interface LoadAgentDirOptions {\n\t/** Dev-chosen, stable key, required. Used by the backend to upsert this spec. */\n\tkey: string;\n\t/** The role this agent plays. Default `'freelancer'` is applied server-side\n\t * when omitted. See {@link AgentRole} for the full vocabulary. */\n\trole?: AgentRole;\n\t/** Platform tool groups to grant. Overrides `agent.json`. */\n\ttools?: SpecToolGroup[];\n\t/** Filesystem for this agent's turns. Overrides `agent.json`. */\n\tsandbox?: SpecSandbox;\n}\n\n/** The optional `agent.json` manifest, spec-level capabilities that belong with\n * the markdown rather than in whatever script happens to push it. */\ninterface AgentManifest {\n\ttools?: SpecToolGroup[];\n\tsandbox?: SpecSandbox;\n\trole?: AgentRole;\n}\n\nexport function loadAgentDir(path: string, opts: LoadAgentDirOptions): SpecInput {\n\tif (!opts.key) {\n\t\tthrow new Error('[loadAgentDir] opts.key is required');\n\t}\n\tif (!existsSync(path) || !statSync(path).isDirectory()) {\n\t\tthrow new Error(`[loadAgentDir] not a directory: ${path}`);\n\t}\n\n\tconst agentMdPath = join(path, 'AGENT.md');\n\tif (!existsSync(agentMdPath)) {\n\t\tthrow new Error(\n\t\t\t`[loadAgentDir] missing required file: ${agentMdPath}. ` +\n\t\t\t\t`Every spec needs an AGENT.md (the CLAUDE.md equivalent: core instructions).`,\n\t\t);\n\t}\n\tconst instructions = readFileSync(agentMdPath, 'utf8');\n\n\tconst subagents = loadFlatMdDir(join(path, 'subagents'));\n\t// A workflows/<name>.md wins over a skills/<name>/SKILL.md of the same name,\n\t// \"same\" as the platform compares them (workflowSlug): it refuses two\n\t// workflows that reduce to one identifier, so both would fail at publish.\n\t// Warned rather than hidden in silence, like commands/.\n\tconst asWorkflow = (f: SpecFile): SpecFile => ({ ...parseWorkflow(f.content, f.name), name: f.name });\n\tconst workflows = loadFlatMdDir(join(path, 'workflows')).map(asWorkflow);\n\tconst key = (name: string) => workflowSlug(name) || name;\n\t// slug -> the file that claimed it, so the warning names the right folder.\n\tconst claimed = new Map<string, string>(workflows.map((w) => [key(w.name), `workflows/${w.name}.md`]));\n\tfor (const skill of loadSkillsDir(join(path, 'skills'))) {\n\t\tconst file = `skills/${skill.name}/SKILL.md`;\n\t\tconst shadow = claimed.get(key(skill.name));\n\t\tif (shadow !== undefined) {\n\t\t\tconsole.warn(`[loadAgentDir] ignoring ${file}: ${shadow} has the same name. Rename one of them to keep both.`);\n\t\t\tcontinue;\n\t\t}\n\t\tclaimed.set(key(skill.name), file);\n\t\tworkflows.push(asWorkflow(skill));\n\t}\n\t// `commands/` is read only to WARN about it. A spec has no commands field any\n\t// more: slash commands had one writer (the dedicated-vm translator) and no\n\t// reader on the default runtime.\n\t//\n\t// Deliberately not folded into workflows, which would be the tempting\n\t// one-liner. A Claude Code slash command is invoked BY A PERSON; a workflow is\n\t// invoked by the model whenever it looks relevant. Folding them would make\n\t// every /command fire unbidden, which is a behaviour change disguised as a\n\t// migration. Warned rather than dropped in silence, and rather than thrown,\n\t// so an existing agent directory still loads.\n\tconst commands = loadFlatMdDir(join(path, 'commands'));\n\tif (commands.length > 0) {\n\t\tconsole.warn(\n\t\t\t`[loadAgentDir] ignoring ${commands.length} file(s) in commands/: agent specs no longer ` +\n\t\t\t\t'carry slash commands. Move any that should run automatically into workflows/.',\n\t\t);\n\t}\n\n\tconst out: SpecInput = { key: opts.key, instructions };\n\tif (subagents.length > 0) out.subagents = subagents;\n\tif (workflows.length > 0) out.workflows = workflows;\n\tconst httpTools = loadHttpTools(join(path, 'http-tools.json'));\n\tif (httpTools && httpTools.length > 0) out.httpTools = httpTools;\n\tconst widgets = loadWidgets(join(path, 'widgets.json'));\n\tif (widgets && widgets.length > 0) out.widgets = widgets;\n\tconst mcpServers = loadMcpServers(join(path, 'mcp-servers.json'));\n\tif (mcpServers && mcpServers.length > 0) out.mcpServers = mcpServers;\n\n\tconst manifest = loadManifest(join(path, 'agent.json'));\n\tconst tools = opts.tools ?? manifest.tools;\n\tif (tools && tools.length > 0) out.tools = tools;\n\tconst sandbox = opts.sandbox ?? manifest.sandbox;\n\tif (sandbox) out.sandbox = sandbox;\n\tconst role = opts.role ?? manifest.role;\n\tif (role) out.role = role;\n\treturn out;\n}\n\n/** agent.json, optional. Absent is the common case and means \"all defaults\". */\nfunction loadManifest(filePath: string): AgentManifest {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return {};\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] agent.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] agent.json: must be an object');\n\t}\n\tconst m = parsed as Record<string, unknown>;\n\tif (m.tools !== undefined && !Array.isArray(m.tools)) {\n\t\tthrow new Error('[loadAgentDir] agent.json: \"tools\" must be an array');\n\t}\n\tif (m.sandbox !== undefined && m.sandbox !== 'drive' && m.sandbox !== 'none') {\n\t\tthrow new Error(`[loadAgentDir] agent.json: \"sandbox\" must be 'drive' or 'none'`);\n\t}\n\t// Group names are NOT validated here on purpose: the platform is the\n\t// authority on which groups exist and rejects unknown ones at push time with\n\t// the current list, so the SDK never goes stale against a newer platform.\n\treturn m as AgentManifest;\n}\n\nfunction loadHttpTools(filePath: string): HttpToolDef[] | undefined {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return undefined;\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] http-tools.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] http-tools.json: must be an array of tool defs');\n\t}\n\treturn parsed as HttpToolDef[];\n}\n\nfunction loadWidgets(filePath: string): WidgetDef[] | undefined {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return undefined;\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] widgets.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] widgets.json: must be an array of widget defs');\n\t}\n\treturn parsed as WidgetDef[];\n}\n\nfunction loadMcpServers(filePath: string): McpServerSpec[] | undefined {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return undefined;\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] mcp-servers.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] mcp-servers.json: must be an array of MCP server defs');\n\t}\n\treturn parsed as McpServerSpec[];\n}\n\n/** skills/<name>/SKILL.md: name comes from the directory. */\nfunction loadSkillsDir(dir: string): SpecFile[] {\n\tif (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n\tconst out: SpecFile[] = [];\n\tfor (const name of readdirSync(dir).sort()) {\n\t\tif (name.startsWith('.')) continue;\n\t\tconst skillPath = join(dir, name, 'SKILL.md');\n\t\tif (existsSync(skillPath) && statSync(skillPath).isFile()) {\n\t\t\tout.push({ name, content: readFileSync(skillPath, 'utf8') });\n\t\t}\n\t}\n\treturn out;\n}\n\n/** subagents/, workflows/: flat directories of <name>.md. */\nfunction loadFlatMdDir(dir: string): SpecFile[] {\n\tif (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n\tconst out: SpecFile[] = [];\n\tfor (const file of readdirSync(dir).sort()) {\n\t\tif (!file.endsWith('.md') || file.startsWith('.')) continue;\n\t\tconst filePath = join(dir, file);\n\t\tif (!statSync(filePath).isFile()) continue;\n\t\tconst name = file.replace(/\\.md$/, '');\n\t\tout.push({ name, content: readFileSync(filePath, 'utf8') });\n\t}\n\treturn out;\n}\n"]}
1
+ {"version":3,"file":"load-agent-dir.js","sourceRoot":"","sources":["../src/load-agent-dir.ts"],"names":[],"mappings":"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","sourcesContent":["// `loadAgentDir` reads a Claude-Code-style folder and returns a SpecInput\n// suitable for `an.specs.push(...)`. Layout it expects:\n//\n// <path>/\n// ├── AGENT.md # required: core instructions (CLAUDE.md equivalent)\n// ├── skills/<name>/SKILL.md # optional, repeated: loaded as workflows\n// ├── subagents/<name>.md # optional, repeated (advanced)\n// ├── workflows/<name>.md # optional, repeated\n// ├── commands/<name>.md # IGNORED (warned): specs carry no commands\n// ├── http-tools.json # optional, declarative HTTP tool array\n// └── agent.json # optional, capabilities (tools, sandbox, role)\n//\n// Files inside `skills/` use the Claude Code convention: one folder per skill,\n// containing `SKILL.md` with YAML frontmatter. The folder name is the skill name.\n// Each one loads as a WORKFLOW: a spec is instructions plus workflows and has no\n// skills collection (PR #873). `skills/` is read only so a Claude Code agent\n// directory drops in unchanged.\n// `subagents/` and `workflows/` are flat directories of `<name>.md`. A\n// `commands/` directory is ignored with a warning: specs no longer carry them.\n//\n// Every workflow, from either folder, is read with `parseWorkflow`, so its\n// frontmatter `when:` (or a SKILL.md's `description:`) becomes the `when` the\n// platform requires. The file or folder name stays the workflow's name.\n//\n// `agent.json` is what makes a folder self-describing: it carries the spec-level\n// capabilities (`tools`, `sandbox`, `role`) alongside the markdown, so an agent\n// directory can be copied between workspaces and still be the same agent. Values\n// passed in `opts` win over the file, so a caller can still override in code.\n//\n// Knowledge base files are NOT loaded by this helper. Use `an.knowledge.upload`\n// for those. Everything else in the bundle is markdown.\n\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Filesystem, HttpTool, McpServer, SpecFile, SpecInput } from './types.ts';\nimport { parseWorkflow, workflowSlug } from './workflows.ts';\n\n// Local aliases, so the body of this file reads the way it was written against\n// the retired package's names. Nothing outside this module uses them.\ntype HttpToolDef = HttpTool;\ntype McpServerSpec = McpServer;\ntype AgentRole = string;\ntype SpecSandbox = Filesystem;\ntype SpecToolGroup = string;\n\nexport interface LoadAgentDirOptions {\n\t/** Dev-chosen, stable key, required. Used by the backend to upsert this spec. */\n\tkey: string;\n\t/** The role this agent plays. Default `'freelancer'` is applied server-side\n\t * when omitted. See {@link AgentRole} for the full vocabulary. */\n\trole?: AgentRole;\n\t/** Platform tool groups to grant. Overrides `agent.json`. */\n\ttools?: SpecToolGroup[];\n\t/** Filesystem for this agent's turns. Overrides `agent.json`. */\n\tsandbox?: SpecSandbox;\n}\n\n/** The optional `agent.json` manifest, spec-level capabilities that belong with\n * the markdown rather than in whatever script happens to push it. */\ninterface AgentManifest {\n\ttools?: SpecToolGroup[];\n\tsandbox?: SpecSandbox;\n\trole?: AgentRole;\n}\n\nexport function loadAgentDir(path: string, opts: LoadAgentDirOptions): SpecInput {\n\tif (!opts.key) {\n\t\tthrow new Error('[loadAgentDir] opts.key is required');\n\t}\n\tif (!existsSync(path) || !statSync(path).isDirectory()) {\n\t\tthrow new Error(`[loadAgentDir] not a directory: ${path}`);\n\t}\n\n\tconst agentMdPath = join(path, 'AGENT.md');\n\tif (!existsSync(agentMdPath)) {\n\t\tthrow new Error(\n\t\t\t`[loadAgentDir] missing required file: ${agentMdPath}. ` +\n\t\t\t\t`Every spec needs an AGENT.md (the CLAUDE.md equivalent: core instructions).`,\n\t\t);\n\t}\n\tconst instructions = readFileSync(agentMdPath, 'utf8');\n\n\tconst subagents = loadFlatMdDir(join(path, 'subagents'));\n\t// A workflows/<name>.md wins over a skills/<name>/SKILL.md of the same name,\n\t// \"same\" as the platform compares them (workflowSlug): it refuses two\n\t// workflows that reduce to one identifier, so both would fail at publish.\n\t// Warned rather than hidden in silence, like commands/.\n\tconst asWorkflow = (f: SpecFile): SpecFile => ({ ...parseWorkflow(f.content, f.name), name: f.name });\n\tconst workflows = loadFlatMdDir(join(path, 'workflows')).map(asWorkflow);\n\tconst key = (name: string) => workflowSlug(name) || name;\n\t// slug -> the file that claimed it, so the warning names the right folder.\n\tconst claimed = new Map<string, string>(workflows.map((w) => [key(w.name), `workflows/${w.name}.md`]));\n\tfor (const skill of loadSkillsDir(join(path, 'skills'))) {\n\t\tconst file = `skills/${skill.name}/SKILL.md`;\n\t\tconst shadow = claimed.get(key(skill.name));\n\t\tif (shadow !== undefined) {\n\t\t\tconsole.warn(`[loadAgentDir] ignoring ${file}: ${shadow} has the same name. Rename one of them to keep both.`);\n\t\t\tcontinue;\n\t\t}\n\t\tclaimed.set(key(skill.name), file);\n\t\tworkflows.push(asWorkflow(skill));\n\t}\n\t// `commands/` is read only to WARN about it. A spec has no commands field any\n\t// more: no runtime reads slash commands, and none ever had a way to author\n\t// them in a UI.\n\t//\n\t// Deliberately not folded into workflows, which would be the tempting\n\t// one-liner. A Claude Code slash command is invoked BY A PERSON; a workflow is\n\t// invoked by the model whenever it looks relevant. Folding them would make\n\t// every /command fire unbidden, which is a behaviour change disguised as a\n\t// migration. Warned rather than dropped in silence, and rather than thrown,\n\t// so an existing agent directory still loads.\n\tconst commands = loadFlatMdDir(join(path, 'commands'));\n\tif (commands.length > 0) {\n\t\tconsole.warn(\n\t\t\t`[loadAgentDir] ignoring ${commands.length} file(s) in commands/: agent specs no longer ` +\n\t\t\t\t'carry slash commands. Move any that should run automatically into workflows/.',\n\t\t);\n\t}\n\n\tconst out: SpecInput = { key: opts.key, instructions };\n\tif (subagents.length > 0) out.subagents = subagents;\n\tif (workflows.length > 0) out.workflows = workflows;\n\tconst httpTools = loadHttpTools(join(path, 'http-tools.json'));\n\tif (httpTools && httpTools.length > 0) out.httpTools = httpTools;\n\tconst mcpServers = loadMcpServers(join(path, 'mcp-servers.json'));\n\tif (mcpServers && mcpServers.length > 0) out.mcpServers = mcpServers;\n\n\tconst manifest = loadManifest(join(path, 'agent.json'));\n\tconst tools = opts.tools ?? manifest.tools;\n\tif (tools && tools.length > 0) out.tools = tools;\n\tconst sandbox = opts.sandbox ?? manifest.sandbox;\n\tif (sandbox) out.sandbox = sandbox;\n\tconst role = opts.role ?? manifest.role;\n\tif (role) out.role = role;\n\treturn out;\n}\n\n/** agent.json, optional. Absent is the common case and means \"all defaults\". */\nfunction loadManifest(filePath: string): AgentManifest {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return {};\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] agent.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] agent.json: must be an object');\n\t}\n\tconst m = parsed as Record<string, unknown>;\n\tif (m.tools !== undefined && !Array.isArray(m.tools)) {\n\t\tthrow new Error('[loadAgentDir] agent.json: \"tools\" must be an array');\n\t}\n\tif (m.sandbox !== undefined && m.sandbox !== 'drive' && m.sandbox !== 'none') {\n\t\tthrow new Error(`[loadAgentDir] agent.json: \"sandbox\" must be 'drive' or 'none'`);\n\t}\n\t// Group names are NOT validated here on purpose: the platform is the\n\t// authority on which groups exist and rejects unknown ones at push time with\n\t// the current list, so the SDK never goes stale against a newer platform.\n\treturn m as AgentManifest;\n}\n\nfunction loadHttpTools(filePath: string): HttpToolDef[] | undefined {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return undefined;\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] http-tools.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] http-tools.json: must be an array of tool defs');\n\t}\n\treturn parsed as HttpToolDef[];\n}\n\nfunction loadMcpServers(filePath: string): McpServerSpec[] | undefined {\n\tif (!existsSync(filePath) || !statSync(filePath).isFile()) return undefined;\n\tconst raw = readFileSync(filePath, 'utf8');\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (e) {\n\t\tthrow new Error(`[loadAgentDir] mcp-servers.json: invalid JSON (${(e as Error).message})`);\n\t}\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error('[loadAgentDir] mcp-servers.json: must be an array of MCP server defs');\n\t}\n\treturn parsed as McpServerSpec[];\n}\n\n/** skills/<name>/SKILL.md: name comes from the directory. */\nfunction loadSkillsDir(dir: string): SpecFile[] {\n\tif (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n\tconst out: SpecFile[] = [];\n\tfor (const name of readdirSync(dir).sort()) {\n\t\tif (name.startsWith('.')) continue;\n\t\tconst skillPath = join(dir, name, 'SKILL.md');\n\t\tif (existsSync(skillPath) && statSync(skillPath).isFile()) {\n\t\t\tout.push({ name, content: readFileSync(skillPath, 'utf8') });\n\t\t}\n\t}\n\treturn out;\n}\n\n/** subagents/, workflows/: flat directories of <name>.md. */\nfunction loadFlatMdDir(dir: string): SpecFile[] {\n\tif (!existsSync(dir) || !statSync(dir).isDirectory()) return [];\n\tconst out: SpecFile[] = [];\n\tfor (const file of readdirSync(dir).sort()) {\n\t\tif (!file.endsWith('.md') || file.startsWith('.')) continue;\n\t\tconst filePath = join(dir, file);\n\t\tif (!statSync(filePath).isFile()) continue;\n\t\tconst name = file.replace(/\\.md$/, '');\n\t\tout.push({ name, content: readFileSync(filePath, 'utf8') });\n\t}\n\treturn out;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerebro-labs/agentnava-sdk",
3
- "version": "0.47.1",
3
+ "version": "0.51.0",
4
4
  "description": "The AgentNava SDK. Configure an agent, start a conversation, and work with it.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -24,7 +24,12 @@
24
24
  "./import-tools": {
25
25
  "types": "./import-tools.d.ts",
26
26
  "default": "./import-tools.js"
27
- }
27
+ },
28
+ "./react": {
29
+ "types": "./react.d.ts",
30
+ "default": "./react.js"
31
+ },
32
+ "./react/styles.css": "./react.css"
28
33
  },
29
34
  "engines": {
30
35
  "node": ">=18",
@@ -32,5 +37,13 @@
32
37
  },
33
38
  "publishConfig": {
34
39
  "access": "public"
40
+ },
41
+ "peerDependencies": {
42
+ "react": ">=18.0.0"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "react": {
46
+ "optional": true
47
+ }
35
48
  }
36
49
  }
package/project.js CHANGED
@@ -129,6 +129,12 @@ export class Project {
129
129
  return res.url;
130
130
  }
131
131
  }
132
+ /** A grade as the server reported it, in this SDK's vocabulary. */
133
+ function readGrade(value) {
134
+ if (value === 'fast')
135
+ return 'standard';
136
+ return GRADE_NAMES.includes(value ?? '') ? value : 'lite';
137
+ }
132
138
  /** The `{ project, agents }` envelope as one flat ProjectData.
133
139
  *
134
140
  * The envelope and the snake_case are what apps/console reads, so the wire
@@ -142,9 +148,10 @@ export function toProjectData(body) {
142
148
  name: p?.name ?? '',
143
149
  goal: p?.goal ?? null,
144
150
  managerInstructions: p?.manager_instructions ?? null,
145
- // Any real grade passes through. This used to keep only 'premium', so a
146
- // fast or best project read back as 'standard'.
147
- grade: GRADE_NAMES.includes(p?.grade ?? '') ? p.grade : 'standard',
151
+ // Any real grade passes through, and the old name `fast` reads as what it
152
+ // means now (`standard`). Anything else is the cheapest, `lite`: guessing
153
+ // upward would tell a caller they pay more than they do.
154
+ grade: readGrade(p?.grade),
148
155
  status: p?.status === 'archived' ? 'archived' : 'active',
149
156
  agents: members.map((m) => ({
150
157
  agentId: m.agent_id,
package/project.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["// A project: several agents, one goal, one shared folder.\n\nimport { Conversation } from './conversation.ts';\nimport type { Http } from './http.ts';\nimport { GRADE_NAMES, type FileEntry, type Grade, type ProjectConversationData, type ProjectData, type ProjectFields } from './types.ts';\n\nexport interface ProjectStartOptions {\n\tparameters?: Record<string, string | number>;\n}\n\nexport interface FileUrlOptions {\n\tdownload?: boolean;\n\tttl?: number;\n}\n\n/**\n * A project you can work with. Holding one makes no request.\n *\n * The difference from an agent is the shared folder: every agent on a project\n * reads and writes the SAME files, which is what lets one pick up where another\n * left off. Two conversations with the same agent share nothing; two agents on\n * the same project share everything in that folder.\n */\nexport class Project {\n\treadonly id: string;\n\treadonly #http: Http;\n\tprivate snapshot?: ProjectData;\n\n\tconstructor(http: Http, id: string, snapshot?: ProjectData) {\n\t\tthis.#http = http;\n\t\tthis.id = id;\n\t\tif (snapshot) this.snapshot = snapshot;\n\t}\n\n\tprivate get base(): string {\n\t\treturn `/v1/projects/${encodeURIComponent(this.id)}`;\n\t}\n\n\tget data(): ProjectData | undefined {\n\t\treturn this.snapshot;\n\t}\n\n\tasync get(): Promise<ProjectData> {\n\t\tconst data = toProjectData(await this.#http.get(this.base));\n\t\tthis.snapshot = data;\n\t\treturn data;\n\t}\n\n\tasync update(fields: ProjectFields): Promise<Project> {\n\t\tawait this.#http.patch(this.base, fields);\n\t\t// PATCH answers the row it wrote; re-read so the object carries the roster\n\t\t// too, which the caller will ask for next.\n\t\treturn new Project(this.#http, this.id, await this.get());\n\t}\n\n\tasync addAgent(agentId: string): Promise<Project> {\n\t\tconst data = toProjectData(await this.#http.post(`${this.base}/agents`, { agentId }));\n\t\tthis.snapshot = data;\n\t\treturn new Project(this.#http, this.id, data);\n\t}\n\n\tasync removeAgent(agentId: string): Promise<Project> {\n\t\tconst data = toProjectData(\n\t\t\tawait this.#http.delete(`${this.base}/agents/${encodeURIComponent(agentId)}`),\n\t\t);\n\t\tthis.snapshot = data;\n\t\treturn new Project(this.#http, this.id, data);\n\t}\n\n\t/** Stop it without destroying it. The folder and the history stay readable. */\n\tasync archive(): Promise<Project> {\n\t\tconst data = toProjectData(await this.#http.post(`${this.base}/archive`));\n\t\tthis.snapshot = data;\n\t\treturn new Project(this.#http, this.id, data);\n\t}\n\n\t/** Delete it, and the shared folder with it. Archive is usually what you\n\t * wanted. */\n\tasync delete(): Promise<void> {\n\t\tawait this.#http.delete(this.base);\n\t}\n\n\tasync start(options: ProjectStartOptions = {}): Promise<Conversation> {\n\t\tconst data = await this.#http.post<ProjectConversationData>(`${this.base}/conversations`, {\n\t\t\t...(options.parameters && { parameters: options.parameters }),\n\t\t});\n\t\t// A project conversation and an agent conversation differ in what STARTED\n\t\t// them, not in what you can do with one, so they share the class. The\n\t\t// distinction that matters (which id it carries) is in the data.\n\t\treturn new Conversation(this.#http, data.id);\n\t}\n\n\t/**\n\t * Address one of this project's conversations by id. No request.\n\t *\n\t * The sibling of `agent.conversation(id)`: the thing that owns it is the\n\t * thing you ask.\n\t *\n\t * A PROJECT HAS ONE CONVERSATION PER AGENT, NOT ONE IN TOTAL. Starting a\n\t * project opens the manager's conversation and one for every agent on the\n\t * roster, so `conversations()` on a two-agent project returns three.\n\t *\n\t * This comment used to say a project had exactly one, which was true when it\n\t * was written and had stopped being true by the time anyone read it.\n\t * Observed on deployed staging on 2026-09-18: starting a project with Sal and\n\t * Otis returned three, the manager's being the one with no `agent_id`.\n\t *\n\t * Tell them apart by `agent_id` on the wire row: absent means the manager,\n\t * which is derived from the project rather than configured as an agent, and\n\t * whose `conversation.agentId` reads `manager-<projectId>`.\n\t *\n\t * It resolves because `getSession` reads both session tables: an id does\n\t * not say which one it came from, so the route answers for either.\n\t */\n\tconversation(conversationId: string): Conversation {\n\t\treturn new Conversation(this.#http, conversationId);\n\t}\n\n\tasync conversations(): Promise<Conversation[]> {\n\t\t// `sessions`, which is what the route actually answers with. This read\n\t\t// `res.conversations`, which is always undefined, so `?? []` turned a SHAPE\n\t\t// MISMATCH into a plausible empty list and every project reported having no\n\t\t// conversations however many it had.\n\t\t//\n\t\t// The URL was never the problem: /conversations is a real alias for\n\t\t// /sessions and both return `{ sessions: [...] }`. Only the key was wrong,\n\t\t// which is why nothing 404'd and nothing looked broken.\n\t\t//\n\t\t// Same shape as the bugs this SDK's own end-to-end suite found on the\n\t\t// server today: an absence read as an emptiness. `?? []` is the line that\n\t\t// does the hiding, and it is load-bearing for a genuinely empty project, so\n\t\t// the fix is to read the right key rather than to remove the fallback.\n\t\tconst res = await this.#http.get<{ sessions: ProjectConversationData[] }>(\n\t\t\t`${this.base}/conversations`,\n\t\t);\n\t\treturn (res.sessions ?? []).map((c) => new Conversation(this.#http, c.id));\n\t}\n\n\t// --------------------------------------------------------- the shared folder\n\n\t/** List one directory of the shared folder. Same `path` parameter as\n\t * everywhere else on this API. */\n\tasync files(path = '/'): Promise<FileEntry[]> {\n\t\tconst res = await this.#http.get<{ files: unknown[] }>(`${this.base}/files`, { path });\n\t\treturn (res.files ?? []) as FileEntry[];\n\t}\n\n\tasync readFile(path: string): Promise<string> {\n\t\t// JSON `{path, content}`, not bare text. See Conversation.readFile.\n\t\tconst res = await this.#http.get<{ path: string; content: string }>(\n\t\t\t`${this.base}/files/content`,\n\t\t\t{ path },\n\t\t);\n\t\treturn res.content ?? '';\n\t}\n\n\tasync fileUrl(path: string, options: FileUrlOptions = {}): Promise<string> {\n\t\tconst res = await this.#http.post<{ url: string }>(`${this.base}/files/url`, {\n\t\t\tpath,\n\t\t\t...(options.download !== undefined && { download: options.download }),\n\t\t\t...(options.ttl !== undefined && { ttl: options.ttl }),\n\t\t});\n\t\treturn res.url;\n\t}\n}\n\n/** A project row as `/v1/projects` returns it: snake_case, nested. */\ninterface WireProject {\n\tid: string;\n\tworkspace_id: string;\n\tname: string;\n\tstatus: string;\n\tgoal: string | null;\n\tmanager_instructions: string | null;\n\tgrade: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\ninterface WireMember {\n\tproject_id: string;\n\tagent_id: string;\n\tagent_version: number | null;\n\tagent_name: string | null;\n\tadded_at: string;\n}\n\n/** The `{ project, agents }` envelope as one flat ProjectData.\n *\n * The envelope and the snake_case are what apps/console reads, so the wire\n * stays and the SDK translates. A caller wants `project.agents`, not\n * `response.agents` alongside `response.project`. */\nexport function toProjectData(\n\tbody: { project?: WireProject; agents?: WireMember[] } | WireProject,\n): ProjectData {\n\tconst p = ('project' in body ? body.project : body) as WireProject;\n\tconst members = ('agents' in body ? (body.agents ?? []) : []) as WireMember[];\n\treturn {\n\t\tid: p?.id ?? '',\n\t\tname: p?.name ?? '',\n\t\tgoal: p?.goal ?? null,\n\t\tmanagerInstructions: p?.manager_instructions ?? null,\n\t\t// Any real grade passes through. This used to keep only 'premium', so a\n\t\t// fast or best project read back as 'standard'.\n\t\tgrade: (GRADE_NAMES as readonly string[]).includes(p?.grade ?? '') ? (p!.grade as Grade) : 'standard',\n\t\tstatus: p?.status === 'archived' ? 'archived' : 'active',\n\t\tagents: members.map((m) => ({\n\t\t\tagentId: m.agent_id,\n\t\t\tname: m.agent_name ?? '',\n\t\t\taddedAt: m.added_at,\n\t\t})),\n\t\tcreatedAt: p?.created_at ?? '',\n\t\tupdatedAt: p?.updated_at ?? '',\n\t};\n}\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["// A project: several agents, one goal, one shared folder.\n\nimport { Conversation } from './conversation.ts';\nimport type { Http } from './http.ts';\nimport { GRADE_NAMES, type FileEntry, type Grade, type ProjectConversationData, type ProjectData, type ProjectFields } from './types.ts';\n\nexport interface ProjectStartOptions {\n\tparameters?: Record<string, string | number>;\n}\n\nexport interface FileUrlOptions {\n\tdownload?: boolean;\n\tttl?: number;\n}\n\n/**\n * A project you can work with. Holding one makes no request.\n *\n * The difference from an agent is the shared folder: every agent on a project\n * reads and writes the SAME files, which is what lets one pick up where another\n * left off. Two conversations with the same agent share nothing; two agents on\n * the same project share everything in that folder.\n */\nexport class Project {\n\treadonly id: string;\n\treadonly #http: Http;\n\tprivate snapshot?: ProjectData;\n\n\tconstructor(http: Http, id: string, snapshot?: ProjectData) {\n\t\tthis.#http = http;\n\t\tthis.id = id;\n\t\tif (snapshot) this.snapshot = snapshot;\n\t}\n\n\tprivate get base(): string {\n\t\treturn `/v1/projects/${encodeURIComponent(this.id)}`;\n\t}\n\n\tget data(): ProjectData | undefined {\n\t\treturn this.snapshot;\n\t}\n\n\tasync get(): Promise<ProjectData> {\n\t\tconst data = toProjectData(await this.#http.get(this.base));\n\t\tthis.snapshot = data;\n\t\treturn data;\n\t}\n\n\tasync update(fields: ProjectFields): Promise<Project> {\n\t\tawait this.#http.patch(this.base, fields);\n\t\t// PATCH answers the row it wrote; re-read so the object carries the roster\n\t\t// too, which the caller will ask for next.\n\t\treturn new Project(this.#http, this.id, await this.get());\n\t}\n\n\tasync addAgent(agentId: string): Promise<Project> {\n\t\tconst data = toProjectData(await this.#http.post(`${this.base}/agents`, { agentId }));\n\t\tthis.snapshot = data;\n\t\treturn new Project(this.#http, this.id, data);\n\t}\n\n\tasync removeAgent(agentId: string): Promise<Project> {\n\t\tconst data = toProjectData(\n\t\t\tawait this.#http.delete(`${this.base}/agents/${encodeURIComponent(agentId)}`),\n\t\t);\n\t\tthis.snapshot = data;\n\t\treturn new Project(this.#http, this.id, data);\n\t}\n\n\t/** Stop it without destroying it. The folder and the history stay readable. */\n\tasync archive(): Promise<Project> {\n\t\tconst data = toProjectData(await this.#http.post(`${this.base}/archive`));\n\t\tthis.snapshot = data;\n\t\treturn new Project(this.#http, this.id, data);\n\t}\n\n\t/** Delete it, and the shared folder with it. Archive is usually what you\n\t * wanted. */\n\tasync delete(): Promise<void> {\n\t\tawait this.#http.delete(this.base);\n\t}\n\n\tasync start(options: ProjectStartOptions = {}): Promise<Conversation> {\n\t\tconst data = await this.#http.post<ProjectConversationData>(`${this.base}/conversations`, {\n\t\t\t...(options.parameters && { parameters: options.parameters }),\n\t\t});\n\t\t// A project conversation and an agent conversation differ in what STARTED\n\t\t// them, not in what you can do with one, so they share the class. The\n\t\t// distinction that matters (which id it carries) is in the data.\n\t\treturn new Conversation(this.#http, data.id);\n\t}\n\n\t/**\n\t * Address one of this project's conversations by id. No request.\n\t *\n\t * The sibling of `agent.conversation(id)`: the thing that owns it is the\n\t * thing you ask.\n\t *\n\t * A PROJECT HAS ONE CONVERSATION PER AGENT, NOT ONE IN TOTAL. Starting a\n\t * project opens the manager's conversation and one for every agent on the\n\t * roster, so `conversations()` on a two-agent project returns three.\n\t *\n\t * This comment used to say a project had exactly one, which was true when it\n\t * was written and had stopped being true by the time anyone read it.\n\t * Observed on deployed staging on 2026-09-18: starting a project with Sal and\n\t * Otis returned three, the manager's being the one with no `agent_id`.\n\t *\n\t * Tell them apart by `agent_id` on the wire row: absent means the manager,\n\t * which is derived from the project rather than configured as an agent, and\n\t * whose `conversation.agentId` reads `manager-<projectId>`.\n\t *\n\t * It resolves because `getSession` reads both session tables: an id does\n\t * not say which one it came from, so the route answers for either.\n\t */\n\tconversation(conversationId: string): Conversation {\n\t\treturn new Conversation(this.#http, conversationId);\n\t}\n\n\tasync conversations(): Promise<Conversation[]> {\n\t\t// `sessions`, which is what the route actually answers with. This read\n\t\t// `res.conversations`, which is always undefined, so `?? []` turned a SHAPE\n\t\t// MISMATCH into a plausible empty list and every project reported having no\n\t\t// conversations however many it had.\n\t\t//\n\t\t// The URL was never the problem: /conversations is a real alias for\n\t\t// /sessions and both return `{ sessions: [...] }`. Only the key was wrong,\n\t\t// which is why nothing 404'd and nothing looked broken.\n\t\t//\n\t\t// Same shape as the bugs this SDK's own end-to-end suite found on the\n\t\t// server today: an absence read as an emptiness. `?? []` is the line that\n\t\t// does the hiding, and it is load-bearing for a genuinely empty project, so\n\t\t// the fix is to read the right key rather than to remove the fallback.\n\t\tconst res = await this.#http.get<{ sessions: ProjectConversationData[] }>(\n\t\t\t`${this.base}/conversations`,\n\t\t);\n\t\treturn (res.sessions ?? []).map((c) => new Conversation(this.#http, c.id));\n\t}\n\n\t// --------------------------------------------------------- the shared folder\n\n\t/** List one directory of the shared folder. Same `path` parameter as\n\t * everywhere else on this API. */\n\tasync files(path = '/'): Promise<FileEntry[]> {\n\t\tconst res = await this.#http.get<{ files: unknown[] }>(`${this.base}/files`, { path });\n\t\treturn (res.files ?? []) as FileEntry[];\n\t}\n\n\tasync readFile(path: string): Promise<string> {\n\t\t// JSON `{path, content}`, not bare text. See Conversation.readFile.\n\t\tconst res = await this.#http.get<{ path: string; content: string }>(\n\t\t\t`${this.base}/files/content`,\n\t\t\t{ path },\n\t\t);\n\t\treturn res.content ?? '';\n\t}\n\n\tasync fileUrl(path: string, options: FileUrlOptions = {}): Promise<string> {\n\t\tconst res = await this.#http.post<{ url: string }>(`${this.base}/files/url`, {\n\t\t\tpath,\n\t\t\t...(options.download !== undefined && { download: options.download }),\n\t\t\t...(options.ttl !== undefined && { ttl: options.ttl }),\n\t\t});\n\t\treturn res.url;\n\t}\n}\n\n/** A project row as `/v1/projects` returns it: snake_case, nested. */\ninterface WireProject {\n\tid: string;\n\tworkspace_id: string;\n\tname: string;\n\tstatus: string;\n\tgoal: string | null;\n\tmanager_instructions: string | null;\n\tgrade: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n}\n\ninterface WireMember {\n\tproject_id: string;\n\tagent_id: string;\n\tagent_version: number | null;\n\tagent_name: string | null;\n\tadded_at: string;\n}\n\n/** A grade as the server reported it, in this SDK's vocabulary. */\nfunction readGrade(value: string | null | undefined): Grade {\n\tif (value === 'fast') return 'standard';\n\treturn (GRADE_NAMES as readonly string[]).includes(value ?? '') ? (value as Grade) : 'lite';\n}\n\n/** The `{ project, agents }` envelope as one flat ProjectData.\n *\n * The envelope and the snake_case are what apps/console reads, so the wire\n * stays and the SDK translates. A caller wants `project.agents`, not\n * `response.agents` alongside `response.project`. */\nexport function toProjectData(\n\tbody: { project?: WireProject; agents?: WireMember[] } | WireProject,\n): ProjectData {\n\tconst p = ('project' in body ? body.project : body) as WireProject;\n\tconst members = ('agents' in body ? (body.agents ?? []) : []) as WireMember[];\n\treturn {\n\t\tid: p?.id ?? '',\n\t\tname: p?.name ?? '',\n\t\tgoal: p?.goal ?? null,\n\t\tmanagerInstructions: p?.manager_instructions ?? null,\n\t\t// Any real grade passes through, and the old name `fast` reads as what it\n\t\t// means now (`standard`). Anything else is the cheapest, `lite`: guessing\n\t\t// upward would tell a caller they pay more than they do.\n\t\tgrade: readGrade(p?.grade),\n\t\tstatus: p?.status === 'archived' ? 'archived' : 'active',\n\t\tagents: members.map((m) => ({\n\t\t\tagentId: m.agent_id,\n\t\t\tname: m.agent_name ?? '',\n\t\t\taddedAt: m.added_at,\n\t\t})),\n\t\tcreatedAt: p?.created_at ?? '',\n\t\tupdatedAt: p?.updated_at ?? '',\n\t};\n}\n"]}
package/react.css ADDED
@@ -0,0 +1,409 @@
1
+ /*
2
+ * Default look for @cerebro-labs/agentnava-sdk/react.
3
+ *
4
+ * import '@cerebro-labs/agentnava-sdk/react/styles.css'
5
+ *
6
+ * Theme it by setting these variables on any ancestor (or :root). Fonts are
7
+ * inherited from your page. Every class starts with an-w- so nothing here
8
+ * reaches the rest of your app.
9
+ */
10
+ .an-w,
11
+ .an-reply {
12
+ --an-w-ink: #16181d;
13
+ --an-w-muted: #6b7280;
14
+ --an-w-line: #e5e7eb;
15
+ --an-w-surface: #f8f9fb;
16
+ --an-w-tint: #eef2f7;
17
+ --an-w-accent: #1f2937;
18
+ --an-w-accent-ink: #ffffff;
19
+ --an-w-s0: #2563eb;
20
+ --an-w-s1: #d97706;
21
+ --an-w-s2: #059669;
22
+ --an-w-s3: #7c3aed;
23
+ --an-w-high: #dc2626;
24
+ --an-w-medium: #d97706;
25
+ --an-w-good: #059669;
26
+ --an-w-info: #2563eb;
27
+ --an-w-radius: 8px;
28
+ }
29
+
30
+ .an-reply > * + * {
31
+ margin-top: 8px;
32
+ }
33
+ .an-reply-text {
34
+ margin: 0;
35
+ }
36
+
37
+ .an-w {
38
+ color: var(--an-w-ink);
39
+ font-size: 0.95em;
40
+ line-height: 1.45;
41
+ max-width: 100%;
42
+ }
43
+ .an-w p,
44
+ .an-w figure,
45
+ .an-w dl,
46
+ .an-w ul,
47
+ .an-w ol {
48
+ margin: 0;
49
+ }
50
+
51
+ .an-w-col {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 10px;
55
+ }
56
+ .an-w-gap-small {
57
+ gap: 6px;
58
+ }
59
+ .an-w-gap-large {
60
+ gap: 16px;
61
+ }
62
+ .an-w-row {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ gap: 8px;
66
+ }
67
+ .an-w-row > * {
68
+ flex: 1 1 0;
69
+ min-width: 96px;
70
+ }
71
+ .an-w-row.is-scroll {
72
+ flex-wrap: nowrap;
73
+ overflow-x: auto;
74
+ scroll-snap-type: x mandatory;
75
+ }
76
+ .an-w-row.is-scroll > * {
77
+ flex: 0 0 72%;
78
+ scroll-snap-align: start;
79
+ }
80
+
81
+ .an-w-card {
82
+ border: 1px solid var(--an-w-line);
83
+ border-radius: var(--an-w-radius);
84
+ background: var(--an-w-surface);
85
+ overflow: hidden;
86
+ }
87
+ .an-w-card > img {
88
+ display: block;
89
+ width: 100%;
90
+ aspect-ratio: 16 / 9;
91
+ object-fit: cover;
92
+ }
93
+ .an-w-card-body {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 6px;
97
+ padding: 10px 12px;
98
+ }
99
+ .an-w-card-link {
100
+ color: inherit;
101
+ text-decoration: none;
102
+ }
103
+ .an-w-card-link:hover .an-w-card {
104
+ border-color: var(--an-w-muted);
105
+ }
106
+
107
+ .an-w-text.is-title {
108
+ font-size: 1.15em;
109
+ font-weight: 600;
110
+ }
111
+ .an-w-text.is-heading {
112
+ font-weight: 600;
113
+ }
114
+ .an-w-text.is-caption {
115
+ color: var(--an-w-muted);
116
+ font-size: 0.9em;
117
+ }
118
+ .an-w-title {
119
+ color: var(--an-w-muted);
120
+ font-size: 0.9em;
121
+ font-weight: 500;
122
+ margin-bottom: 4px;
123
+ }
124
+
125
+ .an-w-stat {
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: 1px;
129
+ padding: 8px 10px;
130
+ border-radius: var(--an-w-radius);
131
+ background: var(--an-w-tint);
132
+ }
133
+ .an-w-stat strong {
134
+ font-size: 1.25em;
135
+ font-weight: 600;
136
+ font-variant-numeric: tabular-nums;
137
+ }
138
+ .an-w-stat span {
139
+ font-weight: 500;
140
+ }
141
+ .an-w-stat small {
142
+ color: var(--an-w-muted);
143
+ font-size: 0.85em;
144
+ line-height: 1.3;
145
+ }
146
+
147
+ .an-w-kv dl > div {
148
+ display: flex;
149
+ justify-content: space-between;
150
+ gap: 12px;
151
+ padding: 5px 0;
152
+ border-bottom: 1px solid var(--an-w-line);
153
+ }
154
+ .an-w-kv dt {
155
+ color: var(--an-w-muted);
156
+ }
157
+ .an-w-kv dd {
158
+ margin: 0;
159
+ text-align: right;
160
+ font-variant-numeric: tabular-nums;
161
+ }
162
+ .an-w-kv .is-total {
163
+ border-bottom: 0;
164
+ font-weight: 600;
165
+ }
166
+ .an-w-kv .is-total dt {
167
+ color: var(--an-w-ink);
168
+ }
169
+
170
+ .an-w-table {
171
+ overflow-x: auto;
172
+ }
173
+ .an-w-table table {
174
+ border-collapse: collapse;
175
+ width: 100%;
176
+ }
177
+ .an-w-table th,
178
+ .an-w-table td {
179
+ text-align: left;
180
+ padding: 5px 10px 5px 0;
181
+ border-bottom: 1px solid var(--an-w-line);
182
+ white-space: nowrap;
183
+ }
184
+ .an-w-table th {
185
+ color: var(--an-w-muted);
186
+ font-weight: 500;
187
+ }
188
+ .an-w-table td.is-num {
189
+ text-align: right;
190
+ font-variant-numeric: tabular-nums;
191
+ }
192
+
193
+ .an-w-chart figcaption {
194
+ color: var(--an-w-muted);
195
+ font-size: 0.9em;
196
+ font-weight: 500;
197
+ margin-bottom: 6px;
198
+ }
199
+ .an-w-chart svg {
200
+ display: block;
201
+ width: 100%;
202
+ height: auto;
203
+ overflow: visible;
204
+ }
205
+ .an-w-chart text {
206
+ font-size: 10px;
207
+ fill: var(--an-w-muted);
208
+ }
209
+ .an-w-chart rect.an-w-s0,
210
+ .an-w-chart circle.an-w-s0 {
211
+ fill: var(--an-w-s0);
212
+ }
213
+ .an-w-chart rect.an-w-s1,
214
+ .an-w-chart circle.an-w-s1 {
215
+ fill: var(--an-w-s1);
216
+ }
217
+ .an-w-chart rect.an-w-s2,
218
+ .an-w-chart circle.an-w-s2 {
219
+ fill: var(--an-w-s2);
220
+ }
221
+ .an-w-chart rect.an-w-s3,
222
+ .an-w-chart circle.an-w-s3 {
223
+ fill: var(--an-w-s3);
224
+ }
225
+ .an-w-line {
226
+ fill: none;
227
+ stroke-width: 2;
228
+ }
229
+ .an-w-line.an-w-s0 {
230
+ stroke: var(--an-w-s0);
231
+ }
232
+ .an-w-line.an-w-s1 {
233
+ stroke: var(--an-w-s1);
234
+ }
235
+ .an-w-line.an-w-s2 {
236
+ stroke: var(--an-w-s2);
237
+ }
238
+ .an-w-line.an-w-s3 {
239
+ stroke: var(--an-w-s3);
240
+ }
241
+ .an-w-ref line {
242
+ stroke: var(--an-w-muted);
243
+ stroke-dasharray: 4 3;
244
+ }
245
+ .an-w-legend {
246
+ display: flex;
247
+ flex-wrap: wrap;
248
+ gap: 12px;
249
+ color: var(--an-w-muted);
250
+ font-size: 0.9em;
251
+ margin-top: 4px;
252
+ }
253
+ .an-w-legend i {
254
+ display: inline-block;
255
+ width: 8px;
256
+ height: 8px;
257
+ border-radius: 50%;
258
+ margin-right: 5px;
259
+ }
260
+ .an-w-legend i.an-w-s0 {
261
+ background: var(--an-w-s0);
262
+ }
263
+ .an-w-legend i.an-w-s1 {
264
+ background: var(--an-w-s1);
265
+ }
266
+ .an-w-legend i.an-w-s2 {
267
+ background: var(--an-w-s2);
268
+ }
269
+ .an-w-legend i.an-w-s3 {
270
+ background: var(--an-w-s3);
271
+ }
272
+
273
+ .an-w-image img {
274
+ display: block;
275
+ max-width: 100%;
276
+ border-radius: var(--an-w-radius);
277
+ }
278
+ .an-w-image figcaption {
279
+ color: var(--an-w-muted);
280
+ font-size: 0.9em;
281
+ margin-top: 4px;
282
+ }
283
+
284
+ .an-w-list ul,
285
+ .an-w-list ol {
286
+ list-style: none;
287
+ padding: 0;
288
+ }
289
+ .an-w-list li {
290
+ display: flex;
291
+ align-items: baseline;
292
+ gap: 8px;
293
+ padding: 6px 0;
294
+ border-bottom: 1px solid var(--an-w-line);
295
+ }
296
+ .an-w-list li:last-child {
297
+ border-bottom: 0;
298
+ }
299
+ .an-w-list li > div {
300
+ flex: 1;
301
+ display: flex;
302
+ flex-direction: column;
303
+ }
304
+ .an-w-list li span,
305
+ .an-w-list li small {
306
+ color: var(--an-w-muted);
307
+ }
308
+ .an-w-dot {
309
+ flex: none;
310
+ width: 8px;
311
+ height: 8px;
312
+ border-radius: 50%;
313
+ background: var(--an-w-info);
314
+ }
315
+ .an-w-dot.is-high {
316
+ background: var(--an-w-high);
317
+ }
318
+ .an-w-dot.is-medium {
319
+ background: var(--an-w-medium);
320
+ }
321
+ .an-w-dot.is-good {
322
+ background: var(--an-w-good);
323
+ }
324
+
325
+ .an-w-note {
326
+ display: flex;
327
+ flex-direction: column;
328
+ gap: 2px;
329
+ padding: 8px 10px;
330
+ border-radius: var(--an-w-radius);
331
+ background: var(--an-w-tint);
332
+ }
333
+
334
+ .an-w-badge {
335
+ align-self: flex-start;
336
+ display: inline-block;
337
+ padding: 2px 8px;
338
+ border-radius: 999px;
339
+ font-size: 0.85em;
340
+ font-weight: 500;
341
+ background: var(--an-w-tint);
342
+ }
343
+ .an-w-badge.is-high {
344
+ color: var(--an-w-high);
345
+ }
346
+ .an-w-badge.is-medium {
347
+ color: var(--an-w-medium);
348
+ }
349
+ .an-w-badge.is-good {
350
+ color: var(--an-w-good);
351
+ }
352
+ .an-w-badge.is-info,
353
+ .an-w-badge.is-low {
354
+ color: var(--an-w-info);
355
+ }
356
+
357
+ .an-w-link {
358
+ color: var(--an-w-info);
359
+ font-weight: 500;
360
+ }
361
+
362
+ .an-w-button {
363
+ align-self: flex-start;
364
+ font: inherit;
365
+ padding: 6px 14px;
366
+ border-radius: var(--an-w-radius);
367
+ border: 1px solid var(--an-w-accent);
368
+ cursor: pointer;
369
+ }
370
+ .an-w-button.is-primary {
371
+ background: var(--an-w-accent);
372
+ color: var(--an-w-accent-ink);
373
+ }
374
+ .an-w-button.is-secondary {
375
+ background: transparent;
376
+ color: var(--an-w-ink);
377
+ border-color: var(--an-w-line);
378
+ }
379
+ .an-w-button:disabled,
380
+ .an-w-suggest button:disabled {
381
+ cursor: default;
382
+ opacity: 0.6;
383
+ }
384
+
385
+ .an-w-suggest {
386
+ display: flex;
387
+ flex-wrap: wrap;
388
+ gap: 6px;
389
+ }
390
+ .an-w-suggest button {
391
+ font: inherit;
392
+ padding: 6px 12px;
393
+ border-radius: 999px;
394
+ border: 1px solid var(--an-w-line);
395
+ background: transparent;
396
+ color: var(--an-w-ink);
397
+ cursor: pointer;
398
+ text-align: left;
399
+ }
400
+ .an-w-suggest button:hover:not(:disabled) {
401
+ border-color: var(--an-w-muted);
402
+ }
403
+
404
+ .an-w-divider {
405
+ border: 0;
406
+ border-top: 1px solid var(--an-w-line);
407
+ margin: 2px 0;
408
+ width: 100%;
409
+ }
package/react.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Widget, WidgetPart } from './types.ts';
3
+ export { placeWidgets, type ReplyPiece } from './widgets.ts';
4
+ /** Every kind of part an agent may draw. */
5
+ export type WidgetKind = 'Column' | 'Row' | 'Card' | 'Text' | 'Stat' | 'KeyValue' | 'Table' | 'Chart' | 'Image' | 'List' | 'Note' | 'Badge' | 'Link' | 'Button' | 'Suggestions' | 'Divider';
6
+ /** What a replacement for one kind of part receives. */
7
+ export interface PartProps {
8
+ part: WidgetPart;
9
+ /** The part's children, already drawn (containers only). */
10
+ children?: ReactNode;
11
+ /** Send words as the reader's next message (Button, Suggestions). */
12
+ onAsk?: ((text: string) => void) | undefined;
13
+ }
14
+ export interface WidgetViewProps {
15
+ widget: Widget;
16
+ /** Called with a Button's label or a tapped suggestion: send it as the next message. */
17
+ onAsk?: (text: string) => void;
18
+ /** Your own component for any kind of part, in place of the default. */
19
+ parts?: Partial<Record<WidgetKind, (props: PartProps) => ReactNode>>;
20
+ /** ISO currency code for charts and numbers marked as money. Default "USD". */
21
+ currency?: string;
22
+ /** Locale for numbers. Default: the reader's. */
23
+ locale?: string;
24
+ /** Whether a link or picture address may be opened. Default: https:// only. */
25
+ allowUrl?: (url: string, use: 'link' | 'image') => boolean;
26
+ className?: string;
27
+ }
28
+ export interface ReplyViewProps extends Omit<WidgetViewProps, 'widget' | 'className'> {
29
+ /** The reply's words, as `text` events or `done.reply` give them. */
30
+ text: string;
31
+ /** The reply's widgets, as `widget` events or `done.widgets` give them. */
32
+ widgets?: readonly Widget[] | undefined;
33
+ /** How to draw the words. Default: paragraphs. Pass your markdown renderer here. */
34
+ renderText?: (text: string) => ReactNode;
35
+ className?: string;
36
+ }
37
+ /** One widget, drawn from its root part. Draws nothing when there is no root yet. */
38
+ export declare function WidgetView(props: WidgetViewProps): import("react/jsx-runtime").JSX.Element | null;
39
+ /** A whole reply: its words, with each widget drawn where it sits. */
40
+ export declare function ReplyView({ text, widgets, renderText, className, ...widgetProps }: ReplyViewProps): import("react/jsx-runtime").JSX.Element;