@stackbone/sdk 0.1.0-alpha.2 → 0.1.0-alpha.4
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 +307 -1
- package/README.md +127 -167
- package/db/index.d.cts +1 -0
- package/db/index.d.ts +1 -0
- package/db/testing/index.cjs +12 -43
- package/db/testing/index.cjs.map +1 -1
- package/db/testing/index.d.cts +12 -21
- package/db/testing/index.d.ts +12 -21
- package/db/testing/index.js +12 -43
- package/db/testing/index.js.map +1 -1
- package/index.cjs +7876 -19124
- package/index.cjs.map +1 -1
- package/index.d.cts +1434 -768
- package/index.d.ts +1434 -768
- package/index.js +7844 -19105
- package/index.js.map +1 -1
- package/observability/index.cjs +610 -0
- package/observability/index.cjs.map +1 -0
- package/observability/index.d.cts +175 -0
- package/observability/index.d.ts +175 -0
- package/observability/index.js +601 -0
- package/observability/index.js.map +1 -0
- package/package.json +14 -12
- package/rag/schema.cjs +42 -82
- package/rag/schema.cjs.map +1 -1
- package/rag/schema.d.cts +1 -446
- package/rag/schema.d.ts +1 -446
- package/rag/schema.js +42 -61
- package/rag/schema.js.map +1 -1
- package/stackbone-sdk-0.1.0-alpha.4.tgz +0 -0
- package/rag/migrations/index.cjs +0 -71
- package/rag/migrations/index.cjs.map +0 -1
- package/rag/migrations/index.d.cts +0 -29
- package/rag/migrations/index.d.ts +0 -29
- package/rag/migrations/index.js +0 -66
- package/rag/migrations/index.js.map +0 -1
- package/stackbone-sdk-0.1.0-alpha.2.tgz +0 -0
package/index.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import { ContractResponse } from '@stackbone/validators';
|
|
1
|
+
import { PublishJobResponse, ScheduleJobResponse, UnscheduleJobResponse, ListSchedulesResponse, ContractResponse } from '@stackbone/validators';
|
|
2
|
+
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
3
|
+
import { Sql } from 'postgres';
|
|
4
|
+
import { IngestJobWriter, IngestRequest, IngestResponse, RagIngestProgress, DeleteOptions, DeleteResponse, RetrieveRequest, RetrieveHit, ChunkOptions, ParseInput, ParseOptions } from '@stackbone/rag-core';
|
|
5
|
+
export { ChunkOptions, ChunkStrategy, DeleteOptions, DeleteResponse, IngestChunk, IngestRequest, IngestRequestAutoEmbed, IngestRequestPrecomputed, IngestResponse, ParseInput, ParseOptions, RagIngestProgress, RetrieveHit, RetrieveRequest, RetrieveRequestAutoEmbed, RetrieveRequestPrecomputed } from '@stackbone/rag-core';
|
|
2
6
|
import OpenAI from 'openai';
|
|
3
7
|
import { ChatCompletionCreateParamsStreaming, ChatCompletionChunk, ChatCompletionCreateParamsNonStreaming, ChatCompletion, EmbeddingCreateParams, CreateEmbeddingResponse, ChatCompletionMessageParam } from 'openai/resources';
|
|
4
8
|
import { Stream } from 'openai/streaming';
|
|
5
|
-
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
6
|
-
import { Sql } from 'postgres';
|
|
7
9
|
import { S3Client } from '@aws-sdk/client-s3';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
export { z } from 'zod';
|
|
12
|
+
import { SecretCipher } from '@stackbone/crypto';
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Optional overrides accepted by `createClient`. All fields are optional;
|
|
11
|
-
*
|
|
16
|
+
* the resolver applies the precedence rule (`config.xxx` first, env var
|
|
17
|
+
* fallback) and produces a fully typed `ResolvedConfig` so downstream
|
|
18
|
+
* modules never have to know the env-var names or the order of precedence.
|
|
12
19
|
*/
|
|
13
20
|
interface ClientConfig {
|
|
14
21
|
/** Ed25519 JWT signed by the control plane. Falls back to `STACKBONE_AGENT_JWT`. */
|
|
@@ -24,11 +31,17 @@ interface ClientConfig {
|
|
|
24
31
|
* every SDK consumer that talks to the agent's Postgres.
|
|
25
32
|
*/
|
|
26
33
|
databaseUrl?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Per-agent encryption key (base64, 32 bytes) used to decrypt the agent's
|
|
36
|
+
* own application secrets read directly from `stackbone_platform.secrets`.
|
|
37
|
+
* Falls back to `STACKBONE_SECRET_KEY` — the per-agent key the saga/harness
|
|
38
|
+
* inject into the runtime env. This is the ONLY new env channel the
|
|
39
|
+
* agent-owned-secrets split adds; the deliberate no-generic-env-channel
|
|
40
|
+
* stance is preserved.
|
|
41
|
+
*/
|
|
42
|
+
secretKey?: string;
|
|
27
43
|
openrouterKey?: string;
|
|
28
44
|
openrouterBaseUrl?: string;
|
|
29
|
-
qstashToken?: string;
|
|
30
|
-
qstashCurrentSigningKey?: string;
|
|
31
|
-
qstashNextSigningKey?: string;
|
|
32
45
|
llamaParseApiKey?: string;
|
|
33
46
|
/** mem0 API key for the long-term memory backend (`client.memory`). Falls back to `MEM0_API_KEY`. */
|
|
34
47
|
mem0ApiKey?: string;
|
|
@@ -86,22 +99,277 @@ interface ClientConfig {
|
|
|
86
99
|
protocolRequired?: number;
|
|
87
100
|
}
|
|
88
101
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
102
|
+
* Fully-resolved, typed snapshot the SDK consumes. The resolver is the **only**
|
|
103
|
+
* place in the library that touches `process.env`; every downstream module
|
|
104
|
+
* reads from typed fields here instead of consulting the environment itself.
|
|
105
|
+
*
|
|
106
|
+
* Precedence rule: `config.xxx` (passed to `createClient`) wins over
|
|
107
|
+
* `process.env['XXX']`. Required env vars surface as `string | undefined` —
|
|
108
|
+
* the resolver never throws when a var is missing; whether a missing value
|
|
109
|
+
* is fatal is a per-surface decision (some error at construction, others
|
|
110
|
+
* lazily on first use).
|
|
111
|
+
*
|
|
112
|
+
* Construction is a snapshot. Tests that mutated `process.env` between
|
|
113
|
+
* `createClient()` and the first module access used to observe the new value
|
|
114
|
+
* through the old `env` channel; they now pass overrides via `createClient`
|
|
115
|
+
* or rebuild a new `ResolvedConfig` after mutating `process.env`. Snapshot
|
|
116
|
+
* semantics make the SDK easier to reason about — one read, one source.
|
|
117
|
+
*
|
|
118
|
+
* Note: there is intentionally no `env` channel. Reading
|
|
119
|
+
* `resolved.env['SOMETHING']` is a type error. Adding a new env-driven
|
|
120
|
+
* setting means adding a typed field here and resolving it in
|
|
121
|
+
* `resolveConfig`.
|
|
92
122
|
*/
|
|
93
123
|
interface ResolvedConfig {
|
|
94
|
-
|
|
95
|
-
|
|
124
|
+
/** The frozen creator-supplied options (untouched, for debug introspection). */
|
|
125
|
+
readonly config: Readonly<ClientConfig>;
|
|
126
|
+
/** Resolved from `config.stackboneApiUrl` ?? `STACKBONE_API_URL`. */
|
|
127
|
+
readonly stackboneApiUrl: string | undefined;
|
|
128
|
+
/** Resolved from `config.agentJwt` ?? `STACKBONE_AGENT_JWT`. */
|
|
129
|
+
readonly agentJwt: string | undefined;
|
|
130
|
+
/** Resolved from `config.installationId` ?? `STACKBONE_INSTALLATION_ID`. */
|
|
131
|
+
readonly installationId: string | undefined;
|
|
132
|
+
/** Resolved from `config.approvalSigningKey` ?? `STACKBONE_APPROVAL_SIGNING_KEY`. */
|
|
133
|
+
readonly approvalSigningKey: string | undefined;
|
|
134
|
+
/** Resolved from `config.agentId` ?? `STACKBONE_AGENT_ID`. */
|
|
135
|
+
readonly agentId: string | undefined;
|
|
136
|
+
/** Resolved from `config.databaseUrl` ?? `STACKBONE_POSTGRES_URL`. Shared by `client.database`, `client.rag`, and the platform observability hooks in `@stackbone/sdk/observability`. */
|
|
137
|
+
readonly databaseUrl: string | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Resolved from `config.secretKey` ?? `STACKBONE_SECRET_KEY`. The per-agent
|
|
140
|
+
* base64 encryption key `client.secrets` uses to decrypt rows read straight
|
|
141
|
+
* out of `stackbone_platform.secrets`.
|
|
142
|
+
*/
|
|
143
|
+
readonly secretKey: string | undefined;
|
|
144
|
+
/** Resolved from `config.openrouterKey` ?? `OPENROUTER_API_KEY`. */
|
|
145
|
+
readonly openrouterKey: string | undefined;
|
|
146
|
+
/** Resolved from `config.openrouterBaseUrl` ?? `OPENROUTER_BASE_URL`. `undefined` means use the OpenRouter default. */
|
|
147
|
+
readonly openrouterBaseUrl: string | undefined;
|
|
148
|
+
/** Resolved from `config.s3.accessKeyId` ?? `STACKBONE_S3_ACCESS_KEY`. */
|
|
149
|
+
readonly s3AccessKeyId: string | undefined;
|
|
150
|
+
/** Resolved from `config.s3.secretAccessKey` ?? `STACKBONE_S3_SECRET_KEY`. */
|
|
151
|
+
readonly s3SecretAccessKey: string | undefined;
|
|
152
|
+
/** Resolved from `config.s3.endpoint` ?? `STACKBONE_S3_ENDPOINT`. */
|
|
153
|
+
readonly s3Endpoint: string | undefined;
|
|
154
|
+
/** Resolved from `config.s3.bucket` ?? `STACKBONE_S3_BUCKET`. */
|
|
155
|
+
readonly s3Bucket: string | undefined;
|
|
156
|
+
/** Resolved from `config.s3.region` ?? `STACKBONE_S3_REGION` ?? `'auto'`. Always defined. */
|
|
157
|
+
readonly s3Region: string;
|
|
158
|
+
/** Resolved from `config.otel.exporterOtlpEndpoint` ?? `OTEL_EXPORTER_OTLP_ENDPOINT`. */
|
|
159
|
+
readonly otelExporterOtlpEndpoint: string | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* `true` unless `STACKBONE_REQUIRE_CONTRACT=0` is set. Consulted by the
|
|
162
|
+
* contract gate to decide whether a `capability_unavailable` /
|
|
163
|
+
* `contract_version_unsupported` error becomes a hard failure or a logged
|
|
164
|
+
* warning (escape-hatch path).
|
|
165
|
+
*/
|
|
166
|
+
readonly requireContract: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Parsed `STACKBONE_CONTRACT_TTL_MS`. `undefined` (default) means cached
|
|
169
|
+
* handshakes never expire within the process lifetime; a positive integer
|
|
170
|
+
* caps the cache entry's `expiresAt`.
|
|
171
|
+
*/
|
|
172
|
+
readonly contractTtlMs: number | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* `true` only when `STACKBONE_DEBUG=1` is set. Consulted by the handshake
|
|
175
|
+
* to emit a one-shot debug line per (store, baseUrl) tuple after a
|
|
176
|
+
* successful negotiation.
|
|
177
|
+
*/
|
|
178
|
+
readonly debug: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Embedding model parsed by the CLI from `agent.yaml.rag.embeddingModel`
|
|
181
|
+
* and forwarded as `config.rag.embeddingModel`. Same value, surfaced
|
|
182
|
+
* directly so consumers don't reach through the nested object.
|
|
183
|
+
*/
|
|
184
|
+
readonly ragEmbeddingModel: string | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* Forwarded from `config.protocolRequired`. The CLI sources it from
|
|
187
|
+
* `agent.yaml.protocol.required`; there is no env-var fallback today.
|
|
188
|
+
*/
|
|
189
|
+
readonly protocolRequired: number | undefined;
|
|
96
190
|
}
|
|
97
191
|
|
|
192
|
+
/**
|
|
193
|
+
* The single source of truth for every `code` an `SdkError` may carry on the
|
|
194
|
+
* `Result` envelope returned from a public SDK method. The README contract
|
|
195
|
+
* (and every creator who pattern-matches on `result.error.code`) is driven by
|
|
196
|
+
* this catalog — adding a new code means editing this file, and **only** this
|
|
197
|
+
* file. The compiler then refuses to ship any `SdkError({ code: '<unknown>' })`
|
|
198
|
+
* downstream.
|
|
199
|
+
*
|
|
200
|
+
* Shape:
|
|
201
|
+
* - `SDK_ERROR_CODE_PREFIXES` declares each prefix family and the suffixes
|
|
202
|
+
* it owns. `<prefix>` + `_<reason>` is the wire-level code.
|
|
203
|
+
* - `SDK_ERROR_CODES_STANDALONE` declares the freestanding codes that don't
|
|
204
|
+
* follow the `<prefix>_<reason>` rule (`not_implemented`, the various
|
|
205
|
+
* `*_missing` setup-bug codes, the `capability_*` / `contract_*` shapes).
|
|
206
|
+
* - `SdkErrorCode` is the union projected from both. Pass it to `err({...})`
|
|
207
|
+
* and the constructor rejects any literal that isn't in this catalog.
|
|
208
|
+
*
|
|
209
|
+
* Stability contract: the wire-level shape of every code in this catalog is
|
|
210
|
+
* part of the README's public error table. Codes are added behind a minor;
|
|
211
|
+
* codes are removed behind a major and the CHANGELOG. Spelling tweaks (even
|
|
212
|
+
* typos) wait for a major — fix the catalog and the call site together.
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* Prefix families. Each entry maps a `<prefix>` to the tuple of `<reason>`
|
|
216
|
+
* suffixes the SDK can emit under it. Two-step structure (object of tuples)
|
|
217
|
+
* is what powers the typed projection at the bottom of this file: the
|
|
218
|
+
* `as const` makes both the prefix names and the suffix strings into literal
|
|
219
|
+
* types so `SdkErrorCode` collapses to a finite string union.
|
|
220
|
+
*/
|
|
221
|
+
declare const SDK_ERROR_CODE_PREFIXES: {
|
|
222
|
+
/**
|
|
223
|
+
* `client.ai` — wraps the OpenAI SDK pointed at OpenRouter. Status codes
|
|
224
|
+
* come from `codeForStatus` in `ai.ts` plus the fetch-error mapper.
|
|
225
|
+
*/
|
|
226
|
+
readonly ai: readonly ["aborted", "credits_exhausted", "forbidden", "moderation_blocked", "network_error", "no_image_generated", "provider_error", "rate_limited", "timeout", "unauthorized", "validation_error"];
|
|
227
|
+
/**
|
|
228
|
+
* `client.storage` — wraps `@aws-sdk/client-s3` against R2/MinIO. Most
|
|
229
|
+
* failures collapse into `s3_error` with the AWS metadata on `meta`; the
|
|
230
|
+
* specific codes below are the early validation paths.
|
|
231
|
+
*/
|
|
232
|
+
readonly s3: readonly ["bucket_missing", "credentials_missing", "empty_response", "error", "invalid_argument", "invalid_key"];
|
|
233
|
+
/**
|
|
234
|
+
* `client.rag` — the agent-local retrieval pipeline. `invalid_request` is
|
|
235
|
+
* the validation rail; the rest map specific Postgres / embedder failures
|
|
236
|
+
* the README documents.
|
|
237
|
+
*/
|
|
238
|
+
readonly rag: readonly ["dim_mismatch", "embedding_failed", "embedding_model_unsupported", "error", "ingest_cancelled", "invalid_request", "job_insert_failed", "jobs_error", "schema_missing"];
|
|
239
|
+
/**
|
|
240
|
+
* `client.approval` — AGENT-LOCAL DB facade plus the HMAC verify helper.
|
|
241
|
+
* The request/cancel/get/list codes (`persist_failed`, `cancel_failed`,
|
|
242
|
+
* `unavailable`) come from the direct write/read against
|
|
243
|
+
* `stackbone_platform.approvals`; the verify-specific codes
|
|
244
|
+
* (`invalid_signature`, etc.) are emitted locally.
|
|
245
|
+
*/
|
|
246
|
+
readonly approval: readonly ["cancel_failed", "forbidden", "invalid_payload", "invalid_request", "invalid_response", "invalid_signature", "not_found", "persist_failed", "rate_limited", "signature_expired", "signing_key_missing", "timeout", "tool_execute_failed", "unauthorized", "unavailable"];
|
|
247
|
+
/**
|
|
248
|
+
* `client.secrets` — AGENT-LOCAL facade. Reads `stackbone_platform.secrets`
|
|
249
|
+
* over the shared `client.database` pool and decrypts with the per-agent
|
|
250
|
+
* key. `not_configured` covers a missing `STACKBONE_SECRET_KEY`;
|
|
251
|
+
* `decrypt_failed` covers a key/envelope mismatch; `unavailable` covers a
|
|
252
|
+
* failed DB read.
|
|
253
|
+
*/
|
|
254
|
+
readonly secrets: readonly ["already_exists", "decrypt_failed", "forbidden", "invalid_request", "invalid_response", "not_configured", "not_found", "rate_limited", "timeout", "unauthorized", "unavailable"];
|
|
255
|
+
/**
|
|
256
|
+
* `client.config` — AGENT-LOCAL facade, reads the singleton
|
|
257
|
+
* `stackbone_platform.agent_config` row over the shared `client.database`
|
|
258
|
+
* pool.
|
|
259
|
+
*/
|
|
260
|
+
readonly config: readonly ["forbidden", "invalid_request", "invalid_response", "not_found", "rate_limited", "timeout", "unauthorized", "unavailable"];
|
|
261
|
+
/**
|
|
262
|
+
* `client.queues` — the agent → control plane job-enqueue surface. Every
|
|
263
|
+
* method (`publish`, `schedule`, `unschedule`, `listSchedules`) POSTs/GETs
|
|
264
|
+
* the BullMQ dispatcher endpoints over `HttpClient`, so the full
|
|
265
|
+
* status→domain remap (`queues_unauthorized`, `queues_not_found`,
|
|
266
|
+
* `queues_unavailable`, …) is in play. The agent never touches Redis — it
|
|
267
|
+
* only calls these endpoints.
|
|
268
|
+
*/
|
|
269
|
+
readonly queues: readonly ["forbidden", "invalid_request", "invalid_response", "not_found", "rate_limited", "timeout", "unauthorized", "unavailable"];
|
|
270
|
+
/**
|
|
271
|
+
* `client.prompts` — AGENT-LOCAL facade. Reads
|
|
272
|
+
* `stackbone_platform.prompts` / `prompt_versions` over the shared
|
|
273
|
+
* `client.database` pool and compiles templates with the local
|
|
274
|
+
* `@stackbone/prompt-compiler` (Mustache subset). `not_configured` covers a
|
|
275
|
+
* missing prompts schema (the `42P01` "run `stackbone db migrate up`" hint);
|
|
276
|
+
* `not_found` covers an absent / soft-deleted key; `missing_var` is the
|
|
277
|
+
* compile-time diagnostic when a `{{var}}` has no value; `unavailable`
|
|
278
|
+
* covers a failed DB read; `already_exists` covers a duplicate `create`.
|
|
279
|
+
*/
|
|
280
|
+
readonly prompts: readonly ["already_exists", "invalid_request", "missing_var", "not_configured", "not_found", "unavailable"];
|
|
281
|
+
/**
|
|
282
|
+
* `client.memory` — reserved. The README documents the prefix; today the
|
|
283
|
+
* pending surface returns `not_implemented` for every method so no
|
|
284
|
+
* `memory_*` codes are emitted yet. Kept as an empty bucket so the
|
|
285
|
+
* catalog matches the README table verbatim.
|
|
286
|
+
*/
|
|
287
|
+
readonly memory: readonly string[];
|
|
288
|
+
/**
|
|
289
|
+
* Transport-level error family — `HttpClient` emits these when no
|
|
290
|
+
* facade-specific `errorMapping.prefix` is configured, or when the
|
|
291
|
+
* failure is pre-/post-HTTP (DNS, abort, body parse).
|
|
292
|
+
*/
|
|
293
|
+
readonly http: readonly ["aborted", "client_error", "forbidden", "invalid_request", "invalid_response", "network_error", "not_found", "parse_error", "rate_limited", "request_failed", "server_error", "timeout", "unauthorized"];
|
|
294
|
+
/**
|
|
295
|
+
* `client.database` (and its cross-surface consumers) — the setup-bug
|
|
296
|
+
* surface stays under one prefix so creators can pattern-match on
|
|
297
|
+
* `database_*` regardless of whether the failure came from the database
|
|
298
|
+
* module, RAG, or the observability run-cost hook.
|
|
299
|
+
*/
|
|
300
|
+
readonly database: readonly ["not_configured"];
|
|
301
|
+
/**
|
|
302
|
+
* Observability — the run-cost rollup hook in `@stackbone/sdk/observability`
|
|
303
|
+
* (wired by the runtime, not a creator surface) emits this when its post-run
|
|
304
|
+
* Postgres write fails; everything else either returns ok or surfaces through
|
|
305
|
+
* the `database_*` family above.
|
|
306
|
+
*/
|
|
307
|
+
readonly observability: readonly ["close_run_failed"];
|
|
308
|
+
/**
|
|
309
|
+
* Contract handshake / gating family — emitted by the per-client
|
|
310
|
+
* `ContractStore` and consumed by every gated facade as a hard error.
|
|
311
|
+
* Documented for creators in the gating section of the SDK ADRs.
|
|
312
|
+
*/
|
|
313
|
+
readonly contract: readonly ["malformed", "unreachable", "version_unsupported"];
|
|
314
|
+
/**
|
|
315
|
+
* Capability gating family — the single code is the table-driven block
|
|
316
|
+
* the gate returns when the negotiated contract lacks the required
|
|
317
|
+
* capability. The shape lives in `@stackbone/validators`; the SDK widens
|
|
318
|
+
* it into `SdkError` verbatim.
|
|
319
|
+
*/
|
|
320
|
+
readonly capability: readonly ["unavailable"];
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Freestanding codes — they don't follow the `<prefix>_<reason>` rule, so
|
|
324
|
+
* they live as full literals here. Two families:
|
|
325
|
+
*
|
|
326
|
+
* - `not_implemented` for the pending surfaces' stubbed methods.
|
|
327
|
+
* - `*_missing` for the setup-bug rail every facade checks before its
|
|
328
|
+
* first network call (`STACKBONE_API_URL`, `STACKBONE_AGENT_ID`,
|
|
329
|
+
* `OPENROUTER_API_KEY`, `STACKBONE_POSTGRES_URL`).
|
|
330
|
+
*/
|
|
331
|
+
declare const SDK_ERROR_CODES_STANDALONE: readonly ["agent_id_missing", "database_url_missing", "not_implemented", "openrouter_key_missing", "stackbone_api_url_missing"];
|
|
332
|
+
/**
|
|
333
|
+
* Type-level projection: `<prefix>_<reason>` for every entry of
|
|
334
|
+
* `SDK_ERROR_CODE_PREFIXES`, plus the standalone codes. The trick is the
|
|
335
|
+
* second mapped-tuple step: `(typeof SDK_ERROR_CODE_PREFIXES)[P][number]`
|
|
336
|
+
* collapses the tuple to the union of its suffixes, then template-string
|
|
337
|
+
* inference builds the full code.
|
|
338
|
+
*/
|
|
339
|
+
type Prefix = keyof typeof SDK_ERROR_CODE_PREFIXES;
|
|
340
|
+
type PrefixedCode<P extends Prefix = Prefix> = {
|
|
341
|
+
[K in P]: `${K}_${(typeof SDK_ERROR_CODE_PREFIXES)[K][number]}`;
|
|
342
|
+
}[P];
|
|
343
|
+
type SdkErrorCode = PrefixedCode | (typeof SDK_ERROR_CODES_STANDALONE)[number];
|
|
344
|
+
/**
|
|
345
|
+
* Type guard for callers that receive a raw string from the wire (e.g. a
|
|
346
|
+
* decoded JSON body) and want to narrow it to the catalog. Returns false
|
|
347
|
+
* for any unknown code so the caller can decide whether to re-throw,
|
|
348
|
+
* fall back, or surface the raw string.
|
|
349
|
+
*/
|
|
350
|
+
declare function isSdkErrorCode(code: string): code is SdkErrorCode;
|
|
351
|
+
/**
|
|
352
|
+
* Prefix names exposed as a tuple for the `errorMapping.prefix` slot on
|
|
353
|
+
* `HttpClient.request`. Statically tying the transport's prefix to the
|
|
354
|
+
* catalog keeps `<prefix>_<reason>` emissions inside `SdkErrorCode` —
|
|
355
|
+
* adding a new prefix means adding it here AND a `<prefix>_invalid_request`
|
|
356
|
+
* / `<prefix>_invalid_response` / standard status-mapped suffix to the
|
|
357
|
+
* catalog above.
|
|
358
|
+
*/
|
|
359
|
+
type SdkErrorPrefix = Prefix;
|
|
360
|
+
|
|
98
361
|
/**
|
|
99
362
|
* Uniform error envelope returned by every SDK method. `cause` and `meta` are
|
|
100
363
|
* optional escape hatches so wrappers can attach upstream errors / context
|
|
101
364
|
* without breaking the public shape.
|
|
365
|
+
*
|
|
366
|
+
* `code` is typed against the catalog declared in `errors/codes.ts` — the
|
|
367
|
+
* compiler refuses any literal that isn't part of the documented inventory.
|
|
368
|
+
* Adding a new code is a single-file edit there; ALL throw sites then update
|
|
369
|
+
* automatically through the union.
|
|
102
370
|
*/
|
|
103
371
|
interface SdkError {
|
|
104
|
-
code:
|
|
372
|
+
code: SdkErrorCode;
|
|
105
373
|
message: string;
|
|
106
374
|
cause?: unknown;
|
|
107
375
|
meta?: Record<string, unknown>;
|
|
@@ -126,55 +394,117 @@ type Result<T> = {
|
|
|
126
394
|
* `Result<T>` of the method.
|
|
127
395
|
*
|
|
128
396
|
* Test seam — every gated module exposes an optional `gate` constructor
|
|
129
|
-
* argument that defaults to `createModuleGate(<id>, resolved)`. Specs
|
|
130
|
-
* a stub returning a synthetic `Result<undefined>` to exercise the
|
|
131
|
-
* paths (ok / `contract_version_unsupported` / `capability_unavailable`
|
|
132
|
-
* `contract_unreachable`) without driving a real handshake.
|
|
397
|
+
* argument that defaults to `createModuleGate(<id>, resolved, store)`. Specs
|
|
398
|
+
* inject a stub returning a synthetic `Result<undefined>` to exercise the
|
|
399
|
+
* gating paths (ok / `contract_version_unsupported` / `capability_unavailable`
|
|
400
|
+
* / `contract_unreachable`) without driving a real handshake.
|
|
133
401
|
*/
|
|
134
402
|
type ModuleGate = () => Promise<Result<undefined>>;
|
|
135
403
|
|
|
136
|
-
/** Subset of `RequestInit['body']` we serialize without modification. */
|
|
137
|
-
type SerializedBody = NonNullable<RequestInit['body']>;
|
|
138
|
-
interface RequestOptions$1 extends Omit<RequestInit, 'body' | 'signal'> {
|
|
139
|
-
params?: Record<string, string>;
|
|
140
|
-
body?: SerializedBody | Record<string, unknown> | unknown[] | null;
|
|
141
|
-
signal?: AbortSignal;
|
|
142
|
-
/** Allow retrying non-idempotent requests (POST, PATCH). Off by default to prevent duplicate writes. */
|
|
143
|
-
idempotent?: boolean;
|
|
144
|
-
}
|
|
145
|
-
interface HttpClientOptions {
|
|
146
|
-
/** Default 30_000. Set to 0 to disable. */
|
|
147
|
-
timeout?: number;
|
|
148
|
-
/** Default 3. Set to 0 to disable. */
|
|
149
|
-
retryCount?: number;
|
|
150
|
-
/** Initial backoff in ms; doubles each attempt with ±15% jitter. Default 500. */
|
|
151
|
-
retryDelay?: number;
|
|
152
|
-
/** Override the global fetch (useful for tests). */
|
|
153
|
-
fetch?: typeof fetch;
|
|
154
|
-
}
|
|
155
404
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* envelope so facades never throw at the SDK boundary.
|
|
405
|
+
* Public surface a creator can use through `client.database`. We pin it to the
|
|
406
|
+
* `postgres-js` Drizzle wrapper because that is the only driver `@stackbone/sdk`
|
|
407
|
+
* ships and the only one `STACKBONE_POSTGRES_URL` is contracted to point at.
|
|
160
408
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
409
|
+
* Re-exporting Drizzle's own type instead of inventing a structural alias keeps
|
|
410
|
+
* the surface 1:1 with what `drizzle-orm` documents and what `@stackbone/sdk/db`
|
|
411
|
+
* re-exports — so types flow end to end without translation.
|
|
163
412
|
*/
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
413
|
+
type DrizzleClient = PostgresJsDatabase<Record<string, never>> & {
|
|
414
|
+
$client: Sql;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* `client.database` is a lazy wrapper over a `drizzle()` instance bound to
|
|
419
|
+
* the resolved `databaseUrl` (config first, then `STACKBONE_POSTGRES_URL`
|
|
420
|
+
* at `resolveConfig` time). The underlying postgres-js connection and the
|
|
421
|
+
* Drizzle facade are constructed on the first method call so partner SDKs
|
|
422
|
+
* only pay the cost when the agent actually touches the database.
|
|
423
|
+
*
|
|
424
|
+
* Native Drizzle methods (`select`, `insert`, `update`, `delete`, `transaction`,
|
|
425
|
+
* `execute`) are exposed verbatim — we explicitly avoid wrapping them in a
|
|
426
|
+
* Result envelope here. Drizzle already throws on misuse and returns typed rows
|
|
427
|
+
* on success; mirroring its API keeps the contract identical to what the
|
|
428
|
+
* `@stackbone/sdk/db` re-exports document, so creators write idiomatic Drizzle.
|
|
429
|
+
*
|
|
430
|
+
* Missing `STACKBONE_POSTGRES_URL` raises `SdkError('database_not_configured')`
|
|
431
|
+
* with an actionable hint that points to `stackbone dev`. This is a creator
|
|
432
|
+
* setup bug, not something the agent's code can recover from at runtime.
|
|
433
|
+
*
|
|
434
|
+
* Methods are declared with their full Drizzle generic signatures (rather than
|
|
435
|
+
* `(...args) => Drizzle[k](...args)` arrow properties) so the inferred row
|
|
436
|
+
* types from `pgTable(...)` flow end-to-end into the caller, which is the
|
|
437
|
+
* promise the `@stackbone/sdk/db` re-export makes.
|
|
438
|
+
*
|
|
439
|
+
* The actual `postgres()` pool and the Drizzle handle are owned by the
|
|
440
|
+
* module-internal singleton in `./shared-handle.ts`. The shared handle is
|
|
441
|
+
* exposed to other SDK surfaces (RAG today, memory / queues tomorrow)
|
|
442
|
+
* through the `shared()` method below — the **canonical accessor** of
|
|
443
|
+
* the SDK's shared-handles pattern. Same instance every call → one pool
|
|
444
|
+
* per agent process. See `shared-handle.spec.ts` for the invariant.
|
|
445
|
+
*/
|
|
446
|
+
declare class DatabaseModule {
|
|
447
|
+
private readonly _gate;
|
|
448
|
+
private readonly _databaseUrl;
|
|
449
|
+
constructor(resolved: ResolvedConfig,
|
|
450
|
+
/**
|
|
451
|
+
* Test seam — see `ModuleGate`. Defaults to the lazy contract gate.
|
|
452
|
+
* The gate is awaited at the terminal `await` of every Drizzle chain
|
|
453
|
+
* (`select/insert/update/delete/execute/transaction`); when it returns
|
|
454
|
+
* an error, a tagged `GateBlockedError` is thrown so callers see the
|
|
455
|
+
* same `Error & { code, message, meta }` surface as the existing
|
|
456
|
+
* `database_not_configured` setup-bug.
|
|
457
|
+
*/
|
|
458
|
+
gate?: ModuleGate);
|
|
459
|
+
/**
|
|
460
|
+
* Escape hatch — returns Drizzle's relational query builder verbatim. The
|
|
461
|
+
* contract gate does NOT fire here because the surface is a synchronous
|
|
462
|
+
* accessor; the user can still bypass the gate via `client.database.raw()`
|
|
463
|
+
* for the same reason. Audit: `query` and `raw()` are intentionally
|
|
464
|
+
* ungated escape hatches; the standard `select/insert/update/delete/
|
|
465
|
+
* execute/transaction` path is fully gated.
|
|
466
|
+
*/
|
|
467
|
+
get query(): DrizzleClient['query'];
|
|
468
|
+
select: DrizzleClient['select'];
|
|
469
|
+
insert: DrizzleClient['insert'];
|
|
470
|
+
update: DrizzleClient['update'];
|
|
471
|
+
delete: DrizzleClient['delete'];
|
|
472
|
+
execute: DrizzleClient['execute'];
|
|
473
|
+
transaction: DrizzleClient['transaction'];
|
|
474
|
+
/**
|
|
475
|
+
* Canonical accessor for the **shared-handles pattern**. Returns the
|
|
476
|
+
* process-wide Drizzle handle backing `client.database`. Cross-surface
|
|
477
|
+
* SDK consumers (RAG today, memory / queues tomorrow) call this — never
|
|
478
|
+
* the implementation-detail singleton in `./shared-handle.ts` — to
|
|
479
|
+
* ensure they share the one `postgres()` pool the agent process owns
|
|
480
|
+
* against `STACKBONE_POSTGRES_URL`.
|
|
481
|
+
*
|
|
482
|
+
* Contract: same input → same instance. Calling `shared()` twice in the
|
|
483
|
+
* same process returns the same reference; the underlying postgres-js
|
|
484
|
+
* `Sql` is reachable as `shared().$client` for callers (e.g. RAG) that
|
|
485
|
+
* need the template-tag function instead of the Drizzle facade.
|
|
486
|
+
*
|
|
487
|
+
* Like `raw()`, this is a synchronous accessor and intentionally does
|
|
488
|
+
* NOT consult the contract gate — surfaces that gate behaviour gate it
|
|
489
|
+
* at their own call sites (`pipeline.ingest`, etc.), not at handle
|
|
490
|
+
* acquisition. Configuration errors (`database_not_configured`) throw
|
|
491
|
+
* with the same tagged `Error` shape every other `client.database`
|
|
492
|
+
* surface uses.
|
|
493
|
+
*/
|
|
494
|
+
shared(): DrizzleClient;
|
|
495
|
+
/**
|
|
496
|
+
* Escape hatch for **creator** callers that want the raw Drizzle handle
|
|
497
|
+
* (e.g. to pass it to a library that expects `PostgresJsDatabase`).
|
|
498
|
+
* Functionally identical to `shared()` — both return the same singleton
|
|
499
|
+
* and trigger the same lazy initialisation — but kept under a distinct
|
|
500
|
+
* name because the audiences differ: `shared()` is consumed by other
|
|
501
|
+
* SDK surfaces and documents the cross-surface contract; `raw()` is
|
|
502
|
+
* documented as the creator's escape hatch out of the structured
|
|
503
|
+
* `select/insert/...` surface. Reading either intentionally does NOT
|
|
504
|
+
* consult the contract gate; the caller is opting out of the SDK's
|
|
505
|
+
* structured surface.
|
|
506
|
+
*/
|
|
507
|
+
raw(): DrizzleClient;
|
|
178
508
|
}
|
|
179
509
|
|
|
180
510
|
interface ApprovalToolSpec<I, O> {
|
|
@@ -289,147 +619,151 @@ interface ApprovalListResult<T = unknown> {
|
|
|
289
619
|
items: ApprovalRecord<T>[];
|
|
290
620
|
nextCursor?: string;
|
|
291
621
|
}
|
|
622
|
+
/**
|
|
623
|
+
* `client.approval` — AGENT-LOCAL. The HITL pause is recorded directly in the
|
|
624
|
+
* agent's own `stackbone_platform.approvals` table over the shared
|
|
625
|
+
* `client.database` pool; there is no control-plane POST anymore. The RESUME
|
|
626
|
+
* half stays Studio-driven: a human decides in Studio, the control plane signs
|
|
627
|
+
* an HMAC callback to `callbackUrl`, and the agent reconciles it with
|
|
628
|
+
* `verify()` (local crypto — see `verify.ts`).
|
|
629
|
+
*
|
|
630
|
+
* The write populates only the columns the cloud `create()` used to set
|
|
631
|
+
* (topic/payload/callback_url/idempotency_key/fallback/metadata/timeout_at;
|
|
632
|
+
* `schema` was sourced from a DTO field the SDK options object does not carry,
|
|
633
|
+
* so it stays NULL). `workspace_id`/`agent_id`/`run_id` stay NULL exactly as
|
|
634
|
+
* they were on the control-plane path — the idempotency `ON CONFLICT` relies on
|
|
635
|
+
* `workspace_id` being NULL with `NULLS NOT DISTINCT` (migration 0008).
|
|
636
|
+
*/
|
|
292
637
|
declare class ApprovalFacade {
|
|
293
638
|
private readonly _resolved;
|
|
294
|
-
|
|
295
|
-
private readonly
|
|
296
|
-
constructor(_resolved: ResolvedConfig,
|
|
297
|
-
/**
|
|
298
|
-
|
|
639
|
+
/** Lazy accessor for `client.database` — reaches the shared `Sql` via `.shared().$client`. */
|
|
640
|
+
private readonly _getDatabase;
|
|
641
|
+
constructor(_resolved: ResolvedConfig,
|
|
642
|
+
/** Lazy accessor for `client.database` — reaches the shared `Sql` via `.shared().$client`. */
|
|
643
|
+
_getDatabase: () => DatabaseModule);
|
|
299
644
|
request<T = unknown>(options: ApprovalRequestOptions<T>): Promise<Result<ApprovalRequest>>;
|
|
300
645
|
cancel(approvalId: string, reason?: string): Promise<Result<void>>;
|
|
301
646
|
get<T = unknown>(approvalId: string): Promise<Result<ApprovalRecord<T>>>;
|
|
302
647
|
list<T = unknown>(options?: ApprovalListOptions): Promise<Result<ApprovalListResult<T>>>;
|
|
303
648
|
/**
|
|
304
|
-
* Local crypto verification — does not touch the
|
|
305
|
-
*
|
|
306
|
-
* issues
|
|
649
|
+
* Local crypto verification — does not touch the database, so it has the same
|
|
650
|
+
* shape it had on the control-plane surface. Auditing rule: a method gates
|
|
651
|
+
* iff it issues a datapath call; this one never does.
|
|
307
652
|
*/
|
|
308
653
|
verify<T = unknown>(request: Request, options?: VerifyOptions): Promise<Result<Decision<T>>>;
|
|
309
654
|
/**
|
|
310
|
-
* Pure factory — returns an `ApprovalTool` whose `invoke()` ultimately
|
|
311
|
-
*
|
|
312
|
-
* gated here.
|
|
655
|
+
* Pure factory — returns an `ApprovalTool` whose `invoke()` ultimately calls
|
|
656
|
+
* back into `ApprovalFacade.request`.
|
|
313
657
|
*/
|
|
314
658
|
tool<I, O>(spec: ApprovalToolSpec<I, O>): ApprovalTool<I, O>;
|
|
659
|
+
private sql;
|
|
315
660
|
}
|
|
316
661
|
|
|
317
662
|
declare class ConfigFacade {
|
|
318
|
-
private readonly
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
663
|
+
private readonly _resolved;
|
|
664
|
+
/** Lazy accessor for `client.database` — see `SecretsFacade`. */
|
|
665
|
+
private readonly _getDatabase;
|
|
666
|
+
constructor(_resolved: ResolvedConfig,
|
|
667
|
+
/** Lazy accessor for `client.database` — see `SecretsFacade`. */
|
|
668
|
+
_getDatabase: () => DatabaseModule);
|
|
323
669
|
get<T = unknown>(key: string): Promise<Result<T>>;
|
|
324
670
|
/**
|
|
325
671
|
* Keys absent from the agent's config come back as omissions in the
|
|
326
|
-
* response — the returned map only contains entries the
|
|
327
|
-
*
|
|
328
|
-
*
|
|
672
|
+
* response — the returned map only contains entries the agent DB actually
|
|
673
|
+
* has, so callers must access fields with `?.` (the return type is
|
|
674
|
+
* `Partial<T>`).
|
|
329
675
|
*/
|
|
330
676
|
getMany<T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]): Promise<Result<Partial<T>>>;
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
/** OAuth connections (Notion, GDrive, Slack…). */
|
|
334
|
-
declare class ConnectionsFacade {
|
|
335
|
-
private readonly _resolved;
|
|
336
|
-
private readonly _http;
|
|
337
|
-
constructor(_resolved: ResolvedConfig, _http: HttpClient);
|
|
338
|
-
list(): Promise<Result<readonly never[]>>;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/** Emit events to the organization event bus. */
|
|
342
|
-
declare class EventsFacade {
|
|
343
|
-
private readonly _http;
|
|
344
|
-
private readonly _gate;
|
|
345
|
-
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
346
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
347
|
-
gate?: ModuleGate);
|
|
348
|
-
emit(_name: string, _payload: unknown): Promise<Result<void>>;
|
|
677
|
+
private loadPayload;
|
|
678
|
+
private sql;
|
|
349
679
|
}
|
|
350
680
|
|
|
351
681
|
/**
|
|
352
|
-
* A prompt
|
|
353
|
-
*
|
|
682
|
+
* A prompt's current/active state. Key-based (workspace-unique). `template` is
|
|
683
|
+
* the current version's content; `version` is the live version number.
|
|
354
684
|
*/
|
|
355
685
|
interface Prompt {
|
|
686
|
+
key: string;
|
|
356
687
|
name: string;
|
|
688
|
+
description: string | null;
|
|
689
|
+
/** The current version's content. */
|
|
357
690
|
template: string;
|
|
358
|
-
/**
|
|
691
|
+
/** The live version number. The first `create` produces version `1`. */
|
|
359
692
|
version: number;
|
|
360
|
-
/**
|
|
361
|
-
variables
|
|
362
|
-
metadata
|
|
693
|
+
/** `{{var}}` names referenced by the current version. */
|
|
694
|
+
variables: readonly string[];
|
|
695
|
+
metadata: Record<string, unknown> | null;
|
|
363
696
|
/** ISO 8601 UTC timestamp. */
|
|
364
697
|
createdAt: string;
|
|
365
698
|
updatedAt: string;
|
|
366
699
|
}
|
|
367
700
|
interface GetPromptOptions {
|
|
368
|
-
/** Pin a specific version. Omitted -> latest. */
|
|
701
|
+
/** Pin a specific version. Omitted -> current/latest. */
|
|
369
702
|
version?: number;
|
|
370
703
|
}
|
|
371
704
|
interface ListPromptsOptions {
|
|
372
705
|
/** 1..100. Default 50. */
|
|
373
706
|
limit?: number;
|
|
374
|
-
cursor?: string;
|
|
375
707
|
}
|
|
376
708
|
interface ListPromptsResult {
|
|
377
|
-
/**
|
|
709
|
+
/** Current version of each (non-deleted) prompt. */
|
|
378
710
|
items: readonly Prompt[];
|
|
379
|
-
nextCursor?: string;
|
|
380
711
|
}
|
|
381
712
|
interface CreatePromptRequest {
|
|
713
|
+
key: string;
|
|
382
714
|
name: string;
|
|
715
|
+
/** The version-1 content. */
|
|
383
716
|
template: string;
|
|
717
|
+
description?: string;
|
|
384
718
|
metadata?: Record<string, unknown>;
|
|
385
719
|
}
|
|
386
720
|
interface UpdatePromptOptions {
|
|
387
|
-
/** New
|
|
721
|
+
/** New content. Appends a new immutable version. */
|
|
388
722
|
template?: string;
|
|
389
|
-
|
|
723
|
+
name?: string;
|
|
724
|
+
description?: string | null;
|
|
725
|
+
/** Replaces the prompt-head metadata. */
|
|
390
726
|
metadata?: Record<string, unknown>;
|
|
391
727
|
}
|
|
392
728
|
interface DeletePromptOptions {
|
|
393
|
-
/**
|
|
729
|
+
/** Reserved for future per-version deletes; ignored today (soft-deletes the key). */
|
|
394
730
|
version?: number;
|
|
395
731
|
}
|
|
396
732
|
interface DeletePromptResult {
|
|
397
|
-
|
|
398
|
-
/** Number of
|
|
733
|
+
key: string;
|
|
734
|
+
/** Number of prompt heads soft-deleted (0 or 1). */
|
|
399
735
|
deleted: number;
|
|
400
736
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
* plane and the public surface defined here is the contract callers can
|
|
407
|
-
* already type against.
|
|
408
|
-
*/
|
|
409
|
-
declare class PromptsFacade {
|
|
410
|
-
private readonly _http;
|
|
411
|
-
constructor(_resolved: ResolvedConfig, _http: HttpClient);
|
|
412
|
-
get(_name: string, _options?: GetPromptOptions): Promise<Result<Prompt>>;
|
|
413
|
-
compile(_name: string, _variables: Record<string, unknown>, _options?: GetPromptOptions): Promise<Result<string>>;
|
|
414
|
-
list(_options?: ListPromptsOptions): Promise<Result<ListPromptsResult>>;
|
|
415
|
-
create(_request: CreatePromptRequest): Promise<Result<Prompt>>;
|
|
416
|
-
update(_name: string, _options: UpdatePromptOptions): Promise<Result<Prompt>>;
|
|
417
|
-
delete(_name: string, _options?: DeletePromptOptions): Promise<Result<DeletePromptResult>>;
|
|
737
|
+
interface CompilePromptResult {
|
|
738
|
+
/** The rendered string. */
|
|
739
|
+
output: string;
|
|
740
|
+
/** The version the compile ran against. */
|
|
741
|
+
version: number;
|
|
418
742
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
private readonly _http;
|
|
422
|
-
private readonly _gate;
|
|
423
|
-
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
424
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
425
|
-
gate?: ModuleGate);
|
|
426
|
-
get(name: string): Promise<Result<string>>;
|
|
743
|
+
declare class PromptsFacade {
|
|
744
|
+
private readonly _resolved;
|
|
427
745
|
/**
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
746
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
747
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
748
|
+
* reaching into a sibling module's implementation file.
|
|
431
749
|
*/
|
|
432
|
-
|
|
750
|
+
private readonly _getDatabase;
|
|
751
|
+
constructor(_resolved: ResolvedConfig,
|
|
752
|
+
/**
|
|
753
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
754
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
755
|
+
* reaching into a sibling module's implementation file.
|
|
756
|
+
*/
|
|
757
|
+
_getDatabase: () => DatabaseModule);
|
|
758
|
+
get(key: string, options?: GetPromptOptions): Promise<Result<Prompt>>;
|
|
759
|
+
compile(key: string, variables: Record<string, unknown>, options?: GetPromptOptions): Promise<Result<CompilePromptResult>>;
|
|
760
|
+
list(options?: ListPromptsOptions): Promise<Result<ListPromptsResult>>;
|
|
761
|
+
create(request: CreatePromptRequest): Promise<Result<Prompt>>;
|
|
762
|
+
update(key: string, options: UpdatePromptOptions): Promise<Result<Prompt>>;
|
|
763
|
+
delete(key: string, _options?: DeletePromptOptions): Promise<Result<DeletePromptResult>>;
|
|
764
|
+
private sql;
|
|
765
|
+
private mapReadError;
|
|
766
|
+
private mapWriteError;
|
|
433
767
|
}
|
|
434
768
|
|
|
435
769
|
/**
|
|
@@ -451,7 +785,14 @@ declare class SecretsFacade {
|
|
|
451
785
|
*
|
|
452
786
|
* Test override: pass `clientOverride` to inject a pre-built `OpenAI`
|
|
453
787
|
* instance (or a stand-in) instead of letting the module build one. The
|
|
454
|
-
* override flows to all four namespaces — `models.list()` included
|
|
788
|
+
* override flows to all four namespaces — `models.list()` included —
|
|
789
|
+
* and to the cross-surface `shared()` accessor.
|
|
790
|
+
*
|
|
791
|
+
* Shared-handles pattern: `shared(): Result<OpenAI>` is the canonical
|
|
792
|
+
* accessor cross-surface SDK consumers (RAG today, memory tomorrow) use
|
|
793
|
+
* to reach the single OpenRouter client. Same instance every call; one
|
|
794
|
+
* credential, one base URL, one retry policy, one error shape. See
|
|
795
|
+
* README + CHANGELOG.
|
|
455
796
|
*/
|
|
456
797
|
declare class AiModule {
|
|
457
798
|
private readonly _resolved;
|
|
@@ -470,20 +811,40 @@ declare class AiModule {
|
|
|
470
811
|
get embeddings(): EmbeddingsNamespace;
|
|
471
812
|
get images(): ImagesNamespace;
|
|
472
813
|
get models(): ModelsNamespace;
|
|
473
|
-
|
|
814
|
+
/**
|
|
815
|
+
* Canonical accessor for the **shared-handles pattern** on the AI
|
|
816
|
+
* surface. Returns the lazily-constructed OpenRouter-flavoured `OpenAI`
|
|
817
|
+
* client backing `client.ai`. Cross-surface SDK consumers (RAG today,
|
|
818
|
+
* memory tomorrow) call this — never instantiate a parallel `OpenAI`
|
|
819
|
+
* client — to keep credential resolution, base-URL routing, retry /
|
|
820
|
+
* timeout policy, and token accounting on a single instance.
|
|
821
|
+
*
|
|
822
|
+
* Contract: same input → same instance. Calling `shared()` twice in
|
|
823
|
+
* the same process (with the same `ResolvedConfig`) returns the same
|
|
824
|
+
* reference. The Result envelope surfaces `openrouter_key_missing`
|
|
825
|
+
* when `OPENROUTER_API_KEY` is unset, matching the rest of the
|
|
826
|
+
* `client.ai.*` API; the gate is intentionally NOT consulted here
|
|
827
|
+
* (surfaces that gate behaviour gate it at their own call sites).
|
|
828
|
+
*
|
|
829
|
+
* The constructor's `clientOverride` flows through this accessor too,
|
|
830
|
+
* so tests that inject a fake `OpenAI` see consistent behaviour across
|
|
831
|
+
* `chat / embeddings / images / models` and any cross-surface
|
|
832
|
+
* consumer.
|
|
833
|
+
*/
|
|
834
|
+
shared(): Result<OpenAI>;
|
|
474
835
|
}
|
|
475
836
|
declare class ChatCompletionsNamespace {
|
|
476
837
|
private readonly _getClient;
|
|
477
838
|
private readonly _gate;
|
|
478
839
|
constructor(_getClient: () => Result<OpenAI>, _gate: ModuleGate);
|
|
479
|
-
create(params: ChatCompletionCreateParamsStreaming, options?: RequestOptions): Promise<Result<Stream<ChatCompletionChunk>>>;
|
|
480
|
-
create(params: ChatCompletionCreateParamsNonStreaming, options?: RequestOptions): Promise<Result<ChatCompletion>>;
|
|
840
|
+
create(params: ChatCompletionCreateParamsStreaming, options?: RequestOptions$1): Promise<Result<Stream<ChatCompletionChunk>>>;
|
|
841
|
+
create(params: ChatCompletionCreateParamsNonStreaming, options?: RequestOptions$1): Promise<Result<ChatCompletion>>;
|
|
481
842
|
}
|
|
482
843
|
declare class EmbeddingsNamespace {
|
|
483
844
|
private readonly _getClient;
|
|
484
845
|
private readonly _gate;
|
|
485
846
|
constructor(_getClient: () => Result<OpenAI>, _gate: ModuleGate);
|
|
486
|
-
create(params: EmbeddingCreateParams, options?: RequestOptions): Promise<Result<CreateEmbeddingResponse>>;
|
|
847
|
+
create(params: EmbeddingCreateParams, options?: RequestOptions$1): Promise<Result<CreateEmbeddingResponse>>;
|
|
487
848
|
}
|
|
488
849
|
interface ImageGenerateParams {
|
|
489
850
|
model: string;
|
|
@@ -520,7 +881,7 @@ declare class ImagesNamespace {
|
|
|
520
881
|
* empty success — surfacing the failure to the caller instead of
|
|
521
882
|
* silently producing zero images.
|
|
522
883
|
*/
|
|
523
|
-
generate(params: ImageGenerateParams, options?: RequestOptions): Promise<Result<ImagesResponse>>;
|
|
884
|
+
generate(params: ImageGenerateParams, options?: RequestOptions$1): Promise<Result<ImagesResponse>>;
|
|
524
885
|
}
|
|
525
886
|
interface OpenRouterModel {
|
|
526
887
|
id: string;
|
|
@@ -550,87 +911,273 @@ declare class ModelsNamespace {
|
|
|
550
911
|
* the namespaces — and any `clientOverride` injected for tests applies
|
|
551
912
|
* here too.
|
|
552
913
|
*/
|
|
553
|
-
list(options?: RequestOptions): Promise<Result<ModelsListResponse>>;
|
|
914
|
+
list(options?: RequestOptions$1): Promise<Result<ModelsListResponse>>;
|
|
554
915
|
}
|
|
555
|
-
interface RequestOptions {
|
|
916
|
+
interface RequestOptions$1 {
|
|
556
917
|
signal?: AbortSignal;
|
|
557
918
|
}
|
|
558
919
|
|
|
559
920
|
/**
|
|
560
|
-
*
|
|
561
|
-
* `
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
* the surface 1:1 with what `drizzle-orm` documents and what `@stackbone/sdk/db`
|
|
566
|
-
* re-exports — so types flow end to end without translation.
|
|
921
|
+
* Handle returned by `client.rag.ingestAsync`. The job id is allocated
|
|
922
|
+
* synchronously against `stackbone_rag_jobs` so the caller can track / cancel
|
|
923
|
+
* the work via the `/api/rag/jobs/:jobId/*` surface; `events` is a streaming
|
|
924
|
+
* channel of progress events (ADR §D9 shape); `result` settles when the
|
|
925
|
+
* pipeline finishes (or fails / is cancelled).
|
|
567
926
|
*/
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
927
|
+
interface IngestAsyncHandle {
|
|
928
|
+
jobId: string;
|
|
929
|
+
events: AsyncIterable<RagIngestProgress>;
|
|
930
|
+
result: Promise<Result<IngestResponse>>;
|
|
931
|
+
}
|
|
572
932
|
/**
|
|
573
|
-
* `client.
|
|
574
|
-
* `
|
|
575
|
-
*
|
|
576
|
-
* post-`createClient()` are still honoured.
|
|
933
|
+
* `client.rag` — `pgvector`-backed retrieval. Two shapes per write/read:
|
|
934
|
+
* pass `model` to let the SDK embed for you; pass embeddings precomputed for
|
|
935
|
+
* provider/dimension/batching control.
|
|
577
936
|
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* `@stackbone/sdk/db` re-exports document, so creators write idiomatic Drizzle.
|
|
937
|
+
* Implementation note: `RagModule` is a thin facade over `RagPipeline`. The
|
|
938
|
+
* pipeline owns parse → chunk → embed → persist; the facade resolves the
|
|
939
|
+
* shared `client.database` pool, builds an `Embedder` on demand, and maps
|
|
940
|
+
* configuration errors. See ADR `2026-05-10-rag-consolidation-on-client-database`.
|
|
583
941
|
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
* promise the `@stackbone/sdk/db` re-export makes.
|
|
942
|
+
* Connection ownership: this module never opens its own postgres pool. It
|
|
943
|
+
* reaches the underlying `postgres-js` `Sql` through the **shared-handles
|
|
944
|
+
* pattern** — `client.database.shared().$client` — so an agent that
|
|
945
|
+
* touches both surfaces still opens exactly one connection pool against
|
|
946
|
+
* `STACKBONE_POSTGRES_URL`. RAG is the canonical first cross-surface
|
|
947
|
+
* consumer; memory and queues will adopt the same accessor when they
|
|
948
|
+
* land.
|
|
592
949
|
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
950
|
+
* Schema readiness: the canonical schema (`stackbone_platform.rag_*`) is
|
|
951
|
+
* provisioned by the platform migrator, present on every install. When an
|
|
952
|
+
* operation still hits `42P01 relation does not exist`, the pipeline returns
|
|
953
|
+
* `SdkError('rag_schema_missing')` with an actionable hint.
|
|
597
954
|
*/
|
|
598
|
-
|
|
955
|
+
/**
|
|
956
|
+
* Test-only seams accepted by `RagModule`. Production callers leave these
|
|
957
|
+
* unset — they are injected by `ingest-async.spec.ts` to substitute the
|
|
958
|
+
* shared-pool lookup and the SQL-backed job writer.
|
|
959
|
+
*/
|
|
960
|
+
interface RagModuleTestDeps {
|
|
961
|
+
/**
|
|
962
|
+
* Replaces the `client.database.shared().$client` lookup used by
|
|
963
|
+
* `ingestAsync`. Tests can plug a fake `Sql` here without standing up
|
|
964
|
+
* the database surface.
|
|
965
|
+
*/
|
|
966
|
+
sqlProvider?: () => Result<Sql>;
|
|
967
|
+
/** Builds an `IngestJobWriter` from the resolved SQL. Defaults to `createSqlJobWriter`. */
|
|
968
|
+
jobWriterFactory?: (sql: Sql) => IngestJobWriter;
|
|
969
|
+
}
|
|
970
|
+
declare class RagModule {
|
|
599
971
|
private readonly _resolved;
|
|
972
|
+
/**
|
|
973
|
+
* Lazy accessor for `client.database`. RAG calls `getDatabase().shared()`
|
|
974
|
+
* to reach the process-wide Drizzle handle (and its `$client` Sql) via
|
|
975
|
+
* the shared-handles pattern — never reaches into a sibling module's
|
|
976
|
+
* implementation file.
|
|
977
|
+
*/
|
|
978
|
+
private readonly _getDatabase;
|
|
979
|
+
/**
|
|
980
|
+
* Lazy accessor for `client.ai`. RAG calls `getAi()` for the embeddings
|
|
981
|
+
* namespace; the `Embedder` adapter reaches the shared OpenAI client
|
|
982
|
+
* through the same `client.ai.embeddings.create` public surface, so
|
|
983
|
+
* RAG never instantiates a parallel `OpenAI` client.
|
|
984
|
+
*/
|
|
985
|
+
private readonly _getAi;
|
|
600
986
|
private readonly _gate;
|
|
987
|
+
private readonly _testSqlOverride?;
|
|
988
|
+
private readonly _testJobWriterFactory?;
|
|
601
989
|
constructor(_resolved: ResolvedConfig,
|
|
602
990
|
/**
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
|
|
608
|
-
|
|
991
|
+
* Lazy accessor for `client.database`. RAG calls `getDatabase().shared()`
|
|
992
|
+
* to reach the process-wide Drizzle handle (and its `$client` Sql) via
|
|
993
|
+
* the shared-handles pattern — never reaches into a sibling module's
|
|
994
|
+
* implementation file.
|
|
995
|
+
*/
|
|
996
|
+
_getDatabase: () => DatabaseModule,
|
|
997
|
+
/**
|
|
998
|
+
* Lazy accessor for `client.ai`. RAG calls `getAi()` for the embeddings
|
|
999
|
+
* namespace; the `Embedder` adapter reaches the shared OpenAI client
|
|
1000
|
+
* through the same `client.ai.embeddings.create` public surface, so
|
|
1001
|
+
* RAG never instantiates a parallel `OpenAI` client.
|
|
1002
|
+
*/
|
|
1003
|
+
_getAi: () => AiModule,
|
|
1004
|
+
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1005
|
+
gate?: ModuleGate, testDeps?: RagModuleTestDeps);
|
|
1006
|
+
ingest(request: IngestRequest): Promise<Result<IngestResponse>>;
|
|
1007
|
+
/**
|
|
1008
|
+
* Asynchronous ingest. Allocates a `stackbone_rag_jobs` row, returns the
|
|
1009
|
+
* job id immediately, and exposes both an `AsyncIterable` of streaming
|
|
1010
|
+
* progress events and the final `Result` so callers can `await` either
|
|
1011
|
+
* surface — typical webhook handlers consume `events` and ignore `result`,
|
|
1012
|
+
* tests `await result` directly. Cancellation is observed by flipping the
|
|
1013
|
+
* row's status (e.g. via `POST /api/rag/jobs/:jobId/cancel`); the worker
|
|
1014
|
+
* polls between chunk batches and bails out with `rag_ingest_cancelled`.
|
|
1015
|
+
*
|
|
1016
|
+
* Connection ownership: the `IngestJobWriter` is bound to the same SQL
|
|
1017
|
+
* pulled off the shared `client.database.shared()` handle, so a single
|
|
1018
|
+
* agent that issues `ingestAsync` and `client.database.select` in
|
|
1019
|
+
* flight still opens exactly one pool against
|
|
1020
|
+
* `STACKBONE_POSTGRES_URL`.
|
|
1021
|
+
*/
|
|
1022
|
+
ingestAsync(request: IngestRequest): Promise<Result<IngestAsyncHandle>>;
|
|
1023
|
+
delete(ids: string | string[], options?: DeleteOptions): Promise<Result<DeleteResponse>>;
|
|
1024
|
+
deleteWhere(filter: Record<string, unknown>, options?: DeleteOptions): Promise<Result<DeleteResponse>>;
|
|
1025
|
+
retrieve(request: RetrieveRequest): Promise<Result<RetrieveHit[]>>;
|
|
1026
|
+
/**
|
|
1027
|
+
* Drops the legacy ad-hoc RAG tables (`rag_chunks` / `_rag_meta`) that
|
|
1028
|
+
* pre-feature-30 agents provisioned on first ingest. Kept for backwards
|
|
1029
|
+
* compatibility so an old agent can clean those up after upgrading. The
|
|
1030
|
+
* canonical RAG schema (`stackbone_platform.rag_*`) is owned by the platform
|
|
1031
|
+
* migrator — this method never touches it.
|
|
1032
|
+
*/
|
|
1033
|
+
reset(): Promise<Result<void>>;
|
|
1034
|
+
/**
|
|
1035
|
+
* Pure helper — exposed verbatim from the chunker. No DB, no AI, no
|
|
1036
|
+
* datapath traffic, so it is intentionally NOT gated.
|
|
1037
|
+
*/
|
|
1038
|
+
chunk(text: string, options?: ChunkOptions): string[];
|
|
1039
|
+
/**
|
|
1040
|
+
* Pure helper — exposed verbatim from the parser. No DB, no AI, no
|
|
1041
|
+
* datapath traffic, so it is intentionally NOT gated.
|
|
1042
|
+
*/
|
|
1043
|
+
parse(input: ParseInput, options?: ParseOptions): Promise<string>;
|
|
1044
|
+
/**
|
|
1045
|
+
* Resolves the postgres-js `Sql` to use for this operation. Goes through
|
|
1046
|
+
* the **shared-handles pattern** — `client.database.shared().$client` —
|
|
1047
|
+
* to pull the postgres-js template-tag function off the same Drizzle
|
|
1048
|
+
* handle the rest of the SDK uses. Single pool per agent process.
|
|
1049
|
+
* Future cross-module transaction propagation plugs a tx-bound `Sql`
|
|
1050
|
+
* into the pipeline at the same call site, replacing the pool-bound
|
|
1051
|
+
* one without touching the rest of RAG.
|
|
1052
|
+
*
|
|
1053
|
+
* Configuration errors (`STACKBONE_POSTGRES_URL` unset) flow up as a
|
|
1054
|
+
* `Result.err` with the same `database_not_configured` shape
|
|
1055
|
+
* `client.database` raises, instead of throwing through the public
|
|
1056
|
+
* surface.
|
|
1057
|
+
*/
|
|
1058
|
+
private sql;
|
|
1059
|
+
private withPipeline;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
declare class SecretsFacade {
|
|
1063
|
+
private readonly _resolved;
|
|
1064
|
+
/**
|
|
1065
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
1066
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
1067
|
+
* reaching into a sibling module's implementation file.
|
|
1068
|
+
*/
|
|
1069
|
+
private readonly _getDatabase;
|
|
1070
|
+
private _cipher?;
|
|
1071
|
+
constructor(_resolved: ResolvedConfig,
|
|
1072
|
+
/**
|
|
1073
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
1074
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
1075
|
+
* reaching into a sibling module's implementation file.
|
|
1076
|
+
*/
|
|
1077
|
+
_getDatabase: () => DatabaseModule);
|
|
1078
|
+
get(name: string): Promise<Result<string>>;
|
|
1079
|
+
/**
|
|
1080
|
+
* Names absent from the agent come back as omissions in the response — the
|
|
1081
|
+
* returned map only contains entries the agent DB actually has. Callers that
|
|
1082
|
+
* need "all-or-nothing" semantics should diff the keys.
|
|
609
1083
|
*/
|
|
1084
|
+
getMany(names: string[]): Promise<Result<Record<string, string>>>;
|
|
1085
|
+
private cipher;
|
|
1086
|
+
private sql;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
interface StorageObject {
|
|
1090
|
+
key: string;
|
|
1091
|
+
size: number;
|
|
1092
|
+
lastModified?: Date;
|
|
1093
|
+
etag?: string;
|
|
1094
|
+
}
|
|
1095
|
+
interface UploadOptions {
|
|
1096
|
+
contentType?: string;
|
|
1097
|
+
metadata?: Record<string, string>;
|
|
1098
|
+
}
|
|
1099
|
+
interface ListOptions {
|
|
1100
|
+
prefix?: string;
|
|
1101
|
+
/** Maximum number of objects to return per page. Must be > 0. */
|
|
1102
|
+
limit?: number;
|
|
1103
|
+
cursor?: string;
|
|
1104
|
+
}
|
|
1105
|
+
interface SignedUrlOptions {
|
|
1106
|
+
/** Seconds until the URL expires. Defaults to 3600 (1h). */
|
|
1107
|
+
expiresIn?: number;
|
|
1108
|
+
/** Only honoured by `getSignedUploadUrl` — pinned into the signature so the uploader must send a matching `Content-Type`. */
|
|
1109
|
+
contentType?: string;
|
|
1110
|
+
}
|
|
1111
|
+
interface SignedUrl {
|
|
1112
|
+
url: string;
|
|
1113
|
+
expiresAt: Date;
|
|
1114
|
+
}
|
|
1115
|
+
type StorageBody = Blob | Uint8Array | string;
|
|
1116
|
+
interface S3Settings {
|
|
1117
|
+
client: S3Client;
|
|
1118
|
+
endpoint: string;
|
|
1119
|
+
bucket: string;
|
|
1120
|
+
agentId: string;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Public surface behind `client.storage`. Wraps `@aws-sdk/client-s3` against
|
|
1124
|
+
* R2 (prod) or MinIO (dev). Exposes a bucket-scoped API via `from(bucket)`.
|
|
1125
|
+
*
|
|
1126
|
+
* Multi-tenancy: every key is prefixed with `agentId/bucket/` before hitting
|
|
1127
|
+
* S3. Credentials are scoped to the agent's physical bucket (env `S3_BUCKET`),
|
|
1128
|
+
* and the `bucket` argument is a logical namespace the agent uses to organise
|
|
1129
|
+
* its own objects. The full prefixed key is an internal detail — public
|
|
1130
|
+
* methods accept and return the user-facing key (without prefix). Metadata
|
|
1131
|
+
* tracking in `_storage_objects` (Neon) is out-of-scope for this iteration.
|
|
1132
|
+
*
|
|
1133
|
+
* Memory note: `download()` materialises the whole object as a `Blob` in
|
|
1134
|
+
* memory. For agents that need to stream large objects, prefer
|
|
1135
|
+
* `getSignedDownloadUrl()` and `fetch()` the URL directly to consume the
|
|
1136
|
+
* stream incrementally.
|
|
1137
|
+
*/
|
|
1138
|
+
declare class StorageModule {
|
|
1139
|
+
private readonly _resolved;
|
|
1140
|
+
private _s3;
|
|
1141
|
+
private readonly _gate;
|
|
1142
|
+
constructor(_resolved: ResolvedConfig,
|
|
1143
|
+
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
610
1144
|
gate?: ModuleGate);
|
|
1145
|
+
from(bucket: string): StorageBucket;
|
|
1146
|
+
private settings;
|
|
1147
|
+
}
|
|
1148
|
+
declare class StorageBucket {
|
|
1149
|
+
private readonly bucketName;
|
|
1150
|
+
private readonly resolveSettings;
|
|
1151
|
+
private readonly gate;
|
|
1152
|
+
constructor(bucketName: string, resolveSettings: () => Result<S3Settings>, gate: ModuleGate);
|
|
1153
|
+
upload(key: string, body: StorageBody, options?: UploadOptions): Promise<Result<{
|
|
1154
|
+
key: string;
|
|
1155
|
+
etag?: string;
|
|
1156
|
+
}>>;
|
|
1157
|
+
download(key: string): Promise<Result<Blob>>;
|
|
1158
|
+
list(options?: ListOptions): Promise<Result<{
|
|
1159
|
+
objects: readonly StorageObject[];
|
|
1160
|
+
nextCursor?: string;
|
|
1161
|
+
}>>;
|
|
1162
|
+
remove(key: string): Promise<Result<{
|
|
1163
|
+
key: string;
|
|
1164
|
+
}>>;
|
|
611
1165
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
* ungated escape hatches; the standard `select/insert/update/delete/
|
|
617
|
-
* execute/transaction` path is fully gated.
|
|
1166
|
+
* Returns the canonical S3-style URL for an object. Pure URL-builder — no
|
|
1167
|
+
* S3 round-trip — so this method intentionally bypasses the contract gate.
|
|
1168
|
+
* Whether the URL is publicly fetchable depends on the bucket policy; for
|
|
1169
|
+
* private buckets, use `getSignedDownloadUrl` instead.
|
|
618
1170
|
*/
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
delete: DrizzleClient['delete'];
|
|
624
|
-
execute: DrizzleClient['execute'];
|
|
625
|
-
transaction: DrizzleClient['transaction'];
|
|
1171
|
+
getPublicUrl(key: string): Result<string>;
|
|
1172
|
+
getSignedUploadUrl(key: string, options?: SignedUrlOptions): Promise<Result<SignedUrl>>;
|
|
1173
|
+
getSignedDownloadUrl(key: string, options?: SignedUrlOptions): Promise<Result<SignedUrl>>;
|
|
1174
|
+
private signUrl;
|
|
626
1175
|
/**
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
* intentionally does NOT consult the contract gate — the surface is sync
|
|
631
|
-
* and the caller is opting out of the SDK's structured surface.
|
|
1176
|
+
* Resolves S3 settings, validates the user key, and prefixes it with
|
|
1177
|
+
* `${agentId}/${bucketName}/`. Rejects path-traversal segments (`..`) so a
|
|
1178
|
+
* caller-controlled key cannot escape the agent's namespace.
|
|
632
1179
|
*/
|
|
633
|
-
|
|
1180
|
+
private resolve;
|
|
634
1181
|
}
|
|
635
1182
|
|
|
636
1183
|
/**
|
|
@@ -727,9 +1274,14 @@ interface EndSessionResult {
|
|
|
727
1274
|
* scaffolding placeholder: every method returns `not_implemented`. The real
|
|
728
1275
|
* backend will be mem0 (`mem0ApiKey` / `MEM0_API_KEY`) and the public surface
|
|
729
1276
|
* defined here is the contract callers can already type against.
|
|
1277
|
+
*
|
|
1278
|
+
* Contract gating: not gated. Memory targets mem0 (a non-Stackbone partner),
|
|
1279
|
+
* so there is no Stackbone Agent Protocol capability to assert against. No
|
|
1280
|
+
* entry in `MODULE_CAPABILITIES` per the rule documented in
|
|
1281
|
+
* `contract/capability-registry.ts`. When/if the runtime moves to a
|
|
1282
|
+
* Stackbone-served backend we add the capability and wire the gate.
|
|
730
1283
|
*/
|
|
731
1284
|
declare class MemoryModule {
|
|
732
|
-
private readonly _resolved;
|
|
733
1285
|
constructor(_resolved: ResolvedConfig);
|
|
734
1286
|
add(_content: MemoryContent, _request: AddMemoryRequest): Promise<Result<MemoryItem>>;
|
|
735
1287
|
search(_query: string, _options?: SearchMemoryOptions): Promise<Result<readonly MemoryHit[]>>;
|
|
@@ -746,628 +1298,285 @@ declare class MemoryModule {
|
|
|
746
1298
|
endSession(_sessionId: string, _options?: EndSessionOptions): Promise<Result<EndSessionResult>>;
|
|
747
1299
|
}
|
|
748
1300
|
|
|
749
|
-
|
|
750
|
-
type
|
|
751
|
-
declare const STEP_TYPE_ATTRIBUTE = "stackbone.step.type";
|
|
752
|
-
declare const RUN_ID_ATTRIBUTE = "stackbone.run.id";
|
|
1301
|
+
/** Subset of `RequestInit['body']` we serialize without modification. */
|
|
1302
|
+
type SerializedBody = NonNullable<RequestInit['body']>;
|
|
753
1303
|
/**
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
*
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
1304
|
+
* Per-status overrides facades attach when an HTTP status carries a
|
|
1305
|
+
* non-canonical meaning for the endpoint — e.g. a `POST` whose `409` means
|
|
1306
|
+
* "already exists" instead of the default "conflict". Keys are HTTP statuses,
|
|
1307
|
+
* values are domain codes the transport should surface verbatim (no prefix
|
|
1308
|
+
* concatenation — the override is the final code).
|
|
1309
|
+
*
|
|
1310
|
+
* The value is typed against the catalog (`SdkErrorCode`) so an endpoint
|
|
1311
|
+
* cannot smuggle a wire code that isn't in the documented inventory.
|
|
760
1312
|
*/
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
unsafe(query: string, params?: unknown[]): Promise<unknown>;
|
|
781
|
-
end?(): Promise<void>;
|
|
782
|
-
}
|
|
783
|
-
interface RunStepsSpanProcessorOptions {
|
|
1313
|
+
type ErrorOverrides = Record<number, SdkErrorCode>;
|
|
1314
|
+
/**
|
|
1315
|
+
* Tells the transport how to remap HTTP status codes onto the surface's
|
|
1316
|
+
* domain code prefix. `404 → '<prefix>_not_found'`, `401 → '<prefix>_unauthorized'`,
|
|
1317
|
+
* etc. Endpoint-specific overrides win over the canonical mapping.
|
|
1318
|
+
*
|
|
1319
|
+
* `prefix` is constrained to `SdkErrorPrefix` (the catalog's prefix tuple) so
|
|
1320
|
+
* an unknown surface name doesn't compile — keeps the wire codes the
|
|
1321
|
+
* transport emits inside the typed inventory.
|
|
1322
|
+
*/
|
|
1323
|
+
interface ErrorMapping {
|
|
1324
|
+
/** Prefix concatenated with `_not_found`, `_unauthorized`, …. Required for any domain remap. */
|
|
1325
|
+
prefix: SdkErrorPrefix;
|
|
1326
|
+
/** Per-status final code that overrides the canonical mapping. */
|
|
1327
|
+
overrides?: ErrorOverrides;
|
|
1328
|
+
}
|
|
1329
|
+
interface RequestOptions extends Omit<RequestInit, 'body' | 'signal'> {
|
|
1330
|
+
/** Scalar query params. `undefined` values are dropped, others get coerced via `String()`. */
|
|
1331
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
784
1332
|
/**
|
|
785
|
-
*
|
|
786
|
-
*
|
|
1333
|
+
* Repeated string-array params validated and serialised by the transport.
|
|
1334
|
+
* Empty arrays / blank elements reject upstream with `<prefix>_invalid_request`
|
|
1335
|
+
* (falls back to `http_invalid_request` when no `errorMapping` is set). Each
|
|
1336
|
+
* array is joined with `,` to match the canonical control-plane shape.
|
|
787
1337
|
*/
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
|
|
793
|
-
/** Override how a span maps to a step type. */
|
|
794
|
-
resolveStepType?: ((span: ReadableSpanLike) => RunStepType) | undefined;
|
|
795
|
-
/** Test seam — inject a postgres-js-compatible client. */
|
|
796
|
-
sql?: PostgresLike | undefined;
|
|
797
|
-
/** Test seam — deterministic UUID generator. */
|
|
798
|
-
newId?: (() => string) | undefined;
|
|
1338
|
+
arrayParams?: Record<string, readonly string[]>;
|
|
1339
|
+
body?: SerializedBody | Record<string, unknown> | unknown[] | null;
|
|
1340
|
+
signal?: AbortSignal;
|
|
1341
|
+
/** Allow retrying non-idempotent requests (POST, PATCH). Off by default to prevent duplicate writes. */
|
|
1342
|
+
idempotent?: boolean;
|
|
799
1343
|
}
|
|
800
1344
|
/**
|
|
801
|
-
*
|
|
802
|
-
*
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
* write fails the processor logs to stderr and keeps accepting spans —
|
|
806
|
-
* observability is never load-bearing for agent execution.
|
|
1345
|
+
* Shape every facade method ideally collapses into: "POST to /path with this
|
|
1346
|
+
* body, validate against this schema, surface errors as `<prefix>_*`". The
|
|
1347
|
+
* transport handles validation, HTTP→domain remapping, querystring assembly
|
|
1348
|
+
* and the `Result` envelope so facades stay tiny orchestrators.
|
|
807
1349
|
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
1350
|
+
* `arrayParams` keys default to `errorMapping.prefix` for the validation
|
|
1351
|
+
* error code (e.g. an empty `names: []` becomes `secrets_invalid_request`).
|
|
810
1352
|
*/
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
private readonly batchSize;
|
|
815
|
-
private readonly intervalMs;
|
|
816
|
-
private readonly resolveStepType;
|
|
817
|
-
private readonly newId;
|
|
818
|
-
private readonly connectionString;
|
|
819
|
-
private readonly ownsSql;
|
|
820
|
-
private sql;
|
|
821
|
-
private timer;
|
|
822
|
-
private inFlight;
|
|
823
|
-
constructor(options?: RunStepsSpanProcessorOptions);
|
|
824
|
-
onStart(span: ReadableSpanLike): void;
|
|
825
|
-
onEnd(span: ReadableSpanLike): void;
|
|
826
|
-
forceFlush(): Promise<void>;
|
|
827
|
-
shutdown(): Promise<void>;
|
|
828
|
-
private buildRow;
|
|
829
|
-
private armTimer;
|
|
830
|
-
private disarmTimer;
|
|
831
|
-
private flush;
|
|
832
|
-
private ensureSql;
|
|
833
|
-
private writeBatch;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
interface AggregateRunCostOptions {
|
|
837
|
-
sql: PostgresLike;
|
|
838
|
-
}
|
|
839
|
-
interface AggregateRunCostResult {
|
|
840
|
-
costEstimatedUsd: number;
|
|
841
|
-
}
|
|
842
|
-
declare function aggregateRunCost(runId: string, options: AggregateRunCostOptions): Promise<AggregateRunCostResult>;
|
|
843
|
-
|
|
844
|
-
declare const LOG_LEVELS: {
|
|
845
|
-
readonly trace: 10;
|
|
846
|
-
readonly debug: 20;
|
|
847
|
-
readonly info: 30;
|
|
848
|
-
readonly warn: 40;
|
|
849
|
-
readonly error: 50;
|
|
850
|
-
readonly fatal: 60;
|
|
851
|
-
};
|
|
852
|
-
type LogLevelName = keyof typeof LOG_LEVELS;
|
|
853
|
-
type LogLevelNumber = (typeof LOG_LEVELS)[LogLevelName];
|
|
854
|
-
interface LogRecord {
|
|
855
|
-
/** Pino-style numeric level. */
|
|
856
|
-
level: LogLevelNumber;
|
|
857
|
-
/** Epoch milliseconds. Pino uses `time` as a number. */
|
|
858
|
-
time: number;
|
|
859
|
-
msg: string;
|
|
860
|
-
run_id: string;
|
|
861
|
-
trace_id?: string;
|
|
862
|
-
installation_id?: string;
|
|
863
|
-
agent_id?: string;
|
|
864
|
-
[key: string]: unknown;
|
|
865
|
-
}
|
|
866
|
-
interface PlatformLoggerOptions {
|
|
867
|
-
runId: string;
|
|
868
|
-
installationId?: string | undefined;
|
|
869
|
-
agentId?: string | undefined;
|
|
1353
|
+
interface TransportRequest<T> extends RequestOptions {
|
|
1354
|
+
method: string;
|
|
1355
|
+
path: string;
|
|
870
1356
|
/**
|
|
871
|
-
*
|
|
872
|
-
*
|
|
873
|
-
* `<
|
|
1357
|
+
* Zod schema (or any object with a `.safeParse()` method) the parsed body
|
|
1358
|
+
* must validate against. On failure the transport surfaces
|
|
1359
|
+
* `<prefix>_invalid_response` (or `http_invalid_response` if no prefix is
|
|
1360
|
+
* configured) with the schema issues attached to `meta.issues`.
|
|
874
1361
|
*/
|
|
875
|
-
|
|
876
|
-
/**
|
|
877
|
-
|
|
878
|
-
/** Cloud-only: extra OTel resource attributes to send with every batch. */
|
|
879
|
-
resourceAttributes?: Record<string, string> | undefined;
|
|
880
|
-
/** Cloud-only: HTTP timeout per OTLP POST in ms. Defaults to 5_000. */
|
|
881
|
-
httpTimeoutMs?: number | undefined;
|
|
882
|
-
/** Flush trigger by buffer size. Defaults to 50. */
|
|
883
|
-
flushBatchSize?: number | undefined;
|
|
884
|
-
/** Flush trigger by elapsed time (ms). Defaults to 1_000. */
|
|
885
|
-
flushIntervalMs?: number | undefined;
|
|
886
|
-
/** Test seam — fetch implementation. Defaults to global `fetch`. */
|
|
887
|
-
fetchImpl?: typeof fetch | undefined;
|
|
888
|
-
/** Test seam — `fs.appendFile`-shaped sink the local destination uses. */
|
|
889
|
-
appendFileImpl?: ((path: string, data: string) => Promise<void>) | undefined;
|
|
890
|
-
/** Test seam — `fs.mkdir`-shaped helper the local destination uses. */
|
|
891
|
-
mkdirImpl?: ((path: string) => Promise<void>) | undefined;
|
|
892
|
-
/** Test seam — clock for `time` field. Defaults to `Date.now`. */
|
|
893
|
-
now?: (() => number) | undefined;
|
|
894
|
-
/** Override mode resolution (skips the env detection). */
|
|
895
|
-
mode?: PlatformLoggerMode | undefined;
|
|
896
|
-
}
|
|
897
|
-
type PlatformLoggerMode = 'local' | 'cloud';
|
|
898
|
-
interface PinoLike {
|
|
899
|
-
trace(msgOrObj: unknown, msg?: string): void;
|
|
900
|
-
debug(msgOrObj: unknown, msg?: string): void;
|
|
901
|
-
info(msgOrObj: unknown, msg?: string): void;
|
|
902
|
-
warn(msgOrObj: unknown, msg?: string): void;
|
|
903
|
-
error(msgOrObj: unknown, msg?: string): void;
|
|
904
|
-
fatal(msgOrObj: unknown, msg?: string): void;
|
|
905
|
-
}
|
|
906
|
-
interface PlatformLogger extends PinoLike {
|
|
907
|
-
readonly mode: PlatformLoggerMode;
|
|
908
|
-
/** Bind extra fields and return a child logger sharing the same destination. */
|
|
909
|
-
child(bindings: Record<string, unknown>): PlatformLogger;
|
|
910
|
-
/** Force-flush pending writes (close file handle or POST OTLP batch). */
|
|
911
|
-
flush(): Promise<void>;
|
|
912
|
-
/** Stop accepting writes and release resources. */
|
|
913
|
-
close(): Promise<void>;
|
|
1362
|
+
responseSchema?: ResponseSchema<T>;
|
|
1363
|
+
/** HTTP→domain remapping. Omit to let `http_*` codes flow through unchanged. */
|
|
1364
|
+
errorMapping?: ErrorMapping;
|
|
914
1365
|
}
|
|
915
1366
|
/**
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
1367
|
+
* Minimal contract the transport needs from a schema — `safeParse`. Compatible
|
|
1368
|
+
* with Zod (`z.object(…).safeParse(value)`) and any hand-rolled validator that
|
|
1369
|
+
* follows the same shape. Avoids leaking Zod's full surface into the transport
|
|
1370
|
+
* signature.
|
|
919
1371
|
*/
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1372
|
+
interface ResponseSchema<T> {
|
|
1373
|
+
safeParse(input: unknown): {
|
|
1374
|
+
success: true;
|
|
1375
|
+
data: T;
|
|
1376
|
+
} | {
|
|
1377
|
+
success: false;
|
|
1378
|
+
error: {
|
|
1379
|
+
issues: unknown;
|
|
1380
|
+
};
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
interface HttpClientOptions {
|
|
1384
|
+
/** Default 30_000. Set to 0 to disable. */
|
|
1385
|
+
timeout?: number;
|
|
1386
|
+
/** Default 3. Set to 0 to disable. */
|
|
1387
|
+
retryCount?: number;
|
|
1388
|
+
/** Initial backoff in ms; doubles each attempt with ±15% jitter. Default 500. */
|
|
1389
|
+
retryDelay?: number;
|
|
1390
|
+
/** Override the global fetch (useful for tests). */
|
|
1391
|
+
fetch?: typeof fetch;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Shared HTTP client used by every facade that talks to the Stackbone
|
|
1395
|
+
* control plane. Wraps `fetch` with timeout, exponential backoff with jitter,
|
|
1396
|
+
* idempotent-only retries, response validation (Zod-compatible schemas) and
|
|
1397
|
+
* a uniform `Result` envelope so facades never throw at the SDK boundary.
|
|
1398
|
+
*
|
|
1399
|
+
* Each facade tells the transport its error prefix (`'secrets'`, `'config'`,
|
|
1400
|
+
* `'approval'`) so 404/401/403/429/5xx remap to surface-specific codes —
|
|
1401
|
+
* `secrets_not_found`, `approval_unauthorized`, etc. Endpoint-specific
|
|
1402
|
+
* remaps (e.g. `409 → 'secrets_already_exists'`) flow through
|
|
1403
|
+
* `errorMapping.overrides`. Transport-level failures (network, timeout,
|
|
1404
|
+
* parse) keep the `http_*` prefix the README contract documents.
|
|
1405
|
+
*
|
|
1406
|
+
* Reads `STACKBONE_API_URL` and `STACKBONE_AGENT_JWT` lazily on every request so
|
|
1407
|
+
* env-var rotation is picked up without restarting the client.
|
|
1408
|
+
*/
|
|
1409
|
+
declare class HttpClient {
|
|
1410
|
+
private readonly resolved;
|
|
1411
|
+
private readonly timeout;
|
|
1412
|
+
private readonly retryCount;
|
|
1413
|
+
private readonly retryDelay;
|
|
1414
|
+
private readonly fetchImpl;
|
|
1415
|
+
constructor(resolved: ResolvedConfig, options?: HttpClientOptions);
|
|
938
1416
|
/**
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
* file handle / buffered timer stays stable across repeated calls inside
|
|
943
|
-
* the same run.
|
|
1417
|
+
* Convenience for `request({ method: 'GET', path, ... })`. Kept so legacy
|
|
1418
|
+
* callers (tests, pending facades) keep compiling — the deep work happens
|
|
1419
|
+
* in `request()`.
|
|
944
1420
|
*/
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1421
|
+
get<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1422
|
+
post<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1423
|
+
put<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1424
|
+
patch<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1425
|
+
delete<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
950
1426
|
/**
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
* Result envelope so the agent's run cleanup never aborts on a flaky
|
|
955
|
-
* Postgres call — the cost is decorative metadata, not load-bearing.
|
|
1427
|
+
* The transport entry point every facade should target. Handles base-URL
|
|
1428
|
+
* resolution, header injection, body/querystring serialisation, retry +
|
|
1429
|
+
* timeout, response validation and HTTP→domain error remapping.
|
|
956
1430
|
*/
|
|
957
|
-
|
|
958
|
-
private
|
|
1431
|
+
request<T>(req: TransportRequest<T>): Promise<Result<T>>;
|
|
1432
|
+
private computeBackoff;
|
|
959
1433
|
}
|
|
960
1434
|
|
|
1435
|
+
/**
|
|
1436
|
+
* Enqueue a one-shot (or deferred) job. The opaque `payload` is whatever the
|
|
1437
|
+
* agent's `receive` handler expects — the core never inspects it.
|
|
1438
|
+
*/
|
|
961
1439
|
interface PublishRequest {
|
|
962
|
-
|
|
963
|
-
|
|
1440
|
+
/** Opaque job name (e.g. `send-email`). Surfaced in the Studio inspector. */
|
|
1441
|
+
name: string;
|
|
1442
|
+
/** Opaque job payload — JSON-serialisable; the core forwards it untouched. */
|
|
1443
|
+
payload: unknown;
|
|
1444
|
+
/** Per-job override of the platform retry default. Omitted → platform default. */
|
|
964
1445
|
retries?: number;
|
|
1446
|
+
/** Deferred delivery in milliseconds. Omitted / 0 → immediate. */
|
|
965
1447
|
delay?: number;
|
|
1448
|
+
/** Idempotency hint — a duplicate publish with the same key collapses to one job. */
|
|
966
1449
|
deduplicationId?: string;
|
|
967
|
-
headers?: Record<string, string>;
|
|
968
1450
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
/** Splitter algorithm. Default `recursive`. */
|
|
982
|
-
strategy?: ChunkStrategy;
|
|
983
|
-
/** Target characters per chunk. Default 512. */
|
|
984
|
-
size?: number;
|
|
985
|
-
/** Characters of overlap between consecutive chunks. Default 64. */
|
|
986
|
-
overlap?: number;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
type RagIngestProgress = {
|
|
990
|
-
type: 'started';
|
|
991
|
-
jobId: string;
|
|
992
|
-
totalChunks: number;
|
|
993
|
-
} | {
|
|
994
|
-
type: 'progress';
|
|
995
|
-
jobId: string;
|
|
996
|
-
processedChunks: number;
|
|
997
|
-
totalChunks: number;
|
|
998
|
-
currentDocument?: {
|
|
999
|
-
source: string;
|
|
1000
|
-
ordinal: number;
|
|
1001
|
-
};
|
|
1002
|
-
} | {
|
|
1003
|
-
type: 'completed';
|
|
1004
|
-
jobId: string;
|
|
1005
|
-
totalChunks: number;
|
|
1006
|
-
} | {
|
|
1007
|
-
type: 'failed';
|
|
1008
|
-
jobId: string;
|
|
1009
|
-
error: string;
|
|
1010
|
-
};
|
|
1011
|
-
interface IngestJobStartArgs {
|
|
1012
|
-
collection: string;
|
|
1013
|
-
source: string;
|
|
1014
|
-
totalChunks: number;
|
|
1015
|
-
}
|
|
1016
|
-
interface IngestJobProgressArgs {
|
|
1017
|
-
jobId: string;
|
|
1018
|
-
processedChunks: number;
|
|
1019
|
-
totalChunks: number;
|
|
1020
|
-
currentDocument?: {
|
|
1021
|
-
source: string;
|
|
1022
|
-
ordinal: number;
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
interface IngestJobCompleteArgs {
|
|
1026
|
-
jobId: string;
|
|
1027
|
-
totalChunks: number;
|
|
1028
|
-
}
|
|
1029
|
-
interface IngestJobFailArgs {
|
|
1030
|
-
jobId: string;
|
|
1031
|
-
error: string;
|
|
1032
|
-
}
|
|
1033
|
-
interface IngestJobWriter {
|
|
1034
|
-
/**
|
|
1035
|
-
* Allocates a `stackbone_rag_jobs` row in `running` state and returns its
|
|
1036
|
-
* id. Caller is expected to surface this id to the user before any
|
|
1037
|
-
* progress event fires so a `client.rag.ingestAsync` consumer can
|
|
1038
|
-
* immediately address the job (e.g. `POST /api/rag/jobs/:jobId/cancel`).
|
|
1039
|
-
*/
|
|
1040
|
-
start(args: IngestJobStartArgs): Promise<Result<{
|
|
1041
|
-
jobId: string;
|
|
1042
|
-
}>>;
|
|
1043
|
-
/** Idempotent — last write wins on `progress` jsonb. */
|
|
1044
|
-
progress(args: IngestJobProgressArgs): Promise<void>;
|
|
1045
|
-
/** Marks the row terminal (status='succeeded'). */
|
|
1046
|
-
complete(args: IngestJobCompleteArgs): Promise<void>;
|
|
1047
|
-
/** Marks the row terminal (status='failed') and stores the error message. */
|
|
1048
|
-
fail(args: IngestJobFailArgs): Promise<void>;
|
|
1049
|
-
/**
|
|
1050
|
-
* Polled between chunk batches. Returning `true` makes the pipeline abort
|
|
1051
|
-
* with `rag_ingest_cancelled`, leaving the row in whatever terminal state
|
|
1052
|
-
* the writer transitions it to next (typically `cancelled`).
|
|
1053
|
-
*/
|
|
1054
|
-
isCancelled(args: {
|
|
1055
|
-
jobId: string;
|
|
1056
|
-
}): Promise<boolean>;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
interface ParseOptions {
|
|
1060
|
-
/** MIME type override. If omitted, the parser sniffs the input (Blob.type or PDF magic bytes). */
|
|
1061
|
-
mime?: string;
|
|
1062
|
-
}
|
|
1063
|
-
type ParseInput = Blob | string | Uint8Array | ArrayBuffer;
|
|
1064
|
-
|
|
1065
|
-
interface IngestChunk {
|
|
1066
|
-
content: string;
|
|
1067
|
-
embedding: number[];
|
|
1068
|
-
}
|
|
1069
|
-
interface IngestRequestBase {
|
|
1070
|
-
/** Document identifier — re-ingesting with the same `id` replaces all its chunks atomically. */
|
|
1071
|
-
id: string;
|
|
1072
|
-
metadata?: Record<string, unknown>;
|
|
1073
|
-
/** Logical namespace for separation. Default `'default'`. */
|
|
1074
|
-
namespace?: string;
|
|
1075
|
-
/**
|
|
1076
|
-
* Canonical collection name used by the `stackbone_rag_jobs` writer (F07)
|
|
1077
|
-
* to bind the row to a `stackbone_rag_collections` parent. Optional for
|
|
1078
|
-
* back-compat with pre-F07 callers — when absent, no job row is written
|
|
1079
|
-
* and progress is delivered only via `onProgress` (if any).
|
|
1080
|
-
*/
|
|
1081
|
-
collection?: string;
|
|
1082
|
-
/**
|
|
1083
|
-
* Optional sink for streaming progress events. Fired in addition to (and
|
|
1084
|
-
* before) the matching `IngestJobWriter` call so synchronous `ingest`
|
|
1085
|
-
* consumers can observe progress without paying the SQL writer cost.
|
|
1086
|
-
*/
|
|
1087
|
-
onProgress?: (event: RagIngestProgress) => void | Promise<void>;
|
|
1088
|
-
}
|
|
1089
|
-
interface IngestRequestPrecomputed extends IngestRequestBase {
|
|
1090
|
-
chunks: IngestChunk[];
|
|
1091
|
-
}
|
|
1092
|
-
interface IngestRequestAutoEmbed extends IngestRequestBase {
|
|
1093
|
-
chunks: string[];
|
|
1094
|
-
/** Embedding model id. When set, the pipeline calls the configured Embedder. */
|
|
1095
|
-
model: string;
|
|
1096
|
-
/**
|
|
1097
|
-
* Items per embeddings request. Consumed by the facade when it builds the
|
|
1098
|
-
* `Embedder`; ignored by the pipeline itself (the embedder is fully
|
|
1099
|
-
* configured at construction time).
|
|
1100
|
-
*/
|
|
1101
|
-
batchSize?: number;
|
|
1102
|
-
}
|
|
1103
|
-
type IngestRequest = IngestRequestPrecomputed | IngestRequestAutoEmbed;
|
|
1104
|
-
interface IngestResponse {
|
|
1105
|
-
id: string;
|
|
1106
|
-
chunks: number;
|
|
1107
|
-
}
|
|
1108
|
-
interface DeleteOptions {
|
|
1109
|
-
namespace?: string;
|
|
1110
|
-
}
|
|
1111
|
-
interface DeleteResponse {
|
|
1112
|
-
deleted: number;
|
|
1113
|
-
}
|
|
1114
|
-
interface RetrieveRequestBase {
|
|
1115
|
-
topK?: number;
|
|
1116
|
-
filter?: Record<string, unknown>;
|
|
1117
|
-
namespace?: string;
|
|
1118
|
-
includeContent?: boolean;
|
|
1119
|
-
includeMetadata?: boolean;
|
|
1120
|
-
}
|
|
1121
|
-
interface RetrieveRequestPrecomputed extends RetrieveRequestBase {
|
|
1122
|
-
embedding: number[];
|
|
1123
|
-
}
|
|
1124
|
-
interface RetrieveRequestAutoEmbed extends RetrieveRequestBase {
|
|
1125
|
-
text: string;
|
|
1126
|
-
/** Must match the model used at ingest time. */
|
|
1127
|
-
model: string;
|
|
1128
|
-
}
|
|
1129
|
-
type RetrieveRequest = RetrieveRequestPrecomputed | RetrieveRequestAutoEmbed;
|
|
1130
|
-
interface RetrieveHit {
|
|
1131
|
-
id: string;
|
|
1132
|
-
chunkIdx: number;
|
|
1133
|
-
content?: string;
|
|
1134
|
-
metadata?: Record<string, unknown>;
|
|
1135
|
-
score: number;
|
|
1451
|
+
/** Register / update a dynamic per-installation cron schedule. */
|
|
1452
|
+
interface ScheduleRequest {
|
|
1453
|
+
/** Schedule name, unique per install. Re-scheduling the same name updates it in place. */
|
|
1454
|
+
name: string;
|
|
1455
|
+
/** Opaque payload delivered on every fire. */
|
|
1456
|
+
payload: unknown;
|
|
1457
|
+
/** Cron expression evaluated by the dispatcher. */
|
|
1458
|
+
cron: string;
|
|
1459
|
+
/** IANA timezone the cron pattern runs in. Defaults to UTC. */
|
|
1460
|
+
tz?: string;
|
|
1461
|
+
/** Per-job override of the platform retry default. */
|
|
1462
|
+
retries?: number;
|
|
1136
1463
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
* synchronously against `stackbone_rag_jobs` so the caller can track / cancel
|
|
1141
|
-
* the work via the `/api/rag/jobs/:jobId/*` surface; `events` is a streaming
|
|
1142
|
-
* channel of progress events (ADR §D9 shape); `result` settles when the
|
|
1143
|
-
* pipeline finishes (or fails / is cancelled).
|
|
1144
|
-
*/
|
|
1145
|
-
interface IngestAsyncHandle {
|
|
1146
|
-
jobId: string;
|
|
1147
|
-
events: AsyncIterable<RagIngestProgress>;
|
|
1148
|
-
result: Promise<Result<IngestResponse>>;
|
|
1464
|
+
/** Cancel a previously registered schedule by name. */
|
|
1465
|
+
interface UnscheduleRequest {
|
|
1466
|
+
name: string;
|
|
1149
1467
|
}
|
|
1150
1468
|
/**
|
|
1151
|
-
* `client.
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1469
|
+
* `client.queues` — the agent's handle on the BullMQ-backed job system. The
|
|
1470
|
+
* agent never touches Redis: each method makes an authenticated call to the
|
|
1471
|
+
* control plane (the dispatcher owns the queue + the clock).
|
|
1154
1472
|
*
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1473
|
+
* - `publish` enqueues a one-shot job (optionally deferred via `delay`, with a
|
|
1474
|
+
* per-job `retries` override and an optional `deduplicationId`).
|
|
1475
|
+
* - `schedule` / `unschedule` manage dynamic per-user cron schedules.
|
|
1476
|
+
* - `listSchedules` lists the install's active schedules.
|
|
1159
1477
|
*
|
|
1160
|
-
*
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
* touches both surfaces still opens exactly one connection pool against
|
|
1164
|
-
* `STACKBONE_POSTGRES_URL`.
|
|
1478
|
+
* Authentication reuses the existing `STACKBONE_AGENT_JWT` channel injected at
|
|
1479
|
+
* provisioning (the `HttpClient` attaches the bearer + install header), so the
|
|
1480
|
+
* core scopes every enqueue to the agent's own install.
|
|
1165
1481
|
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
* `SdkError('rag_schema_missing')` with an actionable hint.
|
|
1482
|
+
* Contract gating: every method awaits the `queues.jobs` module gate before
|
|
1483
|
+
* touching the network, so a stale datapath / missing capability surfaces as
|
|
1484
|
+
* a gating error rather than a failed POST.
|
|
1170
1485
|
*/
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
* unset — they are injected by `ingest-async.spec.ts` to substitute the
|
|
1174
|
-
* shared-pool lookup and the SQL-backed job writer.
|
|
1175
|
-
*/
|
|
1176
|
-
interface RagModuleTestDeps {
|
|
1177
|
-
/** Replaces the lazy `getDatabaseHandle()` lookup used by `ingestAsync`. */
|
|
1178
|
-
sqlProvider?: () => Result<Sql>;
|
|
1179
|
-
/** Builds an `IngestJobWriter` from the resolved SQL. Defaults to `createSqlJobWriter`. */
|
|
1180
|
-
jobWriterFactory?: (sql: Sql) => IngestJobWriter;
|
|
1181
|
-
}
|
|
1182
|
-
declare class RagModule {
|
|
1183
|
-
private readonly _resolved;
|
|
1184
|
-
private readonly _getAi;
|
|
1486
|
+
declare class QueuesModule {
|
|
1487
|
+
private readonly _http;
|
|
1185
1488
|
private readonly _gate;
|
|
1186
|
-
|
|
1187
|
-
private readonly _testJobWriterFactory?;
|
|
1188
|
-
constructor(_resolved: ResolvedConfig, _getAi: () => AiModule,
|
|
1489
|
+
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1189
1490
|
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1190
|
-
gate?: ModuleGate
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
* progress events and the final `Result` so callers can `await` either
|
|
1196
|
-
* surface — typical webhook handlers consume `events` and ignore `result`,
|
|
1197
|
-
* tests `await result` directly. Cancellation is observed by flipping the
|
|
1198
|
-
* row's status (e.g. via `POST /api/rag/jobs/:jobId/cancel`); the worker
|
|
1199
|
-
* polls between chunk batches and bails out with `rag_ingest_cancelled`.
|
|
1200
|
-
*
|
|
1201
|
-
* Connection ownership: the `IngestJobWriter` is bound to the same SQL
|
|
1202
|
-
* pulled off the shared `client.database` handle, so a single agent that
|
|
1203
|
-
* issues `ingestAsync` and `client.database.select` in flight still opens
|
|
1204
|
-
* exactly one pool against `STACKBONE_POSTGRES_URL`.
|
|
1205
|
-
*/
|
|
1206
|
-
ingestAsync(request: IngestRequest): Promise<Result<IngestAsyncHandle>>;
|
|
1207
|
-
delete(ids: string | string[], options?: DeleteOptions): Promise<Result<DeleteResponse>>;
|
|
1208
|
-
deleteWhere(filter: Record<string, unknown>, options?: DeleteOptions): Promise<Result<DeleteResponse>>;
|
|
1209
|
-
retrieve(request: RetrieveRequest): Promise<Result<RetrieveHit[]>>;
|
|
1210
|
-
/**
|
|
1211
|
-
* Drops the legacy ad-hoc RAG schema. Kept for backwards compatibility with
|
|
1212
|
-
* pre-feature-30 agents whose tables were provisioned by `ensureSchema` on
|
|
1213
|
-
* first call. The canonical RAG schema (feature 30) is owned by the CLI and
|
|
1214
|
-
* removed via `stackbone db migrate` flows, not by this method.
|
|
1215
|
-
*/
|
|
1216
|
-
reset(): Promise<Result<void>>;
|
|
1217
|
-
/**
|
|
1218
|
-
* Pure helper — exposed verbatim from the chunker. No DB, no AI, no
|
|
1219
|
-
* datapath traffic, so it is intentionally NOT gated.
|
|
1220
|
-
*/
|
|
1221
|
-
chunk(text: string, options?: ChunkOptions): string[];
|
|
1222
|
-
/**
|
|
1223
|
-
* Pure helper — exposed verbatim from the parser. No DB, no AI, no
|
|
1224
|
-
* datapath traffic, so it is intentionally NOT gated.
|
|
1225
|
-
*/
|
|
1226
|
-
parse(input: ParseInput, options?: ParseOptions): Promise<string>;
|
|
1227
|
-
/**
|
|
1228
|
-
* Resolves the postgres-js `Sql` to use for this operation. Pulls `$client`
|
|
1229
|
-
* off the shared Drizzle handle (single pool per agent process). Slice F03's
|
|
1230
|
-
* NOTE about cross-module transaction propagation lives here: this is the
|
|
1231
|
-
* seam where a future `database.transaction` integration plugs a tx-bound
|
|
1232
|
-
* `Sql` into the pipeline instead of the pool-bound one.
|
|
1233
|
-
*
|
|
1234
|
-
* Configuration errors (`STACKBONE_POSTGRES_URL` unset) flow up as a
|
|
1235
|
-
* `Result.err` with the same `database_not_configured` shape `client.database`
|
|
1236
|
-
* raises, instead of throwing through the public surface.
|
|
1237
|
-
*/
|
|
1238
|
-
private sql;
|
|
1239
|
-
private withPipeline;
|
|
1491
|
+
gate?: ModuleGate);
|
|
1492
|
+
publish(request: PublishRequest): Promise<Result<PublishJobResponse>>;
|
|
1493
|
+
schedule(request: ScheduleRequest): Promise<Result<ScheduleJobResponse>>;
|
|
1494
|
+
unschedule(request: UnscheduleRequest): Promise<Result<UnscheduleJobResponse>>;
|
|
1495
|
+
listSchedules(): Promise<Result<ListSchedulesResponse>>;
|
|
1240
1496
|
}
|
|
1241
1497
|
|
|
1242
|
-
interface StorageObject {
|
|
1243
|
-
key: string;
|
|
1244
|
-
size: number;
|
|
1245
|
-
lastModified?: Date;
|
|
1246
|
-
etag?: string;
|
|
1247
|
-
}
|
|
1248
|
-
interface UploadOptions {
|
|
1249
|
-
contentType?: string;
|
|
1250
|
-
metadata?: Record<string, string>;
|
|
1251
|
-
}
|
|
1252
|
-
interface ListOptions {
|
|
1253
|
-
prefix?: string;
|
|
1254
|
-
/** Maximum number of objects to return per page. Must be > 0. */
|
|
1255
|
-
limit?: number;
|
|
1256
|
-
cursor?: string;
|
|
1257
|
-
}
|
|
1258
|
-
interface SignedUrlOptions {
|
|
1259
|
-
/** Seconds until the URL expires. Defaults to 3600 (1h). */
|
|
1260
|
-
expiresIn?: number;
|
|
1261
|
-
/** Only honoured by `getSignedUploadUrl` — pinned into the signature so the uploader must send a matching `Content-Type`. */
|
|
1262
|
-
contentType?: string;
|
|
1263
|
-
}
|
|
1264
|
-
interface SignedUrl {
|
|
1265
|
-
url: string;
|
|
1266
|
-
expiresAt: Date;
|
|
1267
|
-
}
|
|
1268
|
-
type StorageBody = Blob | Uint8Array | string;
|
|
1269
|
-
interface S3Settings {
|
|
1270
|
-
client: S3Client;
|
|
1271
|
-
endpoint: string;
|
|
1272
|
-
bucket: string;
|
|
1273
|
-
agentId: string;
|
|
1274
|
-
}
|
|
1275
1498
|
/**
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1499
|
+
* The live agent surfaces exposed to the creator's code through a single
|
|
1500
|
+
* `createClient()` entry point. The folder layout under `src/surfaces/`
|
|
1501
|
+
* groups each surface by where its state lives:
|
|
1278
1502
|
*
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1281
|
-
* and
|
|
1282
|
-
* its
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1503
|
+
* - `surfaces/agent-local/` — Postgres branch owned by the agent
|
|
1504
|
+
* (`database`, `rag`, `secrets`, `config`, `approval`). `secrets`,
|
|
1505
|
+
* `config` and `approval` moved here from the control plane: the agent now
|
|
1506
|
+
* reads its secrets/config and records HITL pauses directly against its own
|
|
1507
|
+
* `stackbone_platform.*` tables (decrypting secrets with the per-agent key),
|
|
1508
|
+
* with no control-plane round-trip. The approval RESUME callback stays
|
|
1509
|
+
* Studio-signed and is reconciled locally by `approval.verify()`.
|
|
1510
|
+
* - `surfaces/external/` — managed partner SDKs the SDK wraps directly
|
|
1511
|
+
* (`ai` / OpenRouter, `storage` / S3 + R2 / MinIO). Observability is not a
|
|
1512
|
+
* creator surface: agent code observes runs through the handler-scoped
|
|
1513
|
+
* `ctx.logger` (and plain `console.*`, which the wrapper captures and
|
|
1514
|
+
* correlates), while the platform primitives live in the
|
|
1515
|
+
* `@stackbone/sdk/observability` subpath the runtime wires directly.
|
|
1516
|
+
* - `surfaces/control-plane/` — thin HTTP calls to Stackbone's control
|
|
1517
|
+
* plane (none today after secrets/config/approval moved agent-local).
|
|
1518
|
+
* - `surfaces/pending/` — public surface is stable but the runtime is
|
|
1519
|
+
* not built yet. They are wired as live `client.X` accessors so MVP
|
|
1520
|
+
* agent code can be authored against the eventual contract today; each
|
|
1521
|
+
* method returns `Result<{ error: { code: "not_implemented" } }>` until
|
|
1522
|
+
* the backing runtime ships. Today: queues, memory.
|
|
1285
1523
|
*
|
|
1286
|
-
*
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
constructor(_resolved: ResolvedConfig,
|
|
1296
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1297
|
-
gate?: ModuleGate);
|
|
1298
|
-
from(bucket: string): StorageBucket;
|
|
1299
|
-
private settings;
|
|
1300
|
-
}
|
|
1301
|
-
declare class StorageBucket {
|
|
1302
|
-
private readonly bucketName;
|
|
1303
|
-
private readonly resolveSettings;
|
|
1304
|
-
private readonly gate;
|
|
1305
|
-
constructor(bucketName: string, resolveSettings: () => Result<S3Settings>, gate: ModuleGate);
|
|
1306
|
-
upload(key: string, body: StorageBody, options?: UploadOptions): Promise<Result<{
|
|
1307
|
-
key: string;
|
|
1308
|
-
etag?: string;
|
|
1309
|
-
}>>;
|
|
1310
|
-
download(key: string): Promise<Result<Blob>>;
|
|
1311
|
-
list(options?: ListOptions): Promise<Result<{
|
|
1312
|
-
objects: readonly StorageObject[];
|
|
1313
|
-
nextCursor?: string;
|
|
1314
|
-
}>>;
|
|
1315
|
-
remove(key: string): Promise<Result<{
|
|
1316
|
-
key: string;
|
|
1317
|
-
}>>;
|
|
1318
|
-
/**
|
|
1319
|
-
* Returns the canonical S3-style URL for an object. Pure URL-builder — no
|
|
1320
|
-
* S3 round-trip — so this method intentionally bypasses the contract gate.
|
|
1321
|
-
* Whether the URL is publicly fetchable depends on the bucket policy; for
|
|
1322
|
-
* private buckets, use `getSignedDownloadUrl` instead.
|
|
1323
|
-
*/
|
|
1324
|
-
getPublicUrl(key: string): Result<string>;
|
|
1325
|
-
getSignedUploadUrl(key: string, options?: SignedUrlOptions): Promise<Result<SignedUrl>>;
|
|
1326
|
-
getSignedDownloadUrl(key: string, options?: SignedUrlOptions): Promise<Result<SignedUrl>>;
|
|
1327
|
-
private signUrl;
|
|
1328
|
-
/**
|
|
1329
|
-
* Resolves S3 settings, validates the user key, and prefixes it with
|
|
1330
|
-
* `${agentId}/${bucketName}/`. Rejects path-traversal segments (`..`) so a
|
|
1331
|
-
* caller-controlled key cannot escape the agent's namespace.
|
|
1332
|
-
*/
|
|
1333
|
-
private resolve;
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
/**
|
|
1337
|
-
* Each accessor builds its module on first access and caches it — env vars
|
|
1338
|
-
* and partner SDK init costs are paid only when the agent actually touches
|
|
1339
|
-
* that surface.
|
|
1524
|
+
* Each accessor builds its surface on first access and caches it — env
|
|
1525
|
+
* vars and partner SDK init costs are paid only when the agent actually
|
|
1526
|
+
* touches that surface.
|
|
1527
|
+
*
|
|
1528
|
+
* Capability gating: every surface listed in `MODULE_CAPABILITIES` builds
|
|
1529
|
+
* its `ModuleGate` against the per-client `ContractStore` owned by this
|
|
1530
|
+
* instance — two clients constructed in the same process do NOT share
|
|
1531
|
+
* handshake cache or suppression-warning state. See
|
|
1532
|
+
* `contract/capability-registry.ts` for the membership rule.
|
|
1340
1533
|
*/
|
|
1341
1534
|
declare class StackboneClient {
|
|
1342
1535
|
private readonly resolved;
|
|
1343
1536
|
private _database?;
|
|
1344
1537
|
private _storage?;
|
|
1345
1538
|
private _ai?;
|
|
1346
|
-
private _queues?;
|
|
1347
1539
|
private _rag?;
|
|
1348
|
-
private _memory?;
|
|
1349
|
-
private _observability?;
|
|
1350
1540
|
private _approval?;
|
|
1351
1541
|
private _secrets?;
|
|
1352
|
-
private _connections?;
|
|
1353
1542
|
private _config?;
|
|
1354
|
-
private
|
|
1543
|
+
private _queues?;
|
|
1544
|
+
private _memory?;
|
|
1355
1545
|
private _prompts?;
|
|
1356
1546
|
private _httpClient?;
|
|
1547
|
+
private readonly _contractStore;
|
|
1357
1548
|
constructor(resolved: ResolvedConfig);
|
|
1358
1549
|
private http;
|
|
1359
1550
|
get database(): DatabaseModule;
|
|
1360
1551
|
get storage(): StorageModule;
|
|
1361
1552
|
get ai(): AiModule;
|
|
1362
|
-
get queues(): QueuesModule;
|
|
1363
1553
|
get rag(): RagModule;
|
|
1364
|
-
get memory(): MemoryModule;
|
|
1365
|
-
get observability(): ObservabilityModule;
|
|
1366
1554
|
get approval(): ApprovalFacade;
|
|
1367
1555
|
get secrets(): SecretsFacade;
|
|
1368
|
-
get connections(): ConnectionsFacade;
|
|
1369
1556
|
get config(): ConfigFacade;
|
|
1370
|
-
|
|
1557
|
+
/**
|
|
1558
|
+
* Live surface — the agent's handle on the BullMQ-backed job system.
|
|
1559
|
+
* `publish` / `schedule` / `unschedule` / `listSchedules` make authenticated
|
|
1560
|
+
* calls to the control-plane dispatcher (`/api/v1/agent/queues/*`); the
|
|
1561
|
+
* agent never touches Redis. Gated against `queues.jobs` so the
|
|
1562
|
+
* handshake-blocked / capability-missing paths are exercised the same way
|
|
1563
|
+
* as the other gated surfaces.
|
|
1564
|
+
*/
|
|
1565
|
+
get queues(): QueuesModule;
|
|
1566
|
+
/**
|
|
1567
|
+
* Pending surface — runtime not built. Every method returns
|
|
1568
|
+
* `not_implemented`. Mem0 is a third-party integration, not a Stackbone
|
|
1569
|
+
* Agent Protocol capability, so this surface is intentionally not gated.
|
|
1570
|
+
*/
|
|
1571
|
+
get memory(): MemoryModule;
|
|
1572
|
+
/**
|
|
1573
|
+
* Live agent-local surface — reads prompts from `stackbone_platform.prompts`
|
|
1574
|
+
* in the agent DB over the shared `client.database` pool and compiles
|
|
1575
|
+
* templates with the local `@stackbone/prompt-compiler`. Constructed with
|
|
1576
|
+
* the same `(resolved, () => this.database)` shape as `secrets`/`config`;
|
|
1577
|
+
* like them it is NOT wired to the control-plane handshake gate (it talks to
|
|
1578
|
+
* the agent's own tables, not a control-plane datapath).
|
|
1579
|
+
*/
|
|
1371
1580
|
get prompts(): PromptsFacade;
|
|
1372
1581
|
/**
|
|
1373
1582
|
* Read-only view of the most recently resolved Stackbone Agent Protocol
|
|
@@ -1375,9 +1584,466 @@ declare class StackboneClient {
|
|
|
1375
1584
|
* handshake has resolved successfully (the handshake fires lazily on the
|
|
1376
1585
|
* first gated module call — slices #4/#5). Synchronous, never throws,
|
|
1377
1586
|
* performs no fetches.
|
|
1587
|
+
*
|
|
1588
|
+
* Backed by this client's own `ContractStore`, so two `StackboneClient`
|
|
1589
|
+
* instances see independent `client.contract` views even when they point
|
|
1590
|
+
* at the same baseUrl.
|
|
1378
1591
|
*/
|
|
1379
1592
|
get contract(): ContractResponse | null;
|
|
1380
1593
|
}
|
|
1381
1594
|
declare function createClient(config?: ClientConfig): StackboneClient;
|
|
1382
1595
|
|
|
1383
|
-
|
|
1596
|
+
/**
|
|
1597
|
+
* Structured fields the wrapper, an upstream consumer, or a handler can
|
|
1598
|
+
* attach to a single log line. Values are `unknown` because the wrapper
|
|
1599
|
+
* does not enforce a schema — the destination decides how to serialise
|
|
1600
|
+
* each value.
|
|
1601
|
+
*/
|
|
1602
|
+
type LoggerBindings = Record<string, unknown>;
|
|
1603
|
+
/**
|
|
1604
|
+
* The handler-facing logger. Every method takes a human-readable message
|
|
1605
|
+
* plus an optional bag of structured fields the destination merges with
|
|
1606
|
+
* the bindings already set by the wrapper (typically `invocationId` and
|
|
1607
|
+
* `runId`).
|
|
1608
|
+
*
|
|
1609
|
+
* F5 wired the binding contract only; F6 replaces the stub implementation
|
|
1610
|
+
* with the JSON/STACKBONE_MODE-aware variant. Keeping the surface here
|
|
1611
|
+
* (instead of in the CLI) lets the SDK ship a typed seam creators can
|
|
1612
|
+
* reference from their own tests.
|
|
1613
|
+
*/
|
|
1614
|
+
interface Logger {
|
|
1615
|
+
/** Fine-grained diagnostic information. */
|
|
1616
|
+
debug(msg: string, meta?: LoggerBindings): void;
|
|
1617
|
+
/** Routine progress information. */
|
|
1618
|
+
info(msg: string, meta?: LoggerBindings): void;
|
|
1619
|
+
/** Recoverable problem the handler still resolved. */
|
|
1620
|
+
warn(msg: string, meta?: LoggerBindings): void;
|
|
1621
|
+
/** Unrecoverable failure the handler is about to report. */
|
|
1622
|
+
error(msg: string, meta?: LoggerBindings): void;
|
|
1623
|
+
/**
|
|
1624
|
+
* Returns a new logger that merges `bindings` into every subsequent
|
|
1625
|
+
* log line. The original logger is left untouched.
|
|
1626
|
+
*/
|
|
1627
|
+
child(bindings: LoggerBindings): Logger;
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Minimal writable surface the structured logger drains into. Both
|
|
1631
|
+
* `process.stdout` and arbitrary test doubles satisfy it — every consumer
|
|
1632
|
+
* only needs `write(string): unknown`.
|
|
1633
|
+
*/
|
|
1634
|
+
interface LogSink {
|
|
1635
|
+
write(chunk: string): unknown;
|
|
1636
|
+
}
|
|
1637
|
+
interface StructuredLoggerOptions {
|
|
1638
|
+
/** Output stream. Defaults to `process.stdout` for production use. */
|
|
1639
|
+
stream?: LogSink;
|
|
1640
|
+
/** Initial bindings every line inherits (e.g. process-wide metadata). */
|
|
1641
|
+
bindings?: LoggerBindings;
|
|
1642
|
+
/** Clock seam for the `ts` field. Defaults to `() => new Date()`. */
|
|
1643
|
+
now?: () => Date;
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Builds a structured `Logger` that emits one NDJSON line per call. Output
|
|
1647
|
+
* shape is fixed by the wrapper PRD: `{ ts, level, msg, ...bindings,
|
|
1648
|
+
* ...meta }`. The wrapper layers extra fields on top via `child()`
|
|
1649
|
+
* (`invocationId`, `runId`, `latencyMs`, `status`).
|
|
1650
|
+
*/
|
|
1651
|
+
declare const createStructuredLogger: (opts?: StructuredLoggerOptions) => Logger;
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* The data the wrapper hands to the creator's `run` function. F5 widens the
|
|
1655
|
+
* shape from F1's bare `{ input }` to the full per-invocation context:
|
|
1656
|
+
*
|
|
1657
|
+
* - `signal` is a fresh `AbortSignal` per invocation. The wrapper aborts
|
|
1658
|
+
* it when the deadline expires; the handler is expected to honour it
|
|
1659
|
+
* on cancellable work (fetches, DB queries, model calls).
|
|
1660
|
+
* - `invocationId` / `runId` come straight from the parsed envelope.
|
|
1661
|
+
* - `client` is the **same** `StackboneClient` instance for every
|
|
1662
|
+
* invocation in the process, so sub-modules can keep their internal
|
|
1663
|
+
* pools warm.
|
|
1664
|
+
* - `logger` is a child of the wrapper's structured logger already
|
|
1665
|
+
* bound to `invocationId` and `runId`, so the handler does not have to
|
|
1666
|
+
* pass them on every line. The actual JSON format lands in F6.
|
|
1667
|
+
*/
|
|
1668
|
+
interface InvokeContext<I extends z.ZodType> {
|
|
1669
|
+
input: z.infer<I>;
|
|
1670
|
+
signal: AbortSignal;
|
|
1671
|
+
invocationId: string;
|
|
1672
|
+
runId: string;
|
|
1673
|
+
client: StackboneClient;
|
|
1674
|
+
logger: Logger;
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* The synchronous `invoke` capability. Mirrors the wrapper's three HTTP verbs
|
|
1678
|
+
* (`/invoke`, `/health`, `/schema`): `invoke` is the only request path, its
|
|
1679
|
+
* `output` is the `/invoke` response body, and its schemas drive `/schema`.
|
|
1680
|
+
*
|
|
1681
|
+
* The fields are deliberately enumerated (no `[k: string]: unknown` escape
|
|
1682
|
+
* hatch) so the precise type the creator gets from `defineAgent` rejects
|
|
1683
|
+
* unknown keys on the block at compile time.
|
|
1684
|
+
*/
|
|
1685
|
+
interface InvokeCapability<I extends z.ZodType, O extends z.ZodType> {
|
|
1686
|
+
/** Zod schema for the parsed `/invoke` request body. */
|
|
1687
|
+
input: I;
|
|
1688
|
+
/** Zod schema for the `/invoke` response body. */
|
|
1689
|
+
output: O;
|
|
1690
|
+
/** Creator-supplied handler. Returns either the parsed output or a promise of it. */
|
|
1691
|
+
run: (ctx: InvokeContext<I>) => z.infer<O> | Promise<z.infer<O>>;
|
|
1692
|
+
/** Optional per-invocation timeout in milliseconds. */
|
|
1693
|
+
timeoutMs?: number;
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* A named **job handler** — a sibling of `invoke` at the top level of the spec,
|
|
1697
|
+
* reached asynchronously via `client.queues.publish({ name })` (the BullMQ
|
|
1698
|
+
* dispatcher pushes the job back to `/invoke` with that name; the wrapper routes
|
|
1699
|
+
* it here instead of to `invoke.run`).
|
|
1700
|
+
*
|
|
1701
|
+
* Same shape as `invoke` with one difference: `output` is **optional**. A job is
|
|
1702
|
+
* fire-and-forget — the dispatcher only checks the 2xx, the return value is
|
|
1703
|
+
* journalled, not delivered to a caller. Declaring `output` self-validates the
|
|
1704
|
+
* job's result; omitting it lets `run` return anything (or nothing).
|
|
1705
|
+
*/
|
|
1706
|
+
interface JobCapability<I extends z.ZodType, O extends z.ZodType = z.ZodType> {
|
|
1707
|
+
/** Zod schema for the job payload (`publish`'s `payload`, revalidated here). */
|
|
1708
|
+
input: I;
|
|
1709
|
+
/** Optional Zod schema for the job's return value. Validated + journalled, never returned. */
|
|
1710
|
+
output?: O;
|
|
1711
|
+
/** Creator-supplied job handler. May return nothing — the result is not delivered. */
|
|
1712
|
+
run: (ctx: InvokeContext<I>) => z.infer<O> | void | Promise<z.infer<O> | void>;
|
|
1713
|
+
/** Optional per-job timeout in milliseconds. */
|
|
1714
|
+
timeoutMs?: number;
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Loose, non-generic view of a single capability (invoke or a job handler).
|
|
1718
|
+
* The wrapper / `loadSpec` read the spec through this — the precise per-handler
|
|
1719
|
+
* typing lives on the value `defineAgent` returns to the creator, not here.
|
|
1720
|
+
*
|
|
1721
|
+
* `run` is a method signature (not an arrow property) on purpose: it makes the
|
|
1722
|
+
* parameter bivariant so every concrete `InvokeCapability` / `JobCapability`
|
|
1723
|
+
* (whose `run` is typed against its own narrow input) is assignable to it, which
|
|
1724
|
+
* is what lets `AgentSpec` carry a string index signature of capabilities.
|
|
1725
|
+
*/
|
|
1726
|
+
interface AnyCapability {
|
|
1727
|
+
input: z.ZodType;
|
|
1728
|
+
output?: z.ZodType | undefined;
|
|
1729
|
+
run(ctx: InvokeContext<z.ZodType>): unknown;
|
|
1730
|
+
timeoutMs?: number | undefined;
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* The declarative shape every Stackbone agent returns from `src/index.ts`, as
|
|
1734
|
+
* the wrapper / `loadSpec` consume it: the **loose runtime view**. Every
|
|
1735
|
+
* capability — `invoke` and each named job handler — is an `AnyCapability`
|
|
1736
|
+
* (`output` optional, `run` loose). `invoke` is required; the string index
|
|
1737
|
+
* carries the job handlers, reached by name through the BullMQ dispatcher.
|
|
1738
|
+
*
|
|
1739
|
+
* The precise per-handler typing (each `ctx.input` inferred, `invoke.output`
|
|
1740
|
+
* required) lives on the value `defineAgent` returns, which is assignable to
|
|
1741
|
+
* this. At runtime `invoke.output` is always present — the spec seed forces it —
|
|
1742
|
+
* so the wrapper reads it behind a small guard.
|
|
1743
|
+
*/
|
|
1744
|
+
interface AgentSpec {
|
|
1745
|
+
/** The synchronous entry: the `/invoke` response + the `GET /schema` source. */
|
|
1746
|
+
invoke: AnyCapability;
|
|
1747
|
+
/** Named job handlers, reached via `client.queues.publish({ name })`. */
|
|
1748
|
+
[name: string]: AnyCapability;
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Top-level keys the platform owns. A job handler may not use these names.
|
|
1752
|
+
* Only `invoke` for now; the contract adds `health` / `schema` / `events` /
|
|
1753
|
+
* `schedules` here if/when they become declarable capabilities (see the named
|
|
1754
|
+
* job handlers ADR).
|
|
1755
|
+
*/
|
|
1756
|
+
declare const RESERVED_HANDLER_NAMES: readonly ["invoke"];
|
|
1757
|
+
/**
|
|
1758
|
+
* Hard ceiling on any capability's `timeoutMs`. The platform saga kills the
|
|
1759
|
+
* Machine before the wrapper would time out at higher values, so a creator
|
|
1760
|
+
* declaring a 10-minute timeout would publish a contract the runtime cannot
|
|
1761
|
+
* keep. Surface that conflict at boot instead of as a mysterious mid-invocation
|
|
1762
|
+
* SIGKILL in production.
|
|
1763
|
+
*/
|
|
1764
|
+
declare const INVOKE_TIMEOUT_HARD_CAP_MS = 300000;
|
|
1765
|
+
/**
|
|
1766
|
+
* One handler block, generic **directly** on its input Zod schema `I`. `input: I`
|
|
1767
|
+
* is a single-level type parameter (not a nested indexed access like
|
|
1768
|
+
* `S['input']`), which is the shape TypeScript can invert when reading the spec
|
|
1769
|
+
* literal back — so every handler's `ctx.input` is inferred from its own schema.
|
|
1770
|
+
* `output` stays loose (`z.ZodType`) and `run`'s return is `unknown`: a handler's
|
|
1771
|
+
* result is validated against `output` at runtime (the wrapper), not at the type
|
|
1772
|
+
* layer, so output inference is not needed for the contract to hold.
|
|
1773
|
+
*/
|
|
1774
|
+
type HandlerBlock<I extends z.ZodType> = {
|
|
1775
|
+
input: I;
|
|
1776
|
+
output?: z.ZodType;
|
|
1777
|
+
run: (ctx: InvokeContext<I>) => unknown | Promise<unknown>;
|
|
1778
|
+
timeoutMs?: number;
|
|
1779
|
+
};
|
|
1780
|
+
/**
|
|
1781
|
+
* Per-key precise shape of the spec the creator writes. `T` maps each handler
|
|
1782
|
+
* name to its **input** schema; `[K in keyof T]: HandlerBlock<T[K]>` is a
|
|
1783
|
+
* homomorphic mapped type whose value (`input: T[K]`) is a single-level access,
|
|
1784
|
+
* so TypeScript infers `T` back from the literal and keeps the concrete key
|
|
1785
|
+
* names. The `& { invoke: ... }` arm makes `invoke` required and forces its
|
|
1786
|
+
* `output` to be present (a job's `output` is optional).
|
|
1787
|
+
*/
|
|
1788
|
+
type AgentSpecParam<T extends Record<string, z.ZodType>> = {
|
|
1789
|
+
[K in keyof T]: HandlerBlock<T[K]>;
|
|
1790
|
+
} & {
|
|
1791
|
+
invoke: {
|
|
1792
|
+
input: z.ZodType;
|
|
1793
|
+
output: z.ZodType;
|
|
1794
|
+
};
|
|
1795
|
+
};
|
|
1796
|
+
/**
|
|
1797
|
+
* Anchor + guard for `defineAgent`:
|
|
1798
|
+
*
|
|
1799
|
+
* 1. **Inference anchor** — each handler (`invoke` and every named job) infers
|
|
1800
|
+
* its own `ctx.input` from its `input` schema. The creator never repeats a
|
|
1801
|
+
* type.
|
|
1802
|
+
* 2. **Boot-time invariant** — `invoke` is required; every other top-level key
|
|
1803
|
+
* must be a valid handler object reached via `client.queues.publish({ name })`.
|
|
1804
|
+
* The TypeScript signature already shapes this; the runtime guard is the
|
|
1805
|
+
* safety net for creators forcing a shape through `as any`.
|
|
1806
|
+
*
|
|
1807
|
+
* Returns the spec verbatim so callers can `export default defineAgent({...})`
|
|
1808
|
+
* and the wrapper can read the same object back through dynamic import.
|
|
1809
|
+
*/
|
|
1810
|
+
declare function defineAgent<T extends Record<string, z.ZodType>>(spec: AgentSpecParam<T>): {
|
|
1811
|
+
[K in keyof T]: HandlerBlock<T[K]>;
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
interface InvocationContext {
|
|
1815
|
+
/** The envelope's `invocationId` — surfaced on log lines as `trace_id`. */
|
|
1816
|
+
readonly invocationId: string;
|
|
1817
|
+
/** The run this invoke belongs to — surfaced on log lines as `run_id`. */
|
|
1818
|
+
readonly runId: string;
|
|
1819
|
+
/**
|
|
1820
|
+
* The handler this invoke routed to — `invoke` for a direct entry or the
|
|
1821
|
+
* named job handler (e.g. `demo-log-job`) the dispatcher delivered to.
|
|
1822
|
+
* Surfaced on log lines as `handler` so the Studio logs trail can tell which
|
|
1823
|
+
* entrypoint emitted each line. Optional so a caller that binds a context
|
|
1824
|
+
* without routing info (older tests, lifecycle hooks) still type-checks.
|
|
1825
|
+
*/
|
|
1826
|
+
readonly handler?: string;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Runs `fn` with `context` bound as the active invocation. The context stays
|
|
1830
|
+
* available to every synchronous and asynchronous continuation rooted in this
|
|
1831
|
+
* call (i.e. across `await`s inside `fn`). Returns whatever `fn` returns.
|
|
1832
|
+
*/
|
|
1833
|
+
declare function runWithInvocationContext<T>(context: InvocationContext, fn: () => T): T;
|
|
1834
|
+
/**
|
|
1835
|
+
* Returns the active invocation context, or `undefined` when called outside any
|
|
1836
|
+
* invoke (process boot, lifecycle events, tests that don't set one up).
|
|
1837
|
+
*/
|
|
1838
|
+
declare function getInvocationContext(): InvocationContext | undefined;
|
|
1839
|
+
|
|
1840
|
+
/** The subset of the global `console` this bridge replaces. */
|
|
1841
|
+
interface ConsoleLike {
|
|
1842
|
+
log: (...args: unknown[]) => void;
|
|
1843
|
+
info: (...args: unknown[]) => void;
|
|
1844
|
+
warn: (...args: unknown[]) => void;
|
|
1845
|
+
error: (...args: unknown[]) => void;
|
|
1846
|
+
debug: (...args: unknown[]) => void;
|
|
1847
|
+
}
|
|
1848
|
+
interface InstallConsoleCaptureOptions {
|
|
1849
|
+
/** Console object to patch. Defaults to the global `console`. Test seam. */
|
|
1850
|
+
console?: ConsoleLike;
|
|
1851
|
+
/** Sink for `log` / `info` / `debug`. Defaults to `process.stdout`. Test seam. */
|
|
1852
|
+
stdout?: LogSink;
|
|
1853
|
+
/** Sink for `warn` / `error`. Defaults to `process.stderr`. Test seam. */
|
|
1854
|
+
stderr?: LogSink;
|
|
1855
|
+
/** Clock for the `ts` field. Defaults to `() => new Date()`. Test seam. */
|
|
1856
|
+
now?: () => Date;
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Replaces `console.log/info/debug/warn/error` so that, while an invocation
|
|
1860
|
+
* context is active, each call is re-emitted as a structured log line carrying
|
|
1861
|
+
* `run_id` / `trace_id`. Returns a function that restores the originals.
|
|
1862
|
+
* Calling it twice on the same console is a no-op (returns a no-op restore).
|
|
1863
|
+
*/
|
|
1864
|
+
declare function installInvocationConsoleCapture(options?: InstallConsoleCaptureOptions): () => void;
|
|
1865
|
+
|
|
1866
|
+
declare const STORED_TO_SDK_ENV: Readonly<Record<string, string>>;
|
|
1867
|
+
/** Encrypted envelope row as stored in `stackbone_platform.secrets`. */
|
|
1868
|
+
interface SystemSecretRow {
|
|
1869
|
+
name: string;
|
|
1870
|
+
version: string;
|
|
1871
|
+
nonce: Buffer | Uint8Array;
|
|
1872
|
+
ciphertext: Buffer | Uint8Array;
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Maps the `is_system` rows to the SDK env names, decrypting ONLY the rows that
|
|
1876
|
+
* have a known mapping, then derives two env-only channels. Existing values are
|
|
1877
|
+
* never overwritten (an explicit container/Machine override always wins). The
|
|
1878
|
+
* SDK env names this call populated are returned (sorted) so the caller can log
|
|
1879
|
+
* an observable, value-free "what got rehydrated" line — values are never
|
|
1880
|
+
* logged. Shared by both the cloud neon reader and the postgres-js reader so
|
|
1881
|
+
* they cannot drift on the mapping.
|
|
1882
|
+
*
|
|
1883
|
+
* Decryption is lazy on purpose: an unmapped stored row is skipped WITHOUT
|
|
1884
|
+
* decrypting it, so a non-envelope row (e.g. a probe row a stubbed query
|
|
1885
|
+
* returns) never throws. The two derived channels:
|
|
1886
|
+
* - `STACKBONE_POSTGRES_URL` from `databaseUrl` (the SDK's `client.database`
|
|
1887
|
+
* reads this name; the cloud harness's own Neon driver keeps `DATABASE_URL`).
|
|
1888
|
+
* - `STACKBONE_APPROVAL_SIGNING_KEY` from `HMAC_SECRET` so the SDK's approval
|
|
1889
|
+
* callback verifier reconciles with the signer (which signs with the same
|
|
1890
|
+
* boot-bundle HMAC).
|
|
1891
|
+
*/
|
|
1892
|
+
declare function rehydrateSystemSecretsRows(env: NodeJS.ProcessEnv, rows: readonly SystemSecretRow[], cipher: SecretCipher, databaseUrl: string): string[];
|
|
1893
|
+
/**
|
|
1894
|
+
* Reads the `is_system` rows from `stackbone_platform.secrets`. Injectable so
|
|
1895
|
+
* tests run against a stub without a real Postgres; production opens a
|
|
1896
|
+
* short-lived `postgres-js` connection (see `loadSystemSecretsIntoEnv`).
|
|
1897
|
+
*/
|
|
1898
|
+
type SystemSecretsReader = () => Promise<SystemSecretRow[]>;
|
|
1899
|
+
interface LoadSystemSecretsArgs {
|
|
1900
|
+
/** Connection string for the agent's Postgres (`STACKBONE_POSTGRES_URL`). */
|
|
1901
|
+
readonly databaseUrl: string;
|
|
1902
|
+
/** Per-agent base64 key the rows are decrypted with (`STACKBONE_SECRET_KEY`). */
|
|
1903
|
+
readonly secretKey: string;
|
|
1904
|
+
/** Target env to populate. Defaults to `process.env`. */
|
|
1905
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
1906
|
+
/** Override the row reader (tests inject a stub). */
|
|
1907
|
+
readonly reader?: SystemSecretsReader;
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Reads every `is_system` row from the agent's Postgres via `postgres-js`,
|
|
1911
|
+
* decrypts each with the per-agent key, and rehydrates them into `env` under
|
|
1912
|
+
* the SDK's expected names. Used by the `stackbone dev` emulator and the
|
|
1913
|
+
* self-host runtime entry to give the agent the same credentials the cloud
|
|
1914
|
+
* harness rehydrates — without depending on a Neon HTTP endpoint, which local
|
|
1915
|
+
* dev does not have.
|
|
1916
|
+
*/
|
|
1917
|
+
declare function loadSystemSecretsIntoEnv(args: LoadSystemSecretsArgs): Promise<string[]>;
|
|
1918
|
+
|
|
1919
|
+
/**
|
|
1920
|
+
* Reserved error codes. Order is intentional: keep them flat (string) so
|
|
1921
|
+
* the envelope JSON stays trivial to inspect.
|
|
1922
|
+
*
|
|
1923
|
+
* F2 owns `VALIDATION_ERROR` and `HANDLER_ERROR`. `TIMEOUT_ERROR` is
|
|
1924
|
+
* declared here so the collision check is stable across slices — the F5
|
|
1925
|
+
* timeout slice will start emitting it without needing to amend the
|
|
1926
|
+
* envelope contract.
|
|
1927
|
+
*/
|
|
1928
|
+
declare const RESERVED_ERROR_CODES: readonly ["VALIDATION_ERROR", "HANDLER_ERROR", "TIMEOUT_ERROR"];
|
|
1929
|
+
type ReservedErrorCode = (typeof RESERVED_ERROR_CODES)[number];
|
|
1930
|
+
/**
|
|
1931
|
+
* HTTP header carrying `invocationId` so the dispatcher can correlate
|
|
1932
|
+
* requests without parsing the JSON body.
|
|
1933
|
+
*/
|
|
1934
|
+
declare const INVOCATION_ID_HEADER = "X-Stackbone-Invocation-Id";
|
|
1935
|
+
/**
|
|
1936
|
+
* HTTP header carrying `runId` so the dispatcher can correlate requests
|
|
1937
|
+
* across approval callbacks, log lookups, and trace spans without parsing
|
|
1938
|
+
* the JSON body.
|
|
1939
|
+
*/
|
|
1940
|
+
declare const RUN_ID_HEADER = "X-Stackbone-Run-Id";
|
|
1941
|
+
/**
|
|
1942
|
+
* Envelope a client (the platform dispatcher, the local emulator, or a
|
|
1943
|
+
* curl smoke test) sends to `POST /invoke`. `payload` is unknown at this
|
|
1944
|
+
* layer — the wrapper revalidates it against `spec.invoke.input` before
|
|
1945
|
+
* handing it to the creator's `run`.
|
|
1946
|
+
*/
|
|
1947
|
+
declare const invokeRequestSchema: z.ZodObject<{
|
|
1948
|
+
invocationId: z.ZodString;
|
|
1949
|
+
runId: z.ZodString;
|
|
1950
|
+
payload: z.ZodUnknown;
|
|
1951
|
+
}, z.core.$strip>;
|
|
1952
|
+
type InvokeRequest = z.infer<typeof invokeRequestSchema>;
|
|
1953
|
+
/**
|
|
1954
|
+
* Error portion of a failure envelope. `issues` mirrors Zod's `ZodIssue[]`
|
|
1955
|
+
* shape verbatim so the dispatcher can render the same field-level paths
|
|
1956
|
+
* an SDK consumer would see locally.
|
|
1957
|
+
*/
|
|
1958
|
+
interface InvokeEnvelopeError {
|
|
1959
|
+
code: string;
|
|
1960
|
+
message: string;
|
|
1961
|
+
issues?: z.ZodIssue[];
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Success envelope returned by `/invoke`. `TResult` is left generic so the
|
|
1965
|
+
* wrapper can wrap the parsed output without paying for an extra type
|
|
1966
|
+
* assertion at the call site.
|
|
1967
|
+
*/
|
|
1968
|
+
interface InvokeSuccessEnvelope<TResult = unknown> {
|
|
1969
|
+
invocationId: string;
|
|
1970
|
+
runId: string;
|
|
1971
|
+
result: TResult;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Failure envelope returned by `/invoke`. Keeps `invocationId` and `runId`
|
|
1975
|
+
* at the top level so a client can correlate a 4xx/5xx with the request
|
|
1976
|
+
* without inspecting `error.issues`.
|
|
1977
|
+
*/
|
|
1978
|
+
interface InvokeErrorEnvelope {
|
|
1979
|
+
invocationId: string;
|
|
1980
|
+
runId: string;
|
|
1981
|
+
error: InvokeEnvelopeError;
|
|
1982
|
+
}
|
|
1983
|
+
type InvokeResponse<TResult = unknown> = InvokeSuccessEnvelope<TResult> | InvokeErrorEnvelope;
|
|
1984
|
+
/**
|
|
1985
|
+
* True when `code` is one of the reserved tokens the wrapper owns. Used by
|
|
1986
|
+
* the duck-typed error path: a creator that throws an error tagged with a
|
|
1987
|
+
* reserved code falls back to `HANDLER_ERROR` so reserved codes always
|
|
1988
|
+
* mean what the platform expects.
|
|
1989
|
+
*/
|
|
1990
|
+
declare const isReservedErrorCode: (code: string) => code is ReservedErrorCode;
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* Which half of the contract the diagnostic belongs to. The wrapper's error
|
|
1994
|
+
* message names the half so the creator can find the offending field
|
|
1995
|
+
* immediately ("input.user.email", not just "user.email").
|
|
1996
|
+
*/
|
|
1997
|
+
type SchemaHalf = 'input' | 'output';
|
|
1998
|
+
/**
|
|
1999
|
+
* Constructs the wrapper either rejects outright or warns about. Listed
|
|
2000
|
+
* verbatim so a future ADR can extend the union without changing the type
|
|
2001
|
+
* shape downstream consumers rely on.
|
|
2002
|
+
*/
|
|
2003
|
+
type FatalConstruct = 'transform' | 'preprocess' | 'coerce';
|
|
2004
|
+
type WarnConstruct = 'refine' | 'lazy';
|
|
2005
|
+
interface SchemaDiagnostic<Construct extends string> {
|
|
2006
|
+
half: SchemaHalf;
|
|
2007
|
+
/** Dotted JSON pointer-ish path: `email`, `user.address.street`, `items[]`. */
|
|
2008
|
+
path: string;
|
|
2009
|
+
construct: Construct;
|
|
2010
|
+
}
|
|
2011
|
+
interface AgentSchemaPair {
|
|
2012
|
+
input: z.ZodType;
|
|
2013
|
+
output: z.ZodType;
|
|
2014
|
+
}
|
|
2015
|
+
interface SchemaIntrospectionResult {
|
|
2016
|
+
fatal: SchemaDiagnostic<FatalConstruct>[];
|
|
2017
|
+
warnings: SchemaDiagnostic<WarnConstruct>[];
|
|
2018
|
+
schemas: {
|
|
2019
|
+
input: JsonSchemaDocument;
|
|
2020
|
+
output: JsonSchemaDocument;
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* JSON Schema 2020-12 document. We intentionally keep this loose — Zod's
|
|
2025
|
+
* `toJSONSchema` returns an arbitrary structure and the wrapper only needs
|
|
2026
|
+
* to serialise it verbatim to the catalog. A future ADR can tighten this
|
|
2027
|
+
* to a generated type once the catalog has a real consumer.
|
|
2028
|
+
*/
|
|
2029
|
+
interface JsonSchemaDocument {
|
|
2030
|
+
$schema?: string;
|
|
2031
|
+
type?: string | string[];
|
|
2032
|
+
properties?: Record<string, JsonSchemaDocument>;
|
|
2033
|
+
required?: string[];
|
|
2034
|
+
[key: string]: unknown;
|
|
2035
|
+
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Walks the agent's input/output schemas, classifies offending constructs,
|
|
2038
|
+
* and emits the JSON Schema 2020-12 documents the wrapper serves from
|
|
2039
|
+
* `GET /schema`. The function never throws — the caller decides what to do
|
|
2040
|
+
* with the fatal list.
|
|
2041
|
+
*
|
|
2042
|
+
* Derivation runs through Zod's native `z.toJSONSchema`. We pass
|
|
2043
|
+
* `unrepresentable: 'any'` so a `.refine()`-bearing string still surfaces as
|
|
2044
|
+
* a string (the catalog drops the constraint, the wrapper still runs it at
|
|
2045
|
+
* invoke time).
|
|
2046
|
+
*/
|
|
2047
|
+
declare const analyzeAgentSchemas: (pair: AgentSchemaPair) => SchemaIntrospectionResult;
|
|
2048
|
+
|
|
2049
|
+
export { type AddMemoryRequest, type AgentSchemaPair, type AgentSpec, type AnyCapability, type ApprovalListOptions, type ApprovalListResult, type ApprovalRecord, type ApprovalRequest, type ApprovalRequestOptions, type ApprovalStatus, type ApprovalTool, type ApprovalToolSpec, type ApprovalTopic, type ApproverInfo, type ClientConfig, type CompilePromptResult, type ConsoleLike, type CreatePromptRequest, type Decision, type DecisionStatus, type DeleteAllMemoryRequest, type DeletePromptOptions, type DeletePromptResult, type EndSessionOptions, type EndSessionResult, type FatalConstruct, type GeneratedImage, type GetPromptOptions, INVOCATION_ID_HEADER, INVOKE_TIMEOUT_HARD_CAP_MS, type ImageGenerateParams, type ImagesResponse, type IngestAsyncHandle, type InstallConsoleCaptureOptions, type InvocationContext, type InvokeCapability, type InvokeContext, type InvokeEnvelopeError, type InvokeErrorEnvelope, type InvokeRequest, type InvokeResponse, type InvokeSuccessEnvelope, type JobCapability, type JsonSchemaDocument, type ListMemoryRequest, type ListMemoryResult, type ListOptions, type ListPromptsOptions, type ListPromptsResult, type LoadSystemSecretsArgs, type LogSink, type Logger, type LoggerBindings, type MemoryContent, type MemoryHistoryEntry, type MemoryHistoryEvent, type MemoryHit, type MemoryItem, type MemoryScope, type ModelsListResponse, type OpenRouterModel, type Prompt, type PublishRequest, RESERVED_ERROR_CODES, RESERVED_HANDLER_NAMES, RUN_ID_HEADER, type ReservedErrorCode, type Result, STORED_TO_SDK_ENV, type ScheduleRequest, type SchemaDiagnostic, type SchemaHalf, type SchemaIntrospectionResult, type SdkError, type SdkErrorCode, type SearchMemoryOptions, type SignedUrl, type SignedUrlOptions, StackboneClient, type StorageBody, type StorageObject, type StructuredLoggerOptions, type SystemSecretRow, type SystemSecretsReader, type UnscheduleRequest, type UpdateMemoryOptions, type UpdatePromptOptions, type UploadOptions, type VerifyOptions, type WarnConstruct, analyzeAgentSchemas, createClient, createStructuredLogger, defineAgent, getInvocationContext, installInvocationConsoleCapture, invokeRequestSchema, isReservedErrorCode, isSdkErrorCode, loadSystemSecretsIntoEnv, rehydrateSystemSecretsRows, runWithInvocationContext };
|