@oxy.so/contracts 4.8.0 → 4.9.1

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 (60) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/accountGraph.js +13 -5
  3. package/dist/cjs/accountSubject.js +189 -0
  4. package/dist/cjs/agency.js +56 -4
  5. package/dist/cjs/agentKeys.js +67 -0
  6. package/dist/cjs/capabilityBindings.js +72 -0
  7. package/dist/cjs/index.js +60 -13
  8. package/dist/cjs/inference/decisions.js +41 -0
  9. package/dist/cjs/inference/economics.js +135 -0
  10. package/dist/cjs/products/access.js +60 -0
  11. package/dist/cjs/products/billing.js +32 -0
  12. package/dist/cjs/signIn.js +1 -1
  13. package/dist/esm/.tsbuildinfo +1 -1
  14. package/dist/esm/accountGraph.js +13 -5
  15. package/dist/esm/accountSubject.js +182 -0
  16. package/dist/esm/agency.js +55 -3
  17. package/dist/esm/agentKeys.js +63 -0
  18. package/dist/esm/capabilityBindings.js +67 -0
  19. package/dist/esm/index.js +10 -1
  20. package/dist/esm/inference/decisions.js +41 -0
  21. package/dist/esm/inference/economics.js +132 -0
  22. package/dist/esm/products/access.js +57 -0
  23. package/dist/esm/products/billing.js +29 -0
  24. package/dist/esm/signIn.js +1 -1
  25. package/dist/types/.tsbuildinfo +1 -1
  26. package/dist/types/accountGraph.d.ts +23 -8
  27. package/dist/types/accountSubject.d.ts +210 -0
  28. package/dist/types/agency.d.ts +956 -190
  29. package/dist/types/agentKeys.d.ts +525 -0
  30. package/dist/types/capabilityBindings.d.ts +6 -0
  31. package/dist/types/deviceDirectory.d.ts +50 -50
  32. package/dist/types/deviceSession.d.ts +14 -14
  33. package/dist/types/externalIdentity.d.ts +4 -4
  34. package/dist/types/identity.d.ts +2 -2
  35. package/dist/types/index.d.ts +12 -2
  36. package/dist/types/inference/accountBilling.d.ts +30 -30
  37. package/dist/types/inference/aliaModelRelease.d.ts +83 -83
  38. package/dist/types/inference/catalogue.d.ts +180 -180
  39. package/dist/types/inference/economics.d.ts +376 -0
  40. package/dist/types/inference/entitlement.d.ts +47 -47
  41. package/dist/types/inference/inbox.d.ts +12 -12
  42. package/dist/types/inference/modelDocumentation.d.ts +231 -231
  43. package/dist/types/inference/priceVersion.d.ts +4 -4
  44. package/dist/types/inference/providerConnection.d.ts +38 -38
  45. package/dist/types/inference/realtime.d.ts +802 -802
  46. package/dist/types/inference/request.d.ts +844 -844
  47. package/dist/types/inference/routingPolicy.d.ts +4 -4
  48. package/dist/types/inference/scopedExecution.d.ts +38 -38
  49. package/dist/types/inference/streamEvents.d.ts +4 -4
  50. package/dist/types/inference/usage.d.ts +12 -12
  51. package/dist/types/keyRotation.d.ts +2 -2
  52. package/dist/types/notifications.d.ts +4 -4
  53. package/dist/types/oauth.d.ts +30 -30
  54. package/dist/types/products/access.d.ts +487 -0
  55. package/dist/types/products/billing.d.ts +563 -0
  56. package/dist/types/recommendations.d.ts +2 -2
  57. package/dist/types/sessionStatus.d.ts +10 -10
  58. package/dist/types/signIn.d.ts +6 -6
  59. package/dist/types/transparency.d.ts +2 -2
  60. package/package.json +1 -1
@@ -58,6 +58,12 @@ export const childAccountKindSchema = z.enum(CHILD_ACCOUNT_KINDS);
58
58
  *
59
59
  * Consumers must gate on this predicate rather than testing `kind === 'personal'`,
60
60
  * which silently admits every kind added after it was written.
61
+ *
62
+ * Being act-as-eligible is NOT being without an identity of one's own. A bot is
63
+ * act-as-eligible AND its own actor (`accountKindActorNature` → `'agent'` in
64
+ * `accountSubject.ts`): an application acting as it on a person's authority
65
+ * records that person as the actor, and the bot acting with nobody operating it
66
+ * records the bot. Neither ever records the bot's owner in the bot's place.
61
67
  */
62
68
  export function isDelegatedActAsEligibleKind(kind) {
63
69
  return kind === 'organization' || kind === 'project' || kind === 'bot';
@@ -71,11 +77,13 @@ export function isDelegatedActAsEligibleKind(kind) {
71
77
  * The two differ on exactly one kind, `bot`, and that difference is the whole
72
78
  * reason both exist.
73
79
  *
74
- * **A bot is not something you become. It is something that operates on your
75
- * behalf.** Its whole purpose is to act while nobody is present: an application
76
- * holds a credential, names the human whose authority it borrows, and speaks as
77
- * the bot. That is delegation, and it is what
78
- * {@link isDelegatedActAsEligibleKind} admits it for.
80
+ * **A bot is not something you become. It is somebody of its own.** It is an
81
+ * agent's identity, with its own resources, roles, plan and balance, and it is
82
+ * the actor of what it does unoperated. A person may still act AS it on their
83
+ * own authority — that is delegation, recorded with the person as actor, and it
84
+ * is what {@link isDelegatedActAsEligibleKind} admits it for. What a person
85
+ * never does is occupy its seat. This is a refusal of a SEAT, not a capability
86
+ * restriction: nothing a bot may do depends on this predicate.
79
87
  *
80
88
  * Handing a person the bot's seat instead inverts that. It puts a human inside
81
89
  * the identity that exists to act without one, and it does so on the human's own
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Account subject contract — what an account IS, kept apart from how it proves
3
+ * itself, who may become it, who may act as it, who acted, and whose money moves.
4
+ *
5
+ * `AccountKind` (`accountGraph.ts`) names the nature of an account. Five other
6
+ * questions are routinely asked of an account, and each has its own answer here
7
+ * or in `accountGraph.ts`. None of them may be answered by testing `kind`
8
+ * directly at a call site:
9
+ *
10
+ * | question | answered by |
11
+ * |-------------------------------------------|------------------------------------------|
12
+ * | may a PERSON switch into it? | `isOperatorSwitchTargetKind` |
13
+ * | may someone act AS it on their authority? | `isDelegatedActAsEligibleKind` |
14
+ * | who is the ACTOR when it acts unoperated? | {@link accountKindActorNature} |
15
+ * | who acted, and as whom, on this request? | {@link AccountActorChain} |
16
+ * | whose money / receipt is an effect? | {@link attributeFinancialEffect} |
17
+ * | roles, resources, plan, balance, payer | NOT kind — {@link KIND_INDEPENDENT_ACCOUNT_DIMENSIONS} |
18
+ *
19
+ * ## A bot is a complete account (issue #1520, I01)
20
+ *
21
+ * A `bot` is an AI agent's own identity. It owns resources, holds roles, has a
22
+ * plan, holds a balance, receives funds and pays for itself under exactly the
23
+ * rules a personal account does. Nothing commercial derives from it being a bot,
24
+ * and nothing about it requires its owner's approval merely because it is one.
25
+ *
26
+ * Two things DO differ, and both are about seats, not capability:
27
+ *
28
+ * - a person never switches INTO a bot (`isOperatorSwitchTargetKind`): a bot is
29
+ * somebody, not a seat a human occupies;
30
+ * - when a bot acts with nobody operating it, the actor recorded is the BOT
31
+ * ({@link accountKindActorNature} `'agent'`), never its owner.
32
+ */
33
+ import { z } from 'zod';
34
+ import { oxyAccountIdSchema } from './inference/identifiers.js';
35
+ /** Bumped when any shape in this module changes incompatibly. */
36
+ export const ACCOUNT_SUBJECT_CONTRACT_VERSION = 1;
37
+ // ===========================================================================
38
+ // Actor nature
39
+ // ===========================================================================
40
+ /**
41
+ * Who is recorded as the ACTOR when an account of this kind acts and no other
42
+ * account is operating it.
43
+ *
44
+ * - `person` — a human; a personal account is its own actor.
45
+ * - `agent` — an AI agent; a bot is its own actor (ADR 0018 `ActorRef`
46
+ * `{type:'agent'}`), and audit records the bot, never the human who owns it.
47
+ * - `operated` — never its own actor. An organization, project or channel only
48
+ * acts through a member, and that member is the actor.
49
+ */
50
+ export const ACCOUNT_ACTOR_NATURES = ['person', 'agent', 'operated'];
51
+ export const accountActorNatureSchema = z.enum(ACCOUNT_ACTOR_NATURES);
52
+ /**
53
+ * The actor nature of a kind. A `Record` over the full union, so a new kind does
54
+ * not compile until somebody decides this for it.
55
+ */
56
+ const ACTOR_NATURE_BY_KIND = {
57
+ personal: 'person',
58
+ bot: 'agent',
59
+ organization: 'operated',
60
+ project: 'operated',
61
+ channel: 'operated',
62
+ };
63
+ /** `null` for a value that is not an account kind. Never a guess. */
64
+ export function accountKindActorNature(kind) {
65
+ if (!kind || !Object.prototype.hasOwnProperty.call(ACTOR_NATURE_BY_KIND, kind)) {
66
+ return null;
67
+ }
68
+ return ACTOR_NATURE_BY_KIND[kind];
69
+ }
70
+ /** Whether an account of this kind can be the actor of its own actions. */
71
+ export function accountKindActsAsItself(kind) {
72
+ const nature = accountKindActorNature(kind);
73
+ return nature === 'person' || nature === 'agent';
74
+ }
75
+ // ===========================================================================
76
+ // Kind-independent dimensions
77
+ // ===========================================================================
78
+ /**
79
+ * What an account's KIND never decides.
80
+ *
81
+ * There is deliberately no predicate taking a kind for any of these. A member's
82
+ * role grants the same permissions whatever kind the member is; a plan, a
83
+ * balance, the payer and the beneficiary are resolved from billing state and
84
+ * membership, never from `kind`. A bot and a person with the same role and plan
85
+ * get the same action and the same commercial treatment.
86
+ *
87
+ * Listed so a consumer can cite the rule, and so the API's static guard
88
+ * (`commercialTreatmentIgnoresAccountKind.test.ts`) has a single source.
89
+ */
90
+ export const KIND_INDEPENDENT_ACCOUNT_DIMENSIONS = [
91
+ 'roles',
92
+ 'resources',
93
+ 'plan',
94
+ 'balance',
95
+ 'payer',
96
+ 'beneficiary',
97
+ ];
98
+ // ===========================================================================
99
+ // Actor chain
100
+ // ===========================================================================
101
+ /**
102
+ * Who acted, and as whom, for one authenticated request.
103
+ *
104
+ * - `effectiveAccountId` — the account the request speaks AS (token `sub`).
105
+ * - `actorAccountId` — who actually acted (token `act.sub`): the human operating
106
+ * a managed account, or the account itself when nobody operates it.
107
+ * - `delegated` — exactly `actorAccountId !== effectiveAccountId`. Carried as
108
+ * its own field so a consumer reads a fact instead of re-deriving it, and
109
+ * refined so the two can never disagree.
110
+ *
111
+ * Produced ONLY by the session authority (Oxy's `/session/validate*`) from the
112
+ * session ROW. A header or an unverified token claim never produces one: that is
113
+ * what keeps a modified header from changing the actor.
114
+ */
115
+ export const accountActorChainSchema = z
116
+ .object({
117
+ schemaVersion: z.literal(ACCOUNT_SUBJECT_CONTRACT_VERSION),
118
+ effectiveAccountId: oxyAccountIdSchema,
119
+ actorAccountId: oxyAccountIdSchema,
120
+ delegated: z.boolean(),
121
+ })
122
+ .strict()
123
+ .refine((chain) => chain.delegated === (chain.actorAccountId !== chain.effectiveAccountId), {
124
+ message: 'delegated must be true exactly when the actor differs from the effective account',
125
+ path: ['delegated'],
126
+ });
127
+ /**
128
+ * Build the chain from a session's binding: its subject and the operator
129
+ * recorded on it (`sessions.operated_by_user_id`), if any.
130
+ *
131
+ * The one constructor, so producers cannot each invent a rule. An operator equal
132
+ * to the subject is not a delegation.
133
+ */
134
+ export function accountActorChainFromSession(binding) {
135
+ const actor = binding.operatedByAccountId || binding.subjectAccountId;
136
+ return accountActorChainSchema.parse({
137
+ schemaVersion: ACCOUNT_SUBJECT_CONTRACT_VERSION,
138
+ effectiveAccountId: binding.subjectAccountId,
139
+ actorAccountId: actor,
140
+ delegated: actor !== binding.subjectAccountId,
141
+ });
142
+ }
143
+ // ===========================================================================
144
+ // Financial subject
145
+ // ===========================================================================
146
+ /** `debit` — the subject pays; `credit` — the subject receives. */
147
+ export const FINANCIAL_EFFECT_DIRECTIONS = ['debit', 'credit'];
148
+ /**
149
+ * Whose money a financial effect is, and who caused it.
150
+ *
151
+ * `subjectAccountId` is the account the effect BELONGS to — the payer of a
152
+ * debit, the beneficiary of a credit — and it is always the EFFECTIVE account of
153
+ * the actor chain. Never the operator: a person paying from the bot they operate
154
+ * spends the bot's money, and the receipt names the bot as payer and the person
155
+ * as actor. Never the owner: a bot paying for itself is recorded as the bot.
156
+ *
157
+ * What this does NOT decide is which balance row FUNDS a debit. That is the
158
+ * billing walk's answer (ADR 0014: the account's own active profile, else its
159
+ * nearest ancestor's) and it is kind-independent; this contract only fixes the
160
+ * subject and the actor the receipt must carry. Moving money is not done here.
161
+ */
162
+ export const financialEffectAttributionSchema = z
163
+ .object({
164
+ schemaVersion: z.literal(ACCOUNT_SUBJECT_CONTRACT_VERSION),
165
+ direction: z.enum(FINANCIAL_EFFECT_DIRECTIONS),
166
+ subjectAccountId: oxyAccountIdSchema,
167
+ actor: accountActorChainSchema,
168
+ })
169
+ .strict()
170
+ .refine((effect) => effect.subjectAccountId === effect.actor.effectiveAccountId, {
171
+ message: 'a financial effect belongs to the effective account, never the operator',
172
+ path: ['subjectAccountId'],
173
+ });
174
+ /** Attribute a payment or a receipt to the subject the actor chain speaks as. */
175
+ export function attributeFinancialEffect(direction, actor) {
176
+ return financialEffectAttributionSchema.parse({
177
+ schemaVersion: ACCOUNT_SUBJECT_CONTRACT_VERSION,
178
+ direction,
179
+ subjectAccountId: actor.effectiveAccountId,
180
+ actor,
181
+ });
182
+ }
@@ -35,6 +35,23 @@ export const actorRefSchema = z.discriminatedUnion('type', [
35
35
  accountId: identifierSchema,
36
36
  }).strict(),
37
37
  ]);
38
+ /** Present requester only; standing grants/automation retain actorRefSchema. */
39
+ export const executionActorRefSchema = z.discriminatedUnion('type', [
40
+ ...actorRefSchema.options,
41
+ z.object({ type: z.literal('requester'), accountId: identifierSchema }).strict(),
42
+ ]);
43
+ /** Separate bearer lives only in the SDK option / authority request body. */
44
+ export const foregroundExecutionAuthorizationInputSchema = z.object({
45
+ tool: z.enum(['recommendProfiles', 'readViewerGraph']),
46
+ expectedCatalog: z.object({
47
+ registrationId: identifierSchema,
48
+ version: identifierSchema,
49
+ digest: sha256HexSchema,
50
+ }).strict(),
51
+ runId: identifierSchema,
52
+ stepId: identifierSchema.optional(),
53
+ expiresAt: z.string().datetime(),
54
+ }).strict();
38
55
  export const resourceRefSchema = z.object({
39
56
  appId: identifierSchema,
40
57
  effectiveAccountId: identifierSchema,
@@ -73,6 +90,13 @@ export const capabilityCoordinatorSchema = z.object({
73
90
  applicationId: identifierSchema,
74
91
  credentialId: identifierSchema,
75
92
  }).strict();
93
+ /** Opt-in catalogue pin; legacy issuance omits expectedCatalog and its claim. */
94
+ export const capabilityTicketRequestSchema = z.object({
95
+ executionAuthorizationId: z.string().min(1),
96
+ runId: z.string().min(1).optional(),
97
+ stepId: z.string().min(1).optional(),
98
+ expectedCatalog: capabilityCatalogBindingSchema.optional(),
99
+ }).strict();
76
100
  export const delegationGrantSchema = z.object({
77
101
  id: identifierSchema,
78
102
  ownerAccountId: identifierSchema,
@@ -139,17 +163,19 @@ export const capabilityTicketClaimsSchema = z.object({
139
163
  automationId: identifierSchema.optional(),
140
164
  executionAuthorization: executionAuthorizationRefSchema,
141
165
  coordinator: capabilityCoordinatorSchema,
166
+ /** Signed only for callers that explicitly requested a pinned catalogue. */
167
+ catalog: capabilityCatalogBindingSchema.optional(),
142
168
  grantId: identifierSchema.optional(),
143
169
  requesterAccountId: identifierSchema,
144
170
  ownerAccountId: identifierSchema,
145
- actor: actorRefSchema,
171
+ actor: executionActorRefSchema,
146
172
  resource: resourceRefSchema,
147
173
  tool: identifierSchema,
148
174
  capabilities: z.array(identifierSchema).min(1),
149
175
  limits: z.array(grantLimitSchema).default([]),
150
176
  autonomy: autonomyLevelSchema,
151
177
  }).strict().superRefine((claims, context) => {
152
- const expectedSubject = claims.actor.type === 'agent'
178
+ const expectedSubject = claims.actor.type !== 'alia'
153
179
  ? claims.actor.accountId
154
180
  : `alia:${claims.actor.ownerAccountId}`;
155
181
  if (claims.sub !== expectedSubject) {
@@ -166,6 +192,14 @@ export const capabilityTicketClaimsSchema = z.object({
166
192
  path: ['actor', 'ownerAccountId'],
167
193
  });
168
194
  }
195
+ if (claims.actor.type === 'requester' && (claims.actor.accountId !== claims.requesterAccountId
196
+ || claims.executionAuthorization.kind !== 'direct_request'
197
+ || claims.autonomy !== 'read_only'
198
+ || claims.grantId !== undefined
199
+ || claims.automationId !== undefined)) {
200
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ['actor'],
201
+ message: 'requester actors require their exact principal and direct read-only authority without standing grants' });
202
+ }
169
203
  if (claims.exp <= claims.iat) {
170
204
  context.addIssue({
171
205
  code: z.ZodIssueCode.custom,
@@ -199,6 +233,24 @@ export const capabilityTicketClaimsSchema = z.object({
199
233
  }
200
234
  }
201
235
  });
236
+ /** Structural projections only: an authenticated transport must produce these. */
237
+ export const invocationPrincipalSchema = z.discriminatedUnion('kind', [
238
+ z.object({
239
+ kind: z.literal('oauth'),
240
+ subject: identifierSchema,
241
+ clientId: identifierSchema,
242
+ originAccountId: identifierSchema,
243
+ activeAccountId: identifierSchema,
244
+ scopes: z.array(identifierSchema),
245
+ resource: z.string().url(),
246
+ }).strict(),
247
+ z.object({
248
+ kind: z.literal('capability'),
249
+ claims: capabilityTicketClaimsSchema.refine((claims) => claims.catalog !== undefined, {
250
+ message: 'internal MCP requires a signed catalogue binding',
251
+ }),
252
+ }).strict(),
253
+ ]);
202
254
  export const policyDecisionSchema = z.object({
203
255
  allowed: z.boolean(),
204
256
  reason: identifierSchema,
@@ -214,7 +266,7 @@ export const auditEventSchema = z.object({
214
266
  occurredAt: z.string().datetime(),
215
267
  requesterAccountId: identifierSchema,
216
268
  coordinator: capabilityCoordinatorSchema,
217
- executor: actorRefSchema,
269
+ executor: executionActorRefSchema,
218
270
  effectiveAccountId: identifierSchema,
219
271
  resource: resourceRefSchema,
220
272
  appId: identifierSchema,
@@ -0,0 +1,63 @@
1
+ /** Autonomous runtime proof. No private key crosses this contract. */
2
+ import { z } from 'zod';
3
+ import { reauthProofSchema } from './signIn.js';
4
+ import { canonicalJson } from './identityProof.js';
5
+ export const AGENT_PROOF_AUDIENCE = 'oxy-api/agent';
6
+ export const AGENT_PROOF_TTL_MS = 60000;
7
+ export const AGENT_PROOF_ACTIONS = ['agent_signin', 'agent_enroll', 'agent_rotate', 'agent_recover', 'agent_governance'];
8
+ export const agentProofClaimsSchema = z.object({
9
+ version: z.literal(1),
10
+ audience: z.literal(AGENT_PROOF_AUDIENCE),
11
+ action: z.enum(AGENT_PROOF_ACTIONS),
12
+ accountId: z.string().min(1),
13
+ actorId: z.string().min(1),
14
+ authMethodId: z.string().nullable(),
15
+ publicKey: z.string().min(1),
16
+ payloadDigest: z.string().regex(/^[0-9a-f]{64}$/),
17
+ challenge: z.string().regex(/^[0-9a-f]{64}$/),
18
+ expiresAt: z.number().int().positive(),
19
+ }).strict();
20
+ /** Different proof roles cannot substitute for one another on the same operation. */
21
+ export function buildAgentProofMessage(claims, timestamp, role = 'credential') {
22
+ return canonicalJson({ domain: 'oxy-agent-proof', ...agentProofClaimsSchema.parse(claims), timestamp, role });
23
+ }
24
+ export const agentSignatureSchema = z.object({
25
+ challenge: z.string().regex(/^[0-9a-f]{64}$/),
26
+ signature: z.string().regex(/^[0-9a-f]+$/i).max(160),
27
+ timestamp: z.number().int().positive(),
28
+ }).strict();
29
+ export const agentChallengeRequestSchema = z.object({ publicKey: z.string().trim().min(1).max(130) }).strict();
30
+ export const agentVerifyRequestSchema = agentSignatureSchema.extend({ publicKey: z.string().trim().min(1).max(130) }).strict();
31
+ const newKey = { publicKey: z.string().trim().min(1).max(130), label: z.string().trim().min(1).max(120) };
32
+ export const agentKeyOperationSchema = z.discriminatedUnion('operation', [
33
+ z.object({ operation: z.literal('enroll'), ...newKey }).strict(),
34
+ z.object({ operation: z.literal('recover'), ...newKey }).strict(),
35
+ z.object({ operation: z.literal('rotate'), ...newKey, retireCurrent: z.boolean().optional() }).strict(),
36
+ z.object({ operation: z.literal('revoke'), methodId: z.string().min(1) }).strict(),
37
+ ]);
38
+ export const agentKeyOperationProofSchema = z.object({
39
+ challenge: z.string().regex(/^[0-9a-f]{64}$/),
40
+ timestamp: z.number().int().positive(),
41
+ keySignature: z.string().regex(/^[0-9a-f]+$/i).max(160).optional(),
42
+ governorSignature: z.string().regex(/^[0-9a-f]+$/i).max(160).optional(),
43
+ reauth: reauthProofSchema.optional(),
44
+ }).strict();
45
+ export const executeAgentKeyOperationSchema = z.object({
46
+ operation: agentKeyOperationSchema,
47
+ proof: agentKeyOperationProofSchema,
48
+ }).strict();
49
+ export const agentKeyOperationChallengeSchema = z.object({
50
+ claims: agentProofClaimsSchema,
51
+ governorPublicKey: z.string().nullable(),
52
+ }).strict();
53
+ export const agentKeyOperationResultSchema = z.object({
54
+ methodId: z.string().nullable(),
55
+ revokedMethodIds: z.array(z.string()),
56
+ }).strict();
57
+ export const ACCOUNT_AUTH_METHODS = ['identity', 'agent_key'];
58
+ /** Public credential inventory. Never includes a proof, bearer or private key. */
59
+ export const agentKeyListResponseSchema = z.object({ keys: z.array(z.object({
60
+ id: z.string(), publicKey: z.string(), label: z.string(),
61
+ enrolledByUserId: z.string().nullable(), enrollmentMethod: z.enum(['governor', 'rotation', 'recovery']),
62
+ linkedAt: z.string().datetime(), lastUsedAt: z.string().datetime().nullable(), revokedAt: z.string().datetime().nullable(),
63
+ }).strict()) }).strict();
@@ -0,0 +1,67 @@
1
+ function canonicalValue(value) {
2
+ if (Array.isArray(value))
3
+ return value.map(canonicalValue);
4
+ if (typeof value !== 'object' || value === null)
5
+ return value;
6
+ const record = value;
7
+ const sorted = Object.create(null);
8
+ for (const key of Object.keys(record).sort())
9
+ sorted[key] = canonicalValue(record[key]);
10
+ return sorted;
11
+ }
12
+ /** Registry-compatible JSON: sorted keys, omitted undefined object properties. */
13
+ export function canonicalCapabilityJson(value) {
14
+ return JSON.stringify(canonicalValue(value));
15
+ }
16
+ function valuesAtPath(input, path) {
17
+ let current = [input];
18
+ for (const segment of path.split('.')) {
19
+ const next = [];
20
+ for (const value of current) {
21
+ const records = Array.isArray(value) ? value : [value];
22
+ for (const record of records) {
23
+ if (typeof record !== 'object' || record === null || Array.isArray(record))
24
+ continue;
25
+ if (Object.prototype.hasOwnProperty.call(record, segment)) {
26
+ next.push(record[segment]);
27
+ }
28
+ }
29
+ }
30
+ if (next.length === 0)
31
+ return [];
32
+ current = next;
33
+ }
34
+ return current.flatMap((value) => Array.isArray(value) ? value : [value]);
35
+ }
36
+ /** Enforces the signed per-action bounds before a domain handler runs. */
37
+ export function inputSatisfiesCapabilityLimits(tool, input, limits) {
38
+ for (const limit of limits) {
39
+ if (limit.tool !== tool)
40
+ return false;
41
+ const actualValues = valuesAtPath(input, limit.key);
42
+ if (actualValues.length === 0)
43
+ return false;
44
+ if (typeof limit.value === 'number') {
45
+ const maximum = limit.value;
46
+ if (!actualValues.every((actual) => (typeof actual === 'number' && Number.isFinite(actual) && actual <= maximum)))
47
+ return false;
48
+ continue;
49
+ }
50
+ if (!actualValues.every((actual) => actual === limit.value))
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ export function isLoopbackOrigin(origin) {
56
+ try {
57
+ const parsed = new URL(origin);
58
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
59
+ return false;
60
+ }
61
+ const host = parsed.hostname.toLowerCase();
62
+ return host === 'localhost' || host === '127.0.0.1' || host === '[::1]';
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ }
package/dist/esm/index.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * expo, no `require()` in the ESM build.
11
11
  */
12
12
  export { ACCOUNT_KINDS, accountKindSchema, CHILD_ACCOUNT_KINDS, childAccountKindSchema, isAccountKind, isDelegatedActAsEligibleKind, isOperatorSwitchTargetKind, ACCOUNT_CATEGORY_IDS, ACCOUNT_CATEGORY_KINDS, accountCategoriesSchema, accountCategoryIdSchema, isSelectableAccountCategoryId, kindAcceptsAccountCategories, MAX_ACCOUNT_CATEGORIES, newlyAddedRetiredCategories, RETIRED_ACCOUNT_CATEGORY_IDS, SELECTABLE_ACCOUNT_CATEGORY_IDS, createAccountRequestSchema, } from './accountGraph.js';
13
+ export { ACCOUNT_ACTOR_NATURES, ACCOUNT_SUBJECT_CONTRACT_VERSION, FINANCIAL_EFFECT_DIRECTIONS, KIND_INDEPENDENT_ACCOUNT_DIMENSIONS, accountActorChainFromSession, accountActorChainSchema, accountActorNatureSchema, accountKindActorNature, accountKindActsAsItself, attributeFinancialEffect, financialEffectAttributionSchema, } from './accountSubject.js';
13
14
  export { usernameSchema, usernameSchemaForAccountKind, isValidUsername, stripDisallowedUsernameCharacters, applyBotUsernameSuffix, USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, USERNAME_INVALID_MESSAGE, BOT_USERNAME_INVALID_MESSAGE, RESERVED_USERNAME_MESSAGE, NUMERIC_USERNAME_MESSAGE, } from './username.js';
14
15
  export {
15
16
  // Schemas
@@ -173,7 +174,10 @@ export {
173
174
  // Product entitlements. Allowances are integer counts and money is an exact
174
175
  // decimal, so a plan allowance cannot be added to a balance.
175
176
  PRODUCT_PLAN_STATUSES, productPlanStatusSchema, LIVE_PRODUCT_PLAN_STATUSES, planAllowanceSchema, productPlanSchema, payAsYouGoEntitlementSchema, COST_CENTER_STATUSES, costCenterStatusSchema, costCenterSchema, costCenterSpendSchema, productEntitlementSchema, } from './inference/entitlement.js';
176
- export { AUTONOMY_LEVELS, CAPABILITY_PACKAGES, autonomyLevelSchema, capabilityPackageSchema, actorRefSchema, resourceRefSchema, toolGrantOverrideSchema, grantLimitSchema, capabilityCatalogBindingSchema, executionAuthorizationRefSchema, capabilityCoordinatorSchema, delegationGrantSchema, automationTriggerSchema, automationActorSelectionSchema, automationDataFlowSchema, automationDefinitionSchema, capabilityTicketClaimsSchema, policyDecisionSchema, auditResultSchema, auditEventSchema, catalogToolSchema, catalogEventSchema, appCapabilityCatalogSchema, catalogRegistrationSchema, normalizedAppEventSchema, } from './agency.js';
177
+ export {
178
+ // Economic treatment (I09): derived by the server, never sent by a caller.
179
+ INFERENCE_ECONOMIC_TREATMENTS, inferenceEconomicTreatmentSchema, PROVIDER_COST_SOURCES, providerCostSourceSchema, costCenterUsageSchema, } from './inference/economics.js';
180
+ export { AUTONOMY_LEVELS, CAPABILITY_PACKAGES, autonomyLevelSchema, capabilityPackageSchema, actorRefSchema, executionActorRefSchema, foregroundExecutionAuthorizationInputSchema, resourceRefSchema, toolGrantOverrideSchema, grantLimitSchema, capabilityCatalogBindingSchema, executionAuthorizationRefSchema, capabilityCoordinatorSchema, capabilityTicketRequestSchema, delegationGrantSchema, automationTriggerSchema, automationActorSelectionSchema, automationDataFlowSchema, automationDefinitionSchema, capabilityTicketClaimsSchema, invocationPrincipalSchema, policyDecisionSchema, auditResultSchema, auditEventSchema, catalogToolSchema, catalogEventSchema, appCapabilityCatalogSchema, catalogRegistrationSchema, normalizedAppEventSchema, } from './agency.js';
177
181
  export { catalogInvocationPathParameters, catalogInvocationsOverlap, catalogInvocationTemplatesEquivalent, findOverlappingCatalogInvocations, matchCatalogInvocation, resolveCatalogInvocation, } from './capabilityInvocation.js';
178
182
  export { emailContextAddressSchema, emailContextMailboxSchema, emailContextMessageSchema, emailAgentContextSchema, } from './emailAgentContext.js';
179
183
  export { inboxComposeRequestSchema, inboxDailyBriefRequestSchema, inboxNaturalSearchRequestSchema, inboxMessageInferenceParamsSchema, inboxInferenceTextResponseSchema, inboxNaturalSearchResponseSchema, inboxSmartRepliesResponseSchema, inboxThreadSummaryResponseSchema, inboxInferenceStreamEventSchema, } from './inference/inbox.js';
@@ -186,3 +190,8 @@ export * from './stickers.js';
186
190
  export { decisionEffortSchema, decisionQuestionSchema, decisionInputSchema, decisionRequestSchema, decisionAnswerSchema, decisionResultSchema, decisionSuccessSchema, decisionFailureSchema, decisionInputBudget, decisionFitsGateway, decisionAnswersMatch, } from './inference/decisions.js';
187
191
  export { SCOPED_EXECUTION_CONTRACT_VERSION, SCOPED_REQUEST_ENVELOPE_VERSION, scopedExecutionAudienceSchema, scopedExecutionSchema, canonicalScopedExecutionJson } from './inference/scopedExecution.js';
188
192
  export { scopedInferenceRequestSchema } from './inference/request.js';
193
+ export { productDefinitionSchema, subjectProductAccessQuerySchema, productBenefitSchema, productOfferSchema, productSubscriptionSourceSchema, productAccessGrantSchema, productOfferSegmentSchema, subjectProductAccessSchema, } from './products/access.js';
194
+ export { meteredGenerationSchema } from "./inference/economics.js";
195
+ export { canonicalCapabilityJson, inputSatisfiesCapabilityLimits, isLoopbackOrigin } from './capabilityBindings.js';
196
+ export * from './agentKeys.js';
197
+ export { productSubscriptionSummarySchema, productSubscriptionsResponseSchema, subscriptionCreditGrantSchema, subscriptionCreditGrantsResponseSchema, cancelProductSubscriptionSchema, productSubscriptionCancellationResultSchema } from './products/billing.js';
@@ -69,7 +69,48 @@ export function decisionInputBudget(payload) {
69
69
  export function decisionFitsGateway(payload) {
70
70
  return decisionInputBudget(payload).gateway <= 32000;
71
71
  }
72
+ /** JSON permits escaped lone UTF-16 units; Go replaces them after decoding. */
73
+ function hasUnpairedSurrogate(value) {
74
+ for (let i = 0; i < value.length; i++) {
75
+ const unit = value.charCodeAt(i);
76
+ if (unit >= 0xd800 && unit <= 0xdbff) {
77
+ const next = value.charCodeAt(i + 1);
78
+ if (!(next >= 0xdc00 && next <= 0xdfff))
79
+ return true;
80
+ i++;
81
+ }
82
+ else if (unit >= 0xdc00 && unit <= 0xdfff)
83
+ return true;
84
+ }
85
+ return false;
86
+ }
72
87
  function validateInput(payload, ctx) {
88
+ const fields = [
89
+ { value: payload.state, path: ["state"] },
90
+ { value: payload.instructions, path: ["instructions"] },
91
+ ];
92
+ for (const [index, question] of payload.questions.entries()) {
93
+ for (const field of ["id", "question", "criteria"]) {
94
+ fields.push({ value: question[field], path: ["questions", index, field] });
95
+ }
96
+ if (question.kind === "choice" || question.kind === "score") {
97
+ const field = question.kind === "choice" ? "options" : "levels";
98
+ const labels = question.kind === "choice" ? question.options : question.levels;
99
+ for (const [labelIndex, value] of labels.entries()) {
100
+ fields.push({ value, path: ["questions", index, field, labelIndex] });
101
+ }
102
+ }
103
+ }
104
+ let invalidUnicode = false;
105
+ for (const { value, path } of fields) {
106
+ if (value !== undefined && hasUnpairedSurrogate(value)) {
107
+ invalidUnicode = true;
108
+ ctx.addIssue({ code: "custom", path, message: "Decisions require paired UTF-16 surrogates." });
109
+ }
110
+ }
111
+ // Reject before measuring/reserving text that the execution runtime would change.
112
+ if (invalidUnicode)
113
+ return;
73
114
  if (new Set(payload.questions.map((q) => q.id)).size !==
74
115
  payload.questions.length)
75
116
  ctx.addIssue({