nanocodex 0.6.5 → 0.6.7
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/README.md +311 -6
- package/THIRD-PARTY-LICENSES +23 -0
- package/browser/Agent.d.mts +5 -4
- package/browser/Agent.mjs +4 -0
- package/browser/Claude.d.mts +4 -0
- package/browser/Claude.mjs +11 -0
- package/browser/InlineAgent.mjs +19 -1
- package/browser/Voice.mjs +11 -4
- package/browser/VoiceSession.mjs +20 -6
- package/browser/host.d.mts +54 -1
- package/browser/host.mjs +65 -16
- package/browser/hostManagedWebSocket.mjs +11 -2
- package/browser/index.d.mts +3 -10
- package/browser/index.mjs +2 -5
- package/browser/socket-observations.mjs +203 -0
- package/cloud/Decorator.d.mts +2 -2
- package/cloud/Decorator.mjs +1 -0
- package/cloud/Dialog.d.mts +13 -0
- package/cloud/Dialog.mjs +52 -6
- package/cloud/Transport.d.mts +0 -11
- package/cloud/Transport.mjs +1 -386
- package/cloud/actions/account.d.mts +12 -0
- package/cloud/actions/account.mjs +11 -0
- package/cloud/actions/agent.mjs +3 -0
- package/cloud/actions/connection.d.mts +3 -1
- package/cloud/actions/connection.mjs +19 -4
- package/cloud/index.d.mts +1 -1
- package/cloud/index.mjs +1 -1
- package/cloud/internal.mjs +19 -4
- package/cloud/mercator.mjs +1 -1
- package/cloud/mock.d.mts +17 -0
- package/cloud/mock.mjs +392 -0
- package/cloud/types.d.mts +4 -1
- package/cloudflare/Agent.d.mts +6 -0
- package/cloudflare/Agent.mjs +190 -10
- package/cloudflare/agent-settings.d.mts +1 -1
- package/cloudflare/agent-settings.mjs +12 -3
- package/cloudflare/egress.mjs +9 -6
- package/cloudflare/gateway-binding-responses.mjs +3 -3
- package/cloudflare/gateway-responses.d.mts +1 -0
- package/cloudflare/gateway-responses.mjs +7 -7
- package/cloudflare/managed-auth.d.mts +4 -4
- package/cloudflare/managed-auth.mjs +20 -2
- package/cloudflare/managed-live.d.mts +6 -0
- package/cloudflare/managed-live.mjs +56 -1
- package/cloudflare/tracing.d.mts +16 -0
- package/cloudflare/tracing.mjs +47 -0
- package/cloudflare/workers-ai-responses.d.mts +3 -2
- package/cloudflare/workers-ai-responses.mjs +83 -8
- package/host/Agent.d.mts +6 -2
- package/host/Claude.d.mts +3 -0
- package/host/Claude.mjs +2 -0
- package/host/index.d.mts +6 -10
- package/host/index.mjs +2 -5
- package/index.d.mts +11 -10
- package/index.mjs +0 -5
- package/internal.mjs +33 -3
- package/managed/Agent.d.mts +10 -3
- package/managed/Agent.mjs +60 -10
- package/managed/ManagedError.d.mts +3 -0
- package/managed/Voice.mjs +3 -1
- package/managed/internal.mjs +2 -1
- package/node/Agent.d.mts +6 -2
- package/node/Agent.mjs +14 -1
- package/node/Claude.d.mts +4 -0
- package/node/Claude.mjs +24 -0
- package/node/host.mjs +18 -9
- package/node/index.d.mts +6 -10
- package/node/index.mjs +2 -5
- package/package.json +45 -9
- package/pkg-node/nanocodex.d.ts +123 -2
- package/pkg-node/nanocodex.js +879 -89
- package/pkg-node/nanocodex_bg.wasm.d.ts +49 -3
- package/pkg-web/.nanocodex-bindgen-stamp +4 -4
- package/pkg-web/nanocodex-build.json +1 -1
- package/pkg-web/nanocodex.d.ts +172 -5
- package/pkg-web/nanocodex.js +872 -88
- package/pkg-web/nanocodex_bg.js +872 -88
- package/pkg-web/nanocodex_bg.wasm +0 -0
- package/pkg-web/nanocodex_bg.wasm.d.ts +49 -3
- package/pkg-web/nanocodex_worker.js +1 -1
- package/runtime/claude-host.mjs +308 -0
- package/runtime/claude-subscription.mjs +79 -0
- package/runtime/claude.d.mts +112 -0
- package/runtime/claude.mjs +221 -0
- package/runtime/code-effect-identity.mjs +123 -0
- package/runtime/harnesses.mjs +51 -0
- package/runtime/mcp-payment.mjs +8 -0
- package/runtime/mcp-runtime.d.mts +10 -0
- package/runtime/mcp-runtime.mjs +242 -55
- package/runtime/mercator.mjs +2 -0
- package/runtime/quickjs-evaluator.mjs +23 -11
- package/runtime/response-controls.mjs +16 -10
- package/runtime/responses-http.mjs +4 -2
- package/runtime/retry-after.d.mts +10 -0
- package/runtime/retry-after.mjs +86 -0
- package/runtime/steer-receipt.mjs +8 -1
- package/runtime/subagent-routing.d.mts +4 -2
- package/runtime/subagent-routing.mjs +9 -3
- package/runtime/subagents.d.mts +8 -3
- package/services/account.d.mts +76 -0
- package/services/account.mjs +151 -0
- package/services/hosted.mjs +72 -0
- package/services/index.d.mts +95 -0
- package/services/index.mjs +192 -0
- package/services/scope.mjs +36 -0
- package/{runtime/tempo-provider.d.mts → tempo/index.d.mts} +20 -1
- package/{runtime/tempo-provider.mjs → tempo/index.mjs} +39 -3
- package/tools/browser/browserShell.mjs +18 -8
- package/tools/browser/python.worker.mjs +17 -1
- package/tools/browser/threadGit.mjs +12 -2
- package/tools/environment.d.mts +17 -0
- package/tools/environment.mjs +24 -1
- package/tools/index.d.mts +6 -3
- package/types.d.mts +107 -6
- package/worker/Claude.d.mts +4 -0
- package/worker/Claude.mjs +11 -0
- package/worker/ClaudeSubscription.d.mts +24 -0
- package/worker/ClaudeSubscription.mjs +9 -0
- package/worker/index.d.mts +9 -0
- package/worker/index.mjs +4 -0
package/cloud/mock.mjs
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
// In-memory Nanocodex Connect API for tests and demos. Its prepared access-key
|
|
2
|
+
// witness is the real EIP-191 digest, so this module needs the optional `ox`
|
|
3
|
+
// peer dependency; the core `nanocodex/connect` entry point never imports it.
|
|
4
|
+
import { Hex, PersonalMessage } from "ox";
|
|
5
|
+
|
|
6
|
+
import { HttpError, InvalidResponseError } from "./Errors.mjs";
|
|
7
|
+
import { from } from "./Transport.mjs";
|
|
8
|
+
|
|
9
|
+
export const MOCK_ACCOUNT_ADDRESS = "0x8ba1f109551bd432803012645ac136ddd64dba72";
|
|
10
|
+
export const MOCK_MACHINE_USD_ADDRESS = "0x20c0000000000000000000000000000000000001";
|
|
11
|
+
|
|
12
|
+
export function mock(options = {}) {
|
|
13
|
+
const accountAddress = options.accountAddress ?? MOCK_ACCOUNT_ADDRESS;
|
|
14
|
+
const machineUsdAddress = options.machineUsdAddress ?? MOCK_MACHINE_USD_ADDRESS;
|
|
15
|
+
const appName = options.appName ?? "Acme Workspace";
|
|
16
|
+
const grants = new Map();
|
|
17
|
+
let sequence = 0;
|
|
18
|
+
|
|
19
|
+
return from({
|
|
20
|
+
key: options.key ?? "mock",
|
|
21
|
+
name: options.name ?? "Nanocodex mock",
|
|
22
|
+
type: "mock",
|
|
23
|
+
setup({ appId }) {
|
|
24
|
+
return {
|
|
25
|
+
baseUrl: "memory://nanocodex",
|
|
26
|
+
async request(request) {
|
|
27
|
+
if (request.method === "POST" && request.path === "/v1/connections") {
|
|
28
|
+
const authorization = request.body?.key_authorization;
|
|
29
|
+
const keyId = authorization?.keyId ?? authorization?.address;
|
|
30
|
+
if (typeof keyId !== "string") throw new InvalidResponseError("key authorization is missing keyId");
|
|
31
|
+
const expiry = authorization.expiry ?? Math.floor(Date.now() / 1000) + 30 * 86_400;
|
|
32
|
+
const connectors = requestedConnectors(request.body?.requested_connectors);
|
|
33
|
+
const mcpConnections = requestedMcpConnections(request.body?.requested_mcp_connections);
|
|
34
|
+
const grantId = mockHex(`${appId}:${accountAddress}:${keyId}`, 32);
|
|
35
|
+
const connectorConnections = mockConnectorConnections(connectors, grantId);
|
|
36
|
+
const wire = {
|
|
37
|
+
grant_token: `mock-grant-${grantId.slice(2)}`,
|
|
38
|
+
account_address: accountAddress,
|
|
39
|
+
agent_id: `agent_${grantId.slice(2, 14)}`,
|
|
40
|
+
grant: {
|
|
41
|
+
id: grantId,
|
|
42
|
+
permission: request.body?.permission ?? "agent.run",
|
|
43
|
+
status: "active",
|
|
44
|
+
expires_at: expiry,
|
|
45
|
+
capabilities: [
|
|
46
|
+
"nanocodex.agent",
|
|
47
|
+
"mercator.boost",
|
|
48
|
+
"mpp.mach",
|
|
49
|
+
...connectors,
|
|
50
|
+
...mcpConnections.map(({ id }) => `mcp:${id}`),
|
|
51
|
+
],
|
|
52
|
+
connector_connections: connectorConnections,
|
|
53
|
+
mcp_connections: mcpConnections,
|
|
54
|
+
},
|
|
55
|
+
access_key: {
|
|
56
|
+
address: keyId,
|
|
57
|
+
chain_id: String(authorization.chainId ?? 4217),
|
|
58
|
+
key_id: keyId,
|
|
59
|
+
key_type: authorization.keyType ?? "p256",
|
|
60
|
+
limits: authorization.limits ?? [{ token: machineUsdAddress, limit: "10000000", period: 86_400 }],
|
|
61
|
+
scopes: authorization.scopes ?? [],
|
|
62
|
+
witness: authorization.witness ?? mockHex(`${grantId}:witness`, 32),
|
|
63
|
+
expiry,
|
|
64
|
+
authorization: request.body?.signed_key_authorization ?? "0x01",
|
|
65
|
+
},
|
|
66
|
+
mpp: {
|
|
67
|
+
token: machineUsdAddress,
|
|
68
|
+
symbol: "MACH",
|
|
69
|
+
balance_status: "ready",
|
|
70
|
+
settlement_token: "0x20C000000000000000000000b9537d11c60E8b50",
|
|
71
|
+
settlement_symbol: "USDC.e",
|
|
72
|
+
settlement_balance_atomics: "0",
|
|
73
|
+
limit_atomics: "10000000",
|
|
74
|
+
max_per_request_atomics: "250000",
|
|
75
|
+
period: 86_400,
|
|
76
|
+
balance_atomics: "0",
|
|
77
|
+
spent_atomics: "0",
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
grants.set(grantId, wire);
|
|
81
|
+
return wire;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (request.method === "GET" && request.path.match(/^\/v1\/grants\/0x[0-9a-f]+$/)) {
|
|
85
|
+
return requiredGrant(grants, request.path.split("/")[3]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (request.method === "POST" && request.path === "/v1/connections/prepare") {
|
|
89
|
+
const permission = request.body?.permission;
|
|
90
|
+
if (typeof permission !== "string" || permission.length === 0) {
|
|
91
|
+
throw new TypeError("connection permission must be a non-empty string");
|
|
92
|
+
}
|
|
93
|
+
const resources = request.body?.resources ?? [];
|
|
94
|
+
if (!Array.isArray(resources) || resources.some((resource) => typeof resource !== "string" || resource.length === 0)) {
|
|
95
|
+
throw new TypeError("connection resources must be non-empty strings");
|
|
96
|
+
}
|
|
97
|
+
const connectors = connectorResources(resources);
|
|
98
|
+
const id = `req_${++sequence}`;
|
|
99
|
+
const accessPolicy = request.body?.authorize_access_key;
|
|
100
|
+
const keyId = accessPolicy?.address ?? mockHex(`${appId}:${accountAddress}:${permission}:key`, 20);
|
|
101
|
+
const tokenLimit = accessPolicy?.limits?.find((limit) => limit.token === machineUsdAddress);
|
|
102
|
+
const expiry = accessPolicy?.expiry ?? Math.floor(Date.now() / 1000) + 30 * 86_400;
|
|
103
|
+
const dailyLimit = tokenLimit?.limit ?? "10000000";
|
|
104
|
+
const period = tokenLimit?.period ?? 86_400;
|
|
105
|
+
const appOrigin = options.appOrigin ?? "https://app.example";
|
|
106
|
+
const appUrl = new URL(appOrigin);
|
|
107
|
+
const nonce = mockHex(`${id}:siwe-nonce`, 8).slice(2);
|
|
108
|
+
const issuedAt = new Date().toISOString();
|
|
109
|
+
const authMessage = [
|
|
110
|
+
`${appUrl.host} wants you to sign in with your Ethereum account:`,
|
|
111
|
+
"0x0000000000000000000000000000000000000000",
|
|
112
|
+
"",
|
|
113
|
+
`${appName} wants to connect your Nanocodex agent.`,
|
|
114
|
+
"",
|
|
115
|
+
`URI: ${appUrl.origin}`,
|
|
116
|
+
"Version: 1",
|
|
117
|
+
"Chain ID: 4217",
|
|
118
|
+
`Nonce: ${nonce}`,
|
|
119
|
+
`Issued At: ${issuedAt}`,
|
|
120
|
+
...(resources.length > 0
|
|
121
|
+
? ["Resources:", ...resources.map((resource) => `- ${resource}`)]
|
|
122
|
+
: []),
|
|
123
|
+
].join("\n");
|
|
124
|
+
return {
|
|
125
|
+
request_id: id,
|
|
126
|
+
app: { id: appId, name: appName, origin: appOrigin },
|
|
127
|
+
account_address: accountAddress,
|
|
128
|
+
auth: {
|
|
129
|
+
message: authMessage,
|
|
130
|
+
resources,
|
|
131
|
+
},
|
|
132
|
+
permission: {
|
|
133
|
+
id: permission,
|
|
134
|
+
title: "Use your Nanocodex agent",
|
|
135
|
+
description: "Run an app-owned Nanocodex agent with your approved capabilities.",
|
|
136
|
+
connectors: [
|
|
137
|
+
{ id: "agent", name: "Nanocodex", detail: "Instantiate the real Nanocodex runtime in this app" },
|
|
138
|
+
{ id: "mercator", name: "BOOST with Mercator", detail: "Find and compose the right tools, paying per call through MPP" },
|
|
139
|
+
...connectors.map((provider) => ({
|
|
140
|
+
id: provider,
|
|
141
|
+
name: connectorName(provider),
|
|
142
|
+
detail: `Use the connected ${connectorName(provider)} account through the grant`,
|
|
143
|
+
})),
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
access_key: {
|
|
147
|
+
address: keyId,
|
|
148
|
+
chain_id: accessPolicy?.chainId ?? "4217",
|
|
149
|
+
key_id: keyId,
|
|
150
|
+
public_key: accessPolicy?.publicKey ?? mockHex(`${id}:public`, 65),
|
|
151
|
+
key_type: accessPolicy?.keyType ?? "p256",
|
|
152
|
+
limits: accessPolicy?.limits ?? [{ token: machineUsdAddress, limit: dailyLimit, period }],
|
|
153
|
+
scopes: accessPolicy?.scopes ?? [],
|
|
154
|
+
witness: PersonalMessage.getSignPayload(Hex.fromString(authMessage)),
|
|
155
|
+
expiry,
|
|
156
|
+
},
|
|
157
|
+
mpp: {
|
|
158
|
+
token: machineUsdAddress,
|
|
159
|
+
symbol: "MACH",
|
|
160
|
+
settlement_token: "0x20C000000000000000000000b9537d11c60E8b50",
|
|
161
|
+
settlement_symbol: "USDC.e",
|
|
162
|
+
settlement_balance_atomics: "0",
|
|
163
|
+
limit_atomics: dailyLimit,
|
|
164
|
+
period,
|
|
165
|
+
max_per_request_atomics: "250000",
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (request.method === "POST" && request.path === "/v1/connections/complete") {
|
|
171
|
+
const prepared = request.body?.prepared;
|
|
172
|
+
const approval = request.body?.approval;
|
|
173
|
+
if (!prepared || !approval || approval.approved !== true) {
|
|
174
|
+
throw new InvalidResponseError("connection approval is malformed");
|
|
175
|
+
}
|
|
176
|
+
const approvedCapabilities = approval.capabilities;
|
|
177
|
+
if (
|
|
178
|
+
approvedCapabilities?.keyAuthorization?.witness !== prepared.access_key.witness
|
|
179
|
+
|| approvedCapabilities?.personalSign?.message !== prepared.auth.message
|
|
180
|
+
|| typeof approvedCapabilities?.personalSign?.keyAuthorization !== "string"
|
|
181
|
+
|| typeof approvedCapabilities?.signature !== "string"
|
|
182
|
+
) {
|
|
183
|
+
throw new InvalidResponseError("connection approval is not bound to the SIWE challenge and key authorization");
|
|
184
|
+
}
|
|
185
|
+
const grantId = mockHex(`${prepared.app.id}:${prepared.account_address}:${prepared.access_key.key_id}`, 32);
|
|
186
|
+
const wire = {
|
|
187
|
+
grant_token: `mock-grant-${grantId.slice(2)}`,
|
|
188
|
+
account_address: prepared.account_address,
|
|
189
|
+
agent_id: `agent_${grantId.slice(2, 14)}`,
|
|
190
|
+
grant: {
|
|
191
|
+
id: grantId,
|
|
192
|
+
permission: prepared.permission.id,
|
|
193
|
+
status: "active",
|
|
194
|
+
expires_at: prepared.access_key.expiry,
|
|
195
|
+
capabilities: prepared.permission.connectors.map(({ id }) => id),
|
|
196
|
+
},
|
|
197
|
+
access_key: prepared.access_key,
|
|
198
|
+
mpp: {
|
|
199
|
+
...prepared.mpp,
|
|
200
|
+
balance_atomics: "0",
|
|
201
|
+
balance_status: "ready",
|
|
202
|
+
spent_atomics: "0",
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
grants.set(grantId, wire);
|
|
206
|
+
return wire;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (request.method === "POST" && request.path === "/v1/connections/disconnect") {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (request.method === "GET" && request.path.match(/^\/v1\/grants\/0x[0-9a-f]+\/mpp\/balance$/)) {
|
|
214
|
+
const wire = requiredGrant(grants, request.path.split("/")[3]);
|
|
215
|
+
wire.mpp = { ...wire.mpp, balance_status: "ready" };
|
|
216
|
+
return wire;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (request.method === "POST" && request.path.match(/^\/v1\/grants\/0x[0-9a-f]+\/revoke$/)) {
|
|
220
|
+
const grantId = request.path.split("/")[3];
|
|
221
|
+
const wire = requiredGrant(grants, grantId);
|
|
222
|
+
wire.grant = { ...wire.grant, status: "revoked" };
|
|
223
|
+
return wire.grant;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (request.method === "GET" && request.path === "/v1/machine-usd/config") {
|
|
227
|
+
return {
|
|
228
|
+
chain_id: 4217,
|
|
229
|
+
min_usd_amount_cents: 500,
|
|
230
|
+
max_usd_amount_cents: 10_000,
|
|
231
|
+
stripe_publishable_key: "pk_test_nanocodex",
|
|
232
|
+
token_address: machineUsdAddress,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (request.method === "POST" && request.path.match(/^\/v1\/grants\/0x[0-9a-f]+\/fund$/)) {
|
|
237
|
+
const grantId = request.path.split("/")[3];
|
|
238
|
+
const wire = requiredActiveGrant(grants, grantId);
|
|
239
|
+
const usdAmountCents = request.body?.usd_amount_cents;
|
|
240
|
+
if (!Number.isSafeInteger(usdAmountCents) || usdAmountCents < 500 || usdAmountCents > 10_000) {
|
|
241
|
+
throw new TypeError("MACH amount must be from 500 through 10000 cents");
|
|
242
|
+
}
|
|
243
|
+
const amount = BigInt(usdAmountCents) * 10_000n;
|
|
244
|
+
wire.mpp = {
|
|
245
|
+
...wire.mpp,
|
|
246
|
+
balance_atomics: String(BigInt(wire.mpp.balance_atomics) + amount),
|
|
247
|
+
};
|
|
248
|
+
return {
|
|
249
|
+
order: {
|
|
250
|
+
id: `ord_${++sequence}`,
|
|
251
|
+
status: "complete",
|
|
252
|
+
usd_amount_cents: usdAmountCents,
|
|
253
|
+
machine_usd_amount_atomics: String(amount),
|
|
254
|
+
issuance_transaction_hash: mockHex(`fund:${sequence}`, 32),
|
|
255
|
+
},
|
|
256
|
+
connection: wire,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (request.method === "POST" && request.path.match(/^\/v1\/grants\/0x[0-9a-f]+\/mpp\/charge$/)) {
|
|
261
|
+
const grantId = request.path.split("/")[3];
|
|
262
|
+
const wire = requiredActiveGrant(grants, grantId);
|
|
263
|
+
const amount = BigInt(request.body?.amount_atomics ?? "-1");
|
|
264
|
+
const max = BigInt(wire.mpp.max_per_request_atomics);
|
|
265
|
+
const spent = BigInt(wire.mpp.spent_atomics);
|
|
266
|
+
const limit = BigInt(wire.mpp.limit_atomics);
|
|
267
|
+
const balance = BigInt(wire.mpp.balance_atomics);
|
|
268
|
+
if (amount <= 0n) throw new TypeError("MPP amount must be positive");
|
|
269
|
+
if (amount > max) throw new HttpError(403, "This payment exceeds the per-request permission.", { code: "mpp_request_limit_exceeded" });
|
|
270
|
+
if (spent + amount > limit) throw new HttpError(403, "This payment exceeds the daily MPP permission.", { code: "mpp_period_limit_exceeded" });
|
|
271
|
+
if (amount > balance) throw new HttpError(402, "Add MACH before paying for this capability.", { code: "machine_usd_required" });
|
|
272
|
+
wire.mpp = {
|
|
273
|
+
...wire.mpp,
|
|
274
|
+
balance_atomics: String(balance - amount),
|
|
275
|
+
spent_atomics: String(spent + amount),
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
receipt: {
|
|
279
|
+
id: `mpp_${++sequence}`,
|
|
280
|
+
amount_atomics: String(amount),
|
|
281
|
+
origin: request.body?.origin,
|
|
282
|
+
transaction_hash: mockHex(`mpp:${sequence}`, 32),
|
|
283
|
+
},
|
|
284
|
+
connection: wire,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
throw new HttpError(404, `No mock route for ${request.method ?? "GET"} ${request.path}`, {
|
|
289
|
+
code: "mock_route_not_found",
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function requiredGrant(grants, grantId) {
|
|
298
|
+
const grant = grants.get(grantId);
|
|
299
|
+
if (!grant) throw new HttpError(404, "Nanocodex grant not found", { code: "grant_not_found" });
|
|
300
|
+
return grant;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function requestedConnectors(value) {
|
|
304
|
+
if (value === undefined) return [];
|
|
305
|
+
if (!Array.isArray(value)) throw new TypeError("requested_connectors must be an array");
|
|
306
|
+
const supported = [
|
|
307
|
+
"github",
|
|
308
|
+
"gmail",
|
|
309
|
+
"gdrive",
|
|
310
|
+
"gcalendar",
|
|
311
|
+
"gtasks",
|
|
312
|
+
"gdocs",
|
|
313
|
+
"gsheets",
|
|
314
|
+
"gslides",
|
|
315
|
+
"gcontacts",
|
|
316
|
+
"slack",
|
|
317
|
+
"x",
|
|
318
|
+
"spotify",
|
|
319
|
+
"soundcloud",
|
|
320
|
+
"link",
|
|
321
|
+
"chatgpt",
|
|
322
|
+
];
|
|
323
|
+
if (value.some((provider) => !supported.includes(provider))) {
|
|
324
|
+
throw new TypeError("requested_connectors contains an unsupported provider");
|
|
325
|
+
}
|
|
326
|
+
return supported.filter((provider) => value.includes(provider));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function requestedMcpConnections(value) {
|
|
330
|
+
if (value === undefined) return [];
|
|
331
|
+
if (!Array.isArray(value) || value.length > 16
|
|
332
|
+
|| value.some((id) => typeof id !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(id))
|
|
333
|
+
|| new Set(value).size !== value.length) {
|
|
334
|
+
throw new TypeError("requested_mcp_connections must contain exact hosted connection IDs");
|
|
335
|
+
}
|
|
336
|
+
return value.map((id) => ({ id, name: "MCP connection" }));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function connectorResources(resources) {
|
|
340
|
+
return requestedConnectors(resources
|
|
341
|
+
.filter((resource) => resource.startsWith("urn:nanocodex:connector:"))
|
|
342
|
+
.map((resource) => resource.slice("urn:nanocodex:connector:".length)));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function connectorName(provider) {
|
|
346
|
+
return ({
|
|
347
|
+
github: "GitHub",
|
|
348
|
+
gmail: "Gmail",
|
|
349
|
+
gdrive: "Google Drive",
|
|
350
|
+
gcalendar: "Google Calendar",
|
|
351
|
+
gtasks: "Google Tasks",
|
|
352
|
+
gdocs: "Google Docs",
|
|
353
|
+
gsheets: "Google Sheets",
|
|
354
|
+
gslides: "Google Slides",
|
|
355
|
+
gcontacts: "Google Contacts",
|
|
356
|
+
slack: "Slack",
|
|
357
|
+
x: "X",
|
|
358
|
+
spotify: "Spotify",
|
|
359
|
+
soundcloud: "SoundCloud",
|
|
360
|
+
link: "Stripe Link",
|
|
361
|
+
chatgpt: "ChatGPT",
|
|
362
|
+
})[provider];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function mockConnectorConnections(connectors, grantId) {
|
|
366
|
+
return Object.fromEntries(connectors
|
|
367
|
+
.filter((connector) => connector !== "chatgpt")
|
|
368
|
+
.map((connector) => [
|
|
369
|
+
connector,
|
|
370
|
+
[mockHex(`${grantId}:${connector}:connection`, 32).slice(2, 45)],
|
|
371
|
+
]));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function requiredActiveGrant(grants, grantId) {
|
|
375
|
+
const grant = requiredGrant(grants, grantId);
|
|
376
|
+
if (grant.grant.status !== "active") {
|
|
377
|
+
throw new HttpError(403, "This Nanocodex grant has been revoked.", { code: "grant_revoked" });
|
|
378
|
+
}
|
|
379
|
+
return grant;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function mockHex(seed, bytes) {
|
|
383
|
+
let state = 2166136261;
|
|
384
|
+
let output = "";
|
|
385
|
+
for (let index = 0; output.length < bytes * 2; index += 1) {
|
|
386
|
+
const code = seed.charCodeAt(index % seed.length) + index;
|
|
387
|
+
state ^= code;
|
|
388
|
+
state = Math.imul(state, 16777619) >>> 0;
|
|
389
|
+
output += state.toString(16).padStart(8, "0");
|
|
390
|
+
}
|
|
391
|
+
return `0x${output.slice(0, bytes * 2)}`;
|
|
392
|
+
}
|
package/cloud/types.d.mts
CHANGED
|
@@ -22,6 +22,7 @@ export type ConnectorConnection = Readonly<{
|
|
|
22
22
|
label: string;
|
|
23
23
|
accountId?: string | undefined;
|
|
24
24
|
capabilities?: readonly CloudAccount[] | undefined;
|
|
25
|
+
scopes?: readonly string[] | undefined;
|
|
25
26
|
}>;
|
|
26
27
|
|
|
27
28
|
/** Provider-neutral status for a connector service capability. */
|
|
@@ -119,6 +120,7 @@ export type ConnectAgent = Readonly<{
|
|
|
119
120
|
}>;
|
|
120
121
|
|
|
121
122
|
export type Grant = Readonly<{
|
|
123
|
+
services?: import("../services/index.mjs").ServiceCapabilities | undefined;
|
|
122
124
|
id: Hex;
|
|
123
125
|
permission: string;
|
|
124
126
|
status: "active" | "revoked" | "expired";
|
|
@@ -152,7 +154,8 @@ export type MppPermission = Readonly<{
|
|
|
152
154
|
}>;
|
|
153
155
|
|
|
154
156
|
type ConnectionBase = Readonly<{
|
|
155
|
-
|
|
157
|
+
/** Absent on a standalone services grant. */
|
|
158
|
+
agentId?: string;
|
|
156
159
|
grant: Grant;
|
|
157
160
|
}>;
|
|
158
161
|
|
package/cloudflare/Agent.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
AgentEvent,
|
|
6
6
|
DefaultAgent,
|
|
7
7
|
ToolConfiguration,
|
|
8
|
+
SessionSnapshot,
|
|
8
9
|
} from "../types.mjs";
|
|
9
10
|
import type { CloudflareDurableObjectStorage } from "../runtime/cloudflare-durability-store.mjs";
|
|
10
11
|
import type { Tool as SubagentTool } from "../runtime/subagents.mjs";
|
|
@@ -52,6 +53,9 @@ export type Agent<extended extends object = {}> =
|
|
|
52
53
|
): Agent<extended & extension>;
|
|
53
54
|
}>;
|
|
54
55
|
|
|
56
|
+
/** Copies the exact latest committed model boundary; rejects before the first safe boundary. */
|
|
57
|
+
export function checkpoint(agent: Agent): Promise<SessionSnapshot>;
|
|
58
|
+
|
|
55
59
|
/** Removes the package-owned durable history for one Cloudflare Agent. */
|
|
56
60
|
export function destroy(owner: DurableObjectOwner): void;
|
|
57
61
|
|
|
@@ -92,6 +96,8 @@ export function create(owner: create.Owner, options?: create.Options): Promise<c
|
|
|
92
96
|
export declare namespace create {
|
|
93
97
|
type Owner = DurableObjectOwner;
|
|
94
98
|
type Options = Readonly<{
|
|
99
|
+
/** Observer-only instant steering; disabled unless explicitly enabled. */
|
|
100
|
+
instantToolSteering?: boolean | undefined;
|
|
95
101
|
/** Awaited host preservation barrier; scoped to this root, never inherited by children. */
|
|
96
102
|
beforeCompaction?: AgentOptions["beforeCompaction"];
|
|
97
103
|
/** Stable portable state identity. It cannot change after first construction or import. */
|