@lunora/agent 1.0.0-alpha.9 → 1.0.0-alpha.91

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 -181
  4. package/dist/component.d.mts +2 -0
  5. package/dist/component.d.ts +2 -0
  6. package/dist/component.mjs +2 -418
  7. package/dist/inbound.d.mts +7 -5
  8. package/dist/inbound.d.ts +7 -5
  9. package/dist/inbound.mjs +1 -32
  10. package/dist/index.d.mts +49 -8
  11. package/dist/index.d.ts +49 -8
  12. package/dist/index.mjs +1 -18
  13. package/dist/naming.mjs +1 -8
  14. package/dist/packem_shared/AGENT_MODULE-M4D1EejI.mjs +1 -0
  15. package/dist/packem_shared/VoiceSessionDO-BdvnaLoO.mjs +1 -0
  16. package/dist/packem_shared/adaptMcpResult-CVlq-_TO.mjs +2 -0
  17. package/dist/packem_shared/agent-loop-B3KTMfqm.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-DRkaF9sV.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-DPFZ88dr.mjs +1 -0
  31. package/dist/packem_shared/createAgentGenerate-BOILDUZj.mjs +3 -0
  32. package/dist/packem_shared/createDispatchRunner-BMgSkFiH-DjUQx8vg.mjs +1 -0
  33. package/dist/packem_shared/defineAgent-Q3MlQjYs.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-DJvzWDgh.mjs +1 -0
  42. package/dist/packem_shared/runVoiceTurn-D1WCjVPw.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-D1ZzTzcE.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 -113
  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 -5
  61. package/package.json +18 -9
  62. package/dist/packem_shared/AGENT_MODULE-Dnt_-AAT.mjs +0 -24
  63. package/dist/packem_shared/VoiceSessionDO-BdwlLaXC.mjs +0 -297
  64. package/dist/packem_shared/adaptMcpResult-wtNMvLoP.mjs +0 -65
  65. package/dist/packem_shared/agentAsTool-CUHlWsmt.mjs +0 -98
  66. package/dist/packem_shared/base64-BVwtgRJV.mjs +0 -18
  67. package/dist/packem_shared/braintrustTelemetry-TP7Kwuuj.mjs +0 -47
  68. package/dist/packem_shared/buildModelMessages-BWFigaoo.mjs +0 -69
  69. package/dist/packem_shared/codeTool-CjgJOC9t.mjs +0 -122
  70. package/dist/packem_shared/collectAgenticMemoryTools-QrzpV-WX.mjs +0 -97
  71. package/dist/packem_shared/combineTelemetry-DCyaaWAI.mjs +0 -43
  72. package/dist/packem_shared/common-DQXayow6.mjs +0 -89
  73. package/dist/packem_shared/compileAgentWorkflow-DAfyUuI5.mjs +0 -78
  74. package/dist/packem_shared/consoleTelemetry--3sWfu1R.mjs +0 -93
  75. package/dist/packem_shared/createAgentContext-4xJGXNR4.mjs +0 -50
  76. package/dist/packem_shared/createAgentGenerate-DO7Z96zX.mjs +0 -192
  77. package/dist/packem_shared/createDispatchRunner-DSbp_dph-ZHTtxy3f.mjs +0 -69
  78. package/dist/packem_shared/defineAgent-DAwAZC9P.mjs +0 -148
  79. package/dist/packem_shared/defineSkill-Ctf_S-rz.mjs +0 -22
  80. package/dist/packem_shared/functionTool-D6lCa2jB.mjs +0 -20
  81. package/dist/packem_shared/graph-component-Bbaxxymp.mjs +0 -217
  82. package/dist/packem_shared/memory-D4FPcBsX.mjs +0 -12
  83. package/dist/packem_shared/normalizeEntityName-BouctxLC.mjs +0 -3
  84. package/dist/packem_shared/otlpTelemetry-CKgmWVLg.mjs +0 -170
  85. package/dist/packem_shared/runAgentLoop-M8PKbtWT.mjs +0 -493
  86. package/dist/packem_shared/runVoiceTurn-LnqLvCRR.mjs +0 -211
  87. package/dist/packem_shared/sandboxComponent-DR3pTwBL.mjs +0 -194
  88. package/dist/packem_shared/sentryTelemetry-A4F5ndh9.mjs +0 -36
package/dist/sandbox.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as AgentToolContext, b as AgentToolDefinition } from "./packem_shared/types.d-boAM2Yi1.js";
1
+ import { b as AgentToolContext, c as AgentToolDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
4
  /**
@@ -25,8 +25,8 @@ type BrowserToolInput = {
25
25
  /**
26
26
  * The model-provided input to a {@link containerTool} call — a discriminated
27
27
  * union on `op`. `fetch` sends an HTTP request to the container; `exec` asks it
28
- * to run a command (routed as a POST to `/exec`, since the container surface
29
- * exposes no first-class exec RPC — the container app must serve that route).
28
+ * to run a command (delegated to `ctx.containers.<name>.exec`, the first-class
29
+ * exec contract in `@lunora/container`; the container app serves its route).
30
30
  * @experimental
31
31
  */
32
32
  type ContainerToolInput = {
@@ -112,12 +112,13 @@ interface ContainerToolOptions {
112
112
  description?: string;
113
113
  /**
114
114
  * Gate a call behind a human approval. Defaults to gating any command
115
- * execution: an `exec`, AND a `fetch` whose path resolves to the privileged
116
- * `/exec` route (both reach the same command-execution path in the container,
117
- * so gating on the `op` name alone would let a `fetch` to `/exec` run a
118
- * command unattended). A plain `fetch` to any other route runs unattended.
119
- * Pass a boolean or your own predicate to change that. Evaluated from
120
- * replay-stable input, so keep it deterministic.
115
+ * execution an `exec` plus any `fetch` using a non-idempotent method
116
+ * (POST/PUT/PATCH/DELETE), so a prompt-injected model cannot reach some
117
+ * other mutating route unattended. A read-only `fetch` runs unattended. The
118
+ * exec route itself needs no special case: `@lunora/container` reserves
119
+ * `/__lunora/*` and refuses a `fetch` into it. Pass a boolean or your own
120
+ * predicate to change that. Evaluated from replay-stable input, so keep it
121
+ * deterministic.
121
122
  */
122
123
  needsApproval?: ((input: ContainerToolInput) => boolean) | boolean;
123
124
  }
@@ -149,19 +150,19 @@ interface ContainerToolOptions {
149
150
  declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition<BrowserToolInput, string>;
150
151
  /**
151
152
  * A batteries-included agent tool that talks to a declared Cloudflare
152
- * Container. `name` is the `ctx.containers.&lt;name>` key (the `lunora/containers.ts`
153
+ * Container. `name` is the `ctx.containers.<name>` key (the `lunora/containers.ts`
153
154
  * export). One tool exposes `fetch` (HTTP request) and `exec` (run a command);
154
155
  * the model picks via `op`. The call dispatches to the auto-registered
155
156
  * `sandbox:invoke` action, which carries `ctx.containers`.
156
157
  *
157
158
  * By default a read-only (GET/HEAD/OPTIONS, or method-omitted) `fetch` runs
158
159
  * unattended while everything else is gated behind a human approval — an
159
- * `exec`; a `fetch` whose path resolves to the privileged `/exec` route (both
160
+ * `exec`; a `fetch` whose path resolves to the privileged exec route (both
160
161
  * reach the same command-execution path in the container, so gating on the
161
- * `op` name alone would let a `fetch` to `/exec` run a command unattended);
162
+ * `op` name alone would let a `fetch` to it run a command unattended);
162
163
  * and a `fetch` using a non-idempotent method (POST/PUT/PATCH/DELETE) to ANY
163
164
  * route, since a prompt-injected model could otherwise mutate container state
164
- * through some other privileged route just by avoiding the literal `/exec`
165
+ * through some other privileged route just by avoiding the literal exec
165
166
  * path. A `fetch` can still reach any *other read* route unattended, so scope
166
167
  * the container's GET routes accordingly. Pass `opts.needsApproval` to widen
167
168
  * or disable the gate.
package/dist/sandbox.mjs CHANGED
@@ -1,113 +1 @@
1
- import { LunoraError } from '@lunora/errors';
2
- import { jsonSchema } from 'ai';
3
- import { toFunctionReference, SANDBOX_INVOKE_PATH } from './packem_shared/AGENT_MODULE-Dnt_-AAT.mjs';
4
-
5
- const SANDBOX_REF = toFunctionReference(SANDBOX_INVOKE_PATH);
6
- const DEFAULT_BROWSER_DESCRIPTION = 'Drive a headless browser: screenshot a page, render it to PDF, read its HTML content, or scrape it. Set `op` to "screenshot" | "pdf" | "content" | "scrape" and pass the target `url`.';
7
- const DEFAULT_CONTAINER_DESCRIPTION = 'Talk to a sandboxed container: `fetch` an HTTP path on it, or `exec` a command inside it. Set `op` to "fetch" (with `path`) or "exec" (with `command`).';
8
- const DEFAULT_FS_DESCRIPTION = 'Read and write files in a persistent sandbox filesystem. Set `op` to "ls" (list a directory), "read", "write" (with `content`), "rm", or "stat", and pass `path`.';
9
- const defaultFsGate = (input) => input.op === "write" || input.op === "rm";
10
- const FS_TOOL_SCHEMA = jsonSchema({
11
- properties: {
12
- content: { description: "write: the file contents.", type: "string" },
13
- op: { description: "The filesystem operation to run.", enum: ["ls", "read", "write", "rm", "stat"], type: "string" },
14
- path: { description: "The file or directory path, relative to the sandbox root.", type: "string" }
15
- },
16
- required: ["op"],
17
- type: "object"
18
- });
19
- const BROWSER_TOOL_SCHEMA = jsonSchema({
20
- properties: {
21
- fullPage: { description: "screenshot: capture the full scrollable page rather than just the viewport.", type: "boolean" },
22
- op: { description: "The browser operation to run.", enum: ["screenshot", "scrape", "pdf", "content"], type: "string" },
23
- selector: { description: "scrape: optional CSS selector hint to narrow the extraction.", type: "string" },
24
- type: { description: "screenshot: image encoding (default png).", enum: ["png", "jpeg"], type: "string" },
25
- url: { description: "The URL to operate on.", type: "string" }
26
- },
27
- required: ["op", "url"],
28
- type: "object"
29
- });
30
- const CONTAINER_EXEC_ROUTE = "/exec";
31
- const CONTAINER_PATH_QUERY_SPLIT = /[?#]/u;
32
- const normalizeContainerPath = (path) => {
33
- const [raw = ""] = (path ?? "").split(CONTAINER_PATH_QUERY_SPLIT);
34
- const segments = [];
35
- for (const segment of raw.split("/")) {
36
- if (segment === "" || segment === ".") {
37
- continue;
38
- }
39
- if (segment === "..") {
40
- segments.pop();
41
- continue;
42
- }
43
- segments.push(segment);
44
- }
45
- return `/${segments.join("/")}`;
46
- };
47
- const NON_IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["DELETE", "PATCH", "POST", "PUT"]);
48
- const defaultContainerGate = (input) => {
49
- if (input.op === "exec") {
50
- return true;
51
- }
52
- if (normalizeContainerPath(input.path) === CONTAINER_EXEC_ROUTE) {
53
- return true;
54
- }
55
- return NON_IDEMPOTENT_METHODS.has((input.method ?? "GET").toUpperCase());
56
- };
57
- const CONTAINER_TOOL_SCHEMA = jsonSchema({
58
- properties: {
59
- args: { description: "exec: command arguments.", items: { type: "string" }, type: "array" },
60
- body: { description: "fetch: request body.", type: "string" },
61
- command: { description: "exec: the command to run.", type: "string" },
62
- method: { description: "fetch: HTTP method (default GET).", type: "string" },
63
- op: { description: "The container operation to run.", enum: ["fetch", "exec"], type: "string" },
64
- path: { description: "fetch: request path on the container (e.g. /health).", type: "string" }
65
- },
66
- required: ["op"],
67
- type: "object"
68
- });
69
- const browserTool = (options = {}) => {
70
- return {
71
- description: options.description ?? DEFAULT_BROWSER_DESCRIPTION,
72
- // Pin `kind` LAST so out-of-schema model input can never override it.
73
- execute: (input, context) => context.run(SANDBOX_REF, { ...input, kind: "browser" }),
74
- inputSchema: BROWSER_TOOL_SCHEMA,
75
- isLunoraAgentTool: true,
76
- ...options.needsApproval === void 0 ? {} : { needsApproval: options.needsApproval }
77
- };
78
- };
79
- const containerTool = (name, options = {}) => {
80
- if (typeof name !== "string" || name.length === 0) {
81
- throw new LunoraError("INTERNAL", "@lunora/agent: containerTool requires a container `name` (the ctx.containers.<name> key from lunora/containers.ts)");
82
- }
83
- const needsApproval = options.needsApproval ?? defaultContainerGate;
84
- return {
85
- description: options.description ?? DEFAULT_CONTAINER_DESCRIPTION,
86
- // Pin `kind`/`name` LAST so out-of-schema model input can never override
87
- // the authoritative container the author pinned.
88
- execute: (input, context) => context.run(SANDBOX_REF, { ...input, kind: "container", name }),
89
- inputSchema: CONTAINER_TOOL_SCHEMA,
90
- isLunoraAgentTool: true,
91
- needsApproval
92
- };
93
- };
94
- const fsTool = (bucket, options = {}) => {
95
- if (typeof bucket !== "string" || bucket.length === 0) {
96
- throw new LunoraError("INTERNAL", "@lunora/agent: fsTool requires an R2 `bucket` binding name (the r2_bucket declared in wrangler.jsonc)");
97
- }
98
- return {
99
- description: options.description ?? DEFAULT_FS_DESCRIPTION,
100
- // Pin kind/bucket/root LAST so out-of-schema model input can never override
101
- // the authoritative bucket + sandbox root the author pinned. `root` may be
102
- // a function of the ctx (per-run/owner isolation), resolved here.
103
- execute: (input, context) => {
104
- const root = typeof options.root === "function" ? options.root(context) : options.root ?? "";
105
- return context.run(SANDBOX_REF, { ...input, bucket, kind: "fs", root });
106
- },
107
- inputSchema: FS_TOOL_SCHEMA,
108
- isLunoraAgentTool: true,
109
- needsApproval: options.needsApproval ?? defaultFsGate
110
- };
111
- };
112
-
113
- export { browserTool, containerTool, fsTool };
1
+ import{LunoraError as a}from"@lunora/errors";import{jsonSchema as i}from"ai";import{toFunctionReference as p,SANDBOX_INVOKE_PATH as c}from"./packem_shared/AGENT_MODULE-M4D1EejI.mjs";const s=p(c),d='Drive a headless browser: screenshot a page, render it to PDF, read its HTML content, or scrape it. Set `op` to "screenshot" | "pdf" | "content" | "scrape" and pass the target `url`.',h='Talk to a sandboxed container: `fetch` an HTTP path on it, or `exec` a command inside it. Set `op` to "fetch" (with `path`) or "exec" (with `command`).',l='Read and write files in a persistent sandbox filesystem. Set `op` to "ls" (list a directory), "read", "write" (with `content`), "rm", or "stat", and pass `path`.',u=e=>e.op==="write"||e.op==="rm",T=i({properties:{content:{description:"write: the file contents.",type:"string"},op:{description:"The filesystem operation to run.",enum:["ls","read","write","rm","stat"],type:"string"},path:{description:"The file or directory path, relative to the sandbox root.",type:"string"}},required:["op"],type:"object"}),m=i({properties:{fullPage:{description:"screenshot: capture the full scrollable page rather than just the viewport.",type:"boolean"},op:{description:"The browser operation to run.",enum:["screenshot","scrape","pdf","content"],type:"string"},selector:{description:"scrape: optional CSS selector hint to narrow the extraction.",type:"string"},type:{description:"screenshot: image encoding (default png).",enum:["png","jpeg"],type:"string"},url:{description:"The URL to operate on.",type:"string"}},required:["op","url"],type:"object"}),g=new Set(["DELETE","PATCH","POST","PUT"]),f=e=>e.op==="exec"||g.has((e.method??"GET").toUpperCase()),y=i({properties:{args:{description:"exec: command arguments.",items:{type:"string"},type:"array"},body:{description:"fetch: request body.",type:"string"},command:{description:"exec: the command to run.",type:"string"},method:{description:"fetch: HTTP method (default GET).",type:"string"},op:{description:"The container operation to run.",enum:["fetch","exec"],type:"string"},path:{description:"fetch: request path on the container (e.g. /health).",type:"string"}},required:["op"],type:"object"}),w=(e={})=>({description:e.description??d,execute:(t,r)=>r.run(s,{...t,kind:"browser"}),inputSchema:m,isLunoraAgentTool:!0,...e.needsApproval===void 0?{}:{needsApproval:e.needsApproval}}),O=(e,t={})=>{if(typeof e!="string"||e.length===0)throw new a("INTERNAL","@lunora/agent: containerTool requires a container `name` (the ctx.containers.<name> key from lunora/containers.ts)");const r=t.needsApproval??f;return{description:t.description??h,execute:(o,n)=>n.run(s,{...o,kind:"container",name:e}),inputSchema:y,isLunoraAgentTool:!0,needsApproval:r}},_=(e,t={})=>{if(typeof e!="string"||e.length===0)throw new a("INTERNAL","@lunora/agent: fsTool requires an R2 `bucket` binding name (the r2_bucket declared in wrangler.jsonc)");return{description:t.description??l,execute:(r,o)=>{const n=typeof t.root=="function"?t.root(o):t.root??"";return o.run(s,{...r,bucket:e,kind:"fs",root:n})},inputSchema:T,isLunoraAgentTool:!0,needsApproval:t.needsApproval??u}};export{w as browserTool,O as containerTool,_ as fsTool};
@@ -0,0 +1,36 @@
1
+ import { S as SkillMarkdownExtras, d as SkillDefinition } from "./packem_shared/types.d-B8WZl1rU.mjs";
2
+ import '@lunora/mail/inbound';
3
+ import 'ai';
4
+ /**
5
+ * Declare a skill whose prose lives in a markdown file.
6
+ *
7
+ * ```ts
8
+ * import triage from "./skills/triage/SKILL.md?raw";
9
+ * import { functionTool } from "@lunora/agent";
10
+ * import { skillFromMarkdown } from "@lunora/agent/skill-markdown";
11
+ *
12
+ * export const triageSkill = skillFromMarkdown(triage, {
13
+ * tools: { searchCode: functionTool(api.code.search, { ... }) },
14
+ * });
15
+ * ```
16
+ *
17
+ * ```md
18
+ * ---
19
+ * name: triage
20
+ * ---
21
+ * Reproduce the report before proposing a cause. Cite the failing test.
22
+ * ```
23
+ *
24
+ * The split is the point: the instructions are the part a non-author reads,
25
+ * reviews and copies between projects, so they belong in a file that is legible
26
+ * on its own — and the `name` travels WITH that file, so a skill can move
27
+ * without a matching TypeScript wrapper. Tools and `knowledge` stay in code,
28
+ * because they are code.
29
+ *
30
+ * Getting the markdown here is the caller's job (`?raw` under Vite, a build-time
31
+ * import elsewhere) — a Worker has no filesystem to read it from at runtime, so
32
+ * this takes the string rather than a path.
33
+ * @experimental
34
+ */
35
+ declare const skillFromMarkdown: (markdown: string, extras?: SkillMarkdownExtras) => SkillDefinition;
36
+ export { type SkillMarkdownExtras, skillFromMarkdown };
@@ -0,0 +1,36 @@
1
+ import { S as SkillMarkdownExtras, d as SkillDefinition } from "./packem_shared/types.d-B8WZl1rU.js";
2
+ import '@lunora/mail/inbound';
3
+ import 'ai';
4
+ /**
5
+ * Declare a skill whose prose lives in a markdown file.
6
+ *
7
+ * ```ts
8
+ * import triage from "./skills/triage/SKILL.md?raw";
9
+ * import { functionTool } from "@lunora/agent";
10
+ * import { skillFromMarkdown } from "@lunora/agent/skill-markdown";
11
+ *
12
+ * export const triageSkill = skillFromMarkdown(triage, {
13
+ * tools: { searchCode: functionTool(api.code.search, { ... }) },
14
+ * });
15
+ * ```
16
+ *
17
+ * ```md
18
+ * ---
19
+ * name: triage
20
+ * ---
21
+ * Reproduce the report before proposing a cause. Cite the failing test.
22
+ * ```
23
+ *
24
+ * The split is the point: the instructions are the part a non-author reads,
25
+ * reviews and copies between projects, so they belong in a file that is legible
26
+ * on its own — and the `name` travels WITH that file, so a skill can move
27
+ * without a matching TypeScript wrapper. Tools and `knowledge` stay in code,
28
+ * because they are code.
29
+ *
30
+ * Getting the markdown here is the caller's job (`?raw` under Vite, a build-time
31
+ * import elsewhere) — a Worker has no filesystem to read it from at runtime, so
32
+ * this takes the string rather than a path.
33
+ * @experimental
34
+ */
35
+ declare const skillFromMarkdown: (markdown: string, extras?: SkillMarkdownExtras) => SkillDefinition;
36
+ export { type SkillMarkdownExtras, skillFromMarkdown };
@@ -0,0 +1 @@
1
+ import{LunoraError as o}from"@lunora/errors";import{parse as s}from"yaml";import{defineSkill as l}from"./packem_shared/defineSkill-DGGvDWNq.mjs";const m=/^\uFEFF?[ \t]*---[ \t]*\r?\n/u,h=/^---[ \t]*$/mu,c=t=>{const r=m.exec(t);if(!r)return{body:t.trim(),data:{}};const n=t.slice(r[0].length),e=h.exec(n);if(!e)throw new o("INTERNAL","@lunora/agent: the skill markdown opens with `---` but never closes the frontmatter block. Add a closing `---` on its own line.");let a;try{a=s(n.slice(0,e.index))}catch(i){throw new o("INTERNAL",`@lunora/agent: the skill markdown has invalid YAML frontmatter — ${i instanceof Error?i.message:String(i)}`)}return{body:n.slice(e.index+e[0].length).trim(),data:typeof a=="object"&&a!==null?a:{}}},w=(t,r={})=>{if(typeof t!="string")throw new o("INTERNAL",`@lunora/agent: skillFromMarkdown expects the markdown SOURCE as a string, got ${typeof t}`);const{body:n,data:e}=c(t);if(e.name!==void 0&&typeof e.name!="string")throw new o("INTERNAL",`@lunora/agent: the skill markdown's \`name\` must be a string, got ${Array.isArray(e.name)?"array":typeof e.name}. Quote it if the value looks like a number or a boolean (\`name: "2fa"\`).`);if(typeof e.name!="string")throw new o("INTERNAL","@lunora/agent: skillFromMarkdown found no `name` in the markdown frontmatter. Open the file with a `---` line, put `name: <identifier>` on its own line, and close it with `---`. Other keys are parsed and ignored — only `name` is read here.");if(n==="")throw new o("INTERNAL",`@lunora/agent: the skill markdown for "${e.name}" has no body — the instructions below the closing \`---\` are what the skill contributes.`);return l({...r,instructions:n,name:e.name})};export{w as skillFromMarkdown};
@@ -60,9 +60,24 @@ interface BraintrustTelemetryOptions extends CommonOptions {
60
60
  * A dependency-injected Braintrust bridge for the ai@7 telemetry surface.
61
61
  *
62
62
  * It wraps model calls (`type: "llm"`) and tool executions (`type: "tool"`) in
63
- * `logger.traced` spans and logs structural metadata. Prompts / tool arguments
64
- * are logged only when `recordInputs` is set; generated text / tool results
65
- * only when `recordOutputs` is set. `onError` opens a span and logs the error.
63
+ * `logger.traced` spans and logs structural metadata, including the call's token
64
+ * usage as Braintrust `metrics`. Prompts / tool arguments are logged only when
65
+ * `recordInputs` is set; generated text / tool results only when `recordOutputs`
66
+ * is set. `onError` opens a span and logs the error.
67
+ *
68
+ * The tool span is driven by the agent LOOP, not by `ai`: Lunora exposes tools
69
+ * schema-only so the SDK never executes one (see `telemetry/tool-execution.ts`).
70
+ *
71
+ * **A model-call span closes when the CALL ends, not when `execute()` resolves.**
72
+ * On a streamed turn `execute()` resolves the instant `doStream` hands back the
73
+ * stream — before a token, before any usage — so a span that simply awaited it
74
+ * measured time-to-first-byte and logged the stream handle instead of the
75
+ * generation. `execute()` still runs INSIDE the traced callback, which is what
76
+ * parents the provider's own work under the span; the callback then parks until
77
+ * the SDK's terminal event for that `callId` arrives, so `traced` finishes the
78
+ * span at the real end of the call, with the real usage. The caller gets
79
+ * `execute()`'s value the moment it resolves, exactly as before — the span's
80
+ * lifetime and the caller's are deliberately separate.
66
81
  *
67
82
  * The app owns Braintrust initialization; pass the logger in as `logger`.
68
83
  * @experimental
@@ -173,36 +188,72 @@ interface OtlpTelemetryOptions extends CommonOptions {
173
188
  * An OTLP-over-HTTP telemetry integration for `@lunora/agent`.
174
189
  *
175
190
  * The OTLP counterpart to the `sentryTelemetry` / `braintrustTelemetry` bridges:
176
- * it wraps each language-model call and tool execution in an OTLP **span**
191
+ * it records each language-model call and each tool execution as an OTLP **span**
177
192
  * (`gen_ai.*` semantic-convention attributes — model, provider, token usage,
178
193
  * tool name) and ships it to a collector, so agent generations land in the same
179
194
  * trace store as the rest of an app's telemetry (the Lunora Cloud, or any OTel
180
195
  * collector). Plug it into `defineAgent({ telemetry: { isEnabled: true,
181
196
  * integrations: [otlpTelemetry({ endpoint, token })] } })`.
182
197
  *
183
- * Emitting inside the turn's execution wrapper means one span per **real** turn:
184
- * the agent loop memoizes each `step.do('llm:turn:N')`, so a Workflow replay
185
- * returns the cached result without re-invoking `execute`, and no duplicate span
186
- * is emitted. Privacy-safe by default `recordInputs`/`recordOutputs` both
187
- * default `false`, so no prompt or generated text leaves the worker without an
188
- * explicit opt-in; only structural metadata + token counts are recorded.
198
+ * **A model-call span closes when the CALL ends, not when `execute()` resolves.**
199
+ * On a streamed turn `execute()` resolves the instant `doStream` hands back the
200
+ * stream before a single token, before any usage, and before any mid-stream
201
+ * failure. Closing the span there reported every voice and workflow-streamed turn
202
+ * as a ~1 ms, zero-token, always-OK call. So the span opens in
203
+ * `executeLanguageModelCall` (which also owns the failure path, since a rejected
204
+ * provider call produces no end event) and closes on `onLanguageModelCallEnd`,
205
+ * which the SDK fires once the response is normalized — after the stream's
206
+ * `finish` part, where the duration and the token usage actually live.
207
+ * `onError` / `onAbort` close whatever is still open, so a stream that dies or is
208
+ * barged in on reports a failure instead of a phantom success.
209
+ *
210
+ * **Tool spans come from the agent loop.** Lunora exposes tools to the model
211
+ * schema-only, so `ai` never runs one and never fires its tool telemetry; the
212
+ * loop calls `executeTool` itself from the durable step where the tool really
213
+ * runs (see `telemetry/tool-execution.ts`).
214
+ *
215
+ * One span per REAL execution: the agent loop memoizes each `step.do(...)`, so a
216
+ * Workflow replay returns the cached result without re-invoking the wrapped work
217
+ * and no duplicate span is emitted. Privacy-safe by default —
218
+ * `recordInputs`/`recordOutputs` both default `false`, so no prompt or generated
219
+ * text leaves the worker without an explicit opt-in; only structural metadata +
220
+ * token counts are recorded.
189
221
  *
190
222
  * Each export is fire-and-forget (registered with `waitUntil` when supplied);
191
223
  * every rejection is swallowed so a flaky collector never surfaces to the run.
192
224
  *
193
- * Two deliberate differences from the SDK-backed bridges, which delegate to a
194
- * host tracer. First, no `onError`: a failed call already emits a span with
195
- * `status.code === 2`, so the failure is on the trace and there is no host client
196
- * to also notify. Second, flat not nested: every span gets `traceId` (shared when
197
- * `traceId` is set) but no `parentSpanId`, so model-call and tool spans are
198
- * siblings under the run rather than a tree — OTLP has no ambient span context to
199
- * parent to here.
225
+ * One deliberate difference from the SDK-backed bridges, which delegate to a host
226
+ * tracer: flat, not nested. Every span gets `traceId` (shared when `traceId` is
227
+ * set) but no `parentSpanId`, so model-call and tool spans are siblings under the
228
+ * run rather than a tree OTLP has no ambient span context to parent to here.
200
229
  * @param options `endpoint` (+ optional `token`/`headers`/`serviceName`),
201
230
  * `traceId` to group a run's spans, `waitUntil`, and the `recordInputs`/
202
231
  * `recordOutputs` privacy flags.
203
232
  * @experimental
204
233
  */
205
234
  declare const otlpTelemetry: (options: OtlpTelemetryOptions) => Telemetry;
235
+ /** The span context both `startSpan` and `startSpanManual` are called with. */
236
+ interface SentrySpanContext {
237
+ attributes?: Record<string, unknown>;
238
+ name: string;
239
+ op?: string;
240
+ }
241
+ /**
242
+ * The subset of a Sentry `Span` this bridge drives. Structural, like
243
+ * {@link SentryLike} itself — a real `@sentry/*` span satisfies it.
244
+ * @experimental
245
+ */
246
+ interface SentrySpan {
247
+ /** Finish the span. Called once, from whichever terminal event closes the call. */
248
+ end: () => void;
249
+ /** Attach attributes discovered after the span started (token usage). */
250
+ setAttributes?: (attributes: Record<string, unknown>) => unknown;
251
+ /** `1` = OK, `2` = ERROR (Sentry's `SPAN_STATUS_OK` / `SPAN_STATUS_ERROR`). */
252
+ setStatus?: (status: {
253
+ code: 0 | 1 | 2;
254
+ message?: string;
255
+ }) => unknown;
256
+ }
206
257
  /**
207
258
  * The minimal, **structural** slice of `@sentry/cloudflare` (equivalently
208
259
  * `@sentry/node`/`@sentry/browser`) this bridge needs. `@sentry/cloudflare` is
@@ -214,16 +265,16 @@ declare const otlpTelemetry: (options: OtlpTelemetryOptions) => Telemetry;
214
265
  interface SentryLike {
215
266
  /** Capture a thrown value / exception. */
216
267
  captureException: (exception: unknown) => unknown;
217
- /** Run `callback` inside a new span and return its result. */
218
- startSpan: <T>(context: {
219
- attributes?: Record<string, unknown>;
220
- name: string;
221
- op?: string;
222
- }, callback: (span: {
223
- setStatus?: (status: {
224
- code: number;
225
- } | string) => void;
226
- }) => T) => T;
268
+ /** Run `callback` inside a new span, finished when the callback settles. */
269
+ startSpan: <T>(context: SentrySpanContext, callback: (span: SentrySpan) => T) => T;
270
+ /**
271
+ * Run `callback` inside a new span that is **not** finished automatically —
272
+ * the caller owns its lifetime through `span.end()`. Present on every Sentry
273
+ * SDK built on `@sentry/core` (verified against `@sentry/core@10.55.0`); it is
274
+ * what lets a streamed model call be measured to the end of the stream while
275
+ * still nesting the provider's own work under it.
276
+ */
277
+ startSpanManual: <T>(context: SentrySpanContext, callback: (span: SentrySpan) => T) => T;
227
278
  }
228
279
  /**
229
280
  * Options for {@link sentryTelemetry}.
@@ -241,14 +292,82 @@ interface SentryTelemetryOptions extends CommonOptions {
241
292
  /**
242
293
  * A dependency-injected Sentry bridge for the ai@7 telemetry surface.
243
294
  *
244
- * It wraps model calls and tool executions in Sentry spans (via
245
- * `Sentry.startSpan`) so nested provider/tool work is correctly parented, and
246
- * routes `onError` to `Sentry.captureException`. Span attributes carry only
247
- * structural metadata (model, provider, tool name) unless `recordInputs` is
248
- * set, in which case prompts / tool arguments are attached too.
295
+ * It wraps model calls and tool executions in Sentry spans so nested
296
+ * provider/tool work is correctly parented, and routes `onError` to
297
+ * `Sentry.captureException`. Span attributes carry only structural metadata
298
+ * (model, provider, tool name, token usage) unless `recordInputs` /
299
+ * `recordOutputs` are set, in which case prompts, tool arguments and generated
300
+ * text are attached too.
301
+ *
302
+ * The tool span is driven by the agent LOOP, not by `ai`: Lunora exposes tools
303
+ * schema-only so the SDK never executes one (see `telemetry/tool-execution.ts`).
304
+ *
305
+ * **A model-call span closes when the CALL ends, not when `execute()` resolves.**
306
+ * On a streamed turn `execute()` resolves the instant `doStream` hands back the
307
+ * stream — before a token, before any usage. The span still OPENS around
308
+ * `execute()`, because that is what makes it the active span nested provider work
309
+ * parents to, but it is opened with `startSpanManual` and so is not finished
310
+ * there: `onLanguageModelCallEnd` ends it, once the response is normalized and
311
+ * the real duration and token usage are both known. `onAbort` / `onError` end the
312
+ * call they name as a failure, so a stream that dies or is barged in on reports
313
+ * one instead of a phantom success.
249
314
  *
250
315
  * The app owns Sentry initialization; pass the namespace in as `Sentry`.
251
316
  * @experimental
252
317
  */
253
318
  declare const sentryTelemetry: (options: SentryTelemetryOptions) => Telemetry;
254
- export { type BraintrustLike, type BraintrustSpan, type BraintrustTelemetryOptions, type CommonOptions, type ConsoleLogLevel, type ConsoleLogger, type ConsoleTelemetryOptions, type OtlpTelemetryOptions, type SentryLike, type SentryTelemetryOptions, braintrustTelemetry, combineTelemetry, consoleTelemetry, otlpTelemetry, sentryTelemetry };
319
+ export {
320
+ /**
321
+ * `@lunora/agent/telemetry` — observability integrations for `@lunora/agent`.
322
+ *
323
+ * Every export here produces an ai@7 `Telemetry` object suitable for the
324
+ * `integrations` array of `TelemetryOptions` (`defineAgent({ telemetry: {
325
+ * isEnabled: true, integrations: [...] } })`). `consoleTelemetry` is a
326
+ * zero-dependency structured console tracer; `combineTelemetry` fans the
327
+ * lifecycle out to several integrations and nests their execution wrappers;
328
+ * `sentryTelemetry` / `braintrustTelemetry` are dependency-injected bridges (the
329
+ * app passes its own Sentry namespace / Braintrust logger, so the heavy SDKs are
330
+ * never imported here); and `otlpTelemetry` ships `gen_ai.*` spans over
331
+ * OTLP-over-HTTP to any collector (the Lunora Cloud, or your own), so agent
332
+ * generations land in the same trace store as the rest of the app.
333
+ *
334
+ * All integrations are privacy-safe by default (`recordInputs` / `recordOutputs`
335
+ * both default `false`), so nothing sensitive is recorded without opt-in.
336
+ */
337
+ type BraintrustLike,
338
+ /**
339
+ * `@lunora/agent/telemetry` — observability integrations for `@lunora/agent`.
340
+ *
341
+ * Every export here produces an ai@7 `Telemetry` object suitable for the
342
+ * `integrations` array of `TelemetryOptions` (`defineAgent({ telemetry: {
343
+ * isEnabled: true, integrations: [...] } })`). `consoleTelemetry` is a
344
+ * zero-dependency structured console tracer; `combineTelemetry` fans the
345
+ * lifecycle out to several integrations and nests their execution wrappers;
346
+ * `sentryTelemetry` / `braintrustTelemetry` are dependency-injected bridges (the
347
+ * app passes its own Sentry namespace / Braintrust logger, so the heavy SDKs are
348
+ * never imported here); and `otlpTelemetry` ships `gen_ai.*` spans over
349
+ * OTLP-over-HTTP to any collector (the Lunora Cloud, or your own), so agent
350
+ * generations land in the same trace store as the rest of the app.
351
+ *
352
+ * All integrations are privacy-safe by default (`recordInputs` / `recordOutputs`
353
+ * both default `false`), so nothing sensitive is recorded without opt-in.
354
+ */
355
+ type BraintrustSpan,
356
+ /**
357
+ * `@lunora/agent/telemetry` — observability integrations for `@lunora/agent`.
358
+ *
359
+ * Every export here produces an ai@7 `Telemetry` object suitable for the
360
+ * `integrations` array of `TelemetryOptions` (`defineAgent({ telemetry: {
361
+ * isEnabled: true, integrations: [...] } })`). `consoleTelemetry` is a
362
+ * zero-dependency structured console tracer; `combineTelemetry` fans the
363
+ * lifecycle out to several integrations and nests their execution wrappers;
364
+ * `sentryTelemetry` / `braintrustTelemetry` are dependency-injected bridges (the
365
+ * app passes its own Sentry namespace / Braintrust logger, so the heavy SDKs are
366
+ * never imported here); and `otlpTelemetry` ships `gen_ai.*` spans over
367
+ * OTLP-over-HTTP to any collector (the Lunora Cloud, or your own), so agent
368
+ * generations land in the same trace store as the rest of the app.
369
+ *
370
+ * All integrations are privacy-safe by default (`recordInputs` / `recordOutputs`
371
+ * both default `false`), so nothing sensitive is recorded without opt-in.
372
+ */
373
+ type BraintrustTelemetryOptions, type CommonOptions, type ConsoleLogLevel, type ConsoleLogger, type ConsoleTelemetryOptions, type OtlpTelemetryOptions, type SentryLike, type SentrySpan, type SentryTelemetryOptions, braintrustTelemetry, combineTelemetry, consoleTelemetry, otlpTelemetry, sentryTelemetry };