otto-execute 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 +21 -0
- package/README.md +151 -0
- package/dist/cdp-signer.d.ts +2 -0
- package/dist/cdp-signer.js +3 -0
- package/dist/cdp.d.ts +7 -0
- package/dist/cdp.js +8 -0
- package/dist/chain.d.ts +2 -0
- package/dist/chain.js +3 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +400 -0
- package/dist/delegate.d.ts +117 -0
- package/dist/delegate.js +394 -0
- package/dist/eoa-signer.d.ts +94 -0
- package/dist/eoa-signer.js +289 -0
- package/dist/erc20.d.ts +2 -0
- package/dist/erc20.js +3 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +19 -0
- package/dist/lifi-decode.d.ts +2 -0
- package/dist/lifi-decode.js +3 -0
- package/dist/mutations.d.ts +55 -0
- package/dist/mutations.js +363 -0
- package/dist/refusal.d.ts +2 -0
- package/dist/refusal.js +3 -0
- package/dist/vendor/otto-intel-mcp/VENDORED.json +52 -0
- package/dist/vendor/otto-intel-mcp/adapter/cdp-signer.d.ts +133 -0
- package/dist/vendor/otto-intel-mcp/adapter/cdp-signer.js +356 -0
- package/dist/vendor/otto-intel-mcp/adapter/chain.d.ts +36 -0
- package/dist/vendor/otto-intel-mcp/adapter/chain.js +65 -0
- package/dist/vendor/otto-intel-mcp/adapter/erc20.d.ts +39 -0
- package/dist/vendor/otto-intel-mcp/adapter/erc20.js +17 -0
- package/dist/vendor/otto-intel-mcp/adapter/lifi-decode.d.ts +52 -0
- package/dist/vendor/otto-intel-mcp/adapter/lifi-decode.js +149 -0
- package/dist/vendor/otto-intel-mcp/adapter/refusal.d.ts +21 -0
- package/dist/vendor/otto-intel-mcp/adapter/refusal.js +55 -0
- package/dist/vendor/otto-intel-mcp/adapter/sent-step.d.ts +15 -0
- package/dist/vendor/otto-intel-mcp/adapter/sent-step.js +6 -0
- package/dist/vendor/otto-intel-mcp/adapter/verify.d.ts +149 -0
- package/dist/vendor/otto-intel-mcp/adapter/verify.js +432 -0
- package/dist/vendor/otto-intel-mcp/adapter-cdp-index.d.ts +6 -0
- package/dist/vendor/otto-intel-mcp/adapter-cdp-index.js +7 -0
- package/dist/vendor/otto-intel-mcp/adapter-index.d.ts +15 -0
- package/dist/vendor/otto-intel-mcp/adapter-index.js +15 -0
- package/dist/vendor/otto-intel-mcp/artifact-id.d.ts +16 -0
- package/dist/vendor/otto-intel-mcp/artifact-id.js +60 -0
- package/dist/vendor/otto-intel-mcp/execution-config.d.ts +232 -0
- package/dist/vendor/otto-intel-mcp/execution-config.js +443 -0
- package/dist/vendor/otto-intel-mcp/execution-delegated-definition.d.ts +165 -0
- package/dist/vendor/otto-intel-mcp/execution-delegated-definition.js +116 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-admin-definition.d.ts +208 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-admin-definition.js +170 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-policy.d.ts +257 -0
- package/dist/vendor/otto-intel-mcp/execution-delegation-policy.js +279 -0
- package/dist/vendor/otto-intel-mcp/execution-errors.d.ts +9 -0
- package/dist/vendor/otto-intel-mcp/execution-errors.js +134 -0
- package/dist/vendor/otto-intel-mcp/execution-index.d.ts +17 -0
- package/dist/vendor/otto-intel-mcp/execution-index.js +16 -0
- package/dist/vendor/otto-intel-mcp/execution-intent.d.ts +14 -0
- package/dist/vendor/otto-intel-mcp/execution-intent.js +36 -0
- package/dist/vendor/otto-intel-mcp/execution-tool-definitions.d.ts +1103 -0
- package/dist/vendor/otto-intel-mcp/execution-tool-definitions.js +1051 -0
- package/dist/vendor/otto-intel-mcp/execution-types.d.ts +274 -0
- package/dist/vendor/otto-intel-mcp/execution-types.js +157 -0
- package/dist/verify.d.ts +2 -0
- package/dist/verify.js +3 -0
- package/dist/x402-table.d.ts +99 -0
- package/dist/x402-table.js +221 -0
- package/package.json +97 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-delegated-definition.ts — the ONE execution tool that submits: `otto_submit_under_delegation`.
|
|
3
|
+
* Kept apart from the prepare-only `EXECUTION_TOOL_DEFINITIONS` on purpose: it registers only when the
|
|
4
|
+
* hosted service holds Otto's CDP developer credentials, it is not read-only, and its caller is
|
|
5
|
+
* authenticated per request by a header the arguments never carry.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { EXECUTION_ENVELOPE_SCHEMA, PREPARE_SWAP_INPUT_SCHEMA, } from './execution-tool-definitions.js';
|
|
9
|
+
import { MODEL_B_PER_SWAP_CAP_USD } from './execution-delegation-policy.js';
|
|
10
|
+
export const DELEGATED_EXECUTION_TOOL_NAME = 'otto_submit_under_delegation';
|
|
11
|
+
/** Per-request caller authentication rides THIS header — never a tool argument (arguments are logged). */
|
|
12
|
+
export const DELEGATION_AUTH_HEADER = 'x-otto-delegation-auth';
|
|
13
|
+
const END_USER_ID_PATTERN = /^[a-zA-Z0-9-]{1,100}$/;
|
|
14
|
+
const EVM_ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
|
|
15
|
+
const HEX_PATTERN = /^0x(?:[0-9a-fA-F]{2})*$/;
|
|
16
|
+
export const SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA = z
|
|
17
|
+
.object({
|
|
18
|
+
userId: z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(END_USER_ID_PATTERN, 'must be a CDP end-user id')
|
|
21
|
+
.describe('The CDP end user whose delegation submits the plan. The server reads the user and its accounts from CDP; the caller never binds an address on its own.'),
|
|
22
|
+
envelope: EXECUTION_ENVELOPE_SCHEMA.describe('The otto_prepare_swap envelope, exactly as returned.'),
|
|
23
|
+
prepareInput: PREPARE_SWAP_INPUT_SCHEMA.describe("The EXACT otto_prepare_swap request that produced the envelope (binds intent_digest). slippage.minAmountOut is REQUIRED here: it is the user's own floor, enforced on-chain by the router and re-checked by the verifier."),
|
|
24
|
+
})
|
|
25
|
+
.strict()
|
|
26
|
+
.superRefine((value, context) => {
|
|
27
|
+
if (value.prepareInput.slippage.minAmountOut === undefined) {
|
|
28
|
+
context.addIssue({
|
|
29
|
+
code: 'custom',
|
|
30
|
+
path: ['prepareInput', 'slippage', 'minAmountOut'],
|
|
31
|
+
message: 'required for a delegated send: the caller must state the minimum acceptable output',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (value.prepareInput.accountProfile.kind !== 'eoa') {
|
|
35
|
+
context.addIssue({
|
|
36
|
+
code: 'custom',
|
|
37
|
+
path: ['prepareInput', 'accountProfile', 'kind'],
|
|
38
|
+
message: 'a CDP end-user account is an EOA',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (value.prepareInput.chainId !== 8453 || value.envelope.payload.chain_id !== 8453) {
|
|
42
|
+
context.addIssue({
|
|
43
|
+
code: 'custom',
|
|
44
|
+
path: ['prepareInput', 'chainId'],
|
|
45
|
+
message: 'delegated sends are Base mainnet (8453) only',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* What the tool REGISTERS as its input schema: permissive on purpose. The MCP SDK validates the registered
|
|
51
|
+
* schema BEFORE the handler runs, which would answer an unauthenticated caller with the exact validation
|
|
52
|
+
* failure — a schema oracle in front of the caller check. Registering a passthrough object moves ALL
|
|
53
|
+
* validation behind the header check: the handler parses `SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA` itself,
|
|
54
|
+
* strictly, after the caller is accepted. The strict schema stays exported for consumers.
|
|
55
|
+
*/
|
|
56
|
+
export const DELEGATED_TOOL_TRANSPORT_SCHEMA = z.object({}).passthrough();
|
|
57
|
+
const SENT_STEP_SCHEMA = z
|
|
58
|
+
.object({
|
|
59
|
+
index: z.number().int().min(0).max(2),
|
|
60
|
+
kind: z.enum(['approve_reset', 'approve', 'call']),
|
|
61
|
+
to: z.string().regex(EVM_ADDRESS_PATTERN),
|
|
62
|
+
transaction_hash: z.string().regex(/^0x[0-9a-fA-F]{64}$/),
|
|
63
|
+
status: z.literal('success'),
|
|
64
|
+
block_number: z.string().regex(/^\d+$/),
|
|
65
|
+
gas_used: z.string().regex(/^\d+$/),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
export const DELEGATED_SEND_RESULT_SCHEMA = z
|
|
69
|
+
.object({
|
|
70
|
+
status: z.literal('sent'),
|
|
71
|
+
user_id: z.string().regex(END_USER_ID_PATTERN),
|
|
72
|
+
address: z.string().regex(EVM_ADDRESS_PATTERN),
|
|
73
|
+
chain_id: z.literal(8453),
|
|
74
|
+
artifact_id: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{26}$/),
|
|
75
|
+
steps: z.array(SENT_STEP_SCHEMA).length(3),
|
|
76
|
+
fence: z
|
|
77
|
+
.object({
|
|
78
|
+
policy_id: z.string(),
|
|
79
|
+
policy_name: z.string(),
|
|
80
|
+
rules_digest: z.string().regex(/^[0-9a-f]{64}$/),
|
|
81
|
+
per_swap_cap_usd: z.literal(MODEL_B_PER_SWAP_CAP_USD),
|
|
82
|
+
})
|
|
83
|
+
.strict(),
|
|
84
|
+
delegation: z.object({ expires_at: z.string() }).strict(),
|
|
85
|
+
/** True when this response is the stored result of an EARLIER submission of the same artifact (nothing was re-sent). */
|
|
86
|
+
replayed: z.boolean().optional(),
|
|
87
|
+
verification: z
|
|
88
|
+
.object({
|
|
89
|
+
checks: z.array(z.string()).min(12),
|
|
90
|
+
gross: z.string().regex(/^\d+$/),
|
|
91
|
+
min_amount_out: z.string().regex(/^\d+$/),
|
|
92
|
+
otto_fee_atomic: z.string().regex(/^\d+$/),
|
|
93
|
+
otto_fee_recipient: z.string().regex(EVM_ADDRESS_PATTERN),
|
|
94
|
+
fee_config_version: z.string(),
|
|
95
|
+
receiver: z.string().regex(EVM_ADDRESS_PATTERN),
|
|
96
|
+
call_data_digest: z.string().regex(HEX_PATTERN).optional(),
|
|
97
|
+
})
|
|
98
|
+
.strict(),
|
|
99
|
+
})
|
|
100
|
+
.strict();
|
|
101
|
+
/** Not read-only, not idempotent: every accepted call moves the end user's funds through the delegation. */
|
|
102
|
+
export const DELEGATED_TOOL_ANNOTATIONS = Object.freeze({
|
|
103
|
+
readOnlyHint: false,
|
|
104
|
+
destructiveHint: true,
|
|
105
|
+
idempotentHint: false,
|
|
106
|
+
openWorldHint: true,
|
|
107
|
+
});
|
|
108
|
+
export const DELEGATED_EXECUTION_TOOL_DEFINITION = Object.freeze({
|
|
109
|
+
name: DELEGATED_EXECUTION_TOOL_NAME,
|
|
110
|
+
title: 'Submit an Otto-prepared swap under a CDP end-user delegation',
|
|
111
|
+
description: `Submit a verified otto_prepare_swap envelope on Base under the end user's CDP delegation. Arguments: { userId, envelope (the otto_prepare_swap result), prepareInput (the exact otto_prepare_swap request, with slippage.minAmountOut) } — validated strictly AFTER the caller is authenticated, so the listed input schema is intentionally permissive. Server-to-server only: the caller presents Otto's delegation secret in the \`${DELEGATION_AUTH_HEADER}\` request header (never in arguments); a call without it is refused before anything is read. Before any send the server confirms, in this order: the Model-B project policy is installed and reads back by name and rules digest; the user's grant is active past the envelope's valid_until; the account is one of that end user's own CDP accounts as CDP reports them; the envelope passes the 13-check verifier for exactly the request that produced it; the plan is inside the fence (allow-listed input token, reviewed spender, gross at or below the ${MODEL_B_PER_SWAP_CAP_USD} USD per-swap cap). Then the three steps (allowance reset, approval, swap) are submitted in order under the delegation, each waited to a sealed canonical block; a halt clears the allowance and reports the read-back. One delegated plan at a time per user. Coinbase's engine bounds the delegated key to Model-B swap shapes through LI.FI up to the per-swap cap; the receiver binding is Otto's server-side check, and the user can revoke at any time.`,
|
|
112
|
+
inputSchema: DELEGATED_TOOL_TRANSPORT_SCHEMA,
|
|
113
|
+
strictInputSchema: SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA,
|
|
114
|
+
outputSchema: DELEGATED_SEND_RESULT_SCHEMA,
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=execution-delegated-definition.js.map
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-delegation-admin-definition.ts — the three header-authed delegation ADMIN tools (PR-1b, seat ruling
|
|
3
|
+
* D10 = (a), 2026-08-29): `otto_delegation_fence`, `otto_delegation_status`, `otto_delegation_revoke`.
|
|
4
|
+
*
|
|
5
|
+
* Same seam and admission as `otto_submit_under_delegation`: they register only with a rig, the caller presents
|
|
6
|
+
* Otto's server secret in the `x-otto-delegation-auth` request header (never in arguments), the registered
|
|
7
|
+
* transport schema is permissive and the strict schema is parsed only AFTER the header is accepted (no schema
|
|
8
|
+
* oracle in front of the caller check — `execution-delegated-definition.ts` states why).
|
|
9
|
+
*
|
|
10
|
+
* IDENTITY INVARIANT (seat ruling, 21:4x CEST): `status` and the user path of `revoke` never take a client-sent
|
|
11
|
+
* id — they take the end user's CDP ACCESS TOKEN and the server resolves the userId itself through
|
|
12
|
+
* `cdp.endUser.validateAccessToken`. A bare `userId` on `status` is refused by the strict schema. The SUPPORT path
|
|
13
|
+
* of `revoke` (`{ userId, support_reason }`) exists for an operator holding the server secret; the dApp's public
|
|
14
|
+
* proxy exposes only the token variant (asserted in PR-2).
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { DELEGATED_TOOL_TRANSPORT_SCHEMA } from './execution-delegated-definition.js';
|
|
18
|
+
export declare const DELEGATION_FENCE_TOOL_NAME: "otto_delegation_fence";
|
|
19
|
+
export declare const DELEGATION_STATUS_TOOL_NAME: "otto_delegation_status";
|
|
20
|
+
export declare const DELEGATION_REVOKE_TOOL_NAME: "otto_delegation_revoke";
|
|
21
|
+
export declare const DELEGATION_ADMIN_TOOL_NAMES: readonly ["otto_delegation_fence", "otto_delegation_status", "otto_delegation_revoke"];
|
|
22
|
+
/** The one PUBLIC delegation tool: the fence's public promise (present / name / digest / cap) — never user or grant data. */
|
|
23
|
+
export declare const DELEGATION_FENCE_STATUS_TOOL_NAME: "otto_delegation_fence_status";
|
|
24
|
+
export declare const DELEGATION_FENCE_INPUT_SCHEMA: z.ZodObject<{}, z.core.$strict>;
|
|
25
|
+
export declare const DELEGATION_FENCE_RESULT_SCHEMA: z.ZodObject<{
|
|
26
|
+
status: z.ZodLiteral<"present">;
|
|
27
|
+
action: z.ZodEnum<{
|
|
28
|
+
unchanged: "unchanged";
|
|
29
|
+
created: "created";
|
|
30
|
+
updated: "updated";
|
|
31
|
+
}>;
|
|
32
|
+
policy_id: z.ZodString;
|
|
33
|
+
policy_name: z.ZodString;
|
|
34
|
+
rules_digest: z.ZodString;
|
|
35
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
export declare const DELEGATION_STATUS_INPUT_SCHEMA: z.ZodObject<{
|
|
38
|
+
accessToken: z.ZodString;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
export declare const DELEGATION_STATUS_RESULT_SCHEMA: z.ZodObject<{
|
|
41
|
+
user_id: z.ZodString;
|
|
42
|
+
addresses: z.ZodArray<z.ZodString>;
|
|
43
|
+
fence: z.ZodObject<{
|
|
44
|
+
present: z.ZodBoolean;
|
|
45
|
+
policy_name: z.ZodString;
|
|
46
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
47
|
+
rules_digest: z.ZodOptional<z.ZodString>;
|
|
48
|
+
}, z.core.$strict>;
|
|
49
|
+
delegation: z.ZodObject<{
|
|
50
|
+
active: z.ZodBoolean;
|
|
51
|
+
expires_at: z.ZodOptional<z.ZodString>;
|
|
52
|
+
}, z.core.$strict>;
|
|
53
|
+
revocation_requested_here: z.ZodBoolean;
|
|
54
|
+
}, z.core.$strict>;
|
|
55
|
+
export declare const DELEGATION_REVOKE_INPUT_SCHEMA: z.ZodUnion<readonly [z.ZodObject<{
|
|
56
|
+
accessToken: z.ZodString;
|
|
57
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
58
|
+
userId: z.ZodString;
|
|
59
|
+
support_reason: z.ZodString;
|
|
60
|
+
}, z.core.$strict>]>;
|
|
61
|
+
export declare const DELEGATION_REVOKE_RESULT_SCHEMA: z.ZodObject<{
|
|
62
|
+
user_id: z.ZodString;
|
|
63
|
+
revoked: z.ZodLiteral<true>;
|
|
64
|
+
reads_back: z.ZodLiteral<false>;
|
|
65
|
+
path: z.ZodEnum<{
|
|
66
|
+
user: "user";
|
|
67
|
+
support: "support";
|
|
68
|
+
}>;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
export type DelegationFenceResult = z.infer<typeof DELEGATION_FENCE_RESULT_SCHEMA>;
|
|
71
|
+
export declare const DELEGATION_FENCE_STATUS_INPUT_SCHEMA: z.ZodObject<{}, z.core.$strict>;
|
|
72
|
+
export declare const DELEGATION_FENCE_STATUS_RESULT_SCHEMA: z.ZodObject<{
|
|
73
|
+
present: z.ZodBoolean;
|
|
74
|
+
policy_name: z.ZodString;
|
|
75
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
76
|
+
project_id: z.ZodOptional<z.ZodString>;
|
|
77
|
+
rules_digest: z.ZodOptional<z.ZodString>;
|
|
78
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
79
|
+
absent: "absent";
|
|
80
|
+
ambiguous: "ambiguous";
|
|
81
|
+
name_mismatch: "name_mismatch";
|
|
82
|
+
digest_mismatch: "digest_mismatch";
|
|
83
|
+
}>>;
|
|
84
|
+
}, z.core.$strict>;
|
|
85
|
+
export type DelegationFenceStatusResult = z.infer<typeof DELEGATION_FENCE_STATUS_RESULT_SCHEMA>;
|
|
86
|
+
export declare const DELEGATION_FENCE_STATUS_TOOL_DEFINITION: Readonly<{
|
|
87
|
+
name: "otto_delegation_fence_status";
|
|
88
|
+
title: "Read the Model-B delegation fence (public promise: present, name, rules digest, per-swap cap)";
|
|
89
|
+
description: "PUBLIC and read-only, no header: whether the Model-B project policy that fences every delegated send is installed and v-current on this server, with its versioned name, rules digest, the per-swap cap in USD, and Otto's PUBLIC CDP project id (a UUID — the authority an agent binds its mint to). Exposes ONLY the fence's public promise — never an end user, a grant, an address or a policy's contents. An agent about to mint a delegation (otto-execute delegate) reads this first, mints to the published project_id and no other, and refuses to mint when the fence is not present; the server enforces fence-before-authority on every delegated send regardless. Arguments: {}.";
|
|
90
|
+
inputSchema: z.ZodObject<{}, z.core.$strict>;
|
|
91
|
+
outputSchema: z.ZodObject<{
|
|
92
|
+
present: z.ZodBoolean;
|
|
93
|
+
policy_name: z.ZodString;
|
|
94
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
95
|
+
project_id: z.ZodOptional<z.ZodString>;
|
|
96
|
+
rules_digest: z.ZodOptional<z.ZodString>;
|
|
97
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
98
|
+
absent: "absent";
|
|
99
|
+
ambiguous: "ambiguous";
|
|
100
|
+
name_mismatch: "name_mismatch";
|
|
101
|
+
digest_mismatch: "digest_mismatch";
|
|
102
|
+
}>>;
|
|
103
|
+
}, z.core.$strict>;
|
|
104
|
+
annotations: Readonly<{
|
|
105
|
+
readOnlyHint: true;
|
|
106
|
+
destructiveHint: false;
|
|
107
|
+
idempotentHint: true;
|
|
108
|
+
openWorldHint: true;
|
|
109
|
+
}>;
|
|
110
|
+
}>;
|
|
111
|
+
export type DelegationStatusResult = z.infer<typeof DELEGATION_STATUS_RESULT_SCHEMA>;
|
|
112
|
+
export type DelegationRevokeResult = z.infer<typeof DELEGATION_REVOKE_RESULT_SCHEMA>;
|
|
113
|
+
export interface DelegationAdminToolDefinition<TStrict extends z.ZodTypeAny, TOut extends z.ZodTypeAny> {
|
|
114
|
+
readonly name: (typeof DELEGATION_ADMIN_TOOL_NAMES)[number];
|
|
115
|
+
readonly title: string;
|
|
116
|
+
readonly description: string;
|
|
117
|
+
readonly inputSchema: typeof DELEGATED_TOOL_TRANSPORT_SCHEMA;
|
|
118
|
+
readonly strictInputSchema: TStrict;
|
|
119
|
+
readonly outputSchema: TOut;
|
|
120
|
+
readonly annotations: {
|
|
121
|
+
readonly readOnlyHint: boolean;
|
|
122
|
+
readonly destructiveHint: boolean;
|
|
123
|
+
readonly idempotentHint: boolean;
|
|
124
|
+
readonly openWorldHint: boolean;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export declare const DELEGATION_FENCE_TOOL_DEFINITION: Readonly<{
|
|
128
|
+
name: "otto_delegation_fence";
|
|
129
|
+
title: "Ensure the Model-B project policy (the delegation fence) is installed and reads back as v-current";
|
|
130
|
+
description: `Idempotent ensure-by-digest of the ONE CDP project-scope policy that fences every delegated send, then a read-back. Writes only when no project policy exists (creates it) or when the existing one is Otto's own lineage with stale rules (updates it in place). REFUSES BY NAME if a different project-scope policy is installed \u2014 it is never overwritten or deleted. Never downgrades a newer fence. An OPS action: at most one ensure runs project-wide at a time, never during a rolling deploy, never from the dApp (the mint gate reads fence.present from otto_delegation_status). Arguments: {}. Output: the policy id, versioned name, rules digest and the per-swap cap; the tool fails unless the fence reads back present under the written id. ${string}`;
|
|
131
|
+
inputSchema: z.ZodObject<{}, z.core.$loose>;
|
|
132
|
+
strictInputSchema: z.ZodObject<{}, z.core.$strict>;
|
|
133
|
+
outputSchema: z.ZodObject<{
|
|
134
|
+
status: z.ZodLiteral<"present">;
|
|
135
|
+
action: z.ZodEnum<{
|
|
136
|
+
unchanged: "unchanged";
|
|
137
|
+
created: "created";
|
|
138
|
+
updated: "updated";
|
|
139
|
+
}>;
|
|
140
|
+
policy_id: z.ZodString;
|
|
141
|
+
policy_name: z.ZodString;
|
|
142
|
+
rules_digest: z.ZodString;
|
|
143
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
144
|
+
}, z.core.$strict>;
|
|
145
|
+
annotations: Readonly<{
|
|
146
|
+
readOnlyHint: false;
|
|
147
|
+
destructiveHint: false;
|
|
148
|
+
idempotentHint: true;
|
|
149
|
+
openWorldHint: true;
|
|
150
|
+
}>;
|
|
151
|
+
}>;
|
|
152
|
+
export declare const DELEGATION_STATUS_TOOL_DEFINITION: Readonly<{
|
|
153
|
+
name: "otto_delegation_status";
|
|
154
|
+
title: "Read one end user's delegation status (token-bound)";
|
|
155
|
+
description: `Read back, for the end user identified by a CDP access token, their CDP accounts, whether the Model-B fence is present, and whether an active delegation reads back (with its expiry). Arguments: { accessToken } \u2014 the server resolves the user from the token; a client-sent user id is refused. Output is bound to that user; nothing about any other user is returned. ${string}`;
|
|
156
|
+
inputSchema: z.ZodObject<{}, z.core.$loose>;
|
|
157
|
+
strictInputSchema: z.ZodObject<{
|
|
158
|
+
accessToken: z.ZodString;
|
|
159
|
+
}, z.core.$strict>;
|
|
160
|
+
outputSchema: z.ZodObject<{
|
|
161
|
+
user_id: z.ZodString;
|
|
162
|
+
addresses: z.ZodArray<z.ZodString>;
|
|
163
|
+
fence: z.ZodObject<{
|
|
164
|
+
present: z.ZodBoolean;
|
|
165
|
+
policy_name: z.ZodString;
|
|
166
|
+
per_swap_cap_usd: z.ZodLiteral<5000>;
|
|
167
|
+
rules_digest: z.ZodOptional<z.ZodString>;
|
|
168
|
+
}, z.core.$strict>;
|
|
169
|
+
delegation: z.ZodObject<{
|
|
170
|
+
active: z.ZodBoolean;
|
|
171
|
+
expires_at: z.ZodOptional<z.ZodString>;
|
|
172
|
+
}, z.core.$strict>;
|
|
173
|
+
revocation_requested_here: z.ZodBoolean;
|
|
174
|
+
}, z.core.$strict>;
|
|
175
|
+
annotations: Readonly<{
|
|
176
|
+
readOnlyHint: true;
|
|
177
|
+
destructiveHint: false;
|
|
178
|
+
idempotentHint: true;
|
|
179
|
+
openWorldHint: true;
|
|
180
|
+
}>;
|
|
181
|
+
}>;
|
|
182
|
+
export declare const DELEGATION_REVOKE_TOOL_DEFINITION: Readonly<{
|
|
183
|
+
name: "otto_delegation_revoke";
|
|
184
|
+
title: "Revoke one end user's delegation, developer-side, confirmed by read-back";
|
|
185
|
+
description: `Revoke the end user's delegation from Otto's side and confirm by read-back that it no longer exists; from then on this server submits nothing for that user (fail-closed, even if the read-back had failed). Arguments: EITHER { accessToken } (the user path \u2014 the server resolves the user from the token) OR { userId, support_reason } (the support path, for an operator holding the server secret; the reason is logged, the token never is). ${string}`;
|
|
186
|
+
inputSchema: z.ZodObject<{}, z.core.$loose>;
|
|
187
|
+
strictInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
188
|
+
accessToken: z.ZodString;
|
|
189
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
190
|
+
userId: z.ZodString;
|
|
191
|
+
support_reason: z.ZodString;
|
|
192
|
+
}, z.core.$strict>]>;
|
|
193
|
+
outputSchema: z.ZodObject<{
|
|
194
|
+
user_id: z.ZodString;
|
|
195
|
+
revoked: z.ZodLiteral<true>;
|
|
196
|
+
reads_back: z.ZodLiteral<false>;
|
|
197
|
+
path: z.ZodEnum<{
|
|
198
|
+
user: "user";
|
|
199
|
+
support: "support";
|
|
200
|
+
}>;
|
|
201
|
+
}, z.core.$strict>;
|
|
202
|
+
annotations: Readonly<{
|
|
203
|
+
readOnlyHint: false;
|
|
204
|
+
destructiveHint: true;
|
|
205
|
+
idempotentHint: true;
|
|
206
|
+
openWorldHint: true;
|
|
207
|
+
}>;
|
|
208
|
+
}>;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-delegation-admin-definition.ts — the three header-authed delegation ADMIN tools (PR-1b, seat ruling
|
|
3
|
+
* D10 = (a), 2026-08-29): `otto_delegation_fence`, `otto_delegation_status`, `otto_delegation_revoke`.
|
|
4
|
+
*
|
|
5
|
+
* Same seam and admission as `otto_submit_under_delegation`: they register only with a rig, the caller presents
|
|
6
|
+
* Otto's server secret in the `x-otto-delegation-auth` request header (never in arguments), the registered
|
|
7
|
+
* transport schema is permissive and the strict schema is parsed only AFTER the header is accepted (no schema
|
|
8
|
+
* oracle in front of the caller check — `execution-delegated-definition.ts` states why).
|
|
9
|
+
*
|
|
10
|
+
* IDENTITY INVARIANT (seat ruling, 21:4x CEST): `status` and the user path of `revoke` never take a client-sent
|
|
11
|
+
* id — they take the end user's CDP ACCESS TOKEN and the server resolves the userId itself through
|
|
12
|
+
* `cdp.endUser.validateAccessToken`. A bare `userId` on `status` is refused by the strict schema. The SUPPORT path
|
|
13
|
+
* of `revoke` (`{ userId, support_reason }`) exists for an operator holding the server secret; the dApp's public
|
|
14
|
+
* proxy exposes only the token variant (asserted in PR-2).
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { MODEL_B_PER_SWAP_CAP_USD } from './execution-delegation-policy.js';
|
|
18
|
+
import { DELEGATION_AUTH_HEADER, DELEGATED_TOOL_TRANSPORT_SCHEMA, } from './execution-delegated-definition.js';
|
|
19
|
+
export const DELEGATION_FENCE_TOOL_NAME = 'otto_delegation_fence';
|
|
20
|
+
export const DELEGATION_STATUS_TOOL_NAME = 'otto_delegation_status';
|
|
21
|
+
export const DELEGATION_REVOKE_TOOL_NAME = 'otto_delegation_revoke';
|
|
22
|
+
export const DELEGATION_ADMIN_TOOL_NAMES = Object.freeze([
|
|
23
|
+
DELEGATION_FENCE_TOOL_NAME,
|
|
24
|
+
DELEGATION_STATUS_TOOL_NAME,
|
|
25
|
+
DELEGATION_REVOKE_TOOL_NAME,
|
|
26
|
+
]);
|
|
27
|
+
/** The one PUBLIC delegation tool: the fence's public promise (present / name / digest / cap) — never user or grant data. */
|
|
28
|
+
export const DELEGATION_FENCE_STATUS_TOOL_NAME = 'otto_delegation_fence_status';
|
|
29
|
+
const END_USER_ID_PATTERN = /^[a-zA-Z0-9-]{1,100}$/;
|
|
30
|
+
const EVM_ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
|
|
31
|
+
/** A CDP end-user access token: opaque, bounded, never logged, never echoed. */
|
|
32
|
+
const ACCESS_TOKEN_SCHEMA = z.string().min(16).max(8192);
|
|
33
|
+
/* ── fence ── */
|
|
34
|
+
export const DELEGATION_FENCE_INPUT_SCHEMA = z.object({}).strict();
|
|
35
|
+
export const DELEGATION_FENCE_RESULT_SCHEMA = z
|
|
36
|
+
.object({
|
|
37
|
+
status: z.literal('present'),
|
|
38
|
+
/** What this call did: nothing (already v-current), created it, or updated our own stale lineage in place. */
|
|
39
|
+
action: z.enum(['unchanged', 'created', 'updated']),
|
|
40
|
+
policy_id: z.string(),
|
|
41
|
+
policy_name: z.string(),
|
|
42
|
+
rules_digest: z.string().regex(/^[0-9a-f]{64}$/),
|
|
43
|
+
per_swap_cap_usd: z.literal(MODEL_B_PER_SWAP_CAP_USD),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
/* ── status ── */
|
|
47
|
+
export const DELEGATION_STATUS_INPUT_SCHEMA = z
|
|
48
|
+
.object({ accessToken: ACCESS_TOKEN_SCHEMA })
|
|
49
|
+
.strict();
|
|
50
|
+
export const DELEGATION_STATUS_RESULT_SCHEMA = z
|
|
51
|
+
.object({
|
|
52
|
+
user_id: z.string().regex(END_USER_ID_PATTERN),
|
|
53
|
+
addresses: z.array(z.string().regex(EVM_ADDRESS_PATTERN)),
|
|
54
|
+
fence: z
|
|
55
|
+
.object({
|
|
56
|
+
present: z.boolean(),
|
|
57
|
+
policy_name: z.string(),
|
|
58
|
+
per_swap_cap_usd: z.literal(MODEL_B_PER_SWAP_CAP_USD),
|
|
59
|
+
rules_digest: z
|
|
60
|
+
.string()
|
|
61
|
+
.regex(/^[0-9a-f]{64}$/)
|
|
62
|
+
.optional(),
|
|
63
|
+
})
|
|
64
|
+
.strict(),
|
|
65
|
+
delegation: z
|
|
66
|
+
.object({
|
|
67
|
+
/** Active = reads back from CDP, not expired, and no revocation was requested on this server. */
|
|
68
|
+
active: z.boolean(),
|
|
69
|
+
expires_at: z.string().optional(),
|
|
70
|
+
})
|
|
71
|
+
.strict(),
|
|
72
|
+
revocation_requested_here: z.boolean(),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
75
|
+
/* ── revoke ── */
|
|
76
|
+
export const DELEGATION_REVOKE_INPUT_SCHEMA = z.union([
|
|
77
|
+
z.object({ accessToken: ACCESS_TOKEN_SCHEMA }).strict(),
|
|
78
|
+
z
|
|
79
|
+
.object({
|
|
80
|
+
userId: z.string().regex(END_USER_ID_PATTERN),
|
|
81
|
+
support_reason: z.string().min(8).max(200),
|
|
82
|
+
})
|
|
83
|
+
.strict(),
|
|
84
|
+
]);
|
|
85
|
+
export const DELEGATION_REVOKE_RESULT_SCHEMA = z
|
|
86
|
+
.object({
|
|
87
|
+
user_id: z.string().regex(END_USER_ID_PATTERN),
|
|
88
|
+
revoked: z.literal(true),
|
|
89
|
+
/** The grant no longer reads back from CDP (the revoke is confirmed by read-back, never inferred). */
|
|
90
|
+
reads_back: z.literal(false),
|
|
91
|
+
path: z.enum(['user', 'support']),
|
|
92
|
+
})
|
|
93
|
+
.strict();
|
|
94
|
+
/* ── fence status (public, read-only, no header) ── */
|
|
95
|
+
export const DELEGATION_FENCE_STATUS_INPUT_SCHEMA = z.object({}).strict();
|
|
96
|
+
export const DELEGATION_FENCE_STATUS_RESULT_SCHEMA = z
|
|
97
|
+
.object({
|
|
98
|
+
present: z.boolean(),
|
|
99
|
+
policy_name: z.string(),
|
|
100
|
+
per_swap_cap_usd: z.literal(MODEL_B_PER_SWAP_CAP_USD),
|
|
101
|
+
/** Otto's PUBLIC CDP project id (a UUID): the authority the agent CLI binds its mint to. */
|
|
102
|
+
project_id: z
|
|
103
|
+
.string()
|
|
104
|
+
.regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
|
|
105
|
+
.optional(),
|
|
106
|
+
rules_digest: z
|
|
107
|
+
.string()
|
|
108
|
+
.regex(/^[0-9a-f]{64}$/)
|
|
109
|
+
.optional(),
|
|
110
|
+
/** Why it is not present — a class, never a policy's contents. */
|
|
111
|
+
reason: z.enum(['absent', 'ambiguous', 'name_mismatch', 'digest_mismatch']).optional(),
|
|
112
|
+
})
|
|
113
|
+
.strict();
|
|
114
|
+
export const DELEGATION_FENCE_STATUS_TOOL_DEFINITION = Object.freeze({
|
|
115
|
+
name: DELEGATION_FENCE_STATUS_TOOL_NAME,
|
|
116
|
+
title: 'Read the Model-B delegation fence (public promise: present, name, rules digest, per-swap cap)',
|
|
117
|
+
description: `PUBLIC and read-only, no header: whether the Model-B project policy that fences every delegated send is installed and v-current on this server, with its versioned name, rules digest, the per-swap cap in USD, and Otto's PUBLIC CDP project id (a UUID — the authority an agent binds its mint to). Exposes ONLY the fence's public promise — never an end user, a grant, an address or a policy's contents. An agent about to mint a delegation (otto-execute delegate) reads this first, mints to the published project_id and no other, and refuses to mint when the fence is not present; the server enforces fence-before-authority on every delegated send regardless. Arguments: {}.`,
|
|
118
|
+
inputSchema: DELEGATION_FENCE_STATUS_INPUT_SCHEMA,
|
|
119
|
+
outputSchema: DELEGATION_FENCE_STATUS_RESULT_SCHEMA,
|
|
120
|
+
annotations: Object.freeze({
|
|
121
|
+
readOnlyHint: true,
|
|
122
|
+
destructiveHint: false,
|
|
123
|
+
idempotentHint: true,
|
|
124
|
+
openWorldHint: true,
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
|
+
const AUTH_SENTENCE = `Server-to-server only: the caller presents Otto's delegation secret in the \`${DELEGATION_AUTH_HEADER}\` request header (never in arguments); a call without it is refused before anything is read. The listed input schema is intentionally permissive — the strict schema is validated after the caller is authenticated.`;
|
|
128
|
+
export const DELEGATION_FENCE_TOOL_DEFINITION = Object.freeze({
|
|
129
|
+
name: DELEGATION_FENCE_TOOL_NAME,
|
|
130
|
+
title: 'Ensure the Model-B project policy (the delegation fence) is installed and reads back as v-current',
|
|
131
|
+
description: `Idempotent ensure-by-digest of the ONE CDP project-scope policy that fences every delegated send, then a read-back. Writes only when no project policy exists (creates it) or when the existing one is Otto's own lineage with stale rules (updates it in place). REFUSES BY NAME if a different project-scope policy is installed — it is never overwritten or deleted. Never downgrades a newer fence. An OPS action: at most one ensure runs project-wide at a time, never during a rolling deploy, never from the dApp (the mint gate reads fence.present from otto_delegation_status). Arguments: {}. Output: the policy id, versioned name, rules digest and the per-swap cap; the tool fails unless the fence reads back present under the written id. ${AUTH_SENTENCE}`,
|
|
132
|
+
inputSchema: DELEGATED_TOOL_TRANSPORT_SCHEMA,
|
|
133
|
+
strictInputSchema: DELEGATION_FENCE_INPUT_SCHEMA,
|
|
134
|
+
outputSchema: DELEGATION_FENCE_RESULT_SCHEMA,
|
|
135
|
+
annotations: Object.freeze({
|
|
136
|
+
readOnlyHint: false,
|
|
137
|
+
destructiveHint: false,
|
|
138
|
+
idempotentHint: true,
|
|
139
|
+
openWorldHint: true,
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
export const DELEGATION_STATUS_TOOL_DEFINITION = Object.freeze({
|
|
143
|
+
name: DELEGATION_STATUS_TOOL_NAME,
|
|
144
|
+
title: "Read one end user's delegation status (token-bound)",
|
|
145
|
+
description: `Read back, for the end user identified by a CDP access token, their CDP accounts, whether the Model-B fence is present, and whether an active delegation reads back (with its expiry). Arguments: { accessToken } — the server resolves the user from the token; a client-sent user id is refused. Output is bound to that user; nothing about any other user is returned. ${AUTH_SENTENCE}`,
|
|
146
|
+
inputSchema: DELEGATED_TOOL_TRANSPORT_SCHEMA,
|
|
147
|
+
strictInputSchema: DELEGATION_STATUS_INPUT_SCHEMA,
|
|
148
|
+
outputSchema: DELEGATION_STATUS_RESULT_SCHEMA,
|
|
149
|
+
annotations: Object.freeze({
|
|
150
|
+
readOnlyHint: true,
|
|
151
|
+
destructiveHint: false,
|
|
152
|
+
idempotentHint: true,
|
|
153
|
+
openWorldHint: true,
|
|
154
|
+
}),
|
|
155
|
+
});
|
|
156
|
+
export const DELEGATION_REVOKE_TOOL_DEFINITION = Object.freeze({
|
|
157
|
+
name: DELEGATION_REVOKE_TOOL_NAME,
|
|
158
|
+
title: "Revoke one end user's delegation, developer-side, confirmed by read-back",
|
|
159
|
+
description: `Revoke the end user's delegation from Otto's side and confirm by read-back that it no longer exists; from then on this server submits nothing for that user (fail-closed, even if the read-back had failed). Arguments: EITHER { accessToken } (the user path — the server resolves the user from the token) OR { userId, support_reason } (the support path, for an operator holding the server secret; the reason is logged, the token never is). ${AUTH_SENTENCE}`,
|
|
160
|
+
inputSchema: DELEGATED_TOOL_TRANSPORT_SCHEMA,
|
|
161
|
+
strictInputSchema: DELEGATION_REVOKE_INPUT_SCHEMA,
|
|
162
|
+
outputSchema: DELEGATION_REVOKE_RESULT_SCHEMA,
|
|
163
|
+
annotations: Object.freeze({
|
|
164
|
+
readOnlyHint: false,
|
|
165
|
+
destructiveHint: true,
|
|
166
|
+
idempotentHint: true,
|
|
167
|
+
openWorldHint: true,
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
//# sourceMappingURL=execution-delegation-admin-definition.js.map
|