@lunora/agent 1.0.0-alpha.1 → 1.0.0-alpha.100

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 (92) hide show
  1. package/README.md +2 -0
  2. package/dist/channels.d.mts +33 -21
  3. package/dist/channels.d.ts +33 -21
  4. package/dist/channels.mjs +1 -181
  5. package/dist/component.d.mts +61 -56
  6. package/dist/component.d.ts +61 -56
  7. package/dist/component.mjs +2 -407
  8. package/dist/inbound.d.mts +23 -16
  9. package/dist/inbound.d.ts +23 -16
  10. package/dist/inbound.mjs +1 -32
  11. package/dist/index.d.mts +542 -409
  12. package/dist/index.d.ts +542 -409
  13. package/dist/index.mjs +1 -18
  14. package/dist/naming.d.mts +23 -10
  15. package/dist/naming.d.ts +23 -10
  16. package/dist/naming.mjs +1 -8
  17. package/dist/packem_shared/AGENT_MODULE-M4D1EejI.mjs +1 -0
  18. package/dist/packem_shared/VoiceSessionDO-D238XrlM.mjs +1 -0
  19. package/dist/packem_shared/adaptMcpResult-CVlq-_TO.mjs +2 -0
  20. package/dist/packem_shared/agent-loop-CE_Q6bk9.mjs +3 -0
  21. package/dist/packem_shared/agentAsTool-CgZb6ycK.mjs +1 -0
  22. package/dist/packem_shared/base64-5eyBfWO3.mjs +1 -0
  23. package/dist/packem_shared/braintrustTelemetry-Byne8uPK.mjs +1 -0
  24. package/dist/packem_shared/branch-marker-boZ00zmk.mjs +1 -0
  25. package/dist/packem_shared/buildModelMessages-Y4tGo1T9.mjs +5 -0
  26. package/dist/packem_shared/codeTool-BQb-K3my.mjs +1 -0
  27. package/dist/packem_shared/collectAgenticMemoryTools-BeMWT2qt.mjs +1 -0
  28. package/dist/packem_shared/combineTelemetry-DgE9W9G8.mjs +1 -0
  29. package/dist/packem_shared/common-CJSjtsfv.mjs +1 -0
  30. package/dist/packem_shared/compileAgentWorkflow-DVHi7NYD.mjs +1 -0
  31. package/dist/packem_shared/component-shared-G8ngerkU.mjs +1 -0
  32. package/dist/packem_shared/consoleTelemetry-BZY3Y-Ly.mjs +1 -0
  33. package/dist/packem_shared/createAgentContext-BvU9LP43.mjs +1 -0
  34. package/dist/packem_shared/createAgentGenerate-BOILDUZj.mjs +3 -0
  35. package/dist/packem_shared/createDispatchRunner-BxtGxAQi-DXXP7SxZ.mjs +1 -0
  36. package/dist/packem_shared/defineAgent-KgH-gwYg.mjs +3 -0
  37. package/dist/packem_shared/defineSkill-DGGvDWNq.mjs +1 -0
  38. package/dist/packem_shared/fnv1a-BNN96GYb.mjs +1 -0
  39. package/dist/packem_shared/functionTool-D3CP4f8-.mjs +1 -0
  40. package/dist/packem_shared/in-flight-calls-GH1A_1P3.mjs +1 -0
  41. package/dist/packem_shared/normalizeEntityName-k3fjicAG.mjs +2 -0
  42. package/dist/packem_shared/otlpTelemetry-ByqrB-72.mjs +1 -0
  43. package/dist/packem_shared/positive-integer-ztHqqpBk.mjs +1 -0
  44. package/dist/packem_shared/runAgentLoop-Drn1NcCc.mjs +1 -0
  45. package/dist/packem_shared/runVoiceTurn-BtcmyaBo.mjs +1 -0
  46. package/dist/packem_shared/sandboxComponent-BtOkUYpY.mjs +5 -0
  47. package/dist/packem_shared/sentryTelemetry-BHeydiAB.mjs +1 -0
  48. package/dist/packem_shared/tool-output-Cmk3JWMG.mjs +1 -0
  49. package/dist/packem_shared/types.d-B8WZl1rU.d.mts +1347 -0
  50. package/dist/packem_shared/types.d-B8WZl1rU.d.ts +1347 -0
  51. package/dist/packem_shared/voice-turn-BRj_it54.mjs +1 -0
  52. package/dist/reply.d.mts +40 -0
  53. package/dist/reply.d.ts +40 -0
  54. package/dist/reply.mjs +1 -0
  55. package/dist/sandbox.d.mts +130 -110
  56. package/dist/sandbox.d.ts +130 -110
  57. package/dist/sandbox.mjs +1 -109
  58. package/dist/skill-markdown.d.mts +36 -0
  59. package/dist/skill-markdown.d.ts +36 -0
  60. package/dist/skill-markdown.mjs +1 -0
  61. package/dist/telemetry/index.d.mts +319 -96
  62. package/dist/telemetry/index.d.ts +319 -96
  63. package/dist/telemetry/index.mjs +1 -4
  64. package/package.json +18 -9
  65. package/dist/packem_shared/AGENT_MODULE-Dnt_-AAT.mjs +0 -24
  66. package/dist/packem_shared/VoiceSessionDO-DLoXsHGF.mjs +0 -297
  67. package/dist/packem_shared/adaptMcpResult-wtNMvLoP.mjs +0 -65
  68. package/dist/packem_shared/agentAsTool-Dt8NlU6k.mjs +0 -94
  69. package/dist/packem_shared/base64-BVwtgRJV.mjs +0 -18
  70. package/dist/packem_shared/braintrustTelemetry-wuGDErob.mjs +0 -47
  71. package/dist/packem_shared/buildModelMessages-BWFigaoo.mjs +0 -69
  72. package/dist/packem_shared/codeTool-CjgJOC9t.mjs +0 -122
  73. package/dist/packem_shared/collectAgenticMemoryTools-QrzpV-WX.mjs +0 -97
  74. package/dist/packem_shared/combineTelemetry-DCyaaWAI.mjs +0 -43
  75. package/dist/packem_shared/common-DAeFCot5.mjs +0 -61
  76. package/dist/packem_shared/compileAgentWorkflow-BxJjHgtD.mjs +0 -55
  77. package/dist/packem_shared/consoleTelemetry-z2MiP1jt.mjs +0 -93
  78. package/dist/packem_shared/createAgentContext-4xJGXNR4.mjs +0 -50
  79. package/dist/packem_shared/createAgentGenerate-BQv9YJ01.mjs +0 -192
  80. package/dist/packem_shared/createDispatchRunner-DSbp_dph-ZHTtxy3f.mjs +0 -69
  81. package/dist/packem_shared/defineAgent-D6maSbVc.mjs +0 -148
  82. package/dist/packem_shared/defineSkill-Ctf_S-rz.mjs +0 -22
  83. package/dist/packem_shared/functionTool-D6lCa2jB.mjs +0 -20
  84. package/dist/packem_shared/graph-component-aoUwO-f0.mjs +0 -216
  85. package/dist/packem_shared/memory-D4FPcBsX.mjs +0 -12
  86. package/dist/packem_shared/normalizeEntityName-CyEEWFkR.mjs +0 -3
  87. package/dist/packem_shared/runAgentLoop-Dhg4ZNvw.mjs +0 -493
  88. package/dist/packem_shared/runVoiceTurn-LnqLvCRR.mjs +0 -211
  89. package/dist/packem_shared/sandboxComponent-DR3pTwBL.mjs +0 -194
  90. package/dist/packem_shared/sentryTelemetry-CgqFJyLO.mjs +0 -36
  91. package/dist/packem_shared/types.d-BWG0uUtX.d.mts +0 -1015
  92. package/dist/packem_shared/types.d-BWG0uUtX.d.ts +0 -1015
package/dist/sandbox.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { a as AgentToolContext, b as AgentToolDefinition } from "./packem_shared/types.d-BWG0uUtX.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
  /**
5
- * The model-provided input to a {@link browserTool} call — a discriminated
6
- * union on `op` so one tool exposes every headless-browser capability.
7
- */
5
+ * The model-provided input to a {@link browserTool} call — a discriminated
6
+ * union on `op` so one tool exposes every headless-browser capability.
7
+ * @experimental
8
+ */
8
9
  type BrowserToolInput = {
9
10
  fullPage?: boolean;
10
11
  op: "screenshot";
@@ -22,11 +23,12 @@ type BrowserToolInput = {
22
23
  url: string;
23
24
  };
24
25
  /**
25
- * The model-provided input to a {@link containerTool} call — a discriminated
26
- * union on `op`. `fetch` sends an HTTP request to the container; `exec` asks it
27
- * to run a command (routed as a POST to `/exec`, since the container surface
28
- * exposes no first-class exec RPC — the container app must serve that route).
29
- */
26
+ * The model-provided input to a {@link containerTool} call — a discriminated
27
+ * union on `op`. `fetch` sends an HTTP request to the container; `exec` asks it
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
+ * @experimental
31
+ */
30
32
  type ContainerToolInput = {
31
33
  args?: string[];
32
34
  command?: string;
@@ -37,25 +39,29 @@ type ContainerToolInput = {
37
39
  op: "fetch";
38
40
  path: string;
39
41
  };
40
- /** Author-supplied config for `browserTool`. */
42
+ /**
43
+ * Author-supplied config for `browserTool`.
44
+ * @experimental
45
+ */
41
46
  interface BrowserToolOptions {
42
47
  /** Override the model-facing description (what the tool does). */
43
48
  description?: string;
44
49
  /**
45
- * Gate a call behind a human approval. Defaults to **unattended** (no gate),
46
- * matching the AI SDK. `browserTool` fetches a model-chosen `url` with no
47
- * allowlist, so it is an SSRF surface: a prompt-injected model can point it at
48
- * an internal/link-local address and read the response back into context. Pass
49
- * a boolean or a predicate to gate it (evaluated from replay-stable input, so
50
- * keep it deterministic).
51
- */
50
+ * Gate a call behind a human approval. Defaults to **unattended** (no gate),
51
+ * matching the AI SDK. `browserTool` fetches a model-chosen `url` with no
52
+ * allowlist, so it is an SSRF surface: a prompt-injected model can point it at
53
+ * an internal/link-local address and read the response back into context. Pass
54
+ * a boolean or a predicate to gate it (evaluated from replay-stable input, so
55
+ * keep it deterministic).
56
+ */
52
57
  needsApproval?: ((input: BrowserToolInput) => boolean) | boolean;
53
58
  }
54
59
  /**
55
- * The model-provided input to a {@link fsTool} call — a discriminated union on
56
- * `op` over an R2-backed virtual filesystem. Paths are relative to the tool's
57
- * pinned `root`; a `..` that escapes the root is rejected server-side.
58
- */
60
+ * The model-provided input to a {@link fsTool} call — a discriminated union on
61
+ * `op` over an R2-backed virtual filesystem. Paths are relative to the tool's
62
+ * pinned `root`; a `..` that escapes the root is rejected server-side.
63
+ * @experimental
64
+ */
59
65
  type FsToolInput = {
60
66
  op: "ls";
61
67
  path?: string;
@@ -73,113 +79,127 @@ type FsToolInput = {
73
79
  op: "write";
74
80
  path: string;
75
81
  };
76
- /** Author-supplied config for `fsTool`. */
82
+ /**
83
+ * Author-supplied config for `fsTool`.
84
+ * @experimental
85
+ */
77
86
  interface FsToolOptions {
78
87
  /** Override the model-facing description (what the tool does). */
79
88
  description?: string;
80
89
  /**
81
- * Gate a call behind a human approval. Defaults to gating the WRITING ops
82
- * (`write`, `rm`) — a prompt-injected model shouldn't silently overwrite or
83
- * delete the sandbox — while `ls`/`read`/`stat` run unattended. Pass a boolean
84
- * or a predicate (evaluated from replay-stable input, so keep it deterministic).
85
- */
90
+ * Gate a call behind a human approval. Defaults to gating the WRITING ops
91
+ * (`write`, `rm`) — a prompt-injected model shouldn't silently overwrite or
92
+ * delete the sandbox — while `ls`/`read`/`stat` run unattended. Pass a boolean
93
+ * or a predicate (evaluated from replay-stable input, so keep it deterministic).
94
+ */
86
95
  needsApproval?: ((input: FsToolInput) => boolean) | boolean;
87
96
  /**
88
- * The key prefix every path is scoped under, isolating this tool's files from
89
- * the rest of the bucket. Either a fixed string (e.g. `"agents/support"`) or a
90
- * function of the tool context — return `` `agents/${ctx.threadKey}` `` (or an
91
- * owner-derived prefix) to give each run/user its OWN namespace in a
92
- * multi-tenant agent. Default: the bucket root (shared — set a root for
93
- * multi-tenant use). The server rejects any `..` that would escape it.
94
- */
97
+ * The key prefix every path is scoped under, isolating this tool's files from
98
+ * the rest of the bucket. Either a fixed string (e.g. `"agents/support"`) or a
99
+ * function of the tool context — return `` `agents/${ctx.threadKey}` `` (or an
100
+ * owner-derived prefix) to give each run/user its OWN namespace in a
101
+ * multi-tenant agent. Default: the bucket root (shared — set a root for
102
+ * multi-tenant use). The server rejects any `..` that would escape it.
103
+ */
95
104
  root?: ((context: AgentToolContext) => string) | string;
96
105
  }
97
- /** Author-supplied config for `containerTool`. */
106
+ /**
107
+ * Author-supplied config for `containerTool`.
108
+ * @experimental
109
+ */
98
110
  interface ContainerToolOptions {
99
111
  /** Override the model-facing description (what the tool does). */
100
112
  description?: string;
101
113
  /**
102
- * Gate a call behind a human approval. Defaults to gating any command
103
- * execution: an `exec`, AND a `fetch` whose path resolves to the privileged
104
- * `/exec` route (both reach the same command-execution path in the container,
105
- * so gating on the `op` name alone would let a `fetch` to `/exec` run a
106
- * command unattended). A plain `fetch` to any other route runs unattended.
107
- * Pass a boolean or your own predicate to change that. Evaluated from
108
- * replay-stable input, so keep it deterministic.
109
- */
114
+ * Gate a call behind a human approval. Defaults to gating any command
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.
122
+ */
110
123
  needsApproval?: ((input: ContainerToolInput) => boolean) | boolean;
111
124
  }
112
125
  /**
113
- * A batteries-included agent tool that drives Cloudflare Browser Rendering. One
114
- * tool exposes every browser op (screenshot / pdf / content / scrape); the model
115
- * picks via `op`. The call dispatches to the auto-registered `sandbox:invoke`
116
- * action — which runs on an action ctx carrying `ctx.browser`. Importing the tool
117
- * makes codegen register the dispatcher and provision the `BROWSER` binding; like
118
- * any `ctx.browser` user, the app still supplies a `config.browser` thunk
119
- * (`createBrowser({ binding: env.BROWSER, launch })` with the optional
120
- * `@cloudflare/playwright` `launch` peer) to `createShardDO()` — codegen never
121
- * injects that peer, so the browser op throws a directed error until it is wired.
122
- *
123
- * Security: the model chooses the `url` with no allowlist, so this is an SSRF
124
- * surface — pass `opts.needsApproval` to gate calls a prompt-injected model
125
- * could aim at internal/link-local endpoints.
126
- *
127
- * ```ts
128
- * import { browserTool, defineAgent } from "@lunora/agent/sandbox";
129
- *
130
- * export const researcher = defineAgent({
131
- * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
132
- * tools: { browser: browserTool() },
133
- * });
134
- * ```
135
- */
126
+ * A batteries-included agent tool that drives Cloudflare Browser Rendering. One
127
+ * tool exposes every browser op (screenshot / pdf / content / scrape); the model
128
+ * picks via `op`. The call dispatches to the auto-registered `sandbox:invoke`
129
+ * action — which runs on an action ctx carrying `ctx.browser`. Importing the tool
130
+ * makes codegen register the dispatcher and provision the `BROWSER` binding; like
131
+ * any `ctx.browser` user, the app still supplies a `config.browser` thunk
132
+ * (`createBrowser({ binding: env.BROWSER, launch })` with the optional
133
+ * `@cloudflare/playwright` `launch` peer) to `createShardDO()` — codegen never
134
+ * injects that peer, so the browser op throws a directed error until it is wired.
135
+ *
136
+ * Security: the model chooses the `url` with no allowlist, so this is an SSRF
137
+ * surface — pass `opts.needsApproval` to gate calls a prompt-injected model
138
+ * could aim at internal/link-local endpoints.
139
+ *
140
+ * ```ts
141
+ * import { browserTool, defineAgent } from "@lunora/agent/sandbox";
142
+ *
143
+ * export const researcher = defineAgent({
144
+ * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
145
+ * tools: { browser: browserTool() },
146
+ * });
147
+ * ```
148
+ * @experimental
149
+ */
136
150
  declare const browserTool: (options?: BrowserToolOptions) => AgentToolDefinition<BrowserToolInput, string>;
137
151
  /**
138
- * A batteries-included agent tool that talks to a declared Cloudflare
139
- * Container. `name` is the `ctx.containers.&lt;name>` key (the `lunora/containers.ts`
140
- * export). One tool exposes `fetch` (HTTP request) and `exec` (run a command);
141
- * the model picks via `op`. The call dispatches to the auto-registered
142
- * `sandbox:invoke` action, which carries `ctx.containers`.
143
- *
144
- * By default a `fetch` runs unattended while command execution is gated behind a
145
- * human approval — an `exec`, AND a `fetch` whose path resolves to the privileged
146
- * `/exec` route (both reach the same command-execution path in the container, so
147
- * gating on the `op` name alone would let a `fetch` to `/exec` run a command
148
- * unattended). Note a `fetch` can still reach any *other* container route
149
- * unattended, so scope the container's routes accordingly. Pass
150
- * `opts.needsApproval` to widen or disable the gate.
151
- *
152
- * ```ts
153
- * import { containerTool, defineAgent } from "@lunora/agent/sandbox";
154
- *
155
- * export const ops = defineAgent({
156
- * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
157
- * tools: { sandbox: containerTool("sandbox") },
158
- * });
159
- * ```
160
- */
152
+ * A batteries-included agent tool that talks to a declared Cloudflare
153
+ * Container. `name` is the `ctx.containers.<name>` key (the `lunora/containers.ts`
154
+ * export). One tool exposes `fetch` (HTTP request) and `exec` (run a command);
155
+ * the model picks via `op`. The call dispatches to the auto-registered
156
+ * `sandbox:invoke` action, which carries `ctx.containers`.
157
+ *
158
+ * By default a read-only (GET/HEAD/OPTIONS, or method-omitted) `fetch` runs
159
+ * unattended while everything else is gated behind a human approval — an
160
+ * `exec`; a `fetch` whose path resolves to the privileged exec route (both
161
+ * reach the same command-execution path in the container, so gating on the
162
+ * `op` name alone would let a `fetch` to it run a command unattended);
163
+ * and a `fetch` using a non-idempotent method (POST/PUT/PATCH/DELETE) to ANY
164
+ * route, since a prompt-injected model could otherwise mutate container state
165
+ * through some other privileged route just by avoiding the literal exec
166
+ * path. A `fetch` can still reach any *other read* route unattended, so scope
167
+ * the container's GET routes accordingly. Pass `opts.needsApproval` to widen
168
+ * or disable the gate.
169
+ *
170
+ * ```ts
171
+ * import { containerTool, defineAgent } from "@lunora/agent/sandbox";
172
+ *
173
+ * export const ops = defineAgent({
174
+ * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
175
+ * tools: { sandbox: containerTool("sandbox") },
176
+ * });
177
+ * ```
178
+ * @experimental
179
+ */
161
180
  declare const containerTool: (name: string, options?: ContainerToolOptions) => AgentToolDefinition<ContainerToolInput, string>;
162
181
  /**
163
- * A batteries-included agent tool exposing a persistent, R2-backed virtual
164
- * filesystem. `bucket` is the R2 binding name; every path is scoped under the
165
- * pinned `opts.root` (a `..` that would escape it is rejected). One tool exposes
166
- * `ls`/`read`/`write`/`rm`/`stat`; the model picks via `op`. The call dispatches
167
- * to the auto-registered `sandbox:invoke` action, which reads the bucket from
168
- * `ctx.env`. workerd has no real shell — this is object-store-backed file I/O.
169
- *
170
- * By default the writing ops (`write`/`rm`) pause for a human approval while
171
- * reads run unattended; pass `opts.needsApproval` to change that. Importing the
172
- * tool registers the dispatcher; the app must declare the `r2_bucket` binding in
173
- * `wrangler.jsonc` (the fs op throws a directed error until it is wired).
174
- *
175
- * ```ts
176
- * import { defineAgent, fsTool } from "@lunora/agent/sandbox";
177
- *
178
- * export const coder = defineAgent({
179
- * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
180
- * tools: { fs: fsTool("SANDBOX_BUCKET", { root: "agents/coder" }) },
181
- * });
182
- * ```
183
- */
182
+ * A batteries-included agent tool exposing a persistent, R2-backed virtual
183
+ * filesystem. `bucket` is the R2 binding name; every path is scoped under the
184
+ * pinned `opts.root` (a `..` that would escape it is rejected). One tool exposes
185
+ * `ls`/`read`/`write`/`rm`/`stat`; the model picks via `op`. The call dispatches
186
+ * to the auto-registered `sandbox:invoke` action, which reads the bucket from
187
+ * `ctx.env`. workerd has no real shell — this is object-store-backed file I/O.
188
+ *
189
+ * By default the writing ops (`write`/`rm`) pause for a human approval while
190
+ * reads run unattended; pass `opts.needsApproval` to change that. Importing the
191
+ * tool registers the dispatcher; the app must declare the `r2_bucket` binding in
192
+ * `wrangler.jsonc` (the fs op throws a directed error until it is wired).
193
+ *
194
+ * ```ts
195
+ * import { defineAgent, fsTool } from "@lunora/agent/sandbox";
196
+ *
197
+ * export const coder = defineAgent({
198
+ * model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
199
+ * tools: { fs: fsTool("SANDBOX_BUCKET", { root: "agents/coder" }) },
200
+ * });
201
+ * ```
202
+ * @experimental
203
+ */
184
204
  declare const fsTool: (bucket: string, options?: FsToolOptions) => AgentToolDefinition<FsToolInput>;
185
205
  export { type BrowserToolInput, type BrowserToolOptions, type ContainerToolInput, type ContainerToolOptions, type FsToolInput, type FsToolOptions, browserTool, containerTool, fsTool };
package/dist/sandbox.mjs CHANGED
@@ -1,109 +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 defaultContainerGate = (input) => {
48
- if (input.op === "exec") {
49
- return true;
50
- }
51
- return normalizeContainerPath(input.path) === CONTAINER_EXEC_ROUTE;
52
- };
53
- const CONTAINER_TOOL_SCHEMA = jsonSchema({
54
- properties: {
55
- args: { description: "exec: command arguments.", items: { type: "string" }, type: "array" },
56
- body: { description: "fetch: request body.", type: "string" },
57
- command: { description: "exec: the command to run.", type: "string" },
58
- method: { description: "fetch: HTTP method (default GET).", type: "string" },
59
- op: { description: "The container operation to run.", enum: ["fetch", "exec"], type: "string" },
60
- path: { description: "fetch: request path on the container (e.g. /health).", type: "string" }
61
- },
62
- required: ["op"],
63
- type: "object"
64
- });
65
- const browserTool = (options = {}) => {
66
- return {
67
- description: options.description ?? DEFAULT_BROWSER_DESCRIPTION,
68
- // Pin `kind` LAST so out-of-schema model input can never override it.
69
- execute: (input, context) => context.run(SANDBOX_REF, { ...input, kind: "browser" }),
70
- inputSchema: BROWSER_TOOL_SCHEMA,
71
- isLunoraAgentTool: true,
72
- ...options.needsApproval === void 0 ? {} : { needsApproval: options.needsApproval }
73
- };
74
- };
75
- const containerTool = (name, options = {}) => {
76
- if (typeof name !== "string" || name.length === 0) {
77
- throw new LunoraError("INTERNAL", "@lunora/agent: containerTool requires a container `name` (the ctx.containers.<name> key from lunora/containers.ts)");
78
- }
79
- const needsApproval = options.needsApproval ?? defaultContainerGate;
80
- return {
81
- description: options.description ?? DEFAULT_CONTAINER_DESCRIPTION,
82
- // Pin `kind`/`name` LAST so out-of-schema model input can never override
83
- // the authoritative container the author pinned.
84
- execute: (input, context) => context.run(SANDBOX_REF, { ...input, kind: "container", name }),
85
- inputSchema: CONTAINER_TOOL_SCHEMA,
86
- isLunoraAgentTool: true,
87
- needsApproval
88
- };
89
- };
90
- const fsTool = (bucket, options = {}) => {
91
- if (typeof bucket !== "string" || bucket.length === 0) {
92
- throw new LunoraError("INTERNAL", "@lunora/agent: fsTool requires an R2 `bucket` binding name (the r2_bucket declared in wrangler.jsonc)");
93
- }
94
- return {
95
- description: options.description ?? DEFAULT_FS_DESCRIPTION,
96
- // Pin kind/bucket/root LAST so out-of-schema model input can never override
97
- // the authoritative bucket + sandbox root the author pinned. `root` may be
98
- // a function of the ctx (per-run/owner isolation), resolved here.
99
- execute: (input, context) => {
100
- const root = typeof options.root === "function" ? options.root(context) : options.root ?? "";
101
- return context.run(SANDBOX_REF, { ...input, bucket, kind: "fs", root });
102
- },
103
- inputSchema: FS_TOOL_SCHEMA,
104
- isLunoraAgentTool: true,
105
- needsApproval: options.needsApproval ?? defaultFsGate
106
- };
107
- };
108
-
109
- 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};