{"_id":"@buerli.io/ai","name":"@buerli.io/ai","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@buerli.io/ai","version":"0.0.1","description":"Portable AI agent panel for buerli-based CAD applications","license":"MIT","keywords":["buerli","classcad","cad","ai","agent","llm","react","three"],"type":"module","sideEffects":false,"repository":{"type":"git","url":"git+https://github.com/awv-informatik/buerli-ai.git"},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"bin":{"copilot-proxy":"bin/copilot-proxy.mjs"},"engines":{"node":">=18"},"scripts":{"clean":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"","build":"npm run clean && tsc -p tsconfig.build.json","dev":"tsc -p tsconfig.build.json --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","proxy":"node bin/copilot-proxy.mjs","proxy:auth":"node bin/copilot-proxy.mjs auth","proxy:models":"node bin/copilot-proxy.mjs models"},"publishConfig":{"access":"public"},"peerDependencies":{"@buerli.io/classcad":"*","@buerli.io/core":"*","@react-three/fiber":">=8","react":">=18","zustand":">=4"},"dependencies":{"@classcad/skill":"0.x"},"devDependencies":{"@buerli.io/classcad":"1.0.1","@buerli.io/core":"1.0.1","@react-three/fiber":"8.15.14","@types/react":"^18.0.0","@types/react-dom":"^18.0.0","react":"^18.3.1","typescript":"^5.1.0","zustand":"^4.5.7"},"homepage":"https://github.com/awv-informatik/buerli-ai#readme","bugs":{"url":"https://github.com/awv-informatik/buerli-ai/issues"},"_id":"@buerli.io/ai@0.0.1","_nodeVersion":"25.6.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-D5PQkKiyFkNmnxZzuwBpz21wN3lSAkw3gx1ArcCuIkw5y5heHHIeMsWkX6KEliyP4WuePwbavS6uEbjqMjcD/Q==","shasum":"8d7de37384cd9512e272fbac57483860db4c0729","tarball":"https://registry.npmjs.org/@buerli.io/ai/-/ai-0.0.1.tgz","fileCount":88,"unpackedSize":447842,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH/FA0kn95MhSxhKr9Lp9DIbCoS8rOPhuYC6yjjKUlR3AiASNtNaVjz+3d7QSDonEb2P1X5su3FmEnhyeoeacltN3Q=="}]},"_npmUser":{"name":"drcmda","email":"drcmda@gmail.com"},"directories":{},"maintainers":[{"name":"dm385","email":"daniel.manser@awv-informatik.ch"},{"name":"drcmda","email":"drcmda@gmail.com"},{"name":"awv-build","email":"it@awv-informatik.ch"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai_0.0.1_1781272181480_0.711518742063183"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-12T13:49:41.351Z","0.0.1":"2026-06-12T13:49:41.676Z","modified":"2026-06-12T13:49:41.890Z"},"maintainers":[{"name":"dm385","email":"daniel.manser@awv-informatik.ch"},{"name":"drcmda","email":"drcmda@gmail.com"},{"name":"awv-build","email":"it@awv-informatik.ch"}],"description":"Portable AI agent panel for buerli-based CAD applications","homepage":"https://github.com/awv-informatik/buerli-ai#readme","keywords":["buerli","classcad","cad","ai","agent","llm","react","three"],"repository":{"type":"git","url":"git+https://github.com/awv-informatik/buerli-ai.git"},"bugs":{"url":"https://github.com/awv-informatik/buerli-ai/issues"},"license":"MIT","readme":"# @buerli.io/ai\n\nAI agent for buerli/ClassCAD apps. Drops a chat panel into your app that creates and\nmodifies 3D geometry through natural language — any tool-calling LLM, all CAD operations\nexecuted locally in the browser.\n\n## Install\n\n```bash\nnpm install @buerli.io/ai\n```\n\nPeers (your buerli app has them already): `@buerli.io/classcad`, `@buerli.io/core`,\n`@react-three/fiber` ≥8, `react` ≥18, `zustand` ≥4.\n\n## Setup — provider & models\n\nThe agent talks to any LLM through a small `LLMProvider` interface. Pick one:\n\n| You have | Use |\n| --- | --- |\n| GitHub Copilot subscription | bundled `copilot-proxy` + `createAutoProvider` (below) |\n| OpenAI / Azure key | `createAutoProvider({ apiKey, endpoint? })` |\n| Anthropic key | `createAnthropicProvider({ apiKey })` |\n| Local AI (Ollama, LM Studio, vLLM) | `createOpenAIProvider({ endpoint: 'http://localhost:11434/v1/chat/completions', model })` |\n| Something else | implement `LLMProvider` (one `chat()` method — see Custom integration) |\n\n`createAutoProvider` is the recommended default: it reads the endpoint's `/models` and\nroutes each model to the right API surface automatically (`gpt-5.x`/codex → Responses,\nClaude/Gemini → Chat Completions), and discovers per-model context windows and thinking\nlevels — so the panel's model/thinking pickers just work. Endpoints without `/models`\nfall back to Chat Completions. (The single-surface adapters it wraps —\n`createOpenAIProvider`, `createResponsesProvider` — are exported too if you want to pin\none surface.)\n\n### GitHub Copilot (development)\n\nThe browser can't call `api.githubcopilot.com` (no CORS, short-lived session tokens), so\nthe package ships a tiny local proxy:\n\n```bash\nnpx copilot-proxy auth     # one-time GitHub device-flow login; scaffolds ./.env.local\nnpx copilot-proxy models   # list models your plan exposes\nnpx copilot-proxy          # run on http://localhost:8788\n```\n\n`auth` writes a ready-to-edit `./.env.local`:\n\n```bash\nAI_AGENT_API_KEY=copilot-proxy            # placeholder; the proxy injects the real token\nAI_AGENT_ENDPOINT=http://localhost:8788/v1\nAI_AGENT_MODEL=gpt-5.5                    # default; the panel picker can switch any time\nAI_AGENT_REASONING_EFFORT=medium          # default thinking level\nCOPILOT_OAUTH_TOKEN=ghu_…                 # SERVER-ONLY secret — never bundle into the client\n```\n\n> `COPILOT_OAUTH_TOKEN` must never reach the browser. If your app forwards `.env.local`\n> into client code (Vite `import.meta.env`, Docusaurus `customFields`), expose an\n> **allowlist** of safe keys — never the whole file. Keep `.env.local` gitignored.\n> Port override: `COPILOT_PROXY_PORT`.\n\n`createCopilotProvider({ token })` also exists for Node/VS Code contexts where you\nalready hold a Copilot session token (it does not work in the browser).\n\n## Usage (React / react-three-fiber)\n\n```tsx\nimport { createCadAgent, createAutoProvider, initAgentAsync } from '@buerli.io/ai'\nimport { useBuerli, BuerliGeometry } from '@buerli.io/react'\nimport { Canvas } from '@react-three/fiber'\n\nawait initAgentAsync() // once at startup — loads bundled ClassCAD docs + method registry\n\nconst { AgentPanel, AgentCanvas } = createCadAgent({\n  provider: createAutoProvider({ apiKey: ENV.AI_AGENT_API_KEY, endpoint: ENV.AI_AGENT_ENDPOINT }),\n  modelName: ENV.AI_AGENT_MODEL,            // default model (picker can change it)\n  reasoningEffort: 'medium',                // default thinking level\n  // maxTokens, contextLimit — optional fallbacks; per-model values are auto-discovered\n  // maxIterations: 25                      — tool-loop cap\n  // systemPrompt / extraContext            — see Custom prompts\n})\n\nfunction App() {\n  const drawingId = useBuerli((s) => s.drawing.active || '')\n  const [open, setOpen] = useState(true)\n  return (\n    <div style={{ position: 'relative', width: '100%', height: '100%' }}>\n      <Canvas>\n        <AgentCanvas />                      {/* invisible — lets the agent snapshot the view */}\n        <BuerliGeometry drawingId={drawingId} />\n      </Canvas>\n      <AgentPanel drawingId={drawingId} open={open} onClose={() => setOpen(false)} />\n    </div>\n  )\n}\n```\n\n`<AgentPanel>` props: `drawingId` (required), `open`, `onClose`,\n`position` (`'right' | 'left' | 'bottom'`), `className`, `theme`\n(`{ bg, text, accent, userBubble, assistantBubble, width }`), and `extraContext`\n(per-mount domain prompt — overrides the factory default, useful when one agent serves\nseveral screens).\n\nWhat you get, all capability-driven (each control only appears when the provider/model\nactually supports it):\n\n- **Model picker + thinking picker + context ring** in the footer — fed by the\n  provider's `/models` discovery.\n- **Attachments** — images (vision) and CAD files (STEP/IGES/STL…) via the `+` button.\n- **Stop** to abort a running turn; **per-tool status chips** with results and errors.\n- **Source panel** (`</>` in the header) — the session as a runnable, syntax-highlighted\n  buerli script: runtime IDs threaded into variables, preconditions and failures as\n  comments, one-click copy.\n\n## Custom prompts\n\nThe default system prompt makes the agent a general ClassCAD expert. Add your domain on\ntop with `extraContext` (recommended — keeps the base expertise), or replace the whole\nprompt with `systemPrompt`:\n\n```tsx\ncreateCadAgent({\n  provider,\n  extraContext: `## This app: parametric pipe runs\nSegments are named Default, Pipe1, …; expressions: length, outerDiam, thickness.\nAngles in radians (UI shows degrees). Always recalc after edits.`,\n})\n```\n\nTeach it your model's structure, naming, units, and the exact calls for common\noperations — the more concrete, the fewer tool-discovery turns the agent needs.\nWhen replacing `systemPrompt`, you can compose with the exported\n`DEFAULT_SYSTEM_PROMPT` to keep the base ClassCAD expertise.\n\n## What the agent can do (tools)\n\n| Tool | Purpose |\n| --- | --- |\n| `call_api` | Any `v1.<domain>.<method>` ClassCAD call (also `facade.*` and drawing APIs) |\n| `call_api_batch` | Many calls in one turn; later calls reference earlier results (`\"$0.id\"`) |\n| `tree` / `find` / `inspect` | Read the structure tree, search nodes, full node detail |\n| `get_selection` / `set_selection` | Read or set the user's 3D selection |\n| `list_methods` / `describe_method` | Discover and document API methods (bundled docs) |\n| `snapshot` | See the 3D viewport (PNG → vision) |\n| `load_file` | Import a user-attached CAD file |\n| `download` | Export STEP/STL/OFB as a download button in the chat |\n| `delegate` | Hand a sub-task to a specialist sub-agent |\n\nEverything executes in the browser against the buerli API — no extra server for CAD.\n(`TOOL_SCHEMAS` and `executeTool` are exported for tests or custom executors.)\n\n## Production\n\nNever ship API keys in the browser. Point the provider at your backend and authenticate\nyour users there:\n\n```ts\n// client\ncreateAutoProvider({ apiKey: userSessionToken, endpoint: 'https://your-app.com/api/ai' })\n// server: verify the user, attach the real provider key, forward the body verbatim.\n```\n\nThe bundled Copilot proxy is a development convenience, not a multi-user production\ngateway.\n\n## Custom integration (your own UI)\n\n`<AgentPanel>` is a thin view over `createAgentStore()` — build your own panel on the\nsame store and keep the full agent (tools, batching, attachments, cancel):\n\n```tsx\nimport { createAgentStore, createAutoProvider, initAgentAsync } from '@buerli.io/ai'\nimport type { AgentConfig, UIMessage } from '@buerli.io/ai'\n\nawait initAgentAsync()\nconst useAgent = createAgentStore() // zustand — React hook AND vanilla store\n\nconst config: AgentConfig = {\n  provider: createAutoProvider({ apiKey: '…', endpoint: '…' }),\n  drawingId,\n  model: 'gpt-5.4',            // optional per-message override\n  reasoningEffort: 'none',     // optional\n  extraContext: MY_PROMPT,     // optional\n}\n\nfunction MyPanel() {\n  const messages = useAgent((s) => s.messages)   // UIMessage[]\n  const isRunning = useAgent((s) => s.isRunning)\n  const error = useAgent((s) => s.error)\n  const usage = useAgent((s) => s.usage)         // { inputTokens, outputTokens }\n\n  const send = (text: string) => useAgent.getState().sendMessage(text, config)\n  // attachments: sendMessage(text, config, images?: ImageInput[], files?: FileAttachment[])\n\n  return (\n    <>\n      {messages.map((m: UIMessage, i) => {\n        if (m.type === 'assistant') return <Md key={i} text={m.text} />\n        if (m.type === 'thinking') return <Collapsed key={i} text={m.text} />\n        // m.type === 'tool': { name, label, status: 'running'|'done'|'error',\n        //                      detail?, image? (snapshot data-url), download? }\n        return <ToolChip key={i} {...m} />\n      })}\n      {error && <Error text={error} />}\n      <Input onSubmit={send} disabled={isRunning} />\n      {isRunning && <button onClick={() => useAgent.getState().stop()}>Stop</button>}\n    </>\n  )\n}\n```\n\n- Non-React: same store via `useAgent.getState()` / `useAgent.subscribe()`.\n- `useAgent((s) => s.codeLog)` (`CodeEvent[]`) holds the API-call log for a source view.\n- `reset()` clears the conversation.\n\n**Snapshots without `<AgentCanvas>`** — register a capturer once and the `snapshot` tool\nworks with any viewer:\n\n```ts\nimport { setSnapshotCapturer, createCanvasCapturer } from '@buerli.io/ai'\nsetSnapshotCapturer(createCanvasCapturer(myCanvasElement)) // or a custom SnapshotCapturer → base64 PNG\n```\n\n**Model/thinking pickers** — `provider.getCapabilities()` returns\n`{ models: ModelOption[] }` (ids, context limits, reasoning levels); render your own\npicker and pass the choice as `config.model` / `config.reasoningEffort`.\n\n**Fully headless** — drive the raw event loop, no store, no React:\n\n```ts\nimport { runAgentLoop } from '@buerli.io/ai'\nfor await (const ev of runAgentLoop('Create a 50mm box', [], config)) {\n  // ev.type: 'text' | 'thinking' | 'tool_start' | 'tool_end'\n  //        | 'subagent_start' | 'subagent_end' | 'usage' | 'error' | 'done'\n}\n```\n\n**Custom provider** — one method; must support tool-use blocks. Optionally add\n`getCapabilities()` to power the pickers:\n\n```ts\nconst myProvider: LLMProvider = {\n  async chat({ system, messages, tools, max_tokens, model, reasoningEffort }) {\n    // call your LLM; return { content: ContentBlock[], stop_reason: 'end_turn' | 'tool_use' }\n  },\n}\n```\n\n`initAgent({ skillBundle, methodRegistry })` is the synchronous init variant — pass the\nJSONs yourself (`import bundle from '@classcad/skill/bundle.json' with { type: 'json' }`)\nif you can't await at startup or run in pure-Node ESM.\n\n## Development\n\n```bash\nnpm run typecheck\nnpm run build        # clean + tsc → dist/ — runs automatically on publish\n```\n\nAll ClassCAD knowledge (the doc bundle and the method registry, both generated in the\nclasscad-skill repo from `@classcad/api-js`) ships in the **`@classcad/skill`** runtime\ndependency — `initAgentAsync()` imports it from there, so there is nothing to re-bundle\nor vendor here. Bump that dependency to pick up new docs.\n","readmeFilename":"README.md","_rev":"1-bf7395fb16a3755a42d7d8ca51d842e4"}