@nylorun/runtime 0.13.0-beta → 0.15.0-beta
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/CHANGELOG.md +434 -0
- package/README.md +137 -41
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +40 -10
- package/dist/adapters/replication/pgoutput.d.ts +21 -0
- package/dist/adapters/replication/pgoutput.js +170 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +570 -0
- package/dist/api/a2a/card.d.ts +15 -0
- package/dist/api/a2a/card.js +24 -0
- package/dist/api/a2a/endpoint.d.ts +8 -0
- package/dist/api/a2a/endpoint.js +81 -0
- package/dist/api/a2a/protocol.d.ts +144 -0
- package/dist/api/a2a/protocol.js +188 -0
- package/dist/api/a2a/routes.d.ts +19 -0
- package/dist/api/a2a/routes.js +289 -0
- package/dist/api/a2a/tasks.d.ts +50 -0
- package/dist/api/a2a/tasks.js +186 -0
- package/dist/api/ag-ui/endpoint.d.ts +8 -0
- package/dist/api/ag-ui/endpoint.js +126 -0
- package/dist/api/ag-ui/history.d.ts +7 -0
- package/dist/api/ag-ui/history.js +60 -0
- package/dist/api/ag-ui/routes.d.ts +32 -0
- package/dist/api/ag-ui/routes.js +305 -0
- package/dist/api/ag-ui/session-id.d.ts +1 -0
- package/dist/api/ag-ui/session-id.js +12 -0
- package/dist/api/ag-ui/sse.d.ts +9 -0
- package/dist/api/ag-ui/sse.js +7 -0
- package/dist/api/ag-ui/translate.d.ts +38 -0
- package/dist/api/ag-ui/translate.js +222 -0
- package/dist/api/components.d.ts +8065 -0
- package/dist/api/components.js +82 -0
- package/dist/api/gate/keys.d.ts +14 -0
- package/dist/api/gate/keys.js +59 -0
- package/dist/api/gate/routes.d.ts +34 -0
- package/dist/api/gate/routes.js +235 -0
- package/dist/api/http/app.d.ts +18 -0
- package/dist/api/http/app.js +58 -0
- package/dist/api/http/body.d.ts +4 -0
- package/dist/api/http/body.js +37 -0
- package/dist/api/http/define.d.ts +39 -0
- package/dist/api/http/define.js +122 -0
- package/dist/api/http/respond.d.ts +9 -0
- package/dist/api/http/respond.js +44 -0
- package/dist/api/http/routes/access.d.ts +24 -0
- package/dist/api/http/routes/access.js +266 -0
- package/dist/api/http/routes/actions.d.ts +8 -0
- package/dist/api/http/routes/actions.js +76 -0
- package/dist/api/http/routes/endpoints.d.ts +7 -0
- package/dist/api/http/routes/endpoints.js +100 -0
- package/dist/api/http/routes/sessions.d.ts +3 -0
- package/dist/api/http/routes/sessions.js +205 -0
- package/dist/api/http/routes/tenant.d.ts +11 -0
- package/dist/api/http/routes/tenant.js +244 -0
- package/dist/api/http/routes/vaults.d.ts +8 -0
- package/dist/api/http/routes/vaults.js +167 -0
- package/dist/api/openapi.d.ts +19 -0
- package/dist/api/openapi.js +137 -0
- package/dist/api/route.d.ts +12 -0
- package/dist/api/route.js +4 -0
- package/dist/core/bearer.d.ts +8 -0
- package/dist/core/bearer.js +11 -0
- package/dist/core/flow-host.d.ts +7 -15
- package/dist/core/flow-host.js +16 -41
- package/dist/core/sandbox-routes.d.ts +12 -7
- package/dist/core/sandbox-routes.js +24 -25
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +33 -13
- package/dist/execution/types.d.ts +38 -6
- package/dist/execution/types.js +12 -3
- package/dist/gates/contract.d.ts +51 -0
- package/dist/gates/contract.js +37 -0
- package/dist/gates/handler.d.ts +21 -0
- package/dist/gates/handler.js +55 -0
- package/dist/gates/http-client.d.ts +12 -0
- package/dist/gates/http-client.js +160 -0
- package/dist/gates/in-process.d.ts +32 -0
- package/dist/gates/in-process.js +42 -0
- package/dist/gates/inflight.d.ts +39 -0
- package/dist/gates/inflight.js +70 -0
- package/dist/gates/mcp-handler.d.ts +35 -0
- package/dist/gates/mcp-handler.js +142 -0
- package/dist/gates/meter.d.ts +24 -0
- package/dist/gates/meter.js +165 -0
- package/dist/gates/model-gate.d.ts +45 -0
- package/dist/gates/model-gate.js +1 -0
- package/dist/gates/tenant-vaults.d.ts +64 -0
- package/dist/gates/tenant-vaults.js +94 -0
- package/dist/gates/tool-calls.d.ts +51 -0
- package/dist/gates/tool-calls.js +64 -0
- package/dist/gates/tool-client.d.ts +12 -0
- package/dist/gates/tool-client.js +201 -0
- package/dist/gates/tool-contract.d.ts +116 -0
- package/dist/gates/tool-contract.js +64 -0
- package/dist/gates/tool-gate.d.ts +45 -0
- package/dist/gates/tool-gate.js +10 -0
- package/dist/host/admin-api.d.ts +23 -0
- package/dist/host/admin-api.js +74 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +181 -0
- package/dist/host/config-for.d.ts +3 -1
- package/dist/host/config-for.js +3 -2
- package/dist/host/config.d.ts +11 -0
- package/dist/host/cors.d.ts +27 -0
- package/dist/host/cors.js +64 -0
- package/dist/host/create-host.d.ts +36 -8
- package/dist/host/create-host.js +161 -330
- package/dist/host/execution.d.ts +20 -18
- package/dist/host/execution.js +2 -2
- package/dist/host/gates.d.ts +58 -0
- package/dist/host/gates.js +127 -0
- package/dist/host/http.d.ts +20 -4
- package/dist/host/http.js +81 -23
- package/dist/host/index.d.ts +1 -1
- package/dist/host/main.js +189 -58
- package/dist/host/stack-config.d.ts +91 -19
- package/dist/host/stack-config.js +246 -17
- package/dist/infra/database.d.ts +2 -2
- package/dist/infra/execution.d.ts +5 -5
- package/dist/infra/execution.js +8 -6
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/workers.d.ts +17 -17
- package/dist/infra/workers.js +9 -9
- package/dist/keys/client.d.ts +9 -0
- package/dist/keys/client.js +70 -0
- package/dist/keys/contract.d.ts +24 -0
- package/dist/keys/contract.js +12 -0
- package/dist/keys/keys.d.ts +41 -0
- package/dist/keys/keys.js +50 -0
- package/dist/mcp/connect.d.ts +43 -3
- package/dist/mcp/connect.js +12 -5
- package/dist/mcp/pool.d.ts +39 -3
- package/dist/mcp/pool.js +74 -11
- package/dist/model/catalog.d.ts +2 -10
- package/dist/model/classify.d.ts +13 -0
- package/dist/model/classify.js +61 -0
- package/dist/model/models.d.ts +10 -1
- package/dist/model/models.js +14 -6
- package/dist/model/pi-model.d.ts +17 -2
- package/dist/model/pi-model.js +366 -222
- package/dist/openapi.json +25889 -0
- package/dist/record/append.d.ts +51 -0
- package/dist/record/append.js +20 -0
- package/dist/record/envelope.d.ts +19 -0
- package/dist/record/envelope.js +49 -0
- package/dist/record/index.d.ts +11 -0
- package/dist/record/index.js +11 -0
- package/dist/sandbox/manager.d.ts +13 -1
- package/dist/sandbox/manager.js +8 -11
- package/dist/sandbox/resolve.d.ts +33 -0
- package/dist/sandbox/resolve.js +81 -0
- package/dist/sandbox/session-sandbox.d.ts +11 -0
- package/dist/sandbox/session-sandbox.js +51 -0
- package/dist/sandbox/share.d.ts +14 -2
- package/dist/sandbox/share.js +33 -3
- package/dist/sandbox/tenant-config.d.ts +37 -0
- package/dist/sandbox/tenant-config.js +51 -0
- package/dist/sandbox/tools.d.ts +7 -9
- package/dist/sandbox/tools.js +11 -3
- package/dist/sandbox/types.d.ts +8 -2
- package/dist/store/postgres/connect.d.ts +25 -9
- package/dist/store/postgres/connect.js +27 -10
- package/dist/store/postgres/db.d.ts +16 -0
- package/dist/store/postgres/db.js +28 -0
- package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
- package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
- package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
- package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
- package/dist/store/postgres/migrate.d.ts +68 -0
- package/dist/store/postgres/migrate.js +183 -0
- package/dist/store/postgres/record-writer.d.ts +4 -0
- package/dist/store/postgres/record-writer.js +34 -0
- package/dist/store/postgres/record.d.ts +5 -0
- package/dist/store/postgres/record.js +60 -0
- package/dist/store/postgres/schema.d.ts +3096 -0
- package/dist/store/postgres/schema.js +363 -0
- package/dist/store/postgres/store.d.ts +2 -4
- package/dist/store/postgres/store.js +590 -434
- package/dist/store/postgres/tenant.d.ts +56 -0
- package/dist/store/postgres/tenant.js +154 -0
- package/dist/store/types.d.ts +183 -132
- package/dist/store/types.js +0 -1
- package/dist/streams/basin.d.ts +18 -1
- package/dist/streams/basin.js +34 -2
- package/dist/streams/control.d.ts +18 -0
- package/dist/streams/control.js +33 -0
- package/dist/streams/memory.d.ts +3 -1
- package/dist/streams/memory.js +14 -8
- package/dist/streams/relay/core.d.ts +35 -0
- package/dist/streams/relay/core.js +366 -0
- package/dist/streams/relay/types.d.ts +78 -0
- package/dist/streams/relay/types.js +8 -0
- package/dist/streams/types.d.ts +71 -36
- package/dist/streams/types.js +18 -53
- package/dist/tenant/access-policy.d.ts +30 -0
- package/dist/tenant/access-policy.js +52 -0
- package/dist/tenant/actions.d.ts +10 -149
- package/dist/tenant/actions.js +5 -167
- package/dist/tenant/advance.d.ts +7 -2
- package/dist/tenant/advance.js +125 -14
- package/dist/tenant/auth.d.ts +19 -17
- package/dist/tenant/auth.js +90 -93
- package/dist/tenant/browser.d.ts +15 -0
- package/dist/tenant/browser.js +39 -0
- package/dist/tenant/cause.d.ts +25 -0
- package/dist/tenant/cause.js +45 -0
- package/dist/tenant/commands.d.ts +18 -2
- package/dist/tenant/commands.js +111 -115
- package/dist/tenant/context.d.ts +101 -21
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +104 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +481 -0
- package/dist/tenant/effects.d.ts +20 -1
- package/dist/tenant/effects.js +159 -42
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +22 -7
- package/dist/tenant/ephemeral.js +83 -125
- package/dist/tenant/history.d.ts +64 -0
- package/dist/tenant/history.js +120 -0
- package/dist/tenant/http.d.ts +8 -9
- package/dist/tenant/http.js +13 -27
- package/dist/tenant/jwt.d.ts +16 -0
- package/dist/tenant/jwt.js +50 -0
- package/dist/tenant/model-setting.d.ts +2 -2
- package/dist/tenant/module.d.ts +17 -28
- package/dist/tenant/module.js +133 -279
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/paths.d.ts +11 -16
- package/dist/tenant/paths.js +24 -68
- package/dist/tenant/pool.d.ts +1 -3
- package/dist/tenant/pool.js +1 -19
- package/dist/tenant/principals.d.ts +23 -11
- package/dist/tenant/principals.js +61 -49
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +28 -20
- package/dist/tenant/runtime.js +95 -64
- package/dist/tenant/scheduler.d.ts +10 -3
- package/dist/tenant/scheduler.js +9 -5
- package/dist/tenant/session-streams.d.ts +127 -0
- package/dist/tenant/session-streams.js +404 -0
- package/dist/tenant/sessions.d.ts +28 -4
- package/dist/tenant/sessions.js +118 -24
- package/dist/tenant/signing-keys.d.ts +55 -0
- package/dist/tenant/signing-keys.js +172 -0
- package/dist/tenant/slim.d.ts +8 -0
- package/dist/tenant/slim.js +21 -0
- package/dist/tenant/status.d.ts +3 -3
- package/dist/tenant/status.js +20 -13
- package/dist/tenant/store-pg.d.ts +9 -6
- package/dist/tenant/store-pg.js +60 -125
- package/dist/tenant/streams.d.ts +62 -85
- package/dist/tenant/streams.js +143 -226
- package/dist/tenant/subject-limits.d.ts +16 -0
- package/dist/tenant/subject-limits.js +45 -0
- package/dist/tenant/sweep.d.ts +1 -10
- package/dist/tenant/sweep.js +29 -69
- package/dist/tenant/tokens.d.ts +14 -0
- package/dist/tenant/tokens.js +180 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +69 -98
- package/dist/tenant/types.js +4 -15
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +13 -7
- package/dist/vault/host-model.d.ts +31 -0
- package/dist/vault/host-model.js +57 -0
- package/dist/vault/service.d.ts +44 -17
- package/dist/vault/service.js +126 -121
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -9
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/store/memory.d.ts +0 -57
- package/dist/store/memory.js +0 -645
- package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
- package/dist/store/postgres/migrations/001_initial.js +0 -196
- package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
- package/dist/store/postgres/migrations/002_session_owner.js +0 -13
- package/dist/store/postgres/migrations/index.d.ts +0 -49
- package/dist/store/postgres/migrations/index.js +0 -76
- package/dist/store/postgres/names.d.ts +0 -11
- package/dist/store/postgres/names.js +0 -42
- package/dist/store/postgres/tenants.d.ts +0 -90
- package/dist/store/postgres/tenants.js +0 -214
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/envelope.d.ts +0 -7
- package/dist/tenant/envelope.js +0 -22
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- package/dist/tenant/quarantine-error.d.ts +0 -6
- package/dist/tenant/quarantine-error.js +0 -9
- package/dist/tenant/quarantine.d.ts +0 -27
- package/dist/tenant/quarantine.js +0 -72
- package/dist/tenant/routes-tenant.d.ts +0 -12
- package/dist/tenant/routes-tenant.js +0 -137
- package/dist/tenant/routes.d.ts +0 -9
- package/dist/tenant/routes.js +0 -151
- package/dist/tenant/store-memory.d.ts +0 -16
- package/dist/tenant/store-memory.js +0 -111
- /package/dist/{store → record}/cursor.d.ts +0 -0
- /package/dist/{store → record}/cursor.js +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CreateCredentialRequest, CredentialInfo, HostModelView, PutHostModelRequest, RotateCredentialRequest, SelectHostModelRequest, SigningKeyView } from "@nylorun/core/contracts";
|
|
2
|
+
import type { SessionStore } from "../store/types.js";
|
|
3
|
+
import { type SigningKeys } from "../tenant/signing-keys.js";
|
|
4
|
+
import type { VaultService } from "../vault/service.js";
|
|
5
|
+
/** One JWT to sign: its `typ` and every claim, `iss`, `aud`, `sub`, `iat`, `exp` and `jti` included. */
|
|
6
|
+
export interface SignRequest {
|
|
7
|
+
readonly typ: string;
|
|
8
|
+
readonly claims: Readonly<Record<string, unknown>>;
|
|
9
|
+
}
|
|
10
|
+
export interface Signed {
|
|
11
|
+
readonly token: string;
|
|
12
|
+
/** The signing key's id, the token's `kid`. */
|
|
13
|
+
readonly keyId: string;
|
|
14
|
+
}
|
|
15
|
+
export interface Keys {
|
|
16
|
+
createCredential(vaultId: string, body: CreateCredentialRequest): Promise<CredentialInfo>;
|
|
17
|
+
rotateCredential(vaultId: string, id: string, body: RotateCredentialRequest): Promise<CredentialInfo>;
|
|
18
|
+
putHostModel(body: PutHostModelRequest): Promise<HostModelView>;
|
|
19
|
+
selectHostModel(body: SelectHostModelRequest): Promise<HostModelView>;
|
|
20
|
+
/** Signs with the current key, creating the Tenant's keys if it has none. */
|
|
21
|
+
sign(request: SignRequest): Promise<Signed>;
|
|
22
|
+
/** Rotates the signing keys (`SigningKeys.rotate`); 409 while the previous key may verify live tokens. */
|
|
23
|
+
rotateSigningKeys(request: {
|
|
24
|
+
maxTtlSeconds: number;
|
|
25
|
+
force: boolean;
|
|
26
|
+
}): Promise<SigningKeyView[]>;
|
|
27
|
+
/** Creates the current and standby keys when the Tenant has none. */
|
|
28
|
+
ensureSigningKeys(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/** The operations, by the name the HTTP route carries. */
|
|
31
|
+
export declare const KEYS_OPERATIONS: readonly ["createCredential", "rotateCredential", "putHostModel", "selectHostModel", "sign", "rotateSigningKeys", "ensureSigningKeys"];
|
|
32
|
+
export type KeysOperation = (typeof KEYS_OPERATIONS)[number];
|
|
33
|
+
export interface InProcessKeysOptions {
|
|
34
|
+
readonly store: SessionStore;
|
|
35
|
+
/** Its vault key is `kek`. */
|
|
36
|
+
readonly vault: VaultService;
|
|
37
|
+
readonly signingKeys: SigningKeys;
|
|
38
|
+
/** The vault key. */
|
|
39
|
+
readonly kek: () => Buffer;
|
|
40
|
+
}
|
|
41
|
+
export declare function inProcessKeys(options: InProcessKeysOptions): Keys;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keys seam (blueprint §14, F4.2): everything that needs the vault key. Vault writes that
|
|
3
|
+
* touch a secret (creating and rotating a credential, setting and selecting the host model;
|
|
4
|
+
* rotation and selection decrypt the old secret first) and all token signing (subject and
|
|
5
|
+
* delivery tokens, signing-key rotation) run here, whole, so the process that calls them never
|
|
6
|
+
* holds the key or a private signing key.
|
|
7
|
+
*
|
|
8
|
+
* Two implementations: `inProcessKeys` (embedding, the ephemeral Runtime, tests, and the gates
|
|
9
|
+
* service itself, which serves the HTTP routes with it), and `httpKeys` (`client.ts`), the
|
|
10
|
+
* runtime container's client of the `keys` service in the gateway.
|
|
11
|
+
*/
|
|
12
|
+
import { SignJWT } from "jose";
|
|
13
|
+
import { signingKeyView } from "../tenant/signing-keys.js";
|
|
14
|
+
/** The operations, by the name the HTTP route carries. */
|
|
15
|
+
export const KEYS_OPERATIONS = [
|
|
16
|
+
"createCredential",
|
|
17
|
+
"rotateCredential",
|
|
18
|
+
"putHostModel",
|
|
19
|
+
"selectHostModel",
|
|
20
|
+
"sign",
|
|
21
|
+
"rotateSigningKeys",
|
|
22
|
+
"ensureSigningKeys",
|
|
23
|
+
];
|
|
24
|
+
export function inProcessKeys(options) {
|
|
25
|
+
const { store, vault, signingKeys, kek } = options;
|
|
26
|
+
return {
|
|
27
|
+
createCredential: (vaultId, body) => vault.createCredential(vaultId, body),
|
|
28
|
+
rotateCredential: (vaultId, id, body) => vault.rotateCredential(vaultId, id, body),
|
|
29
|
+
putHostModel: (body) => vault.putHostModel(body),
|
|
30
|
+
selectHostModel: (body) => vault.selectHostModel(body),
|
|
31
|
+
async sign(request) {
|
|
32
|
+
const key = kek();
|
|
33
|
+
const row = await store.tx((t) => signingKeys.ensure(t, key));
|
|
34
|
+
const signer = await signingKeys.privateKey(row, key);
|
|
35
|
+
const token = await new SignJWT({ ...request.claims })
|
|
36
|
+
.setProtectedHeader({ alg: "ES256", typ: request.typ, kid: signer.id })
|
|
37
|
+
.sign(signer.key);
|
|
38
|
+
return { token, keyId: signer.id };
|
|
39
|
+
},
|
|
40
|
+
async rotateSigningKeys(request) {
|
|
41
|
+
const key = kek();
|
|
42
|
+
const rows = await store.tx((t) => signingKeys.rotate(t, key, request.maxTtlSeconds, request.force));
|
|
43
|
+
return rows.map(signingKeyView);
|
|
44
|
+
},
|
|
45
|
+
async ensureSigningKeys() {
|
|
46
|
+
const key = kek();
|
|
47
|
+
await store.tx((t) => signingKeys.ensure(t, key));
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
package/dist/mcp/connect.d.ts
CHANGED
|
@@ -2,10 +2,49 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
2
2
|
import type { McpServerManifest } from "@nylorun/core/define";
|
|
3
3
|
import type { AuthorizeResult } from "../vault/service.js";
|
|
4
4
|
import { type McpDiagnostic, type McpToolRecord } from "./snapshot.js";
|
|
5
|
+
/** Options of one MCP request. */
|
|
6
|
+
export interface McpRequestOptions {
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
/** Options of one tool call. */
|
|
10
|
+
export interface McpCallOptions extends McpRequestOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The call's effect id. A gate runs a keyed call once and keeps its outcome, so a re-send
|
|
13
|
+
* after a takeover joins it (F4.1). An in-process connection ignores it.
|
|
14
|
+
*/
|
|
15
|
+
readonly key?: string;
|
|
16
|
+
}
|
|
17
|
+
/** A page of `tools/list`, as the MCP SDK returns it. */
|
|
18
|
+
export interface McpToolPage {
|
|
19
|
+
readonly tools: readonly {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly description?: string;
|
|
22
|
+
readonly inputSchema?: unknown;
|
|
23
|
+
readonly outputSchema?: unknown;
|
|
24
|
+
}[];
|
|
25
|
+
readonly nextCursor?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What the pool needs of an MCP client: the SDK `Client` over a transport of this process
|
|
29
|
+
* (`sdkClient`), or a client of the gates service that holds the real connection
|
|
30
|
+
* (`gates/tool-client.ts`).
|
|
31
|
+
*/
|
|
32
|
+
export interface McpClient {
|
|
33
|
+
listTools(params?: {
|
|
34
|
+
cursor?: string;
|
|
35
|
+
}, options?: McpRequestOptions): Promise<McpToolPage>;
|
|
36
|
+
/** The raw `CallToolResult`; `callMcpTool` reads it. */
|
|
37
|
+
callTool(params: {
|
|
38
|
+
name: string;
|
|
39
|
+
arguments: Record<string, unknown>;
|
|
40
|
+
}, options?: McpCallOptions): Promise<Record<string, unknown>>;
|
|
41
|
+
}
|
|
5
42
|
export interface LiveConnection {
|
|
6
|
-
readonly client:
|
|
43
|
+
readonly client: McpClient;
|
|
7
44
|
close(): Promise<void>;
|
|
8
45
|
}
|
|
46
|
+
/** An SDK `Client` as an `McpClient`. */
|
|
47
|
+
export declare function sdkClient(client: Client): McpClient;
|
|
9
48
|
export declare function openMcpServer(input: {
|
|
10
49
|
server: McpServerManifest;
|
|
11
50
|
authorize?: (url: string) => Promise<AuthorizeResult>;
|
|
@@ -13,15 +52,16 @@ export declare function openMcpServer(input: {
|
|
|
13
52
|
pluginData: string;
|
|
14
53
|
childEnv?: Readonly<Record<string, string>>;
|
|
15
54
|
}): Promise<LiveConnection>;
|
|
16
|
-
export declare function listMcpTools(client:
|
|
55
|
+
export declare function listMcpTools(client: McpClient, input: {
|
|
17
56
|
capabilityId: string;
|
|
18
57
|
serverName: string;
|
|
19
58
|
taken: Set<string>;
|
|
59
|
+
signal?: AbortSignal;
|
|
20
60
|
}): Promise<{
|
|
21
61
|
tools: McpToolRecord[];
|
|
22
62
|
omitted: string[];
|
|
23
63
|
}>;
|
|
24
|
-
export declare function callMcpTool(client:
|
|
64
|
+
export declare function callMcpTool(client: McpClient, serverToolName: string, args: unknown, options?: McpCallOptions): Promise<{
|
|
25
65
|
kind: "completed";
|
|
26
66
|
output: unknown;
|
|
27
67
|
} | {
|
package/dist/mcp/connect.js
CHANGED
|
@@ -5,6 +5,13 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
5
5
|
import { schemaFromJSON } from "@nylorun/core/define";
|
|
6
6
|
import { modelToolName, } from "./snapshot.js";
|
|
7
7
|
import { prepareStdioLaunch } from "./stdio.js";
|
|
8
|
+
/** An SDK `Client` as an `McpClient`. */
|
|
9
|
+
export function sdkClient(client) {
|
|
10
|
+
return {
|
|
11
|
+
listTools: (params, options) => client.listTools(params, options?.signal ? { signal: options.signal } : undefined),
|
|
12
|
+
callTool: (params, options) => client.callTool(params, undefined, options?.signal ? { signal: options.signal } : undefined),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
8
15
|
export async function openMcpServer(input) {
|
|
9
16
|
if (input.server.type === "stdio") {
|
|
10
17
|
const launch = prepareStdioLaunch(input.server, {
|
|
@@ -23,7 +30,7 @@ export async function openMcpServer(input) {
|
|
|
23
30
|
transport.stderr?.on("data", () => { });
|
|
24
31
|
const client = createClient();
|
|
25
32
|
await client.connect(transport);
|
|
26
|
-
return { client, close: () => client.close() };
|
|
33
|
+
return { client: sdkClient(client), close: () => client.close() };
|
|
27
34
|
}
|
|
28
35
|
const authorize = input.authorize;
|
|
29
36
|
if (!authorize)
|
|
@@ -42,14 +49,14 @@ export async function openMcpServer(input) {
|
|
|
42
49
|
: new StreamableHTTPClientTransport(url, { fetch: fetchImpl });
|
|
43
50
|
const client = createClient();
|
|
44
51
|
await client.connect(transport);
|
|
45
|
-
return { client, close: () => client.close() };
|
|
52
|
+
return { client: sdkClient(client), close: () => client.close() };
|
|
46
53
|
}
|
|
47
54
|
export async function listMcpTools(client, input) {
|
|
48
55
|
const tools = [];
|
|
49
56
|
const omitted = [];
|
|
50
57
|
let cursor;
|
|
51
58
|
for (let page = 0; page < 100; page += 1) {
|
|
52
|
-
const listed = await client.listTools(cursor ? { cursor } : undefined);
|
|
59
|
+
const listed = await client.listTools(cursor ? { cursor } : undefined, input.signal ? { signal: input.signal } : undefined);
|
|
53
60
|
for (const tool of listed.tools) {
|
|
54
61
|
if (typeof tool.name !== "string" || tool.name.length === 0)
|
|
55
62
|
continue;
|
|
@@ -81,13 +88,13 @@ export async function listMcpTools(client, input) {
|
|
|
81
88
|
}
|
|
82
89
|
return { tools, omitted };
|
|
83
90
|
}
|
|
84
|
-
export async function callMcpTool(client, serverToolName, args) {
|
|
91
|
+
export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
85
92
|
const result = await client.callTool({
|
|
86
93
|
name: serverToolName,
|
|
87
94
|
arguments: args && typeof args === "object" && !Array.isArray(args)
|
|
88
95
|
? args
|
|
89
96
|
: {},
|
|
90
|
-
});
|
|
97
|
+
}, options);
|
|
91
98
|
if ("isError" in result && result.isError) {
|
|
92
99
|
return {
|
|
93
100
|
kind: "failed",
|
package/dist/mcp/pool.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import type { AgentManifest, McpServerManifest } from "@nylorun/core/define";
|
|
2
2
|
import type { AuthorizeResult } from "../vault/service.js";
|
|
3
|
-
import { callMcpTool } from "./connect.js";
|
|
3
|
+
import { callMcpTool, openMcpServer, type LiveConnection } from "./connect.js";
|
|
4
4
|
import { type McpDiagnostic, type McpSnapshot, type McpToolRecord } from "./snapshot.js";
|
|
5
|
+
/** A connection unused this long is closed by the sweep; the next call opens it again. */
|
|
6
|
+
export declare const DEFAULT_MCP_IDLE_MS: number;
|
|
5
7
|
export declare class McpPool {
|
|
6
8
|
private readonly options;
|
|
7
9
|
private readonly live;
|
|
10
|
+
/** Connections being opened by `call`, so concurrent calls share one. */
|
|
11
|
+
private readonly opening;
|
|
12
|
+
private readonly idleMs;
|
|
13
|
+
private readonly now;
|
|
14
|
+
private readonly open;
|
|
8
15
|
constructor(options: {
|
|
9
16
|
/** Absolute Tenant `plugin-data` directory. */
|
|
10
17
|
readonly pluginData: string;
|
|
@@ -13,6 +20,18 @@ export declare class McpPool {
|
|
|
13
20
|
url: string;
|
|
14
21
|
serverName: string;
|
|
15
22
|
}) => Promise<AuthorizeResult>;
|
|
23
|
+
/** Default `DEFAULT_MCP_IDLE_MS`. */
|
|
24
|
+
readonly idleMs?: number;
|
|
25
|
+
readonly now?: () => number;
|
|
26
|
+
/** Tests replace how a declared server is opened. */
|
|
27
|
+
readonly open?: typeof openMcpServer;
|
|
28
|
+
/**
|
|
29
|
+
* Opens a remote (`streamable-http` or `sse`) server somewhere else: the gates service
|
|
30
|
+
* holds the connection and its credential, and the loop never sees either (F4.1).
|
|
31
|
+
* Absent: remote servers are opened in this process, with `authorize`. Stdio servers
|
|
32
|
+
* always run here.
|
|
33
|
+
*/
|
|
34
|
+
readonly openRemote?: (server: McpServerRef) => Promise<LiveConnection>;
|
|
16
35
|
});
|
|
17
36
|
discover(input: {
|
|
18
37
|
sessionId: string;
|
|
@@ -40,13 +59,30 @@ export declare class McpPool {
|
|
|
40
59
|
args: unknown;
|
|
41
60
|
manifest: AgentManifest;
|
|
42
61
|
pluginRoots: Readonly<Record<string, string>>;
|
|
62
|
+
/** The effect id: a gate runs the call once under it (F4.1). */
|
|
63
|
+
effectId?: string;
|
|
64
|
+
signal?: AbortSignal;
|
|
43
65
|
}): Promise<Awaited<ReturnType<typeof callMcpTool>>>;
|
|
66
|
+
/**
|
|
67
|
+
* Closes connections unused for the idle timeout (the Tenant sweep). A session that calls
|
|
68
|
+
* one again reconnects, as after a restart; this bounds the stdio processes a Tenant keeps.
|
|
69
|
+
*/
|
|
70
|
+
sweep(): Promise<void>;
|
|
44
71
|
close(): Promise<void>;
|
|
72
|
+
/** Keeps the connection, or closes it when the server is already connected for the session. */
|
|
45
73
|
private remember;
|
|
46
74
|
private liveKey;
|
|
47
75
|
private connectDeclared;
|
|
48
76
|
}
|
|
49
|
-
|
|
77
|
+
/** One declared MCP server of a session: what a gate needs to find it in the pinned manifest. */
|
|
78
|
+
export interface McpServerRef {
|
|
79
|
+
readonly sessionId: string;
|
|
80
|
+
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
81
|
+
readonly agentId?: string;
|
|
82
|
+
readonly capabilityId: string;
|
|
83
|
+
readonly serverName: string;
|
|
84
|
+
}
|
|
85
|
+
export interface DeclaredServer {
|
|
50
86
|
/** Absent for the session's root agent. */
|
|
51
87
|
readonly agentId?: string;
|
|
52
88
|
/** The manifest of the agent that declares the server. */
|
|
@@ -56,4 +92,4 @@ interface DeclaredServer {
|
|
|
56
92
|
}
|
|
57
93
|
/** Servers of the root agent, then of each agent it uses as a tool. */
|
|
58
94
|
export declare function serversOf(manifest: AgentManifest): DeclaredServer[];
|
|
59
|
-
export
|
|
95
|
+
export declare function findServer(manifest: AgentManifest, agentId: string | undefined, capabilityId: string, serverName: string): DeclaredServer | undefined;
|
package/dist/mcp/pool.js
CHANGED
|
@@ -2,11 +2,21 @@ import { join } from "node:path";
|
|
|
2
2
|
import { delegatesOf } from "@nylorun/core/define";
|
|
3
3
|
import { callMcpTool, diagnosticFromError, listMcpTools, openMcpServer, } from "./connect.js";
|
|
4
4
|
import { declaredToolNames, } from "./snapshot.js";
|
|
5
|
+
/** A connection unused this long is closed by the sweep; the next call opens it again. */
|
|
6
|
+
export const DEFAULT_MCP_IDLE_MS = 15 * 60_000;
|
|
5
7
|
export class McpPool {
|
|
6
8
|
options;
|
|
7
9
|
live = new Map();
|
|
10
|
+
/** Connections being opened by `call`, so concurrent calls share one. */
|
|
11
|
+
opening = new Map();
|
|
12
|
+
idleMs;
|
|
13
|
+
now;
|
|
14
|
+
open;
|
|
8
15
|
constructor(options) {
|
|
9
16
|
this.options = options;
|
|
17
|
+
this.idleMs = options.idleMs ?? DEFAULT_MCP_IDLE_MS;
|
|
18
|
+
this.now = options.now ?? Date.now;
|
|
19
|
+
this.open = options.open ?? openMcpServer;
|
|
10
20
|
}
|
|
11
21
|
async discover(input) {
|
|
12
22
|
// Each agent names its own tools, so collisions are checked per agent.
|
|
@@ -27,6 +37,7 @@ export class McpPool {
|
|
|
27
37
|
capabilityId: declared.capabilityId,
|
|
28
38
|
serverName: declared.server.name,
|
|
29
39
|
taken: taken.get(declared.agentId),
|
|
40
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
30
41
|
});
|
|
31
42
|
tools.push(...listed.tools.map((tool) => owned(declared, tool)));
|
|
32
43
|
diagnostics.push({
|
|
@@ -62,8 +73,12 @@ export class McpPool {
|
|
|
62
73
|
if (seen.has(key))
|
|
63
74
|
continue;
|
|
64
75
|
seen.add(key);
|
|
65
|
-
|
|
76
|
+
const live = this.live.get(key);
|
|
77
|
+
if (live) {
|
|
78
|
+
// The advance is about to use it: not idle.
|
|
79
|
+
live.lastUsedAt = this.now();
|
|
66
80
|
continue;
|
|
81
|
+
}
|
|
67
82
|
input.signal?.throwIfAborted();
|
|
68
83
|
const declared = findServer(input.manifest, tool.agentId, tool.capabilityId, tool.serverName);
|
|
69
84
|
if (!declared) {
|
|
@@ -92,34 +107,82 @@ export class McpPool {
|
|
|
92
107
|
const declared = findServer(input.manifest, input.agentId, input.capabilityId, input.serverName);
|
|
93
108
|
if (!declared)
|
|
94
109
|
throw new Error(`MCP server '${input.serverName}' is not declared`);
|
|
95
|
-
|
|
110
|
+
let opening = this.opening.get(key);
|
|
111
|
+
if (!opening) {
|
|
112
|
+
opening = this.connectDeclared(input, declared).finally(() => this.opening.delete(key));
|
|
113
|
+
this.opening.set(key, opening);
|
|
114
|
+
}
|
|
115
|
+
const opened = await opening;
|
|
96
116
|
if (!opened.ok)
|
|
97
117
|
throw new Error(opened.diagnostic.message);
|
|
98
|
-
this.remember(input.sessionId, declared, opened.connection);
|
|
99
|
-
|
|
118
|
+
live = this.remember(input.sessionId, declared, opened.connection);
|
|
119
|
+
}
|
|
120
|
+
live.active += 1;
|
|
121
|
+
try {
|
|
122
|
+
return await callMcpTool(live.connection.client, input.serverToolName, input.args, {
|
|
123
|
+
...(input.effectId === undefined ? {} : { key: input.effectId }),
|
|
124
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
live.active -= 1;
|
|
129
|
+
live.lastUsedAt = this.now();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Closes connections unused for the idle timeout (the Tenant sweep). A session that calls
|
|
134
|
+
* one again reconnects, as after a restart; this bounds the stdio processes a Tenant keeps.
|
|
135
|
+
*/
|
|
136
|
+
async sweep() {
|
|
137
|
+
const now = this.now();
|
|
138
|
+
const idle = [];
|
|
139
|
+
for (const [key, live] of this.live) {
|
|
140
|
+
if (live.active > 0 || now - live.lastUsedAt < this.idleMs)
|
|
141
|
+
continue;
|
|
142
|
+
this.live.delete(key);
|
|
143
|
+
idle.push(live);
|
|
100
144
|
}
|
|
101
|
-
|
|
102
|
-
throw new Error(`MCP server '${input.serverName}' is not connected`);
|
|
103
|
-
return callMcpTool(live.connection.client, input.serverToolName, input.args);
|
|
145
|
+
await Promise.all(idle.map((item) => item.connection.close().catch(() => { })));
|
|
104
146
|
}
|
|
105
147
|
async close() {
|
|
106
148
|
const connections = [...this.live.values()];
|
|
107
149
|
this.live.clear();
|
|
108
150
|
await Promise.all(connections.map((item) => item.connection.close().catch(() => { })));
|
|
109
151
|
}
|
|
152
|
+
/** Keeps the connection, or closes it when the server is already connected for the session. */
|
|
110
153
|
remember(sessionId, declared, connection) {
|
|
111
|
-
this.
|
|
154
|
+
const key = this.liveKey(sessionId, declared.agentId, declared.capabilityId, declared.server.name);
|
|
155
|
+
const existing = this.live.get(key);
|
|
156
|
+
if (existing) {
|
|
157
|
+
if (existing.connection !== connection)
|
|
158
|
+
void connection.close().catch(() => { });
|
|
159
|
+
return existing;
|
|
160
|
+
}
|
|
161
|
+
const live = {
|
|
112
162
|
capabilityId: declared.capabilityId,
|
|
113
163
|
serverName: declared.server.name,
|
|
114
164
|
connection,
|
|
115
|
-
|
|
165
|
+
lastUsedAt: this.now(),
|
|
166
|
+
active: 0,
|
|
167
|
+
};
|
|
168
|
+
this.live.set(key, live);
|
|
169
|
+
return live;
|
|
116
170
|
}
|
|
117
171
|
liveKey(sessionId, agentId, capabilityId, serverName) {
|
|
118
172
|
return JSON.stringify([sessionId, agentId ?? null, capabilityId, serverName]);
|
|
119
173
|
}
|
|
120
174
|
async connectDeclared(input, declared) {
|
|
121
175
|
try {
|
|
122
|
-
|
|
176
|
+
if (declared.server.type !== "stdio" && this.options.openRemote) {
|
|
177
|
+
const connection = await this.options.openRemote({
|
|
178
|
+
sessionId: input.sessionId,
|
|
179
|
+
...ownerOf(declared),
|
|
180
|
+
capabilityId: declared.capabilityId,
|
|
181
|
+
serverName: declared.server.name,
|
|
182
|
+
});
|
|
183
|
+
return { ok: true, connection };
|
|
184
|
+
}
|
|
185
|
+
const connection = await this.open({
|
|
123
186
|
server: declared.server,
|
|
124
187
|
pluginRoot: input.pluginRoots[pluginKey(declared)],
|
|
125
188
|
pluginData: join(this.options.pluginData, ...pluginKey(declared).split("/")),
|
|
@@ -159,7 +222,7 @@ export function serversOf(manifest) {
|
|
|
159
222
|
add(child.manifest, child.manifest.id);
|
|
160
223
|
return servers;
|
|
161
224
|
}
|
|
162
|
-
function findServer(manifest, agentId, capabilityId, serverName) {
|
|
225
|
+
export function findServer(manifest, agentId, capabilityId, serverName) {
|
|
163
226
|
return serversOf(manifest).find((item) => item.agentId === agentId &&
|
|
164
227
|
item.capabilityId === capabilityId &&
|
|
165
228
|
item.server.name === serverName);
|
package/dist/model/catalog.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
id: string;
|
|
4
|
-
name: string;
|
|
5
|
-
models: {
|
|
6
|
-
id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
}[];
|
|
9
|
-
}[];
|
|
10
|
-
};
|
|
1
|
+
import type { HostModelCatalog } from "@nylorun/core/contracts";
|
|
2
|
+
export type { HostModelCatalog };
|
|
11
3
|
/** Public provider and model names. No credentials. */
|
|
12
4
|
export declare function hostModelCatalog(): HostModelCatalog;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type AssistantMessage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ModelFailureCode, ModelFailureOutcome } from "@nylorun/core/define";
|
|
3
|
+
/**
|
|
4
|
+
* Classify a failed pi-ai response (`stopReason: "error"`) into a failure outcome
|
|
5
|
+
* (Model Calls §6.1). Context overflow is checked first, with the binding's window so
|
|
6
|
+
* silent overflow is caught too.
|
|
7
|
+
*/
|
|
8
|
+
export declare function classifyAssistantError(message: AssistantMessage, contextWindow?: number): ModelFailureOutcome;
|
|
9
|
+
/** Classify an error a model provider threw (gateway, fixture, or a setup failure). */
|
|
10
|
+
export declare function classifyThrown(error: unknown, contextWindow?: number): ModelFailureOutcome;
|
|
11
|
+
export declare function failure(code: ModelFailureCode, message: string, retryable: boolean): ModelFailureOutcome;
|
|
12
|
+
/** A pi-ai error message, for classifying failures that did not come from a stream. */
|
|
13
|
+
export declare function errorMessage(text: string): AssistantMessage;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isContextOverflow, isRetryableAssistantError, } from "@earendil-works/pi-ai";
|
|
2
|
+
const RATE_LIMITED = /\b429\b|rate.?limit|too many requests/i;
|
|
3
|
+
const QUOTA = /insufficient_quota|quota exceeded|out of budget|billing|usage limit/i;
|
|
4
|
+
const OVERLOADED = /overloaded|\b529\b|\b503\b|service.?unavailable|high demand/i;
|
|
5
|
+
const TIMEOUT = /timed? out|timeout/i;
|
|
6
|
+
const CONTENT_POLICY = /content.?(policy|filter|management)|safety|flagged|moderation/i;
|
|
7
|
+
const AUTH = /\b401\b|\b403\b|unauthori[sz]ed|forbidden|invalid.?api.?key|incorrect api key|authentication|not configured/i;
|
|
8
|
+
/**
|
|
9
|
+
* Classify a failed pi-ai response (`stopReason: "error"`) into a failure outcome
|
|
10
|
+
* (Model Calls §6.1). Context overflow is checked first, with the binding's window so
|
|
11
|
+
* silent overflow is caught too.
|
|
12
|
+
*/
|
|
13
|
+
export function classifyAssistantError(message, contextWindow) {
|
|
14
|
+
const text = message.errorMessage ?? `Provider stopped: ${message.stopReason}`;
|
|
15
|
+
if (isContextOverflow(message, contextWindow))
|
|
16
|
+
return failure("context_overflow", text, false);
|
|
17
|
+
if (RATE_LIMITED.test(text))
|
|
18
|
+
return failure("rate_limited", text, !QUOTA.test(text));
|
|
19
|
+
if (QUOTA.test(text))
|
|
20
|
+
return failure("rate_limited", text, false);
|
|
21
|
+
if (OVERLOADED.test(text))
|
|
22
|
+
return failure("overloaded", text, true);
|
|
23
|
+
if (TIMEOUT.test(text))
|
|
24
|
+
return failure("timeout", text, true);
|
|
25
|
+
if (CONTENT_POLICY.test(text))
|
|
26
|
+
return failure("content_policy", text, false);
|
|
27
|
+
if (AUTH.test(text))
|
|
28
|
+
return failure("auth", text, false);
|
|
29
|
+
if (isRetryableAssistantError(message))
|
|
30
|
+
return failure("transient", text, true);
|
|
31
|
+
return failure("invalid_request", text, false);
|
|
32
|
+
}
|
|
33
|
+
/** Classify an error a model provider threw (gateway, fixture, or a setup failure). */
|
|
34
|
+
export function classifyThrown(error, contextWindow) {
|
|
35
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
36
|
+
return classifyAssistantError(errorMessage(text), contextWindow);
|
|
37
|
+
}
|
|
38
|
+
export function failure(code, message, retryable) {
|
|
39
|
+
return { kind: "failed", code, message, retryable };
|
|
40
|
+
}
|
|
41
|
+
/** A pi-ai error message, for classifying failures that did not come from a stream. */
|
|
42
|
+
export function errorMessage(text) {
|
|
43
|
+
return {
|
|
44
|
+
role: "assistant",
|
|
45
|
+
content: [],
|
|
46
|
+
api: "openai-completions",
|
|
47
|
+
provider: "unknown",
|
|
48
|
+
model: "unknown",
|
|
49
|
+
usage: {
|
|
50
|
+
input: 0,
|
|
51
|
+
output: 0,
|
|
52
|
+
cacheRead: 0,
|
|
53
|
+
cacheWrite: 0,
|
|
54
|
+
totalTokens: 0,
|
|
55
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
56
|
+
},
|
|
57
|
+
stopReason: "error",
|
|
58
|
+
errorMessage: text,
|
|
59
|
+
timestamp: 0,
|
|
60
|
+
};
|
|
61
|
+
}
|
package/dist/model/models.d.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { type CredentialStore } from "@earendil-works/pi-ai";
|
|
1
|
+
import { type AuthContext, type CredentialStore } from "@earendil-works/pi-ai";
|
|
2
2
|
export type Selection = Readonly<{
|
|
3
3
|
provider: string;
|
|
4
4
|
model: string;
|
|
5
5
|
custom?: Readonly<{
|
|
6
6
|
baseUrl: string;
|
|
7
|
+
/** Model Settings for the endpoint (Model Calls §7); defaults are deliberately small. */
|
|
8
|
+
contextWindow?: number;
|
|
9
|
+
maxTokens?: number;
|
|
10
|
+
reasoning?: boolean;
|
|
11
|
+
compat?: Readonly<Record<string, unknown>>;
|
|
7
12
|
}>;
|
|
8
13
|
}>;
|
|
14
|
+
/** A custom endpoint whose window is unknown is assumed small: an early compaction beats a failed call. */
|
|
15
|
+
export declare const CUSTOM_CONTEXT_WINDOW = 32768;
|
|
16
|
+
export declare const CUSTOM_MAX_TOKENS = 8192;
|
|
9
17
|
/**
|
|
10
18
|
* Build the pi-ai model registry from explicit credentials only.
|
|
11
19
|
* Ambient process environment / provider env auth is never consulted (A7, A10).
|
|
12
20
|
*/
|
|
13
21
|
export declare function modelsFor(selection: Selection, credentials: CredentialStore, options?: {
|
|
14
22
|
environment?: boolean;
|
|
23
|
+
authContext?: AuthContext;
|
|
15
24
|
}): import("@earendil-works/pi-ai").MutableModels;
|
package/dist/model/models.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { createProvider, } from "@earendil-works/pi-ai";
|
|
2
2
|
import { stream, streamSimple, } from "@earendil-works/pi-ai/api/openai-completions";
|
|
3
3
|
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
|
|
4
|
+
/** A custom endpoint whose window is unknown is assumed small: an early compaction beats a failed call. */
|
|
5
|
+
export const CUSTOM_CONTEXT_WINDOW = 32_768;
|
|
6
|
+
export const CUSTOM_MAX_TOKENS = 8_192;
|
|
4
7
|
/**
|
|
5
8
|
* Build the pi-ai model registry from explicit credentials only.
|
|
6
9
|
* Ambient process environment / provider env auth is never consulted (A7, A10).
|
|
@@ -9,7 +12,10 @@ export function modelsFor(selection, credentials, options = {}) {
|
|
|
9
12
|
if (options.environment) {
|
|
10
13
|
throw new Error("Ambient model environment is not supported; pass credentials explicitly");
|
|
11
14
|
}
|
|
12
|
-
const models = builtinModels({
|
|
15
|
+
const models = builtinModels({
|
|
16
|
+
credentials,
|
|
17
|
+
...(options.authContext ? { authContext: options.authContext } : {}),
|
|
18
|
+
});
|
|
13
19
|
if (!selection.custom)
|
|
14
20
|
return models;
|
|
15
21
|
const model = {
|
|
@@ -18,11 +24,14 @@ export function modelsFor(selection, credentials, options = {}) {
|
|
|
18
24
|
api: "openai-completions",
|
|
19
25
|
provider: "custom",
|
|
20
26
|
baseUrl: selection.custom.baseUrl,
|
|
21
|
-
reasoning: false,
|
|
27
|
+
reasoning: selection.custom.reasoning ?? false,
|
|
22
28
|
input: ["text", "image"],
|
|
23
29
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
24
|
-
contextWindow:
|
|
25
|
-
maxTokens:
|
|
30
|
+
contextWindow: selection.custom.contextWindow ?? CUSTOM_CONTEXT_WINDOW,
|
|
31
|
+
maxTokens: selection.custom.maxTokens ?? CUSTOM_MAX_TOKENS,
|
|
32
|
+
...(selection.custom.compat
|
|
33
|
+
? { compat: { ...selection.custom.compat } }
|
|
34
|
+
: {}),
|
|
26
35
|
};
|
|
27
36
|
models.setProvider(createProvider({
|
|
28
37
|
id: "custom",
|
|
@@ -30,8 +39,7 @@ export function modelsFor(selection, credentials, options = {}) {
|
|
|
30
39
|
baseUrl: selection.custom.baseUrl,
|
|
31
40
|
auth: {
|
|
32
41
|
apiKey: {
|
|
33
|
-
|
|
34
|
-
schemas: [],
|
|
42
|
+
name: "Custom API key",
|
|
35
43
|
async resolve() {
|
|
36
44
|
const credential = await credentials.read("custom");
|
|
37
45
|
if (!credential || credential.type !== "api_key")
|
package/dist/model/pi-model.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Credential } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { RuntimeModelAdapter } from "../contracts.js";
|
|
3
3
|
import type { RuntimeMedia } from "../adapters/media.js";
|
|
4
4
|
import type { HostModelSecret } from "../vault/service.js";
|
|
@@ -6,12 +6,27 @@ export interface ModelPreview {
|
|
|
6
6
|
readonly invocationId: string;
|
|
7
7
|
readonly text: string;
|
|
8
8
|
}
|
|
9
|
+
/** Retry, timeout and cache settings for one model call (Model Calls §5, §6). */
|
|
10
|
+
export interface ModelCallSettings {
|
|
11
|
+
/** Attempts per call, including the first. Default 3. */
|
|
12
|
+
readonly attempts?: number;
|
|
13
|
+
/** Base delay between attempts; doubles each time, with jitter. Default 2 s. */
|
|
14
|
+
readonly retryBaseDelayMs?: number;
|
|
15
|
+
/** A stream with no event for this long is aborted and retried. Default 300 s. */
|
|
16
|
+
readonly idleTimeoutMs?: number;
|
|
17
|
+
/** Whole-request timeout passed to the provider SDK. Default 600 s. */
|
|
18
|
+
readonly requestTimeoutMs?: number;
|
|
19
|
+
}
|
|
9
20
|
export interface PiModelOptions {
|
|
10
21
|
readonly root?: string;
|
|
11
22
|
readonly onPreview?: (preview: ModelPreview) => void;
|
|
12
23
|
readonly media?: Pick<RuntimeMedia, "dataUrl">;
|
|
13
24
|
readonly readHostModel?: () => HostModelSecret | undefined | Promise<HostModelSecret | undefined>;
|
|
14
25
|
readonly writeHostCredential?: (credential: Credential) => void | Promise<void>;
|
|
26
|
+
readonly settings?: ModelCallSettings;
|
|
15
27
|
}
|
|
16
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Node model adapter. Local provider configuration is read only when invoked.
|
|
30
|
+
* A provider failure is returned as a failure outcome; only an abort is thrown.
|
|
31
|
+
*/
|
|
17
32
|
export declare function piModel(options?: PiModelOptions): RuntimeModelAdapter;
|