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

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 (39) hide show
  1. package/README.md +2 -0
  2. package/dist/channels.d.mts +42 -22
  3. package/dist/channels.d.ts +42 -22
  4. package/dist/channels.mjs +1 -1
  5. package/dist/component.d.mts +59 -56
  6. package/dist/component.d.ts +59 -56
  7. package/dist/component.mjs +20 -9
  8. package/dist/inbound.d.mts +21 -16
  9. package/dist/inbound.d.ts +21 -16
  10. package/dist/index.d.mts +500 -408
  11. package/dist/index.d.ts +500 -408
  12. package/dist/index.mjs +6 -6
  13. package/dist/naming.d.mts +23 -10
  14. package/dist/naming.d.ts +23 -10
  15. package/dist/packem_shared/{VoiceSessionDO-DLoXsHGF.mjs → VoiceSessionDO-BdwlLaXC.mjs} +2 -2
  16. package/dist/packem_shared/{agentAsTool-Dt8NlU6k.mjs → agentAsTool-CUHlWsmt.mjs} +5 -1
  17. package/dist/packem_shared/{braintrustTelemetry-wuGDErob.mjs → braintrustTelemetry-TP7Kwuuj.mjs} +1 -1
  18. package/dist/packem_shared/{common-DAeFCot5.mjs → common-DQXayow6.mjs} +29 -1
  19. package/dist/packem_shared/compileAgentWorkflow-DYFFyx7i.mjs +78 -0
  20. package/dist/packem_shared/{consoleTelemetry-z2MiP1jt.mjs → consoleTelemetry--3sWfu1R.mjs} +1 -1
  21. package/dist/packem_shared/{createAgentGenerate-BQv9YJ01.mjs → createAgentGenerate-DO7Z96zX.mjs} +1 -1
  22. package/dist/packem_shared/{defineAgent-D6maSbVc.mjs → defineAgent-DAwAZC9P.mjs} +1 -1
  23. package/dist/packem_shared/{graph-component-aoUwO-f0.mjs → graph-component-Bbaxxymp.mjs} +3 -2
  24. package/dist/packem_shared/{normalizeEntityName-CyEEWFkR.mjs → normalizeEntityName-BouctxLC.mjs} +1 -1
  25. package/dist/packem_shared/otlpTelemetry-DU5ZmoEV.mjs +177 -0
  26. package/dist/packem_shared/{runAgentLoop-Dhg4ZNvw.mjs → runAgentLoop-M8PKbtWT.mjs} +2 -2
  27. package/dist/packem_shared/{sentryTelemetry-CgqFJyLO.mjs → sentryTelemetry-A4F5ndh9.mjs} +1 -1
  28. package/dist/packem_shared/types.d-boAM2Yi1.d.mts +1114 -0
  29. package/dist/packem_shared/types.d-boAM2Yi1.d.ts +1114 -0
  30. package/dist/sandbox.d.mts +129 -110
  31. package/dist/sandbox.d.ts +129 -110
  32. package/dist/sandbox.mjs +5 -1
  33. package/dist/telemetry/index.d.mts +190 -86
  34. package/dist/telemetry/index.d.ts +190 -86
  35. package/dist/telemetry/index.mjs +4 -3
  36. package/package.json +8 -8
  37. package/dist/packem_shared/compileAgentWorkflow-BxJjHgtD.mjs +0 -55
  38. package/dist/packem_shared/types.d-BWG0uUtX.d.mts +0 -1015
  39. package/dist/packem_shared/types.d-BWG0uUtX.d.ts +0 -1015
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @lunora/agent
2
2
 
3
+ > **Experimental** — this package is outside the Lunora 1.0 stability promise: its API may change in any release, without a major version bump.
4
+
3
5
  Durable AI agents for [Lunora](https://lunora.sh): `defineAgent` compiles a replay-safe tool-loop onto Cloudflare Workflows — each LLM turn and each tool call is a named durable step, thread messages persist idempotently in DO SQLite, and clients watch the conversation live over Lunora's reactive subscriptions.
4
6
 
5
7
  ```ts
@@ -1,12 +1,13 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-BWG0uUtX.mjs";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-BWG0uUtX.mjs";
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";
3
3
  import '@lunora/mail/inbound';
4
4
  import 'ai';
5
5
  /**
6
- * Verify a Slack request signature (`x-slack-signature` = `v0=` + HMAC over
7
- * `v0:timestamp:body`), rejecting a stale timestamp to bound replay. `now` is
8
- * injectable for deterministic tests (defaults to the wall clock).
9
- */
6
+ * Verify a Slack request signature (`x-slack-signature` = `v0=` + HMAC over
7
+ * `v0:timestamp:body`), rejecting a stale timestamp to bound replay. `now` is
8
+ * injectable for deterministic tests (defaults to the wall clock).
9
+ * @experimental
10
+ */
10
11
  declare const verifySlack: (options: {
11
12
  body: string;
12
13
  now?: number;
@@ -15,40 +16,59 @@ declare const verifySlack: (options: {
15
16
  timestamp: string | undefined;
16
17
  tolerance?: number;
17
18
  }) => Promise<boolean>;
18
- /** Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body). */
19
+ /**
20
+ * Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body).
21
+ * @experimental
22
+ */
19
23
  declare const verifyGithub: (options: {
20
24
  body: string;
21
25
  secret: string;
22
26
  signature: string | undefined;
23
27
  }) => Promise<boolean>;
24
28
  /**
25
- * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
26
- * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
27
- */
29
+ * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
30
+ * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
31
+ * @experimental
32
+ */
28
33
  declare const verifyDiscord: (options: {
29
34
  body: string;
30
35
  publicKey: string;
31
36
  signature: string | undefined;
32
37
  timestamp: string | undefined;
33
38
  }) => Promise<boolean>;
34
- /** One agent wired into the inbound channel HTTP handler. */
39
+ /**
40
+ * One agent wired into the inbound channel HTTP handler.
41
+ * @experimental
42
+ */
35
43
  interface AgentChannelTarget {
36
44
  /** The agent definition — only its `onInbound` config is read. */
37
45
  agent: Pick<AgentDefinition, "onInbound">;
38
46
  /** The `AGENT_*` Workflow binding name (off `env`) that starts a run. */
39
47
  binding: string;
40
48
  }
41
- /** The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route. */
49
+ /**
50
+ * The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route.
51
+ * @experimental
52
+ */
42
53
  type InboundChannelHandler = (request: Request, env: Record<string, unknown>) => Promise<Response>;
43
54
  /**
44
- * Build an HTTP handler that starts a durable agent run from a verified inbound
45
- * webhook. The channel is detected from the signature headers; EACH eligible
46
- * target is verified against ITS OWN secret before its mapper is offered the
47
- * event (so one app's valid signature can't trigger another app's agent), and
48
- * the first target that both verifies and claims starts a run on its Workflow
49
- * binding. Returns `401` when NO target's signature verifies, `200` on a claim
50
- * (or a Discord PONG), `204` when a verified event is declined by every mapper,
51
- * and `400` for an unrecognized webhook.
52
- */
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
+ /**
63
+ * Build an HTTP handler that starts a durable agent run from a verified inbound
64
+ * webhook. The channel is detected from the signature headers; EACH eligible
65
+ * target is verified against ITS OWN secret before its mapper is offered the
66
+ * event (so one app's valid signature can't trigger another app's agent), and
67
+ * the first target that both verifies and claims starts a run on its Workflow
68
+ * binding. Returns `401` when NO target's signature verifies, `200` on a claim
69
+ * (or a Discord PONG), `204` when a verified event is declined by every mapper,
70
+ * and `400` for an unrecognized webhook.
71
+ * @experimental
72
+ */
53
73
  declare const dispatchAgentChannel: (targets: ReadonlyArray<AgentChannelTarget>) => InboundChannelHandler;
54
- export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
74
+ export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, isDuplicateInstanceError, verifyDiscord, verifyGithub, verifySlack };
@@ -1,12 +1,13 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-BWG0uUtX.js";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-BWG0uUtX.js";
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";
3
3
  import '@lunora/mail/inbound';
4
4
  import 'ai';
5
5
  /**
6
- * Verify a Slack request signature (`x-slack-signature` = `v0=` + HMAC over
7
- * `v0:timestamp:body`), rejecting a stale timestamp to bound replay. `now` is
8
- * injectable for deterministic tests (defaults to the wall clock).
9
- */
6
+ * Verify a Slack request signature (`x-slack-signature` = `v0=` + HMAC over
7
+ * `v0:timestamp:body`), rejecting a stale timestamp to bound replay. `now` is
8
+ * injectable for deterministic tests (defaults to the wall clock).
9
+ * @experimental
10
+ */
10
11
  declare const verifySlack: (options: {
11
12
  body: string;
12
13
  now?: number;
@@ -15,40 +16,59 @@ declare const verifySlack: (options: {
15
16
  timestamp: string | undefined;
16
17
  tolerance?: number;
17
18
  }) => Promise<boolean>;
18
- /** Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body). */
19
+ /**
20
+ * Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body).
21
+ * @experimental
22
+ */
19
23
  declare const verifyGithub: (options: {
20
24
  body: string;
21
25
  secret: string;
22
26
  signature: string | undefined;
23
27
  }) => Promise<boolean>;
24
28
  /**
25
- * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
26
- * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
27
- */
29
+ * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
30
+ * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
31
+ * @experimental
32
+ */
28
33
  declare const verifyDiscord: (options: {
29
34
  body: string;
30
35
  publicKey: string;
31
36
  signature: string | undefined;
32
37
  timestamp: string | undefined;
33
38
  }) => Promise<boolean>;
34
- /** One agent wired into the inbound channel HTTP handler. */
39
+ /**
40
+ * One agent wired into the inbound channel HTTP handler.
41
+ * @experimental
42
+ */
35
43
  interface AgentChannelTarget {
36
44
  /** The agent definition — only its `onInbound` config is read. */
37
45
  agent: Pick<AgentDefinition, "onInbound">;
38
46
  /** The `AGENT_*` Workflow binding name (off `env`) that starts a run. */
39
47
  binding: string;
40
48
  }
41
- /** The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route. */
49
+ /**
50
+ * The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route.
51
+ * @experimental
52
+ */
42
53
  type InboundChannelHandler = (request: Request, env: Record<string, unknown>) => Promise<Response>;
43
54
  /**
44
- * Build an HTTP handler that starts a durable agent run from a verified inbound
45
- * webhook. The channel is detected from the signature headers; EACH eligible
46
- * target is verified against ITS OWN secret before its mapper is offered the
47
- * event (so one app's valid signature can't trigger another app's agent), and
48
- * the first target that both verifies and claims starts a run on its Workflow
49
- * binding. Returns `401` when NO target's signature verifies, `200` on a claim
50
- * (or a Discord PONG), `204` when a verified event is declined by every mapper,
51
- * and `400` for an unrecognized webhook.
52
- */
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
+ /**
63
+ * Build an HTTP handler that starts a durable agent run from a verified inbound
64
+ * webhook. The channel is detected from the signature headers; EACH eligible
65
+ * target is verified against ITS OWN secret before its mapper is offered the
66
+ * event (so one app's valid signature can't trigger another app's agent), and
67
+ * the first target that both verifies and claims starts a run on its Workflow
68
+ * binding. Returns `401` when NO target's signature verifies, `200` on a claim
69
+ * (or a Discord PONG), `204` when a verified event is declined by every mapper,
70
+ * and `400` for an unrecognized webhook.
71
+ * @experimental
72
+ */
53
73
  declare const dispatchAgentChannel: (targets: ReadonlyArray<AgentChannelTarget>) => InboundChannelHandler;
54
- export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
74
+ export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, isDuplicateInstanceError, verifyDiscord, verifyGithub, verifySlack };
package/dist/channels.mjs CHANGED
@@ -178,4 +178,4 @@ const dispatchAgentChannel = (targets) => async (request, env) => {
178
178
  return verifiedAny ? new Response(void 0, { status: 204 }) : new Response("Invalid signature", { status: 401 });
179
179
  };
180
180
 
181
- export { dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
181
+ export { dispatchAgentChannel, isDuplicateInstanceError, verifyDiscord, verifyGithub, verifySlack };
@@ -1,71 +1,73 @@
1
1
  import { SchemaExtension } from '@lunora/server';
2
2
  /**
3
- * Loose structural view of a registered Lunora function — wide enough for any
4
- * concrete `RegisteredMutation`/`RegisteredQuery` (whose precise validator-map
5
- * generics make them invariant), narrow enough for re-export, dispatch, and
6
- * tests. Codegen registers the runtime value; it never needs the generics.
7
- */
3
+ * Loose structural view of a registered Lunora function — wide enough for any
4
+ * concrete `RegisteredMutation`/`RegisteredQuery` (whose precise validator-map
5
+ * generics make them invariant), narrow enough for re-export, dispatch, and
6
+ * tests. Codegen registers the runtime value; it never needs the generics.
7
+ */
8
8
  interface AgentRegisteredFunction {
9
9
  readonly args: unknown;
10
10
  readonly handler: (context: unknown, args: never) => unknown;
11
11
  readonly kind: "mutation" | "query";
12
12
  readonly visibility?: "internal" | "public";
13
13
  }
14
- /** Stamp a registered function internal — server-side callable only. */
15
-
16
14
  /**
17
- * The per-owner dedup key for an entity name: trim, collapse internal
18
- * whitespace, lowercase. Deterministic (no locale) so a workflow replay or
19
- * retry writes the exact same key — the graph upsert stays idempotent.
20
- */
15
+ * The per-owner dedup key for an entity name: trim, collapse internal
16
+ * whitespace, lowercase. Deterministic (no locale) so a workflow replay or
17
+ * retry writes the exact same key — the graph upsert stays idempotent.
18
+ * @experimental
19
+ */
21
20
  declare const normalizeEntityName: (name: string) => string;
22
- /** The two internal graph-memory functions the durable loop dispatches to. */
23
-
24
21
  /**
25
- * Loose structural view of the registered sandbox action — wide enough for the
26
- * concrete `RegisteredAction`, narrow enough for re-export + dispatch. Codegen
27
- * registers the runtime value; it never needs the precise generics.
28
- */
22
+ * Loose structural view of the registered sandbox action — wide enough for the
23
+ * concrete `RegisteredAction`, narrow enough for re-export + dispatch. Codegen
24
+ * registers the runtime value; it never needs the precise generics.
25
+ * @experimental
26
+ */
29
27
  interface SandboxRegisteredFunction {
30
28
  readonly args: unknown;
31
29
  readonly handler: (context: unknown, args: never) => unknown;
32
30
  readonly kind: "action";
33
31
  readonly visibility?: "internal" | "public";
34
32
  }
33
+ /**
34
+ * `SandboxComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
35
+ * @experimental
36
+ */
35
37
  interface SandboxComponent {
36
38
  invoke: SandboxRegisteredFunction;
37
39
  }
38
40
  /**
39
- * Resolve a model-supplied `path` to an absolute R2 key UNDER `root`, rejecting
40
- * any `..` that would escape the sandbox root. Every returned key is guaranteed
41
- * to start with the normalized `root`, so the model can never read or write
42
- * outside its own prefix the fs sandbox's core isolation guarantee.
43
- */
44
- /**
45
- * Build the sandbox runtime component: the single internal action the
46
- * batteries-included `browserTool`/`containerTool` dispatch to. Codegen
47
- * auto-registers it at `sandbox:invoke` whenever `lunora/` imports a sandbox
48
- * tool. It runs as an **action** because that is the only ctx codegen attaches
49
- * `ctx.browser` to (and `ctx.containers` rides every ctx once a container is
50
- * declared) — the durable tool step itself has neither.
51
- */
41
+ * Build the sandbox runtime component: the single internal action the
42
+ * batteries-included `browserTool`/`containerTool` dispatch to. Codegen
43
+ * auto-registers it at `sandbox:invoke` whenever `lunora/` imports a sandbox
44
+ * tool. It runs as an **action** because that is the only ctx codegen attaches
45
+ * `ctx.browser` to (and `ctx.containers` rides every ctx once a container is
46
+ * declared) — the durable tool step itself has neither.
47
+ * @experimental
48
+ */
52
49
  declare const sandboxComponent: () => SandboxComponent;
53
50
  /**
54
- * The agent thread tables, shipped as a schema extension so an app merges
55
- * them with one call and they can never collide with app tables:
56
- *
57
- * ```ts
58
- * // lunora/schema.ts
59
- * export default defineSchema({ ... }).extend(agentExtension);
60
- * ```
61
- *
62
- * Message ordering follows the Convex-agent model: `seq` is the monotonic
63
- * per-thread position (allocated from the thread's `messageCount` counter, so
64
- * allocation is O(1) inside the serialized mutation), and `messageKey` is the
65
- * deterministic idempotency key — a workflow replay that re-persists the same
66
- * message is a no-op instead of a duplicate.
67
- */
51
+ * The agent thread tables, shipped as a schema extension so an app merges
52
+ * them with one call and they can never collide with app tables:
53
+ *
54
+ * ```ts
55
+ * // lunora/schema.ts
56
+ * export default defineSchema({ ... }).extend(agentExtension);
57
+ * ```
58
+ *
59
+ * Message ordering follows the Convex-agent model: `seq` is the monotonic
60
+ * per-thread position (allocated from the thread's `messageCount` counter, so
61
+ * allocation is O(1) inside the serialized mutation), and `messageKey` is the
62
+ * deterministic idempotency key — a workflow replay that re-persists the same
63
+ * message is a no-op instead of a duplicate.
64
+ * @experimental
65
+ */
68
66
  declare const agentExtension: SchemaExtension;
67
+ /**
68
+ * `AgentComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
69
+ * @experimental
70
+ */
69
71
  interface AgentComponent {
70
72
  extension: SchemaExtension;
71
73
  functions: {
@@ -85,17 +87,18 @@ interface AgentComponent {
85
87
  };
86
88
  }
87
89
  /**
88
- * Build the agent runtime component: the thread schema extension plus the
89
- * functions the durable loop dispatches to (and the client subscribes to).
90
- * Codegen auto-registers them under the `agents:*` namespace whenever
91
- * `lunora/agents.ts` declares an agent — the loop's dispatch paths assume
92
- * that namespace, and apps never re-export these by hand.
93
- *
94
- * Most mutations are **internal** (only the workflow's admin-authenticated
95
- * dispatch may call them); the queries are public so a client can subscribe
96
- * to `agents:agentMessages` for a live thread view. Two mutations are public:
97
- * `agentResolveApproval` (a client resolves a HITL approval) and `agentRun`
98
- * (an HTTP client starts a durable run) — both owner-gated.
99
- */
90
+ * Build the agent runtime component: the thread schema extension plus the
91
+ * functions the durable loop dispatches to (and the client subscribes to).
92
+ * Codegen auto-registers them under the `agents:*` namespace whenever
93
+ * `lunora/agents.ts` declares an agent — the loop's dispatch paths assume
94
+ * that namespace, and apps never re-export these by hand.
95
+ *
96
+ * Most mutations are **internal** (only the workflow's admin-authenticated
97
+ * dispatch may call them); the queries are public so a client can subscribe
98
+ * to `agents:agentMessages` for a live thread view. Two mutations are public:
99
+ * `agentResolveApproval` (a client resolves a HITL approval) and `agentRun`
100
+ * (an HTTP client starts a durable run) — both owner-gated.
101
+ * @experimental
102
+ */
100
103
  declare const agentComponent: () => AgentComponent;
101
104
  export { AgentComponent, type SandboxComponent, type SandboxRegisteredFunction, agentComponent, agentExtension, normalizeEntityName, sandboxComponent };
@@ -1,71 +1,73 @@
1
1
  import { SchemaExtension } from '@lunora/server';
2
2
  /**
3
- * Loose structural view of a registered Lunora function — wide enough for any
4
- * concrete `RegisteredMutation`/`RegisteredQuery` (whose precise validator-map
5
- * generics make them invariant), narrow enough for re-export, dispatch, and
6
- * tests. Codegen registers the runtime value; it never needs the generics.
7
- */
3
+ * Loose structural view of a registered Lunora function — wide enough for any
4
+ * concrete `RegisteredMutation`/`RegisteredQuery` (whose precise validator-map
5
+ * generics make them invariant), narrow enough for re-export, dispatch, and
6
+ * tests. Codegen registers the runtime value; it never needs the generics.
7
+ */
8
8
  interface AgentRegisteredFunction {
9
9
  readonly args: unknown;
10
10
  readonly handler: (context: unknown, args: never) => unknown;
11
11
  readonly kind: "mutation" | "query";
12
12
  readonly visibility?: "internal" | "public";
13
13
  }
14
- /** Stamp a registered function internal — server-side callable only. */
15
-
16
14
  /**
17
- * The per-owner dedup key for an entity name: trim, collapse internal
18
- * whitespace, lowercase. Deterministic (no locale) so a workflow replay or
19
- * retry writes the exact same key — the graph upsert stays idempotent.
20
- */
15
+ * The per-owner dedup key for an entity name: trim, collapse internal
16
+ * whitespace, lowercase. Deterministic (no locale) so a workflow replay or
17
+ * retry writes the exact same key — the graph upsert stays idempotent.
18
+ * @experimental
19
+ */
21
20
  declare const normalizeEntityName: (name: string) => string;
22
- /** The two internal graph-memory functions the durable loop dispatches to. */
23
-
24
21
  /**
25
- * Loose structural view of the registered sandbox action — wide enough for the
26
- * concrete `RegisteredAction`, narrow enough for re-export + dispatch. Codegen
27
- * registers the runtime value; it never needs the precise generics.
28
- */
22
+ * Loose structural view of the registered sandbox action — wide enough for the
23
+ * concrete `RegisteredAction`, narrow enough for re-export + dispatch. Codegen
24
+ * registers the runtime value; it never needs the precise generics.
25
+ * @experimental
26
+ */
29
27
  interface SandboxRegisteredFunction {
30
28
  readonly args: unknown;
31
29
  readonly handler: (context: unknown, args: never) => unknown;
32
30
  readonly kind: "action";
33
31
  readonly visibility?: "internal" | "public";
34
32
  }
33
+ /**
34
+ * `SandboxComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
35
+ * @experimental
36
+ */
35
37
  interface SandboxComponent {
36
38
  invoke: SandboxRegisteredFunction;
37
39
  }
38
40
  /**
39
- * Resolve a model-supplied `path` to an absolute R2 key UNDER `root`, rejecting
40
- * any `..` that would escape the sandbox root. Every returned key is guaranteed
41
- * to start with the normalized `root`, so the model can never read or write
42
- * outside its own prefix the fs sandbox's core isolation guarantee.
43
- */
44
- /**
45
- * Build the sandbox runtime component: the single internal action the
46
- * batteries-included `browserTool`/`containerTool` dispatch to. Codegen
47
- * auto-registers it at `sandbox:invoke` whenever `lunora/` imports a sandbox
48
- * tool. It runs as an **action** because that is the only ctx codegen attaches
49
- * `ctx.browser` to (and `ctx.containers` rides every ctx once a container is
50
- * declared) — the durable tool step itself has neither.
51
- */
41
+ * Build the sandbox runtime component: the single internal action the
42
+ * batteries-included `browserTool`/`containerTool` dispatch to. Codegen
43
+ * auto-registers it at `sandbox:invoke` whenever `lunora/` imports a sandbox
44
+ * tool. It runs as an **action** because that is the only ctx codegen attaches
45
+ * `ctx.browser` to (and `ctx.containers` rides every ctx once a container is
46
+ * declared) — the durable tool step itself has neither.
47
+ * @experimental
48
+ */
52
49
  declare const sandboxComponent: () => SandboxComponent;
53
50
  /**
54
- * The agent thread tables, shipped as a schema extension so an app merges
55
- * them with one call and they can never collide with app tables:
56
- *
57
- * ```ts
58
- * // lunora/schema.ts
59
- * export default defineSchema({ ... }).extend(agentExtension);
60
- * ```
61
- *
62
- * Message ordering follows the Convex-agent model: `seq` is the monotonic
63
- * per-thread position (allocated from the thread's `messageCount` counter, so
64
- * allocation is O(1) inside the serialized mutation), and `messageKey` is the
65
- * deterministic idempotency key — a workflow replay that re-persists the same
66
- * message is a no-op instead of a duplicate.
67
- */
51
+ * The agent thread tables, shipped as a schema extension so an app merges
52
+ * them with one call and they can never collide with app tables:
53
+ *
54
+ * ```ts
55
+ * // lunora/schema.ts
56
+ * export default defineSchema({ ... }).extend(agentExtension);
57
+ * ```
58
+ *
59
+ * Message ordering follows the Convex-agent model: `seq` is the monotonic
60
+ * per-thread position (allocated from the thread's `messageCount` counter, so
61
+ * allocation is O(1) inside the serialized mutation), and `messageKey` is the
62
+ * deterministic idempotency key — a workflow replay that re-persists the same
63
+ * message is a no-op instead of a duplicate.
64
+ * @experimental
65
+ */
68
66
  declare const agentExtension: SchemaExtension;
67
+ /**
68
+ * `AgentComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
69
+ * @experimental
70
+ */
69
71
  interface AgentComponent {
70
72
  extension: SchemaExtension;
71
73
  functions: {
@@ -85,17 +87,18 @@ interface AgentComponent {
85
87
  };
86
88
  }
87
89
  /**
88
- * Build the agent runtime component: the thread schema extension plus the
89
- * functions the durable loop dispatches to (and the client subscribes to).
90
- * Codegen auto-registers them under the `agents:*` namespace whenever
91
- * `lunora/agents.ts` declares an agent — the loop's dispatch paths assume
92
- * that namespace, and apps never re-export these by hand.
93
- *
94
- * Most mutations are **internal** (only the workflow's admin-authenticated
95
- * dispatch may call them); the queries are public so a client can subscribe
96
- * to `agents:agentMessages` for a live thread view. Two mutations are public:
97
- * `agentResolveApproval` (a client resolves a HITL approval) and `agentRun`
98
- * (an HTTP client starts a durable run) — both owner-gated.
99
- */
90
+ * Build the agent runtime component: the thread schema extension plus the
91
+ * functions the durable loop dispatches to (and the client subscribes to).
92
+ * Codegen auto-registers them under the `agents:*` namespace whenever
93
+ * `lunora/agents.ts` declares an agent — the loop's dispatch paths assume
94
+ * that namespace, and apps never re-export these by hand.
95
+ *
96
+ * Most mutations are **internal** (only the workflow's admin-authenticated
97
+ * dispatch may call them); the queries are public so a client can subscribe
98
+ * to `agents:agentMessages` for a live thread view. Two mutations are public:
99
+ * `agentResolveApproval` (a client resolves a HITL approval) and `agentRun`
100
+ * (an HTTP client starts a durable run) — both owner-gated.
101
+ * @experimental
102
+ */
100
103
  declare const agentComponent: () => AgentComponent;
101
104
  export { AgentComponent, type SandboxComponent, type SandboxRegisteredFunction, agentComponent, agentExtension, normalizeEntityName, sandboxComponent };
@@ -1,8 +1,8 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
2
  import { defineTable, initLunora, defineSchemaExtension } from '@lunora/server';
3
3
  import { v } from '@lunora/values';
4
- import { a as asInternal, A as AGENT_EXTENSION_KEY, g as graphTables, d as definedColumns, b as graphComponent } from './packem_shared/graph-component-aoUwO-f0.mjs';
5
- export { n as normalizeEntityName } from './packem_shared/graph-component-aoUwO-f0.mjs';
4
+ import { a as asInternal, A as AGENT_EXTENSION_KEY, g as graphTables, d as definedColumns, b as graphComponent } from './packem_shared/graph-component-Bbaxxymp.mjs';
5
+ export { n as normalizeEntityName } from './packem_shared/graph-component-Bbaxxymp.mjs';
6
6
  export { sandboxComponent } from './packem_shared/sandboxComponent-DR3pTwBL.mjs';
7
7
 
8
8
  const EPISODES_TABLE = "agent_episodes";
@@ -179,7 +179,7 @@ const agentComponent = () => {
179
179
  throw new Error(`@lunora/agent: thread "${args.key}" belongs to another owner`);
180
180
  }
181
181
  const priorInstanceId = existing["instanceId"];
182
- const isConcurrentRun = (existing["status"] === "running" || existing["status"] === "awaiting_input") && priorInstanceId !== void 0 && args.instanceId !== void 0 && priorInstanceId !== args.instanceId;
182
+ const isConcurrentRun = (existing["status"] === "running" || existing["status"] === "awaiting_input") && priorInstanceId !== void 0 && (args.instanceId === void 0 || args.instanceId !== priorInstanceId);
183
183
  if (isConcurrentRun) {
184
184
  const policy = args.onConcurrentRun ?? "reject";
185
185
  if (policy !== "replace") {
@@ -188,7 +188,12 @@ const agentComponent = () => {
188
188
  `@lunora/agent: thread "${args.key}" already has a run in flight (instance "${priorInstanceId}") — onConcurrentRun="${policy}"`
189
189
  );
190
190
  }
191
- await context.db.patch(existing["_id"], { error: void 0, instanceId: args.instanceId, status: "running", updatedAt: now });
191
+ await context.db.patch(existing["_id"], {
192
+ error: void 0,
193
+ status: "running",
194
+ updatedAt: now,
195
+ ...args.instanceId === void 0 ? {} : { instanceId: args.instanceId }
196
+ });
192
197
  return { created: false, priorInstanceId, replaced: true };
193
198
  }
194
199
  await context.db.patch(existing["_id"], {
@@ -310,9 +315,11 @@ const agentComponent = () => {
310
315
  if (readableThread(thread, context.auth) === void 0) {
311
316
  return [];
312
317
  }
313
- const rows = await context.db.query(MESSAGES_TABLE).withIndex("byThread", (q) => q.eq("threadKey", args.key)).collect();
314
- const ordered = rows.toSorted((a, b) => a["seq"] - b["seq"]);
315
- return args.limit === void 0 ? ordered : ordered.slice(Math.max(0, ordered.length - args.limit));
318
+ if (args.limit !== void 0) {
319
+ const tail = await context.db.query(MESSAGES_TABLE).withIndex("byThread", (q) => q.eq("threadKey", args.key)).order("desc").take(args.limit);
320
+ return tail.toReversed();
321
+ }
322
+ return context.db.query(MESSAGES_TABLE).withIndex("byThread", (q) => q.eq("threadKey", args.key)).collect();
316
323
  });
317
324
  const agentResolveApproval = mutation.input({
318
325
  decision: v.union(v.literal("approve"), v.literal("reject")),
@@ -326,6 +333,9 @@ const agentComponent = () => {
326
333
  if (readable === void 0) {
327
334
  throw new LunoraError("FORBIDDEN", `@lunora/agent: not allowed to resolve approvals on thread "${args.threadKey}"`);
328
335
  }
336
+ if (readable["instanceId"] !== args.instanceId) {
337
+ throw new LunoraError("FORBIDDEN", `@lunora/agent: instance "${args.instanceId}" does not own thread "${args.threadKey}"`);
338
+ }
329
339
  const agentName = readable["agent"];
330
340
  const { agents } = context;
331
341
  const handle = agents?.[agentName];
@@ -336,8 +346,9 @@ const agentComponent = () => {
336
346
  );
337
347
  }
338
348
  await handle.sendEvent(args.instanceId, {
339
- payload: { decision: args.decision, ...args.note === void 0 ? {} : { note: args.note } },
340
- type: "agent-approval"
349
+ payload: { decision: args.decision, toolCallId: args.toolCallId, ...args.note === void 0 ? {} : { note: args.note } },
350
+ // Scoped per tool call — see the doc comment above.
351
+ type: `agent-approval:${args.toolCallId}`
341
352
  });
342
353
  return { resolved: true };
343
354
  });
@@ -1,29 +1,34 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-BWG0uUtX.mjs";
1
+ import { A as AgentDefinition } from "./packem_shared/types.d-boAM2Yi1.mjs";
2
2
  import '@lunora/mail/inbound';
3
3
  import 'ai';
4
- /** One agent wired into the inbound `email()` handler. */
4
+ /**
5
+ * One agent wired into the inbound `email()` handler.
6
+ * @experimental
7
+ */
5
8
  interface AgentEmailTarget {
6
9
  /**
7
- * The agent definition — only its `onEmail` mapper is read, deciding whether
8
- * this agent claims the message and, if so, the run to start.
9
- */
10
+ * The agent definition — only its `onEmail` mapper is read, deciding whether
11
+ * this agent claims the message and, if so, the run to start.
12
+ */
10
13
  agent: Pick<AgentDefinition, "onEmail">;
11
14
  /** The `AGENT_*` Workflow binding name (off `env`) that starts a run. */
12
15
  binding: string;
13
16
  }
14
17
  /**
15
- * The worker `email(message, env, ctx)` callback. Typed with `unknown`
16
- * parameters so it drops straight onto the generated `composed.email` slot
17
- * without a cast.
18
- */
18
+ * The worker `email(message, env, ctx)` callback. Typed with `unknown`
19
+ * parameters so it drops straight onto the generated `composed.email` slot
20
+ * without a cast.
21
+ * @experimental
22
+ */
19
23
  type InboundAgentEmailHandler = (message: unknown, env: unknown, context: unknown) => Promise<void>;
20
24
  /**
21
- * Build the inbound `email()` handler for one or more `onEmail` agents. The
22
- * returned callback parses the message, then walks `targets` in order and starts
23
- * a durable run for the first agent whose `onEmail` mapper returns a run. If the
24
- * matched agent's Workflow binding is missing from `env` (run codegen/dev so
25
- * `wrangler.jsonc` declares it) the dispatch throws, and the inbound handler's
26
- * `onError` rejects (bounces) the message.
27
- */
25
+ * Build the inbound `email()` handler for one or more `onEmail` agents. The
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.
31
+ * @experimental
32
+ */
28
33
  declare const dispatchAgentEmail: (targets: ReadonlyArray<AgentEmailTarget>) => InboundAgentEmailHandler;
29
34
  export { type AgentEmailTarget, type InboundAgentEmailHandler, dispatchAgentEmail };