@mayiapp/eve 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Aaron HS
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,272 @@
1
+ # `@mayiapp/eve`
2
+
3
+ Durable Mayi approval channel for [Eve](https://github.com/vercel/eve) agents.
4
+
5
+ ## Quick start
6
+
7
+ ```sh
8
+ npm install @mayiapp/eve eve
9
+ ```
10
+
11
+ Create the Mayi channel in the root agent:
12
+
13
+ ```ts
14
+ // agent/channels/mayi.ts
15
+ import { mayiChannel } from "@mayiapp/eve";
16
+ import { credentials } from "../credentials.server";
17
+
18
+ export default mayiChannel({
19
+ getAccessToken: () => credentials.getAccessToken("mayi"),
20
+ });
21
+ ```
22
+
23
+ The host owns Mayi's Authorization Code + PKCE flow, securely stores and
24
+ refreshes the OAuth grant, and returns a current access token from
25
+ `getAccessToken`. The adapter and `@mayiapp/sdk` do not store tokens. Do not put an
26
+ access token, refresh token, static API key, or webhook endpoint ID in agent code.
27
+ Eden's generated integration supplies this credential binding; see the complete
28
+ [Eden agent template](https://github.com/asiraky/mayi/tree/main/packages/eve/examples/eden-agent).
29
+
30
+ An approval-gated tool remains ordinary Eve code:
31
+
32
+ ```ts
33
+ // agent/tools/deploy-production.ts
34
+ import { defineTool } from "eve/tools";
35
+ import { always } from "eve/tools/approval";
36
+ import { z } from "zod";
37
+
38
+ export default defineTool({
39
+ description: "Deploy a version to production.",
40
+ inputSchema: z.object({ version: z.string() }),
41
+ approval: always(),
42
+ async execute({ version }) {
43
+ return deployToProduction(version);
44
+ },
45
+ });
46
+ ```
47
+
48
+ ## Approval evidence
49
+
50
+ Approval happens before Eve calls the gated tool's `execute()` function. An
51
+ evidence hook can render from the proposed tool input, fetch an existing object,
52
+ or read a file created by an earlier tool. It cannot use the gated tool's result,
53
+ because that result does not exist until after the human approves.
54
+
55
+ Configure `artefacts` when an approval should include a PDF or image:
56
+
57
+ ```ts
58
+ export default mayiChannel({
59
+ getAccessToken: () => credentials.getAccessToken("mayi"),
60
+ async artefacts({ request, signal }) {
61
+ const pdf = await renderDeploymentPlan(request.action.input, { signal });
62
+ return [{
63
+ filename: "deployment-plan.pdf",
64
+ mediaType: "application/pdf",
65
+ body: pdf,
66
+ }];
67
+ },
68
+ });
69
+ ```
70
+
71
+ The hook receives the complete Eve input request, a read-only session snapshot,
72
+ `getSandbox()`, and an abort signal. For evidence already in object storage:
73
+
74
+ ```ts
75
+ async artefacts({ request, signal }) {
76
+ const response = await fetch(String(request.action.input.previewUrl), { signal });
77
+ return [{ filename: "preview.webp", mediaType: "image/webp", body: response.body! }];
78
+ }
79
+ ```
80
+
81
+ For a file produced by an earlier tool, read through Eve's supported sandbox
82
+ API and return the bytes:
83
+
84
+ ```ts
85
+ async artefacts({ getSandbox }) {
86
+ const sandbox = await getSandbox();
87
+ const body = await sandbox.readBinaryFile("/workspace/output/report.pdf");
88
+ return [{ filename: "report.pdf", mediaType: "application/pdf", body }];
89
+ }
90
+ ```
91
+
92
+ `undefined`, `null`, and an empty array all mean no evidence and preserve the
93
+ ordinary one-call approval path. A request may return at most 20 artefacts;
94
+ each must be a PDF, PNG, JPEG, or WebP no larger than 25 MiB. Returned order is
95
+ the order shown to the approver and bound into the approval manifest. Hook,
96
+ validation, timeout, or upload failures fail closed: Mayi does not create an
97
+ approval with silently missing evidence. `artefactTimeoutMs` defaults to 30
98
+ seconds and its signal should be passed to rendering, fetch, and file work.
99
+
100
+ ## Scheduled and cross-channel handoff
101
+
102
+ Mayi handles `input.requested` only for sessions owned by the Mayi channel. It
103
+ does not intercept a session that belongs to Slack, Discord, the default HTTP
104
+ channel, or another custom channel. Start work on Mayi with Eve's
105
+ `receive(mayi, ...)` whenever that work may need a Mayi approval.
106
+
107
+ Task-mode schedules cannot park for human input. Use the handler form of an Eve
108
+ schedule and hand the run to Mayi:
109
+
110
+ ```ts
111
+ // agent/schedules/check-production.ts
112
+ import { defineSchedule } from "eve/schedules";
113
+ import mayi from "../channels/mayi";
114
+
115
+ export default defineSchedule({
116
+ cron: "0 * * * *",
117
+ run({ receive, waitUntil, appAuth }) {
118
+ waitUntil(receive(mayi, {
119
+ message: "Check production and deploy if needed.",
120
+ target: { mayiUserId: "AbCdEfGhIjKl" },
121
+ auth: appAuth,
122
+ }));
123
+ },
124
+ });
125
+ ```
126
+
127
+ The same rule applies to cross-channel work. A custom channel route receives an
128
+ Eve `receive` helper; import the Mayi channel and hand the new session to it:
129
+
130
+ ```ts
131
+ import { defineChannel, POST } from "eve/channels";
132
+ import mayi from "./mayi";
133
+
134
+ export default defineChannel({
135
+ routes: [
136
+ POST("/deploy-request", async (request, { receive, waitUntil }) => {
137
+ const deployment = await request.json();
138
+ waitUntil(receive(mayi, {
139
+ message: `Review and deploy ${deployment.version}.`,
140
+ target: { mayiUserId: deployment.approverId },
141
+ auth: {
142
+ authenticator: "eden",
143
+ principalType: "service",
144
+ principalId: deployment.requestedBy,
145
+ attributes: {},
146
+ },
147
+ }));
148
+ return new Response("accepted", { status: 202 });
149
+ }),
150
+ ],
151
+ });
152
+ ```
153
+
154
+ `target.mayiUserId` is optional. When supplied, it must be a 12-letter Mayi user
155
+ ID and is sent as the suggested approver. The caller supplies the initial
156
+ message, target, and Eve authentication context; the adapter owns the raw
157
+ continuation token and durable channel state.
158
+
159
+ ## Callback route and resume semantics
160
+
161
+ `mayiChannel()` registers this route in the deployed Eve service:
162
+
163
+ ```text
164
+ POST /eve/v1/mayi/approval-resolved
165
+ ```
166
+
167
+ Authors do not create that route, its URL, encrypted state, a correlation table,
168
+ or a signature verifier. The adapter builds the callback URL from the stable
169
+ public Eve origin. Register that exact HTTPS URL as an
170
+ entry in `approval_callback_uris` for the Eden/host OAuth client.
171
+
172
+ The explicit `POST()` route is intentional. Eve 0.24.2 compiles a channel
173
+ `route.path` verbatim; it does not prepend a channel namespace. Inventing a
174
+ `/channels/mayi/...` prefix registers the wrong OAuth callback and produces a
175
+ 404 instead of a resume.
176
+
177
+ For every delivery, the adapter verifies Mayi's EdDSA JWS against Mayi's JWKS
178
+ before it touches encrypted state or resumes Eve. It then opens the authenticated
179
+ state and submits the response against the original Eve request ID and the
180
+ original channel-local continuation token. An `approved` result chooses Eve's
181
+ `approve` option. `denied`, `expired`, and `cancelled` choose `deny`, so the
182
+ parked session resumes but the gated tool does not run.
183
+
184
+ Callback delivery is at least once. A `2xx` response means Eve accepted the
185
+ resume or had already accepted the same verified event. The adapter does not
186
+ acknowledge before Eve accepts. Eve's continuation fence and event stream let
187
+ the adapter recognize an already-accepted resume, so retrying the same stable
188
+ event ID cannot execute the tool twice. Verification, decryption, or resume
189
+ failures return a non-`2xx` response so Mayi continues its durable retry policy.
190
+ The signed event and encrypted callback state remain acceptable for seven days
191
+ after resolution. Manual replay keeps the original event ID and occurrence time,
192
+ so operators must replay a dead letter within that window and consumers retain
193
+ the same duplicate-safety identity.
194
+
195
+ Hosts that already have a durable event store may pass `eventStore` with
196
+ `isProcessed(eventId)` and `markProcessed(eventId)` methods. The duplicate check
197
+ runs only after webhook verification, and `markProcessed` runs only after Eve
198
+ accepts the resume. A crash before acceptance therefore leaves the event
199
+ retryable. The hook is optional; ordinary Eden-generated integrations can rely
200
+ on Eve's durable acceptance check without adding a consumer database. The
201
+ advanced `webhookFetch` option can inject the fetch implementation used to load
202
+ Mayi's JWKS; normal deployments use the runtime's global `fetch`.
203
+
204
+ ## Deployment configuration
205
+
206
+ The deployment host provisions:
207
+
208
+ - `EVE_PUBLIC_ORIGIN`: the stable public HTTPS origin of the deployed Eve agent,
209
+ such as `https://agent.example`. Do not use a transient preview URL.
210
+ - `MAYI_CALLBACK_STATE_KEY_ID`: the identifier for the current callback-state
211
+ encryption key.
212
+ - `MAYI_CALLBACK_STATE_KEY`: a stable base64url-encoded 32-byte encryption key.
213
+ - `MAYI_CALLBACK_STATE_PREVIOUS_KEYS` (optional): a JSON array of decrypt-only
214
+ `{ "kid", "key" }` entries retained while approvals and callback retries made
215
+ with older keys may still be outstanding.
216
+ - `MAYI_ORIGIN` (optional): the Mayi API and JWKS origin. It defaults to
217
+ `https://app.mayi.sh`.
218
+
219
+ Keep callback-state keys stable across process restarts and deploys. Rotate by
220
+ installing a new current key and retaining the old key in
221
+ `MAYI_CALLBACK_STATE_PREVIOUS_KEYS` until its approvals and the seven-day
222
+ callback acceptance window have elapsed. Never derive these keys from OAuth
223
+ credentials.
224
+
225
+ For local development, pass a public HTTPS tunnel origin as `publicOrigin` to
226
+ `mayiChannel()`, and register the resulting callback URL on the development
227
+ OAuth client. This override is refused in production. Mayi signing keys are
228
+ read from `${MAYI_ORIGIN}/.well-known/jwks.json` and cached with bounded refresh;
229
+ they are not copied into the agent's environment.
230
+
231
+ Never log decrypted callback state, continuation tokens, OAuth credentials,
232
+ receipts, or sensitive tool input.
233
+
234
+ ## Troubleshooting
235
+
236
+ - **Public origin:** set `EVE_PUBLIC_ORIGIN` to one stable public HTTPS origin.
237
+ Vercel production can use `VERCEL_PROJECT_PRODUCTION_URL`; preview URLs,
238
+ localhost, paths, ports, and private hosts are refused.
239
+ - **Origin changes:** `approval_callback_uris` are immutable. Register a new
240
+ OAuth client with the new callback, reconnect through Authorization Code +
241
+ PKCE, confirm the new agent works, then revoke the old agent connection.
242
+ - **Local tunnels:** pass the tunnel origin through `publicOrigin` only in local
243
+ development and register its exact callback on a development OAuth client.
244
+ Tunnel rotation requires updating that development registration.
245
+ - **State rotation:** install a new callback-state key and retain old decrypt-only
246
+ keys until every outstanding approval plus Mayi's seven-day acceptance window has
247
+ elapsed. Unknown, tampered, expired, or wrong-key state fails closed.
248
+ - **Signing rotation:** Mayi publishes current and retained Ed25519 public keys at
249
+ `/.well-known/jwks.json`. Keep old signing public keys available through the
250
+ callback retry/event-age window; bounded JWKS refresh handles a newly seen key.
251
+ - **Retries and recovery:** non-`2xx` responses remain retryable according to
252
+ Mayi's outbox policy. Inspect the Mayi callback job, correct the receiver, and
253
+ replay only a `DEAD_LETTER` job through the authenticated replay endpoint. The
254
+ stable event ID makes duplicate delivery safe.
255
+
256
+ ## Eve 0.24.2 limitation for unsupported input
257
+
258
+ Phase one supports approval-shaped confirmation requests only. A freeform or
259
+ select `ask_question` produces a descriptive `UnsupportedMayiInputError`; it is
260
+ never converted into an approval and is never treated as a successful no-op.
261
+
262
+ Eve 0.24.2 catches, logs, and swallows exceptions thrown by channel event
263
+ handlers, and exposes no public send/resume operation inside an event handler.
264
+ Consequently this rejection is visible in Eve's server logs but cannot currently
265
+ be propagated to the author or used to fail the session through Eve's public
266
+ contract; the session remains parked. Treat this as an unsupported-input error
267
+ and monitor Eve logs. It is a residual Eve contract gap, not approval behavior.
268
+
269
+ The package is ESM-only, supports Node.js 24 and later (matching Eve), and pins
270
+ its Eve peer to the tested `eve@0.24.2` contract.
271
+
272
+ Licensed under the [Apache License 2.0](LICENSE).
@@ -0,0 +1,109 @@
1
+ import { GetAccessToken, MayiFetch, ArtefactMediaType, ArtefactBody, WebhookVerifierFetch, CallbackStateCodec } from '@mayiapp/sdk';
2
+ import { SessionHandle, Channel } from 'eve/channels';
3
+ import { SandboxSession } from 'eve/sandbox';
4
+
5
+ declare const MAYI_CALLBACK_PATH = "/eve/v1/mayi/approval-resolved";
6
+ interface MayiEnvironment {
7
+ readonly EVE_PUBLIC_ORIGIN?: string;
8
+ readonly MAYI_CALLBACK_STATE_KEY?: string;
9
+ readonly MAYI_CALLBACK_STATE_KEY_ID?: string;
10
+ readonly MAYI_CALLBACK_STATE_PREVIOUS_KEYS?: string;
11
+ readonly MAYI_ORIGIN?: string;
12
+ readonly NODE_ENV?: string;
13
+ readonly VERCEL_BRANCH_URL?: string;
14
+ readonly VERCEL_ENV?: string;
15
+ readonly VERCEL_PROJECT_PRODUCTION_URL?: string;
16
+ readonly VERCEL_URL?: string;
17
+ }
18
+ type MayiEveConfigurationErrorCode = "CALLBACK_STATE_KEY_UNAVAILABLE" | "INVALID_CALLBACK_STATE_KEYS" | "INVALID_CONFIG" | "INVALID_PUBLIC_ORIGIN" | "INVALID_RECEIVE_TARGET" | "PREVIEW_ORIGIN_REFUSED" | "PUBLIC_ORIGIN_UNAVAILABLE";
19
+ declare class MayiEveConfigurationError extends Error {
20
+ readonly code: MayiEveConfigurationErrorCode;
21
+ constructor(code: MayiEveConfigurationErrorCode, message: string);
22
+ }
23
+ interface ResolvePublicOriginOptions {
24
+ readonly environment?: MayiEnvironment;
25
+ /** Explicit local-development or tunnel origin. Ignored in production. */
26
+ readonly developmentOverride?: string;
27
+ }
28
+ /** Resolves the stable origin Eve exposes publicly for durable callbacks. */
29
+ declare function resolvePublicOrigin(options?: ResolvePublicOriginOptions): string;
30
+
31
+ interface MayiReceiveTarget {
32
+ /** Mayi user to suggest as the approver for approvals started by this session. */
33
+ readonly mayiUserId?: string;
34
+ }
35
+ interface MayiChannelState {
36
+ /** The channel-local token passed to Eve send(); never derive this by stripping a namespace. */
37
+ rawContinuationToken: string | null;
38
+ target: MayiReceiveTarget | null;
39
+ /** Opaque callback material retained so a redelivered Eve event is retry-compatible. */
40
+ callbackRequests?: Record<string, MayiCallbackRequestState>;
41
+ }
42
+ interface MayiCallbackRequestState {
43
+ readonly state: string;
44
+ readonly approvalExpiresAt: string;
45
+ }
46
+ interface MayiWebhookEventStore {
47
+ /** Returns true only after Eve has accepted this event's resume. */
48
+ readonly isProcessed: (eventId: string) => boolean | Promise<boolean>;
49
+ /** Durably records an event after Eve has accepted its resume. */
50
+ readonly markProcessed: (eventId: string) => void | Promise<void>;
51
+ }
52
+ type MayiArtefactMediaType = ArtefactMediaType;
53
+ type MayiArtefactBody = ArtefactBody;
54
+ interface MayiApprovalArtefact {
55
+ readonly filename: string;
56
+ readonly mediaType: MayiArtefactMediaType;
57
+ readonly body: MayiArtefactBody;
58
+ readonly size?: number;
59
+ }
60
+ interface MayiArtefactsContext {
61
+ readonly request: Readonly<EveInputRequest>;
62
+ readonly session: Readonly<Pick<SessionHandle, "id" | "continuationToken" | "auth">>;
63
+ readonly getSandbox: () => Promise<SandboxSession>;
64
+ readonly signal: AbortSignal;
65
+ }
66
+ type MayiArtefactsHook = (context: MayiArtefactsContext) => readonly MayiApprovalArtefact[] | null | undefined | Promise<readonly MayiApprovalArtefact[] | null | undefined>;
67
+ interface MayiChannelConfig {
68
+ readonly getAccessToken: GetAccessToken;
69
+ /** Mayi API origin. Defaults to MAYI_ORIGIN, then https://app.mayi.sh. */
70
+ readonly mayiOrigin?: string;
71
+ /** Explicit HTTPS origin for local development or a tunnel. Never used in production. */
72
+ readonly publicOrigin?: string;
73
+ readonly approvalExpiresInSeconds?: number;
74
+ readonly fetch?: MayiFetch;
75
+ /** Produces evidence before the gated tool executes. Omit to preserve the no-evidence path. */
76
+ readonly artefacts?: MayiArtefactsHook;
77
+ /** Maximum time for one request's hook and uploads. Defaults to 30 seconds. */
78
+ readonly artefactTimeoutMs?: number;
79
+ /** Optional durable duplicate fence. Both hooks must be backed by the same store. */
80
+ readonly eventStore?: MayiWebhookEventStore;
81
+ /** Advanced host/testing injection for Mayi's public webhook JWKS request. */
82
+ readonly webhookFetch?: WebhookVerifierFetch;
83
+ /** Advanced host/testing injection. Normal deployments use the host-provisioned key environment. */
84
+ readonly callbackStateCodec?: CallbackStateCodec;
85
+ /** Advanced host/testing injection for runtimes that do not expose process.env. */
86
+ readonly environment?: MayiEnvironment;
87
+ }
88
+ interface EveInputRequest {
89
+ readonly action: {
90
+ readonly kind: "tool-call";
91
+ readonly toolName: string;
92
+ readonly callId: string;
93
+ readonly input: Record<string, unknown>;
94
+ };
95
+ readonly allowFreeform?: boolean;
96
+ readonly display?: "confirmation" | "select" | "text";
97
+ readonly options?: readonly {
98
+ readonly id: string;
99
+ }[];
100
+ readonly prompt: string;
101
+ readonly requestId: string;
102
+ }
103
+ declare class UnsupportedMayiInputError extends Error {
104
+ readonly requestId: string;
105
+ constructor(requestId: string, display?: string);
106
+ }
107
+ declare function mayiChannel(config: MayiChannelConfig): Channel<MayiChannelState, MayiReceiveTarget>;
108
+
109
+ export { MAYI_CALLBACK_PATH, type MayiChannelConfig, type MayiChannelState, type MayiEnvironment, MayiEveConfigurationError, type MayiEveConfigurationErrorCode, type MayiReceiveTarget, type MayiWebhookEventStore, type ResolvePublicOriginOptions, UnsupportedMayiInputError, mayiChannel, resolvePublicOrigin };
package/dist/index.js ADDED
@@ -0,0 +1,584 @@
1
+ // src/channel.ts
2
+ import {
3
+ CallbackStateError,
4
+ CALLBACK_ACCEPTANCE_WINDOW_SECONDS,
5
+ MAYI_SIGNATURE_HEADER,
6
+ MAX_WEBHOOK_BODY_BYTES,
7
+ MayiClient,
8
+ WebhookConfigurationError,
9
+ WebhookVerificationError,
10
+ createCallbackStateCodec,
11
+ createWebhookVerifier
12
+ } from "@mayiapp/sdk";
13
+ import {
14
+ POST,
15
+ defineChannel
16
+ } from "eve/channels";
17
+
18
+ // src/origin.ts
19
+ var MAYI_CALLBACK_PATH = "/eve/v1/mayi/approval-resolved";
20
+ var MayiEveConfigurationError = class extends Error {
21
+ code;
22
+ constructor(code, message) {
23
+ super(message);
24
+ this.name = "MayiEveConfigurationError";
25
+ this.code = code;
26
+ }
27
+ };
28
+ function runtimeEnvironment() {
29
+ const runtime = globalThis;
30
+ return runtime.process?.env ?? {};
31
+ }
32
+ function hostnameFromEnvironmentUrl(value) {
33
+ if (!value) return void 0;
34
+ try {
35
+ const candidate = value.includes("://") ? value : `https://${value}`;
36
+ return new URL(candidate).hostname.toLowerCase();
37
+ } catch {
38
+ return void 0;
39
+ }
40
+ }
41
+ function isNonPublicHostname(hostname) {
42
+ const lower = hostname.toLowerCase();
43
+ if (lower === "localhost" || lower.endsWith(".localhost") || lower.endsWith(".local") || lower.endsWith(".internal") || !lower.includes(".")) return true;
44
+ return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(lower) || lower.includes(":");
45
+ }
46
+ function normalizePublicHttpsOrigin(value) {
47
+ try {
48
+ const url = new URL(value);
49
+ if (url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash || url.port || isNonPublicHostname(url.hostname)) throw new Error("invalid origin");
50
+ return url.origin;
51
+ } catch {
52
+ throw new MayiEveConfigurationError(
53
+ "INVALID_PUBLIC_ORIGIN",
54
+ "The Eve public origin must be a public HTTPS origin without credentials, a path, query, fragment, or non-default port"
55
+ );
56
+ }
57
+ }
58
+ function assertNotTransientPreview(origin, environment) {
59
+ const hostname = new URL(origin).hostname.toLowerCase();
60
+ const productionHostname = hostnameFromEnvironmentUrl(environment.VERCEL_PROJECT_PRODUCTION_URL);
61
+ const transientHostnames = [environment.VERCEL_URL, environment.VERCEL_BRANCH_URL].map(hostnameFromEnvironmentUrl).filter((item) => item !== void 0 && item !== productionHostname);
62
+ if (transientHostnames.includes(hostname)) {
63
+ throw new MayiEveConfigurationError(
64
+ "PREVIEW_ORIGIN_REFUSED",
65
+ "Transient Vercel preview URLs cannot be used for durable Mayi approval callbacks"
66
+ );
67
+ }
68
+ }
69
+ function resolvePublicOrigin(options = {}) {
70
+ const environment = options.environment ?? runtimeEnvironment();
71
+ if (environment.EVE_PUBLIC_ORIGIN !== void 0) {
72
+ const origin = normalizePublicHttpsOrigin(environment.EVE_PUBLIC_ORIGIN);
73
+ assertNotTransientPreview(origin, environment);
74
+ return origin;
75
+ }
76
+ if (environment.VERCEL_ENV === "production" && environment.VERCEL_PROJECT_PRODUCTION_URL) {
77
+ const value = environment.VERCEL_PROJECT_PRODUCTION_URL;
78
+ const origin = normalizePublicHttpsOrigin(value.includes("://") ? value : `https://${value}`);
79
+ assertNotTransientPreview(origin, environment);
80
+ return origin;
81
+ }
82
+ const production = environment.NODE_ENV === "production" || environment.VERCEL_ENV === "production";
83
+ if (!production && options.developmentOverride !== void 0) {
84
+ const origin = normalizePublicHttpsOrigin(options.developmentOverride);
85
+ assertNotTransientPreview(origin, environment);
86
+ return origin;
87
+ }
88
+ throw new MayiEveConfigurationError(
89
+ "PUBLIC_ORIGIN_UNAVAILABLE",
90
+ "A stable public Eve origin is required; provision EVE_PUBLIC_ORIGIN for this deployment"
91
+ );
92
+ }
93
+ function getRuntimeEnvironment() {
94
+ return runtimeEnvironment();
95
+ }
96
+
97
+ // src/channel.ts
98
+ var DEFAULT_MAYI_ORIGIN = "https://app.mayi.sh";
99
+ var DEFAULT_APPROVAL_EXPIRES_IN_SECONDS = 60 * 60;
100
+ var MAYI_USER_ID_PATTERN = /^[A-Za-z]{12}$/;
101
+ var MAX_CONTINUATION_TOKEN_LENGTH = 4096;
102
+ var MAX_CORRELATION_ID_LENGTH = 512;
103
+ var MAX_EXPIRY_LENGTH = 64;
104
+ var ACCEPTANCE_RECONCILIATION_TIMEOUT_MS = 2e3;
105
+ var DEFAULT_ARTEFACT_TIMEOUT_MS = 3e4;
106
+ var MAX_ARTEFACTS = 20;
107
+ var ARTEFACT_CONCURRENCY = 3;
108
+ var MAX_CALLBACK_BODY_BYTES = MAX_WEBHOOK_BODY_BYTES;
109
+ var Semaphore = class {
110
+ constructor(maximum) {
111
+ this.maximum = maximum;
112
+ }
113
+ maximum;
114
+ active = 0;
115
+ waiting = [];
116
+ async run(task) {
117
+ if (this.active >= this.maximum) await new Promise((resolve) => this.waiting.push(resolve));
118
+ this.active++;
119
+ try {
120
+ return await task();
121
+ } finally {
122
+ this.active--;
123
+ this.waiting.shift()?.();
124
+ }
125
+ }
126
+ };
127
+ var UnsupportedMayiInputError = class extends Error {
128
+ requestId;
129
+ constructor(requestId, display = "unspecified") {
130
+ super(
131
+ `@mayiapp/eve supports only tool approval confirmations; Eve input request "${requestId}" uses unsupported display "${display}"`
132
+ );
133
+ this.name = "UnsupportedMayiInputError";
134
+ this.requestId = requestId;
135
+ }
136
+ };
137
+ function parsePreviousKeys(value) {
138
+ if (!value) return [];
139
+ let parsed;
140
+ try {
141
+ parsed = JSON.parse(value);
142
+ } catch {
143
+ throw new MayiEveConfigurationError(
144
+ "INVALID_CALLBACK_STATE_KEYS",
145
+ "MAYI_CALLBACK_STATE_PREVIOUS_KEYS must be a JSON array of callback-state keys"
146
+ );
147
+ }
148
+ if (!Array.isArray(parsed) || parsed.some((item) => !item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).some((key) => key !== "kid" && key !== "key") || typeof item.kid !== "string" || typeof item.key !== "string")) {
149
+ throw new MayiEveConfigurationError(
150
+ "INVALID_CALLBACK_STATE_KEYS",
151
+ "MAYI_CALLBACK_STATE_PREVIOUS_KEYS must be a JSON array of callback-state keys"
152
+ );
153
+ }
154
+ return parsed;
155
+ }
156
+ async function callbackStateCodecFromEnvironment(environment) {
157
+ const kid = environment.MAYI_CALLBACK_STATE_KEY_ID;
158
+ const key = environment.MAYI_CALLBACK_STATE_KEY;
159
+ if (!kid || !key) {
160
+ throw new MayiEveConfigurationError(
161
+ "CALLBACK_STATE_KEY_UNAVAILABLE",
162
+ "The host must provision MAYI_CALLBACK_STATE_KEY_ID and MAYI_CALLBACK_STATE_KEY"
163
+ );
164
+ }
165
+ try {
166
+ return await createCallbackStateCodec({
167
+ currentKey: { kid, key },
168
+ previousKeys: parsePreviousKeys(environment.MAYI_CALLBACK_STATE_PREVIOUS_KEYS),
169
+ maximumRetryWindowSeconds: CALLBACK_ACCEPTANCE_WINDOW_SECONDS
170
+ });
171
+ } catch (error) {
172
+ if (error instanceof MayiEveConfigurationError) throw error;
173
+ throw new MayiEveConfigurationError(
174
+ "INVALID_CALLBACK_STATE_KEYS",
175
+ "The host-provisioned callback-state key configuration is invalid"
176
+ );
177
+ }
178
+ }
179
+ function createRuntime(config, dependencies = {}) {
180
+ if (!config || typeof config !== "object" || typeof config.getAccessToken !== "function") {
181
+ throw new MayiEveConfigurationError("INVALID_CONFIG", "mayiChannel requires getAccessToken");
182
+ }
183
+ const expiresInSeconds = config.approvalExpiresInSeconds ?? DEFAULT_APPROVAL_EXPIRES_IN_SECONDS;
184
+ if (!Number.isInteger(expiresInSeconds) || expiresInSeconds < 60 || expiresInSeconds > 7 * 24 * 60 * 60) {
185
+ throw new MayiEveConfigurationError(
186
+ "INVALID_CONFIG",
187
+ "approvalExpiresInSeconds must be an integer between 60 and 604800"
188
+ );
189
+ }
190
+ if (config.eventStore !== void 0 && (!config.eventStore || typeof config.eventStore !== "object" || typeof config.eventStore.isProcessed !== "function" || typeof config.eventStore.markProcessed !== "function")) {
191
+ throw new MayiEveConfigurationError(
192
+ "INVALID_CONFIG",
193
+ "eventStore must provide both isProcessed and markProcessed"
194
+ );
195
+ }
196
+ const artefactTimeoutMs = config.artefactTimeoutMs ?? DEFAULT_ARTEFACT_TIMEOUT_MS;
197
+ if (!Number.isInteger(artefactTimeoutMs) || artefactTimeoutMs < 1 || artefactTimeoutMs > 5 * 60 * 1e3) {
198
+ throw new MayiEveConfigurationError(
199
+ "INVALID_CONFIG",
200
+ "artefactTimeoutMs must be an integer between 1 and 300000"
201
+ );
202
+ }
203
+ if (config.artefacts !== void 0 && typeof config.artefacts !== "function") {
204
+ throw new MayiEveConfigurationError("INVALID_CONFIG", "artefacts must be a function");
205
+ }
206
+ const environment = config.environment ?? getRuntimeEnvironment();
207
+ const mayiOrigin = config.mayiOrigin ?? environment.MAYI_ORIGIN ?? DEFAULT_MAYI_ORIGIN;
208
+ const client = new MayiClient({
209
+ origin: mayiOrigin,
210
+ getAccessToken: config.getAccessToken,
211
+ ...config.fetch === void 0 ? {} : { fetch: config.fetch }
212
+ });
213
+ let codecPromise;
214
+ let verifier;
215
+ return {
216
+ ...config.artefacts === void 0 ? {} : { artefacts: config.artefacts },
217
+ artefactSemaphore: new Semaphore(ARTEFACT_CONCURRENCY),
218
+ artefactTimeoutMs,
219
+ client,
220
+ environment,
221
+ ...config.eventStore === void 0 ? {} : { eventStore: config.eventStore },
222
+ expiresInSeconds,
223
+ now: dependencies.now ?? Date.now,
224
+ ...config.publicOrigin === void 0 ? {} : { publicOriginOverride: config.publicOrigin },
225
+ codec() {
226
+ codecPromise ??= config.callbackStateCodec ? Promise.resolve(config.callbackStateCodec) : callbackStateCodecFromEnvironment(environment);
227
+ return codecPromise;
228
+ },
229
+ verifier() {
230
+ verifier ??= createWebhookVerifier({
231
+ mayiOrigin,
232
+ maximumEventAgeSeconds: CALLBACK_ACCEPTANCE_WINDOW_SECONDS,
233
+ now: dependencies.now ?? Date.now,
234
+ ...config.webhookFetch === void 0 ? {} : { fetch: config.webhookFetch },
235
+ ...config.eventStore === void 0 ? {} : { isProcessed: config.eventStore.isProcessed }
236
+ });
237
+ return verifier;
238
+ }
239
+ };
240
+ }
241
+ function isApprovalRequest(request) {
242
+ if (request.display !== "confirmation" || request.allowFreeform === true) return false;
243
+ const optionIds = request.options?.map((option) => option.id) ?? [];
244
+ return optionIds.length === 2 && optionIds.includes("approve") && optionIds.includes("deny");
245
+ }
246
+ function bytesToBase64Url(bytes) {
247
+ let binary = "";
248
+ for (const byte of bytes) binary += String.fromCharCode(byte);
249
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
250
+ }
251
+ async function createMayiIdempotencyKey(sessionId, requestId) {
252
+ const material = new TextEncoder().encode(JSON.stringify(["mayi-eve-v1", sessionId, requestId]));
253
+ const digest = await crypto.subtle.digest("SHA-256", material);
254
+ return `mayi:eve:v1:${bytesToBase64Url(new Uint8Array(digest))}`;
255
+ }
256
+ async function submitOneRequest(request, channel, context, runtime) {
257
+ if (!isApprovalRequest(request)) {
258
+ throw new UnsupportedMayiInputError(request.requestId, request.display);
259
+ }
260
+ const rawContinuationToken = channel.state.rawContinuationToken;
261
+ if (!rawContinuationToken || !channel.state.target) {
262
+ throw new MayiEveConfigurationError(
263
+ "INVALID_CONFIG",
264
+ "The Mayi channel is missing its durable receive state"
265
+ );
266
+ }
267
+ let callbackRequest = channel.state.callbackRequests?.[request.requestId];
268
+ if (!callbackRequest) {
269
+ const now = runtime.now();
270
+ const approvalExpiresAt = new Date(now + runtime.expiresInSeconds * 1e3).toISOString();
271
+ const stateExpiresAt = new Date(
272
+ now + (runtime.expiresInSeconds + CALLBACK_ACCEPTANCE_WINDOW_SECONDS) * 1e3
273
+ ).toISOString();
274
+ const codec = await runtime.codec();
275
+ const state = await codec.seal({
276
+ version: 1,
277
+ rawContinuationToken,
278
+ requestId: request.requestId,
279
+ sessionId: context.session.id,
280
+ expiresAt: stateExpiresAt
281
+ }, { approvalExpiresAt });
282
+ callbackRequest = { state, approvalExpiresAt };
283
+ channel.state.callbackRequests ??= {};
284
+ channel.state.callbackRequests[request.requestId] = callbackRequest;
285
+ }
286
+ const publicOrigin = resolvePublicOrigin({
287
+ environment: runtime.environment,
288
+ ...runtime.publicOriginOverride === void 0 ? {} : { developmentOverride: runtime.publicOriginOverride }
289
+ });
290
+ const idempotencyKey = await createMayiIdempotencyKey(context.session.id, request.requestId);
291
+ const artefactIds = await stageRequestArtefacts(request, channel, context, runtime, idempotencyKey);
292
+ await runtime.client.approvals.request({
293
+ action: request.action,
294
+ explanation: request.prompt,
295
+ expiresInSeconds: runtime.expiresInSeconds,
296
+ ...channel.state.target.mayiUserId === void 0 ? {} : { suggestedApproverId: channel.state.target.mayiUserId },
297
+ callback: {
298
+ url: `${publicOrigin}${MAYI_CALLBACK_PATH}`,
299
+ state: callbackRequest.state
300
+ },
301
+ ...artefactIds === void 0 ? {} : { artefactIds }
302
+ }, { idempotencyKey });
303
+ }
304
+ async function stageRequestArtefacts(request, channel, context, runtime, idempotencyKey) {
305
+ if (!runtime.artefacts) return void 0;
306
+ if (!channel.session || !context.getSandbox) {
307
+ throw new MayiEveConfigurationError("INVALID_CONFIG", "Eve did not provide the artefact hook context");
308
+ }
309
+ const session = channel.session;
310
+ const getSandbox = context.getSandbox;
311
+ const controller = new AbortController();
312
+ const timeout = setTimeout(() => controller.abort(new Error("Mayi artefact preparation timed out")), runtime.artefactTimeoutMs);
313
+ try {
314
+ return await Promise.race([
315
+ runtime.artefactSemaphore.run(async () => {
316
+ const artefacts = await runtime.artefacts({
317
+ request,
318
+ session,
319
+ getSandbox,
320
+ signal: controller.signal
321
+ });
322
+ if (controller.signal.aborted) throw controller.signal.reason;
323
+ if (artefacts == null || artefacts.length === 0) return void 0;
324
+ if (!Array.isArray(artefacts) || artefacts.length > MAX_ARTEFACTS) {
325
+ throw new MayiEveConfigurationError("INVALID_CONFIG", "artefacts must return at most 20 items");
326
+ }
327
+ const ids = [];
328
+ for (const [ordinal, artefact] of artefacts.entries()) {
329
+ if (controller.signal.aborted) throw controller.signal.reason;
330
+ const staged = await runtime.client.stageRequestArtefact(
331
+ idempotencyKey,
332
+ ordinal,
333
+ artefact.filename,
334
+ artefact.mediaType,
335
+ artefact.body,
336
+ {
337
+ signal: controller.signal,
338
+ ...artefact.size === void 0 ? {} : { size: artefact.size }
339
+ }
340
+ );
341
+ ids.push(staged.id);
342
+ }
343
+ return ids;
344
+ }),
345
+ new Promise((_, reject) => {
346
+ controller.signal.addEventListener("abort", () => reject(controller.signal.reason), { once: true });
347
+ })
348
+ ]);
349
+ } finally {
350
+ clearTimeout(timeout);
351
+ }
352
+ }
353
+ function createInputRequestedHandler(runtime) {
354
+ return async (data, channel, context) => {
355
+ const results = await Promise.allSettled(
356
+ data.requests.map((request) => submitOneRequest(request, channel, context, runtime))
357
+ );
358
+ const failures = results.filter((result) => result.status === "rejected").map((result) => result.reason);
359
+ if (failures.length) throw new AggregateError(failures, "One or more Mayi approval requests failed");
360
+ };
361
+ }
362
+ function genericResponse(status, message) {
363
+ return Response.json({ error: message }, {
364
+ status,
365
+ headers: { "cache-control": "no-store" }
366
+ });
367
+ }
368
+ async function readCallbackBody(request) {
369
+ const contentLength = request.headers.get("content-length");
370
+ if (contentLength !== null && (!/^\d+$/.test(contentLength) || Number(contentLength) > MAX_CALLBACK_BODY_BYTES)) throw new Error("Invalid callback request");
371
+ if (!request.body) return "";
372
+ const reader = request.body.getReader();
373
+ const chunks = [];
374
+ let length = 0;
375
+ try {
376
+ while (true) {
377
+ const { done, value } = await reader.read();
378
+ if (done) break;
379
+ length += value.byteLength;
380
+ if (length > MAX_CALLBACK_BODY_BYTES) {
381
+ await reader.cancel();
382
+ throw new Error("Invalid callback request");
383
+ }
384
+ chunks.push(value);
385
+ }
386
+ } finally {
387
+ reader.releaseLock();
388
+ }
389
+ const bytes = new Uint8Array(length);
390
+ let offset = 0;
391
+ for (const chunk of chunks) {
392
+ bytes.set(chunk, offset);
393
+ offset += chunk.byteLength;
394
+ }
395
+ try {
396
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
397
+ } catch {
398
+ throw new Error("Invalid callback request");
399
+ }
400
+ }
401
+ function validBoundedString(value, maximumLength) {
402
+ return typeof value === "string" && value.length > 0 && value.length <= maximumLength;
403
+ }
404
+ function parseContinuationState(value, currentTime) {
405
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).length !== 5 || value.version !== 1 || !validBoundedString(
406
+ value.rawContinuationToken,
407
+ MAX_CONTINUATION_TOKEN_LENGTH
408
+ ) || !validBoundedString(value.requestId, MAX_CORRELATION_ID_LENGTH) || !validBoundedString(value.sessionId, MAX_CORRELATION_ID_LENGTH) || !validBoundedString(value.expiresAt, MAX_EXPIRY_LENGTH)) throw new CallbackStateError("INVALID_STATE");
409
+ const state = value;
410
+ const expiresAt = Date.parse(state.expiresAt);
411
+ if (!Number.isFinite(expiresAt)) throw new CallbackStateError("INVALID_STATE");
412
+ if (expiresAt <= currentTime) throw new CallbackStateError("EXPIRED");
413
+ return state;
414
+ }
415
+ async function requestWasAccepted(getSession, sessionId, requestId) {
416
+ let callId;
417
+ try {
418
+ const stream = await getSession(sessionId).getEventStream();
419
+ const reader = stream.getReader();
420
+ try {
421
+ const deadline = Date.now() + ACCEPTANCE_RECONCILIATION_TIMEOUT_MS;
422
+ while (true) {
423
+ const remaining = deadline - Date.now();
424
+ if (remaining <= 0) {
425
+ await reader.cancel();
426
+ return false;
427
+ }
428
+ let timeout;
429
+ const result = await Promise.race([
430
+ reader.read(),
431
+ new Promise((resolve) => {
432
+ timeout = setTimeout(() => resolve(null), remaining);
433
+ })
434
+ ]).finally(() => clearTimeout(timeout));
435
+ if (result === null) {
436
+ await reader.cancel();
437
+ return false;
438
+ }
439
+ const { done, value } = result;
440
+ if (done) return false;
441
+ if (value.type === "input.requested") {
442
+ const request = value.data.requests.find((candidate) => candidate.requestId === requestId);
443
+ if (request?.action.kind === "tool-call") callId = request.action.callId;
444
+ } else if (callId !== void 0 && value.type === "action.result" && value.data.result.kind === "tool-result" && value.data.result.callId === callId) {
445
+ return true;
446
+ }
447
+ }
448
+ } finally {
449
+ reader.releaseLock();
450
+ }
451
+ } catch {
452
+ return false;
453
+ }
454
+ }
455
+ async function markProcessed(eventId, runtime) {
456
+ if (!runtime.eventStore) return true;
457
+ try {
458
+ await runtime.eventStore.markProcessed(eventId);
459
+ return true;
460
+ } catch {
461
+ return false;
462
+ }
463
+ }
464
+ async function alreadyProcessed(eventId, runtime) {
465
+ if (!runtime.eventStore) return false;
466
+ try {
467
+ return await runtime.eventStore.isProcessed(eventId) === true;
468
+ } catch {
469
+ return false;
470
+ }
471
+ }
472
+ function createApprovalResolvedHandler(runtime) {
473
+ return async (request, args) => {
474
+ let body;
475
+ try {
476
+ body = await readCallbackBody(request);
477
+ } catch {
478
+ return genericResponse(400, "Invalid callback request");
479
+ }
480
+ let verification;
481
+ try {
482
+ verification = await runtime.verifier().verify({
483
+ body,
484
+ signature: request.headers.get(MAYI_SIGNATURE_HEADER)
485
+ });
486
+ } catch (error) {
487
+ if (error instanceof WebhookConfigurationError) {
488
+ return genericResponse(503, "Callback verification is temporarily unavailable");
489
+ }
490
+ if (error instanceof WebhookVerificationError && (error.code === "KEY_SET_UNAVAILABLE" || error.code === "DUPLICATE_CHECK_FAILED")) return genericResponse(503, "Callback verification is temporarily unavailable");
491
+ return genericResponse(401, "Callback verification failed");
492
+ }
493
+ if (verification.duplicate) {
494
+ return new Response(null, { status: 208, headers: { "cache-control": "no-store" } });
495
+ }
496
+ let state;
497
+ let codec;
498
+ try {
499
+ codec = await runtime.codec();
500
+ } catch {
501
+ return genericResponse(503, "Callback state handling is temporarily unavailable");
502
+ }
503
+ try {
504
+ state = parseContinuationState(
505
+ await codec.open(verification.event.state),
506
+ runtime.now()
507
+ );
508
+ } catch {
509
+ return genericResponse(400, "Callback state is invalid");
510
+ }
511
+ const optionId = verification.event.status === "approved" ? "approve" : "deny";
512
+ try {
513
+ const session = await args.send({
514
+ inputResponses: [{ requestId: state.requestId, optionId }]
515
+ }, {
516
+ auth: null,
517
+ continuationToken: state.rawContinuationToken,
518
+ state: { rawContinuationToken: state.rawContinuationToken, target: null }
519
+ });
520
+ if (session.id !== state.sessionId) throw new Error("resume session mismatch");
521
+ } catch {
522
+ if (await alreadyProcessed(verification.event.id, runtime)) {
523
+ return new Response(null, { status: 208, headers: { "cache-control": "no-store" } });
524
+ }
525
+ if (!await requestWasAccepted(args.getSession, state.sessionId, state.requestId)) {
526
+ return genericResponse(503, "Eve did not accept the callback resume");
527
+ }
528
+ if (!await markProcessed(verification.event.id, runtime)) {
529
+ return genericResponse(503, "Callback acknowledgement is temporarily unavailable");
530
+ }
531
+ return new Response(null, { status: 208, headers: { "cache-control": "no-store" } });
532
+ }
533
+ if (!await markProcessed(verification.event.id, runtime)) {
534
+ return genericResponse(503, "Callback acknowledgement is temporarily unavailable");
535
+ }
536
+ return new Response(null, { status: 202, headers: { "cache-control": "no-store" } });
537
+ };
538
+ }
539
+ function createRawContinuationToken() {
540
+ if (!globalThis.crypto || typeof globalThis.crypto.randomUUID !== "function") {
541
+ throw new MayiEveConfigurationError("INVALID_CONFIG", "A cryptographically secure random UUID source is required");
542
+ }
543
+ return `mayi:${globalThis.crypto.randomUUID()}`;
544
+ }
545
+ function mayiChannel(config) {
546
+ const runtime = createRuntime(config);
547
+ const inputRequested = createInputRequestedHandler(runtime);
548
+ return defineChannel({
549
+ kindHint: "mayi",
550
+ state: { rawContinuationToken: null, target: null, callbackRequests: {} },
551
+ context(state, session) {
552
+ return {
553
+ state,
554
+ session: { id: session.id, continuationToken: session.continuationToken, auth: session.auth }
555
+ };
556
+ },
557
+ routes: [POST(MAYI_CALLBACK_PATH, createApprovalResolvedHandler(runtime))],
558
+ async receive(input, { send }) {
559
+ const mayiUserId = input.target.mayiUserId;
560
+ if (mayiUserId !== void 0 && !MAYI_USER_ID_PATTERN.test(mayiUserId)) {
561
+ throw new MayiEveConfigurationError(
562
+ "INVALID_RECEIVE_TARGET",
563
+ "target.mayiUserId must be exactly 12 ASCII letters"
564
+ );
565
+ }
566
+ const rawContinuationToken = createRawContinuationToken();
567
+ const target = mayiUserId === void 0 ? {} : { mayiUserId };
568
+ return send(input.message, {
569
+ auth: input.auth,
570
+ continuationToken: rawContinuationToken,
571
+ state: { rawContinuationToken, target, callbackRequests: {} }
572
+ });
573
+ },
574
+ events: { "input.requested": inputRequested }
575
+ });
576
+ }
577
+ export {
578
+ MAYI_CALLBACK_PATH,
579
+ MayiEveConfigurationError,
580
+ UnsupportedMayiInputError,
581
+ mayiChannel,
582
+ resolvePublicOrigin
583
+ };
584
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["https://raw.githubusercontent.com/asiraky/mayi/main/packages/eve/src/channel.ts","https://raw.githubusercontent.com/asiraky/mayi/main/packages/eve/src/origin.ts"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@mayiapp/eve",
3
+ "version": "0.1.0",
4
+ "description": "Durable Mayi approval channel for Eve agents",
5
+ "type": "module",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "sideEffects": false,
20
+ "license": "Apache-2.0",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/asiraky/mayi",
24
+ "directory": "packages/eve"
25
+ },
26
+ "engines": {
27
+ "node": ">=24"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "@mayiapp/sdk": "^0.1.0"
34
+ },
35
+ "peerDependencies": {
36
+ "eve": "0.24.2"
37
+ },
38
+ "devDependencies": {
39
+ "eve": "0.24.2",
40
+ "tsup": "^8.5.1",
41
+ "typescript": "^5.9.0",
42
+ "zod": "^4.4.3"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup && node ./scripts/normalize-source-maps.mjs",
46
+ "test:harness": "cd test-fixtures/approval-harness && eve eval --skip-report --timeout 30000 --max-concurrency 1",
47
+ "test:package": "node ./scripts/test-package.mjs",
48
+ "typecheck": "tsc --noEmit -p tsconfig.json"
49
+ }
50
+ }