@lunora/agent 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (79) hide show
  1. package/dist/channels.d.mts +39 -31
  2. package/dist/channels.d.ts +39 -31
  3. package/dist/channels.mjs +1 -181
  4. package/dist/component.d.mts +57 -68
  5. package/dist/component.d.ts +57 -68
  6. package/dist/component.mjs +2 -407
  7. package/dist/inbound.d.mts +20 -20
  8. package/dist/inbound.d.ts +20 -20
  9. package/dist/inbound.mjs +1 -32
  10. package/dist/index.d.mts +474 -490
  11. package/dist/index.d.ts +474 -490
  12. package/dist/index.mjs +1 -18
  13. package/dist/naming.d.mts +19 -19
  14. package/dist/naming.d.ts +19 -19
  15. package/dist/naming.mjs +1 -8
  16. package/dist/packem_shared/AGENT_MODULE-BXx-tSCe.mjs +1 -0
  17. package/dist/packem_shared/VoiceSessionDO-CTaBSdXk.mjs +1 -0
  18. package/dist/packem_shared/adaptMcpResult-18G7zpFo.mjs +2 -0
  19. package/dist/packem_shared/agentAsTool-CJCTyAQG.mjs +1 -0
  20. package/dist/packem_shared/base64-jU-Ci6Tu.mjs +1 -0
  21. package/dist/packem_shared/braintrustTelemetry-CnOI5WAw.mjs +1 -0
  22. package/dist/packem_shared/buildModelMessages-Dm2wVNQy.mjs +5 -0
  23. package/dist/packem_shared/codeTool-DYJVy-bH.mjs +1 -0
  24. package/dist/packem_shared/collectAgenticMemoryTools-DBcP8dUa.mjs +1 -0
  25. package/dist/packem_shared/combineTelemetry-oLCV_EPV.mjs +1 -0
  26. package/dist/packem_shared/common-Dck97z3Y.mjs +1 -0
  27. package/dist/packem_shared/compileAgentWorkflow-C4xqSw82.mjs +1 -0
  28. package/dist/packem_shared/consoleTelemetry-CaoI-NGg.mjs +1 -0
  29. package/dist/packem_shared/createAgentContext-B1IfI7ro.mjs +1 -0
  30. package/dist/packem_shared/createAgentGenerate-BUVImo85.mjs +3 -0
  31. package/dist/packem_shared/createDispatchRunner-CQ8HXscn-BrqT0bIG.mjs +1 -0
  32. package/dist/packem_shared/defineAgent-CKhzzmys.mjs +3 -0
  33. package/dist/packem_shared/defineSkill-DfJxCwfZ.mjs +1 -0
  34. package/dist/packem_shared/functionTool-CWHhkYqd.mjs +1 -0
  35. package/dist/packem_shared/graph-component-B2YmC2eL.mjs +2 -0
  36. package/dist/packem_shared/memory-DNRyyp1b.mjs +1 -0
  37. package/dist/packem_shared/normalizeEntityName-BQpyssvL.mjs +1 -0
  38. package/dist/packem_shared/otlpTelemetry-DcW9mOLV.mjs +1 -0
  39. package/dist/packem_shared/runAgentLoop-DNAt-pGz.mjs +3 -0
  40. package/dist/packem_shared/runVoiceTurn-PkMsfG_v.mjs +1 -0
  41. package/dist/packem_shared/sandboxComponent-BhaGHsQZ.mjs +1 -0
  42. package/dist/packem_shared/sentryTelemetry-Dr2aPuvl.mjs +1 -0
  43. package/dist/packem_shared/types.d-boAM2Yi1.d.mts +1114 -0
  44. package/dist/packem_shared/types.d-boAM2Yi1.d.ts +1114 -0
  45. package/dist/sandbox.d.mts +126 -122
  46. package/dist/sandbox.d.ts +126 -122
  47. package/dist/sandbox.mjs +1 -109
  48. package/dist/telemetry/index.d.mts +185 -104
  49. package/dist/telemetry/index.d.ts +185 -104
  50. package/dist/telemetry/index.mjs +1 -4
  51. package/package.json +8 -8
  52. package/dist/packem_shared/AGENT_MODULE-Dnt_-AAT.mjs +0 -24
  53. package/dist/packem_shared/VoiceSessionDO-DLoXsHGF.mjs +0 -297
  54. package/dist/packem_shared/adaptMcpResult-wtNMvLoP.mjs +0 -65
  55. package/dist/packem_shared/agentAsTool-Dt8NlU6k.mjs +0 -94
  56. package/dist/packem_shared/base64-BVwtgRJV.mjs +0 -18
  57. package/dist/packem_shared/braintrustTelemetry-wuGDErob.mjs +0 -47
  58. package/dist/packem_shared/buildModelMessages-BWFigaoo.mjs +0 -69
  59. package/dist/packem_shared/codeTool-CjgJOC9t.mjs +0 -122
  60. package/dist/packem_shared/collectAgenticMemoryTools-QrzpV-WX.mjs +0 -97
  61. package/dist/packem_shared/combineTelemetry-DCyaaWAI.mjs +0 -43
  62. package/dist/packem_shared/common-DAeFCot5.mjs +0 -61
  63. package/dist/packem_shared/compileAgentWorkflow-BxJjHgtD.mjs +0 -55
  64. package/dist/packem_shared/consoleTelemetry-z2MiP1jt.mjs +0 -93
  65. package/dist/packem_shared/createAgentContext-4xJGXNR4.mjs +0 -50
  66. package/dist/packem_shared/createAgentGenerate-BQv9YJ01.mjs +0 -192
  67. package/dist/packem_shared/createDispatchRunner-DSbp_dph-ZHTtxy3f.mjs +0 -69
  68. package/dist/packem_shared/defineAgent-D6maSbVc.mjs +0 -148
  69. package/dist/packem_shared/defineSkill-Ctf_S-rz.mjs +0 -22
  70. package/dist/packem_shared/functionTool-D6lCa2jB.mjs +0 -20
  71. package/dist/packem_shared/graph-component-aoUwO-f0.mjs +0 -216
  72. package/dist/packem_shared/memory-D4FPcBsX.mjs +0 -12
  73. package/dist/packem_shared/normalizeEntityName-CyEEWFkR.mjs +0 -3
  74. package/dist/packem_shared/runAgentLoop-Dhg4ZNvw.mjs +0 -493
  75. package/dist/packem_shared/runVoiceTurn-LnqLvCRR.mjs +0 -211
  76. package/dist/packem_shared/sandboxComponent-DR3pTwBL.mjs +0 -194
  77. package/dist/packem_shared/sentryTelemetry-CgqFJyLO.mjs +0 -36
  78. package/dist/packem_shared/types.d-BhJFTvz_.d.mts +0 -1114
  79. package/dist/packem_shared/types.d-BhJFTvz_.d.ts +0 -1114
@@ -1,13 +1,13 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-BhJFTvz_.mjs";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-BhJFTvz_.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
- * @experimental
10
- */
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
+ */
11
11
  declare const verifySlack: (options: {
12
12
  body: string;
13
13
  now?: number;
@@ -17,19 +17,19 @@ declare const verifySlack: (options: {
17
17
  tolerance?: number;
18
18
  }) => Promise<boolean>;
19
19
  /**
20
- * Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body).
21
- * @experimental
22
- */
20
+ * Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body).
21
+ * @experimental
22
+ */
23
23
  declare const verifyGithub: (options: {
24
24
  body: string;
25
25
  secret: string;
26
26
  signature: string | undefined;
27
27
  }) => Promise<boolean>;
28
28
  /**
29
- * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
30
- * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
31
- * @experimental
32
- */
29
+ * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
30
+ * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
31
+ * @experimental
32
+ */
33
33
  declare const verifyDiscord: (options: {
34
34
  body: string;
35
35
  publicKey: string;
@@ -37,9 +37,9 @@ declare const verifyDiscord: (options: {
37
37
  timestamp: string | undefined;
38
38
  }) => Promise<boolean>;
39
39
  /**
40
- * One agent wired into the inbound channel HTTP handler.
41
- * @experimental
42
- */
40
+ * One agent wired into the inbound channel HTTP handler.
41
+ * @experimental
42
+ */
43
43
  interface AgentChannelTarget {
44
44
  /** The agent definition — only its `onInbound` config is read. */
45
45
  agent: Pick<AgentDefinition, "onInbound">;
@@ -47,20 +47,28 @@ interface AgentChannelTarget {
47
47
  binding: string;
48
48
  }
49
49
  /**
50
- * The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route.
51
- * @experimental
52
- */
50
+ * The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route.
51
+ * @experimental
52
+ */
53
53
  type InboundChannelHandler = (request: Request, env: Record<string, unknown>) => Promise<Response>;
54
54
  /**
55
- * Build an HTTP handler that starts a durable agent run from a verified inbound
56
- * webhook. The channel is detected from the signature headers; EACH eligible
57
- * target is verified against ITS OWN secret before its mapper is offered the
58
- * event (so one app's valid signature can't trigger another app's agent), and
59
- * the first target that both verifies and claims starts a run on its Workflow
60
- * binding. Returns `401` when NO target's signature verifies, `200` on a claim
61
- * (or a Discord PONG), `204` when a verified event is declined by every mapper,
62
- * and `400` for an unrecognized webhook.
63
- * @experimental
64
- */
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
+ */
65
73
  declare const dispatchAgentChannel: (targets: ReadonlyArray<AgentChannelTarget>) => InboundChannelHandler;
66
- export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
74
+ export { type AgentChannelTarget, type InboundChannelHandler, dispatchAgentChannel, isDuplicateInstanceError, verifyDiscord, verifyGithub, verifySlack };
@@ -1,13 +1,13 @@
1
- import { A as AgentDefinition } from "./packem_shared/types.d-BhJFTvz_.js";
2
- export type { I as InboundChannelEvent } from "./packem_shared/types.d-BhJFTvz_.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
- * @experimental
10
- */
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
+ */
11
11
  declare const verifySlack: (options: {
12
12
  body: string;
13
13
  now?: number;
@@ -17,19 +17,19 @@ declare const verifySlack: (options: {
17
17
  tolerance?: number;
18
18
  }) => Promise<boolean>;
19
19
  /**
20
- * Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body).
21
- * @experimental
22
- */
20
+ * Verify a GitHub webhook signature (`x-hub-signature-256` = `sha256=` + HMAC over the body).
21
+ * @experimental
22
+ */
23
23
  declare const verifyGithub: (options: {
24
24
  body: string;
25
25
  secret: string;
26
26
  signature: string | undefined;
27
27
  }) => Promise<boolean>;
28
28
  /**
29
- * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
30
- * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
31
- * @experimental
32
- */
29
+ * Verify a Discord interaction signature: Ed25519 over `timestamp + body`,
30
+ * `x-signature-ed25519` (hex) against the application's `publicKey` (hex).
31
+ * @experimental
32
+ */
33
33
  declare const verifyDiscord: (options: {
34
34
  body: string;
35
35
  publicKey: string;
@@ -37,9 +37,9 @@ declare const verifyDiscord: (options: {
37
37
  timestamp: string | undefined;
38
38
  }) => Promise<boolean>;
39
39
  /**
40
- * One agent wired into the inbound channel HTTP handler.
41
- * @experimental
42
- */
40
+ * One agent wired into the inbound channel HTTP handler.
41
+ * @experimental
42
+ */
43
43
  interface AgentChannelTarget {
44
44
  /** The agent definition — only its `onInbound` config is read. */
45
45
  agent: Pick<AgentDefinition, "onInbound">;
@@ -47,20 +47,28 @@ interface AgentChannelTarget {
47
47
  binding: string;
48
48
  }
49
49
  /**
50
- * The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route.
51
- * @experimental
52
- */
50
+ * The HTTP handler `dispatchAgentChannel` returns — mount it on a webhook route.
51
+ * @experimental
52
+ */
53
53
  type InboundChannelHandler = (request: Request, env: Record<string, unknown>) => Promise<Response>;
54
54
  /**
55
- * Build an HTTP handler that starts a durable agent run from a verified inbound
56
- * webhook. The channel is detected from the signature headers; EACH eligible
57
- * target is verified against ITS OWN secret before its mapper is offered the
58
- * event (so one app's valid signature can't trigger another app's agent), and
59
- * the first target that both verifies and claims starts a run on its Workflow
60
- * binding. Returns `401` when NO target's signature verifies, `200` on a claim
61
- * (or a Discord PONG), `204` when a verified event is declined by every mapper,
62
- * and `400` for an unrecognized webhook.
63
- * @experimental
64
- */
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
+ */
65
73
  declare const dispatchAgentChannel: (targets: ReadonlyArray<AgentChannelTarget>) => InboundChannelHandler;
66
- 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
@@ -1,181 +1 @@
1
- import { LunoraError } from '@lunora/errors';
2
-
3
- const DEFAULT_TIMESTAMP_TOLERANCE = 300;
4
- const NON_HEX = /[^0-9a-f]/iu;
5
- const encoder = new TextEncoder();
6
- const utf8 = (value) => new Uint8Array(encoder.encode(value));
7
- const hexToBytes = (hex) => {
8
- if (hex.length % 2 !== 0 || NON_HEX.test(hex)) {
9
- throw new LunoraError("BAD_REQUEST", "@lunora/agent: malformed hex signature");
10
- }
11
- const bytes = new Uint8Array(hex.length / 2);
12
- for (let index = 0; index < bytes.length; index += 1) {
13
- bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
14
- }
15
- return bytes;
16
- };
17
- const verifyHmacSha256 = async (secret, message, signatureHex) => {
18
- try {
19
- const key = await crypto.subtle.importKey("raw", utf8(secret), { hash: "SHA-256", name: "HMAC" }, false, ["verify"]);
20
- return await crypto.subtle.verify("HMAC", key, hexToBytes(signatureHex), utf8(message));
21
- } catch {
22
- return false;
23
- }
24
- };
25
- const isFreshTimestamp = (timestampSeconds, nowSeconds, tolerance) => Number.isFinite(timestampSeconds) && Math.abs(nowSeconds - timestampSeconds) <= tolerance;
26
- const verifySlack = async (options) => {
27
- const { body, now = Date.now() / 1e3, signature, signingSecret, timestamp, tolerance = DEFAULT_TIMESTAMP_TOLERANCE } = options;
28
- if (!signature || !timestamp || !signature.startsWith("v0=")) {
29
- return false;
30
- }
31
- if (!isFreshTimestamp(Number(timestamp), now, tolerance)) {
32
- return false;
33
- }
34
- return verifyHmacSha256(signingSecret, `v0:${timestamp}:${body}`, signature.slice("v0=".length));
35
- };
36
- const verifyGithub = async (options) => {
37
- const { body, secret, signature } = options;
38
- if (!signature?.startsWith("sha256=")) {
39
- return false;
40
- }
41
- return verifyHmacSha256(secret, body, signature.slice("sha256=".length));
42
- };
43
- const verifyDiscord = async (options) => {
44
- const { body, publicKey, signature, timestamp } = options;
45
- if (!signature || !timestamp) {
46
- return false;
47
- }
48
- try {
49
- const key = await crypto.subtle.importKey("raw", hexToBytes(publicKey), { name: "Ed25519" }, false, ["verify"]);
50
- return await crypto.subtle.verify({ name: "Ed25519" }, key, hexToBytes(signature), utf8(timestamp + body));
51
- } catch {
52
- return false;
53
- }
54
- };
55
- const detectChannel = (headers) => {
56
- if (headers.has("x-slack-signature")) {
57
- return "slack";
58
- }
59
- if (headers.has("x-hub-signature-256")) {
60
- return "github";
61
- }
62
- if (headers.has("x-signature-ed25519")) {
63
- return "discord";
64
- }
65
- return void 0;
66
- };
67
- const resolveSecret = (secret, env) => {
68
- if (typeof secret === "function") {
69
- return secret(env);
70
- }
71
- const value = env[secret];
72
- return typeof value === "string" ? value : void 0;
73
- };
74
- const verifyChannel = async (channel, secret, headers, body) => {
75
- switch (channel) {
76
- case "discord": {
77
- return verifyDiscord({
78
- body,
79
- publicKey: secret,
80
- signature: headers.get("x-signature-ed25519") ?? void 0,
81
- timestamp: headers.get("x-signature-timestamp") ?? void 0
82
- });
83
- }
84
- case "github": {
85
- return verifyGithub({ body, secret, signature: headers.get("x-hub-signature-256") ?? void 0 });
86
- }
87
- case "slack": {
88
- return verifySlack({
89
- body,
90
- signature: headers.get("x-slack-signature") ?? void 0,
91
- signingSecret: secret,
92
- timestamp: headers.get("x-slack-request-timestamp") ?? void 0
93
- });
94
- }
95
- default: {
96
- return false;
97
- }
98
- }
99
- };
100
- const discordPongResponse = (channel, body) => {
101
- if (channel !== "discord") {
102
- return void 0;
103
- }
104
- try {
105
- if (JSON.parse(body).type === 1) {
106
- return Response.json({ type: 1 });
107
- }
108
- } catch {
109
- }
110
- return void 0;
111
- };
112
- const UNSAFE_INSTANCE_ID = /[^\w-]/gu;
113
- const DUPLICATE_INSTANCE = /already[\s-]?exists/iu;
114
- const ack = () => new Response(void 0, { status: 200 });
115
- const isDuplicateInstanceError = (error) => DUPLICATE_INSTANCE.test(error instanceof Error ? error.message : String(error));
116
- const deliveryId = (channel, headers, body) => {
117
- if (channel === "github") {
118
- return headers.get("x-github-delivery") ?? void 0;
119
- }
120
- try {
121
- const parsed = JSON.parse(body);
122
- const raw = channel === "slack" ? parsed.event_id : parsed.id;
123
- return typeof raw === "string" ? raw : void 0;
124
- } catch {
125
- return void 0;
126
- }
127
- };
128
- const startChannelRun = async (workflow, run, channel, id) => {
129
- const sanitizedId = id === void 0 ? "" : id.replaceAll(UNSAFE_INSTANCE_ID, "");
130
- if (sanitizedId === "") {
131
- await workflow.create({ params: run });
132
- return ack();
133
- }
134
- try {
135
- await workflow.create({ id: `${channel}-${sanitizedId}`.slice(0, 60), params: run });
136
- } catch (error) {
137
- if (isDuplicateInstanceError(error)) {
138
- return ack();
139
- }
140
- throw error;
141
- }
142
- return ack();
143
- };
144
- const dispatchAgentChannel = (targets) => async (request, env) => {
145
- const body = await request.text();
146
- const channel = detectChannel(request.headers);
147
- if (channel === void 0) {
148
- return new Response("Unrecognized webhook", { status: 400 });
149
- }
150
- const eligible = targets.map((target) => {
151
- return { config: target.agent.onInbound, target };
152
- }).filter((entry) => entry.config?.channel === channel);
153
- const event = { channel, headers: request.headers, json: () => JSON.parse(body), rawBody: body };
154
- let verifiedAny = false;
155
- for (const { config, target } of eligible) {
156
- const secret = resolveSecret(config.secret, env);
157
- if (secret === void 0 || !await verifyChannel(channel, secret, request.headers, body)) {
158
- continue;
159
- }
160
- verifiedAny = true;
161
- const pong = discordPongResponse(channel, body);
162
- if (pong) {
163
- return pong;
164
- }
165
- const run = await config.map(event);
166
- if (!run) {
167
- continue;
168
- }
169
- const workflow = env[target.binding];
170
- if (!workflow || typeof workflow.create !== "function") {
171
- throw new LunoraError(
172
- "INTERNAL",
173
- `@lunora/agent: no Workflow binding "${target.binding}" on env for an inbound-channel agent — run codegen/dev so wrangler.jsonc declares it`
174
- );
175
- }
176
- return await startChannelRun(workflow, run, channel, deliveryId(channel, request.headers, body));
177
- }
178
- return verifiedAny ? new Response(void 0, { status: 204 }) : new Response("Invalid signature", { status: 401 });
179
- };
180
-
181
- export { dispatchAgentChannel, verifyDiscord, verifyGithub, verifySlack };
1
+ import{LunoraError as w}from"@lunora/errors";const m=300,v=/[^0-9a-f]/iu,x=new TextEncoder,d=e=>new Uint8Array(x.encode(e)),g=e=>{if(e.length%2!==0||v.test(e))throw new w("BAD_REQUEST","@lunora/agent: malformed hex signature");const t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r+=1)t[r]=Number.parseInt(e.slice(r*2,r*2+2),16);return t},b=async(e,t,r)=>{try{const s=await crypto.subtle.importKey("raw",d(e),{hash:"SHA-256",name:"HMAC"},!1,["verify"]);return await crypto.subtle.verify("HMAC",s,g(r),d(t))}catch{return!1}},k=(e,t,r)=>Number.isFinite(e)&&Math.abs(t-e)<=r,A=async e=>{const{body:t,now:r=Date.now()/1e3,signature:s,signingSecret:n,timestamp:a,tolerance:o=m}=e;return!s||!a||!s.startsWith("v0=")||!k(Number(a),r,o)?!1:b(n,`v0:${a}:${t}`,s.slice(3))},E=async e=>{const{body:t,secret:r,signature:s}=e;return s?.startsWith("sha256=")?b(r,t,s.slice(7)):!1},S=async e=>{const{body:t,publicKey:r,signature:s,timestamp:n}=e;if(!s||!n)return!1;try{const a=await crypto.subtle.importKey("raw",g(r),{name:"Ed25519"},!1,["verify"]);return await crypto.subtle.verify({name:"Ed25519"},a,g(s),d(n+t))}catch{return!1}},N=e=>{if(e.has("x-slack-signature"))return"slack";if(e.has("x-hub-signature-256"))return"github";if(e.has("x-signature-ed25519"))return"discord"},R=(e,t)=>{if(typeof e=="function")return e(t);const r=t[e];return typeof r=="string"?r:void 0},I=async(e,t,r,s)=>{switch(e){case"discord":return S({body:s,publicKey:t,signature:r.get("x-signature-ed25519")??void 0,timestamp:r.get("x-signature-timestamp")??void 0});case"github":return E({body:s,secret:t,signature:r.get("x-hub-signature-256")??void 0});case"slack":return A({body:s,signature:r.get("x-slack-signature")??void 0,signingSecret:t,timestamp:r.get("x-slack-request-timestamp")??void 0});default:return!1}},D=(e,t)=>{if(e==="discord")try{if(JSON.parse(t).type===1)return Response.json({type:1})}catch{}},K=/[^\w-]/gu,U=/already[\s-]?exists/iu,u=()=>new Response(void 0,{status:200}),$=e=>U.test(e instanceof Error?e.message:String(e)),C=(e,t,r)=>{if(e==="github")return t.get("x-github-delivery")??void 0;try{const s=JSON.parse(r),n=e==="slack"?s.event_id:s.id;return typeof n=="string"?n:void 0}catch{return}},H=async(e,t,r,s)=>{const n=s===void 0?"":s.replaceAll(K,"");if(n==="")return await e.create({params:t}),u();try{await e.create({id:`${r}-${n}`.slice(0,60),params:t})}catch(a){if($(a))return u();throw a}return u()},j=e=>async(t,r)=>{const s=await t.text(),n=N(t.headers);if(n===void 0)return new Response("Unrecognized webhook",{status:400});const a=e.map(i=>({config:i.agent.onInbound,target:i})).filter(i=>i.config?.channel===n),o={channel:n,headers:t.headers,json:()=>JSON.parse(s),rawBody:s};let f=!1;for(const{config:i,target:l}of a){const y=R(i.secret,r);if(y===void 0||!await I(n,y,t.headers,s))continue;f=!0;const h=D(n,s);if(h)return h;const p=await i.map(o);if(!p)continue;const c=r[l.binding];if(!c||typeof c.create!="function")throw new w("INTERNAL",`@lunora/agent: no Workflow binding "${l.binding}" on env for an inbound-channel agent — run codegen/dev so wrangler.jsonc declares it`);return await H(c,p,n,C(n,t.headers,s))}return f?new Response(void 0,{status:204}):new Response("Invalid signature",{status:401})};export{j as dispatchAgentChannel,$ as isDuplicateInstanceError,S as verifyDiscord,E as verifyGithub,A as verifySlack};
@@ -1,33 +1,29 @@
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
- * @experimental
21
- */
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
+ */
22
20
  declare const normalizeEntityName: (name: string) => string;
23
- /** The two internal graph-memory functions the durable loop dispatches to. */
24
-
25
21
  /**
26
- * Loose structural view of the registered sandbox action — wide enough for the
27
- * concrete `RegisteredAction`, narrow enough for re-export + dispatch. Codegen
28
- * registers the runtime value; it never needs the precise generics.
29
- * @experimental
30
- */
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
+ */
31
27
  interface SandboxRegisteredFunction {
32
28
  readonly args: unknown;
33
29
  readonly handler: (context: unknown, args: never) => unknown;
@@ -35,50 +31,43 @@ interface SandboxRegisteredFunction {
35
31
  readonly visibility?: "internal" | "public";
36
32
  }
37
33
  /**
38
- * `SandboxComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
39
- * @experimental
40
- */
34
+ * `SandboxComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
35
+ * @experimental
36
+ */
41
37
  interface SandboxComponent {
42
38
  invoke: SandboxRegisteredFunction;
43
39
  }
44
40
  /**
45
- * Resolve a model-supplied `path` to an absolute R2 key UNDER `root`, rejecting
46
- * any `..` that would escape the sandbox root. Every returned key is guaranteed
47
- * to start with the normalized `root`, so the model can never read or write
48
- * outside its own prefix the fs sandbox's core isolation guarantee.
49
- */
50
-
51
- /**
52
- * Build the sandbox runtime component: the single internal action the
53
- * batteries-included `browserTool`/`containerTool` dispatch to. Codegen
54
- * auto-registers it at `sandbox:invoke` whenever `lunora/` imports a sandbox
55
- * tool. It runs as an **action** because that is the only ctx codegen attaches
56
- * `ctx.browser` to (and `ctx.containers` rides every ctx once a container is
57
- * declared) — the durable tool step itself has neither.
58
- * @experimental
59
- */
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
+ */
60
49
  declare const sandboxComponent: () => SandboxComponent;
61
50
  /**
62
- * The agent thread tables, shipped as a schema extension so an app merges
63
- * them with one call and they can never collide with app tables:
64
- *
65
- * ```ts
66
- * // lunora/schema.ts
67
- * export default defineSchema({ ... }).extend(agentExtension);
68
- * ```
69
- *
70
- * Message ordering follows the Convex-agent model: `seq` is the monotonic
71
- * per-thread position (allocated from the thread's `messageCount` counter, so
72
- * allocation is O(1) inside the serialized mutation), and `messageKey` is the
73
- * deterministic idempotency key — a workflow replay that re-persists the same
74
- * message is a no-op instead of a duplicate.
75
- * @experimental
76
- */
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
+ */
77
66
  declare const agentExtension: SchemaExtension;
78
67
  /**
79
- * `AgentComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
80
- * @experimental
81
- */
68
+ * `AgentComponent` is part of the experimental `@lunora/agent` API and may change without a major version bump.
69
+ * @experimental
70
+ */
82
71
  interface AgentComponent {
83
72
  extension: SchemaExtension;
84
73
  functions: {
@@ -98,18 +87,18 @@ interface AgentComponent {
98
87
  };
99
88
  }
100
89
  /**
101
- * Build the agent runtime component: the thread schema extension plus the
102
- * functions the durable loop dispatches to (and the client subscribes to).
103
- * Codegen auto-registers them under the `agents:*` namespace whenever
104
- * `lunora/agents.ts` declares an agent — the loop's dispatch paths assume
105
- * that namespace, and apps never re-export these by hand.
106
- *
107
- * Most mutations are **internal** (only the workflow's admin-authenticated
108
- * dispatch may call them); the queries are public so a client can subscribe
109
- * to `agents:agentMessages` for a live thread view. Two mutations are public:
110
- * `agentResolveApproval` (a client resolves a HITL approval) and `agentRun`
111
- * (an HTTP client starts a durable run) — both owner-gated.
112
- * @experimental
113
- */
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
+ */
114
103
  declare const agentComponent: () => AgentComponent;
115
104
  export { AgentComponent, type SandboxComponent, type SandboxRegisteredFunction, agentComponent, agentExtension, normalizeEntityName, sandboxComponent };