@intentic/sandbox-contract 1.220.0 → 1.222.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/agent-catalog.d.ts +1 -0
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +1 -0
- package/dist/agent-catalog.js.map +1 -1
- package/dist/contracts/endpoints.contract.d.ts +2 -0
- package/dist/contracts/endpoints.contract.d.ts.map +1 -1
- package/dist/contracts/endpoints.contract.js +1 -0
- package/dist/contracts/endpoints.contract.js.map +1 -1
- 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 +10 -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/agent-catalog.ts +16 -0
- package/src/contracts/endpoints.contract.ts +11 -0
- 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.222.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/
|
|
93
|
-
"@intentic/extension-manifest": "1.
|
|
94
|
-
"@intentic/
|
|
92
|
+
"@intentic/constants": "1.222.0",
|
|
93
|
+
"@intentic/extension-manifest": "1.222.0",
|
|
94
|
+
"@intentic/registry": "1.222.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@types/node": "24.13.2",
|
package/src/agent-catalog.ts
CHANGED
|
@@ -115,6 +115,22 @@ export const ACCESS_COST: Record<AccessKind, number> = { free: 0, subscription:
|
|
|
115
115
|
export const TRIAL_ENDPOINT_ID = "free-trial";
|
|
116
116
|
export const TRIAL_PROVIDER = "endpoint/free-trial";
|
|
117
117
|
export const isTrialProvider = (provider: AgentProvider): boolean => provider === TRIAL_PROVIDER;
|
|
118
|
+
|
|
119
|
+
/* THE ONLY MODEL THE TRIAL PUBLISHES — a synthetic id, not one of Google's, and that is the point.
|
|
120
|
+
*
|
|
121
|
+
* The trial used to publish whatever the upstream listed. Two things were wrong with that and neither could be
|
|
122
|
+
* fixed by filtering harder. Google lists ~54 models on a fresh key and declares `generateContent` for many that
|
|
123
|
+
* cannot serve an agent turn — deep-research, antigravity, gemma, robotics and computer-use previews all pass a
|
|
124
|
+
* capability check and then fail the first message — so the picker was full of rows whose only outcome was an
|
|
125
|
+
* error. And the list MOVED: the translator's routing table is written at boot and on capability edits, while
|
|
126
|
+
* the picker re-reads the catalog every minute, so a model discovered in between was pickable and unroutable,
|
|
127
|
+
* refused with "unknown provider for model".
|
|
128
|
+
*
|
|
129
|
+
* One id, never changing, ends both. There is nothing to filter because nothing is discovered, and the routing
|
|
130
|
+
* table cannot drift from a list of one constant. WHICH real model answers is decided per message by the
|
|
131
|
+
* platform, which is the only party that can see which of its keys still has quota on which model — the sandbox
|
|
132
|
+
* cannot, and a user choosing blind between rows they know nothing about was never a choice worth offering. */
|
|
133
|
+
export const TRIAL_MODEL_ID = "auto";
|
|
118
134
|
// What the picker calls it, and the sentence the surfaces put underneath. One wording, so the composer's notice
|
|
119
135
|
// and the picker's row cannot end up describing different bargains.
|
|
120
136
|
export const TRIAL_LABEL = "Free trial";
|
|
@@ -29,6 +29,17 @@ export const TrialStatusSchema = z.object({
|
|
|
29
29
|
resetsAt: z.string().optional(),
|
|
30
30
|
// Earliest known time a quarantined upstream key can be tried again.
|
|
31
31
|
retryAt: z.string().optional(),
|
|
32
|
+
/* THE REAL MODEL BEHIND THE TRIAL'S ONE PUBLISHED ID, on this account's most recent message.
|
|
33
|
+
*
|
|
34
|
+
* The trial routes per message across a ladder of models (the platform's trial-ladder.ts), so the id the
|
|
35
|
+
* user selected names the trial rather than what answered them. Telling them which model ran is what keeps
|
|
36
|
+
* that from being a black box: an answer that reads as weak has a visible cause, and a bug report can name
|
|
37
|
+
* the model instead of guessing at one.
|
|
38
|
+
*
|
|
39
|
+
* It arrives on the STATUS poll rather than in the turn's own stream because the translator sits between the
|
|
40
|
+
* platform and this daemon and does not forward response headers. The client refreshes this when a trial
|
|
41
|
+
* turn ends, so in practice it is the model that served the turn just finished. Absent until one has been. */
|
|
42
|
+
servedModel: z.string().optional(),
|
|
32
43
|
});
|
|
33
44
|
export type TrialStatusResponse = z.infer<typeof TrialStatusSchema>;
|
|
34
45
|
|
|
@@ -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
|