@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.
- package/README.md +2 -0
- package/dist/channels.d.mts +42 -22
- package/dist/channels.d.ts +42 -22
- package/dist/channels.mjs +1 -1
- package/dist/component.d.mts +59 -56
- package/dist/component.d.ts +59 -56
- package/dist/component.mjs +20 -9
- package/dist/inbound.d.mts +21 -16
- package/dist/inbound.d.ts +21 -16
- package/dist/index.d.mts +500 -408
- package/dist/index.d.ts +500 -408
- package/dist/index.mjs +6 -6
- package/dist/naming.d.mts +23 -10
- package/dist/naming.d.ts +23 -10
- package/dist/packem_shared/{VoiceSessionDO-DLoXsHGF.mjs → VoiceSessionDO-BdwlLaXC.mjs} +2 -2
- package/dist/packem_shared/{agentAsTool-Dt8NlU6k.mjs → agentAsTool-CUHlWsmt.mjs} +5 -1
- package/dist/packem_shared/{braintrustTelemetry-wuGDErob.mjs → braintrustTelemetry-TP7Kwuuj.mjs} +1 -1
- package/dist/packem_shared/{common-DAeFCot5.mjs → common-DQXayow6.mjs} +29 -1
- package/dist/packem_shared/compileAgentWorkflow-DYFFyx7i.mjs +78 -0
- package/dist/packem_shared/{consoleTelemetry-z2MiP1jt.mjs → consoleTelemetry--3sWfu1R.mjs} +1 -1
- package/dist/packem_shared/{createAgentGenerate-BQv9YJ01.mjs → createAgentGenerate-DO7Z96zX.mjs} +1 -1
- package/dist/packem_shared/{defineAgent-D6maSbVc.mjs → defineAgent-DAwAZC9P.mjs} +1 -1
- package/dist/packem_shared/{graph-component-aoUwO-f0.mjs → graph-component-Bbaxxymp.mjs} +3 -2
- package/dist/packem_shared/{normalizeEntityName-CyEEWFkR.mjs → normalizeEntityName-BouctxLC.mjs} +1 -1
- package/dist/packem_shared/otlpTelemetry-DU5ZmoEV.mjs +177 -0
- package/dist/packem_shared/{runAgentLoop-Dhg4ZNvw.mjs → runAgentLoop-M8PKbtWT.mjs} +2 -2
- package/dist/packem_shared/{sentryTelemetry-CgqFJyLO.mjs → sentryTelemetry-A4F5ndh9.mjs} +1 -1
- package/dist/packem_shared/types.d-boAM2Yi1.d.mts +1114 -0
- package/dist/packem_shared/types.d-boAM2Yi1.d.ts +1114 -0
- package/dist/sandbox.d.mts +129 -110
- package/dist/sandbox.d.ts +129 -110
- package/dist/sandbox.mjs +5 -1
- package/dist/telemetry/index.d.mts +190 -86
- package/dist/telemetry/index.d.ts +190 -86
- package/dist/telemetry/index.mjs +4 -3
- package/package.json +8 -8
- package/dist/packem_shared/compileAgentWorkflow-BxJjHgtD.mjs +0 -55
- package/dist/packem_shared/types.d-BWG0uUtX.d.mts +0 -1015
- 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
|
package/dist/channels.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { A as AgentDefinition } from "./packem_shared/types.d-
|
|
2
|
-
export type { I as InboundChannelEvent } from "./packem_shared/types.d-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { A as AgentDefinition } from "./packem_shared/types.d-
|
|
2
|
-
export type { I as InboundChannelEvent } from "./packem_shared/types.d-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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 };
|
package/dist/component.d.mts
CHANGED
|
@@ -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
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
|
|
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 };
|
package/dist/component.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
|
|
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 };
|
package/dist/component.mjs
CHANGED
|
@@ -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-
|
|
5
|
-
export { n as normalizeEntityName } from './packem_shared/graph-component-
|
|
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
|
|
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"], {
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
});
|
package/dist/inbound.d.mts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
|
-
import { A as AgentDefinition } from "./packem_shared/types.d-
|
|
1
|
+
import { A as AgentDefinition } from "./packem_shared/types.d-boAM2Yi1.mjs";
|
|
2
2
|
import '@lunora/mail/inbound';
|
|
3
3
|
import 'ai';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* One agent wired into the inbound `email()` handler.
|
|
6
|
+
* @experimental
|
|
7
|
+
*/
|
|
5
8
|
interface AgentEmailTarget {
|
|
6
9
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
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 };
|