@stackbone/sdk 0.1.0-alpha.3 → 0.1.0-alpha.5
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 +42 -0
- package/README.md +113 -87
- package/db/index.d.cts +1 -0
- package/db/index.d.ts +1 -0
- package/index.cjs +16462 -3519
- package/index.cjs.map +1 -1
- package/index.d.cts +863 -711
- package/index.d.ts +863 -711
- package/index.js +16446 -3511
- package/index.js.map +1 -1
- package/observability/index.cjs +14 -0
- package/observability/index.cjs.map +1 -1
- package/observability/index.d.cts +79 -79
- package/observability/index.d.ts +79 -79
- package/observability/index.js +14 -0
- package/observability/index.js.map +1 -1
- package/package.json +3 -13
- 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.5.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.3.tgz +0 -0
package/index.d.cts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { ContractResponse } from '@stackbone/validators';
|
|
1
|
+
import { AgentConnectionListResponse, InvokeConnectorActionResponse, PublishJobResponse, ScheduleJobResponse, UnscheduleJobResponse, ListSchedulesResponse, ContractResponse } from '@stackbone/validators';
|
|
2
2
|
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
3
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';
|
|
4
6
|
import OpenAI from 'openai';
|
|
5
7
|
import { ChatCompletionCreateParamsStreaming, ChatCompletionChunk, ChatCompletionCreateParamsNonStreaming, ChatCompletion, EmbeddingCreateParams, CreateEmbeddingResponse, ChatCompletionMessageParam } from 'openai/resources';
|
|
6
8
|
import { Stream } from 'openai/streaming';
|
|
7
|
-
import { RunStepsSpanProcessorOptions, RunStepsSpanProcessor, PlatformLoggerOptions, PlatformLogger, AggregateRunCostResult } from './observability/index.cjs';
|
|
8
9
|
import { S3Client } from '@aws-sdk/client-s3';
|
|
9
10
|
import { z } from 'zod';
|
|
10
11
|
export { z } from 'zod';
|
|
12
|
+
import { SecretCipher } from '@stackbone/crypto';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Optional overrides accepted by `createClient`. All fields are optional;
|
|
@@ -29,11 +31,17 @@ interface ClientConfig {
|
|
|
29
31
|
* every SDK consumer that talks to the agent's Postgres.
|
|
30
32
|
*/
|
|
31
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;
|
|
32
43
|
openrouterKey?: string;
|
|
33
44
|
openrouterBaseUrl?: string;
|
|
34
|
-
qstashToken?: string;
|
|
35
|
-
qstashCurrentSigningKey?: string;
|
|
36
|
-
qstashNextSigningKey?: string;
|
|
37
45
|
llamaParseApiKey?: string;
|
|
38
46
|
/** mem0 API key for the long-term memory backend (`client.memory`). Falls back to `MEM0_API_KEY`. */
|
|
39
47
|
mem0ApiKey?: string;
|
|
@@ -125,8 +133,14 @@ interface ResolvedConfig {
|
|
|
125
133
|
readonly approvalSigningKey: string | undefined;
|
|
126
134
|
/** Resolved from `config.agentId` ?? `STACKBONE_AGENT_ID`. */
|
|
127
135
|
readonly agentId: string | undefined;
|
|
128
|
-
/** Resolved from `config.databaseUrl` ?? `STACKBONE_POSTGRES_URL`. Shared by `client.database`, `client.rag
|
|
136
|
+
/** Resolved from `config.databaseUrl` ?? `STACKBONE_POSTGRES_URL`. Shared by `client.database`, `client.rag`, and the platform observability hooks in `@stackbone/sdk/observability`. */
|
|
129
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;
|
|
130
144
|
/** Resolved from `config.openrouterKey` ?? `OPENROUTER_API_KEY`. */
|
|
131
145
|
readonly openrouterKey: string | undefined;
|
|
132
146
|
/** Resolved from `config.openrouterBaseUrl` ?? `OPENROUTER_BASE_URL`. `undefined` means use the OpenRouter default. */
|
|
@@ -223,22 +237,59 @@ declare const SDK_ERROR_CODE_PREFIXES: {
|
|
|
223
237
|
*/
|
|
224
238
|
readonly rag: readonly ["dim_mismatch", "embedding_failed", "embedding_model_unsupported", "error", "ingest_cancelled", "invalid_request", "job_insert_failed", "jobs_error", "schema_missing"];
|
|
225
239
|
/**
|
|
226
|
-
* `client.approval` —
|
|
227
|
-
* The
|
|
228
|
-
* `
|
|
229
|
-
* verify-specific codes
|
|
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.
|
|
230
245
|
*/
|
|
231
|
-
readonly approval: readonly ["forbidden", "invalid_payload", "invalid_request", "invalid_response", "invalid_signature", "not_found", "rate_limited", "signature_expired", "signing_key_missing", "timeout", "tool_execute_failed", "unauthorized", "unavailable"];
|
|
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"];
|
|
232
247
|
/**
|
|
233
|
-
* `client.secrets` —
|
|
234
|
-
*
|
|
235
|
-
*
|
|
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.
|
|
236
253
|
*/
|
|
237
|
-
readonly secrets: readonly ["already_exists", "forbidden", "invalid_request", "invalid_response", "not_found", "rate_limited", "timeout", "unauthorized", "unavailable"];
|
|
254
|
+
readonly secrets: readonly ["already_exists", "decrypt_failed", "forbidden", "invalid_request", "invalid_response", "not_configured", "not_found", "rate_limited", "timeout", "unauthorized", "unavailable"];
|
|
238
255
|
/**
|
|
239
|
-
* `client.config` —
|
|
256
|
+
* `client.config` — AGENT-LOCAL facade, reads the singleton
|
|
257
|
+
* `stackbone_platform.agent_config` row over the shared `client.database`
|
|
258
|
+
* pool.
|
|
240
259
|
*/
|
|
241
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.connections` — the agent → control plane connectors proxy. Both
|
|
272
|
+
* methods (`list`, `invoke`) hit the `/api/v1/agent/connections/*` endpoints
|
|
273
|
+
* over `HttpClient`, so the full status→domain remap (`connections_unauthorized`,
|
|
274
|
+
* `connections_not_found`, `connections_unavailable`, …) is in play. The agent
|
|
275
|
+
* never holds a credential — it only calls these endpoints; the control plane
|
|
276
|
+
* resolves the workspace connection and runs the connector action. `ambiguous`
|
|
277
|
+
* is the 409 the proxy returns when the workspace holds several connections of
|
|
278
|
+
* the same connector and `invoke` carried no `{ connection }` selector to pick
|
|
279
|
+
* one — the error message lists the candidate names/ids so the author can.
|
|
280
|
+
*/
|
|
281
|
+
readonly connections: readonly ["ambiguous", "forbidden", "invalid_request", "invalid_response", "not_found", "rate_limited", "timeout", "unauthorized", "unavailable"];
|
|
282
|
+
/**
|
|
283
|
+
* `client.prompts` — AGENT-LOCAL facade. Reads
|
|
284
|
+
* `stackbone_platform.prompts` / `prompt_versions` over the shared
|
|
285
|
+
* `client.database` pool and compiles templates with the local
|
|
286
|
+
* `@stackbone/prompt-compiler` (Mustache subset). `not_configured` covers a
|
|
287
|
+
* missing prompts schema (the `42P01` "run `stackbone db migrate up`" hint);
|
|
288
|
+
* `not_found` covers an absent / soft-deleted key; `missing_var` is the
|
|
289
|
+
* compile-time diagnostic when a `{{var}}` has no value; `unavailable`
|
|
290
|
+
* covers a failed DB read; `already_exists` covers a duplicate `create`.
|
|
291
|
+
*/
|
|
292
|
+
readonly prompts: readonly ["already_exists", "invalid_request", "missing_var", "not_configured", "not_found", "unavailable"];
|
|
242
293
|
/**
|
|
243
294
|
* `client.memory` — reserved. The README documents the prefix; today the
|
|
244
295
|
* pending surface returns `not_implemented` for every method so no
|
|
@@ -260,9 +311,10 @@ declare const SDK_ERROR_CODE_PREFIXES: {
|
|
|
260
311
|
*/
|
|
261
312
|
readonly database: readonly ["not_configured"];
|
|
262
313
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
314
|
+
* Observability — the run-cost rollup hook in `@stackbone/sdk/observability`
|
|
315
|
+
* (wired by the runtime, not a creator surface) emits this when its post-run
|
|
316
|
+
* Postgres write fails; everything else either returns ok or surfaces through
|
|
317
|
+
* the `database_*` family above.
|
|
266
318
|
*/
|
|
267
319
|
readonly observability: readonly ["close_run_failed"];
|
|
268
320
|
/**
|
|
@@ -467,6 +519,265 @@ declare class DatabaseModule {
|
|
|
467
519
|
raw(): DrizzleClient;
|
|
468
520
|
}
|
|
469
521
|
|
|
522
|
+
interface ApprovalToolSpec<I, O> {
|
|
523
|
+
name: string;
|
|
524
|
+
description: string;
|
|
525
|
+
parameters: Record<string, unknown>;
|
|
526
|
+
needsApproval?: boolean | ((input: I) => boolean | Promise<boolean>);
|
|
527
|
+
/**
|
|
528
|
+
* Maps the LLM input to the approval request fields. `topic` and `payload`
|
|
529
|
+
* default to the tool name and the raw input respectively when omitted;
|
|
530
|
+
* `onDecide` must always be supplied so the control plane knows where to
|
|
531
|
+
* deliver the eventual decision.
|
|
532
|
+
*/
|
|
533
|
+
toRequest: (input: I) => Omit<ApprovalRequestOptions<I>, 'payload' | 'topic'> & Partial<Pick<ApprovalRequestOptions<I>, 'payload' | 'topic'>>;
|
|
534
|
+
execute: (input: I) => Promise<O> | O;
|
|
535
|
+
}
|
|
536
|
+
interface OpenAIToolSpec {
|
|
537
|
+
type: 'function';
|
|
538
|
+
function: {
|
|
539
|
+
name: string;
|
|
540
|
+
description: string;
|
|
541
|
+
parameters: Record<string, unknown>;
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
type ToolInvokeResult<O> = {
|
|
545
|
+
status: 'pending';
|
|
546
|
+
approvalId: string;
|
|
547
|
+
expiresAt: string;
|
|
548
|
+
} | {
|
|
549
|
+
status: 'ok';
|
|
550
|
+
result: O;
|
|
551
|
+
};
|
|
552
|
+
interface ApprovalTool<I, O> {
|
|
553
|
+
name: string;
|
|
554
|
+
description: string;
|
|
555
|
+
parameters: Record<string, unknown>;
|
|
556
|
+
openaiSpec(): OpenAIToolSpec;
|
|
557
|
+
invoke(input: I): Promise<Result<ToolInvokeResult<O>>>;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
interface VerifyOptions {
|
|
561
|
+
/** Override the signing key resolved from `STACKBONE_APPROVAL_SIGNING_KEY` / `approvalSigningKey`. */
|
|
562
|
+
signingKey?: string;
|
|
563
|
+
/** Reject signatures whose timestamp is older than this (seconds). Default 300. */
|
|
564
|
+
toleranceSeconds?: number;
|
|
565
|
+
/** Inject a clock for tests. Defaults to `Date.now`. */
|
|
566
|
+
now?: () => number;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
type ApprovalTopic = string;
|
|
570
|
+
declare const DECISION_STATUSES: readonly ["approved", "rejected", "timed_out", "cancelled"];
|
|
571
|
+
type DecisionStatus = (typeof DECISION_STATUSES)[number];
|
|
572
|
+
type ApprovalStatus = 'pending' | DecisionStatus;
|
|
573
|
+
type Decision<T = unknown> = {
|
|
574
|
+
status: 'approved' | 'rejected';
|
|
575
|
+
payload: T;
|
|
576
|
+
approver: ApproverInfo;
|
|
577
|
+
decidedAt: string;
|
|
578
|
+
reason?: string;
|
|
579
|
+
} | {
|
|
580
|
+
status: 'timed_out';
|
|
581
|
+
decidedAt: string;
|
|
582
|
+
} | {
|
|
583
|
+
status: 'cancelled';
|
|
584
|
+
decidedAt: string;
|
|
585
|
+
reason?: string;
|
|
586
|
+
};
|
|
587
|
+
interface ApproverInfo {
|
|
588
|
+
id: string;
|
|
589
|
+
email: string;
|
|
590
|
+
name?: string;
|
|
591
|
+
}
|
|
592
|
+
interface ApprovalRequestOptions<T = unknown> {
|
|
593
|
+
topic: ApprovalTopic;
|
|
594
|
+
payload: T;
|
|
595
|
+
title?: string;
|
|
596
|
+
description?: string;
|
|
597
|
+
/** Path on the agent the control plane will POST the decision to (resolved against the agent's public URL server-side). */
|
|
598
|
+
onDecide: string;
|
|
599
|
+
/** ISO 8601 duration (`'24h'`) or milliseconds. Default 24h. */
|
|
600
|
+
timeout?: string | number;
|
|
601
|
+
onTimeout?: 'reject' | 'approve' | 'ignore';
|
|
602
|
+
approver?: string;
|
|
603
|
+
/** Same `(topic, idempotencyKey)` returns the same `approvalId` instead of creating a new one. */
|
|
604
|
+
idempotencyKey?: string;
|
|
605
|
+
metadata?: Record<string, unknown>;
|
|
606
|
+
}
|
|
607
|
+
interface ApprovalRequest {
|
|
608
|
+
approvalId: string;
|
|
609
|
+
status: 'pending';
|
|
610
|
+
callbackUrl: string;
|
|
611
|
+
expiresAt: string;
|
|
612
|
+
}
|
|
613
|
+
interface ApprovalRecord<T = unknown> {
|
|
614
|
+
approvalId: string;
|
|
615
|
+
topic: ApprovalTopic;
|
|
616
|
+
status: ApprovalStatus;
|
|
617
|
+
payload: T;
|
|
618
|
+
decision?: Decision<T>;
|
|
619
|
+
createdAt: string;
|
|
620
|
+
expiresAt: string;
|
|
621
|
+
metadata?: Record<string, unknown>;
|
|
622
|
+
}
|
|
623
|
+
interface ApprovalListOptions {
|
|
624
|
+
status?: ApprovalStatus;
|
|
625
|
+
topic?: ApprovalTopic;
|
|
626
|
+
cursor?: string;
|
|
627
|
+
/** 1..100. Default 50. */
|
|
628
|
+
limit?: number;
|
|
629
|
+
}
|
|
630
|
+
interface ApprovalListResult<T = unknown> {
|
|
631
|
+
items: ApprovalRecord<T>[];
|
|
632
|
+
nextCursor?: string;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* `client.approval` — AGENT-LOCAL. The HITL pause is recorded directly in the
|
|
636
|
+
* agent's own `stackbone_platform.approvals` table over the shared
|
|
637
|
+
* `client.database` pool; there is no control-plane POST anymore. The RESUME
|
|
638
|
+
* half stays Studio-driven: a human decides in Studio, the control plane signs
|
|
639
|
+
* an HMAC callback to `callbackUrl`, and the agent reconciles it with
|
|
640
|
+
* `verify()` (local crypto — see `verify.ts`).
|
|
641
|
+
*
|
|
642
|
+
* The write populates only the columns the cloud `create()` used to set
|
|
643
|
+
* (topic/payload/callback_url/idempotency_key/fallback/metadata/timeout_at;
|
|
644
|
+
* `schema` was sourced from a DTO field the SDK options object does not carry,
|
|
645
|
+
* so it stays NULL). `workspace_id`/`agent_id`/`run_id` stay NULL exactly as
|
|
646
|
+
* they were on the control-plane path — the idempotency `ON CONFLICT` relies on
|
|
647
|
+
* `workspace_id` being NULL with `NULLS NOT DISTINCT` (migration 0008).
|
|
648
|
+
*/
|
|
649
|
+
declare class ApprovalFacade {
|
|
650
|
+
private readonly _resolved;
|
|
651
|
+
/** Lazy accessor for `client.database` — reaches the shared `Sql` via `.shared().$client`. */
|
|
652
|
+
private readonly _getDatabase;
|
|
653
|
+
constructor(_resolved: ResolvedConfig,
|
|
654
|
+
/** Lazy accessor for `client.database` — reaches the shared `Sql` via `.shared().$client`. */
|
|
655
|
+
_getDatabase: () => DatabaseModule);
|
|
656
|
+
request<T = unknown>(options: ApprovalRequestOptions<T>): Promise<Result<ApprovalRequest>>;
|
|
657
|
+
cancel(approvalId: string, reason?: string): Promise<Result<void>>;
|
|
658
|
+
get<T = unknown>(approvalId: string): Promise<Result<ApprovalRecord<T>>>;
|
|
659
|
+
list<T = unknown>(options?: ApprovalListOptions): Promise<Result<ApprovalListResult<T>>>;
|
|
660
|
+
/**
|
|
661
|
+
* Local crypto verification — does not touch the database, so it has the same
|
|
662
|
+
* shape it had on the control-plane surface. Auditing rule: a method gates
|
|
663
|
+
* iff it issues a datapath call; this one never does.
|
|
664
|
+
*/
|
|
665
|
+
verify<T = unknown>(request: Request, options?: VerifyOptions): Promise<Result<Decision<T>>>;
|
|
666
|
+
/**
|
|
667
|
+
* Pure factory — returns an `ApprovalTool` whose `invoke()` ultimately calls
|
|
668
|
+
* back into `ApprovalFacade.request`.
|
|
669
|
+
*/
|
|
670
|
+
tool<I, O>(spec: ApprovalToolSpec<I, O>): ApprovalTool<I, O>;
|
|
671
|
+
private sql;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
declare class ConfigFacade {
|
|
675
|
+
private readonly _resolved;
|
|
676
|
+
/** Lazy accessor for `client.database` — see `SecretsFacade`. */
|
|
677
|
+
private readonly _getDatabase;
|
|
678
|
+
constructor(_resolved: ResolvedConfig,
|
|
679
|
+
/** Lazy accessor for `client.database` — see `SecretsFacade`. */
|
|
680
|
+
_getDatabase: () => DatabaseModule);
|
|
681
|
+
get<T = unknown>(key: string): Promise<Result<T>>;
|
|
682
|
+
/**
|
|
683
|
+
* Keys absent from the agent's config come back as omissions in the
|
|
684
|
+
* response — the returned map only contains entries the agent DB actually
|
|
685
|
+
* has, so callers must access fields with `?.` (the return type is
|
|
686
|
+
* `Partial<T>`).
|
|
687
|
+
*/
|
|
688
|
+
getMany<T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]): Promise<Result<Partial<T>>>;
|
|
689
|
+
private loadPayload;
|
|
690
|
+
private sql;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* A prompt's current/active state. Key-based (workspace-unique). `template` is
|
|
695
|
+
* the current version's content; `version` is the live version number.
|
|
696
|
+
*/
|
|
697
|
+
interface Prompt {
|
|
698
|
+
key: string;
|
|
699
|
+
name: string;
|
|
700
|
+
description: string | null;
|
|
701
|
+
/** The current version's content. */
|
|
702
|
+
template: string;
|
|
703
|
+
/** The live version number. The first `create` produces version `1`. */
|
|
704
|
+
version: number;
|
|
705
|
+
/** `{{var}}` names referenced by the current version. */
|
|
706
|
+
variables: readonly string[];
|
|
707
|
+
metadata: Record<string, unknown> | null;
|
|
708
|
+
/** ISO 8601 UTC timestamp. */
|
|
709
|
+
createdAt: string;
|
|
710
|
+
updatedAt: string;
|
|
711
|
+
}
|
|
712
|
+
interface GetPromptOptions {
|
|
713
|
+
/** Pin a specific version. Omitted -> current/latest. */
|
|
714
|
+
version?: number;
|
|
715
|
+
}
|
|
716
|
+
interface ListPromptsOptions {
|
|
717
|
+
/** 1..100. Default 50. */
|
|
718
|
+
limit?: number;
|
|
719
|
+
}
|
|
720
|
+
interface ListPromptsResult {
|
|
721
|
+
/** Current version of each (non-deleted) prompt. */
|
|
722
|
+
items: readonly Prompt[];
|
|
723
|
+
}
|
|
724
|
+
interface CreatePromptRequest {
|
|
725
|
+
key: string;
|
|
726
|
+
name: string;
|
|
727
|
+
/** The version-1 content. */
|
|
728
|
+
template: string;
|
|
729
|
+
description?: string;
|
|
730
|
+
metadata?: Record<string, unknown>;
|
|
731
|
+
}
|
|
732
|
+
interface UpdatePromptOptions {
|
|
733
|
+
/** New content. Appends a new immutable version. */
|
|
734
|
+
template?: string;
|
|
735
|
+
name?: string;
|
|
736
|
+
description?: string | null;
|
|
737
|
+
/** Replaces the prompt-head metadata. */
|
|
738
|
+
metadata?: Record<string, unknown>;
|
|
739
|
+
}
|
|
740
|
+
interface DeletePromptOptions {
|
|
741
|
+
/** Reserved for future per-version deletes; ignored today (soft-deletes the key). */
|
|
742
|
+
version?: number;
|
|
743
|
+
}
|
|
744
|
+
interface DeletePromptResult {
|
|
745
|
+
key: string;
|
|
746
|
+
/** Number of prompt heads soft-deleted (0 or 1). */
|
|
747
|
+
deleted: number;
|
|
748
|
+
}
|
|
749
|
+
interface CompilePromptResult {
|
|
750
|
+
/** The rendered string. */
|
|
751
|
+
output: string;
|
|
752
|
+
/** The version the compile ran against. */
|
|
753
|
+
version: number;
|
|
754
|
+
}
|
|
755
|
+
declare class PromptsFacade {
|
|
756
|
+
private readonly _resolved;
|
|
757
|
+
/**
|
|
758
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
759
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
760
|
+
* reaching into a sibling module's implementation file.
|
|
761
|
+
*/
|
|
762
|
+
private readonly _getDatabase;
|
|
763
|
+
constructor(_resolved: ResolvedConfig,
|
|
764
|
+
/**
|
|
765
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
766
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
767
|
+
* reaching into a sibling module's implementation file.
|
|
768
|
+
*/
|
|
769
|
+
_getDatabase: () => DatabaseModule);
|
|
770
|
+
get(key: string, options?: GetPromptOptions): Promise<Result<Prompt>>;
|
|
771
|
+
compile(key: string, variables: Record<string, unknown>, options?: GetPromptOptions): Promise<Result<CompilePromptResult>>;
|
|
772
|
+
list(options?: ListPromptsOptions): Promise<Result<ListPromptsResult>>;
|
|
773
|
+
create(request: CreatePromptRequest): Promise<Result<Prompt>>;
|
|
774
|
+
update(key: string, options: UpdatePromptOptions): Promise<Result<Prompt>>;
|
|
775
|
+
delete(key: string, _options?: DeletePromptOptions): Promise<Result<DeletePromptResult>>;
|
|
776
|
+
private sql;
|
|
777
|
+
private mapReadError;
|
|
778
|
+
private mapWriteError;
|
|
779
|
+
}
|
|
780
|
+
|
|
470
781
|
/**
|
|
471
782
|
* Public surface behind `client.ai`. Wraps the official `openai` SDK with a
|
|
472
783
|
* `baseURL` override pointing to OpenRouter, so 300+ models are reachable
|
|
@@ -618,206 +929,47 @@ interface RequestOptions$1 {
|
|
|
618
929
|
signal?: AbortSignal;
|
|
619
930
|
}
|
|
620
931
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
type RagIngestProgress = {
|
|
632
|
-
type: 'started';
|
|
633
|
-
jobId: string;
|
|
634
|
-
totalChunks: number;
|
|
635
|
-
} | {
|
|
636
|
-
type: 'progress';
|
|
637
|
-
jobId: string;
|
|
638
|
-
processedChunks: number;
|
|
639
|
-
totalChunks: number;
|
|
640
|
-
currentDocument?: {
|
|
641
|
-
source: string;
|
|
642
|
-
ordinal: number;
|
|
643
|
-
};
|
|
644
|
-
} | {
|
|
645
|
-
type: 'completed';
|
|
646
|
-
jobId: string;
|
|
647
|
-
totalChunks: number;
|
|
648
|
-
} | {
|
|
649
|
-
type: 'failed';
|
|
650
|
-
jobId: string;
|
|
651
|
-
error: string;
|
|
652
|
-
};
|
|
653
|
-
interface IngestJobStartArgs {
|
|
654
|
-
collection: string;
|
|
655
|
-
source: string;
|
|
656
|
-
totalChunks: number;
|
|
657
|
-
}
|
|
658
|
-
interface IngestJobProgressArgs {
|
|
659
|
-
jobId: string;
|
|
660
|
-
processedChunks: number;
|
|
661
|
-
totalChunks: number;
|
|
662
|
-
currentDocument?: {
|
|
663
|
-
source: string;
|
|
664
|
-
ordinal: number;
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
interface IngestJobCompleteArgs {
|
|
668
|
-
jobId: string;
|
|
669
|
-
totalChunks: number;
|
|
670
|
-
}
|
|
671
|
-
interface IngestJobFailArgs {
|
|
932
|
+
/**
|
|
933
|
+
* Handle returned by `client.rag.ingestAsync`. The job id is allocated
|
|
934
|
+
* synchronously against `stackbone_rag_jobs` so the caller can track / cancel
|
|
935
|
+
* the work via the `/api/rag/jobs/:jobId/*` surface; `events` is a streaming
|
|
936
|
+
* channel of progress events (ADR §D9 shape); `result` settles when the
|
|
937
|
+
* pipeline finishes (or fails / is cancelled).
|
|
938
|
+
*/
|
|
939
|
+
interface IngestAsyncHandle {
|
|
672
940
|
jobId: string;
|
|
673
|
-
|
|
941
|
+
events: AsyncIterable<RagIngestProgress>;
|
|
942
|
+
result: Promise<Result<IngestResponse>>;
|
|
674
943
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
type ParseInput = Blob | string | Uint8Array | ArrayBuffer;
|
|
706
|
-
|
|
707
|
-
interface IngestChunk {
|
|
708
|
-
content: string;
|
|
709
|
-
embedding: number[];
|
|
710
|
-
}
|
|
711
|
-
interface IngestRequestBase {
|
|
712
|
-
/** Document identifier — re-ingesting with the same `id` replaces all its chunks atomically. */
|
|
713
|
-
id: string;
|
|
714
|
-
metadata?: Record<string, unknown>;
|
|
715
|
-
/** Logical namespace for separation. Default `'default'`. */
|
|
716
|
-
namespace?: string;
|
|
717
|
-
/**
|
|
718
|
-
* Canonical collection name used by the `stackbone_rag_jobs` writer (F07)
|
|
719
|
-
* to bind the row to a `stackbone_rag_collections` parent. Optional for
|
|
720
|
-
* back-compat with pre-F07 callers — when absent, no job row is written
|
|
721
|
-
* and progress is delivered only via `onProgress` (if any).
|
|
722
|
-
*/
|
|
723
|
-
collection?: string;
|
|
724
|
-
/**
|
|
725
|
-
* Optional sink for streaming progress events. Fired in addition to (and
|
|
726
|
-
* before) the matching `IngestJobWriter` call so synchronous `ingest`
|
|
727
|
-
* consumers can observe progress without paying the SQL writer cost.
|
|
728
|
-
*/
|
|
729
|
-
onProgress?: (event: RagIngestProgress) => void | Promise<void>;
|
|
730
|
-
}
|
|
731
|
-
interface IngestRequestPrecomputed extends IngestRequestBase {
|
|
732
|
-
chunks: IngestChunk[];
|
|
733
|
-
}
|
|
734
|
-
interface IngestRequestAutoEmbed extends IngestRequestBase {
|
|
735
|
-
chunks: string[];
|
|
736
|
-
/** Embedding model id. When set, the pipeline calls the configured Embedder. */
|
|
737
|
-
model: string;
|
|
738
|
-
/**
|
|
739
|
-
* Items per embeddings request. Consumed by the facade when it builds the
|
|
740
|
-
* `Embedder`; ignored by the pipeline itself (the embedder is fully
|
|
741
|
-
* configured at construction time).
|
|
742
|
-
*/
|
|
743
|
-
batchSize?: number;
|
|
744
|
-
}
|
|
745
|
-
type IngestRequest = IngestRequestPrecomputed | IngestRequestAutoEmbed;
|
|
746
|
-
interface IngestResponse {
|
|
747
|
-
id: string;
|
|
748
|
-
chunks: number;
|
|
749
|
-
}
|
|
750
|
-
interface DeleteOptions {
|
|
751
|
-
namespace?: string;
|
|
752
|
-
}
|
|
753
|
-
interface DeleteResponse {
|
|
754
|
-
deleted: number;
|
|
755
|
-
}
|
|
756
|
-
interface RetrieveRequestBase {
|
|
757
|
-
topK?: number;
|
|
758
|
-
filter?: Record<string, unknown>;
|
|
759
|
-
namespace?: string;
|
|
760
|
-
includeContent?: boolean;
|
|
761
|
-
includeMetadata?: boolean;
|
|
762
|
-
}
|
|
763
|
-
interface RetrieveRequestPrecomputed extends RetrieveRequestBase {
|
|
764
|
-
embedding: number[];
|
|
765
|
-
}
|
|
766
|
-
interface RetrieveRequestAutoEmbed extends RetrieveRequestBase {
|
|
767
|
-
text: string;
|
|
768
|
-
/** Must match the model used at ingest time. */
|
|
769
|
-
model: string;
|
|
770
|
-
}
|
|
771
|
-
type RetrieveRequest = RetrieveRequestPrecomputed | RetrieveRequestAutoEmbed;
|
|
772
|
-
interface RetrieveHit {
|
|
773
|
-
id: string;
|
|
774
|
-
chunkIdx: number;
|
|
775
|
-
content?: string;
|
|
776
|
-
metadata?: Record<string, unknown>;
|
|
777
|
-
score: number;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/**
|
|
781
|
-
* Handle returned by `client.rag.ingestAsync`. The job id is allocated
|
|
782
|
-
* synchronously against `stackbone_rag_jobs` so the caller can track / cancel
|
|
783
|
-
* the work via the `/api/rag/jobs/:jobId/*` surface; `events` is a streaming
|
|
784
|
-
* channel of progress events (ADR §D9 shape); `result` settles when the
|
|
785
|
-
* pipeline finishes (or fails / is cancelled).
|
|
786
|
-
*/
|
|
787
|
-
interface IngestAsyncHandle {
|
|
788
|
-
jobId: string;
|
|
789
|
-
events: AsyncIterable<RagIngestProgress>;
|
|
790
|
-
result: Promise<Result<IngestResponse>>;
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* `client.rag` — `pgvector`-backed retrieval. Two shapes per write/read:
|
|
794
|
-
* pass `model` to let the SDK embed for you; pass embeddings precomputed for
|
|
795
|
-
* provider/dimension/batching control.
|
|
796
|
-
*
|
|
797
|
-
* Implementation note: `RagModule` is a thin facade over `RagPipeline`. The
|
|
798
|
-
* pipeline owns parse → chunk → embed → persist; the facade resolves the
|
|
799
|
-
* shared `client.database` pool, builds an `Embedder` on demand, and maps
|
|
800
|
-
* configuration errors. See ADR `2026-05-10-rag-consolidation-on-client-database`.
|
|
801
|
-
*
|
|
802
|
-
* Connection ownership: this module never opens its own postgres pool. It
|
|
803
|
-
* reaches the underlying `postgres-js` `Sql` through the **shared-handles
|
|
804
|
-
* pattern** — `client.database.shared().$client` — so an agent that
|
|
805
|
-
* touches both surfaces still opens exactly one connection pool against
|
|
806
|
-
* `STACKBONE_POSTGRES_URL`. RAG is the canonical first cross-surface
|
|
807
|
-
* consumer; memory and queues will adopt the same accessor when they
|
|
808
|
-
* land.
|
|
809
|
-
*
|
|
810
|
-
* Schema readiness: starting with feature 30, the canonical schema is
|
|
811
|
-
* installed by the CLI (`stackbone db migrate add-rag` + `migrate up`). When
|
|
812
|
-
* an operation hits `42P01 relation does not exist`, the pipeline returns
|
|
813
|
-
* `SdkError('rag_schema_missing')` with an actionable hint.
|
|
814
|
-
*/
|
|
815
|
-
/**
|
|
816
|
-
* Test-only seams accepted by `RagModule`. Production callers leave these
|
|
817
|
-
* unset — they are injected by `ingest-async.spec.ts` to substitute the
|
|
818
|
-
* shared-pool lookup and the SQL-backed job writer.
|
|
819
|
-
*/
|
|
820
|
-
interface RagModuleTestDeps {
|
|
944
|
+
/**
|
|
945
|
+
* `client.rag` — `pgvector`-backed retrieval. Two shapes per write/read:
|
|
946
|
+
* pass `model` to let the SDK embed for you; pass embeddings precomputed for
|
|
947
|
+
* provider/dimension/batching control.
|
|
948
|
+
*
|
|
949
|
+
* Implementation note: `RagModule` is a thin facade over `RagPipeline`. The
|
|
950
|
+
* pipeline owns parse → chunk → embed → persist; the facade resolves the
|
|
951
|
+
* shared `client.database` pool, builds an `Embedder` on demand, and maps
|
|
952
|
+
* configuration errors. See ADR `2026-05-10-rag-consolidation-on-client-database`.
|
|
953
|
+
*
|
|
954
|
+
* Connection ownership: this module never opens its own postgres pool. It
|
|
955
|
+
* reaches the underlying `postgres-js` `Sql` through the **shared-handles
|
|
956
|
+
* pattern** — `client.database.shared().$client` — so an agent that
|
|
957
|
+
* touches both surfaces still opens exactly one connection pool against
|
|
958
|
+
* `STACKBONE_POSTGRES_URL`. RAG is the canonical first cross-surface
|
|
959
|
+
* consumer; memory and queues will adopt the same accessor when they
|
|
960
|
+
* land.
|
|
961
|
+
*
|
|
962
|
+
* Schema readiness: the canonical schema (`stackbone_platform.rag_*`) is
|
|
963
|
+
* provisioned by the platform migrator, present on every install. When an
|
|
964
|
+
* operation still hits `42P01 relation does not exist`, the pipeline returns
|
|
965
|
+
* `SdkError('rag_schema_missing')` with an actionable hint.
|
|
966
|
+
*/
|
|
967
|
+
/**
|
|
968
|
+
* Test-only seams accepted by `RagModule`. Production callers leave these
|
|
969
|
+
* unset — they are injected by `ingest-async.spec.ts` to substitute the
|
|
970
|
+
* shared-pool lookup and the SQL-backed job writer.
|
|
971
|
+
*/
|
|
972
|
+
interface RagModuleTestDeps {
|
|
821
973
|
/**
|
|
822
974
|
* Replaces the `client.database.shared().$client` lookup used by
|
|
823
975
|
* `ingestAsync`. Tests can plug a fake `Sql` here without standing up
|
|
@@ -884,10 +1036,11 @@ declare class RagModule {
|
|
|
884
1036
|
deleteWhere(filter: Record<string, unknown>, options?: DeleteOptions): Promise<Result<DeleteResponse>>;
|
|
885
1037
|
retrieve(request: RetrieveRequest): Promise<Result<RetrieveHit[]>>;
|
|
886
1038
|
/**
|
|
887
|
-
* Drops the legacy ad-hoc RAG
|
|
888
|
-
* pre-feature-30 agents
|
|
889
|
-
*
|
|
890
|
-
*
|
|
1039
|
+
* Drops the legacy ad-hoc RAG tables (`rag_chunks` / `_rag_meta`) that
|
|
1040
|
+
* pre-feature-30 agents provisioned on first ingest. Kept for backwards
|
|
1041
|
+
* compatibility so an old agent can clean those up after upgrading. The
|
|
1042
|
+
* canonical RAG schema (`stackbone_platform.rag_*`) is owned by the platform
|
|
1043
|
+
* migrator — this method never touches it.
|
|
891
1044
|
*/
|
|
892
1045
|
reset(): Promise<Result<void>>;
|
|
893
1046
|
/**
|
|
@@ -917,356 +1070,32 @@ declare class RagModule {
|
|
|
917
1070
|
private sql;
|
|
918
1071
|
private withPipeline;
|
|
919
1072
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
type SerializedBody = NonNullable<RequestInit['body']>;
|
|
923
|
-
/**
|
|
924
|
-
* Per-status overrides facades attach when an HTTP status carries a
|
|
925
|
-
* non-canonical meaning for the endpoint — e.g. a `POST` whose `409` means
|
|
926
|
-
* "already exists" instead of the default "conflict". Keys are HTTP statuses,
|
|
927
|
-
* values are domain codes the transport should surface verbatim (no prefix
|
|
928
|
-
* concatenation — the override is the final code).
|
|
929
|
-
*
|
|
930
|
-
* The value is typed against the catalog (`SdkErrorCode`) so an endpoint
|
|
931
|
-
* cannot smuggle a wire code that isn't in the documented inventory.
|
|
932
|
-
*/
|
|
933
|
-
type ErrorOverrides = Record<number, SdkErrorCode>;
|
|
934
|
-
/**
|
|
935
|
-
* Tells the transport how to remap HTTP status codes onto the surface's
|
|
936
|
-
* domain code prefix. `404 → '<prefix>_not_found'`, `401 → '<prefix>_unauthorized'`,
|
|
937
|
-
* etc. Endpoint-specific overrides win over the canonical mapping.
|
|
938
|
-
*
|
|
939
|
-
* `prefix` is constrained to `SdkErrorPrefix` (the catalog's prefix tuple) so
|
|
940
|
-
* an unknown surface name doesn't compile — keeps the wire codes the
|
|
941
|
-
* transport emits inside the typed inventory.
|
|
942
|
-
*/
|
|
943
|
-
interface ErrorMapping {
|
|
944
|
-
/** Prefix concatenated with `_not_found`, `_unauthorized`, …. Required for any domain remap. */
|
|
945
|
-
prefix: SdkErrorPrefix;
|
|
946
|
-
/** Per-status final code that overrides the canonical mapping. */
|
|
947
|
-
overrides?: ErrorOverrides;
|
|
948
|
-
}
|
|
949
|
-
interface RequestOptions extends Omit<RequestInit, 'body' | 'signal'> {
|
|
950
|
-
/** Scalar query params. `undefined` values are dropped, others get coerced via `String()`. */
|
|
951
|
-
params?: Record<string, string | number | boolean | undefined>;
|
|
952
|
-
/**
|
|
953
|
-
* Repeated string-array params validated and serialised by the transport.
|
|
954
|
-
* Empty arrays / blank elements reject upstream with `<prefix>_invalid_request`
|
|
955
|
-
* (falls back to `http_invalid_request` when no `errorMapping` is set). Each
|
|
956
|
-
* array is joined with `,` to match the canonical control-plane shape.
|
|
957
|
-
*/
|
|
958
|
-
arrayParams?: Record<string, readonly string[]>;
|
|
959
|
-
body?: SerializedBody | Record<string, unknown> | unknown[] | null;
|
|
960
|
-
signal?: AbortSignal;
|
|
961
|
-
/** Allow retrying non-idempotent requests (POST, PATCH). Off by default to prevent duplicate writes. */
|
|
962
|
-
idempotent?: boolean;
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Shape every facade method ideally collapses into: "POST to /path with this
|
|
966
|
-
* body, validate against this schema, surface errors as `<prefix>_*`". The
|
|
967
|
-
* transport handles validation, HTTP→domain remapping, querystring assembly
|
|
968
|
-
* and the `Result` envelope so facades stay tiny orchestrators.
|
|
969
|
-
*
|
|
970
|
-
* `arrayParams` keys default to `errorMapping.prefix` for the validation
|
|
971
|
-
* error code (e.g. an empty `names: []` becomes `secrets_invalid_request`).
|
|
972
|
-
*/
|
|
973
|
-
interface TransportRequest<T> extends RequestOptions {
|
|
974
|
-
method: string;
|
|
975
|
-
path: string;
|
|
976
|
-
/**
|
|
977
|
-
* Zod schema (or any object with a `.safeParse()` method) the parsed body
|
|
978
|
-
* must validate against. On failure the transport surfaces
|
|
979
|
-
* `<prefix>_invalid_response` (or `http_invalid_response` if no prefix is
|
|
980
|
-
* configured) with the schema issues attached to `meta.issues`.
|
|
981
|
-
*/
|
|
982
|
-
responseSchema?: ResponseSchema<T>;
|
|
983
|
-
/** HTTP→domain remapping. Omit to let `http_*` codes flow through unchanged. */
|
|
984
|
-
errorMapping?: ErrorMapping;
|
|
985
|
-
}
|
|
986
|
-
/**
|
|
987
|
-
* Minimal contract the transport needs from a schema — `safeParse`. Compatible
|
|
988
|
-
* with Zod (`z.object(…).safeParse(value)`) and any hand-rolled validator that
|
|
989
|
-
* follows the same shape. Avoids leaking Zod's full surface into the transport
|
|
990
|
-
* signature.
|
|
991
|
-
*/
|
|
992
|
-
interface ResponseSchema<T> {
|
|
993
|
-
safeParse(input: unknown): {
|
|
994
|
-
success: true;
|
|
995
|
-
data: T;
|
|
996
|
-
} | {
|
|
997
|
-
success: false;
|
|
998
|
-
error: {
|
|
999
|
-
issues: unknown;
|
|
1000
|
-
};
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
interface HttpClientOptions {
|
|
1004
|
-
/** Default 30_000. Set to 0 to disable. */
|
|
1005
|
-
timeout?: number;
|
|
1006
|
-
/** Default 3. Set to 0 to disable. */
|
|
1007
|
-
retryCount?: number;
|
|
1008
|
-
/** Initial backoff in ms; doubles each attempt with ±15% jitter. Default 500. */
|
|
1009
|
-
retryDelay?: number;
|
|
1010
|
-
/** Override the global fetch (useful for tests). */
|
|
1011
|
-
fetch?: typeof fetch;
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Shared HTTP client used by every facade that talks to the Stackbone
|
|
1015
|
-
* control plane. Wraps `fetch` with timeout, exponential backoff with jitter,
|
|
1016
|
-
* idempotent-only retries, response validation (Zod-compatible schemas) and
|
|
1017
|
-
* a uniform `Result` envelope so facades never throw at the SDK boundary.
|
|
1018
|
-
*
|
|
1019
|
-
* Each facade tells the transport its error prefix (`'secrets'`, `'config'`,
|
|
1020
|
-
* `'approval'`) so 404/401/403/429/5xx remap to surface-specific codes —
|
|
1021
|
-
* `secrets_not_found`, `approval_unauthorized`, etc. Endpoint-specific
|
|
1022
|
-
* remaps (e.g. `409 → 'secrets_already_exists'`) flow through
|
|
1023
|
-
* `errorMapping.overrides`. Transport-level failures (network, timeout,
|
|
1024
|
-
* parse) keep the `http_*` prefix the README contract documents.
|
|
1025
|
-
*
|
|
1026
|
-
* Reads `STACKBONE_API_URL` and `STACKBONE_AGENT_JWT` lazily on every request so
|
|
1027
|
-
* env-var rotation is picked up without restarting the client.
|
|
1028
|
-
*/
|
|
1029
|
-
declare class HttpClient {
|
|
1030
|
-
private readonly resolved;
|
|
1031
|
-
private readonly timeout;
|
|
1032
|
-
private readonly retryCount;
|
|
1033
|
-
private readonly retryDelay;
|
|
1034
|
-
private readonly fetchImpl;
|
|
1035
|
-
constructor(resolved: ResolvedConfig, options?: HttpClientOptions);
|
|
1036
|
-
/**
|
|
1037
|
-
* Convenience for `request({ method: 'GET', path, ... })`. Kept so legacy
|
|
1038
|
-
* callers (tests, pending facades) keep compiling — the deep work happens
|
|
1039
|
-
* in `request()`.
|
|
1040
|
-
*/
|
|
1041
|
-
get<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1042
|
-
post<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1043
|
-
put<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1044
|
-
patch<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1045
|
-
delete<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1046
|
-
/**
|
|
1047
|
-
* The transport entry point every facade should target. Handles base-URL
|
|
1048
|
-
* resolution, header injection, body/querystring serialisation, retry +
|
|
1049
|
-
* timeout, response validation and HTTP→domain error remapping.
|
|
1050
|
-
*/
|
|
1051
|
-
request<T>(req: TransportRequest<T>): Promise<Result<T>>;
|
|
1052
|
-
private computeBackoff;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
interface ApprovalToolSpec<I, O> {
|
|
1056
|
-
name: string;
|
|
1057
|
-
description: string;
|
|
1058
|
-
parameters: Record<string, unknown>;
|
|
1059
|
-
needsApproval?: boolean | ((input: I) => boolean | Promise<boolean>);
|
|
1060
|
-
/**
|
|
1061
|
-
* Maps the LLM input to the approval request fields. `topic` and `payload`
|
|
1062
|
-
* default to the tool name and the raw input respectively when omitted;
|
|
1063
|
-
* `onDecide` must always be supplied so the control plane knows where to
|
|
1064
|
-
* deliver the eventual decision.
|
|
1065
|
-
*/
|
|
1066
|
-
toRequest: (input: I) => Omit<ApprovalRequestOptions<I>, 'payload' | 'topic'> & Partial<Pick<ApprovalRequestOptions<I>, 'payload' | 'topic'>>;
|
|
1067
|
-
execute: (input: I) => Promise<O> | O;
|
|
1068
|
-
}
|
|
1069
|
-
interface OpenAIToolSpec {
|
|
1070
|
-
type: 'function';
|
|
1071
|
-
function: {
|
|
1072
|
-
name: string;
|
|
1073
|
-
description: string;
|
|
1074
|
-
parameters: Record<string, unknown>;
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
type ToolInvokeResult<O> = {
|
|
1078
|
-
status: 'pending';
|
|
1079
|
-
approvalId: string;
|
|
1080
|
-
expiresAt: string;
|
|
1081
|
-
} | {
|
|
1082
|
-
status: 'ok';
|
|
1083
|
-
result: O;
|
|
1084
|
-
};
|
|
1085
|
-
interface ApprovalTool<I, O> {
|
|
1086
|
-
name: string;
|
|
1087
|
-
description: string;
|
|
1088
|
-
parameters: Record<string, unknown>;
|
|
1089
|
-
openaiSpec(): OpenAIToolSpec;
|
|
1090
|
-
invoke(input: I): Promise<Result<ToolInvokeResult<O>>>;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
interface VerifyOptions {
|
|
1094
|
-
/** Override the signing key resolved from `STACKBONE_APPROVAL_SIGNING_KEY` / `approvalSigningKey`. */
|
|
1095
|
-
signingKey?: string;
|
|
1096
|
-
/** Reject signatures whose timestamp is older than this (seconds). Default 300. */
|
|
1097
|
-
toleranceSeconds?: number;
|
|
1098
|
-
/** Inject a clock for tests. Defaults to `Date.now`. */
|
|
1099
|
-
now?: () => number;
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
type ApprovalTopic = string;
|
|
1103
|
-
declare const DECISION_STATUSES: readonly ["approved", "rejected", "timed_out", "cancelled"];
|
|
1104
|
-
type DecisionStatus = (typeof DECISION_STATUSES)[number];
|
|
1105
|
-
type ApprovalStatus = 'pending' | DecisionStatus;
|
|
1106
|
-
type Decision<T = unknown> = {
|
|
1107
|
-
status: 'approved' | 'rejected';
|
|
1108
|
-
payload: T;
|
|
1109
|
-
approver: ApproverInfo;
|
|
1110
|
-
decidedAt: string;
|
|
1111
|
-
reason?: string;
|
|
1112
|
-
} | {
|
|
1113
|
-
status: 'timed_out';
|
|
1114
|
-
decidedAt: string;
|
|
1115
|
-
} | {
|
|
1116
|
-
status: 'cancelled';
|
|
1117
|
-
decidedAt: string;
|
|
1118
|
-
reason?: string;
|
|
1119
|
-
};
|
|
1120
|
-
interface ApproverInfo {
|
|
1121
|
-
id: string;
|
|
1122
|
-
email: string;
|
|
1123
|
-
name?: string;
|
|
1124
|
-
}
|
|
1125
|
-
interface ApprovalRequestOptions<T = unknown> {
|
|
1126
|
-
topic: ApprovalTopic;
|
|
1127
|
-
payload: T;
|
|
1128
|
-
title?: string;
|
|
1129
|
-
description?: string;
|
|
1130
|
-
/** Path on the agent the control plane will POST the decision to (resolved against the agent's public URL server-side). */
|
|
1131
|
-
onDecide: string;
|
|
1132
|
-
/** ISO 8601 duration (`'24h'`) or milliseconds. Default 24h. */
|
|
1133
|
-
timeout?: string | number;
|
|
1134
|
-
onTimeout?: 'reject' | 'approve' | 'ignore';
|
|
1135
|
-
approver?: string;
|
|
1136
|
-
/** Same `(topic, idempotencyKey)` returns the same `approvalId` instead of creating a new one. */
|
|
1137
|
-
idempotencyKey?: string;
|
|
1138
|
-
metadata?: Record<string, unknown>;
|
|
1139
|
-
}
|
|
1140
|
-
interface ApprovalRequest {
|
|
1141
|
-
approvalId: string;
|
|
1142
|
-
status: 'pending';
|
|
1143
|
-
callbackUrl: string;
|
|
1144
|
-
expiresAt: string;
|
|
1145
|
-
}
|
|
1146
|
-
interface ApprovalRecord<T = unknown> {
|
|
1147
|
-
approvalId: string;
|
|
1148
|
-
topic: ApprovalTopic;
|
|
1149
|
-
status: ApprovalStatus;
|
|
1150
|
-
payload: T;
|
|
1151
|
-
decision?: Decision<T>;
|
|
1152
|
-
createdAt: string;
|
|
1153
|
-
expiresAt: string;
|
|
1154
|
-
metadata?: Record<string, unknown>;
|
|
1155
|
-
}
|
|
1156
|
-
interface ApprovalListOptions {
|
|
1157
|
-
status?: ApprovalStatus;
|
|
1158
|
-
topic?: ApprovalTopic;
|
|
1159
|
-
cursor?: string;
|
|
1160
|
-
/** 1..100. Default 50. */
|
|
1161
|
-
limit?: number;
|
|
1162
|
-
}
|
|
1163
|
-
interface ApprovalListResult<T = unknown> {
|
|
1164
|
-
items: ApprovalRecord<T>[];
|
|
1165
|
-
nextCursor?: string;
|
|
1166
|
-
}
|
|
1167
|
-
declare class ApprovalFacade {
|
|
1168
|
-
private readonly _resolved;
|
|
1169
|
-
private readonly _http;
|
|
1170
|
-
private readonly _gate;
|
|
1171
|
-
constructor(_resolved: ResolvedConfig, _http: HttpClient,
|
|
1172
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1173
|
-
gate?: ModuleGate);
|
|
1174
|
-
request<T = unknown>(options: ApprovalRequestOptions<T>): Promise<Result<ApprovalRequest>>;
|
|
1175
|
-
cancel(approvalId: string, reason?: string): Promise<Result<void>>;
|
|
1176
|
-
get<T = unknown>(approvalId: string): Promise<Result<ApprovalRecord<T>>>;
|
|
1177
|
-
list<T = unknown>(options?: ApprovalListOptions): Promise<Result<ApprovalListResult<T>>>;
|
|
1178
|
-
/**
|
|
1179
|
-
* Local crypto verification — does not touch the datapath, so it is NOT
|
|
1180
|
-
* gated by the contract handshake. Auditing rule: a method gates iff it
|
|
1181
|
-
* issues an HTTP request to the configured baseUrl.
|
|
1182
|
-
*/
|
|
1183
|
-
verify<T = unknown>(request: Request, options?: VerifyOptions): Promise<Result<Decision<T>>>;
|
|
1184
|
-
/**
|
|
1185
|
-
* Pure factory — returns an `ApprovalTool` whose `invoke()` ultimately
|
|
1186
|
-
* calls back into `ApprovalFacade.request`, so the gate fires there. Not
|
|
1187
|
-
* gated here.
|
|
1188
|
-
*/
|
|
1189
|
-
tool<I, O>(spec: ApprovalToolSpec<I, O>): ApprovalTool<I, O>;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
declare class ConfigFacade {
|
|
1193
|
-
private readonly _http;
|
|
1194
|
-
private readonly _gate;
|
|
1195
|
-
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1196
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1197
|
-
gate?: ModuleGate);
|
|
1198
|
-
get<T = unknown>(key: string): Promise<Result<T>>;
|
|
1199
|
-
/**
|
|
1200
|
-
* Keys absent from the agent's config come back as omissions in the
|
|
1201
|
-
* response — the returned map only contains entries the control plane
|
|
1202
|
-
* actually has, so callers must access fields with `?.` (the return type
|
|
1203
|
-
* is `Partial<T>`).
|
|
1204
|
-
*/
|
|
1205
|
-
getMany<T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]): Promise<Result<Partial<T>>>;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
declare class SecretsFacade {
|
|
1209
|
-
private readonly _http;
|
|
1210
|
-
private readonly _gate;
|
|
1211
|
-
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1212
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1213
|
-
gate?: ModuleGate);
|
|
1214
|
-
get(name: string): Promise<Result<string>>;
|
|
1215
|
-
/**
|
|
1216
|
-
* Names absent from the organization come back as omissions in the response —
|
|
1217
|
-
* the returned map only contains entries the control plane actually has.
|
|
1218
|
-
* Callers that need "all-or-nothing" semantics should diff the keys.
|
|
1219
|
-
*/
|
|
1220
|
-
getMany(names: string[]): Promise<Result<Record<string, string>>>;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
/**
|
|
1224
|
-
* `client.observability` — span processor + per-run JSONL/OTLP logger +
|
|
1225
|
-
* run-cost rollup. Local mode writes JSONL to `~/.stackbone/dev/runs/`; cloud
|
|
1226
|
-
* mode forwards OTLP/HTTP to the collector.
|
|
1227
|
-
*
|
|
1228
|
-
* Contract gating: not gated. Local writes touch the filesystem (no
|
|
1229
|
-
* Stackbone wire surface) and cloud writes go to OpenTelemetry, not the
|
|
1230
|
-
* Stackbone Agent Protocol — so there is no `Capability` to assert against.
|
|
1231
|
-
* No entry in `MODULE_CAPABILITIES` per the rule documented in
|
|
1232
|
-
* `contract/capability-registry.ts`.
|
|
1233
|
-
*/
|
|
1234
|
-
declare class ObservabilityModule {
|
|
1073
|
+
|
|
1074
|
+
declare class SecretsFacade {
|
|
1235
1075
|
private readonly _resolved;
|
|
1236
|
-
private _processor?;
|
|
1237
|
-
private readonly _loggers;
|
|
1238
|
-
private _sql;
|
|
1239
|
-
private _sqlResolved;
|
|
1240
|
-
constructor(_resolved: ResolvedConfig);
|
|
1241
1076
|
/**
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
* Platform Ops.
|
|
1077
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
1078
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
1079
|
+
* reaching into a sibling module's implementation file.
|
|
1246
1080
|
*/
|
|
1247
|
-
|
|
1248
|
-
|
|
1081
|
+
private readonly _getDatabase;
|
|
1082
|
+
private _cipher?;
|
|
1083
|
+
constructor(_resolved: ResolvedConfig,
|
|
1249
1084
|
/**
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
1253
|
-
* file handle / buffered timer stays stable across repeated calls inside
|
|
1254
|
-
* the same run.
|
|
1085
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
1086
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
1087
|
+
* reaching into a sibling module's implementation file.
|
|
1255
1088
|
*/
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
} & Partial<PlatformLoggerOptions>): PlatformLogger;
|
|
1259
|
-
/** Closes the per-run logger (flushes the JSONL file or the OTLP buffer). */
|
|
1260
|
-
closeLogger(runId: string): Promise<void>;
|
|
1089
|
+
_getDatabase: () => DatabaseModule);
|
|
1090
|
+
get(name: string): Promise<Result<string>>;
|
|
1261
1091
|
/**
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
* Result envelope so the agent's run cleanup never aborts on a flaky
|
|
1266
|
-
* Postgres call — the cost is decorative metadata, not load-bearing.
|
|
1092
|
+
* Names absent from the agent come back as omissions in the response — the
|
|
1093
|
+
* returned map only contains entries the agent DB actually has. Callers that
|
|
1094
|
+
* need "all-or-nothing" semantics should diff the keys.
|
|
1267
1095
|
*/
|
|
1268
|
-
|
|
1269
|
-
private
|
|
1096
|
+
getMany(names: string[]): Promise<Result<Record<string, string>>>;
|
|
1097
|
+
private cipher;
|
|
1098
|
+
private sql;
|
|
1270
1099
|
}
|
|
1271
1100
|
|
|
1272
1101
|
interface StorageObject {
|
|
@@ -1363,28 +1192,190 @@ declare class StorageBucket {
|
|
|
1363
1192
|
private resolve;
|
|
1364
1193
|
}
|
|
1365
1194
|
|
|
1195
|
+
/** Subset of `RequestInit['body']` we serialize without modification. */
|
|
1196
|
+
type SerializedBody = NonNullable<RequestInit['body']>;
|
|
1197
|
+
/**
|
|
1198
|
+
* Per-status overrides facades attach when an HTTP status carries a
|
|
1199
|
+
* non-canonical meaning for the endpoint — e.g. a `POST` whose `409` means
|
|
1200
|
+
* "already exists" instead of the default "conflict". Keys are HTTP statuses,
|
|
1201
|
+
* values are domain codes the transport should surface verbatim (no prefix
|
|
1202
|
+
* concatenation — the override is the final code).
|
|
1203
|
+
*
|
|
1204
|
+
* The value is typed against the catalog (`SdkErrorCode`) so an endpoint
|
|
1205
|
+
* cannot smuggle a wire code that isn't in the documented inventory.
|
|
1206
|
+
*/
|
|
1207
|
+
type ErrorOverrides = Record<number, SdkErrorCode>;
|
|
1208
|
+
/**
|
|
1209
|
+
* Tells the transport how to remap HTTP status codes onto the surface's
|
|
1210
|
+
* domain code prefix. `404 → '<prefix>_not_found'`, `401 → '<prefix>_unauthorized'`,
|
|
1211
|
+
* etc. Endpoint-specific overrides win over the canonical mapping.
|
|
1212
|
+
*
|
|
1213
|
+
* `prefix` is constrained to `SdkErrorPrefix` (the catalog's prefix tuple) so
|
|
1214
|
+
* an unknown surface name doesn't compile — keeps the wire codes the
|
|
1215
|
+
* transport emits inside the typed inventory.
|
|
1216
|
+
*/
|
|
1217
|
+
interface ErrorMapping {
|
|
1218
|
+
/** Prefix concatenated with `_not_found`, `_unauthorized`, …. Required for any domain remap. */
|
|
1219
|
+
prefix: SdkErrorPrefix;
|
|
1220
|
+
/** Per-status final code that overrides the canonical mapping. */
|
|
1221
|
+
overrides?: ErrorOverrides;
|
|
1222
|
+
}
|
|
1223
|
+
interface RequestOptions extends Omit<RequestInit, 'body' | 'signal'> {
|
|
1224
|
+
/** Scalar query params. `undefined` values are dropped, others get coerced via `String()`. */
|
|
1225
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
1226
|
+
/**
|
|
1227
|
+
* Repeated string-array params validated and serialised by the transport.
|
|
1228
|
+
* Empty arrays / blank elements reject upstream with `<prefix>_invalid_request`
|
|
1229
|
+
* (falls back to `http_invalid_request` when no `errorMapping` is set). Each
|
|
1230
|
+
* array is joined with `,` to match the canonical control-plane shape.
|
|
1231
|
+
*/
|
|
1232
|
+
arrayParams?: Record<string, readonly string[]>;
|
|
1233
|
+
body?: SerializedBody | Record<string, unknown> | unknown[] | null;
|
|
1234
|
+
signal?: AbortSignal;
|
|
1235
|
+
/** Allow retrying non-idempotent requests (POST, PATCH). Off by default to prevent duplicate writes. */
|
|
1236
|
+
idempotent?: boolean;
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Shape every facade method ideally collapses into: "POST to /path with this
|
|
1240
|
+
* body, validate against this schema, surface errors as `<prefix>_*`". The
|
|
1241
|
+
* transport handles validation, HTTP→domain remapping, querystring assembly
|
|
1242
|
+
* and the `Result` envelope so facades stay tiny orchestrators.
|
|
1243
|
+
*
|
|
1244
|
+
* `arrayParams` keys default to `errorMapping.prefix` for the validation
|
|
1245
|
+
* error code (e.g. an empty `names: []` becomes `secrets_invalid_request`).
|
|
1246
|
+
*/
|
|
1247
|
+
interface TransportRequest<T> extends RequestOptions {
|
|
1248
|
+
method: string;
|
|
1249
|
+
path: string;
|
|
1250
|
+
/**
|
|
1251
|
+
* Zod schema (or any object with a `.safeParse()` method) the parsed body
|
|
1252
|
+
* must validate against. On failure the transport surfaces
|
|
1253
|
+
* `<prefix>_invalid_response` (or `http_invalid_response` if no prefix is
|
|
1254
|
+
* configured) with the schema issues attached to `meta.issues`.
|
|
1255
|
+
*/
|
|
1256
|
+
responseSchema?: ResponseSchema<T>;
|
|
1257
|
+
/** HTTP→domain remapping. Omit to let `http_*` codes flow through unchanged. */
|
|
1258
|
+
errorMapping?: ErrorMapping;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Minimal contract the transport needs from a schema — `safeParse`. Compatible
|
|
1262
|
+
* with Zod (`z.object(…).safeParse(value)`) and any hand-rolled validator that
|
|
1263
|
+
* follows the same shape. Avoids leaking Zod's full surface into the transport
|
|
1264
|
+
* signature.
|
|
1265
|
+
*/
|
|
1266
|
+
interface ResponseSchema<T> {
|
|
1267
|
+
safeParse(input: unknown): {
|
|
1268
|
+
success: true;
|
|
1269
|
+
data: T;
|
|
1270
|
+
} | {
|
|
1271
|
+
success: false;
|
|
1272
|
+
error: {
|
|
1273
|
+
issues: unknown;
|
|
1274
|
+
};
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
interface HttpClientOptions {
|
|
1278
|
+
/** Default 30_000. Set to 0 to disable. */
|
|
1279
|
+
timeout?: number;
|
|
1280
|
+
/** Default 3. Set to 0 to disable. */
|
|
1281
|
+
retryCount?: number;
|
|
1282
|
+
/** Initial backoff in ms; doubles each attempt with ±15% jitter. Default 500. */
|
|
1283
|
+
retryDelay?: number;
|
|
1284
|
+
/** Override the global fetch (useful for tests). */
|
|
1285
|
+
fetch?: typeof fetch;
|
|
1286
|
+
}
|
|
1366
1287
|
/**
|
|
1367
|
-
*
|
|
1288
|
+
* Shared HTTP client used by every facade that talks to the Stackbone
|
|
1289
|
+
* control plane. Wraps `fetch` with timeout, exponential backoff with jitter,
|
|
1290
|
+
* idempotent-only retries, response validation (Zod-compatible schemas) and
|
|
1291
|
+
* a uniform `Result` envelope so facades never throw at the SDK boundary.
|
|
1368
1292
|
*
|
|
1369
|
-
*
|
|
1370
|
-
*
|
|
1371
|
-
* `
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1293
|
+
* Each facade tells the transport its error prefix (`'secrets'`, `'config'`,
|
|
1294
|
+
* `'approval'`) so 404/401/403/429/5xx remap to surface-specific codes —
|
|
1295
|
+
* `secrets_not_found`, `approval_unauthorized`, etc. Endpoint-specific
|
|
1296
|
+
* remaps (e.g. `409 → 'secrets_already_exists'`) flow through
|
|
1297
|
+
* `errorMapping.overrides`. Transport-level failures (network, timeout,
|
|
1298
|
+
* parse) keep the `http_*` prefix the README contract documents.
|
|
1299
|
+
*
|
|
1300
|
+
* Reads `STACKBONE_API_URL` and `STACKBONE_AGENT_JWT` lazily on every request so
|
|
1301
|
+
* env-var rotation is picked up without restarting the client.
|
|
1374
1302
|
*/
|
|
1375
|
-
declare class
|
|
1376
|
-
|
|
1377
|
-
|
|
1303
|
+
declare class HttpClient {
|
|
1304
|
+
private readonly resolved;
|
|
1305
|
+
private readonly timeout;
|
|
1306
|
+
private readonly retryCount;
|
|
1307
|
+
private readonly retryDelay;
|
|
1308
|
+
private readonly fetchImpl;
|
|
1309
|
+
constructor(resolved: ResolvedConfig, options?: HttpClientOptions);
|
|
1310
|
+
/**
|
|
1311
|
+
* Convenience for `request({ method: 'GET', path, ... })`. Kept so legacy
|
|
1312
|
+
* callers (tests, pending facades) keep compiling — the deep work happens
|
|
1313
|
+
* in `request()`.
|
|
1314
|
+
*/
|
|
1315
|
+
get<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1316
|
+
post<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1317
|
+
put<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1318
|
+
patch<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1319
|
+
delete<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1320
|
+
/**
|
|
1321
|
+
* The transport entry point every facade should target. Handles base-URL
|
|
1322
|
+
* resolution, header injection, body/querystring serialisation, retry +
|
|
1323
|
+
* timeout, response validation and HTTP→domain error remapping.
|
|
1324
|
+
*/
|
|
1325
|
+
request<T>(req: TransportRequest<T>): Promise<Result<T>>;
|
|
1326
|
+
private computeBackoff;
|
|
1378
1327
|
}
|
|
1379
1328
|
|
|
1380
|
-
/**
|
|
1381
|
-
|
|
1329
|
+
/** Options for `invoke`. */
|
|
1330
|
+
interface InvokeOptions {
|
|
1331
|
+
/**
|
|
1332
|
+
* Selector to disambiguate when the workspace holds several connections of the
|
|
1333
|
+
* same connector. Accepts a connection **id** or a unique **name** (both are
|
|
1334
|
+
* returned by `list()`). Omitted → the proxy resolves the sole connection, or
|
|
1335
|
+
* fails `connections_ambiguous` when there is more than one.
|
|
1336
|
+
*/
|
|
1337
|
+
connection?: string;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* `client.connections` — the agent's handle on the workspace's connector
|
|
1341
|
+
* connections. The credentials never enter the agent container: each method
|
|
1342
|
+
* makes an authenticated call to the control plane (the connector executor owns
|
|
1343
|
+
* the resolved credential, in a platform process).
|
|
1344
|
+
*
|
|
1345
|
+
* - `list` returns the workspace's connections (connector id, label, auth kind,
|
|
1346
|
+
* health) — never any credential material — so the agent can adapt to what the
|
|
1347
|
+
* customer has connected.
|
|
1348
|
+
* - `invoke(connector, action, args)` runs a connector action imperatively. The
|
|
1349
|
+
* control plane resolves the workspace's connection for `connector`, validates
|
|
1350
|
+
* `args` against the action's input schema, executes it, and returns the
|
|
1351
|
+
* validated output verbatim. `args` is opaque — the SDK forwards it untouched.
|
|
1352
|
+
*
|
|
1353
|
+
* Authentication reuses the existing `STACKBONE_AGENT_JWT` channel injected at
|
|
1354
|
+
* provisioning (the `HttpClient` attaches the bearer + install header), so the
|
|
1355
|
+
* control plane scopes every call to the agent's own install → organization.
|
|
1356
|
+
*
|
|
1357
|
+
* Contract gating: every method awaits the `connections.actions` module gate
|
|
1358
|
+
* before touching the network, so a stale datapath / missing capability surfaces
|
|
1359
|
+
* as a gating error rather than a failed request.
|
|
1360
|
+
*/
|
|
1361
|
+
declare class ConnectionsModule {
|
|
1382
1362
|
private readonly _http;
|
|
1383
1363
|
private readonly _gate;
|
|
1384
1364
|
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1385
1365
|
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1386
1366
|
gate?: ModuleGate);
|
|
1387
|
-
|
|
1367
|
+
/** List the workspace's connections (no credentials). */
|
|
1368
|
+
list(): Promise<Result<AgentConnectionListResponse>>;
|
|
1369
|
+
/**
|
|
1370
|
+
* Invoke a connector action against the workspace's connection for that
|
|
1371
|
+
* connector. `args` is forwarded opaquely; the control plane validates it
|
|
1372
|
+
* against the action's own input schema and returns the validated output.
|
|
1373
|
+
*
|
|
1374
|
+
* When the workspace holds several connections of the same connector, pass
|
|
1375
|
+
* `{ connection }` (a connection id or unique name) to pick one; omitting it
|
|
1376
|
+
* with more than one connection fails `connections_ambiguous`.
|
|
1377
|
+
*/
|
|
1378
|
+
invoke(connector: string, action: string, args: unknown, opts?: InvokeOptions): Promise<Result<InvokeConnectorActionResponse>>;
|
|
1388
1379
|
}
|
|
1389
1380
|
|
|
1390
1381
|
/**
|
|
@@ -1506,95 +1497,66 @@ declare class MemoryModule {
|
|
|
1506
1497
|
}
|
|
1507
1498
|
|
|
1508
1499
|
/**
|
|
1509
|
-
*
|
|
1510
|
-
*
|
|
1500
|
+
* Enqueue a one-shot (or deferred) job. The opaque `payload` is whatever the
|
|
1501
|
+
* agent's `receive` handler expects — the core never inspects it.
|
|
1511
1502
|
*/
|
|
1512
|
-
interface
|
|
1503
|
+
interface PublishRequest {
|
|
1504
|
+
/** Opaque job name (e.g. `send-email`). Surfaced in the Studio inspector. */
|
|
1513
1505
|
name: string;
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
/**
|
|
1521
|
-
|
|
1522
|
-
updatedAt: string;
|
|
1523
|
-
}
|
|
1524
|
-
interface GetPromptOptions {
|
|
1525
|
-
/** Pin a specific version. Omitted -> latest. */
|
|
1526
|
-
version?: number;
|
|
1527
|
-
}
|
|
1528
|
-
interface ListPromptsOptions {
|
|
1529
|
-
/** 1..100. Default 50. */
|
|
1530
|
-
limit?: number;
|
|
1531
|
-
cursor?: string;
|
|
1532
|
-
}
|
|
1533
|
-
interface ListPromptsResult {
|
|
1534
|
-
/** Latest version of each prompt. */
|
|
1535
|
-
items: readonly Prompt[];
|
|
1536
|
-
nextCursor?: string;
|
|
1506
|
+
/** Opaque job payload — JSON-serialisable; the core forwards it untouched. */
|
|
1507
|
+
payload: unknown;
|
|
1508
|
+
/** Per-job override of the platform retry default. Omitted → platform default. */
|
|
1509
|
+
retries?: number;
|
|
1510
|
+
/** Deferred delivery in milliseconds. Omitted / 0 → immediate. */
|
|
1511
|
+
delay?: number;
|
|
1512
|
+
/** Idempotency hint — a duplicate publish with the same key collapses to one job. */
|
|
1513
|
+
deduplicationId?: string;
|
|
1537
1514
|
}
|
|
1538
|
-
|
|
1515
|
+
/** Register / update a dynamic per-installation cron schedule. */
|
|
1516
|
+
interface ScheduleRequest {
|
|
1517
|
+
/** Schedule name, unique per install. Re-scheduling the same name updates it in place. */
|
|
1539
1518
|
name: string;
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
/**
|
|
1545
|
-
|
|
1546
|
-
/**
|
|
1547
|
-
|
|
1548
|
-
}
|
|
1549
|
-
interface DeletePromptOptions {
|
|
1550
|
-
/** Delete a specific version. Omitted -> delete every version under `name`. */
|
|
1551
|
-
version?: number;
|
|
1519
|
+
/** Opaque payload delivered on every fire. */
|
|
1520
|
+
payload: unknown;
|
|
1521
|
+
/** Cron expression evaluated by the dispatcher. */
|
|
1522
|
+
cron: string;
|
|
1523
|
+
/** IANA timezone the cron pattern runs in. Defaults to UTC. */
|
|
1524
|
+
tz?: string;
|
|
1525
|
+
/** Per-job override of the platform retry default. */
|
|
1526
|
+
retries?: number;
|
|
1552
1527
|
}
|
|
1553
|
-
|
|
1528
|
+
/** Cancel a previously registered schedule by name. */
|
|
1529
|
+
interface UnscheduleRequest {
|
|
1554
1530
|
name: string;
|
|
1555
|
-
/** Number of versions actually removed. */
|
|
1556
|
-
deleted: number;
|
|
1557
1531
|
}
|
|
1558
1532
|
/**
|
|
1559
|
-
* `client.
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
* returns `not_implemented`. The real backend will be the Stackbone control
|
|
1563
|
-
* plane and the public surface defined here is the contract callers can
|
|
1564
|
-
* already type against.
|
|
1533
|
+
* `client.queues` — the agent's handle on the BullMQ-backed job system. The
|
|
1534
|
+
* agent never touches Redis: each method makes an authenticated call to the
|
|
1535
|
+
* control plane (the dispatcher owns the queue + the clock).
|
|
1565
1536
|
*
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1537
|
+
* - `publish` enqueues a one-shot job (optionally deferred via `delay`, with a
|
|
1538
|
+
* per-job `retries` override and an optional `deduplicationId`).
|
|
1539
|
+
* - `schedule` / `unschedule` manage dynamic per-user cron schedules.
|
|
1540
|
+
* - `listSchedules` lists the install's active schedules.
|
|
1541
|
+
*
|
|
1542
|
+
* Authentication reuses the existing `STACKBONE_AGENT_JWT` channel injected at
|
|
1543
|
+
* provisioning (the `HttpClient` attaches the bearer + install header), so the
|
|
1544
|
+
* core scopes every enqueue to the agent's own install.
|
|
1545
|
+
*
|
|
1546
|
+
* Contract gating: every method awaits the `queues.jobs` module gate before
|
|
1547
|
+
* touching the network, so a stale datapath / missing capability surfaces as
|
|
1548
|
+
* a gating error rather than a failed POST.
|
|
1571
1549
|
*/
|
|
1572
|
-
declare class PromptsFacade {
|
|
1573
|
-
constructor(_resolved: ResolvedConfig, _http: HttpClient);
|
|
1574
|
-
get(_name: string, _options?: GetPromptOptions): Promise<Result<Prompt>>;
|
|
1575
|
-
compile(_name: string, _variables: Record<string, unknown>, _options?: GetPromptOptions): Promise<Result<string>>;
|
|
1576
|
-
list(_options?: ListPromptsOptions): Promise<Result<ListPromptsResult>>;
|
|
1577
|
-
create(_request: CreatePromptRequest): Promise<Result<Prompt>>;
|
|
1578
|
-
update(_name: string, _options: UpdatePromptOptions): Promise<Result<Prompt>>;
|
|
1579
|
-
delete(_name: string, _options?: DeletePromptOptions): Promise<Result<DeletePromptResult>>;
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
interface PublishRequest {
|
|
1583
|
-
url: string;
|
|
1584
|
-
body: unknown;
|
|
1585
|
-
retries?: number;
|
|
1586
|
-
delay?: number;
|
|
1587
|
-
deduplicationId?: string;
|
|
1588
|
-
headers?: Record<string, string>;
|
|
1589
|
-
}
|
|
1590
1550
|
declare class QueuesModule {
|
|
1551
|
+
private readonly _http;
|
|
1591
1552
|
private readonly _gate;
|
|
1592
|
-
constructor(resolved: ResolvedConfig,
|
|
1553
|
+
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1593
1554
|
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1594
1555
|
gate?: ModuleGate);
|
|
1595
|
-
publish(
|
|
1596
|
-
|
|
1597
|
-
|
|
1556
|
+
publish(request: PublishRequest): Promise<Result<PublishJobResponse>>;
|
|
1557
|
+
schedule(request: ScheduleRequest): Promise<Result<ScheduleJobResponse>>;
|
|
1558
|
+
unschedule(request: UnscheduleRequest): Promise<Result<UnscheduleJobResponse>>;
|
|
1559
|
+
listSchedules(): Promise<Result<ListSchedulesResponse>>;
|
|
1598
1560
|
}
|
|
1599
1561
|
|
|
1600
1562
|
/**
|
|
@@ -1603,18 +1565,25 @@ declare class QueuesModule {
|
|
|
1603
1565
|
* groups each surface by where its state lives:
|
|
1604
1566
|
*
|
|
1605
1567
|
* - `surfaces/agent-local/` — Postgres branch owned by the agent
|
|
1606
|
-
* (`database`, `rag`).
|
|
1568
|
+
* (`database`, `rag`, `secrets`, `config`, `approval`). `secrets`,
|
|
1569
|
+
* `config` and `approval` moved here from the control plane: the agent now
|
|
1570
|
+
* reads its secrets/config and records HITL pauses directly against its own
|
|
1571
|
+
* `stackbone_platform.*` tables (decrypting secrets with the per-agent key),
|
|
1572
|
+
* with no control-plane round-trip. The approval RESUME callback stays
|
|
1573
|
+
* Studio-signed and is reconciled locally by `approval.verify()`.
|
|
1607
1574
|
* - `surfaces/external/` — managed partner SDKs the SDK wraps directly
|
|
1608
|
-
* (`ai` / OpenRouter, `storage` / S3 + R2 / MinIO
|
|
1609
|
-
*
|
|
1575
|
+
* (`ai` / OpenRouter, `storage` / S3 + R2 / MinIO). Observability is not a
|
|
1576
|
+
* creator surface: agent code observes runs through the handler-scoped
|
|
1577
|
+
* `ctx.logger` (and plain `console.*`, which the wrapper captures and
|
|
1578
|
+
* correlates), while the platform primitives live in the
|
|
1579
|
+
* `@stackbone/sdk/observability` subpath the runtime wires directly.
|
|
1610
1580
|
* - `surfaces/control-plane/` — thin HTTP calls to Stackbone's control
|
|
1611
|
-
* plane (
|
|
1581
|
+
* plane (none today after secrets/config/approval moved agent-local).
|
|
1612
1582
|
* - `surfaces/pending/` — public surface is stable but the runtime is
|
|
1613
1583
|
* not built yet. They are wired as live `client.X` accessors so MVP
|
|
1614
1584
|
* agent code can be authored against the eventual contract today; each
|
|
1615
1585
|
* method returns `Result<{ error: { code: "not_implemented" } }>` until
|
|
1616
|
-
* the backing runtime ships. Today: queues, memory
|
|
1617
|
-
* connections, events.
|
|
1586
|
+
* the backing runtime ships. Today: queues, memory.
|
|
1618
1587
|
*
|
|
1619
1588
|
* Each accessor builds its surface on first access and caches it — env
|
|
1620
1589
|
* vars and partner SDK init costs are paid only when the agent actually
|
|
@@ -1632,15 +1601,13 @@ declare class StackboneClient {
|
|
|
1632
1601
|
private _storage?;
|
|
1633
1602
|
private _ai?;
|
|
1634
1603
|
private _rag?;
|
|
1635
|
-
private _observability?;
|
|
1636
1604
|
private _approval?;
|
|
1637
1605
|
private _secrets?;
|
|
1638
1606
|
private _config?;
|
|
1639
1607
|
private _queues?;
|
|
1608
|
+
private _connections?;
|
|
1640
1609
|
private _memory?;
|
|
1641
|
-
private _events?;
|
|
1642
1610
|
private _prompts?;
|
|
1643
|
-
private _connections?;
|
|
1644
1611
|
private _httpClient?;
|
|
1645
1612
|
private readonly _contractStore;
|
|
1646
1613
|
constructor(resolved: ResolvedConfig);
|
|
@@ -1649,18 +1616,27 @@ declare class StackboneClient {
|
|
|
1649
1616
|
get storage(): StorageModule;
|
|
1650
1617
|
get ai(): AiModule;
|
|
1651
1618
|
get rag(): RagModule;
|
|
1652
|
-
get observability(): ObservabilityModule;
|
|
1653
1619
|
get approval(): ApprovalFacade;
|
|
1654
1620
|
get secrets(): SecretsFacade;
|
|
1655
1621
|
get config(): ConfigFacade;
|
|
1656
1622
|
/**
|
|
1657
|
-
*
|
|
1658
|
-
* `
|
|
1659
|
-
*
|
|
1660
|
-
* Gated against `queues.
|
|
1661
|
-
* missing paths are exercised the same way
|
|
1623
|
+
* Live surface — the agent's handle on the BullMQ-backed job system.
|
|
1624
|
+
* `publish` / `schedule` / `unschedule` / `listSchedules` make authenticated
|
|
1625
|
+
* calls to the control-plane dispatcher (`/api/v1/agent/queues/*`); the
|
|
1626
|
+
* agent never touches Redis. Gated against `queues.jobs` so the
|
|
1627
|
+
* handshake-blocked / capability-missing paths are exercised the same way
|
|
1628
|
+
* as the other gated surfaces.
|
|
1662
1629
|
*/
|
|
1663
1630
|
get queues(): QueuesModule;
|
|
1631
|
+
/**
|
|
1632
|
+
* Live surface — the agent's handle on the workspace's connector connections.
|
|
1633
|
+
* `list` / `invoke` make authenticated calls to the control-plane connectors
|
|
1634
|
+
* proxy (`/api/v1/agent/connections/*`); the credentials never enter the agent
|
|
1635
|
+
* container. Gated against `connections.actions` so the handshake-blocked /
|
|
1636
|
+
* capability-missing paths are exercised the same way as the other gated
|
|
1637
|
+
* surfaces.
|
|
1638
|
+
*/
|
|
1639
|
+
get connections(): ConnectionsModule;
|
|
1664
1640
|
/**
|
|
1665
1641
|
* Pending surface — runtime not built. Every method returns
|
|
1666
1642
|
* `not_implemented`. Mem0 is a third-party integration, not a Stackbone
|
|
@@ -1668,23 +1644,14 @@ declare class StackboneClient {
|
|
|
1668
1644
|
*/
|
|
1669
1645
|
get memory(): MemoryModule;
|
|
1670
1646
|
/**
|
|
1671
|
-
*
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
* Pending surface — runtime not built. Every method returns
|
|
1678
|
-
* `not_implemented`. Will gate once a capability row is declared in
|
|
1679
|
-
* `MODULE_CAPABILITIES`; until then it is intentionally ungated.
|
|
1647
|
+
* Live agent-local surface — reads prompts from `stackbone_platform.prompts`
|
|
1648
|
+
* in the agent DB over the shared `client.database` pool and compiles
|
|
1649
|
+
* templates with the local `@stackbone/prompt-compiler`. Constructed with
|
|
1650
|
+
* the same `(resolved, () => this.database)` shape as `secrets`/`config`;
|
|
1651
|
+
* like them it is NOT wired to the control-plane handshake gate (it talks to
|
|
1652
|
+
* the agent's own tables, not a control-plane datapath).
|
|
1680
1653
|
*/
|
|
1681
1654
|
get prompts(): PromptsFacade;
|
|
1682
|
-
/**
|
|
1683
|
-
* Pending surface — runtime not built. Returns an empty list today.
|
|
1684
|
-
* Connections target third-party OAuth providers, not the Stackbone
|
|
1685
|
-
* Agent Protocol, so this surface is intentionally not gated.
|
|
1686
|
-
*/
|
|
1687
|
-
get connections(): ConnectionsFacade;
|
|
1688
1655
|
/**
|
|
1689
1656
|
* Read-only view of the most recently resolved Stackbone Agent Protocol
|
|
1690
1657
|
* contract for this client's `stackboneApiUrl`. Returns `null` until a
|
|
@@ -1781,13 +1748,13 @@ interface InvokeContext<I extends z.ZodType> {
|
|
|
1781
1748
|
logger: Logger;
|
|
1782
1749
|
}
|
|
1783
1750
|
/**
|
|
1784
|
-
* The
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1751
|
+
* The synchronous `invoke` capability. Mirrors the wrapper's three HTTP verbs
|
|
1752
|
+
* (`/invoke`, `/health`, `/schema`): `invoke` is the only request path, its
|
|
1753
|
+
* `output` is the `/invoke` response body, and its schemas drive `/schema`.
|
|
1787
1754
|
*
|
|
1788
1755
|
* The fields are deliberately enumerated (no `[k: string]: unknown` escape
|
|
1789
|
-
* hatch) so the
|
|
1790
|
-
*
|
|
1756
|
+
* hatch) so the precise type the creator gets from `defineAgent` rejects
|
|
1757
|
+
* unknown keys on the block at compile time.
|
|
1791
1758
|
*/
|
|
1792
1759
|
interface InvokeCapability<I extends z.ZodType, O extends z.ZodType> {
|
|
1793
1760
|
/** Zod schema for the parsed `/invoke` request body. */
|
|
@@ -1796,47 +1763,232 @@ interface InvokeCapability<I extends z.ZodType, O extends z.ZodType> {
|
|
|
1796
1763
|
output: O;
|
|
1797
1764
|
/** Creator-supplied handler. Returns either the parsed output or a promise of it. */
|
|
1798
1765
|
run: (ctx: InvokeContext<I>) => z.infer<O> | Promise<z.infer<O>>;
|
|
1799
|
-
/** Optional per-invocation timeout in milliseconds.
|
|
1766
|
+
/** Optional per-invocation timeout in milliseconds. */
|
|
1767
|
+
timeoutMs?: number;
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* A named **job handler** — a sibling of `invoke` at the top level of the spec,
|
|
1771
|
+
* reached asynchronously via `client.queues.publish({ name })` (the BullMQ
|
|
1772
|
+
* dispatcher pushes the job back to `/invoke` with that name; the wrapper routes
|
|
1773
|
+
* it here instead of to `invoke.run`).
|
|
1774
|
+
*
|
|
1775
|
+
* Same shape as `invoke` with one difference: `output` is **optional**. A job is
|
|
1776
|
+
* fire-and-forget — the dispatcher only checks the 2xx, the return value is
|
|
1777
|
+
* journalled, not delivered to a caller. Declaring `output` self-validates the
|
|
1778
|
+
* job's result; omitting it lets `run` return anything (or nothing).
|
|
1779
|
+
*/
|
|
1780
|
+
interface JobCapability<I extends z.ZodType, O extends z.ZodType = z.ZodType> {
|
|
1781
|
+
/** Zod schema for the job payload (`publish`'s `payload`, revalidated here). */
|
|
1782
|
+
input: I;
|
|
1783
|
+
/** Optional Zod schema for the job's return value. Validated + journalled, never returned. */
|
|
1784
|
+
output?: O;
|
|
1785
|
+
/** Creator-supplied job handler. May return nothing — the result is not delivered. */
|
|
1786
|
+
run: (ctx: InvokeContext<I>) => z.infer<O> | void | Promise<z.infer<O> | void>;
|
|
1787
|
+
/** Optional per-job timeout in milliseconds. */
|
|
1800
1788
|
timeoutMs?: number;
|
|
1801
1789
|
}
|
|
1802
1790
|
/**
|
|
1803
|
-
*
|
|
1791
|
+
* Loose, non-generic view of a single capability (invoke or a job handler).
|
|
1792
|
+
* The wrapper / `loadSpec` read the spec through this — the precise per-handler
|
|
1793
|
+
* typing lives on the value `defineAgent` returns to the creator, not here.
|
|
1804
1794
|
*
|
|
1805
|
-
* `
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1795
|
+
* `run` is a method signature (not an arrow property) on purpose: it makes the
|
|
1796
|
+
* parameter bivariant so every concrete `InvokeCapability` / `JobCapability`
|
|
1797
|
+
* (whose `run` is typed against its own narrow input) is assignable to it, which
|
|
1798
|
+
* is what lets `AgentSpec` carry a string index signature of capabilities.
|
|
1799
|
+
*/
|
|
1800
|
+
interface AnyCapability {
|
|
1801
|
+
input: z.ZodType;
|
|
1802
|
+
output?: z.ZodType | undefined;
|
|
1803
|
+
run(ctx: InvokeContext<z.ZodType>): unknown;
|
|
1804
|
+
timeoutMs?: number | undefined;
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* The declarative shape every Stackbone agent returns from `src/index.ts`, as
|
|
1808
|
+
* the wrapper / `loadSpec` consume it: the **loose runtime view**. Every
|
|
1809
|
+
* capability — `invoke` and each named job handler — is an `AnyCapability`
|
|
1810
|
+
* (`output` optional, `run` loose). `invoke` is required; the string index
|
|
1811
|
+
* carries the job handlers, reached by name through the BullMQ dispatcher.
|
|
1809
1812
|
*
|
|
1810
|
-
*
|
|
1811
|
-
*
|
|
1813
|
+
* The precise per-handler typing (each `ctx.input` inferred, `invoke.output`
|
|
1814
|
+
* required) lives on the value `defineAgent` returns, which is assignable to
|
|
1815
|
+
* this. At runtime `invoke.output` is always present — the spec seed forces it —
|
|
1816
|
+
* so the wrapper reads it behind a small guard.
|
|
1812
1817
|
*/
|
|
1813
|
-
interface AgentSpec
|
|
1814
|
-
|
|
1818
|
+
interface AgentSpec {
|
|
1819
|
+
/** The synchronous entry: the `/invoke` response + the `GET /schema` source. */
|
|
1820
|
+
invoke: AnyCapability;
|
|
1821
|
+
/** Named job handlers, reached via `client.queues.publish({ name })`. */
|
|
1822
|
+
[name: string]: AnyCapability;
|
|
1815
1823
|
}
|
|
1816
1824
|
/**
|
|
1817
|
-
*
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1820
|
-
*
|
|
1825
|
+
* Top-level keys the platform owns. A job handler may not use these names.
|
|
1826
|
+
* Only `invoke` for now; the contract adds `health` / `schema` / `events` /
|
|
1827
|
+
* `schedules` here if/when they become declarable capabilities (see the named
|
|
1828
|
+
* job handlers ADR).
|
|
1829
|
+
*/
|
|
1830
|
+
declare const RESERVED_HANDLER_NAMES: readonly ["invoke"];
|
|
1831
|
+
/**
|
|
1832
|
+
* Hard ceiling on any capability's `timeoutMs`. The platform saga kills the
|
|
1833
|
+
* Machine before the wrapper would time out at higher values, so a creator
|
|
1834
|
+
* declaring a 10-minute timeout would publish a contract the runtime cannot
|
|
1835
|
+
* keep. Surface that conflict at boot instead of as a mysterious mid-invocation
|
|
1821
1836
|
* SIGKILL in production.
|
|
1822
1837
|
*/
|
|
1823
1838
|
declare const INVOKE_TIMEOUT_HARD_CAP_MS = 300000;
|
|
1824
1839
|
/**
|
|
1825
|
-
*
|
|
1840
|
+
* One handler block, generic **directly** on its input Zod schema `I`. `input: I`
|
|
1841
|
+
* is a single-level type parameter (not a nested indexed access like
|
|
1842
|
+
* `S['input']`), which is the shape TypeScript can invert when reading the spec
|
|
1843
|
+
* literal back — so every handler's `ctx.input` is inferred from its own schema.
|
|
1844
|
+
* `output` stays loose (`z.ZodType`) and `run`'s return is `unknown`: a handler's
|
|
1845
|
+
* result is validated against `output` at runtime (the wrapper), not at the type
|
|
1846
|
+
* layer, so output inference is not needed for the contract to hold.
|
|
1847
|
+
*/
|
|
1848
|
+
type HandlerBlock<I extends z.ZodType> = {
|
|
1849
|
+
input: I;
|
|
1850
|
+
output?: z.ZodType;
|
|
1851
|
+
run: (ctx: InvokeContext<I>) => unknown | Promise<unknown>;
|
|
1852
|
+
timeoutMs?: number;
|
|
1853
|
+
};
|
|
1854
|
+
/**
|
|
1855
|
+
* Per-key precise shape of the spec the creator writes. `T` maps each handler
|
|
1856
|
+
* name to its **input** schema; `[K in keyof T]: HandlerBlock<T[K]>` is a
|
|
1857
|
+
* homomorphic mapped type whose value (`input: T[K]`) is a single-level access,
|
|
1858
|
+
* so TypeScript infers `T` back from the literal and keeps the concrete key
|
|
1859
|
+
* names. The `& { invoke: ... }` arm makes `invoke` required and forces its
|
|
1860
|
+
* `output` to be present (a job's `output` is optional).
|
|
1861
|
+
*/
|
|
1862
|
+
type AgentSpecParam<T extends Record<string, z.ZodType>> = {
|
|
1863
|
+
[K in keyof T]: HandlerBlock<T[K]>;
|
|
1864
|
+
} & {
|
|
1865
|
+
invoke: {
|
|
1866
|
+
input: z.ZodType;
|
|
1867
|
+
output: z.ZodType;
|
|
1868
|
+
};
|
|
1869
|
+
};
|
|
1870
|
+
/**
|
|
1871
|
+
* Anchor + guard for `defineAgent`:
|
|
1826
1872
|
*
|
|
1827
|
-
* 1. **Inference anchor** —
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
* 2. **Boot-time invariant** —
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
1834
|
-
* requires the runtime check too.
|
|
1873
|
+
* 1. **Inference anchor** — each handler (`invoke` and every named job) infers
|
|
1874
|
+
* its own `ctx.input` from its `input` schema. The creator never repeats a
|
|
1875
|
+
* type.
|
|
1876
|
+
* 2. **Boot-time invariant** — `invoke` is required; every other top-level key
|
|
1877
|
+
* must be a valid handler object reached via `client.queues.publish({ name })`.
|
|
1878
|
+
* The TypeScript signature already shapes this; the runtime guard is the
|
|
1879
|
+
* safety net for creators forcing a shape through `as any`.
|
|
1835
1880
|
*
|
|
1836
1881
|
* Returns the spec verbatim so callers can `export default defineAgent({...})`
|
|
1837
1882
|
* and the wrapper can read the same object back through dynamic import.
|
|
1838
1883
|
*/
|
|
1839
|
-
declare
|
|
1884
|
+
declare function defineAgent<T extends Record<string, z.ZodType>>(spec: AgentSpecParam<T>): {
|
|
1885
|
+
[K in keyof T]: HandlerBlock<T[K]>;
|
|
1886
|
+
};
|
|
1887
|
+
|
|
1888
|
+
interface InvocationContext {
|
|
1889
|
+
/** The envelope's `invocationId` — surfaced on log lines as `trace_id`. */
|
|
1890
|
+
readonly invocationId: string;
|
|
1891
|
+
/** The run this invoke belongs to — surfaced on log lines as `run_id`. */
|
|
1892
|
+
readonly runId: string;
|
|
1893
|
+
/**
|
|
1894
|
+
* The handler this invoke routed to — `invoke` for a direct entry or the
|
|
1895
|
+
* named job handler (e.g. `demo-log-job`) the dispatcher delivered to.
|
|
1896
|
+
* Surfaced on log lines as `handler` so the Studio logs trail can tell which
|
|
1897
|
+
* entrypoint emitted each line. Optional so a caller that binds a context
|
|
1898
|
+
* without routing info (older tests, lifecycle hooks) still type-checks.
|
|
1899
|
+
*/
|
|
1900
|
+
readonly handler?: string;
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Runs `fn` with `context` bound as the active invocation. The context stays
|
|
1904
|
+
* available to every synchronous and asynchronous continuation rooted in this
|
|
1905
|
+
* call (i.e. across `await`s inside `fn`). Returns whatever `fn` returns.
|
|
1906
|
+
*/
|
|
1907
|
+
declare function runWithInvocationContext<T>(context: InvocationContext, fn: () => T): T;
|
|
1908
|
+
/**
|
|
1909
|
+
* Returns the active invocation context, or `undefined` when called outside any
|
|
1910
|
+
* invoke (process boot, lifecycle events, tests that don't set one up).
|
|
1911
|
+
*/
|
|
1912
|
+
declare function getInvocationContext(): InvocationContext | undefined;
|
|
1913
|
+
|
|
1914
|
+
/** The subset of the global `console` this bridge replaces. */
|
|
1915
|
+
interface ConsoleLike {
|
|
1916
|
+
log: (...args: unknown[]) => void;
|
|
1917
|
+
info: (...args: unknown[]) => void;
|
|
1918
|
+
warn: (...args: unknown[]) => void;
|
|
1919
|
+
error: (...args: unknown[]) => void;
|
|
1920
|
+
debug: (...args: unknown[]) => void;
|
|
1921
|
+
}
|
|
1922
|
+
interface InstallConsoleCaptureOptions {
|
|
1923
|
+
/** Console object to patch. Defaults to the global `console`. Test seam. */
|
|
1924
|
+
console?: ConsoleLike;
|
|
1925
|
+
/** Sink for `log` / `info` / `debug`. Defaults to `process.stdout`. Test seam. */
|
|
1926
|
+
stdout?: LogSink;
|
|
1927
|
+
/** Sink for `warn` / `error`. Defaults to `process.stderr`. Test seam. */
|
|
1928
|
+
stderr?: LogSink;
|
|
1929
|
+
/** Clock for the `ts` field. Defaults to `() => new Date()`. Test seam. */
|
|
1930
|
+
now?: () => Date;
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Replaces `console.log/info/debug/warn/error` so that, while an invocation
|
|
1934
|
+
* context is active, each call is re-emitted as a structured log line carrying
|
|
1935
|
+
* `run_id` / `trace_id`. Returns a function that restores the originals.
|
|
1936
|
+
* Calling it twice on the same console is a no-op (returns a no-op restore).
|
|
1937
|
+
*/
|
|
1938
|
+
declare function installInvocationConsoleCapture(options?: InstallConsoleCaptureOptions): () => void;
|
|
1939
|
+
|
|
1940
|
+
declare const STORED_TO_SDK_ENV: Readonly<Record<string, string>>;
|
|
1941
|
+
/** Encrypted envelope row as stored in `stackbone_platform.secrets`. */
|
|
1942
|
+
interface SystemSecretRow {
|
|
1943
|
+
name: string;
|
|
1944
|
+
version: string;
|
|
1945
|
+
nonce: Buffer | Uint8Array;
|
|
1946
|
+
ciphertext: Buffer | Uint8Array;
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Maps the `is_system` rows to the SDK env names, decrypting ONLY the rows that
|
|
1950
|
+
* have a known mapping, then derives two env-only channels. Existing values are
|
|
1951
|
+
* never overwritten (an explicit container/Machine override always wins). The
|
|
1952
|
+
* SDK env names this call populated are returned (sorted) so the caller can log
|
|
1953
|
+
* an observable, value-free "what got rehydrated" line — values are never
|
|
1954
|
+
* logged. Shared by both the cloud neon reader and the postgres-js reader so
|
|
1955
|
+
* they cannot drift on the mapping.
|
|
1956
|
+
*
|
|
1957
|
+
* Decryption is lazy on purpose: an unmapped stored row is skipped WITHOUT
|
|
1958
|
+
* decrypting it, so a non-envelope row (e.g. a probe row a stubbed query
|
|
1959
|
+
* returns) never throws. The two derived channels:
|
|
1960
|
+
* - `STACKBONE_POSTGRES_URL` from `databaseUrl` (the SDK's `client.database`
|
|
1961
|
+
* reads this name; the cloud harness's own Neon driver keeps `DATABASE_URL`).
|
|
1962
|
+
* - `STACKBONE_APPROVAL_SIGNING_KEY` from `HMAC_SECRET` so the SDK's approval
|
|
1963
|
+
* callback verifier reconciles with the signer (which signs with the same
|
|
1964
|
+
* boot-bundle HMAC).
|
|
1965
|
+
*/
|
|
1966
|
+
declare function rehydrateSystemSecretsRows(env: NodeJS.ProcessEnv, rows: readonly SystemSecretRow[], cipher: SecretCipher, databaseUrl: string): string[];
|
|
1967
|
+
/**
|
|
1968
|
+
* Reads the `is_system` rows from `stackbone_platform.secrets`. Injectable so
|
|
1969
|
+
* tests run against a stub without a real Postgres; production opens a
|
|
1970
|
+
* short-lived `postgres-js` connection (see `loadSystemSecretsIntoEnv`).
|
|
1971
|
+
*/
|
|
1972
|
+
type SystemSecretsReader = () => Promise<SystemSecretRow[]>;
|
|
1973
|
+
interface LoadSystemSecretsArgs {
|
|
1974
|
+
/** Connection string for the agent's Postgres (`STACKBONE_POSTGRES_URL`). */
|
|
1975
|
+
readonly databaseUrl: string;
|
|
1976
|
+
/** Per-agent base64 key the rows are decrypted with (`STACKBONE_SECRET_KEY`). */
|
|
1977
|
+
readonly secretKey: string;
|
|
1978
|
+
/** Target env to populate. Defaults to `process.env`. */
|
|
1979
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
1980
|
+
/** Override the row reader (tests inject a stub). */
|
|
1981
|
+
readonly reader?: SystemSecretsReader;
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Reads every `is_system` row from the agent's Postgres via `postgres-js`,
|
|
1985
|
+
* decrypts each with the per-agent key, and rehydrates them into `env` under
|
|
1986
|
+
* the SDK's expected names. Used by the `stackbone dev` emulator and the
|
|
1987
|
+
* self-host runtime entry to give the agent the same credentials the cloud
|
|
1988
|
+
* harness rehydrates — without depending on a Neon HTTP endpoint, which local
|
|
1989
|
+
* dev does not have.
|
|
1990
|
+
*/
|
|
1991
|
+
declare function loadSystemSecretsIntoEnv(args: LoadSystemSecretsArgs): Promise<string[]>;
|
|
1840
1992
|
|
|
1841
1993
|
/**
|
|
1842
1994
|
* Reserved error codes. Order is intentional: keep them flat (string) so
|
|
@@ -1968,4 +2120,4 @@ interface JsonSchemaDocument {
|
|
|
1968
2120
|
*/
|
|
1969
2121
|
declare const analyzeAgentSchemas: (pair: AgentSchemaPair) => SchemaIntrospectionResult;
|
|
1970
2122
|
|
|
1971
|
-
export { type AddMemoryRequest, type AgentSchemaPair, type AgentSpec, type ApprovalListOptions, type ApprovalListResult, type ApprovalRecord, type ApprovalRequest, type ApprovalRequestOptions, type ApprovalStatus, type ApprovalTool, type ApprovalToolSpec, type ApprovalTopic, type ApproverInfo, type
|
|
2123
|
+
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 InvokeOptions, 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 };
|