@intentic/sandbox-contract 1.219.0 → 1.221.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/dist/contracts/push.contract.d.ts +9 -3
- package/dist/contracts/push.contract.d.ts.map +1 -1
- package/dist/contracts/push.contract.js +3 -3
- package/dist/contracts/push.contract.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/schemas.d.ts +28 -5
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +13 -6
- package/dist/schemas.js.map +1 -1
- package/package.json +4 -4
- package/src/contracts/push.contract.ts +8 -7
- package/src/schemas.ts +43 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/sandbox-contract",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.221.0",
|
|
4
4
|
"description": "oRPC wire contract for the intentic sandbox daemon — shared by the daemon and its browser client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -89,9 +89,9 @@
|
|
|
89
89
|
"@orpc/contract": "1.14.13",
|
|
90
90
|
"tslib": "2.8.1",
|
|
91
91
|
"zod": "4.4.3",
|
|
92
|
-
"@intentic/constants": "1.
|
|
93
|
-
"@intentic/
|
|
94
|
-
"@intentic/
|
|
92
|
+
"@intentic/constants": "1.221.0",
|
|
93
|
+
"@intentic/extension-manifest": "1.221.0",
|
|
94
|
+
"@intentic/registry": "1.221.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@types/node": "24.13.2",
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
|
-
import { OkSchema,
|
|
2
|
+
import { OkSchema, PushChannelIdSchema, PushChannelSchema, PushConfigQuerySchema, PushConfigSchema, PushTestSchema } from "../schemas.js";
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Push notifications for this sandbox. The daemon owns the VAPID keypair and the channel list (see
|
|
5
5
|
// push/push-store.ts for why the key lives on the history volume), and sends on the three moments where the
|
|
6
6
|
// operator's attention is genuinely wanted: a turn finished, the agent is blocked on an answer, and an
|
|
7
|
-
// automation is waiting for approval.
|
|
7
|
+
// automation is waiting for approval. A channel is either a browser's web-push subscription or a native
|
|
8
|
+
// install reached through the platform's push relay — see PushChannelSchema for the split and why.
|
|
8
9
|
//
|
|
9
10
|
// `test` exists because a notification pipeline has four independent failure points the user cannot inspect
|
|
10
|
-
// (
|
|
11
|
-
// that proves the whole chain end-to-end is worth more than any amount of status rendering.
|
|
11
|
+
// (device permission, service-worker or shell registration, the daemon's key, the push service itself) — a
|
|
12
|
+
// button that proves the whole chain end-to-end is worth more than any amount of status rendering.
|
|
12
13
|
export const pushContract = {
|
|
13
14
|
config: oc.route({ method: "GET", path: "/push/config" }).input(PushConfigQuerySchema).output(PushConfigSchema),
|
|
14
|
-
subscribe: oc.route({ method: "POST", path: "/push/subscribe" }).input(
|
|
15
|
-
unsubscribe: oc.route({ method: "POST", path: "/push/unsubscribe" }).input(
|
|
15
|
+
subscribe: oc.route({ method: "POST", path: "/push/subscribe" }).input(PushChannelSchema).output(OkSchema),
|
|
16
|
+
unsubscribe: oc.route({ method: "POST", path: "/push/unsubscribe" }).input(PushChannelIdSchema).output(OkSchema),
|
|
16
17
|
test: oc.route({ method: "POST", path: "/push/test" }).output(PushTestSchema),
|
|
17
18
|
};
|
package/src/schemas.ts
CHANGED
|
@@ -3619,9 +3619,7 @@ export const EndpointConfigSchema = z.object({
|
|
|
3619
3619
|
* `allow`/`deny` are hostname lists (comma- or newline-separated). Empty allow = any host, each behind its
|
|
3620
3620
|
* card; deny wins over allow. One capability per sandbox (singleton card): a second balance would just be a
|
|
3621
3621
|
* second opinion about the same owner's wallet. */
|
|
3622
|
-
const usdAmount = z
|
|
3623
|
-
.string()
|
|
3624
|
-
.regex(/^\d+(\.\d{1,6})?$/, "a USD amount like 0.50 (up to six decimals — USDC's own precision)");
|
|
3622
|
+
const usdAmount = z.string().regex(/^\d+(\.\d{1,6})?$/, "a USD amount like 0.50 (up to six decimals — USDC's own precision)");
|
|
3625
3623
|
export const WalletNetworkSchema = z.enum(["eip155:8453", "eip155:84532"]);
|
|
3626
3624
|
export type WalletNetwork = z.infer<typeof WalletNetworkSchema>;
|
|
3627
3625
|
export const WalletConfigSchema = z.object({
|
|
@@ -6680,15 +6678,23 @@ export const PresenceReportSchema = z.object({
|
|
|
6680
6678
|
});
|
|
6681
6679
|
export type PresenceReport = z.infer<typeof PresenceReportSchema>;
|
|
6682
6680
|
|
|
6683
|
-
// ---- push:
|
|
6684
|
-
// The daemon is the only tier that knows what the agent is doing, so it is the sender.
|
|
6685
|
-
// per-
|
|
6686
|
-
//
|
|
6687
|
-
//
|
|
6681
|
+
// ---- push: notifications to the owner's devices ----
|
|
6682
|
+
// The daemon is the only tier that knows what the agent is doing, so it is the sender. A registration is
|
|
6683
|
+
// per-DEVICE and comes in two kinds, distinguished by who can be posted to directly:
|
|
6684
|
+
// webpush a browser (including the Android TWA, which IS Chrome). The endpoint is minted by that
|
|
6685
|
+
// browser's push service and the daemon sends to it directly, end-to-end encrypted.
|
|
6686
|
+
// relay a native app install (the iOS shell), whose OS push service (APNs) only accepts sends from
|
|
6687
|
+
// the app's vendor. The daemon posts plain JSON to the platform's push relay, which holds the
|
|
6688
|
+
// vendor credential and forwards. The payload transits the relay readable — the price of Apple
|
|
6689
|
+
// requiring the vendor in the loop — which is why the channel records WHERE to post (`url`)
|
|
6690
|
+
// rather than the daemon knowing any platform by name.
|
|
6691
|
+
// Channels live here and not on the platform because the daemon is on the command path: the platform would
|
|
6692
|
+
// have to be told about every turn to be useful.
|
|
6688
6693
|
|
|
6689
6694
|
// A browser's PushSubscription, in the exact shape `web-push` consumes — the browser produces it via
|
|
6690
6695
|
// PushManager.subscribe() and the client posts it back verbatim, so the daemon never reshapes it.
|
|
6691
|
-
export const
|
|
6696
|
+
export const WebPushChannelSchema = z.object({
|
|
6697
|
+
kind: z.literal("webpush"),
|
|
6692
6698
|
endpoint: z.url(),
|
|
6693
6699
|
keys: z.object({
|
|
6694
6700
|
// The client's public key and auth secret for payload encryption (RFC 8291). Opaque base64url here.
|
|
@@ -6696,7 +6702,27 @@ export const PushSubscriptionSchema = z.object({
|
|
|
6696
6702
|
auth: z.string().min(1),
|
|
6697
6703
|
}),
|
|
6698
6704
|
});
|
|
6699
|
-
export type
|
|
6705
|
+
export type WebPushChannel = z.infer<typeof WebPushChannelSchema>;
|
|
6706
|
+
|
|
6707
|
+
// A native install, addressed through a push relay. `secret` is the send capability the relay minted at
|
|
6708
|
+
// registration — the daemon proves it may notify this device by presenting it; the relay never learns which
|
|
6709
|
+
// sandbox is calling. `deviceId` doubles as the channel's identity (see channelId below).
|
|
6710
|
+
export const RelayChannelSchema = z.object({
|
|
6711
|
+
kind: z.literal("relay"),
|
|
6712
|
+
// The absolute URL the daemon POSTs a send to — minted by the relay at registration, stored verbatim.
|
|
6713
|
+
url: z.url(),
|
|
6714
|
+
deviceId: z.string().min(1),
|
|
6715
|
+
secret: z.string().min(1),
|
|
6716
|
+
});
|
|
6717
|
+
export type RelayChannel = z.infer<typeof RelayChannelSchema>;
|
|
6718
|
+
|
|
6719
|
+
export const PushChannelSchema = z.discriminatedUnion("kind", [WebPushChannelSchema, RelayChannelSchema]);
|
|
6720
|
+
export type PushChannel = z.infer<typeof PushChannelSchema>;
|
|
6721
|
+
|
|
6722
|
+
// The one identity every push route speaks: subscribe upserts by it, unsubscribe and the config probe name
|
|
6723
|
+
// devices by it. Shape-derived so the daemon and the web app can never disagree about what identifies a row —
|
|
6724
|
+
// a browser is its push endpoint, a native install is the deviceId its relay registration minted.
|
|
6725
|
+
export const channelId = (channel: PushChannel): string => (channel.kind === "webpush" ? channel.endpoint : channel.deviceId);
|
|
6700
6726
|
|
|
6701
6727
|
// What the service worker renders. `url` is the in-app route the notification opens (the click handler
|
|
6702
6728
|
// focuses an existing tab there rather than spawning a new one); `tag` collapses repeats — a second
|
|
@@ -6715,14 +6741,14 @@ export const PushNotificationSchema = z.object({
|
|
|
6715
6741
|
});
|
|
6716
6742
|
export type PushNotification = z.infer<typeof PushNotificationSchema>;
|
|
6717
6743
|
|
|
6718
|
-
// The VAPID public key a browser needs to subscribe
|
|
6719
|
-
// so the settings toggle can render its true state instead of trusting
|
|
6720
|
-
// granted permission with no
|
|
6744
|
+
// The VAPID public key a browser needs to subscribe (native shells ignore it), plus whether the asking
|
|
6745
|
+
// device's channel is already known — so the settings toggle can render its true state instead of trusting
|
|
6746
|
+
// the device's permission alone (a granted permission with no daemon-side row would notify nothing).
|
|
6721
6747
|
export const PushConfigSchema = z.object({ publicKey: z.string(), subscribed: z.boolean() });
|
|
6722
|
-
export const
|
|
6723
|
-
// The optional `
|
|
6724
|
-
// sandbox as a whole, which is never the question the settings toggle needs answered.
|
|
6725
|
-
export const PushConfigQuerySchema = z.object({
|
|
6748
|
+
export const PushChannelIdSchema = z.object({ id: z.string().min(1) });
|
|
6749
|
+
// The optional `id` says WHICH device is asking (see channelId); without it `subscribed` could only speak
|
|
6750
|
+
// for the sandbox as a whole, which is never the question the settings toggle needs answered.
|
|
6751
|
+
export const PushConfigQuerySchema = z.object({ id: z.string().min(1).optional() });
|
|
6726
6752
|
|
|
6727
6753
|
// What a test send actually achieved. `{ ok: true }` would be a lie the one place it matters most: the button
|
|
6728
6754
|
// exists to prove a chain the user cannot inspect, so "the daemon accepted the request" is not the answer to
|