@lunora/agent 1.0.0-alpha.124 → 1.0.0-alpha.126

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,5 +1,5 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-B8WZl1rU.mjs";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-B8WZl1rU.mjs";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-B_VV-x4x.mjs";
2
+ export type { I as InboundChannelEvent } from "./packem_shared/types.d-B_VV-x4x.mjs";
3
3
  import '@lunora/mail/inbound';
4
4
  import 'ai';
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-B8WZl1rU.js";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-B_VV-x4x.js";
2
+ export type { I as InboundChannelEvent } from "./packem_shared/types.d-B_VV-x4x.js";
3
3
  import '@lunora/mail/inbound';
4
4
  import 'ai';
5
5
  /**
@@ -1,2 +1,2 @@
1
- import{LunoraError as g}from"@lunora/errors";import{defineTable as v,initLunora as D,defineSchemaExtension as j}from"@lunora/server";import{v as e}from"@lunora/values";import{a as q,A as _,b as P,d as F}from"./packem_shared/component-shared-G8ngerkU.mjs";import{graphTables as G,graphComponent as H}from"./packem_shared/normalizeEntityName-k3fjicAG.mjs";import{normalizeEntityName as pe}from"./packem_shared/normalizeEntityName-k3fjicAG.mjs";import{sandboxComponent as we}from"./packem_shared/sandboxComponent-BtOkUYpY.mjs";const k="agent_episodes",X=5,Q=20,Y=500,x=200,z=/\s+/gu,V=u=>u===void 0||!Number.isFinite(u)?X:Math.min(Q,Math.max(1,Math.trunc(u))),W={episodes:v({createdAt:e.number(),messageKey:e.string(),owner:e.string(),summary:e.string(),threadKey:e.optional(e.string())}).index("byOwnerCreatedAt",["owner","createdAt"]).index("byOwnerMessageKey",["owner","messageKey"],{unique:!0}).public()},{mutation:J,query:Z}=D.dataModel().create(),ee=()=>{const u=J.input({createdAt:e.optional(e.number()),messageKey:e.string(),owner:e.string(),summary:e.string(),threadKey:e.optional(e.string())}).mutation(async({args:s,ctx:c})=>{const h=s.summary.replaceAll(z," ").trim().slice(0,Y);if(h.length===0)return{recorded:!1};if(await c.db.query(k).withIndex("byOwnerMessageKey",p=>p.eq("owner",s.owner).eq("messageKey",s.messageKey)).first())return{recorded:!1};await c.db.insert(k,{createdAt:s.createdAt??Date.now(),messageKey:s.messageKey,owner:s.owner,summary:h,...s.threadKey===void 0?{}:{threadKey:s.threadKey}});const w=await c.db.query(k).withIndex("byOwnerCreatedAt",p=>p.eq("owner",s.owner)).order("asc").take(x+1);for(const p of w.slice(0,Math.max(0,w.length-x)))await c.db.delete(p._id);return{recorded:!0}}),o=Z.input({limit:e.optional(e.number()),owner:e.string()}).query(async({args:s,ctx:c})=>{const h=await c.db.query(k).withIndex("byOwnerCreatedAt",b=>b.eq("owner",s.owner)).order("desc").take(V(s.limit));return h.length===0?{context:""}:{context:h.toReversed().map(b=>`- ${b.summary}`).join(`
2
- `)}});return{agentEpisodeRecall:q(o),agentEpisodeUpsert:q(u)}},S="threads",$="messages",M="run_queue",y=`${_}_${S}`,E=`${_}_${$}`,A=`${_}_${M}`,N=5,te=780*60*1e3,ne=j(_,{tables:{[$]:v({content:e.string(),createdAt:e.number(),messageKey:e.string(),role:e.union(e.literal("user"),e.literal("assistant"),e.literal("tool"),e.literal("system")),seq:e.number(),status:e.optional(e.union(e.literal("awaiting_approval"),e.literal("approved"),e.literal("rejected"))),stepName:e.optional(e.string()),threadKey:e.string(),toolCallId:e.optional(e.string()),toolCalls:e.optional(e.array(e.object({id:e.string(),input:e.any(),name:e.string()}))),toolName:e.optional(e.string())}).index("byThread",["threadKey","seq"]).index("byMessageKey",["threadKey","messageKey"],{unique:!0}).public(),[S]:v({agent:e.string(),createdAt:e.number(),error:e.optional(e.string().nullable()),completedInstanceId:e.optional(e.string()),instanceId:e.optional(e.string()),key:e.string(),messageCount:e.number(),owner:e.optional(e.string()),state:e.optional(e.any()),status:e.union(e.literal("idle"),e.literal("running"),e.literal("error"),e.literal("cancelled"),e.literal("awaiting_input")),title:e.optional(e.string()),updatedAt:e.number(),usage:e.optional(e.object({inputTokens:e.optional(e.number()),outputTokens:e.optional(e.number()),totalTokens:e.optional(e.number())}))}).index("byKey",["key"],{unique:!0}).index("byAgent",["agent"]).index("byInstance",["instanceId"]).public(),[M]:v({agent:e.string(),enqueuedAt:e.number(),instanceId:e.string(),position:e.number(),threadKey:e.string()}).index("byThread",["threadKey","position"]).index("byThreadInstance",["threadKey","instanceId"],{unique:!0}).public(),...G,...W}}),{mutation:f,query:T}=D.dataModel().create(),ie=async(u,o,s)=>{if(o.instanceId===void 0)throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" already has a run in flight (instance "${o.priorInstanceId}") — cannot queue a dispatch with no instance id`);const{instanceId:c}=o,h=await u.query(A).withIndex("byThreadInstance",p=>p.eq("threadKey",o.key).eq("instanceId",c)).first();if(h)return{outcome:"queued",position:h.position};const b=await u.query(A).withIndex("byThread",p=>p.eq("threadKey",o.key)).collect();if(b.length>=N)throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" run queue is full (depth ${String(N)}) — rejecting instance "${c}"`);const w=(b.at(-1)?.position??-1)+1;return await u.insert(A,{agent:o.agent,enqueuedAt:s,instanceId:c,position:w,threadKey:o.key}),{outcome:"queued",position:w}},ae=async(u,o,s)=>{if(o.policy==="queue")return ie(u,{agent:o.agent,instanceId:o.instanceId,key:o.key,priorInstanceId:o.priorInstanceId},s);if(o.policy!=="replace")throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" already has a run in flight (instance "${o.priorInstanceId}") — onConcurrentRun="${o.policy}"`);return await u.patch(o.existingId,{error:null,status:"running",updatedAt:s,...o.instanceId===void 0?{}:{instanceId:o.instanceId}}),{outcome:"replaced",priorInstanceId:o.priorInstanceId}},le=()=>{const u=f.input({agent:e.string(),initialState:e.optional(e.any()),instanceId:e.optional(e.string()),key:e.string(),onConcurrentRun:e.optional(e.union(e.literal("reject"),e.literal("queue"),e.literal("replace"))),owner:e.optional(e.string()),title:e.optional(e.string())}).mutation(async({args:t,ctx:n})=>{const a=Date.now(),i=await n.db.query(y).withIndex("byKey",r=>r.eq("key",t.key)).first();if(i){if(i.owner!==t.owner)throw new Error(`@lunora/agent: thread "${t.key}" belongs to another owner`);const r=i.instanceId,d=typeof i.updatedAt=="number"?i.updatedAt:0,l=i.status==="awaiting_input"?P:te;return!(a-d>l)&&(i.status==="running"||i.status==="awaiting_input")&&r!==void 0&&(t.instanceId===void 0||t.instanceId!==r)?ae(n.db,{agent:t.agent,existingId:i._id,instanceId:t.instanceId,key:t.key,policy:t.onConcurrentRun??"reject",priorInstanceId:r},a):(await n.db.patch(i._id,{error:null,status:"running",updatedAt:a,...t.instanceId===void 0?{}:{instanceId:t.instanceId}}),{outcome:"continued"})}return await n.db.insert(y,{agent:t.agent,createdAt:a,key:t.key,messageCount:0,status:"running",updatedAt:a,...F({instanceId:t.instanceId,owner:t.owner,state:t.initialState,title:t.title})}),{outcome:"created"}}),o=f.input({content:e.string(),messageKey:e.string(),role:e.union(e.literal("user"),e.literal("assistant"),e.literal("tool"),e.literal("system")),status:e.optional(e.union(e.literal("awaiting_approval"),e.literal("approved"),e.literal("rejected"))),stepName:e.optional(e.string()),threadKey:e.string(),toolCallId:e.optional(e.string()),toolCalls:e.optional(e.array(e.object({id:e.string(),input:e.any(),name:e.string()}))),toolName:e.optional(e.string())}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(E).withIndex("byMessageKey",l=>l.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();if(a)return{seq:a.seq};const i=await n.db.query(y).withIndex("byKey",l=>l.eq("key",t.threadKey)).first();if(!i)throw new Error(`@lunora/agent: cannot append to unknown thread "${t.threadKey}" — run agentEnsureThread first`);const r=i.messageCount,d=Date.now();return await n.db.insert(E,{content:t.content,createdAt:d,messageKey:t.messageKey,role:t.role,seq:r,threadKey:t.threadKey,...t.status===void 0?{}:{status:t.status},...t.stepName===void 0?{}:{stepName:t.stepName},...t.toolCallId===void 0?{}:{toolCallId:t.toolCallId},...t.toolCalls===void 0?{}:{toolCalls:t.toolCalls},...t.toolName===void 0?{}:{toolName:t.toolName}}),await n.db.patch(i._id,{messageCount:r+1,updatedAt:d}),{seq:r}}),s=f.input({messageKey:e.string(),threadKey:e.string()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(E).withIndex("byMessageKey",i=>i.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();a&&await n.db.delete(a._id)}),c=f.input({error:e.optional(e.string()),instanceId:e.optional(e.string()),key:e.optional(e.string()),status:e.optional(e.union(e.literal("idle"),e.literal("running"),e.literal("error"),e.literal("cancelled"),e.literal("awaiting_input"))),title:e.optional(e.string()),usage:e.optional(e.object({inputTokens:e.optional(e.number()),outputTokens:e.optional(e.number()),totalTokens:e.optional(e.number())}))}).mutation(async({args:t,ctx:n})=>{const{instanceId:a,key:i}=t;let r;i!==void 0?r=await n.db.query(y).withIndex("byKey",d=>d.eq("key",i)).first():a!==void 0&&(r=await n.db.query(y).withIndex("byInstance",d=>d.eq("instanceId",a)).first()),r&&await n.db.patch(r._id,{updatedAt:Date.now(),...t.error===void 0?{}:{error:t.error},...t.status===void 0?{}:{status:t.status},...t.title===void 0?{}:{title:t.title},...t.usage===void 0?{}:{usage:t.usage}})}),h=f.input({error:e.optional(e.string()),instanceId:e.string(),key:e.string(),status:e.union(e.literal("idle"),e.literal("error"),e.literal("cancelled")),usage:e.optional(e.object({inputTokens:e.optional(e.number()),outputTokens:e.optional(e.number()),totalTokens:e.optional(e.number())}))}).mutation(async({args:t,ctx:n})=>{const a=Date.now(),i=await n.db.query(y).withIndex("byKey",I=>I.eq("key",t.key)).first();if(i?.instanceId!==t.instanceId){const I=await n.db.query(A).withIndex("byThreadInstance",K=>K.eq("threadKey",t.key).eq("instanceId",t.instanceId)).first();return I&&await n.db.delete(I._id),{}}const l=(i.completedInstanceId===t.instanceId?[]:await n.db.query(A).withIndex("byThread",I=>I.eq("threadKey",t.key)).collect())[0];if(!l)return await n.db.patch(i._id,{completedInstanceId:t.instanceId,status:t.status,updatedAt:a,...t.error===void 0?{}:{error:t.error},...t.usage===void 0?{}:{usage:t.usage}}),{};const m=l.instanceId;return await n.db.delete(l._id),await n.db.patch(i._id,{instanceId:m,status:"running",updatedAt:a,...t.error===void 0?{}:{error:t.error},...t.usage===void 0?{}:{usage:t.usage}}),{dequeued:m}}),b=f.input({key:e.string(),state:e.any()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(y).withIndex("byKey",i=>i.eq("key",t.key)).first();a&&await n.db.patch(a._id,{state:t.state,updatedAt:Date.now()})}),w=(t,n)=>{if(!t)return;const{owner:a}=t;if(!(a!==void 0&&a!==(n.userId??void 0)))return t},p=T.input({key:e.string()}).query(async({args:t,ctx:n})=>{const a=await n.db.query(y).withIndex("byKey",i=>i.eq("key",t.key)).first();return w(a,n.auth)}),O=T.input({key:e.string()}).query(async({args:t,ctx:n})=>{const a=await n.db.query(y).withIndex("byKey",i=>i.eq("key",t.key)).first();return w(a,n.auth)?.state}),L=T.input({key:e.string(),limit:e.optional(e.number())}).query(async({args:t,ctx:n})=>{const a=await n.db.query(y).withIndex("byKey",i=>i.eq("key",t.key)).first();return w(a,n.auth)===void 0?[]:t.limit!==void 0?(await n.db.query(E).withIndex("byThread",r=>r.eq("threadKey",t.key)).order("desc").take(t.limit)).toReversed():n.db.query(E).withIndex("byThread",i=>i.eq("threadKey",t.key)).collect()}),U=f.input({decision:e.union(e.literal("approve"),e.literal("reject")),instanceId:e.string(),note:e.optional(e.string()),threadKey:e.string(),toolCallId:e.string()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(y).withIndex("byKey",m=>m.eq("key",t.threadKey)).first(),i=w(a,n.auth);if(i===void 0)throw new g("FORBIDDEN",`@lunora/agent: not allowed to resolve approvals on thread "${t.threadKey}"`);if(i.instanceId!==t.instanceId)throw new g("FORBIDDEN",`@lunora/agent: instance "${t.instanceId}" does not own thread "${t.threadKey}"`);const r=i.agent,{agents:d}=n,l=d?.[r];if(typeof l?.sendEvent!="function")throw new g("INTERNAL",`@lunora/agent: no ctx.agents["${r}"] producer to resolve the approval — run codegen/dev so the agent binding is wired`);return await l.sendEvent(t.instanceId,{payload:{decision:t.decision,toolCallId:t.toolCallId,...t.note===void 0?{}:{note:t.note}},type:`agent-approval:${t.toolCallId}`}),{resolved:!0}}),B=f.input({agent:e.string(),input:e.string(),threadKey:e.string(),title:e.optional(e.string())}).mutation(async({args:t,ctx:n})=>{const{agents:a}=n,i=a?.[t.agent];if(typeof i?.run!="function")throw new g("INTERNAL",`@lunora/agent: no ctx.agents["${t.agent}"] producer to start a run — run codegen/dev so the agent binding is wired, and check the agent name`);if(i.publicRun!==!0)throw new g("FORBIDDEN",`@lunora/agent: agent "${t.agent}" is not enabled for public runs — set defineAgent({ publicRun: true }) to allow an external client (e.g. the @lunora/mcp server) to start it`);const r=n.auth.userId??void 0,d=await n.db.query(y).withIndex("byKey",m=>m.eq("key",t.threadKey)).first();if(d){const m=d.status,I=d.instanceId,K=d.owner;if((m==="running"||m==="awaiting_input")&&I!==void 0&&(K===void 0||K===r))return{id:I,threadKey:t.threadKey};if(K!==r)throw new g("FORBIDDEN",`@lunora/agent: thread "${t.threadKey}" belongs to another owner`)}const{id:l}=await i.run({input:t.input,threadKey:t.threadKey,...r===void 0?{}:{owner:r},...t.title===void 0?{}:{title:t.title}});return{id:l,threadKey:t.threadKey}}),C=H(),R=ee();return{extension:ne,functions:{agentAppendMessage:q(o),agentCompleteRun:q(h),agentEnsureThread:q(u),agentEpisodeRecall:R.agentEpisodeRecall,agentEpisodeUpsert:R.agentEpisodeUpsert,agentGraphTraverse:C.agentGraphTraverse,agentGraphUpsert:C.agentGraphUpsert,agentMessages:L,agentDeleteMessage:q(s),agentPatchThread:q(c),agentResolveApproval:U,agentRun:B,agentSetState:q(b),agentState:O,agentThread:p}}};export{le as agentComponent,ne as agentExtension,pe as normalizeEntityName,we as sandboxComponent};
1
+ import{LunoraError as g}from"@lunora/errors";import{defineTable as k,initLunora as D,defineSchemaExtension as j}from"@lunora/server";import{v as e}from"@lunora/values";import{a as q,A as _,d as P,b as F}from"./packem_shared/component-shared-G8ngerkU.mjs";import{graphTables as G,graphComponent as H}from"./packem_shared/normalizeEntityName-k3fjicAG.mjs";import{normalizeEntityName as he}from"./packem_shared/normalizeEntityName-k3fjicAG.mjs";import{sandboxComponent as we}from"./packem_shared/sandboxComponent-CKX-iNXC.mjs";const v="agent_episodes",X=5,Q=20,W=500,N=200,Y=/\s+/gu,z=d=>d===void 0||!Number.isFinite(d)?X:Math.min(Q,Math.max(1,Math.trunc(d))),V={episodes:k({createdAt:e.number(),messageKey:e.string(),owner:e.string(),summary:e.string(),threadKey:e.optional(e.string())}).index("byOwnerCreatedAt",["owner","createdAt"]).index("byOwnerMessageKey",["owner","messageKey"],{unique:!0}).public()},{mutation:J,query:Z}=D.dataModel().create(),ee=()=>{const d=J.input({createdAt:e.optional(e.number()),messageKey:e.string(),owner:e.string(),summary:e.string(),threadKey:e.optional(e.string())}).mutation(async({args:s,ctx:u})=>{const c=s.summary.replaceAll(Y," ").trim().slice(0,W);if(c.length===0)return{recorded:!1};if(await u.db.query(v).withIndex("byOwnerMessageKey",I=>I.eq("owner",s.owner).eq("messageKey",s.messageKey)).first())return{recorded:!1};await u.db.insert(v,{createdAt:s.createdAt??Date.now(),messageKey:s.messageKey,owner:s.owner,summary:c,...s.threadKey===void 0?{}:{threadKey:s.threadKey}});const w=await u.db.query(v).withIndex("byOwnerCreatedAt",I=>I.eq("owner",s.owner)).order("asc").take(N+1);for(const I of w.slice(0,Math.max(0,w.length-N)))await u.db.delete(I._id);return{recorded:!0}}),o=Z.input({limit:e.optional(e.number()),owner:e.string()}).query(async({args:s,ctx:u})=>{const c=await u.db.query(v).withIndex("byOwnerCreatedAt",h=>h.eq("owner",s.owner)).order("desc").take(z(s.limit));return c.length===0?{context:""}:{context:c.toReversed().map(h=>`- ${h.summary}`).join(`
2
+ `)}});return{agentEpisodeRecall:q(o),agentEpisodeUpsert:q(d)}},S="threads",$="messages",M="run_queue",p=`${_}_${S}`,E=`${_}_${$}`,A=`${_}_${M}`,x=5,te=780*60*1e3,ne=j(_,{tables:{[$]:k({content:e.string(),createdAt:e.number(),messageKey:e.string(),role:e.union(e.literal("user"),e.literal("assistant"),e.literal("tool"),e.literal("system")),seq:e.number(),status:e.optional(e.union(e.literal("awaiting_approval"),e.literal("approved"),e.literal("rejected"))),stepName:e.optional(e.string()),threadKey:e.string(),toolCallId:e.optional(e.string()),toolCalls:e.optional(e.array(e.object({id:e.string(),input:e.any(),name:e.string()}))),toolName:e.optional(e.string())}).index("byThread",["threadKey","seq"]).index("byMessageKey",["threadKey","messageKey"],{unique:!0}).public(),[S]:k({agent:e.string(),createdAt:e.number(),error:e.optional(e.string().nullable()),completedInstanceId:e.optional(e.string().nullable()),instanceId:e.optional(e.string()),key:e.string(),messageCount:e.number(),owner:e.optional(e.string()),state:e.optional(e.any()),status:e.union(e.literal("idle"),e.literal("running"),e.literal("error"),e.literal("cancelled"),e.literal("awaiting_input")),title:e.optional(e.string()),updatedAt:e.number(),usage:e.optional(e.object({inputTokens:e.optional(e.number()),outputTokens:e.optional(e.number()),totalTokens:e.optional(e.number())}))}).index("byKey",["key"],{unique:!0}).index("byAgent",["agent"]).index("byInstance",["instanceId"]).public(),[M]:k({agent:e.string(),enqueuedAt:e.number(),instanceId:e.string(),position:e.number(),threadKey:e.string()}).index("byThread",["threadKey","position"]).index("byThreadInstance",["threadKey","instanceId"],{unique:!0}).public(),...G,...V}}),{mutation:f,query:T}=D.dataModel().create(),ie=async(d,o,s)=>{if(o.instanceId===void 0)throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" already has a run in flight (instance "${o.priorInstanceId}") — cannot queue a dispatch with no instance id`);const{instanceId:u}=o,c=await d.query(A).withIndex("byThreadInstance",I=>I.eq("threadKey",o.key).eq("instanceId",u)).first();if(c)return{outcome:"queued",position:c.position};const h=await d.query(A).withIndex("byThread",I=>I.eq("threadKey",o.key)).collect();if(h.length>=x)throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" run queue is full (depth ${String(x)}) — rejecting instance "${u}"`);const w=(h.at(-1)?.position??-1)+1;return await d.insert(A,{agent:o.agent,enqueuedAt:s,instanceId:u,position:w,threadKey:o.key}),{outcome:"queued",position:w}},ae=async(d,o,s)=>{if(o.policy==="queue")return ie(d,{agent:o.agent,instanceId:o.instanceId,key:o.key,priorInstanceId:o.priorInstanceId},s);if(o.policy!=="replace")throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" already has a run in flight (instance "${o.priorInstanceId}") — onConcurrentRun="${o.policy}"`);return await d.patch(o.existingId,{error:null,status:"running",updatedAt:s,...o.instanceId===void 0?{}:{completedInstanceId:null,instanceId:o.instanceId}}),{outcome:"replaced",priorInstanceId:o.priorInstanceId}},oe=(d,o,s)=>{const u=d.instanceId;if(u===void 0||o===u||d.status!=="running"&&d.status!=="awaiting_input")return;const c=typeof d.updatedAt=="number"?d.updatedAt:0,h=d.status==="awaiting_input"?F:te;return s-c>h?void 0:u},ce=()=>{const d=f.input({agent:e.string(),initialState:e.optional(e.any()),instanceId:e.optional(e.string()),key:e.string(),onConcurrentRun:e.optional(e.union(e.literal("reject"),e.literal("queue"),e.literal("replace"))),owner:e.optional(e.string()),title:e.optional(e.string())}).mutation(async({args:t,ctx:n})=>{const a=Date.now(),i=await n.db.query(p).withIndex("byKey",r=>r.eq("key",t.key)).first();if(i){if(i.owner!==t.owner)throw new Error(`@lunora/agent: thread "${t.key}" belongs to another owner`);if(t.instanceId!==void 0&&i.completedInstanceId===t.instanceId)return{outcome:"completed"};const r=oe(i,t.instanceId,a);return r!==void 0?ae(n.db,{agent:t.agent,existingId:i._id,instanceId:t.instanceId,key:t.key,policy:t.onConcurrentRun??"reject",priorInstanceId:r},a):(await n.db.patch(i._id,{error:null,status:"running",updatedAt:a,...t.instanceId===void 0?{}:{completedInstanceId:null,instanceId:t.instanceId}}),{outcome:"continued"})}return await n.db.insert(p,{agent:t.agent,createdAt:a,key:t.key,messageCount:0,status:"running",updatedAt:a,...P({instanceId:t.instanceId,owner:t.owner,state:t.initialState,title:t.title})}),{outcome:"created"}}),o=f.input({content:e.string(),messageKey:e.string(),role:e.union(e.literal("user"),e.literal("assistant"),e.literal("tool"),e.literal("system")),status:e.optional(e.union(e.literal("awaiting_approval"),e.literal("approved"),e.literal("rejected"))),stepName:e.optional(e.string()),threadKey:e.string(),toolCallId:e.optional(e.string()),toolCalls:e.optional(e.array(e.object({id:e.string(),input:e.any(),name:e.string()}))),toolName:e.optional(e.string())}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(E).withIndex("byMessageKey",y=>y.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();if(a)return{seq:a.seq};const i=await n.db.query(p).withIndex("byKey",y=>y.eq("key",t.threadKey)).first();if(!i)throw new Error(`@lunora/agent: cannot append to unknown thread "${t.threadKey}" — run agentEnsureThread first`);const r=i.messageCount,l=Date.now();return await n.db.insert(E,{content:t.content,createdAt:l,messageKey:t.messageKey,role:t.role,seq:r,threadKey:t.threadKey,...t.status===void 0?{}:{status:t.status},...t.stepName===void 0?{}:{stepName:t.stepName},...t.toolCallId===void 0?{}:{toolCallId:t.toolCallId},...t.toolCalls===void 0?{}:{toolCalls:t.toolCalls},...t.toolName===void 0?{}:{toolName:t.toolName}}),await n.db.patch(i._id,{messageCount:r+1,updatedAt:l}),{seq:r}}),s=f.input({messageKey:e.string(),threadKey:e.string()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(E).withIndex("byMessageKey",i=>i.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();a&&await n.db.delete(a._id)}),u=f.input({error:e.optional(e.string()),instanceId:e.optional(e.string()),key:e.optional(e.string()),status:e.optional(e.union(e.literal("idle"),e.literal("running"),e.literal("error"),e.literal("cancelled"),e.literal("awaiting_input"))),title:e.optional(e.string()),usage:e.optional(e.object({inputTokens:e.optional(e.number()),outputTokens:e.optional(e.number()),totalTokens:e.optional(e.number())}))}).mutation(async({args:t,ctx:n})=>{const{instanceId:a,key:i}=t;let r;i!==void 0?r=await n.db.query(p).withIndex("byKey",l=>l.eq("key",i)).first():a!==void 0&&(r=await n.db.query(p).withIndex("byInstance",l=>l.eq("instanceId",a)).first()),r&&await n.db.patch(r._id,{updatedAt:Date.now(),...t.error===void 0?{}:{error:t.error},...t.status===void 0?{}:{status:t.status},...t.title===void 0?{}:{title:t.title},...t.usage===void 0?{}:{usage:t.usage}})}),c=f.input({error:e.optional(e.string()),instanceId:e.string(),key:e.string(),status:e.union(e.literal("idle"),e.literal("error"),e.literal("cancelled")),usage:e.optional(e.object({inputTokens:e.optional(e.number()),outputTokens:e.optional(e.number()),totalTokens:e.optional(e.number())}))}).mutation(async({args:t,ctx:n})=>{const a=Date.now(),i=await n.db.query(p).withIndex("byKey",b=>b.eq("key",t.key)).first();if(i?.instanceId!==t.instanceId){if(i?.completedInstanceId===t.instanceId&&i.status==="running"&&typeof i.instanceId=="string")return{dequeued:i.instanceId};const b=await n.db.query(A).withIndex("byThreadInstance",K=>K.eq("threadKey",t.key).eq("instanceId",t.instanceId)).first();return b&&await n.db.delete(b._id),{}}const y=(i.completedInstanceId===t.instanceId?[]:await n.db.query(A).withIndex("byThread",b=>b.eq("threadKey",t.key)).collect())[0];if(!y)return await n.db.patch(i._id,{completedInstanceId:t.instanceId,status:t.status,updatedAt:a,...t.error===void 0?{}:{error:t.error},...t.usage===void 0?{}:{usage:t.usage}}),{};const m=y.instanceId;return await n.db.delete(y._id),await n.db.patch(i._id,{completedInstanceId:t.instanceId,instanceId:m,status:"running",updatedAt:a,...t.error===void 0?{}:{error:t.error},...t.usage===void 0?{}:{usage:t.usage}}),{dequeued:m}}),h=f.input({key:e.string(),state:e.any()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(p).withIndex("byKey",i=>i.eq("key",t.key)).first();a&&await n.db.patch(a._id,{state:t.state,updatedAt:Date.now()})}),w=(t,n)=>{if(!t)return;const{owner:a}=t;if(!(a!==void 0&&a!==(n.userId??void 0)))return t},I=T.input({key:e.string()}).query(async({args:t,ctx:n})=>{const a=await n.db.query(p).withIndex("byKey",i=>i.eq("key",t.key)).first();return w(a,n.auth)}),O=T.input({key:e.string()}).query(async({args:t,ctx:n})=>{const a=await n.db.query(p).withIndex("byKey",i=>i.eq("key",t.key)).first();return w(a,n.auth)?.state}),L=T.input({key:e.string(),limit:e.optional(e.number())}).query(async({args:t,ctx:n})=>{const a=await n.db.query(p).withIndex("byKey",i=>i.eq("key",t.key)).first();return w(a,n.auth)===void 0?[]:t.limit!==void 0?(await n.db.query(E).withIndex("byThread",r=>r.eq("threadKey",t.key)).order("desc").take(t.limit)).toReversed():n.db.query(E).withIndex("byThread",i=>i.eq("threadKey",t.key)).collect()}),U=f.input({decision:e.union(e.literal("approve"),e.literal("reject")),instanceId:e.string(),note:e.optional(e.string()),threadKey:e.string(),toolCallId:e.string()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(p).withIndex("byKey",m=>m.eq("key",t.threadKey)).first(),i=w(a,n.auth);if(i===void 0)throw new g("FORBIDDEN",`@lunora/agent: not allowed to resolve approvals on thread "${t.threadKey}"`);if(i.instanceId!==t.instanceId)throw new g("FORBIDDEN",`@lunora/agent: instance "${t.instanceId}" does not own thread "${t.threadKey}"`);const r=i.agent,{agents:l}=n,y=l?.[r];if(typeof y?.sendEvent!="function")throw new g("INTERNAL",`@lunora/agent: no ctx.agents["${r}"] producer to resolve the approval — run codegen/dev so the agent binding is wired`);return await y.sendEvent(t.instanceId,{payload:{decision:t.decision,toolCallId:t.toolCallId,...t.note===void 0?{}:{note:t.note}},type:`agent-approval:${t.toolCallId}`}),{resolved:!0}}),B=f.input({agent:e.string(),input:e.string(),threadKey:e.string(),title:e.optional(e.string())}).mutation(async({args:t,ctx:n})=>{const{agents:a}=n,i=a?.[t.agent];if(typeof i?.run!="function")throw new g("INTERNAL",`@lunora/agent: no ctx.agents["${t.agent}"] producer to start a run — run codegen/dev so the agent binding is wired, and check the agent name`);if(i.publicRun!==!0)throw new g("FORBIDDEN",`@lunora/agent: agent "${t.agent}" is not enabled for public runs — set defineAgent({ publicRun: true }) to allow an external client (e.g. the @lunora/mcp server) to start it`);const r=n.auth.userId??void 0,l=await n.db.query(p).withIndex("byKey",m=>m.eq("key",t.threadKey)).first();if(l){const m=l.status,b=l.instanceId,K=l.owner;if((m==="running"||m==="awaiting_input")&&b!==void 0&&(K===void 0||K===r))return{id:b,threadKey:t.threadKey};if(K!==r)throw new g("FORBIDDEN",`@lunora/agent: thread "${t.threadKey}" belongs to another owner`)}const{id:y}=await i.run({input:t.input,threadKey:t.threadKey,...r===void 0?{}:{owner:r},...t.title===void 0?{}:{title:t.title}});return{id:y,threadKey:t.threadKey}}),C=H(),R=ee();return{extension:ne,functions:{agentAppendMessage:q(o),agentCompleteRun:q(c),agentEnsureThread:q(d),agentEpisodeRecall:R.agentEpisodeRecall,agentEpisodeUpsert:R.agentEpisodeUpsert,agentGraphTraverse:C.agentGraphTraverse,agentGraphUpsert:C.agentGraphUpsert,agentMessages:L,agentDeleteMessage:q(s),agentPatchThread:q(u),agentResolveApproval:U,agentRun:B,agentSetState:q(h),agentState:O,agentThread:I}}};export{ce as agentComponent,ne as agentExtension,he as normalizeEntityName,we as sandboxComponent};
@@ -1,4 +1,4 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-B8WZl1rU.mjs";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-B_VV-x4x.mjs";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
package/dist/inbound.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-B_VV-x4x.js";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { A as AgentDefinition, e as AgentCompact, f as AgentEpisodeExtract, g as AgentGraphExtract, h as AgentGenerate, i as AgentTokenSink, j as AgentRunInput, k as AgentFunctionPaths, l as AgentRunFunction, m as AgentStepLike, n as AgentStreamGenerate, o as AgentRunResult, p as AgentMessageRow, q as AgentConfig, d as SkillDefinition, r as AnyAgentTool, s as AgentAsToolOptions, t as AgentSubToolInput, c as AgentToolDefinition, u as AgentBindingSpec, v as AgentHandle, w as AgentToolConfig, x as AgentFunctionReference, y as AgentModelInput, z as SkillConfig } from "./packem_shared/types.d-B8WZl1rU.mjs";
2
- export type { B as AgentApprovalContext, C as AgentEmailMapper, D as AgentEmailRun, E as AgentGenerateOptions, F as AgentGenerateResult, G as AgentInstructionsContext, H as AgentLiveEvent, J as AgentMemoryOptions, K as AgentMemorySource, L as AgentMessageStatus, M as AgentOnReply, N as AgentOnStepFinish, O as AgentPrepareStep, P as AgentPrepareStepInput, Q as AgentPrepareStepResult, R as AgentProgressEvent, T as AgentReplyInfo, a as AgentReplyRef, U as AgentRunHandle, V as AgentStepFinishInfo, W as AgentStepInfo, X as AgentThreadStatus, Y as AgentTokenDelta, Z as AgentToolCall, b as AgentToolContext, _ as AgentUsage, $ as AgentVoiceConfig, a0 as AgentWorkflowBindingLike, a1 as AgentWorkflowInstanceLike, a2 as EnsureThreadOutcome } from "./packem_shared/types.d-B8WZl1rU.mjs";
1
+ import { A as AgentDefinition, e as AgentCompact, f as AgentEpisodeExtract, g as AgentGraphExtract, h as AgentGenerate, i as AgentTokenSink, j as AgentRunInput, k as AgentFunctionPaths, l as AgentRunFunction, m as AgentStepLike, n as AgentStreamGenerate, o as AgentRunResult, p as AgentMessageRow, q as AgentConfig, d as SkillDefinition, r as AnyAgentTool, s as AgentAsToolOptions, t as AgentSubToolInput, c as AgentToolDefinition, u as AgentBindingSpec, v as AgentHandle, w as AgentToolConfig, x as AgentFunctionReference, y as AgentModelInput, z as SkillConfig } from "./packem_shared/types.d-B_VV-x4x.mjs";
2
+ export type { B as AgentApprovalContext, C as AgentEmailMapper, D as AgentEmailRun, E as AgentGenerateOptions, F as AgentGenerateResult, G as AgentInstructionsContext, H as AgentLiveEvent, J as AgentMemoryOptions, K as AgentMemorySource, L as AgentMessageStatus, M as AgentOnReply, N as AgentOnStepFinish, O as AgentPrepareStep, P as AgentPrepareStepInput, Q as AgentPrepareStepResult, R as AgentProgressEvent, T as AgentReplyInfo, a as AgentReplyRef, U as AgentRunHandle, V as AgentRunOptions, W as AgentStepFinishInfo, X as AgentStepInfo, Y as AgentThreadStatus, Z as AgentTokenDelta, _ as AgentToolCall, b as AgentToolContext, $ as AgentUsage, a0 as AgentVoiceConfig, a1 as AgentWorkflowBindingLike, a2 as AgentWorkflowInstanceLike, a3 as EnsureThreadOutcome } from "./packem_shared/types.d-B_VV-x4x.mjs";
3
3
  export { type AgentComponent, agentComponent, agentExtension } from "./component.mjs";
4
4
  import { FlexibleSchema, LanguageModel, ModelMessage } from 'ai';
5
5
  export { agentBindingName, agentClassName, agentDefaultName, voiceBindingName, voiceClassName } from "./naming.mjs";
6
- export { type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool } from "./sandbox.mjs";
6
+ export { type BrowserRenderResult, type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool } from "./sandbox.mjs";
7
7
  import { createAi } from '@lunora/ai';
8
8
  import { WorkflowDefinition } from '@lunora/workflow';
9
9
  import '@lunora/mail/inbound';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { A as AgentDefinition, e as AgentCompact, f as AgentEpisodeExtract, g as AgentGraphExtract, h as AgentGenerate, i as AgentTokenSink, j as AgentRunInput, k as AgentFunctionPaths, l as AgentRunFunction, m as AgentStepLike, n as AgentStreamGenerate, o as AgentRunResult, p as AgentMessageRow, q as AgentConfig, d as SkillDefinition, r as AnyAgentTool, s as AgentAsToolOptions, t as AgentSubToolInput, c as AgentToolDefinition, u as AgentBindingSpec, v as AgentHandle, w as AgentToolConfig, x as AgentFunctionReference, y as AgentModelInput, z as SkillConfig } from "./packem_shared/types.d-B8WZl1rU.js";
2
- export type { B as AgentApprovalContext, C as AgentEmailMapper, D as AgentEmailRun, E as AgentGenerateOptions, F as AgentGenerateResult, G as AgentInstructionsContext, H as AgentLiveEvent, J as AgentMemoryOptions, K as AgentMemorySource, L as AgentMessageStatus, M as AgentOnReply, N as AgentOnStepFinish, O as AgentPrepareStep, P as AgentPrepareStepInput, Q as AgentPrepareStepResult, R as AgentProgressEvent, T as AgentReplyInfo, a as AgentReplyRef, U as AgentRunHandle, V as AgentStepFinishInfo, W as AgentStepInfo, X as AgentThreadStatus, Y as AgentTokenDelta, Z as AgentToolCall, b as AgentToolContext, _ as AgentUsage, $ as AgentVoiceConfig, a0 as AgentWorkflowBindingLike, a1 as AgentWorkflowInstanceLike, a2 as EnsureThreadOutcome } from "./packem_shared/types.d-B8WZl1rU.js";
1
+ import { A as AgentDefinition, e as AgentCompact, f as AgentEpisodeExtract, g as AgentGraphExtract, h as AgentGenerate, i as AgentTokenSink, j as AgentRunInput, k as AgentFunctionPaths, l as AgentRunFunction, m as AgentStepLike, n as AgentStreamGenerate, o as AgentRunResult, p as AgentMessageRow, q as AgentConfig, d as SkillDefinition, r as AnyAgentTool, s as AgentAsToolOptions, t as AgentSubToolInput, c as AgentToolDefinition, u as AgentBindingSpec, v as AgentHandle, w as AgentToolConfig, x as AgentFunctionReference, y as AgentModelInput, z as SkillConfig } from "./packem_shared/types.d-B_VV-x4x.js";
2
+ export type { B as AgentApprovalContext, C as AgentEmailMapper, D as AgentEmailRun, E as AgentGenerateOptions, F as AgentGenerateResult, G as AgentInstructionsContext, H as AgentLiveEvent, J as AgentMemoryOptions, K as AgentMemorySource, L as AgentMessageStatus, M as AgentOnReply, N as AgentOnStepFinish, O as AgentPrepareStep, P as AgentPrepareStepInput, Q as AgentPrepareStepResult, R as AgentProgressEvent, T as AgentReplyInfo, a as AgentReplyRef, U as AgentRunHandle, V as AgentRunOptions, W as AgentStepFinishInfo, X as AgentStepInfo, Y as AgentThreadStatus, Z as AgentTokenDelta, _ as AgentToolCall, b as AgentToolContext, $ as AgentUsage, a0 as AgentVoiceConfig, a1 as AgentWorkflowBindingLike, a2 as AgentWorkflowInstanceLike, a3 as EnsureThreadOutcome } from "./packem_shared/types.d-B_VV-x4x.js";
3
3
  export { type AgentComponent, agentComponent, agentExtension } from "./component.js";
4
4
  import { FlexibleSchema, LanguageModel, ModelMessage } from 'ai';
5
5
  export { agentBindingName, agentClassName, agentDefaultName, voiceBindingName, voiceClassName } from "./naming.js";
6
- export { type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool } from "./sandbox.js";
6
+ export { type BrowserRenderResult, type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool } from "./sandbox.js";
7
7
  import { createAi } from '@lunora/ai';
8
8
  import { WorkflowDefinition } from '@lunora/workflow';
9
9
  import '@lunora/mail/inbound';
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{r as t,s as r}from"./packem_shared/agent-loop-ByRhLA54.mjs";import{collectAgenticMemoryTools as a,toSearchResults as i}from"./packem_shared/collectAgenticMemoryTools-BeMWT2qt.mjs";import{agentAsTool as l}from"./packem_shared/agentAsTool-CgZb6ycK.mjs";import{codeTool as m}from"./packem_shared/codeTool-BQb-K3my.mjs";import{agentComponent as c,agentExtension as x}from"./component.mjs";import{default as A}from"./packem_shared/createAgentContext-BvU9LP43.mjs";import{defineAgent as d,defineAgentTool as u,isAgentDefinition as N}from"./packem_shared/defineAgent-C3FzOwlh.mjs";import{functionTool as D}from"./packem_shared/functionTool-D3CP4f8-.mjs";import{createAgentGenerate as M,createEpisodeExtract as O,createGraphExtract as _,createStreamGenerate as C,resolveAgentModel as G}from"./packem_shared/createAgentGenerate-BOILDUZj.mjs";import{adaptMcpResult as F,mcpTools as L}from"./packem_shared/adaptMcpResult-CVlq-_TO.mjs";import{buildModelMessages as k}from"./packem_shared/buildModelMessages-Y4tGo1T9.mjs";import{agentBindingName as R,agentClassName as V,agentDefaultName as b,voiceBindingName as h,voiceClassName as w}from"./naming.mjs";import{AGENT_MODULE as I,DEFAULT_AGENT_FUNCTION_PATHS as P,SANDBOX_INVOKE_PATH as X,SANDBOX_MODULE as y,toFunctionReference as K}from"./packem_shared/AGENT_MODULE-M4D1EejI.mjs";import{browserTool as j,containerTool as q,fsTool as z}from"./sandbox.mjs";import{defineSkill as Q,isSkillDefinition as Y}from"./packem_shared/defineSkill-DGGvDWNq.mjs";import{default as $}from"./packem_shared/VoiceSessionDO-Ci1gaR3m.mjs";import{r as oe}from"./packem_shared/voice-turn-CYXdylKF.mjs";import{default as re}from"./packem_shared/compileAgentWorkflow-CzfL6bQS.mjs";export{I as AGENT_MODULE,P as DEFAULT_AGENT_FUNCTION_PATHS,X as SANDBOX_INVOKE_PATH,y as SANDBOX_MODULE,$ as VoiceSessionDO,F as adaptMcpResult,l as agentAsTool,R as agentBindingName,V as agentClassName,c as agentComponent,b as agentDefaultName,x as agentExtension,j as browserTool,k as buildModelMessages,m as codeTool,a as collectAgenticMemoryTools,re as compileAgentWorkflow,q as containerTool,A as createAgentContext,M as createAgentGenerate,O as createEpisodeExtract,_ as createGraphExtract,C as createStreamGenerate,d as defineAgent,u as defineAgentTool,Q as defineSkill,z as fsTool,D as functionTool,N as isAgentDefinition,Y as isSkillDefinition,L as mcpTools,G as resolveAgentModel,t as runAgentLoop,oe as runVoiceTurn,r as splitForCompaction,K as toFunctionReference,i as toSearchResults,h as voiceBindingName,w as voiceClassName};
1
+ import{r as t,s as r}from"./packem_shared/agent-loop-ByRhLA54.mjs";import{collectAgenticMemoryTools as a,toSearchResults as i}from"./packem_shared/collectAgenticMemoryTools-BeMWT2qt.mjs";import{agentAsTool as l}from"./packem_shared/agentAsTool-CgZb6ycK.mjs";import{codeTool as m}from"./packem_shared/codeTool-BQb-K3my.mjs";import{agentComponent as c,agentExtension as x}from"./component.mjs";import{default as A}from"./packem_shared/createAgentContext-BvU9LP43.mjs";import{defineAgent as d,defineAgentTool as u,isAgentDefinition as N}from"./packem_shared/defineAgent-C3FzOwlh.mjs";import{functionTool as D}from"./packem_shared/functionTool-D3CP4f8-.mjs";import{createAgentGenerate as M,createEpisodeExtract as O,createGraphExtract as _,createStreamGenerate as C,resolveAgentModel as G}from"./packem_shared/createAgentGenerate-BOILDUZj.mjs";import{adaptMcpResult as F,mcpTools as L}from"./packem_shared/adaptMcpResult-CVlq-_TO.mjs";import{buildModelMessages as k}from"./packem_shared/buildModelMessages-Y4tGo1T9.mjs";import{agentBindingName as R,agentClassName as V,agentDefaultName as b,voiceBindingName as h,voiceClassName as w}from"./naming.mjs";import{AGENT_MODULE as I,DEFAULT_AGENT_FUNCTION_PATHS as P,SANDBOX_INVOKE_PATH as X,SANDBOX_MODULE as y,toFunctionReference as K}from"./packem_shared/AGENT_MODULE-M4D1EejI.mjs";import{browserTool as j,containerTool as q,fsTool as z}from"./sandbox.mjs";import{defineSkill as Q,isSkillDefinition as Y}from"./packem_shared/defineSkill-DGGvDWNq.mjs";import{default as $}from"./packem_shared/VoiceSessionDO-Z-TCvJx2.mjs";import{r as oe}from"./packem_shared/voice-turn-hyURXfXc.mjs";import{default as re}from"./packem_shared/compileAgentWorkflow-DGw7K1vH.mjs";export{I as AGENT_MODULE,P as DEFAULT_AGENT_FUNCTION_PATHS,X as SANDBOX_INVOKE_PATH,y as SANDBOX_MODULE,$ as VoiceSessionDO,F as adaptMcpResult,l as agentAsTool,R as agentBindingName,V as agentClassName,c as agentComponent,b as agentDefaultName,x as agentExtension,j as browserTool,k as buildModelMessages,m as codeTool,a as collectAgenticMemoryTools,re as compileAgentWorkflow,q as containerTool,A as createAgentContext,M as createAgentGenerate,O as createEpisodeExtract,_ as createGraphExtract,C as createStreamGenerate,d as defineAgent,u as defineAgentTool,Q as defineSkill,z as fsTool,D as functionTool,N as isAgentDefinition,Y as isSkillDefinition,L as mcpTools,G as resolveAgentModel,t as runAgentLoop,oe as runVoiceTurn,r as splitForCompaction,K as toFunctionReference,i as toSearchResults,h as voiceBindingName,w as voiceClassName};
@@ -0,0 +1 @@
1
+ import{createAi as A}from"@lunora/ai";import{t as b}from"./createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs";import{p as x,d as T,a as _,i as p,b as y,c as v,e as E,t as k,f as M,r as w,g as I}from"./voice-turn-hyURXfXc.mjs";import{createStreamGenerate as C,createCompact as R}from"./createAgentGenerate-BOILDUZj.mjs";import{DEFAULT_AGENT_FUNCTION_PATHS as L,toFunctionReference as m}from"./AGENT_MODULE-M4D1EejI.mjs";const N="@cf/openai/whisper-large-v3-turbo",O="@cf/deepgram/aura-1",U=8*1024*1024,B=100,f=4e3,g=f*4+1024,F=4002,D=4003,z=4004,W=256*1024,S=15,G=5e3,X=h=>{if(h===null||typeof h!="object")return!1;const{text:t,type:e}=h;return e==="commit"||e==="interrupt"||e==="text"&&typeof t=="string"};class J{agent;ai;env;exportName;paths;streamGenerate;sttModel;ttsModel;maxSessionTurns;audioBuffers=new Map;bufferedBytes=new Map;controllers=new Map;state;constructor(t,e,s,r){this.state=t,this.env=e,this.agent=s,this.exportName=r,this.paths=L,this.ai=A({binding:e.AI,env:e}),this.streamGenerate=C(s,e),this.sttModel=s.voice?.stt??N,this.ttsModel=s.voice?.tts??O,this.maxSessionTurns=s.voice?.maxTurns??B}fetch(t){if(t.headers.get("Upgrade")!=="websocket")return new Response("Expected a WebSocket upgrade",{status:426});const s=new URL(t.url).searchParams.get("threadKey");if(!s)return new Response("Missing threadKey",{status:400});const r=globalThis.WebSocketPair,i=new r,n=i[0],o=i[1];this.state.acceptWebSocket(o);const c=crypto.randomUUID(),d=x(t.headers.get("x-lunora-identity")),a=T(t.headers.get("x-lunora-userid")),u=_(t.headers.get("x-lunora-identity-exp"));if(p(u))return y(o),new Response(null,{status:101,webSocket:n});o.serializeAttachment?.({connectionId:c,threadKey:s,turn:0,...u===void 0?{}:{expiresAt:u},...d===void 0?{}:{identity:d},...a===void 0?{}:{userId:a}}),this.send(o,{audioFormat:this.agent.voice?.audioFormat??"mp3",type:"ready"});const l=this.agent.voice?.greeting;return l&&l.length>0&&this.state.waitUntil?.(this.speakGreeting(o,c,s,a,d,l)),new Response(null,{status:101,webSocket:n})}async webSocketMessage(t,e){const s=t.deserializeAttachment?.();if(s){if(p(s.expiresAt)){y(t);return}try{if(typeof e=="string"){await this.handleControl(t,s,e);return}this.bufferAudio(s.connectionId,new Uint8Array(e),t)}catch(r){this.send(t,{message:r instanceof Error?r.message:String(r),type:"error"})}}}webSocketClose(t){this.cleanupSocket(t)}webSocketError(t){this.cleanupSocket(t)}resolveRun(t,e){const s=t===void 0&&e===void 0?void 0:{...e===void 0?{}:{claims:e},...t===void 0?{}:{userId:t}};return b({env:this.env,label:"@lunora/agent voice",...s===void 0?{}:{identity:s}})}async transcribe(t){const e=v(t);return E(await this.ai.run(this.sttModel,{audio:k(e)}))}async synthesize(t,e){const s=this.agent.voice?.speaker;return M(await this.ai.run(this.ttsModel,{text:t,...s===void 0?{}:{speaker:s}},e===void 0?void 0:{signal:e}))}async handleControl(t,e,s){if(s.length>g){this.send(t,{message:`control frame exceeds the maximum of ${String(g)} characters`,type:"error"}),this.closeSocket(t,z,"control_frame_limit");return}let r;try{r=JSON.parse(s)}catch{return}if(!X(r)){this.send(t,{message:'unsupported control frame — expected { type: "text", text }, { type: "commit" } or { type: "interrupt" }',type:"error"});return}const i=r;if(i.type==="interrupt"){this.controllers.get(e.connectionId)?.abort();return}if(i.type==="text"&&i.text.length>f){this.send(t,{message:`text frame exceeds the maximum of ${String(f)} characters`,type:"error"});return}if(e.turn>=this.maxSessionTurns){this.send(t,{message:`voice session reached its ${String(this.maxSessionTurns)}-turn limit — reconnect to continue`,type:"error"}),this.closeSocket(t,F,"turn_limit");return}if(this.controllers.has(e.connectionId)){i.type==="commit"&&this.drainAudio(e.connectionId),this.send(t,{message:"a turn is already in progress — send an interrupt before the next utterance",type:"error"});return}if(i.type==="commit"){const n=this.drainAudio(e.connectionId);await this.runTurn(t,e,{pcm:n});return}await this.runTurn(t,e,{text:i.text})}bufferAudio(t,e,s){const r=(this.bufferedBytes.get(t)??0)+e.byteLength;if(r>U){this.audioBuffers.delete(t),this.bufferedBytes.delete(t),this.send(s,{message:"utterance exceeded the maximum buffer — send a commit sooner",type:"error"}),this.closeSocket(s,D,"utterance_too_large");return}const i=this.audioBuffers.get(t)??[];i.push(e),this.audioBuffers.set(t,i),this.bufferedBytes.set(t,r)}drainAudio(t){const e=this.audioBuffers.get(t)??[];this.audioBuffers.delete(t),this.bufferedBytes.delete(t);const s=e.reduce((n,o)=>n+o.byteLength,0),r=new Uint8Array(s);let i=0;for(const n of e)r.set(n,i),i+=n.byteLength;return r}async runTurn(t,e,s){const r=new AbortController;this.controllers.set(e.connectionId,r);try{await w({agent:this.agent,compact:R(),connectionId:e.connectionId,env:this.env,exportName:this.exportName,paths:this.paths,run:this.resolveRun(e.userId,e.identity),send:i=>{this.send(t,i)},sendAudio:i=>{this.sendAudio(t,i)},signal:r.signal,streamGenerate:this.streamGenerate,synthesize:async(i,n)=>this.synthesizeWithSignal(i,n),threadKey:e.threadKey,transcribe:async i=>this.transcribe(i),turn:e.turn,waitForDrain:async()=>this.waitForSocketDrain(t),...e.userId===void 0?{}:{owner:e.userId},...s.pcm===void 0?{}:{pcm:s.pcm},...s.text===void 0?{}:{text:s.text}})}finally{this.controllers.get(e.connectionId)===r&&this.controllers.delete(e.connectionId),t.serializeAttachment?.({...e,turn:e.turn+1})}}async speakGreeting(t,e,s,r,i,n){const o=this.resolveRun(r,i),c=new AbortController,d=()=>c.signal.aborted;this.controllers.set(e,c);try{if((await o(m(this.paths.ensureThread),{agent:this.exportName,key:s,...this.agent.initialState===void 0?{}:{initialState:this.agent.initialState},...r===void 0?{}:{owner:r}}))?.outcome!=="created")return;for await(const u of I(await this.synthesizeWithSignal(n,c.signal))){if(d()||(await this.waitForSocketDrain(t),d()))break;this.sendAudio(t,u)}d()||(await o(m(this.paths.appendMessage),{content:n,messageKey:"voice:greeting:assistant",role:"assistant",threadKey:s}),this.send(t,{text:n,type:"assistant_done"}))}catch(a){this.send(t,{message:a instanceof Error?a.message:String(a),type:"error"})}finally{this.controllers.get(e)===c&&this.controllers.delete(e)}}async synthesizeWithSignal(t,e){return e.aborted?new Uint8Array(0):this.synthesize(t,e)}cleanupSocket(t){const e=t.deserializeAttachment?.();e&&(this.controllers.get(e.connectionId)?.abort(),this.controllers.delete(e.connectionId),this.audioBuffers.delete(e.connectionId),this.bufferedBytes.delete(e.connectionId))}closeSocket(t,e,s){try{t.close?.(e,s)}catch{}}send(t,e){try{t.send(JSON.stringify(e))}catch{}}sendAudio(t,e){try{t.send(e)}catch{}}async waitForSocketDrain(t){const e=t;let s=0;for(;typeof e.bufferedAmount=="number"&&e.bufferedAmount>W&&s<G;)await new Promise(r=>{setTimeout(r,S)}),s+=S}}export{J as default};
@@ -0,0 +1 @@
1
+ import{defineWorkflow as s}from"@lunora/workflow";import{r as m}from"./agent-loop-ByRhLA54.mjs";import{createStreamGenerate as p,createAgentGenerate as l,createEpisodeExtract as f,createGraphExtract as d,createCompact as c}from"./createAgentGenerate-BOILDUZj.mjs";import{agentDefaultName as u}from"../naming.mjs";import{DEFAULT_AGENT_FUNCTION_PATHS as A}from"./AGENT_MODULE-M4D1EejI.mjs";import{t as T}from"./createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs";import{otlpTelemetry as y}from"./otlpTelemetry-ByqrB-72.mjs";const E=(r,t,a)=>T({env:t,label:"@lunora/agent",...a===void 0?{}:{fetchImpl:a},...r===void 0?{}:{identity:{userId:r}}}),O=(r,t,a)=>{const e=t.LUNORA_OTLP_ENDPOINT;if(typeof e!="string"||e===""||r.telemetry?.isEnabled===!1)return r;const n=typeof t.LUNORA_OTLP_TOKEN=="string"?t.LUNORA_OTLP_TOKEN:void 0,o=[r.telemetry?.integrations].flat().filter(i=>i!==void 0);return{...r,telemetry:{...r.telemetry,integrations:[...o,y({conversationId:a,endpoint:e,token:n})],isEnabled:!0}}},I=(r,t,a)=>s({handler:async e=>{const n=O(r,e.env,e.params.threadKey);return m({agent:n,compact:c(),env:e.env,exportName:t,extractGraph:d(),extractEpisode:f(),generate:l(n,e.env),instanceId:e.event.instanceId,params:e.params,paths:a?.paths??A,run:E(e.params.owner,e.env,e.fetchImpl),step:e.step,streamGenerate:p(n,e.env)})},name:r.name??u(t)});export{I as default};
@@ -0,0 +1 @@
1
+ import{p as s,c as i,f as p,e as n,r as m,g as c}from"./voice-turn-hyURXfXc.mjs";import"./agent-loop-ByRhLA54.mjs";import"./buildModelMessages-Y4tGo1T9.mjs";import"./AGENT_MODULE-M4D1EejI.mjs";export{s as parseIdentity,i as pcmToWav,p as readSynthesisAudio,n as readTranscriptionText,m as runVoiceTurn,c as toByteIterable};
@@ -0,0 +1 @@
1
+ const _=12e4,S=15e4,T=12e4,O=12e4,E=15e4;export{S,E as a,_ as b,T as c,O as d};
@@ -0,0 +1,7 @@
1
+ import{LunoraError as i}from"@lunora/errors";import{initLunora as x}from"@lunora/server";import{v as a}from"@lunora/values";import{b as T,c as S,d as _}from"./sandbox-budgets-DDvj4Orr.mjs";const v=async(e,t,n)=>{if(e===null)return{overflowed:!1,text:""};const r=e.getReader(),o=new TextDecoder,s=()=>{r.cancel().catch(()=>{})};n?.addEventListener("abort",s);let c="",d=0,l=!1;try{for(;n?.aborted!==!0;){const{done:h,value:p}=await r.read();if(h)break;if(d+p.byteLength>t){l=!0,c+=o.decode(p.subarray(0,t-d),{stream:!0});break}d+=p.byteLength,c+=o.decode(p,{stream:!0})}c+=o.decode()}finally{n?.removeEventListener("abort",s),r.cancel().catch(()=>{})}if(n?.aborted===!0)throw n.reason;return{overflowed:l,text:c}},{internalAction:A}=x.dataModel().create(),N=()=>globalThis.document?.documentElement?.outerHTML??"",u=1e6,g=1e6,$=20,R=new TextEncoder,L=e=>{const t=[`exit code: ${String(e.code)}`];return e.stdout.length>0&&t.push(`stdout:
2
+ ${e.stdout}`),e.stderr.length>0&&t.push(`stderr:
3
+ ${e.stderr}`),t.join(`
4
+
5
+ `)},B=async(e,t)=>{const n=AbortSignal.timeout(_);try{const r=await e.fetch(t.path??"/",{...t.body===void 0?{}:{body:t.body},method:t.method??"GET",signal:n}),o=await v(r.body,g,n);return o.overflowed?`${o.text}
6
+
7
+ [truncated at ${String(g)} bytes]`:o.text}catch(r){return`fetch failed: ${r instanceof Error?r.message:String(r)}`}},O=async(e,t)=>{if(t.instance===void 0||t.instance.length===0)throw new i("BAD_REQUEST","@lunora/agent: sandbox container op arrived with no `instance` — the tool pins the thread's own container");const n=e.get(t.instance);if(t.op==="exec")try{return L(await n.exec(t.command??"",{args:t.args??[],timeoutMs:S}))}catch(r){return`exec failed: ${r instanceof Error?r.message:String(r)}`}if(t.op==="fetch")return await B(n,t);throw new i("INTERNAL",`@lunora/agent: sandbox container op "${t.op}" is not supported`)},m=(e,t)=>new i("INTERNAL",`@lunora/agent: sandbox ${e} op found no R2 bucket "${t}" on env — declare the r2_bucket binding in wrangler.jsonc and run codegen`),f=(e,t)=>{const n=e.env?.[t.bucket??""];return n&&typeof n.get=="function"?n:void 0},w=(e,t)=>{if(!e)throw m("browser",t.bucket??"");return e},E=e=>{let t=0,n=e.length;for(;t<n&&e[t]==="/";)t+=1;for(;n>t&&e[n-1]==="/";)n-=1;return e.slice(t,n)},y=(e,t)=>{const n=E(e),r=[];for(const s of t.split("/"))if(!(s===""||s===".")){if(s===".."){if(r.length===0)throw new i("BAD_REQUEST","@lunora/agent: fs path escapes the sandbox root");r.pop();continue}r.push(s)}const o=r.join("/");return n&&o?`${n}/${o}`:n||o},k=async(e,t,n)=>{const r=t.length>0?`${t}/`:"",o=n.length>0?n.length+1:0,s=[];let c;for(let d=0;d<$;d+=1){const l=await e.list({prefix:r,...c===void 0?{}:{cursor:c}});for(const h of l.objects)s.push(h.key.slice(o));if(!l.truncated||l.cursor===void 0)return{entries:s,truncated:!1};c=l.cursor}return{entries:s,truncated:!0}},b=async(e,t,n,r,o)=>{const s=y(t,n);return await e.put(s,r),{bytes:r.byteLength,key:s,mediaType:o}},M=async(e,t,n)=>{const r=t.url??"",o={timeoutMs:T};switch(t.op){case"content":return e.content(r,o);case"pdf":return b(w(n,t),t.root??"",t.path??"",await e.pdf(r,o),"application/pdf");case"scrape":return e.scrape(r,N,o);case"screenshot":{const s=w(n,t),c=await e.screenshot(r,{...o,...t.fullPage===void 0?{}:{fullPage:t.fullPage},...t.type===void 0?{}:{type:t.type}});return b(s,t.root??"",t.path??"",c,t.type==="jpeg"?"image/jpeg":"image/png")}default:throw new i("INTERNAL",`@lunora/agent: sandbox browser op "${t.op}" is not supported`)}},I=async(e,t,n)=>{const r=E(t),o=y(t,n.path??"");switch(n.op){case"ls":{const{entries:s,truncated:c}=await k(e,o,r);return c?{entries:s,truncated:!0}:{entries:s}}case"read":{const s=await e.head(o);if(s&&s.size>u)throw new i("BAD_REQUEST",`@lunora/agent: fs read: "${n.path??""}" is ${String(s.size)} bytes (max ${String(u)})`);const c=await e.get(o);if(!c)throw new i("NOT_FOUND",`@lunora/agent: fs read: no file at "${n.path??""}"`);return c.text()}case"rm":return await e.delete(o),{path:n.path??"",removed:!0};case"stat":{const s=await e.head(o);return s?{exists:!0,size:s.size}:{exists:!1}}case"write":{const s=n.content??"",c=R.encode(s).length;if(c>u)throw new i("BAD_REQUEST",`@lunora/agent: fs write: ${String(c)} bytes exceeds the max (${String(u)})`);return await e.put(o,s),{bytes:c,path:n.path??"",wrote:!0}}default:throw new i("INTERNAL",`@lunora/agent: sandbox fs op "${n.op}" is not supported`)}},U=()=>({invoke:A.input({args:a.optional(a.array(a.string())),body:a.optional(a.string()),bucket:a.optional(a.string()),command:a.optional(a.string()),content:a.optional(a.string()),fullPage:a.optional(a.boolean()),instance:a.optional(a.string()),kind:a.union(a.literal("browser"),a.literal("container"),a.literal("fs")),method:a.optional(a.string()),name:a.optional(a.string()),op:a.string(),path:a.optional(a.string()),root:a.optional(a.string()),selector:a.optional(a.string()),type:a.optional(a.string()),url:a.optional(a.string())}).action(async({args:t,ctx:n})=>{const r=n,o=t;if(o.kind==="browser"){if(!r.browser)throw new i("INTERNAL","@lunora/agent: sandbox browser op needs `ctx.browser` — install @lunora/browser and run codegen");return M(r.browser,o,f(r,o))}if(o.kind==="fs"){const d=f(r,o);if(!d)throw m("fs",o.bucket??"");return I(d,o.root??"",o)}const s=o.name??"",c=r.containers?.[s];if(!c)throw new i("INTERNAL",`@lunora/agent: sandbox container op found no ctx.containers["${s}"] — declare the container in lunora/containers.ts and run codegen`);return O(c,o)})});export{y as resolveFsKey,I as runFsOp,U as sandboxComponent};
@@ -9,11 +9,33 @@ import { FlexibleSchema, LanguageModel, ModelMessage, ToolSet, ToolChoice, ToolC
9
9
  interface AgentFunctionReference {
10
10
  __lunoraRef: string;
11
11
  }
12
+ /**
13
+ * Per-call options an {@link AgentRunFunction} forwards to the dispatcher —
14
+ * a structural subset of `@lunora/dispatch`'s `RunFunctionOptions`, declared
15
+ * locally so this module stays free of that import.
16
+ * @experimental
17
+ */
18
+ interface AgentRunOptions {
19
+ /**
20
+ * Abort the dispatch after this many ms, overriding the runner's 30s
21
+ * default (`DEFAULT_DISPATCH_TIMEOUT_MS`).
22
+ *
23
+ * Load-bearing for any tool whose target legitimately runs longer than
24
+ * that. A dispatch timeout answers 503, which is NOT a deterministic
25
+ * dispatch failure, so the tool's `step.do` rethrows and the host retries
26
+ * the step — dispatching the same call again while the FIRST one is still
27
+ * running. For a side-effecting target (a container command, a billed page
28
+ * render) that is a second execution, not a second attempt. Set this at
29
+ * least as wide as the target's own budget, and give the target an inner
30
+ * deadline strictly under it so the target aborts itself first.
31
+ */
32
+ timeoutMs?: number;
33
+ }
12
34
  /**
13
35
  * `ctx.run`-shaped dispatcher the loop uses to call Lunora functions.
14
36
  * @experimental
15
37
  */
16
- type AgentRunFunction = (reference: AgentFunctionReference, args?: Record<string, unknown>) => Promise<unknown>;
38
+ type AgentRunFunction = (reference: AgentFunctionReference, args?: Record<string, unknown>, options?: AgentRunOptions) => Promise<unknown>;
17
39
  /**
18
40
  * Structural subset of the Cloudflare Workflows durable-step API the loop needs.
19
41
  * @experimental
@@ -912,16 +934,20 @@ interface AgentDefinition extends AgentConfig {
912
934
  /**
913
935
  * What `agentEnsureThread` reports back to the loop.
914
936
  *
915
- * A discriminated union rather than a bag of optional booleans: the four
916
- * outcomes are mutually exclusive, and the data each carries only exists for its
917
- * own case. `queued` has a position, `replaced` has the instance it took the
918
- * thread from, and the other two have nothing — encoding that as five
919
- * independent optional fields made every reader re-derive which combination was
920
- * legal.
937
+ * A discriminated union rather than a bag of optional booleans: the outcomes are
938
+ * mutually exclusive, and the data each carries only exists for its own case.
939
+ * `queued` has a position, `replaced` has the instance it took the thread from,
940
+ * and the rest have nothing — encoding that as independent optional fields made
941
+ * every reader re-derive which combination was legal.
942
+ *
943
+ * `completed` is the odd one: the thread was NOT (re)opened. This run already
944
+ * reported its outcome and is replaying because only the reply was lost, so it
945
+ * takes no ownership and revives nothing — it runs its body out of the step
946
+ * journal to re-dispatch the effects that never landed.
921
947
  * @experimental
922
948
  */
923
949
  type EnsureThreadOutcome = {
924
- outcome: "continued" | "created";
950
+ outcome: "completed" | "continued" | "created";
925
951
  } | {
926
952
  outcome: "queued";
927
953
  position: number;
@@ -1344,4 +1370,4 @@ interface AgentHandle {
1344
1370
  /** Read a run's workflow status by instance id. */
1345
1371
  status: (id: string) => Promise<unknown>;
1346
1372
  }
1347
- export { AgentVoiceConfig as $, AgentDefinition as A, AgentApprovalContext as B, AgentEmailMapper as C, AgentEmailRun as D, AgentGenerateOptions as E, AgentGenerateResult as F, AgentInstructionsContext as G, AgentLiveEvent as H, InboundChannelEvent as I, AgentMemoryOptions as J, AgentMemorySource as K, AgentMessageStatus as L, AgentOnReply as M, AgentOnStepFinish as N, AgentPrepareStep as O, AgentPrepareStepInput as P, AgentPrepareStepResult as Q, AgentProgressEvent as R, SkillMarkdownExtras as S, AgentReplyInfo as T, AgentRunHandle as U, AgentStepFinishInfo as V, AgentStepInfo as W, AgentThreadStatus as X, AgentTokenDelta as Y, AgentToolCall as Z, AgentUsage as _, AgentReplyRef as a, AgentWorkflowBindingLike as a0, AgentWorkflowInstanceLike as a1, EnsureThreadOutcome as a2, AgentToolContext as b, AgentToolDefinition as c, SkillDefinition as d, AgentCompact as e, AgentEpisodeExtract as f, AgentGraphExtract as g, AgentGenerate as h, AgentTokenSink as i, AgentRunInput as j, AgentFunctionPaths as k, AgentRunFunction as l, AgentStepLike as m, AgentStreamGenerate as n, AgentRunResult as o, AgentMessageRow as p, AgentConfig as q, AnyAgentTool as r, AgentAsToolOptions as s, AgentSubToolInput as t, AgentBindingSpec as u, AgentHandle as v, AgentToolConfig as w, AgentFunctionReference as x, AgentModelInput as y, SkillConfig as z };
1373
+ export { AgentUsage as $, AgentDefinition as A, AgentApprovalContext as B, AgentEmailMapper as C, AgentEmailRun as D, AgentGenerateOptions as E, AgentGenerateResult as F, AgentInstructionsContext as G, AgentLiveEvent as H, InboundChannelEvent as I, AgentMemoryOptions as J, AgentMemorySource as K, AgentMessageStatus as L, AgentOnReply as M, AgentOnStepFinish as N, AgentPrepareStep as O, AgentPrepareStepInput as P, AgentPrepareStepResult as Q, AgentProgressEvent as R, SkillMarkdownExtras as S, AgentReplyInfo as T, AgentRunHandle as U, AgentRunOptions as V, AgentStepFinishInfo as W, AgentStepInfo as X, AgentThreadStatus as Y, AgentTokenDelta as Z, AgentToolCall as _, AgentReplyRef as a, AgentVoiceConfig as a0, AgentWorkflowBindingLike as a1, AgentWorkflowInstanceLike as a2, EnsureThreadOutcome as a3, AgentToolContext as b, AgentToolDefinition as c, SkillDefinition as d, AgentCompact as e, AgentEpisodeExtract as f, AgentGraphExtract as g, AgentGenerate as h, AgentTokenSink as i, AgentRunInput as j, AgentFunctionPaths as k, AgentRunFunction as l, AgentStepLike as m, AgentStreamGenerate as n, AgentRunResult as o, AgentMessageRow as p, AgentConfig as q, AnyAgentTool as r, AgentAsToolOptions as s, AgentSubToolInput as t, AgentBindingSpec as u, AgentHandle as v, AgentToolConfig as w, AgentFunctionReference as x, AgentModelInput as y, SkillConfig as z };
@@ -9,11 +9,33 @@ import { FlexibleSchema, LanguageModel, ModelMessage, ToolSet, ToolChoice, ToolC
9
9
  interface AgentFunctionReference {
10
10
  __lunoraRef: string;
11
11
  }
12
+ /**
13
+ * Per-call options an {@link AgentRunFunction} forwards to the dispatcher —
14
+ * a structural subset of `@lunora/dispatch`'s `RunFunctionOptions`, declared
15
+ * locally so this module stays free of that import.
16
+ * @experimental
17
+ */
18
+ interface AgentRunOptions {
19
+ /**
20
+ * Abort the dispatch after this many ms, overriding the runner's 30s
21
+ * default (`DEFAULT_DISPATCH_TIMEOUT_MS`).
22
+ *
23
+ * Load-bearing for any tool whose target legitimately runs longer than
24
+ * that. A dispatch timeout answers 503, which is NOT a deterministic
25
+ * dispatch failure, so the tool's `step.do` rethrows and the host retries
26
+ * the step — dispatching the same call again while the FIRST one is still
27
+ * running. For a side-effecting target (a container command, a billed page
28
+ * render) that is a second execution, not a second attempt. Set this at
29
+ * least as wide as the target's own budget, and give the target an inner
30
+ * deadline strictly under it so the target aborts itself first.
31
+ */
32
+ timeoutMs?: number;
33
+ }
12
34
  /**
13
35
  * `ctx.run`-shaped dispatcher the loop uses to call Lunora functions.
14
36
  * @experimental
15
37
  */
16
- type AgentRunFunction = (reference: AgentFunctionReference, args?: Record<string, unknown>) => Promise<unknown>;
38
+ type AgentRunFunction = (reference: AgentFunctionReference, args?: Record<string, unknown>, options?: AgentRunOptions) => Promise<unknown>;
17
39
  /**
18
40
  * Structural subset of the Cloudflare Workflows durable-step API the loop needs.
19
41
  * @experimental
@@ -912,16 +934,20 @@ interface AgentDefinition extends AgentConfig {
912
934
  /**
913
935
  * What `agentEnsureThread` reports back to the loop.
914
936
  *
915
- * A discriminated union rather than a bag of optional booleans: the four
916
- * outcomes are mutually exclusive, and the data each carries only exists for its
917
- * own case. `queued` has a position, `replaced` has the instance it took the
918
- * thread from, and the other two have nothing — encoding that as five
919
- * independent optional fields made every reader re-derive which combination was
920
- * legal.
937
+ * A discriminated union rather than a bag of optional booleans: the outcomes are
938
+ * mutually exclusive, and the data each carries only exists for its own case.
939
+ * `queued` has a position, `replaced` has the instance it took the thread from,
940
+ * and the rest have nothing — encoding that as independent optional fields made
941
+ * every reader re-derive which combination was legal.
942
+ *
943
+ * `completed` is the odd one: the thread was NOT (re)opened. This run already
944
+ * reported its outcome and is replaying because only the reply was lost, so it
945
+ * takes no ownership and revives nothing — it runs its body out of the step
946
+ * journal to re-dispatch the effects that never landed.
921
947
  * @experimental
922
948
  */
923
949
  type EnsureThreadOutcome = {
924
- outcome: "continued" | "created";
950
+ outcome: "completed" | "continued" | "created";
925
951
  } | {
926
952
  outcome: "queued";
927
953
  position: number;
@@ -1344,4 +1370,4 @@ interface AgentHandle {
1344
1370
  /** Read a run's workflow status by instance id. */
1345
1371
  status: (id: string) => Promise<unknown>;
1346
1372
  }
1347
- export { AgentVoiceConfig as $, AgentDefinition as A, AgentApprovalContext as B, AgentEmailMapper as C, AgentEmailRun as D, AgentGenerateOptions as E, AgentGenerateResult as F, AgentInstructionsContext as G, AgentLiveEvent as H, InboundChannelEvent as I, AgentMemoryOptions as J, AgentMemorySource as K, AgentMessageStatus as L, AgentOnReply as M, AgentOnStepFinish as N, AgentPrepareStep as O, AgentPrepareStepInput as P, AgentPrepareStepResult as Q, AgentProgressEvent as R, SkillMarkdownExtras as S, AgentReplyInfo as T, AgentRunHandle as U, AgentStepFinishInfo as V, AgentStepInfo as W, AgentThreadStatus as X, AgentTokenDelta as Y, AgentToolCall as Z, AgentUsage as _, AgentReplyRef as a, AgentWorkflowBindingLike as a0, AgentWorkflowInstanceLike as a1, EnsureThreadOutcome as a2, AgentToolContext as b, AgentToolDefinition as c, SkillDefinition as d, AgentCompact as e, AgentEpisodeExtract as f, AgentGraphExtract as g, AgentGenerate as h, AgentTokenSink as i, AgentRunInput as j, AgentFunctionPaths as k, AgentRunFunction as l, AgentStepLike as m, AgentStreamGenerate as n, AgentRunResult as o, AgentMessageRow as p, AgentConfig as q, AnyAgentTool as r, AgentAsToolOptions as s, AgentSubToolInput as t, AgentBindingSpec as u, AgentHandle as v, AgentToolConfig as w, AgentFunctionReference as x, AgentModelInput as y, SkillConfig as z };
1373
+ export { AgentUsage as $, AgentDefinition as A, AgentApprovalContext as B, AgentEmailMapper as C, AgentEmailRun as D, AgentGenerateOptions as E, AgentGenerateResult as F, AgentInstructionsContext as G, AgentLiveEvent as H, InboundChannelEvent as I, AgentMemoryOptions as J, AgentMemorySource as K, AgentMessageStatus as L, AgentOnReply as M, AgentOnStepFinish as N, AgentPrepareStep as O, AgentPrepareStepInput as P, AgentPrepareStepResult as Q, AgentProgressEvent as R, SkillMarkdownExtras as S, AgentReplyInfo as T, AgentRunHandle as U, AgentRunOptions as V, AgentStepFinishInfo as W, AgentStepInfo as X, AgentThreadStatus as Y, AgentTokenDelta as Z, AgentToolCall as _, AgentReplyRef as a, AgentVoiceConfig as a0, AgentWorkflowBindingLike as a1, AgentWorkflowInstanceLike as a2, EnsureThreadOutcome as a3, AgentToolContext as b, AgentToolDefinition as c, SkillDefinition as d, AgentCompact as e, AgentEpisodeExtract as f, AgentGraphExtract as g, AgentGenerate as h, AgentTokenSink as i, AgentRunInput as j, AgentFunctionPaths as k, AgentRunFunction as l, AgentStepLike as m, AgentStreamGenerate as n, AgentRunResult as o, AgentMessageRow as p, AgentConfig as q, AnyAgentTool as r, AgentAsToolOptions as s, AgentSubToolInput as t, AgentBindingSpec as u, AgentHandle as v, AgentToolConfig as w, AgentFunctionReference as x, AgentModelInput as y, SkillConfig as z };
@@ -0,0 +1 @@
1
+ import{c as z}from"./agent-loop-ByRhLA54.mjs";import{buildModelMessages as G}from"./buildModelMessages-Y4tGo1T9.mjs";import{toFunctionReference as A}from"./AGENT_MODULE-M4D1EejI.mjs";const ot=t=>{let e="";for(let r=0;r<t.length;r+=32768)e+=String.fromCharCode(...t.subarray(r,r+32768));return btoa(e)},P=t=>{const e=atob(t),n=new Uint8Array(e.length);for(let r=0;r<e.length;r+=1)n[r]=e.codePointAt(r)??0;return n},C=t=>{const e=t.replaceAll("-","+").replaceAll("_","/"),n=e+"=".repeat((4-e.length%4)%4);return P(n)},D=new TextDecoder;new TextEncoder;const N="=",Q=t=>{if(t)try{const e=t[0]==="{"?t:D.decode(C(t)),n=JSON.parse(e);if(n&&typeof n=="object"&&!Array.isArray(n))return n}catch{}},ct=t=>{if(t){if(!t.startsWith(N))return t;try{return D.decode(C(t.slice(N.length)))}catch{return}}},dt=t=>{const e=Number(t);return Number.isFinite(e)&&e>0?e:void 0},ut=t=>typeof t=="number"&&Date.now()>=t,yt=t=>{try{t.send(JSON.stringify({code:"TOKEN_EXPIRED",error:{code:"TOKEN_EXPIRED",message:"authentication token expired"},message:"authentication token expired",type:"error"})),t.close?.(4001,"token_expired")}catch{}},Y=16e3,Z=1,tt=16,M=/[.!?]\s/u,et=/\s/u,nt=async function*(t){if(t instanceof Uint8Array){yield t;return}if(t instanceof ReadableStream){const e=t.getReader();try{for(;;){const{done:n,value:r}=await e.read();if(n)break;yield r}}finally{e.releaseLock()}return}yield*t},ft=(t,e=Y,n=Z,r=tt)=>{const i=n*r/8,l=e*i,m=new ArrayBuffer(44+t.byteLength),s=new DataView(m),u=(p,b)=>{for(let d=0;d<b.length;d+=1)s.setUint8(p+d,b.codePointAt(d)??0)};u(0,"RIFF"),s.setUint32(4,36+t.byteLength,!0),u(8,"WAVE"),u(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,n,!0),s.setUint32(24,e,!0),s.setUint32(28,l,!0),s.setUint16(32,i,!0),s.setUint16(34,r,!0),u(36,"data"),s.setUint32(40,t.byteLength,!0);const a=new Uint8Array(m);return a.set(t,44),a},rt=t=>{const e=[];let n=t,r=M.exec(n);for(;r;){let i=r.index+1;for(;i<n.length&&et.test(n[i]??"");)i+=1;const l=n.slice(0,i).trim();l.length>0&&e.push(l),n=n.slice(i),r=M.exec(n)}return{rest:n,sentences:e}},lt=async t=>{const{agent:e,compact:n,connectionId:r,env:i,exportName:l,owner:m,paths:s,pcm:u,run:a,send:p,sendAudio:b,signal:d,streamGenerate:H,synthesize:L,text:B,threadKey:o,transcribe:F,turn:U,waitForDrain:O}=t,h=()=>d.aborted,v=A(s.appendMessage),$=A(s.ensureThread),K=A(s.listMessages),x=A(s.patchThread);await a($,{agent:l,key:o,...e.initialState===void 0?{}:{initialState:e.initialState},...m===void 0?{}:{owner:m}});try{const y=(B??(u?await F(u):"")).trim();if(y.length===0)return await a(x,{key:o,status:"idle"}),{assistantText:"",interrupted:!1,userText:""};p({text:y,type:"user_transcript"}),await a(v,{content:y,messageKey:`voice:${r}:${String(U)}:user`,role:"user",threadKey:o}),await a(x,{key:o,status:"running"});const _=typeof e.instructions=="function"?e.instructions({env:i,input:y,threadKey:o}):e.instructions,j=await a(K,{key:o}),{history:W,summary:I}=await z({agent:e,compact:n,env:i},j),V=G({history:W,..._===void 0?{}:{instructions:_},...I===void 0?{}:{summary:I}});let g="",E="",k=Promise.resolve();const X=async c=>{if(h()||c.length===0)return;let w=!1;try{for await(const f of nt(await L(c,d))){if(h()||(await O?.(),h()))break;b(f),w=!0}}catch(f){p({message:f instanceof Error?f.message:String(f),type:"error"})}w&&(E=E.length>0?`${E} ${c}`:c)},R=c=>{k=k.then(async()=>X(c))},J=await H({messages:V,signal:d},c=>{if(h())return;p({text:c,type:"assistant_delta"}),g+=c;const{rest:w,sentences:f}=rt(g);g=w;for(const q of f)R(q)});!h()&&g.trim().length>0&&R(g.trim()),g="",await k;const S=h(),T=S?E:J.text;return await a(v,{content:T,messageKey:`voice:${r}:${String(U)}:assistant`,role:"assistant",threadKey:o}),await a(x,{key:o,status:"idle"}),p(S?{type:"interrupted"}:{text:T,type:"assistant_done"}),{assistantText:T,interrupted:S,userText:y}}catch(y){throw await a(x,{key:o,status:"idle"}),y}},pt=t=>Q(t),ht=t=>{if(typeof t=="object"&&t!==null&&"text"in t){const{text:e}=t;return typeof e=="string"?e:""}return""},gt=t=>{if(t instanceof Uint8Array||t instanceof ReadableStream)return t;if(t instanceof Response&&t.body)return t.body;if(typeof t=="object"&&t!==null&&"audio"in t){const{audio:e}=t;if(typeof e=="string")return P(e);if(e instanceof ReadableStream||e instanceof Uint8Array)return e}return new Uint8Array(0)};export{dt as a,yt as b,ft as c,ct as d,ht as e,gt as f,nt as g,ut as i,pt as p,lt as r,ot as t};
package/dist/reply.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Mailer } from '@lunora/mail';
2
2
  import { InboundEmail } from '@lunora/mail/inbound';
3
- import { a as AgentReplyRef } from "./packem_shared/types.d-B8WZl1rU.mjs";
3
+ import { a as AgentReplyRef } from "./packem_shared/types.d-B_VV-x4x.mjs";
4
4
  import 'ai';
5
5
  /** The email arm of {@link AgentReplyRef}, narrowed for the helpers below. */
6
6
  type EmailReplyRef = Extract<AgentReplyRef, {
package/dist/reply.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Mailer } from '@lunora/mail';
2
2
  import { InboundEmail } from '@lunora/mail/inbound';
3
- import { a as AgentReplyRef } from "./packem_shared/types.d-B8WZl1rU.js";
3
+ import { a as AgentReplyRef } from "./packem_shared/types.d-B_VV-x4x.js";
4
4
  import 'ai';
5
5
  /** The email arm of {@link AgentReplyRef}, narrowed for the helpers below. */
6
6
  type EmailReplyRef = Extract<AgentReplyRef, {
@@ -1,6 +1,20 @@
1
- import { b as AgentToolContext, c as AgentToolDefinition } from "./packem_shared/types.d-B8WZl1rU.mjs";
1
+ import { b as AgentToolContext, c as AgentToolDefinition } from "./packem_shared/types.d-B_VV-x4x.mjs";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
+ /**
5
+ * What a byte-returning browser op resolves to. The render is written to R2 and
6
+ * its key comes back — never the bytes, which would be truncated to the model's
7
+ * output cap and billed for nothing.
8
+ * @experimental
9
+ */
10
+ interface BrowserRenderResult {
11
+ /** Size of the stored object. */
12
+ bytes: number;
13
+ /** The absolute R2 key the render was written to. */
14
+ key: string;
15
+ /** The render's content type, e.g. `image/jpeg`. */
16
+ mediaType: string;
17
+ }
4
18
  /**
5
19
  * The model-provided input to a {@link browserTool} call — a discriminated
6
20
  * union on `op` so one tool exposes every headless-browser capability.
@@ -44,6 +58,17 @@ type ContainerToolInput = {
44
58
  * @experimental
45
59
  */
46
60
  interface BrowserToolOptions {
61
+ /**
62
+ * R2 binding name the byte-returning ops (`screenshot`, `pdf`) write their
63
+ * render to. REQUIRED for those two: a render is billed, and a tool result
64
+ * is capped at `MAX_TOOL_OUTPUT_CHARS`, so base64 bytes inline would be
65
+ * truncated mid-string and unusable by the model — paid for and thrown
66
+ * away. With a bucket the op returns `{ bytes, key, mediaType }` and the
67
+ * app serves or post-processes the object by key. `content`/`scrape`
68
+ * return text and need no bucket; without one, a `screenshot`/`pdf` call is
69
+ * refused as a tool result (never dispatched) so nothing is billed.
70
+ */
71
+ bucket?: string;
47
72
  /** Override the model-facing description (what the tool does). */
48
73
  description?: string;
49
74
  /**
@@ -55,6 +80,14 @@ interface BrowserToolOptions {
55
80
  * keep it deterministic).
56
81
  */
57
82
  needsApproval?: ((input: BrowserToolInput) => boolean) | boolean;
83
+ /**
84
+ * Key prefix inside {@link BrowserToolOptions.bucket} that renders are
85
+ * written under, isolating them from the rest of the bucket. Default: the
86
+ * bucket root. Each render lands at `<root>/<threadKey>/<toolCallId>.<ext>`
87
+ * — derived only from replay-stable identifiers, so a retried step
88
+ * overwrites its own object rather than accumulating one per attempt.
89
+ */
90
+ root?: string;
58
91
  }
59
92
  /**
60
93
  * The model-provided input to a {@link fsTool} call — a discriminated union on
@@ -133,6 +166,12 @@ interface ContainerToolOptions {
133
166
  * `@cloudflare/playwright` `launch` peer) to `createShardDO()` — codegen never
134
167
  * injects that peer, so the browser op throws a directed error until it is wired.
135
168
  *
169
+ * `content`/`scrape` return text. `screenshot`/`pdf` write the render to
170
+ * `opts.bucket` and return `{ bytes, key, mediaType }` — never the bytes
171
+ * themselves, which a 4000-char tool-output cap would truncate into something
172
+ * unusable after paying for the render. Without a bucket those two ops are
173
+ * refused as a tool result and never dispatched.
174
+ *
136
175
  * Security: the model chooses the `url` with no allowlist, so this is an SSRF
137
176
  * surface — pass `opts.needsApproval` to gate calls a prompt-injected model
138
177
  * could aim at internal/link-local endpoints.
@@ -142,12 +181,12 @@ interface ContainerToolOptions {
142
181
  *
143
182
  * export const researcher = defineAgent({
144
183
  * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
145
- * tools: { browser: browserTool() },
184
+ * tools: { browser: browserTool({ bucket: "SANDBOX_BUCKET", root: "renders" }) },
146
185
  * });
147
186
  * ```
148
187
  * @experimental
149
188
  */
150
- declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition<BrowserToolInput, string>;
189
+ declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition<BrowserToolInput, BrowserRenderResult | string>;
151
190
  /**
152
191
  * A batteries-included agent tool that talks to a declared Cloudflare
153
192
  * Container. `name` is the `ctx.containers.<name>` key (the `lunora/containers.ts`
@@ -155,6 +194,12 @@ declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition
155
194
  * the model picks via `op`. The call dispatches to the auto-registered
156
195
  * `sandbox:invoke` action, which carries `ctx.containers`.
157
196
  *
197
+ * Every call is addressed to ONE instance per thread (`ctx.containers.<name>
198
+ * .get(threadKey)`), not a random pool member, so a multi-step session shares a
199
+ * filesystem — size the container's `maxInstances` for the number of live
200
+ * threads. See `sandbox-budgets.ts` for the time budgets and the re-execution
201
+ * ceiling that remains.
202
+ *
158
203
  * By default a read-only (GET/HEAD/OPTIONS, or method-omitted) `fetch` runs
159
204
  * unattended while everything else is gated behind a human approval — an
160
205
  * `exec`; a `fetch` whose path resolves to the privileged exec route (both
@@ -202,4 +247,4 @@ declare const containerTool: (name: string, options?: ContainerToolOptions) => A
202
247
  * @experimental
203
248
  */
204
249
  declare const fsTool: (bucket: string, options?: FsToolOptions) => AgentToolDefinition<FsToolInput>;
205
- export { type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool };
250
+ export { type BrowserRenderResult, type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool };
package/dist/sandbox.d.ts CHANGED
@@ -1,6 +1,20 @@
1
- import { b as AgentToolContext, c as AgentToolDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
1
+ import { b as AgentToolContext, c as AgentToolDefinition } from "./packem_shared/types.d-B_VV-x4x.js";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
+ /**
5
+ * What a byte-returning browser op resolves to. The render is written to R2 and
6
+ * its key comes back — never the bytes, which would be truncated to the model's
7
+ * output cap and billed for nothing.
8
+ * @experimental
9
+ */
10
+ interface BrowserRenderResult {
11
+ /** Size of the stored object. */
12
+ bytes: number;
13
+ /** The absolute R2 key the render was written to. */
14
+ key: string;
15
+ /** The render's content type, e.g. `image/jpeg`. */
16
+ mediaType: string;
17
+ }
4
18
  /**
5
19
  * The model-provided input to a {@link browserTool} call — a discriminated
6
20
  * union on `op` so one tool exposes every headless-browser capability.
@@ -44,6 +58,17 @@ type ContainerToolInput = {
44
58
  * @experimental
45
59
  */
46
60
  interface BrowserToolOptions {
61
+ /**
62
+ * R2 binding name the byte-returning ops (`screenshot`, `pdf`) write their
63
+ * render to. REQUIRED for those two: a render is billed, and a tool result
64
+ * is capped at `MAX_TOOL_OUTPUT_CHARS`, so base64 bytes inline would be
65
+ * truncated mid-string and unusable by the model — paid for and thrown
66
+ * away. With a bucket the op returns `{ bytes, key, mediaType }` and the
67
+ * app serves or post-processes the object by key. `content`/`scrape`
68
+ * return text and need no bucket; without one, a `screenshot`/`pdf` call is
69
+ * refused as a tool result (never dispatched) so nothing is billed.
70
+ */
71
+ bucket?: string;
47
72
  /** Override the model-facing description (what the tool does). */
48
73
  description?: string;
49
74
  /**
@@ -55,6 +80,14 @@ interface BrowserToolOptions {
55
80
  * keep it deterministic).
56
81
  */
57
82
  needsApproval?: ((input: BrowserToolInput) => boolean) | boolean;
83
+ /**
84
+ * Key prefix inside {@link BrowserToolOptions.bucket} that renders are
85
+ * written under, isolating them from the rest of the bucket. Default: the
86
+ * bucket root. Each render lands at `<root>/<threadKey>/<toolCallId>.<ext>`
87
+ * — derived only from replay-stable identifiers, so a retried step
88
+ * overwrites its own object rather than accumulating one per attempt.
89
+ */
90
+ root?: string;
58
91
  }
59
92
  /**
60
93
  * The model-provided input to a {@link fsTool} call — a discriminated union on
@@ -133,6 +166,12 @@ interface ContainerToolOptions {
133
166
  * `@cloudflare/playwright` `launch` peer) to `createShardDO()` — codegen never
134
167
  * injects that peer, so the browser op throws a directed error until it is wired.
135
168
  *
169
+ * `content`/`scrape` return text. `screenshot`/`pdf` write the render to
170
+ * `opts.bucket` and return `{ bytes, key, mediaType }` — never the bytes
171
+ * themselves, which a 4000-char tool-output cap would truncate into something
172
+ * unusable after paying for the render. Without a bucket those two ops are
173
+ * refused as a tool result and never dispatched.
174
+ *
136
175
  * Security: the model chooses the `url` with no allowlist, so this is an SSRF
137
176
  * surface — pass `opts.needsApproval` to gate calls a prompt-injected model
138
177
  * could aim at internal/link-local endpoints.
@@ -142,12 +181,12 @@ interface ContainerToolOptions {
142
181
  *
143
182
  * export const researcher = defineAgent({
144
183
  * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
145
- * tools: { browser: browserTool() },
184
+ * tools: { browser: browserTool({ bucket: "SANDBOX_BUCKET", root: "renders" }) },
146
185
  * });
147
186
  * ```
148
187
  * @experimental
149
188
  */
150
- declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition<BrowserToolInput, string>;
189
+ declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition<BrowserToolInput, BrowserRenderResult | string>;
151
190
  /**
152
191
  * A batteries-included agent tool that talks to a declared Cloudflare
153
192
  * Container. `name` is the `ctx.containers.<name>` key (the `lunora/containers.ts`
@@ -155,6 +194,12 @@ declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition
155
194
  * the model picks via `op`. The call dispatches to the auto-registered
156
195
  * `sandbox:invoke` action, which carries `ctx.containers`.
157
196
  *
197
+ * Every call is addressed to ONE instance per thread (`ctx.containers.<name>
198
+ * .get(threadKey)`), not a random pool member, so a multi-step session shares a
199
+ * filesystem — size the container's `maxInstances` for the number of live
200
+ * threads. See `sandbox-budgets.ts` for the time budgets and the re-execution
201
+ * ceiling that remains.
202
+ *
158
203
  * By default a read-only (GET/HEAD/OPTIONS, or method-omitted) `fetch` runs
159
204
  * unattended while everything else is gated behind a human approval — an
160
205
  * `exec`; a `fetch` whose path resolves to the privileged exec route (both
@@ -202,4 +247,4 @@ declare const containerTool: (name: string, options?: ContainerToolOptions) => A
202
247
  * @experimental
203
248
  */
204
249
  declare const fsTool: (bucket: string, options?: FsToolOptions) => AgentToolDefinition<FsToolInput>;
205
- export { type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool };
250
+ export { type BrowserRenderResult, type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool };
package/dist/sandbox.mjs CHANGED
@@ -1 +1 @@
1
- import{LunoraError as a}from"@lunora/errors";import{jsonSchema as i}from"ai";import{toFunctionReference as p,SANDBOX_INVOKE_PATH as c}from"./packem_shared/AGENT_MODULE-M4D1EejI.mjs";const s=p(c),d='Drive a headless browser: screenshot a page, render it to PDF, read its HTML content, or scrape it. Set `op` to "screenshot" | "pdf" | "content" | "scrape" and pass the target `url`.',h='Talk to a sandboxed container: `fetch` an HTTP path on it, or `exec` a command inside it. Set `op` to "fetch" (with `path`) or "exec" (with `command`).',l='Read and write files in a persistent sandbox filesystem. Set `op` to "ls" (list a directory), "read", "write" (with `content`), "rm", or "stat", and pass `path`.',u=e=>e.op==="write"||e.op==="rm",T=i({properties:{content:{description:"write: the file contents.",type:"string"},op:{description:"The filesystem operation to run.",enum:["ls","read","write","rm","stat"],type:"string"},path:{description:"The file or directory path, relative to the sandbox root.",type:"string"}},required:["op"],type:"object"}),m=i({properties:{fullPage:{description:"screenshot: capture the full scrollable page rather than just the viewport.",type:"boolean"},op:{description:"The browser operation to run.",enum:["screenshot","scrape","pdf","content"],type:"string"},selector:{description:"scrape: optional CSS selector hint to narrow the extraction.",type:"string"},type:{description:"screenshot: image encoding (default png).",enum:["png","jpeg"],type:"string"},url:{description:"The URL to operate on.",type:"string"}},required:["op","url"],type:"object"}),g=new Set(["DELETE","PATCH","POST","PUT"]),f=e=>e.op==="exec"||g.has((e.method??"GET").toUpperCase()),y=i({properties:{args:{description:"exec: command arguments.",items:{type:"string"},type:"array"},body:{description:"fetch: request body.",type:"string"},command:{description:"exec: the command to run.",type:"string"},method:{description:"fetch: HTTP method (default GET).",type:"string"},op:{description:"The container operation to run.",enum:["fetch","exec"],type:"string"},path:{description:"fetch: request path on the container (e.g. /health).",type:"string"}},required:["op"],type:"object"}),w=(e={})=>({description:e.description??d,execute:(t,r)=>r.run(s,{...t,kind:"browser"}),inputSchema:m,isLunoraAgentTool:!0,...e.needsApproval===void 0?{}:{needsApproval:e.needsApproval}}),O=(e,t={})=>{if(typeof e!="string"||e.length===0)throw new a("INTERNAL","@lunora/agent: containerTool requires a container `name` (the ctx.containers.<name> key from lunora/containers.ts)");const r=t.needsApproval??f;return{description:t.description??h,execute:(o,n)=>n.run(s,{...o,kind:"container",name:e}),inputSchema:y,isLunoraAgentTool:!0,needsApproval:r}},_=(e,t={})=>{if(typeof e!="string"||e.length===0)throw new a("INTERNAL","@lunora/agent: fsTool requires an R2 `bucket` binding name (the r2_bucket declared in wrangler.jsonc)");return{description:t.description??l,execute:(r,o)=>{const n=typeof t.root=="function"?t.root(o):t.root??"";return o.run(s,{...r,bucket:e,kind:"fs",root:n})},inputSchema:T,isLunoraAgentTool:!0,needsApproval:t.needsApproval??u}};export{w as browserTool,O as containerTool,_ as fsTool};
1
+ import{LunoraError as a}from"@lunora/errors";import{jsonSchema as s}from"ai";import{toFunctionReference as h,SANDBOX_INVOKE_PATH as u}from"./packem_shared/AGENT_MODULE-M4D1EejI.mjs";import{S as l,a as T}from"./packem_shared/sandbox-budgets-DDvj4Orr.mjs";const i=h(u),f=e=>{if(e.op==="pdf")return"pdf";if(e.op==="screenshot")return e.type==="jpeg"?"jpeg":"png"},g=new Set(["bucket","path","root"]),m='Drive a headless browser: screenshot a page, render it to PDF, read its HTML content, or scrape it. Set `op` to "screenshot" | "pdf" | "content" | "scrape" and pass the target `url`.',E='Talk to a sandboxed container: `fetch` an HTTP path on it, or `exec` a command inside it. Set `op` to "fetch" (with `path`) or "exec" (with `command`).',y='Read and write files in a persistent sandbox filesystem. Set `op` to "ls" (list a directory), "read", "write" (with `content`), "rm", or "stat", and pass `path`.',S=e=>e.op==="write"||e.op==="rm",A=s({properties:{content:{description:"write: the file contents.",type:"string"},op:{description:"The filesystem operation to run.",enum:["ls","read","write","rm","stat"],type:"string"},path:{description:"The file or directory path, relative to the sandbox root.",type:"string"}},required:["op"],type:"object"}),O=s({properties:{fullPage:{description:"screenshot: capture the full scrollable page rather than just the viewport.",type:"boolean"},op:{description:"The browser operation to run.",enum:["screenshot","scrape","pdf","content"],type:"string"},selector:{description:"scrape: optional CSS selector hint to narrow the extraction.",type:"string"},type:{description:"screenshot: image encoding (default png).",enum:["png","jpeg"],type:"string"},url:{description:"The URL to operate on.",type:"string"}},required:["op","url"],type:"object"}),_=new Set(["DELETE","PATCH","POST","PUT"]),b=e=>e.op==="exec"||_.has((e.method??"GET").toUpperCase()),w=s({properties:{args:{description:"exec: command arguments.",items:{type:"string"},type:"array"},body:{description:"fetch: request body.",type:"string"},command:{description:"exec: the command to run.",type:"string"},method:{description:"fetch: HTTP method (default GET).",type:"string"},op:{description:"The container operation to run.",enum:["fetch","exec"],type:"string"},path:{description:"fetch: request path on the container (e.g. /health).",type:"string"}},required:["op"],type:"object"}),v=(e={})=>{const t=e.bucket!==void 0&&e.bucket.length>0?e.bucket:void 0;return{description:e.description??m,execute:async(n,r)=>{const o=f(n);if(o!==void 0&&t===void 0)return`Cannot run a "${n.op}": this browser tool has no \`bucket\` configured, so the render has nowhere to go. Use "content" or "scrape" for page text, or ask the operator to pass \`browserTool({ bucket })\`.`;const c=o===void 0?{}:{bucket:t,path:`${r.threadKey}/${r.toolCallId}.${o}`,...e.root===void 0?{}:{root:e.root}},p=Object.fromEntries(Object.entries(n).filter(([d])=>!g.has(d)));return await r.run(i,{...p,...c,kind:"browser"},{timeoutMs:l})},inputSchema:O,isLunoraAgentTool:!0,...e.needsApproval===void 0?{}:{needsApproval:e.needsApproval}}},C=(e,t={})=>{if(typeof e!="string"||e.length===0)throw new a("INTERNAL","@lunora/agent: containerTool requires a container `name` (the ctx.containers.<name> key from lunora/containers.ts)");const n=t.needsApproval??b;return{description:t.description??E,execute:(r,o)=>o.run(i,{...r,instance:o.threadKey,kind:"container",name:e},{timeoutMs:T}),inputSchema:w,isLunoraAgentTool:!0,needsApproval:n}},L=(e,t={})=>{if(typeof e!="string"||e.length===0)throw new a("INTERNAL","@lunora/agent: fsTool requires an R2 `bucket` binding name (the r2_bucket declared in wrangler.jsonc)");return{description:t.description??y,execute:(n,r)=>{const o=typeof t.root=="function"?t.root(r):t.root??"";return r.run(i,{...n,bucket:e,kind:"fs",root:o})},inputSchema:A,isLunoraAgentTool:!0,needsApproval:t.needsApproval??S}};export{v as browserTool,C as containerTool,L as fsTool};
@@ -1,4 +1,4 @@
1
- import { S as SkillMarkdownExtras, d as SkillDefinition } from "./packem_shared/types.d-B8WZl1rU.mjs";
1
+ import { S as SkillMarkdownExtras, d as SkillDefinition } from "./packem_shared/types.d-B_VV-x4x.mjs";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { S as SkillMarkdownExtras, d as SkillDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
1
+ import { S as SkillMarkdownExtras, d as SkillDefinition } from "./packem_shared/types.d-B_VV-x4x.js";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/agent",
3
- "version": "1.0.0-alpha.124",
3
+ "version": "1.0.0-alpha.126",
4
4
  "description": "Durable AI agents for Lunora: defineAgent compiles a replay-safe tool-loop onto Cloudflare Workflows, with DO SQLite threads and live message subscriptions",
5
5
  "keywords": [
6
6
  "agent",
@@ -84,12 +84,12 @@
84
84
  "access": "public"
85
85
  },
86
86
  "dependencies": {
87
- "@lunora/ai": "1.0.0-alpha.93",
87
+ "@lunora/ai": "1.0.0-alpha.94",
88
88
  "@lunora/errors": "1.0.0-alpha.39",
89
89
  "@lunora/mail": "1.0.0-alpha.84",
90
90
  "@lunora/server": "1.0.0-alpha.134",
91
91
  "@lunora/values": "1.0.0-alpha.49",
92
- "@lunora/workflow": "1.0.0-alpha.57",
92
+ "@lunora/workflow": "1.0.0-alpha.58",
93
93
  "@modelcontextprotocol/sdk": "^1.30.0",
94
94
  "ai": "7.0.93",
95
95
  "yaml": "^2.9.0"
@@ -1 +0,0 @@
1
- import{createAi as A}from"@lunora/ai";import{t as b}from"./createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs";import{t as x}from"./base64-5eyBfWO3.mjs";import{p as T,d as _,a as v,i as p,b as y,c as E,e as k,f as M,r as w,t as I}from"./voice-turn-CYXdylKF.mjs";import{createStreamGenerate as C,createCompact as R}from"./createAgentGenerate-BOILDUZj.mjs";import{DEFAULT_AGENT_FUNCTION_PATHS as L,toFunctionReference as m}from"./AGENT_MODULE-M4D1EejI.mjs";const N="@cf/openai/whisper-large-v3-turbo",O="@cf/deepgram/aura-1",U=8*1024*1024,B=100,f=4e3,g=f*4+1024,F=4002,D=4003,z=4004,W=256*1024,S=15,G=5e3,X=h=>{if(h===null||typeof h!="object")return!1;const{text:t,type:e}=h;return e==="commit"||e==="interrupt"||e==="text"&&typeof t=="string"};class Y{agent;ai;env;exportName;paths;streamGenerate;sttModel;ttsModel;maxSessionTurns;audioBuffers=new Map;bufferedBytes=new Map;controllers=new Map;state;constructor(t,e,r,s){this.state=t,this.env=e,this.agent=r,this.exportName=s,this.paths=L,this.ai=A({binding:e.AI,env:e}),this.streamGenerate=C(r,e),this.sttModel=r.voice?.stt??N,this.ttsModel=r.voice?.tts??O,this.maxSessionTurns=r.voice?.maxTurns??B}fetch(t){if(t.headers.get("Upgrade")!=="websocket")return new Response("Expected a WebSocket upgrade",{status:426});const r=new URL(t.url).searchParams.get("threadKey");if(!r)return new Response("Missing threadKey",{status:400});const s=globalThis.WebSocketPair,i=new s,n=i[0],o=i[1];this.state.acceptWebSocket(o);const c=crypto.randomUUID(),d=T(t.headers.get("x-lunora-identity")),a=_(t.headers.get("x-lunora-userid")),u=v(t.headers.get("x-lunora-identity-exp"));if(p(u))return y(o),new Response(null,{status:101,webSocket:n});o.serializeAttachment?.({connectionId:c,threadKey:r,turn:0,...u===void 0?{}:{expiresAt:u},...d===void 0?{}:{identity:d},...a===void 0?{}:{userId:a}}),this.send(o,{audioFormat:this.agent.voice?.audioFormat??"mp3",type:"ready"});const l=this.agent.voice?.greeting;return l&&l.length>0&&this.state.waitUntil?.(this.speakGreeting(o,c,r,a,d,l)),new Response(null,{status:101,webSocket:n})}async webSocketMessage(t,e){const r=t.deserializeAttachment?.();if(r){if(p(r.expiresAt)){y(t);return}try{if(typeof e=="string"){await this.handleControl(t,r,e);return}this.bufferAudio(r.connectionId,new Uint8Array(e),t)}catch(s){this.send(t,{message:s instanceof Error?s.message:String(s),type:"error"})}}}webSocketClose(t){this.cleanupSocket(t)}webSocketError(t){this.cleanupSocket(t)}resolveRun(t,e){const r=t===void 0&&e===void 0?void 0:{...e===void 0?{}:{claims:e},...t===void 0?{}:{userId:t}};return b({env:this.env,label:"@lunora/agent voice",...r===void 0?{}:{identity:r}})}async transcribe(t){const e=E(t);return k(await this.ai.run(this.sttModel,{audio:x(e)}))}async synthesize(t,e){const r=this.agent.voice?.speaker;return M(await this.ai.run(this.ttsModel,{text:t,...r===void 0?{}:{speaker:r}},e===void 0?void 0:{signal:e}))}async handleControl(t,e,r){if(r.length>g){this.send(t,{message:`control frame exceeds the maximum of ${String(g)} characters`,type:"error"}),this.closeSocket(t,z,"control_frame_limit");return}let s;try{s=JSON.parse(r)}catch{return}if(!X(s)){this.send(t,{message:'unsupported control frame — expected { type: "text", text }, { type: "commit" } or { type: "interrupt" }',type:"error"});return}const i=s;if(i.type==="interrupt"){this.controllers.get(e.connectionId)?.abort();return}if(i.type==="text"&&i.text.length>f){this.send(t,{message:`text frame exceeds the maximum of ${String(f)} characters`,type:"error"});return}if(e.turn>=this.maxSessionTurns){this.send(t,{message:`voice session reached its ${String(this.maxSessionTurns)}-turn limit — reconnect to continue`,type:"error"}),this.closeSocket(t,F,"turn_limit");return}if(this.controllers.has(e.connectionId)){i.type==="commit"&&this.drainAudio(e.connectionId),this.send(t,{message:"a turn is already in progress — send an interrupt before the next utterance",type:"error"});return}if(i.type==="commit"){const n=this.drainAudio(e.connectionId);await this.runTurn(t,e,{pcm:n});return}await this.runTurn(t,e,{text:i.text})}bufferAudio(t,e,r){const s=(this.bufferedBytes.get(t)??0)+e.byteLength;if(s>U){this.audioBuffers.delete(t),this.bufferedBytes.delete(t),this.send(r,{message:"utterance exceeded the maximum buffer — send a commit sooner",type:"error"}),this.closeSocket(r,D,"utterance_too_large");return}const i=this.audioBuffers.get(t)??[];i.push(e),this.audioBuffers.set(t,i),this.bufferedBytes.set(t,s)}drainAudio(t){const e=this.audioBuffers.get(t)??[];this.audioBuffers.delete(t),this.bufferedBytes.delete(t);const r=e.reduce((n,o)=>n+o.byteLength,0),s=new Uint8Array(r);let i=0;for(const n of e)s.set(n,i),i+=n.byteLength;return s}async runTurn(t,e,r){const s=new AbortController;this.controllers.set(e.connectionId,s);try{await w({agent:this.agent,compact:R(),connectionId:e.connectionId,env:this.env,exportName:this.exportName,paths:this.paths,run:this.resolveRun(e.userId,e.identity),send:i=>{this.send(t,i)},sendAudio:i=>{this.sendAudio(t,i)},signal:s.signal,streamGenerate:this.streamGenerate,synthesize:async(i,n)=>this.synthesizeWithSignal(i,n),threadKey:e.threadKey,transcribe:async i=>this.transcribe(i),turn:e.turn,waitForDrain:async()=>this.waitForSocketDrain(t),...e.userId===void 0?{}:{owner:e.userId},...r.pcm===void 0?{}:{pcm:r.pcm},...r.text===void 0?{}:{text:r.text}})}finally{this.controllers.get(e.connectionId)===s&&this.controllers.delete(e.connectionId),t.serializeAttachment?.({...e,turn:e.turn+1})}}async speakGreeting(t,e,r,s,i,n){const o=this.resolveRun(s,i),c=new AbortController,d=()=>c.signal.aborted;this.controllers.set(e,c);try{if((await o(m(this.paths.ensureThread),{agent:this.exportName,key:r,...this.agent.initialState===void 0?{}:{initialState:this.agent.initialState},...s===void 0?{}:{owner:s}}))?.outcome!=="created")return;for await(const u of I(await this.synthesizeWithSignal(n,c.signal))){if(d()||(await this.waitForSocketDrain(t),d()))break;this.sendAudio(t,u)}d()||(await o(m(this.paths.appendMessage),{content:n,messageKey:"voice:greeting:assistant",role:"assistant",threadKey:r}),this.send(t,{text:n,type:"assistant_done"}))}catch(a){this.send(t,{message:a instanceof Error?a.message:String(a),type:"error"})}finally{this.controllers.get(e)===c&&this.controllers.delete(e)}}async synthesizeWithSignal(t,e){return e.aborted?new Uint8Array(0):this.synthesize(t,e)}cleanupSocket(t){const e=t.deserializeAttachment?.();e&&(this.controllers.get(e.connectionId)?.abort(),this.controllers.delete(e.connectionId),this.audioBuffers.delete(e.connectionId),this.bufferedBytes.delete(e.connectionId))}closeSocket(t,e,r){try{t.close?.(e,r)}catch{}}send(t,e){try{t.send(JSON.stringify(e))}catch{}}sendAudio(t,e){try{t.send(e)}catch{}}async waitForSocketDrain(t){const e=t;let r=0;for(;typeof e.bufferedAmount=="number"&&e.bufferedAmount>W&&r<G;)await new Promise(s=>{setTimeout(s,S)}),r+=S}}export{Y as default};
@@ -1 +0,0 @@
1
- const a=n=>{let t="";for(let e=0;e<n.length;e+=32768)t+=String.fromCharCode(...n.subarray(e,e+32768));return btoa(t)},o=n=>{const t=atob(n),r=new Uint8Array(t.length);for(let e=0;e<t.length;e+=1)r[e]=t.codePointAt(e)??0;return r},s=n=>{const t=n.replaceAll("-","+").replaceAll("_","/"),r=t+"=".repeat((4-t.length%4)%4);return o(r)};export{o as a,s as f,a as t};
@@ -1 +0,0 @@
1
- import{defineWorkflow as i}from"@lunora/workflow";import{r as m}from"./agent-loop-ByRhLA54.mjs";import{createStreamGenerate as p,createAgentGenerate as l,createEpisodeExtract as f,createGraphExtract as d,createCompact as c}from"./createAgentGenerate-BOILDUZj.mjs";import{agentDefaultName as u}from"../naming.mjs";import{DEFAULT_AGENT_FUNCTION_PATHS as A}from"./AGENT_MODULE-M4D1EejI.mjs";import{t as T}from"./createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs";import{otlpTelemetry as y}from"./otlpTelemetry-ByqrB-72.mjs";const E=(t,r)=>T({env:r,label:"@lunora/agent",...t===void 0?{}:{identity:{userId:t}}}),O=(t,r,n)=>{const e=r.LUNORA_OTLP_ENDPOINT;if(typeof e!="string"||e===""||t.telemetry?.isEnabled===!1)return t;const a=typeof r.LUNORA_OTLP_TOKEN=="string"?r.LUNORA_OTLP_TOKEN:void 0,o=[t.telemetry?.integrations].flat().filter(s=>s!==void 0);return{...t,telemetry:{...t.telemetry,integrations:[...o,y({conversationId:n,endpoint:e,token:a})],isEnabled:!0}}},I=(t,r,n)=>i({handler:async e=>{const a=O(t,e.env,e.params.threadKey);return m({agent:a,compact:c(),env:e.env,exportName:r,extractGraph:d(),extractEpisode:f(),generate:l(a,e.env),instanceId:e.event.instanceId,params:e.params,paths:n?.paths??A,run:E(e.params.owner,e.env),step:e.step,streamGenerate:p(a,e.env)})},name:t.name??u(r)});export{I as default};
@@ -1 +0,0 @@
1
- import"./base64-5eyBfWO3.mjs";import{p as s,c as p,f as m,e as n,r as c,t as d}from"./voice-turn-CYXdylKF.mjs";import"./agent-loop-ByRhLA54.mjs";import"./buildModelMessages-Y4tGo1T9.mjs";import"./AGENT_MODULE-M4D1EejI.mjs";export{s as parseIdentity,p as pcmToWav,m as readSynthesisAudio,n as readTranscriptionText,c as runVoiceTurn,d as toByteIterable};
@@ -1,5 +0,0 @@
1
- import{LunoraError as c}from"@lunora/errors";import{initLunora as h}from"@lunora/server";import{v as t}from"@lunora/values";import{t as g}from"./base64-5eyBfWO3.mjs";const{internalAction:w}=h.dataModel().create(),m=()=>globalThis.document?.documentElement?.outerHTML??"",l=1e6,b=20,x=new TextEncoder,y=async(e,n)=>{const o=n.url??"";switch(n.op){case"content":return e.content(o);case"pdf":return{data:g(await e.pdf(o)),encoding:"base64",mediaType:"application/pdf"};case"scrape":return e.scrape(o,m);case"screenshot":{const s=await e.screenshot(o,{...n.fullPage===void 0?{}:{fullPage:n.fullPage},...n.type===void 0?{}:{type:n.type}});return{data:g(s),encoding:"base64",mediaType:n.type==="jpeg"?"image/jpeg":"image/png"}}default:throw new c("INTERNAL",`@lunora/agent: sandbox browser op "${n.op}" is not supported`)}},E=e=>{const n=[`exit code: ${String(e.code)}`];return e.stdout.length>0&&n.push(`stdout:
2
- ${e.stdout}`),e.stderr.length>0&&n.push(`stderr:
3
- ${e.stderr}`),n.join(`
4
-
5
- `)},$=async(e,n)=>{const o=e.any();if(n.op==="exec")try{return E(await o.exec(n.command??"",{args:n.args??[]}))}catch(s){return`exec failed: ${s instanceof Error?s.message:String(s)}`}if(n.op==="fetch")return(await o.fetch(n.path??"/",{...n.body===void 0?{}:{body:n.body},method:n.method??"GET"})).text();throw new c("INTERNAL",`@lunora/agent: sandbox container op "${n.op}" is not supported`)},u=e=>{let n=0,o=e.length;for(;n<o&&e[n]==="/";)n+=1;for(;o>n&&e[o-1]==="/";)o-=1;return e.slice(n,o)},T=(e,n)=>{const o=u(e),s=[];for(const r of n.split("/"))if(!(r===""||r===".")){if(r===".."){if(s.length===0)throw new c("BAD_REQUEST","@lunora/agent: fs path escapes the sandbox root");s.pop();continue}s.push(r)}const a=s.join("/");return o&&a?`${o}/${a}`:o||a},S=async(e,n,o)=>{const s=n.length>0?`${n}/`:"",a=o.length>0?o.length+1:0,r=[];let i;for(let d=0;d<b;d+=1){const p=await e.list({prefix:s,...i===void 0?{}:{cursor:i}});for(const f of p.objects)r.push(f.key.slice(a));if(!p.truncated||p.cursor===void 0)return{entries:r,truncated:!1};i=p.cursor}return{entries:r,truncated:!0}},N=async(e,n,o)=>{const s=u(n),a=T(n,o.path??"");switch(o.op){case"ls":{const{entries:r,truncated:i}=await S(e,a,s);return i?{entries:r,truncated:!0}:{entries:r}}case"read":{const r=await e.head(a);if(r&&r.size>l)throw new c("BAD_REQUEST",`@lunora/agent: fs read: "${o.path??""}" is ${String(r.size)} bytes (max ${String(l)})`);const i=await e.get(a);if(!i)throw new c("NOT_FOUND",`@lunora/agent: fs read: no file at "${o.path??""}"`);return i.text()}case"rm":return await e.delete(a),{path:o.path??"",removed:!0};case"stat":{const r=await e.head(a);return r?{exists:!0,size:r.size}:{exists:!1}}case"write":{const r=o.content??"",i=x.encode(r).length;if(i>l)throw new c("BAD_REQUEST",`@lunora/agent: fs write: ${String(i)} bytes exceeds the max (${String(l)})`);return await e.put(a,r),{bytes:i,path:o.path??"",wrote:!0}}default:throw new c("INTERNAL",`@lunora/agent: sandbox fs op "${o.op}" is not supported`)}},_=()=>({invoke:w.input({args:t.optional(t.array(t.string())),body:t.optional(t.string()),bucket:t.optional(t.string()),command:t.optional(t.string()),content:t.optional(t.string()),fullPage:t.optional(t.boolean()),kind:t.union(t.literal("browser"),t.literal("container"),t.literal("fs")),method:t.optional(t.string()),name:t.optional(t.string()),op:t.string(),path:t.optional(t.string()),root:t.optional(t.string()),selector:t.optional(t.string()),type:t.optional(t.string()),url:t.optional(t.string())}).action(async({args:n,ctx:o})=>{const s=o,a=n;if(a.kind==="browser"){if(!s.browser)throw new c("INTERNAL","@lunora/agent: sandbox browser op needs `ctx.browser` — install @lunora/browser and run codegen");return y(s.browser,a)}if(a.kind==="fs"){const d=s.env?.[a.bucket??""];if(!d||typeof d.get!="function")throw new c("INTERNAL",`@lunora/agent: sandbox fs op found no R2 bucket "${a.bucket??""}" on env — declare the r2_bucket binding in wrangler.jsonc and run codegen`);return N(d,a.root??"",a)}const r=a.name??"",i=s.containers?.[r];if(!i)throw new c("INTERNAL",`@lunora/agent: sandbox container op found no ctx.containers["${r}"] — declare the container in lunora/containers.ts and run codegen`);return $(i,a)})});export{T as resolveFsKey,N as runFsOp,_ as sandboxComponent};
@@ -1 +0,0 @@
1
- import{f as P,a as q}from"./base64-5eyBfWO3.mjs";import{c as z}from"./agent-loop-ByRhLA54.mjs";import{buildModelMessages as G}from"./buildModelMessages-Y4tGo1T9.mjs";import{toFunctionReference as T}from"./AGENT_MODULE-M4D1EejI.mjs";const D=new TextDecoder;new TextEncoder;const N="=",Q=t=>{if(t)try{const e=t[0]==="{"?t:D.decode(P(t)),n=JSON.parse(e);if(n&&typeof n=="object"&&!Array.isArray(n))return n}catch{}},ct=t=>{if(t){if(!t.startsWith(N))return t;try{return D.decode(P(t.slice(N.length)))}catch{return}}},dt=t=>{const e=Number(t);return Number.isFinite(e)&&e>0?e:void 0},ut=t=>typeof t=="number"&&Date.now()>=t,ft=t=>{try{t.send(JSON.stringify({code:"TOKEN_EXPIRED",error:{code:"TOKEN_EXPIRED",message:"authentication token expired"},message:"authentication token expired",type:"error"})),t.close?.(4001,"token_expired")}catch{}},Y=16e3,Z=1,tt=16,M=/[.!?]\s/u,et=/\s/u,nt=async function*(t){if(t instanceof Uint8Array){yield t;return}if(t instanceof ReadableStream){const e=t.getReader();try{for(;;){const{done:n,value:r}=await e.read();if(n)break;yield r}}finally{e.releaseLock()}return}yield*t},yt=(t,e=Y,n=Z,r=tt)=>{const i=n*r/8,p=e*i,m=new ArrayBuffer(44+t.byteLength),s=new DataView(m),u=(l,E)=>{for(let d=0;d<E.length;d+=1)s.setUint8(l+d,E.codePointAt(d)??0)};u(0,"RIFF"),s.setUint32(4,36+t.byteLength,!0),u(8,"WAVE"),u(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,n,!0),s.setUint32(24,e,!0),s.setUint32(28,p,!0),s.setUint16(32,i,!0),s.setUint16(34,r,!0),u(36,"data"),s.setUint32(40,t.byteLength,!0);const a=new Uint8Array(m);return a.set(t,44),a},st=t=>{const e=[];let n=t,r=M.exec(n);for(;r;){let i=r.index+1;for(;i<n.length&&et.test(n[i]??"");)i+=1;const p=n.slice(0,i).trim();p.length>0&&e.push(p),n=n.slice(i),r=M.exec(n)}return{rest:n,sentences:e}},pt=async t=>{const{agent:e,compact:n,connectionId:r,env:i,exportName:p,owner:m,paths:s,pcm:u,run:a,send:l,sendAudio:E,signal:d,streamGenerate:C,synthesize:H,text:L,threadKey:o,transcribe:F,turn:U,waitForDrain:O}=t,h=()=>d.aborted,v=T(s.appendMessage),$=T(s.ensureThread),B=T(s.listMessages),b=T(s.patchThread);await a($,{agent:p,key:o,...e.initialState===void 0?{}:{initialState:e.initialState},...m===void 0?{}:{owner:m}});try{const f=(L??(u?await F(u):"")).trim();if(f.length===0)return await a(b,{key:o,status:"idle"}),{assistantText:"",interrupted:!1,userText:""};l({text:f,type:"user_transcript"}),await a(v,{content:f,messageKey:`voice:${r}:${String(U)}:user`,role:"user",threadKey:o}),await a(b,{key:o,status:"running"});const I=typeof e.instructions=="function"?e.instructions({env:i,input:f,threadKey:o}):e.instructions,K=await a(B,{key:o}),{history:j,summary:R}=await z({agent:e,compact:n,env:i},K),W=G({history:j,...I===void 0?{}:{instructions:I},...R===void 0?{}:{summary:R}});let g="",w="",S=Promise.resolve();const V=async c=>{if(h()||c.length===0)return;let x=!1;try{for await(const y of nt(await H(c,d))){if(h()||(await O?.(),h()))break;E(y),x=!0}}catch(y){l({message:y instanceof Error?y.message:String(y),type:"error"})}x&&(w=w.length>0?`${w} ${c}`:c)},_=c=>{S=S.then(async()=>V(c))},X=await C({messages:W,signal:d},c=>{if(h())return;l({text:c,type:"assistant_delta"}),g+=c;const{rest:x,sentences:y}=st(g);g=x;for(const J of y)_(J)});!h()&&g.trim().length>0&&_(g.trim()),g="",await S;const k=h(),A=k?w:X.text;return await a(v,{content:A,messageKey:`voice:${r}:${String(U)}:assistant`,role:"assistant",threadKey:o}),await a(b,{key:o,status:"idle"}),l(k?{type:"interrupted"}:{text:A,type:"assistant_done"}),{assistantText:A,interrupted:k,userText:f}}catch(f){throw await a(b,{key:o,status:"idle"}),f}},lt=t=>Q(t),ht=t=>{if(typeof t=="object"&&t!==null&&"text"in t){const{text:e}=t;return typeof e=="string"?e:""}return""},gt=t=>{if(t instanceof Uint8Array||t instanceof ReadableStream)return t;if(t instanceof Response&&t.body)return t.body;if(typeof t=="object"&&t!==null&&"audio"in t){const{audio:e}=t;if(typeof e=="string")return q(e);if(e instanceof ReadableStream||e instanceof Uint8Array)return e}return new Uint8Array(0)};export{dt as a,ft as b,yt as c,ct as d,ht as e,gt as f,ut as i,lt as p,pt as r,nt as t};