@captra-ai/cli 0.3.0 → 0.5.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/package.json +1 -1
- package/templates/fullstack/package.json +1 -1
- package/templates/fullstack/src/App.tsx +6 -5
- package/templates/fullstack/src/capabilities/hello.test.ts +1 -1
- package/templates/fullstack/src/capabilities/hello.tsx +9 -11
- package/templates/fullstack/src/hello-ai.css +2 -0
- package/templates/fullstack/src/server.ts +1 -1
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { useCallback,useState } from 'react'
|
|
2
2
|
import { CapabilityOutput } from 'captra/react'
|
|
3
|
-
import type
|
|
3
|
+
import { CaptraDevtools,type DevtoolsReplay } from 'captra/devtools'
|
|
4
|
+
import type { CaptraEvent,RemoteCapabilityResult } from 'captra'
|
|
4
5
|
import { HelloCard } from './capabilities/hello.js'
|
|
5
6
|
import { client } from './client.js'
|
|
6
7
|
import { BrandIntro } from './BrandIntro.js'
|
|
8
|
+
import './hello-ai.css'
|
|
7
9
|
|
|
8
10
|
export default function App(){
|
|
9
|
-
const [intro,setIntro]=useState(true)
|
|
10
|
-
const
|
|
11
|
-
const run=async()=>{setPending(true);setError('');try{setResult(await client.execute('hello',{name}))}catch(caught){setError(caught instanceof Error?caught.message:'Run failed')}finally{setPending(false)}}
|
|
11
|
+
const [intro,setIntro]=useState(true),[message,setMessage]=useState('I am building a fintech dashboard'),[result,setResult]=useState<RemoteCapabilityResult|null>(null),[events,setEvents]=useState<CaptraEvent[]>([]),[replay,setReplay]=useState<DevtoolsReplay|null>(null),[pending,setPending]=useState(false),[error,setError]=useState('')
|
|
12
|
+
const run=async()=>{setPending(true);setError('');setResult(null);setReplay(null);setEvents([]);try{for await(const item of client.stream('hello-ai',{message})){if(item.event==='trace')setEvents(current=>[...current,item.data]);if(item.event==='result'){setResult(item.data);setReplay(await client.replay(item.data.run.id))}if(item.event==='error')throw new Error(item.data.message)}}catch(caught){setError(caught instanceof Error?caught.message:'Run failed')}finally{setPending(false)}}
|
|
12
13
|
const closeIntro=useCallback(()=>setIntro(false),[])
|
|
13
|
-
return <>{intro&&<BrandIntro onComplete={closeIntro}/>}<main className="shell"><div className="topline"><div className="eyebrow">CAPTRA /
|
|
14
|
+
return <>{intro&&<BrandIntro onComplete={closeIntro}/>}<main className="shell"><div className="topline"><div className="eyebrow">CAPTRA / HELLO AI WORLD</div><button onClick={()=>setIntro(true)}>Replay intro</button></div><h1>Your first agent.<br/>Fully inspectable.</h1><p className="intro">This starter runs Captra’s real agent loop, structured output, scoped memory, tool calls, streaming events, and Devtools. It works in demo mode now; add <code>OPENAI_API_KEY</code> for a live model.</p><section className="runner"><label htmlFor="message">What are you building?</label><textarea id="message" value={message} onChange={event=>setMessage(event.target.value)}/><button disabled={pending||message.trim().length<2} onClick={run}>{pending?'Agent running…':'Run Hello AI →'}</button></section>{pending&&<p className="live">● Live trace · {events.at(-1)?.type??'starting'}</p>}{error&&<p className="error">{error}</p>}{result&&<CapabilityOutput result={result} views={{HelloCard}}/>}<div className="devtools"><CaptraDevtools replay={replay}/></div><footer><span>React client :5173</span><span>Captra runtime :8787</span><span>Demo model · set OPENAI_API_KEY for live AI</span></footer></main></>
|
|
14
15
|
}
|
|
@@ -4,4 +4,4 @@ import { hello } from './hello.js'
|
|
|
4
4
|
|
|
5
5
|
const runtime=createRuntime(defineApp({name:'Test',scopes:['user'],capabilities:[hello],surface:capabilityViews()}))
|
|
6
6
|
const identity={tenantId:'test',userId:'u1',projectId:'p1'}
|
|
7
|
-
describe('hello',()=>{it('
|
|
7
|
+
describe('hello-ai',()=>{it('runs an agent, uses a tool, and recalls memory',async()=>{const first=await runtime.execute({capability:'hello-ai',input:{message:'I am building a fintech dashboard'},identity});expect(first.value).toMatchObject({provider:'demo',remembered:true});expect(first.trace.some(event=>event.type==='tool.completed')).toBe(true);const second=await runtime.execute({capability:'hello-ai',input:{message:'I am building a fintech dashboard'},identity});expect(second.value).toMatchObject({provider:'demo',remembered:true});expect(second.trace.some(event=>event.type==='memory.recalled'&&event.detail==='1 records')).toBe(true)})})
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { agent,defineTool,type CapabilityViewProps,type ModelAdapter } from 'captra'
|
|
2
|
+
import { createOpenAI } from 'captra/openai'
|
|
2
3
|
import { z } from 'zod'
|
|
3
4
|
|
|
4
|
-
const HelloResult=z.object({
|
|
5
|
+
const HelloResult=z.object({reply:z.string(),remembered:z.boolean(),provider:z.enum(['demo','openai'])})
|
|
5
6
|
type HelloResult=z.infer<typeof HelloResult>
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const rememberContext=defineTool({name:'starter.remember',description:'Remember one useful fact about what the developer is building.',input:z.object({fact:z.string().min(1)}),output:z.object({saved:z.literal(true)}),async execute({fact},context){await context.memory.remember({tenantId:context.tenantId,scope:'user',ownerId:context.userId,type:'fact',content:fact,confidence:1});return {saved:true as const}}})
|
|
9
|
+
const demoModel:ModelAdapter={async generate(request){const user=[...request.messages].reverse().find(item=>item.role==='user'),input=JSON.parse(user?.content??'{}') as {message?:string},message=input.message??'Hello AI',system=request.messages.find(item=>item.role==='system')?.content??'',usedTool=request.messages.some(item=>item.role==='tool'),remembered=system.includes(message);if(!remembered&&!usedTool)return {text:'',model:'captra-demo',usage:{inputTokens:24,outputTokens:12},toolCalls:[{id:'remember-1',name:'starter.remember',input:{fact:message}}]};return {text:JSON.stringify({reply:`I’m running through Captra’s agent loop. A strong first feature for “${message}” is a typed assistant that remembers context and shows every tool call.`,remembered:remembered||usedTool,provider:'demo'}),model:'captra-demo',usage:{inputTokens:42,outputTokens:38}}}}
|
|
10
|
+
const environment=(globalThis as {process?:{env?:Record<string,string|undefined>}}).process?.env,provider=environment?.OPENAI_API_KEY&&environment.NODE_ENV!=='test'?'openai':'demo',model=provider==='openai'?createOpenAI({model:environment?.OPENAI_MODEL??'gpt-5-mini'}):demoModel
|
|
9
11
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
memory:['user'],approval:'never',
|
|
14
|
-
async run({name},context){const remembered=context.memories.some(item=>item.content===name);if(!remembered)await context.memory.remember({tenantId:context.tenantId,scope:'user',ownerId:context.userId,type:'preference',content:name,confidence:1});return {message:`Hello ${name}`,remembered}},
|
|
15
|
-
view:HelloCard,
|
|
16
|
-
})
|
|
12
|
+
export function HelloCard({value}:CapabilityViewProps<HelloResult>){return <article className="result"><span>HELLO AI WORLD · {value.provider.toUpperCase()}</span><h2>{value.reply}</h2><p>{value.remembered?'Captra used or saved scoped memory for this run.':'No memory was needed.'}</p></article>}
|
|
13
|
+
HelloCard.displayName='HelloCard'
|
|
14
|
+
export const hello=agent({name:'hello-ai',description:'Run a real typed agent with scoped memory and an inspectable trace.',instructions:`You are the first Captra agent. Give one concise, useful suggestion based on the developer's message. If the exact message is not already in relevant memory, call starter.remember once. Return JSON with reply, remembered, and provider set to ${provider}.`,input:z.object({message:z.string().min(2)}),output:HelloResult,memory:['user'],tools:[rememberContext],budget:{maxSteps:3,maxToolCalls:1,maxTokens:1_200},approval:'never',model,view:HelloCard})
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.runner{display:grid;gap:12px}.runner textarea{min-height:100px;resize:vertical;border:1px solid #4b4e46;border-radius:10px;background:#171916;color:white;padding:14px;font:16px/1.5 inherit;outline:none}.runner textarea:focus{border-color:#22d3ee}.runner>button{min-height:50px}.live{color:#22d3ee;font:12px 'DM Mono'}.devtools{margin-top:18px}.devtools>section{padding:22px;border:1px solid #3c3f38;border-radius:18px;background:#111713;color:#dce4dc}.devtools header{display:flex;justify-content:space-between;gap:16px}.devtools header div{display:flex;gap:12px}.devtools header span,.devtools summary small{color:#22d3ee}.devtools dl{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.devtools dl div{padding:12px;background:#1c231e;border-radius:10px}.devtools dt{color:#88948b;font:10px 'DM Mono'}.devtools dd{margin:5px 0 0;font-weight:800}.devtools details{border-top:1px solid #303a32;padding:14px 0}.devtools summary{cursor:pointer;font-weight:700}.devtools li{margin:12px 0}.devtools time{color:#768178;font:10px 'DM Mono'}.devtools p{margin:4px 0;color:#aab5ac}.devtools pre{overflow:auto;padding:10px;background:#080c09;border-radius:8px;font:11px 'DM Mono'}
|
|
2
|
+
@media(max-width:620px){.devtools dl{grid-template-columns:repeat(2,1fr)}}
|
|
@@ -5,6 +5,6 @@ import { createNodeHandler } from 'captra/server/node'
|
|
|
5
5
|
import { app } from './captra.app.js'
|
|
6
6
|
|
|
7
7
|
const runtime=createRuntime(app)
|
|
8
|
-
const handler=createCaptraHandler(runtime,{basePath:'/api/captra',auth:async()=>({tenantId:'local',userId:'developer',projectId:'starter'})})
|
|
8
|
+
const handler=createCaptraHandler(runtime,{basePath:'/api/captra',rateLimit:{limit:30,windowMs:60_000},auth:async()=>({tenantId:'local',userId:'developer',projectId:'starter'})})
|
|
9
9
|
const port=Number(process.env.PORT??8787)
|
|
10
10
|
createServer(createNodeHandler(handler)).listen(port,'127.0.0.1',()=>console.log(`Captra backend ready on http://127.0.0.1:${port}/api/captra`))
|