@lunora/agent 1.0.0-alpha.11 → 1.0.0-alpha.111

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.
Files changed (88) hide show
  1. package/dist/channels.d.mts +3 -11
  2. package/dist/channels.d.ts +3 -11
  3. package/dist/channels.mjs +1 -1
  4. package/dist/component.d.mts +2 -0
  5. package/dist/component.d.ts +2 -0
  6. package/dist/component.mjs +2 -2
  7. package/dist/inbound.d.mts +7 -5
  8. package/dist/inbound.d.ts +7 -5
  9. package/dist/inbound.mjs +1 -1
  10. package/dist/index.d.mts +49 -8
  11. package/dist/index.d.ts +49 -8
  12. package/dist/index.mjs +1 -1
  13. package/dist/naming.mjs +1 -1
  14. package/dist/packem_shared/AGENT_MODULE-M4D1EejI.mjs +1 -0
  15. package/dist/packem_shared/VoiceSessionDO-D238XrlM.mjs +1 -0
  16. package/dist/packem_shared/adaptMcpResult-CVlq-_TO.mjs +2 -0
  17. package/dist/packem_shared/agent-loop-CE_Q6bk9.mjs +3 -0
  18. package/dist/packem_shared/agentAsTool-CgZb6ycK.mjs +1 -0
  19. package/dist/packem_shared/base64-5eyBfWO3.mjs +1 -0
  20. package/dist/packem_shared/braintrustTelemetry-Byne8uPK.mjs +1 -0
  21. package/dist/packem_shared/branch-marker-boZ00zmk.mjs +1 -0
  22. package/dist/packem_shared/buildModelMessages-Y4tGo1T9.mjs +5 -0
  23. package/dist/packem_shared/codeTool-BQb-K3my.mjs +1 -0
  24. package/dist/packem_shared/collectAgenticMemoryTools-BeMWT2qt.mjs +1 -0
  25. package/dist/packem_shared/combineTelemetry-DgE9W9G8.mjs +1 -0
  26. package/dist/packem_shared/common-CJSjtsfv.mjs +1 -0
  27. package/dist/packem_shared/compileAgentWorkflow-DVHi7NYD.mjs +1 -0
  28. package/dist/packem_shared/component-shared-G8ngerkU.mjs +1 -0
  29. package/dist/packem_shared/consoleTelemetry-BZY3Y-Ly.mjs +1 -0
  30. package/dist/packem_shared/createAgentContext-BvU9LP43.mjs +1 -0
  31. package/dist/packem_shared/createAgentGenerate-BOILDUZj.mjs +3 -0
  32. package/dist/packem_shared/createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs +1 -0
  33. package/dist/packem_shared/defineAgent-KgH-gwYg.mjs +3 -0
  34. package/dist/packem_shared/defineSkill-DGGvDWNq.mjs +1 -0
  35. package/dist/packem_shared/fnv1a-BNN96GYb.mjs +1 -0
  36. package/dist/packem_shared/functionTool-D3CP4f8-.mjs +1 -0
  37. package/dist/packem_shared/in-flight-calls-GH1A_1P3.mjs +1 -0
  38. package/dist/packem_shared/normalizeEntityName-k3fjicAG.mjs +2 -0
  39. package/dist/packem_shared/otlpTelemetry-ByqrB-72.mjs +1 -0
  40. package/dist/packem_shared/positive-integer-ztHqqpBk.mjs +1 -0
  41. package/dist/packem_shared/runAgentLoop-Drn1NcCc.mjs +1 -0
  42. package/dist/packem_shared/runVoiceTurn-BtcmyaBo.mjs +1 -0
  43. package/dist/packem_shared/sandboxComponent-BtOkUYpY.mjs +5 -0
  44. package/dist/packem_shared/sentryTelemetry-BHeydiAB.mjs +1 -0
  45. package/dist/packem_shared/tool-output-Cmk3JWMG.mjs +1 -0
  46. package/dist/packem_shared/{types.d-boAM2Yi1.d.mts → types.d-B8WZl1rU.d.mts} +255 -22
  47. package/dist/packem_shared/{types.d-boAM2Yi1.d.ts → types.d-B8WZl1rU.d.ts} +255 -22
  48. package/dist/packem_shared/voice-turn-BRj_it54.mjs +1 -0
  49. package/dist/reply.d.mts +40 -0
  50. package/dist/reply.d.ts +40 -0
  51. package/dist/reply.mjs +1 -0
  52. package/dist/sandbox.d.mts +14 -13
  53. package/dist/sandbox.d.ts +14 -13
  54. package/dist/sandbox.mjs +1 -1
  55. package/dist/skill-markdown.d.mts +36 -0
  56. package/dist/skill-markdown.d.ts +36 -0
  57. package/dist/skill-markdown.mjs +1 -0
  58. package/dist/telemetry/index.d.mts +152 -33
  59. package/dist/telemetry/index.d.ts +152 -33
  60. package/dist/telemetry/index.mjs +1 -1
  61. package/package.json +18 -9
  62. package/dist/packem_shared/AGENT_MODULE-BXx-tSCe.mjs +0 -1
  63. package/dist/packem_shared/VoiceSessionDO-CTaBSdXk.mjs +0 -1
  64. package/dist/packem_shared/adaptMcpResult-18G7zpFo.mjs +0 -2
  65. package/dist/packem_shared/agentAsTool-CJCTyAQG.mjs +0 -1
  66. package/dist/packem_shared/base64-jU-Ci6Tu.mjs +0 -1
  67. package/dist/packem_shared/braintrustTelemetry-CnOI5WAw.mjs +0 -1
  68. package/dist/packem_shared/buildModelMessages-Dm2wVNQy.mjs +0 -5
  69. package/dist/packem_shared/codeTool-DYJVy-bH.mjs +0 -1
  70. package/dist/packem_shared/collectAgenticMemoryTools-DBcP8dUa.mjs +0 -1
  71. package/dist/packem_shared/combineTelemetry-oLCV_EPV.mjs +0 -1
  72. package/dist/packem_shared/common-Dck97z3Y.mjs +0 -1
  73. package/dist/packem_shared/compileAgentWorkflow-Dwp-xNNO.mjs +0 -1
  74. package/dist/packem_shared/consoleTelemetry-CaoI-NGg.mjs +0 -1
  75. package/dist/packem_shared/createAgentContext-B1IfI7ro.mjs +0 -1
  76. package/dist/packem_shared/createAgentGenerate-BUVImo85.mjs +0 -3
  77. package/dist/packem_shared/createDispatchRunner-CQ8HXscn-BrqT0bIG.mjs +0 -1
  78. package/dist/packem_shared/defineAgent-CKhzzmys.mjs +0 -3
  79. package/dist/packem_shared/defineSkill-DfJxCwfZ.mjs +0 -1
  80. package/dist/packem_shared/functionTool-CWHhkYqd.mjs +0 -1
  81. package/dist/packem_shared/graph-component-B2YmC2eL.mjs +0 -2
  82. package/dist/packem_shared/memory-DNRyyp1b.mjs +0 -1
  83. package/dist/packem_shared/normalizeEntityName-BQpyssvL.mjs +0 -1
  84. package/dist/packem_shared/otlpTelemetry-B829MdU-.mjs +0 -1
  85. package/dist/packem_shared/runAgentLoop-DNAt-pGz.mjs +0 -3
  86. package/dist/packem_shared/runVoiceTurn-PkMsfG_v.mjs +0 -1
  87. package/dist/packem_shared/sandboxComponent-BhaGHsQZ.mjs +0 -1
  88. package/dist/packem_shared/sentryTelemetry-Dr2aPuvl.mjs +0 -1
@@ -1,5 +1,5 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-boAM2Yi1.mjs";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-boAM2Yi1.mjs";
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";
3
3
  import '@lunora/mail/inbound';
4
4
  import 'ai';
5
5
  /**
@@ -51,14 +51,6 @@ interface AgentChannelTarget {
51
51
  * @experimental
52
52
  */
53
53
  type InboundChannelHandler = (request: Request, env: Record<string, unknown>) => Promise<Response>;
54
- /**
55
- * Whether a `workflow.create()` rejection is a duplicate-instance-id error — the
56
- * idempotency signal — as opposed to a transient/config failure (Workflows
57
- * service error, instance-creation quota, bad params). Only the former may be
58
- * acked; every other failure MUST surface so the handler returns non-2xx and the
59
- * provider redelivers, rather than silently dropping the event.
60
- */
61
- declare const isDuplicateInstanceError: (error: unknown) => boolean;
62
54
  /**
63
55
  * Build an HTTP handler that starts a durable agent run from a verified inbound
64
56
  * webhook. The channel is detected from the signature headers; EACH eligible
@@ -71,4 +63,4 @@ declare const isDuplicateInstanceError: (error: unknown) => boolean;
71
63
  * @experimental
72
64
  */
73
65
  declare const dispatchAgentChannel: (targets: ReadonlyArray<AgentChannelTarget>) => InboundChannelHandler;
74
- export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, isDuplicateInstanceError, verifyDiscord, verifyGithub, verifySlack };
66
+ export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
@@ -1,5 +1,5 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-boAM2Yi1.js";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-boAM2Yi1.js";
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";
3
3
  import '@lunora/mail/inbound';
4
4
  import 'ai';
5
5
  /**
@@ -51,14 +51,6 @@ interface AgentChannelTarget {
51
51
  * @experimental
52
52
  */
53
53
  type InboundChannelHandler = (request: Request, env: Record<string, unknown>) => Promise<Response>;
54
- /**
55
- * Whether a `workflow.create()` rejection is a duplicate-instance-id error — the
56
- * idempotency signal — as opposed to a transient/config failure (Workflows
57
- * service error, instance-creation quota, bad params). Only the former may be
58
- * acked; every other failure MUST surface so the handler returns non-2xx and the
59
- * provider redelivers, rather than silently dropping the event.
60
- */
61
- declare const isDuplicateInstanceError: (error: unknown) => boolean;
62
54
  /**
63
55
  * Build an HTTP handler that starts a durable agent run from a verified inbound
64
56
  * webhook. The channel is detected from the signature headers; EACH eligible
@@ -71,4 +63,4 @@ declare const isDuplicateInstanceError: (error: unknown) => boolean;
71
63
  * @experimental
72
64
  */
73
65
  declare const dispatchAgentChannel: (targets: ReadonlyArray<AgentChannelTarget>) => InboundChannelHandler;
74
- export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, isDuplicateInstanceError, verifyDiscord, verifyGithub, verifySlack };
66
+ export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
package/dist/channels.mjs CHANGED
@@ -1 +1 @@
1
- import{LunoraError as w}from"@lunora/errors";const m=300,v=/[^0-9a-f]/iu,x=new TextEncoder,d=e=>new Uint8Array(x.encode(e)),g=e=>{if(e.length%2!==0||v.test(e))throw new w("BAD_REQUEST","@lunora/agent: malformed hex signature");const t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r+=1)t[r]=Number.parseInt(e.slice(r*2,r*2+2),16);return t},b=async(e,t,r)=>{try{const s=await crypto.subtle.importKey("raw",d(e),{hash:"SHA-256",name:"HMAC"},!1,["verify"]);return await crypto.subtle.verify("HMAC",s,g(r),d(t))}catch{return!1}},k=(e,t,r)=>Number.isFinite(e)&&Math.abs(t-e)<=r,A=async e=>{const{body:t,now:r=Date.now()/1e3,signature:s,signingSecret:n,timestamp:a,tolerance:o=m}=e;return!s||!a||!s.startsWith("v0=")||!k(Number(a),r,o)?!1:b(n,`v0:${a}:${t}`,s.slice(3))},E=async e=>{const{body:t,secret:r,signature:s}=e;return s?.startsWith("sha256=")?b(r,t,s.slice(7)):!1},S=async e=>{const{body:t,publicKey:r,signature:s,timestamp:n}=e;if(!s||!n)return!1;try{const a=await crypto.subtle.importKey("raw",g(r),{name:"Ed25519"},!1,["verify"]);return await crypto.subtle.verify({name:"Ed25519"},a,g(s),d(n+t))}catch{return!1}},N=e=>{if(e.has("x-slack-signature"))return"slack";if(e.has("x-hub-signature-256"))return"github";if(e.has("x-signature-ed25519"))return"discord"},R=(e,t)=>{if(typeof e=="function")return e(t);const r=t[e];return typeof r=="string"?r:void 0},I=async(e,t,r,s)=>{switch(e){case"discord":return S({body:s,publicKey:t,signature:r.get("x-signature-ed25519")??void 0,timestamp:r.get("x-signature-timestamp")??void 0});case"github":return E({body:s,secret:t,signature:r.get("x-hub-signature-256")??void 0});case"slack":return A({body:s,signature:r.get("x-slack-signature")??void 0,signingSecret:t,timestamp:r.get("x-slack-request-timestamp")??void 0});default:return!1}},D=(e,t)=>{if(e==="discord")try{if(JSON.parse(t).type===1)return Response.json({type:1})}catch{}},K=/[^\w-]/gu,U=/already[\s-]?exists/iu,u=()=>new Response(void 0,{status:200}),$=e=>U.test(e instanceof Error?e.message:String(e)),C=(e,t,r)=>{if(e==="github")return t.get("x-github-delivery")??void 0;try{const s=JSON.parse(r),n=e==="slack"?s.event_id:s.id;return typeof n=="string"?n:void 0}catch{return}},H=async(e,t,r,s)=>{const n=s===void 0?"":s.replaceAll(K,"");if(n==="")return await e.create({params:t}),u();try{await e.create({id:`${r}-${n}`.slice(0,60),params:t})}catch(a){if($(a))return u();throw a}return u()},j=e=>async(t,r)=>{const s=await t.text(),n=N(t.headers);if(n===void 0)return new Response("Unrecognized webhook",{status:400});const a=e.map(i=>({config:i.agent.onInbound,target:i})).filter(i=>i.config?.channel===n),o={channel:n,headers:t.headers,json:()=>JSON.parse(s),rawBody:s};let f=!1;for(const{config:i,target:l}of a){const y=R(i.secret,r);if(y===void 0||!await I(n,y,t.headers,s))continue;f=!0;const h=D(n,s);if(h)return h;const p=await i.map(o);if(!p)continue;const c=r[l.binding];if(!c||typeof c.create!="function")throw new w("INTERNAL",`@lunora/agent: no Workflow binding "${l.binding}" on env for an inbound-channel agent — run codegen/dev so wrangler.jsonc declares it`);return await H(c,p,n,C(n,t.headers,s))}return f?new Response(void 0,{status:204}):new Response("Invalid signature",{status:401})};export{j as dispatchAgentChannel,$ as isDuplicateInstanceError,S as verifyDiscord,E as verifyGithub,A as verifySlack};
1
+ import{LunoraError as d}from"@lunora/errors";import{isDuplicateInstanceError as w}from"@lunora/workflow";import{h as b,B as E}from"./packem_shared/branch-marker-boZ00zmk.mjs";import{f as A}from"./packem_shared/fnv1a-BNN96GYb.mjs";const R=300,k=/[^0-9a-f]/iu,x=new TextEncoder,f=t=>new Uint8Array(x.encode(t)),l=t=>{if(t.length%2!==0||k.test(t))throw new d("BAD_REQUEST","@lunora/agent: malformed hex signature");const e=new Uint8Array(t.length/2);for(let n=0;n<e.length;n+=1)e[n]=Number.parseInt(t.slice(n*2,n*2+2),16);return e},m=async(t,e,n)=>{try{const r=await crypto.subtle.importKey("raw",f(t),{hash:"SHA-256",name:"HMAC"},!1,["verify"]);return await crypto.subtle.verify("HMAC",r,l(n),f(e))}catch{return!1}},N=(t,e,n)=>Number.isFinite(t)&&Math.abs(e-t)<=n,S=async t=>{const{body:e,now:n=Date.now()/1e3,signature:r,signingSecret:s,timestamp:a,tolerance:o=R}=t;return!r||!a||!r.startsWith("v0=")||!N(Number(a),n,o)?!1:m(s,`v0:${a}:${e}`,r.slice(3))},T=async t=>{const{body:e,secret:n,signature:r}=t;return r?.startsWith("sha256=")?m(n,e,r.slice(7)):!1},C=async t=>{const{body:e,publicKey:n,signature:r,timestamp:s}=t;if(!r||!s)return!1;try{const a=await crypto.subtle.importKey("raw",l(n),{name:"Ed25519"},!1,["verify"]);return await crypto.subtle.verify({name:"Ed25519"},a,l(r),f(s+e))}catch{return!1}},I=t=>{if(t.has("x-slack-signature"))return"slack";if(t.has("x-hub-signature-256"))return"github";if(t.has("x-signature-ed25519"))return"discord"},_=(t,e)=>{if(typeof t=="function")return t(e);const n=e[t];return typeof n=="string"?n:void 0},B=async(t,e,n,r)=>{switch(t){case"discord":return C({body:r,publicKey:e,signature:n.get("x-signature-ed25519")??void 0,timestamp:n.get("x-signature-timestamp")??void 0});case"github":return T({body:r,secret:e,signature:n.get("x-hub-signature-256")??void 0});case"slack":return S({body:r,signature:n.get("x-slack-signature")??void 0,signingSecret:e,timestamp:n.get("x-slack-request-timestamp")??void 0});default:return!1}},H=(t,e)=>{if(t==="discord")try{if(JSON.parse(e).type===1)return Response.json({type:1})}catch{}},u=()=>new Response(void 0,{status:200}),M=(t,e,n)=>{if(t==="github")return e.get("x-github-delivery")??void 0;try{const r=JSON.parse(n),s=t==="slack"?r.event_id:r.id;return typeof s=="string"?s:void 0}catch{return}},D=async(t,e,n,r)=>{if(b(e))throw new d("BAD_REQUEST",`@lunora/agent: inbound channel run params ${E}`);if(r===void 0||r==="")return await t.create({params:e}),u();try{await t.create({id:`${n}-${A(r)}`,params:e})}catch(s){if(w(s))return u();throw s}return u()},J=t=>async(e,n)=>{const r=await e.text(),s=I(e.headers);if(s===void 0)return new Response("Unrecognized webhook",{status:400});const a=t.map(i=>({config:i.agent.onInbound,target:i})).filter(i=>i.config?.channel===s),o={channel:s,headers:e.headers,json:()=>JSON.parse(r),rawBody:r};let g=!1;for(const{config:i,target:y}of a){const h=_(i.secret,n);if(h===void 0||!await B(s,h,e.headers,r))continue;g=!0;const p=H(s,r);if(p)return p;const v=await i.map(o);if(!v)continue;const c=n[y.binding];if(!c||typeof c.create!="function")throw new d("INTERNAL",`@lunora/agent: no Workflow binding "${y.binding}" on env for an inbound-channel agent — run codegen/dev so wrangler.jsonc declares it`);return await D(c,v,s,M(s,e.headers,r))}return g?new Response(void 0,{status:204}):new Response("Invalid signature",{status:401})};export{J as dispatchAgentChannel,C as verifyDiscord,T as verifyGithub,S as verifySlack};
@@ -72,6 +72,8 @@ interface AgentComponent {
72
72
  extension: SchemaExtension;
73
73
  functions: {
74
74
  agentAppendMessage: AgentRegisteredFunction;
75
+ agentCompleteRun: AgentRegisteredFunction;
76
+ agentDeleteMessage: AgentRegisteredFunction;
75
77
  agentEnsureThread: AgentRegisteredFunction;
76
78
  agentEpisodeRecall: AgentRegisteredFunction;
77
79
  agentEpisodeUpsert: AgentRegisteredFunction;
@@ -72,6 +72,8 @@ interface AgentComponent {
72
72
  extension: SchemaExtension;
73
73
  functions: {
74
74
  agentAppendMessage: AgentRegisteredFunction;
75
+ agentCompleteRun: AgentRegisteredFunction;
76
+ agentDeleteMessage: AgentRegisteredFunction;
75
77
  agentEnsureThread: AgentRegisteredFunction;
76
78
  agentEpisodeRecall: AgentRegisteredFunction;
77
79
  agentEpisodeUpsert: AgentRegisteredFunction;
@@ -1,2 +1,2 @@
1
- import{LunoraError as w}from"@lunora/errors";import{defineTable as x,initLunora as $,defineSchemaExtension as j}from"@lunora/server";import{v as e}from"@lunora/values";import{i as b,o as k,q as O,s as S,j as F}from"./packem_shared/graph-component-B2YmC2eL.mjs";import{p as ne}from"./packem_shared/graph-component-B2YmC2eL.mjs";import{sandboxComponent as ie}from"./packem_shared/sandboxComponent-BhaGHsQZ.mjs";const I="agent_episodes",L=5,U=20,B=500,N=200,G=/\s+/gu,z=c=>c===void 0||!Number.isFinite(c)?L:Math.min(U,Math.max(1,Math.trunc(c))),P={episodes:x({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:H,query:J}=$.dataModel().create(),Q=()=>{const c=H.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 l=s.summary.replaceAll(G," ").trim().slice(0,B);if(l.length===0)return{recorded:!1};if(await u.db.query(I).withIndex("byOwnerMessageKey",p=>p.eq("owner",s.owner).eq("messageKey",s.messageKey)).first())return{recorded:!1};await u.db.insert(I,{createdAt:s.createdAt??Date.now(),messageKey:s.messageKey,owner:s.owner,summary:l,...s.threadKey===void 0?{}:{threadKey:s.threadKey}});const g=await u.db.query(I).withIndex("byOwnerCreatedAt",p=>p.eq("owner",s.owner)).order("asc").take(N+1);for(const p of g.slice(0,Math.max(0,g.length-N)))await u.db.delete(p._id);return{recorded:!0}}),v=J.input({limit:e.optional(e.number()),owner:e.string()}).query(async({args:s,ctx:u})=>{const l=await u.db.query(I).withIndex("byOwnerCreatedAt",g=>g.eq("owner",s.owner)).order("desc").take(z(s.limit));return l.length===0?{context:""}:{context:l.toReversed().map(g=>`- ${g.summary}`).join(`
2
- `)}});return{agentEpisodeRecall:b(v),agentEpisodeUpsert:b(c)}},T="threads",R="messages",d=`${k}_${T}`,K=`${k}_${R}`,V=j(k,{tables:{[R]:x({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(),[T]:x({agent:e.string(),createdAt:e.number(),error: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(),...O,...P}}),{mutation:m,query:q}=$.dataModel().create(),ee=()=>{const c=m.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:a})=>{const r=Date.now(),n=await a.db.query(d).withIndex("byKey",i=>i.eq("key",t.key)).first();if(n){if(n.owner!==t.owner&&t.owner!==void 0)throw new Error(`@lunora/agent: thread "${t.key}" belongs to another owner`);const i=n.instanceId;if((n.status==="running"||n.status==="awaiting_input")&&i!==void 0&&(t.instanceId===void 0||t.instanceId!==i)){const o=t.onConcurrentRun??"reject";if(o!=="replace")throw new w("CONFLICT",`@lunora/agent: thread "${t.key}" already has a run in flight (instance "${i}") — onConcurrentRun="${o}"`);return await a.db.patch(n._id,{error:void 0,status:"running",updatedAt:r,...t.instanceId===void 0?{}:{instanceId:t.instanceId}}),{created:!1,priorInstanceId:i,replaced:!0}}return await a.db.patch(n._id,{error:void 0,status:"running",updatedAt:r,...t.instanceId===void 0?{}:{instanceId:t.instanceId}}),{created:!1}}return await a.db.insert(d,{agent:t.agent,createdAt:r,key:t.key,messageCount:0,status:"running",updatedAt:r,...S({instanceId:t.instanceId,owner:t.owner,state:t.initialState,title:t.title})}),{created:!0}}),v=m.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:a})=>{const r=await a.db.query(K).withIndex("byMessageKey",y=>y.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();if(r)return{seq:r.seq};const n=await a.db.query(d).withIndex("byKey",y=>y.eq("key",t.threadKey)).first();if(!n)throw new Error(`@lunora/agent: cannot append to unknown thread "${t.threadKey}" — run agentEnsureThread first`);const i=n.messageCount,o=Date.now();return await a.db.insert(K,{content:t.content,createdAt:o,messageKey:t.messageKey,role:t.role,seq:i,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 a.db.patch(n._id,{messageCount:i+1,updatedAt:o}),{seq:i}}),s=m.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:a})=>{const{instanceId:r,key:n}=t;let i;n!==void 0?i=await a.db.query(d).withIndex("byKey",o=>o.eq("key",n)).first():r!==void 0&&(i=await a.db.query(d).withIndex("byInstance",o=>o.eq("instanceId",r)).first()),i&&await a.db.patch(i._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}})}),u=m.input({key:e.string(),state:e.any()}).mutation(async({args:t,ctx:a})=>{const r=await a.db.query(d).withIndex("byKey",n=>n.eq("key",t.key)).first();r&&await a.db.patch(r._id,{state:t.state,updatedAt:Date.now()})}),l=(t,a)=>{if(!t)return;const{owner:r}=t;if(!(r!==void 0&&r!==(a.userId??void 0)))return t},g=q.input({key:e.string()}).query(async({args:t,ctx:a})=>{const r=await a.db.query(d).withIndex("byKey",n=>n.eq("key",t.key)).first();return l(r,a.auth)}),p=q.input({key:e.string()}).query(async({args:t,ctx:a})=>{const r=await a.db.query(d).withIndex("byKey",n=>n.eq("key",t.key)).first();return l(r,a.auth)?.state}),_=q.input({key:e.string(),limit:e.optional(e.number())}).query(async({args:t,ctx:a})=>{const r=await a.db.query(d).withIndex("byKey",n=>n.eq("key",t.key)).first();return l(r,a.auth)===void 0?[]:t.limit!==void 0?(await a.db.query(K).withIndex("byThread",n=>n.eq("threadKey",t.key)).order("desc").take(t.limit)).toReversed():a.db.query(K).withIndex("byThread",n=>n.eq("threadKey",t.key)).collect()}),D=m.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:a})=>{const r=await a.db.query(d).withIndex("byKey",h=>h.eq("key",t.threadKey)).first(),n=l(r,a.auth);if(n===void 0)throw new w("FORBIDDEN",`@lunora/agent: not allowed to resolve approvals on thread "${t.threadKey}"`);if(n.instanceId!==t.instanceId)throw new w("FORBIDDEN",`@lunora/agent: instance "${t.instanceId}" does not own thread "${t.threadKey}"`);const i=n.agent,{agents:o}=a,y=o?.[i];if(typeof y?.sendEvent!="function")throw new w("INTERNAL",`@lunora/agent: no ctx.agents["${i}"] 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}}),M=m.input({agent:e.string(),input:e.string(),threadKey:e.string(),title:e.optional(e.string())}).mutation(async({args:t,ctx:a})=>{const{agents:r}=a,n=r?.[t.agent];if(typeof n?.run!="function")throw new w("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(n.publicRun!==!0)throw new w("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 i=a.auth.userId??void 0,o=await a.db.query(d).withIndex("byKey",h=>h.eq("key",t.threadKey)).first();if(o){const h=o.status,E=o.instanceId,f=o.owner;if((h==="running"||h==="awaiting_input")&&E!==void 0&&(f===void 0||f===i))return{id:E,threadKey:t.threadKey};if(f!==i&&i!==void 0)throw new w("FORBIDDEN",`@lunora/agent: thread "${t.threadKey}" belongs to another owner`)}const{id:y}=await n.run({input:t.input,threadKey:t.threadKey,...i===void 0?{}:{owner:i},...t.title===void 0?{}:{title:t.title}});return{id:y,threadKey:t.threadKey}}),C=F(),A=Q();return{extension:V,functions:{agentAppendMessage:b(v),agentEnsureThread:b(c),agentEpisodeRecall:A.agentEpisodeRecall,agentEpisodeUpsert:A.agentEpisodeUpsert,agentGraphTraverse:C.agentGraphTraverse,agentGraphUpsert:C.agentGraphUpsert,agentMessages:_,agentPatchThread:b(s),agentResolveApproval:D,agentRun:M,agentSetState:b(u),agentState:p,agentThread:g}}};export{ee as agentComponent,V as agentExtension,ne as normalizeEntityName,ie as sandboxComponent};
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 f,A as k,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 A="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:y})=>{const w=s.summary.replaceAll(z," ").trim().slice(0,Y);if(w.length===0)return{recorded:!1};if(await y.db.query(A).withIndex("byOwnerMessageKey",h=>h.eq("owner",s.owner).eq("messageKey",s.messageKey)).first())return{recorded:!1};await y.db.insert(A,{createdAt:s.createdAt??Date.now(),messageKey:s.messageKey,owner:s.owner,summary:w,...s.threadKey===void 0?{}:{threadKey:s.threadKey}});const m=await y.db.query(A).withIndex("byOwnerCreatedAt",h=>h.eq("owner",s.owner)).order("asc").take(x+1);for(const h of m.slice(0,Math.max(0,m.length-x)))await y.db.delete(h._id);return{recorded:!0}}),o=Z.input({limit:e.optional(e.number()),owner:e.string()}).query(async({args:s,ctx:y})=>{const w=await y.db.query(A).withIndex("byOwnerCreatedAt",I=>I.eq("owner",s.owner)).order("desc").take(V(s.limit));return w.length===0?{context:""}:{context:w.toReversed().map(I=>`- ${I.summary}`).join(`
2
+ `)}});return{agentEpisodeRecall:f(o),agentEpisodeUpsert:f(u)}},S="threads",$="messages",M="run_queue",p=`${k}_${S}`,K=`${k}_${$}`,E=`${k}_${M}`,N=5,te=780*60*1e3,ne=j(k,{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()),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:b,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:y}=o,w=await u.query(E).withIndex("byThreadInstance",h=>h.eq("threadKey",o.key).eq("instanceId",y)).first();if(w)return{outcome:"queued",position:w.position};const I=await u.query(E).withIndex("byThread",h=>h.eq("threadKey",o.key)).collect();if(I.length>=N)throw new g("CONFLICT",`@lunora/agent: thread "${o.key}" run queue is full (depth ${String(N)}) — rejecting instance "${y}"`);const m=(I.at(-1)?.position??-1)+1;return await u.insert(E,{agent:o.agent,enqueuedAt:s,instanceId:y,position:m,threadKey:o.key}),{outcome:"queued",position:m}},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:void 0,status:"running",updatedAt:s,...o.instanceId===void 0?{}:{instanceId:o.instanceId}}),{outcome:"replaced",priorInstanceId:o.priorInstanceId}},le=()=>{const u=b.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`);const r=i.instanceId,d=typeof i.updatedAt=="number"?i.updatedAt:0,c=i.status==="awaiting_input"?P:te;return!(a-d>c)&&(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:void 0,status:"running",updatedAt:a,...t.instanceId===void 0?{}:{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,...F({instanceId:t.instanceId,owner:t.owner,state:t.initialState,title:t.title})}),{outcome:"created"}}),o=b.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(K).withIndex("byMessageKey",c=>c.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();if(a)return{seq:a.seq};const i=await n.db.query(p).withIndex("byKey",c=>c.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(K,{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=b.input({messageKey:e.string(),threadKey:e.string()}).mutation(async({args:t,ctx:n})=>{const a=await n.db.query(K).withIndex("byMessageKey",i=>i.eq("threadKey",t.threadKey).eq("messageKey",t.messageKey)).first();a&&await n.db.delete(a._id)}),y=b.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",d=>d.eq("key",i)).first():a!==void 0&&(r=await n.db.query(p).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}})}),w=b.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",l=>l.eq("key",t.key)).first();if(i?.instanceId!==t.instanceId){const l=await n.db.query(E).withIndex("byThreadInstance",q=>q.eq("threadKey",t.key).eq("instanceId",t.instanceId)).first();return l&&await n.db.delete(l._id),{}}const d=(await n.db.query(E).withIndex("byThread",l=>l.eq("threadKey",t.key)).collect())[0];if(!d)return await n.db.patch(i._id,{status:t.status,updatedAt:a,...t.error===void 0?{}:{error:t.error},...t.usage===void 0?{}:{usage:t.usage}}),{};const c=d.instanceId;return await n.db.delete(d._id),await n.db.patch(i._id,{instanceId:c,status:"running",updatedAt:a,...t.error===void 0?{}:{error:t.error},...t.usage===void 0?{}:{usage:t.usage}}),{dequeued:c}}),I=b.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()})}),m=(t,n)=>{if(!t)return;const{owner:a}=t;if(!(a!==void 0&&a!==(n.userId??void 0)))return t},h=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 m(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 m(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 m(a,n.auth)===void 0?[]:t.limit!==void 0?(await n.db.query(K).withIndex("byThread",r=>r.eq("threadKey",t.key)).order("desc").take(t.limit)).toReversed():n.db.query(K).withIndex("byThread",i=>i.eq("threadKey",t.key)).collect()}),U=b.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",l=>l.eq("key",t.threadKey)).first(),i=m(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,c=d?.[r];if(typeof c?.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 c.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=b.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(p).withIndex("byKey",l=>l.eq("key",t.threadKey)).first();if(d){const l=d.status,q=d.instanceId,_=d.owner;if((l==="running"||l==="awaiting_input")&&q!==void 0&&(_===void 0||_===r))return{id:q,threadKey:t.threadKey};if(_!==r)throw new g("FORBIDDEN",`@lunora/agent: thread "${t.threadKey}" belongs to another owner`)}const{id:c}=await i.run({input:t.input,threadKey:t.threadKey,...r===void 0?{}:{owner:r},...t.title===void 0?{}:{title:t.title}});return{id:c,threadKey:t.threadKey}}),C=H(),R=ee();return{extension:ne,functions:{agentAppendMessage:f(o),agentCompleteRun:f(w),agentEnsureThread:f(u),agentEpisodeRecall:R.agentEpisodeRecall,agentEpisodeUpsert:R.agentEpisodeUpsert,agentGraphTraverse:C.agentGraphTraverse,agentGraphUpsert:C.agentGraphUpsert,agentMessages:L,agentDeleteMessage:f(s),agentPatchThread:f(y),agentResolveApproval:U,agentRun:B,agentSetState:f(I),agentState:O,agentThread:h}}};export{le as agentComponent,ne as agentExtension,pe as normalizeEntityName,we as sandboxComponent};
@@ -1,4 +1,4 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-boAM2Yi1.mjs";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-B8WZl1rU.mjs";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
@@ -24,10 +24,12 @@ type InboundAgentEmailHandler = (message: unknown, env: unknown, context: unknow
24
24
  /**
25
25
  * Build the inbound `email()` handler for one or more `onEmail` agents. The
26
26
  * returned callback parses the message, then walks `targets` in order and starts
27
- * a durable run for the first agent whose `onEmail` mapper returns a run. If the
28
- * matched agent's Workflow binding is missing from `env` (run codegen/dev so
29
- * `wrangler.jsonc` declares it) the dispatch throws, and the inbound handler's
30
- * `onError` rejects (bounces) the message.
27
+ * a durable run for the first agent whose `onEmail` mapper returns a run.
28
+ *
29
+ * Two dispatch failures bounce the message permanently rather than retrying: a
30
+ * missing Workflow binding (run codegen/dev so `wrangler.jsonc` declares it) and
31
+ * a run carrying the reserved workflow branch-marker key. See
32
+ * {@link rejectPermanently}.
31
33
  * @experimental
32
34
  */
33
35
  declare const dispatchAgentEmail: (targets: ReadonlyArray<AgentEmailTarget>) => InboundAgentEmailHandler;
package/dist/inbound.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-boAM2Yi1.js";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
@@ -24,10 +24,12 @@ type InboundAgentEmailHandler = (message: unknown, env: unknown, context: unknow
24
24
  /**
25
25
  * Build the inbound `email()` handler for one or more `onEmail` agents. The
26
26
  * returned callback parses the message, then walks `targets` in order and starts
27
- * a durable run for the first agent whose `onEmail` mapper returns a run. If the
28
- * matched agent's Workflow binding is missing from `env` (run codegen/dev so
29
- * `wrangler.jsonc` declares it) the dispatch throws, and the inbound handler's
30
- * `onError` rejects (bounces) the message.
27
+ * a durable run for the first agent whose `onEmail` mapper returns a run.
28
+ *
29
+ * Two dispatch failures bounce the message permanently rather than retrying: a
30
+ * missing Workflow binding (run codegen/dev so `wrangler.jsonc` declares it) and
31
+ * a run carrying the reserved workflow branch-marker key. See
32
+ * {@link rejectPermanently}.
31
33
  * @experimental
32
34
  */
33
35
  declare const dispatchAgentEmail: (targets: ReadonlyArray<AgentEmailTarget>) => InboundAgentEmailHandler;
package/dist/inbound.mjs CHANGED
@@ -1 +1 @@
1
- import{LunoraError as s}from"@lunora/errors";import{createInboundEmailHandler as d,parseInboundEmail as u}from"@lunora/mail/inbound";const m=i=>{const c=d({dispatch:async(o,a)=>{for(const n of i){const t=n.agent.onEmail;if(!t)continue;const e=await t(o);if(e==null)continue;const r=a.env[n.binding];if(!r||typeof r.create!="function")throw new s("INTERNAL",`@lunora/agent: no Workflow binding "${n.binding}" on env for an inbound agent — run codegen/dev so wrangler.jsonc declares it`);await r.create({params:e});return}},parse:u});return async(o,a,n)=>c(o,a,n)};export{m as dispatchAgentEmail};
1
+ import{createInboundEmailHandler as u,authenticatesFrom as d,parseInboundEmail as g}from"@lunora/mail/inbound";import{h as l,B as m}from"./packem_shared/branch-marker-boZ00zmk.mjs";const f="message could not be processed",c=(r,o)=>{console.error("@lunora/agent/inbound: bouncing message —",o),r.message.setReject(f)},b=r=>{const o=u({dispatch:async(t,n)=>{for(const e of r){const s=e.agent.onEmail;if(!s)continue;const a=await s(t);if(a==null)continue;const i=n.env[e.binding];if(!i||typeof i.create!="function"){c(n,`@lunora/agent: no Workflow binding "${e.binding}" on env for an inbound agent — run codegen/dev so wrangler.jsonc declares it`);return}if(l(a)){c(n,`@lunora/agent: inbound run params ${m}`);return}await i.create({params:a});return}},parse:g,verify:d});return async(t,n,e)=>o(t,n,e)};export{b as dispatchAgentEmail};
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AgentDefinition, c as AgentCompact, d as AgentEpisodeExtract, e as AgentGraphExtract, f as AgentGenerate, g as AgentTokenSink, h as AgentRunInput, i as AgentFunctionPaths, j as AgentRunFunction, k as AgentStepLike, l as AgentStreamGenerate, m as AgentRunResult, n as AgentMessageRow, o as AgentConfig, S as SkillDefinition, p as AnyAgentTool, q as AgentAsToolOptions, r as AgentSubToolInput, b as AgentToolDefinition, s as AgentBindingSpec, t as AgentHandle, u as AgentToolConfig, v as AgentFunctionReference, w as AgentModelInput, x as SkillConfig } from "./packem_shared/types.d-boAM2Yi1.mjs";
2
- export type { y as AgentEmailMapper, z as AgentEmailRun, B as AgentGenerateOptions, C as AgentGenerateResult, D as AgentInstructionsContext, E as AgentLiveEvent, F as AgentMemoryOptions, G as AgentMemorySource, H as AgentMessageStatus, J as AgentOnStepFinish, K as AgentPrepareStep, L as AgentPrepareStepInput, M as AgentPrepareStepResult, N as AgentProgressEvent, O as AgentRunHandle, P as AgentStepFinishInfo, Q as AgentStepInfo, R as AgentThreadStatus, T as AgentTokenDelta, U as AgentToolCall, a as AgentToolContext, V as AgentUsage, W as AgentVoiceConfig, X as AgentWorkflowBindingLike, Y as AgentWorkflowInstanceLike } from "./packem_shared/types.d-boAM2Yi1.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-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";
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";
@@ -183,7 +183,7 @@ declare const agentAsTool: (options: AgentAsToolOptions) => AgentToolDefinition<
183
183
  interface ToolScriptStep {
184
184
  /** A stable name later steps reference the output by. */
185
185
  id: string;
186
- /** The tool's input; values may embed `{ "$from": "&lt;stepId>", "$path": "a.b" }` refs. */
186
+ /** The tool's input; values may embed `{ "$from": "<stepId>", "$path": "a.b" }` refs. */
187
187
  input?: Record<string, unknown>;
188
188
  /** The tool to call — one of the tools handed to {@link codeTool}. */
189
189
  tool: string;
@@ -239,6 +239,16 @@ interface CodeToolOptions {
239
239
  * keep approval-gated tools as normal top-level tools. Gate the whole script via
240
240
  * `opts.needsApproval` instead.
241
241
  *
242
+ * STEP-OUTPUT CONTRACT — **every step's output must be JSON-serializable.** Each
243
+ * step runs inside its own durable `step.do`, so the workflow host serializes
244
+ * the returned value before the next step (or the tool result) ever sees it. A
245
+ * `Date` comes back as an ISO string, a `Map`/`Set` as `{}`, `undefined` as
246
+ * `null` or a dropped key, and a `bigint` throws outright. That applies to the
247
+ * value a later step reads through `{ "$from": … }` just as much as to the
248
+ * returned `results` — the hand-off is durable state, not an in-memory one. A
249
+ * composed tool that wants to pass a rich value should return its JSON form
250
+ * (`date.toISOString()`, `[...map]`) and let the consuming step rebuild it.
251
+ *
242
252
  * ```ts
243
253
  * import { codeTool, defineAgent, functionTool } from "@lunora/agent";
244
254
  *
@@ -344,8 +354,14 @@ interface FunctionToolOptions<Input> {
344
354
  * ```
345
355
  *
346
356
  * The function reference may be a typed `api.*` reference or a `"module:name"`
347
- * path. The model-provided input is passed verbatim as the function's args, so
348
- * `inputSchema` should mirror the referenced function's argument validator.
357
+ * path. The model-provided input is passed as the function's args, so
358
+ * `inputSchema` should mirror the referenced function's argument validator
359
+ * PLUS the call's `idempotencyKey`, pinned onto the args after the model
360
+ * input (so out-of-schema model input can never override it). A function
361
+ * that wants to dedupe a side-effecting call across an at-least-once step
362
+ * retry declares `idempotencyKey: v.optional(v.string())` in its own args
363
+ * and checks it; a function that ignores it is unaffected — an args field
364
+ * the function's own validator doesn't declare is dropped, not rejected.
349
365
  * @experimental
350
366
  */
351
367
  declare const functionTool: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(reference: AgentFunctionReference | string, options: FunctionToolOptions<Input>) => AgentToolDefinition<Input, Output>;
@@ -471,6 +487,17 @@ interface McpToolsOptions {
471
487
  command?: string;
472
488
  /** Identify this client to the server on connect. Default `"lunora-agent"`. */
473
489
  name?: string;
490
+ /**
491
+ * Gate an adapted tool's call behind a human approval. Defaults to
492
+ * **unattended** (no gate) — matching today's behavior. An MCP tool's
493
+ * result feeds back into the model's context (`adaptMcpResult`), so a
494
+ * hostile downstream server can pair indirect prompt injection with an
495
+ * unattended mutating tool call; pass a boolean to gate every adapted
496
+ * tool the same way, or a predicate `(toolName, input) => boolean` to
497
+ * gate per tool (evaluated from replay-stable input, so keep it
498
+ * deterministic).
499
+ */
500
+ needsApproval?: ((toolName: string, input: Record<string, unknown>) => boolean) | boolean;
474
501
  /** Only adapt these tool names (default: every tool the server lists). */
475
502
  only?: ReadonlyArray<string>;
476
503
  /** Prefix added to each adapted tool's key (disambiguate multiple servers). */
@@ -510,6 +537,9 @@ declare const adaptMcpResult: (result: McpCallResult) => unknown;
510
537
  * every call. In the Workers runtime only the HTTP/SSE transports run (stdio
511
538
  * does not) — pass `url`, or inject an already-connected `client` for a custom
512
539
  * transport (also the test seam).
540
+ *
541
+ * Every adapted tool runs UNATTENDED by default (no approval gate) — pass
542
+ * `opts.needsApproval` to change that; see {@link McpToolsOptions.needsApproval}.
513
543
  * @experimental
514
544
  */
515
545
  declare const mcpTools: (options: McpToolsOptions) => Promise<Record<string, AgentToolDefinition<Record<string, unknown>>>>;
@@ -674,6 +704,13 @@ interface VoiceTurnResult {
674
704
  interface RunVoiceTurnOptions {
675
705
  /** The agent whose thread + models back this session. */
676
706
  agent: AgentDefinition;
707
+ /**
708
+ * History-compaction seam — the SAME one the durable loop uses. Voice and
709
+ * text turns share one thread, so an agent that declares `compaction` must
710
+ * get it here too; absent (or with no `compaction` config) the turn takes
711
+ * the byte-identical uncompacted path.
712
+ */
713
+ compact?: AgentCompact;
677
714
  /** Stable per-socket id — the message-key prefix that keeps persisted rows idempotent. */
678
715
  connectionId: string;
679
716
  /** The Worker env (resolves a dynamic `instructions` thunk). */
@@ -755,6 +792,8 @@ declare class VoiceSessionDO {
755
792
  protected readonly streamGenerate: AgentStreamGenerate;
756
793
  protected readonly sttModel: string;
757
794
  protected readonly ttsModel: string;
795
+ /** Turn budget for one socket — see {@link DEFAULT_MAX_SESSION_TURNS}. */
796
+ private readonly maxSessionTurns;
758
797
  private readonly audioBuffers;
759
798
  private readonly bufferedBytes;
760
799
  private readonly controllers;
@@ -793,6 +832,8 @@ declare class VoiceSessionDO {
793
832
  private synthesizeWithSignal;
794
833
  /** Abort an in-flight turn and free a socket's transient buffers. */
795
834
  private cleanupSocket;
835
+ /** Close a socket that broke a session bound, swallowing an already-closed error (never throw from a handler). */
836
+ private closeSocket;
796
837
  /** Send a JSON control frame, swallowing a closed-socket error (never throw from a handler). */
797
838
  private send;
798
839
  /** Send a binary audio frame, swallowing a closed-socket error. */
@@ -808,15 +849,15 @@ declare class VoiceSessionDO {
808
849
  }
809
850
  /**
810
851
  * Compile a `defineAgent` definition into the workflow the generated
811
- * `&lt;Name>AgentWorkflow` entrypoint class runs. Codegen emits, per agent:
852
+ * `<Name>AgentWorkflow` entrypoint class runs. Codegen emits, per agent:
812
853
  *
813
854
  * ```ts
814
855
  * import LunoraWorkflow from "@lunora/workflow/do";
815
856
  * import { compileAgentWorkflow } from "@lunora/agent";
816
857
  * import { support } from "../agents.js";
817
858
  *
818
- * export class SupportAgentWorkflow extends LunoraWorkflow&lt;AgentRunInput, AgentRunResult> {
819
- * public constructor(ctx: ConstructorParameters&lt;typeof LunoraWorkflow>[0], env: Record&lt;string, unknown>) {
859
+ * export class SupportAgentWorkflow extends LunoraWorkflow<AgentRunInput, AgentRunResult> {
860
+ * public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
820
861
  * super(ctx, env, compileAgentWorkflow(support, "support"), "support");
821
862
  * }
822
863
  * }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AgentDefinition, c as AgentCompact, d as AgentEpisodeExtract, e as AgentGraphExtract, f as AgentGenerate, g as AgentTokenSink, h as AgentRunInput, i as AgentFunctionPaths, j as AgentRunFunction, k as AgentStepLike, l as AgentStreamGenerate, m as AgentRunResult, n as AgentMessageRow, o as AgentConfig, S as SkillDefinition, p as AnyAgentTool, q as AgentAsToolOptions, r as AgentSubToolInput, b as AgentToolDefinition, s as AgentBindingSpec, t as AgentHandle, u as AgentToolConfig, v as AgentFunctionReference, w as AgentModelInput, x as SkillConfig } from "./packem_shared/types.d-boAM2Yi1.js";
2
- export type { y as AgentEmailMapper, z as AgentEmailRun, B as AgentGenerateOptions, C as AgentGenerateResult, D as AgentInstructionsContext, E as AgentLiveEvent, F as AgentMemoryOptions, G as AgentMemorySource, H as AgentMessageStatus, J as AgentOnStepFinish, K as AgentPrepareStep, L as AgentPrepareStepInput, M as AgentPrepareStepResult, N as AgentProgressEvent, O as AgentRunHandle, P as AgentStepFinishInfo, Q as AgentStepInfo, R as AgentThreadStatus, T as AgentTokenDelta, U as AgentToolCall, a as AgentToolContext, V as AgentUsage, W as AgentVoiceConfig, X as AgentWorkflowBindingLike, Y as AgentWorkflowInstanceLike } from "./packem_shared/types.d-boAM2Yi1.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-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";
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";
@@ -183,7 +183,7 @@ declare const agentAsTool: (options: AgentAsToolOptions) => AgentToolDefinition<
183
183
  interface ToolScriptStep {
184
184
  /** A stable name later steps reference the output by. */
185
185
  id: string;
186
- /** The tool's input; values may embed `{ "$from": "&lt;stepId>", "$path": "a.b" }` refs. */
186
+ /** The tool's input; values may embed `{ "$from": "<stepId>", "$path": "a.b" }` refs. */
187
187
  input?: Record<string, unknown>;
188
188
  /** The tool to call — one of the tools handed to {@link codeTool}. */
189
189
  tool: string;
@@ -239,6 +239,16 @@ interface CodeToolOptions {
239
239
  * keep approval-gated tools as normal top-level tools. Gate the whole script via
240
240
  * `opts.needsApproval` instead.
241
241
  *
242
+ * STEP-OUTPUT CONTRACT — **every step's output must be JSON-serializable.** Each
243
+ * step runs inside its own durable `step.do`, so the workflow host serializes
244
+ * the returned value before the next step (or the tool result) ever sees it. A
245
+ * `Date` comes back as an ISO string, a `Map`/`Set` as `{}`, `undefined` as
246
+ * `null` or a dropped key, and a `bigint` throws outright. That applies to the
247
+ * value a later step reads through `{ "$from": … }` just as much as to the
248
+ * returned `results` — the hand-off is durable state, not an in-memory one. A
249
+ * composed tool that wants to pass a rich value should return its JSON form
250
+ * (`date.toISOString()`, `[...map]`) and let the consuming step rebuild it.
251
+ *
242
252
  * ```ts
243
253
  * import { codeTool, defineAgent, functionTool } from "@lunora/agent";
244
254
  *
@@ -344,8 +354,14 @@ interface FunctionToolOptions<Input> {
344
354
  * ```
345
355
  *
346
356
  * The function reference may be a typed `api.*` reference or a `"module:name"`
347
- * path. The model-provided input is passed verbatim as the function's args, so
348
- * `inputSchema` should mirror the referenced function's argument validator.
357
+ * path. The model-provided input is passed as the function's args, so
358
+ * `inputSchema` should mirror the referenced function's argument validator
359
+ * PLUS the call's `idempotencyKey`, pinned onto the args after the model
360
+ * input (so out-of-schema model input can never override it). A function
361
+ * that wants to dedupe a side-effecting call across an at-least-once step
362
+ * retry declares `idempotencyKey: v.optional(v.string())` in its own args
363
+ * and checks it; a function that ignores it is unaffected — an args field
364
+ * the function's own validator doesn't declare is dropped, not rejected.
349
365
  * @experimental
350
366
  */
351
367
  declare const functionTool: <Input extends Record<string, unknown> = Record<string, unknown>, Output = unknown>(reference: AgentFunctionReference | string, options: FunctionToolOptions<Input>) => AgentToolDefinition<Input, Output>;
@@ -471,6 +487,17 @@ interface McpToolsOptions {
471
487
  command?: string;
472
488
  /** Identify this client to the server on connect. Default `"lunora-agent"`. */
473
489
  name?: string;
490
+ /**
491
+ * Gate an adapted tool's call behind a human approval. Defaults to
492
+ * **unattended** (no gate) — matching today's behavior. An MCP tool's
493
+ * result feeds back into the model's context (`adaptMcpResult`), so a
494
+ * hostile downstream server can pair indirect prompt injection with an
495
+ * unattended mutating tool call; pass a boolean to gate every adapted
496
+ * tool the same way, or a predicate `(toolName, input) => boolean` to
497
+ * gate per tool (evaluated from replay-stable input, so keep it
498
+ * deterministic).
499
+ */
500
+ needsApproval?: ((toolName: string, input: Record<string, unknown>) => boolean) | boolean;
474
501
  /** Only adapt these tool names (default: every tool the server lists). */
475
502
  only?: ReadonlyArray<string>;
476
503
  /** Prefix added to each adapted tool's key (disambiguate multiple servers). */
@@ -510,6 +537,9 @@ declare const adaptMcpResult: (result: McpCallResult) => unknown;
510
537
  * every call. In the Workers runtime only the HTTP/SSE transports run (stdio
511
538
  * does not) — pass `url`, or inject an already-connected `client` for a custom
512
539
  * transport (also the test seam).
540
+ *
541
+ * Every adapted tool runs UNATTENDED by default (no approval gate) — pass
542
+ * `opts.needsApproval` to change that; see {@link McpToolsOptions.needsApproval}.
513
543
  * @experimental
514
544
  */
515
545
  declare const mcpTools: (options: McpToolsOptions) => Promise<Record<string, AgentToolDefinition<Record<string, unknown>>>>;
@@ -674,6 +704,13 @@ interface VoiceTurnResult {
674
704
  interface RunVoiceTurnOptions {
675
705
  /** The agent whose thread + models back this session. */
676
706
  agent: AgentDefinition;
707
+ /**
708
+ * History-compaction seam — the SAME one the durable loop uses. Voice and
709
+ * text turns share one thread, so an agent that declares `compaction` must
710
+ * get it here too; absent (or with no `compaction` config) the turn takes
711
+ * the byte-identical uncompacted path.
712
+ */
713
+ compact?: AgentCompact;
677
714
  /** Stable per-socket id — the message-key prefix that keeps persisted rows idempotent. */
678
715
  connectionId: string;
679
716
  /** The Worker env (resolves a dynamic `instructions` thunk). */
@@ -755,6 +792,8 @@ declare class VoiceSessionDO {
755
792
  protected readonly streamGenerate: AgentStreamGenerate;
756
793
  protected readonly sttModel: string;
757
794
  protected readonly ttsModel: string;
795
+ /** Turn budget for one socket — see {@link DEFAULT_MAX_SESSION_TURNS}. */
796
+ private readonly maxSessionTurns;
758
797
  private readonly audioBuffers;
759
798
  private readonly bufferedBytes;
760
799
  private readonly controllers;
@@ -793,6 +832,8 @@ declare class VoiceSessionDO {
793
832
  private synthesizeWithSignal;
794
833
  /** Abort an in-flight turn and free a socket's transient buffers. */
795
834
  private cleanupSocket;
835
+ /** Close a socket that broke a session bound, swallowing an already-closed error (never throw from a handler). */
836
+ private closeSocket;
796
837
  /** Send a JSON control frame, swallowing a closed-socket error (never throw from a handler). */
797
838
  private send;
798
839
  /** Send a binary audio frame, swallowing a closed-socket error. */
@@ -808,15 +849,15 @@ declare class VoiceSessionDO {
808
849
  }
809
850
  /**
810
851
  * Compile a `defineAgent` definition into the workflow the generated
811
- * `&lt;Name>AgentWorkflow` entrypoint class runs. Codegen emits, per agent:
852
+ * `<Name>AgentWorkflow` entrypoint class runs. Codegen emits, per agent:
812
853
  *
813
854
  * ```ts
814
855
  * import LunoraWorkflow from "@lunora/workflow/do";
815
856
  * import { compileAgentWorkflow } from "@lunora/agent";
816
857
  * import { support } from "../agents.js";
817
858
  *
818
- * export class SupportAgentWorkflow extends LunoraWorkflow&lt;AgentRunInput, AgentRunResult> {
819
- * public constructor(ctx: ConstructorParameters&lt;typeof LunoraWorkflow>[0], env: Record&lt;string, unknown>) {
859
+ * export class SupportAgentWorkflow extends LunoraWorkflow<AgentRunInput, AgentRunResult> {
860
+ * public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
820
861
  * super(ctx, env, compileAgentWorkflow(support, "support"), "support");
821
862
  * }
822
863
  * }
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{runAgentLoop as t,splitForCompaction as r}from"./packem_shared/runAgentLoop-DNAt-pGz.mjs";import{collectAgenticMemoryTools as a,toSearchResults as i}from"./packem_shared/collectAgenticMemoryTools-DBcP8dUa.mjs";import{agentAsTool as l}from"./packem_shared/agentAsTool-CJCTyAQG.mjs";import{codeTool as p}from"./packem_shared/codeTool-DYJVy-bH.mjs";import{agentComponent as c,agentExtension as x}from"./component.mjs";import{default as A}from"./packem_shared/createAgentContext-B1IfI7ro.mjs";import{defineAgent as d,defineAgentTool as u,isAgentDefinition as N}from"./packem_shared/defineAgent-CKhzzmys.mjs";import{functionTool as D}from"./packem_shared/functionTool-CWHhkYqd.mjs";import{createAgentGenerate as M,createEpisodeExtract as O,createGraphExtract as _,createStreamGenerate as C,resolveAgentModel as G}from"./packem_shared/createAgentGenerate-BUVImo85.mjs";import{adaptMcpResult as F,mcpTools as L}from"./packem_shared/adaptMcpResult-18G7zpFo.mjs";import{buildModelMessages as k}from"./packem_shared/buildModelMessages-Dm2wVNQy.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-BXx-tSCe.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-DfJxCwfZ.mjs";import{default as $}from"./packem_shared/VoiceSessionDO-CTaBSdXk.mjs";import{runVoiceTurn as oe}from"./packem_shared/runVoiceTurn-PkMsfG_v.mjs";import{default as re}from"./packem_shared/compileAgentWorkflow-Dwp-xNNO.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,p 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-CE_Q6bk9.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-KgH-gwYg.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-D238XrlM.mjs";import{r as oe}from"./packem_shared/voice-turn-BRj_it54.mjs";import{default as re}from"./packem_shared/compileAgentWorkflow-DVHi7NYD.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};
package/dist/naming.mjs CHANGED
@@ -1 +1 @@
1
- const a=/([a-z0-9])([A-Z])/gu,s=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}AgentWorkflow`,t=e=>`AGENT_${e.replaceAll(a,"$1_$2").toUpperCase()}`,l=e=>`agent-${e.replaceAll(a,"$1-$2").toLowerCase()}`,o=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}VoiceDO`,r=e=>`VOICE_${e.replaceAll(a,"$1_$2").toUpperCase()}`;export{t as agentBindingName,s as agentClassName,l as agentDefaultName,r as voiceBindingName,o as voiceClassName};
1
+ const a=/([a-z0-9])([A-Z])/gu,t=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}AgentWorkflow`,s=e=>`AGENT_${e.replaceAll(a,"$1_$2").toUpperCase()}`,c=e=>`agent-${e.replaceAll(a,"$1-$2").toLowerCase()}`,o=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}VoiceDO`,l=e=>`VOICE_${e.replaceAll(a,"$1_$2").toUpperCase()}`;export{s as agentBindingName,t as agentClassName,c as agentDefaultName,l as voiceBindingName,o as voiceClassName};
@@ -0,0 +1 @@
1
+ const e="agents",a="sandbox",n=`${a}:invoke`,s={appendMessage:`${e}:agentAppendMessage`,completeRun:`${e}:agentCompleteRun`,deleteMessage:`${e}:agentDeleteMessage`,ensureThread:`${e}:agentEnsureThread`,episodeRecall:`${e}:agentEpisodeRecall`,episodeUpsert:`${e}:agentEpisodeUpsert`,graphTraverse:`${e}:agentGraphTraverse`,graphUpsert:`${e}:agentGraphUpsert`,listMessages:`${e}:agentMessages`,patchThread:`${e}:agentPatchThread`,run:`${e}:agentRun`,setState:`${e}:agentSetState`,state:`${e}:agentState`},r=t=>typeof t=="string"?{__lunoraRef:t}:t;export{e as AGENT_MODULE,s as DEFAULT_AGENT_FUNCTION_PATHS,n as SANDBOX_INVOKE_PATH,a as SANDBOX_MODULE,r as toFunctionReference};
@@ -0,0 +1 @@
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-BRj_it54.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};
@@ -0,0 +1,2 @@
1
+ import{LunoraError as c}from"@lunora/errors";import{jsonSchema as i}from"ai";const s=t=>{if(t.structuredContent!==void 0)return t.structuredContent;const n=(t.content??[]).filter(e=>e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
2
+ `);return t.isError===!0?`MCP tool error: ${n.length>0?n:"unknown error"}`:n.length>0?n:t.content},l=(t,n,e)=>({description:n.description??`MCP tool "${n.name}".`,execute:async r=>s(await t.callTool({arguments:r,name:n.name})),inputSchema:i(n.inputSchema),isLunoraAgentTool:!0,...e===void 0?{}:{needsApproval:typeof e=="function"?r=>e(n.name,r):e}}),u=async t=>{if(t.command!==void 0)throw new c("INTERNAL","@lunora/agent: mcpTools `command` (stdio transport) cannot run in the Workers runtime — pass a `url` for an HTTP/SSE MCP server, or inject a connected `client`");if(t.url===void 0)throw new c("INTERNAL","@lunora/agent: mcpTools requires a `url` (an HTTP/SSE MCP server) or an injected `client`");const n=new URL(t.url),{Client:e}=await import("@modelcontextprotocol/sdk/client/index.js");let r;if(t.transport==="sse"){const{SSEClientTransport:a}=await import("@modelcontextprotocol/sdk/client/sse.js");r=new a(n)}else{const{StreamableHTTPClientTransport:a}=await import("@modelcontextprotocol/sdk/client/streamableHttp.js");r=new a(n)}const o=new e({name:t.name??"lunora-agent",version:t.version??"0.0.0"});return await o.connect(r),o},T=async t=>{const n=t.client??await u(t),{tools:e}=await n.listTools(),r={};for(const o of e)t.only!==void 0&&!t.only.includes(o.name)||(r[`${t.prefix??""}${o.name}`]=l(n,o,t.needsApproval));return r};export{s as adaptMcpResult,T as mcpTools};
@@ -0,0 +1,3 @@
1
+ import{e as Z}from"./createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs";import{c as j,d as tt}from"./component-shared-G8ngerkU.mjs";import{resolveAgentModel as et}from"./createAgentGenerate-BOILDUZj.mjs";import{buildModelMessages as G}from"./buildModelMessages-Y4tGo1T9.mjs";import{agentBindingName as L}from"../naming.mjs";import{toFunctionReference as h}from"./AGENT_MODULE-M4D1EejI.mjs";import{i as ot}from"./positive-integer-ztHqqpBk.mjs";import{combineTelemetry as nt}from"./combineTelemetry-DgE9W9G8.mjs";import{c as q}from"./tool-output-Cmk3JWMG.mjs";const at=e=>e.kind==="graph"||e.kind==="episodic"||e.mode!=="agentic",P=e=>e.memorySources?e.memorySources:e.memory&&at(e.memory)?[{key:"default",...e.memory}]:[],st=e=>P(e).find(t=>t.kind==="graph"),rt=e=>P(e).find(t=>t.kind==="episodic"),S=(e,t)=>t==="default"?e:`${e}:${t}`,it=e=>{if(e===void 0||e.isEnabled===!1)return;const{integrations:t}=e;if(t===void 0)return;const n=Array.isArray(t)?t:[t];return n.length===0?void 0:nt(...n)},ct=async(e,t,n)=>{const o=it(e);if(o===void 0)return n();const a={dynamic:!0,input:t.input,toolCallId:t.id,toolName:t.name,type:"tool-call"},s={callId:t.id,messages:[],toolCall:a,toolContext:void 0},u=Date.now();await o.onToolExecutionStart?.(s);let d;const c=()=>(d??=n(),d);try{if(o.executeTool===void 0)await c();else try{await o.executeTool({...s,execute:c,toolCallId:t.id})}catch{}const i=await c();return await o.onToolExecutionEnd?.({...s,toolExecutionMs:Date.now()-u,toolOutput:{dynamic:!0,input:t.input,output:i,toolCallId:t.id,toolName:t.name,type:"tool-result"}}),i}catch(i){throw await o.onToolExecutionEnd?.({...s,toolExecutionMs:Date.now()-u,toolOutput:{dynamic:!0,error:i,input:t.input,toolCallId:t.id,toolName:t.name,type:"tool-error"}}),i}},ut=8,dt=e=>ot(e)?e:0,W=e=>e===void 0?"null":JSON.stringify(e),lt=async(e,t,n)=>{try{await e({error:t instanceof Error?t.message:String(t),status:"error",...n===void 0?{}:{usage:n}})}catch(o){t instanceof Error&&t.cause===void 0&&(t.cause=o)}},pt=e=>e===void 0?[]:[e].flat(),mt=async(e,t)=>(await Promise.all(e.map(async o=>o({steps:t})))).some(Boolean),yt=(e,t)=>{if(t===void 0)return e;if(e===void 0)return{...t};const n={};for(const o of["inputTokens","outputTokens","totalTokens"])(e[o]!==void 0||t[o]!==void 0)&&(n[o]=(e[o]??0)+(t[o]??0));return n},ft=async(e,t,n,o,a)=>{const{needsApproval:s}=e;return s===void 0||s===!1?!1:s===!0?!0:n.do(o,async()=>s(t,a))},vt="WorkflowTimeoutError",ht=e=>e instanceof Error&&e.name===vt,b=4320*60*1e3,gt={day:864e5,hour:36e5,minute:6e4,month:30*864e5,second:1e3,week:7*864e5,year:365*864e5},wt=/^(\d+(?:\.\d+)?) (day|hour|minute|month|second|week|year)s?$/u,Tt=e=>{if(e===void 0)return b;if(typeof e=="number")return Math.min(e,j);const t=wt.exec(e);return t===null?b:Math.min(Number(t[1])*gt[t[2]],j)},xt=async(e,t)=>{const{agent:n,deleteMessage:o,instanceId:a,patchThread:s,persist:u,step:d}=e;await u({content:`Awaiting approval to run tool "${t.name}".`,messageKey:`${a}:approval:${t.id}`,role:"tool",status:"awaiting_approval",toolCallId:t.id,toolName:t.name}),await s({status:"awaiting_input"});const c=await d.waitForEvent(`approval:${t.id}`,{timeout:Tt(n.approvalTimeout),type:`agent-approval:${t.id}`}).then(i=>({decision:i.payload.decision,...i.payload.note===void 0?{}:{note:i.payload.note}})).catch(i=>{if(!ht(i))throw i;return{decision:"reject",note:"approval timed out"}});return await o(`${a}:approval:${t.id}`),await s({status:"running"}),c},A="lunoraToolOutcome",Ct=e=>typeof e=="object"&&e!==null&&A in e?e[A]:{ok:e},St=async(e,t)=>{const{agent:n,depth:o,env:a,getState:s,instanceId:u,onTokenDelta:d,owner:c,persist:i,run:r,setState:l,step:p,threadKey:m,tools:w}=e,v=`tool:${t.name}:${t.id}`,T=w[t.name],y=`${u}:tool:${t.id}`;if(!T){await i({content:`Error: unknown tool "${t.name}"`,messageKey:y,role:"tool",stepName:v,toolCallId:t.id,toolName:t.name});return}if(t.invalid!==void 0){await i({content:`Error: invalid input for tool "${t.name}" — it was not run. ${t.invalid}`,messageKey:y,role:"tool",stepName:v,toolCallId:t.id,toolName:t.name});return}const M={depth:o,env:a,getState:s,idempotencyKey:v,owner:c,reportProgress:g=>{d?.({data:g,kind:"progress",threadKey:m,toolCallId:t.id})},run:r,setState:l,step:p,threadKey:m,toolCallId:t.id},O={depth:o,env:a,getState:s,idempotencyKey:v,run:r,threadKey:m,toolCallId:t.id},_=`tool:approval-gate:${t.id}`;let x;if(await ft(T,t.input,p,_,O)){const{decision:g,note:E}=await xt(e,t);if(g==="reject"){const U=E===void 0?"":` Reason: ${E}`;await i({content:`Tool "${t.name}" was rejected by the user and not run.${U}`,messageKey:y,role:"tool",status:"rejected",stepName:v,toolCallId:t.id,toolName:t.name});return}x="approved"}const $=Ct(await p.do(v,async()=>{try{const g=()=>Promise.resolve(T.execute(t.input,M)),E=await ct(n.telemetry,{id:t.id,input:t.input,name:t.name},g);return{[A]:{ok:E}}}catch(g){if(Z(g))return{[A]:{failed:g.message}};throw g}}));if("failed"in $){await i({content:q(`Error: tool "${t.name}" failed and will not be retried. ${$.failed}`),messageKey:y,role:"tool",stepName:v,...x===void 0?{}:{status:x},toolCallId:t.id,toolName:t.name});return}const R=$.ok;await i({content:q(typeof R=="string"?R:W(R)),messageKey:y,role:"tool",stepName:v,...x===void 0?{}:{status:x},toolCallId:t.id,toolName:t.name})},Et=(e,t,n)=>{if(!t)return e;let{messages:o}=e;return t.messages!==void 0&&(o=t.messages),t.system!==void 0&&(o=[{content:t.system,role:"system"},...o]),{activeTools:t.activeTools??e.activeTools,messages:o,model:t.model===void 0?e.model:et(t.model,n),toolChoice:t.toolChoice??e.toolChoice}},kt=(e,t)=>{if(t===void 0||e.length<=t.maxMessages)return;const n=Math.max(1,t.keepRecent??Math.ceil(t.maxMessages/2));if(e.length<=n)return;let o=e.length-n;for(;o>0&&e[o]?.role==="tool";)o-=1;if(!(o<=0))return{older:e.slice(0,o),recent:e.slice(o)}},Mt=async(e,t)=>{const{agent:n,compact:o,env:a}=e,s=kt(t,n.compaction);if(s===void 0||o===void 0)return{history:t,summary:void 0};try{const u=await o({env:a,messages:G({history:s.older}),model:n.compaction?.model??n.model});return u.length>0?{history:s.recent,summary:u}:{history:t,summary:void 0}}catch{return{history:t,summary:void 0}}},$t=async(e,t,n)=>{const{agent:o,env:a,generate:s,instructions:u,listMessages:d,memoryContext:c,onTokenDelta:i,run:r,step:l,streamGenerate:p,threadKey:m}=e;return l.do(`llm:turn:${String(t)}`,async()=>{const w=await r(d,{key:m}),{history:v,summary:T}=await Mt(e,w);let y={activeTools:o.activeTools,messages:G({history:v,instructions:u,memoryContext:c,summary:T}),model:void 0,toolChoice:o.toolChoice};o.prepareStep&&(y=Et(y,await o.prepareStep({messages:y.messages,stepNumber:t,steps:n}),a));const k={messages:y.messages,...y.activeTools===void 0?{}:{activeTools:y.activeTools},...y.model===void 0?{}:{model:y.model},...y.toolChoice===void 0?{}:{toolChoice:y.toolChoice}};return p&&i?p(k,M=>{i({text:M,threadKey:m,turn:t})}):s(k)})},B=async(e,t)=>{const{agent:n,step:o}=e;n.onStepFinish&&await o.do(`agent:step-finish:${String(t.turn)}`,async()=>(await n.onStepFinish?.(t),!0))},Rt=e=>({text:e.text,toolCalls:e.toolCalls.map(t=>({input:t.input,toolCallId:t.id,toolName:t.name})),...e.usage===void 0?{}:{usage:e.usage}}),F=e=>{const t=e?.context;return typeof t=="string"&&t.length>0?t:void 0},It=e=>tt({depth:e?.depth,fanOut:e?.fanOut,maxNodes:e?.maxNodes,maxSeeds:e?.maxSeeds}),Kt=async(e,t,n,o)=>{if(e.source===void 0)return;const a=h(e.source),{topK:s}=e,u=S("memory:retrieve",e.key),d=await n.do(u,async()=>o(a,{query:t,...s===void 0?{}:{topK:s}}));return F(d)},Nt=async(e,t,n,o,a,s)=>{if(n===void 0)return;const u=S("memory:traverse",e.key),d=await a.do(u,async()=>s(o,{owner:n,query:t,...It(e.graph)}));return F(d)},At=async(e,t,n,o,a)=>{if(t===void 0)return;const s=S("memory:recall",e.key),u={owner:t,...e.episodic?.recall===void 0?{}:{limit:e.episodic.recall}},d=await o.do(s,async()=>a(n,u));return F(d)},Ot=async(e,t)=>e.kind==="episodic"?At(e,t.owner,t.episodeRecall,t.step,t.run):e.kind==="graph"?Nt(e,t.input,t.owner,t.graphTraverse,t.step,t.run):Kt(e,t.input,t.step,t.run),_t=e=>{const t=e.findIndex(n=>n.kind==="episodic");return e.filter((n,o)=>n.kind!=="episodic"||o===t)},Ut=async(e,t,n,o,a,s)=>{const u=_t(P(e));if(u.length===0)return;const d=h(o.graphTraverse),c=h(o.episodeRecall),i=[];for(const r of u){const l=await Ot(r,{episodeRecall:c,graphTraverse:d,input:t,owner:n,run:s,step:a});l!==void 0&&i.push(l)}return i.length>0?i.join(`
2
+
3
+ `):void 0},Dt=async e=>{const{agent:t,env:n,extractGraph:o,finalText:a,input:s,instanceId:u,owner:d,paths:c,run:i,step:r}=e;if(o===void 0||d===void 0||a===void 0||a.length===0)return;const l=st(t);if(l!==void 0)try{const p={assistantText:a,env:n,model:l.graph?.extractionModel??t.model,userInput:s},m=await r.do(S("memory:extract",l.key),async()=>o(p));await i(h(c.graphUpsert),{...m,messageKey:`${u}:${S("extract",l.key)}`,owner:d})}catch{}},Pt=async e=>{const{agent:t,env:n,extractEpisode:o,finalText:a,input:s,instanceId:u,owner:d,paths:c,run:i,step:r,threadKey:l}=e;if(o===void 0||d===void 0||a===void 0||a.length===0)return;const p=rt(t);if(p!==void 0)try{const m={assistantText:a,env:n,model:p.episodic?.extractionModel??t.model,userInput:s},{summary:w}=await r.do(S("memory:episode",p.key),async()=>o(m));await i(h(c.episodeUpsert),{messageKey:`${u}:${S("episode",p.key)}`,owner:d,summary:w,threadKey:l})}catch{}},Ft=async(e,t,n)=>{const o=e[L(t)];if(!(!o||typeof o.get!="function"))try{await(await o.get(n)).terminate()}catch{}},jt="12 hours",qt=async(e,t,n)=>{await e.waitForEvent(`dequeue:${n}`,{timeout:jt,type:`agent-dequeue:${t}:${n}`})},bt=async(e,t,n)=>{const o=e.env[L(e.exportName)];if(!o||typeof o.get!="function")return;const a=o.get.bind(o);try{await e.step.do(`dequeue-wake:${n}`,async()=>(await(await a(n)).sendEvent({payload:{threadKey:t},type:`agent-dequeue:${t}:${n}`}),n))}catch{}},Bt=async(e,t,n)=>{const{onReply:o}=e,{replyRef:a}=t.params;if(!(o===void 0||a===void 0))try{await t.step.do("agent:reply",async()=>(await o({env:t.env,replyRef:a,result:n,threadKey:t.params.threadKey}),a.channel))}catch{}},Gt=async(e,t,n,o,a)=>{const{instanceId:s,persist:u}=e,d=n.usage===void 0?{}:{usage:n.usage};if(n.toolCalls.length===0){const c=n.output!==void 0&&n.text.length===0?W(n.output):n.text;return await u({content:c,messageKey:`${s}:assistant:${String(t)}`,role:"assistant"}),await B(e,{text:n.text,toolCalls:[],turn:t,...d}),{final:{stopped:"final",text:n.text,turns:t+1,...n.output===void 0?{}:{output:n.output}}}}await u({content:n.text,messageKey:`${s}:assistant:${String(t)}`,role:"assistant",toolCalls:n.toolCalls});for(const c of n.toolCalls)await St(e,c);if(await B(e,{text:n.text,toolCalls:n.toolCalls,turn:t,...d}),o.push(Rt(n)),a.length>0&&await mt(a,o))return"stopCondition"},Lt=async(e,t,n,o)=>{const a=[];let s,u=!1,d=0;for(let c=0;c<t;c+=1){const i=await $t(e,c,a);o.value=yt(o.value,i.usage),d=c+1;const r=await Gt(e,c,i,a,n);if(r==="stopCondition"){u=!0;break}if(r!==void 0){s=r.final;break}}return{final:s,stoppedByCondition:u,turnsRun:d}},te=async e=>{const{agent:t,compact:n,env:o,exportName:a,extractEpisode:s,extractGraph:u,generate:d,instanceId:c,onTokenDelta:i,params:r,paths:l,run:p,step:m,streamGenerate:w}=e,v=t.maxTurns??ut,T=pt(t.stopWhen),y=h(l.appendMessage),k=h(l.completeRun),M=h(l.ensureThread),O=h(l.listMessages),_=h(l.deleteMessage),x=h(l.patchThread),$=h(l.setState),R=h(l.state),g=async f=>{await p(y,{threadKey:r.threadKey,...f})},E=async f=>{await p(x,{key:r.threadKey,...f})},U=async f=>{await p(_,{messageKey:f,threadKey:r.threadKey})},z=async()=>{await g({content:r.input,messageKey:`${c}:user`,role:"user"})},I=async f=>{const N=await p(k,{instanceId:c,key:r.threadKey,...f});N?.dequeued!==void 0&&await bt({env:o,exportName:a,step:m},r.threadKey,N.dequeued)},H=async()=>await p(R,{key:r.threadKey}),V=async f=>{await p($,{key:r.threadKey,state:f})},D=await p(M,{agent:a,instanceId:c,key:r.threadKey,...t.initialState===void 0?{}:{initialState:t.initialState},...t.onConcurrentRun===void 0?{}:{onConcurrentRun:t.onConcurrentRun},...r.owner===void 0?{}:{owner:r.owner},...r.title===void 0?{}:{title:r.title}});D?.outcome==="replaced"&&await Ft(o,a,D.priorInstanceId);const X=await Ut(t,r.input,r.owner,l,m,p),J=typeof t.instructions=="function"?t.instructions({env:o,input:r.input,threadKey:r.threadKey}):t.instructions,Q={agent:t,compact:n,depth:dt(r.depth),env:o,generate:d,getState:H,instanceId:c,instructions:J,listMessages:O,memoryContext:X,onTokenDelta:i,owner:r.owner,deleteMessage:U,patchThread:E,persist:g,run:p,setState:V,step:m,streamGenerate:w,threadKey:r.threadKey,tools:t.tools??{}},K={value:void 0};try{D?.outcome==="queued"&&await qt(m,r.threadKey,c),await z();const{final:f,stoppedByCondition:N,turnsRun:Y}=await Lt(Q,v,T,K),C=K.value===void 0?{}:{usage:K.value};return await Dt({agent:t,env:o,extractGraph:u,finalText:f?.text,input:r.input,instanceId:c,owner:r.owner,paths:l,run:p,step:m}),await Pt({agent:t,env:o,extractEpisode:s,finalText:f?.text,input:r.input,instanceId:c,owner:r.owner,paths:l,run:p,step:m,threadKey:r.threadKey}),f?(await Bt(t,{env:o,params:r,step:m},{...f,...C}),await I({status:"idle",...C}),{...f,...C}):N?(await I({status:"idle",...C}),{stopped:"stopCondition",turns:Y,...C}):(await I({error:`agent stopped: maxTurns (${String(v)}) reached`,status:"error",...C}),{stopped:"maxTurns",turns:v,...C})}catch(f){throw await lt(I,f,K.value),f}};export{Tt as a,Mt as c,at as i,te as r,kt as s};
@@ -0,0 +1 @@
1
+ import{LunoraError as c}from"@lunora/errors";import{isDuplicateInstanceError as N}from"@lunora/workflow";import{jsonSchema as I}from"ai";import{f as L}from"./fnv1a-BNN96GYb.mjs";import{agentBindingName as S}from"../naming.mjs";import{toFunctionReference as v,DEFAULT_AGENT_FUNCTION_PATHS as $}from"./AGENT_MODULE-M4D1EejI.mjs";import{i as h}from"./positive-integer-ztHqqpBk.mjs";const _=new Set(["complete","errored","terminated"]),P=600,w=3,M=500,R=t=>t!==null&&typeof t=="object"&&typeof t.status=="string"?t.status:"unknown",D=t=>t!==null&&typeof t=="object"?t.output:void 0,U=async t=>{await new Promise(e=>{setTimeout(e,t)})},j=async(t,e,n,m)=>{for(let a=0;a<e;a+=1){const i=await t.status(),s=R(i);if(_.has(s))return{output:D(i),status:s};await m(n)}return{output:void 0,status:"timeout"}},k=t=>{if(t!==null&&typeof t=="object"&&typeof t.stopped=="string")return t.stopped},x=t=>{for(let e=t.length-1;e>=0;e-=1){const n=t[e];if(n?.role==="assistant"&&(n.toolCalls===void 0||n.toolCalls.length===0))return n.content}return""},W=t=>{if(typeof t.name!="string"||t.name.length===0)throw new c("INTERNAL","@lunora/agent: agent.asTool requires a `name` (the child agent's export name, selecting its AGENT_* binding)");if(typeof t.description!="string"||t.description.length===0)throw new c("INTERNAL","@lunora/agent: agent.asTool requires a non-empty `description` (the parent model decides from it)");if(t.maxPolls!==void 0&&!h(t.maxPolls))throw new c("INTERNAL","@lunora/agent: agent.asTool `maxPolls` must be a positive integer");const{name:e}=t,n=S(e),m=t.maxPolls??P,a=t.pollIntervalMs??M,i=t.wait??U,s=v($.listMessages),T=async(y,r)=>{const p=(h(r.depth)?r.depth:0)+1;if(p>w)return`Sub-agent "${e}" was not started: the maximum delegation depth of ${String(w)} is already reached. Answer with what you have instead of delegating further.`;const o=r.env[n];if(!o||typeof o.create!="function"||typeof o.get!="function")throw new c("INTERNAL",`@lunora/agent: agent.asTool("${e}") found no Workflow binding "${n}" on env — declare the "${e}" agent so codegen wires its binding`);const g=`${r.threadKey}::sub::${e}::${r.toolCallId}`,f=`sub-${e}-${L(r.toolCallId)}`,A={depth:p,input:y.prompt,threadKey:g,...r.owner===void 0?{}:{owner:r.owner}};let l;try{const d=await o.create({id:f,params:A});l=await o.get(d.id)}catch(d){if(!N(d))throw d;l=await o.get(f)}const{output:b,status:u}=await j(l,m,a,i);if(u==="errored"||u==="terminated")return`Sub-agent "${e}" ${u} before producing an answer.`;if(u==="timeout"){try{await l.terminate()}catch{}return`Sub-agent "${e}" did not finish within the allotted time.`}if(k(b)==="maxTurns")return`Sub-agent "${e}" hit its turn cap (maxTurns) before producing a final answer. Ask it something narrower, or answer with what you have.`;const E=await r.run(s,{key:g});return x(E)};return{description:t.description,execute:T,inputSchema:I({properties:{prompt:{description:"The task or question to delegate to the sub-agent.",type:"string"}},required:["prompt"],type:"object"}),isLunoraAgentTool:!0}};export{W as agentAsTool};
@@ -0,0 +1 @@
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};