@stackbone/sdk 0.1.0-alpha.3 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +113 -87
- package/db/index.d.cts +1 -0
- package/db/index.d.ts +1 -0
- package/index.cjs +6723 -2639
- package/index.cjs.map +1 -1
- package/index.d.cts +775 -697
- package/index.d.ts +775 -697
- package/index.js +6716 -2640
- 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 +2 -12
- package/rag/schema.cjs +42 -82
- package/rag/schema.cjs.map +1 -1
- package/rag/schema.d.cts +1 -446
- package/rag/schema.d.ts +1 -446
- package/rag/schema.js +42 -61
- package/rag/schema.js.map +1 -1
- package/stackbone-sdk-0.1.0-alpha.4.tgz +0 -0
- package/rag/migrations/index.cjs +0 -71
- package/rag/migrations/index.cjs.map +0 -1
- package/rag/migrations/index.d.cts +0 -29
- package/rag/migrations/index.d.ts +0 -29
- package/rag/migrations/index.js +0 -66
- package/rag/migrations/index.js.map +0 -1
- package/stackbone-sdk-0.1.0-alpha.3.tgz +0 -0
package/index.d.cts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { ContractResponse } from '@stackbone/validators';
|
|
1
|
+
import { 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,47 @@ 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.prompts` — AGENT-LOCAL facade. Reads
|
|
272
|
+
* `stackbone_platform.prompts` / `prompt_versions` over the shared
|
|
273
|
+
* `client.database` pool and compiles templates with the local
|
|
274
|
+
* `@stackbone/prompt-compiler` (Mustache subset). `not_configured` covers a
|
|
275
|
+
* missing prompts schema (the `42P01` "run `stackbone db migrate up`" hint);
|
|
276
|
+
* `not_found` covers an absent / soft-deleted key; `missing_var` is the
|
|
277
|
+
* compile-time diagnostic when a `{{var}}` has no value; `unavailable`
|
|
278
|
+
* covers a failed DB read; `already_exists` covers a duplicate `create`.
|
|
279
|
+
*/
|
|
280
|
+
readonly prompts: readonly ["already_exists", "invalid_request", "missing_var", "not_configured", "not_found", "unavailable"];
|
|
242
281
|
/**
|
|
243
282
|
* `client.memory` — reserved. The README documents the prefix; today the
|
|
244
283
|
* pending surface returns `not_implemented` for every method so no
|
|
@@ -260,9 +299,10 @@ declare const SDK_ERROR_CODE_PREFIXES: {
|
|
|
260
299
|
*/
|
|
261
300
|
readonly database: readonly ["not_configured"];
|
|
262
301
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
302
|
+
* Observability — the run-cost rollup hook in `@stackbone/sdk/observability`
|
|
303
|
+
* (wired by the runtime, not a creator surface) emits this when its post-run
|
|
304
|
+
* Postgres write fails; everything else either returns ok or surfaces through
|
|
305
|
+
* the `database_*` family above.
|
|
266
306
|
*/
|
|
267
307
|
readonly observability: readonly ["close_run_failed"];
|
|
268
308
|
/**
|
|
@@ -467,6 +507,265 @@ declare class DatabaseModule {
|
|
|
467
507
|
raw(): DrizzleClient;
|
|
468
508
|
}
|
|
469
509
|
|
|
510
|
+
interface ApprovalToolSpec<I, O> {
|
|
511
|
+
name: string;
|
|
512
|
+
description: string;
|
|
513
|
+
parameters: Record<string, unknown>;
|
|
514
|
+
needsApproval?: boolean | ((input: I) => boolean | Promise<boolean>);
|
|
515
|
+
/**
|
|
516
|
+
* Maps the LLM input to the approval request fields. `topic` and `payload`
|
|
517
|
+
* default to the tool name and the raw input respectively when omitted;
|
|
518
|
+
* `onDecide` must always be supplied so the control plane knows where to
|
|
519
|
+
* deliver the eventual decision.
|
|
520
|
+
*/
|
|
521
|
+
toRequest: (input: I) => Omit<ApprovalRequestOptions<I>, 'payload' | 'topic'> & Partial<Pick<ApprovalRequestOptions<I>, 'payload' | 'topic'>>;
|
|
522
|
+
execute: (input: I) => Promise<O> | O;
|
|
523
|
+
}
|
|
524
|
+
interface OpenAIToolSpec {
|
|
525
|
+
type: 'function';
|
|
526
|
+
function: {
|
|
527
|
+
name: string;
|
|
528
|
+
description: string;
|
|
529
|
+
parameters: Record<string, unknown>;
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
type ToolInvokeResult<O> = {
|
|
533
|
+
status: 'pending';
|
|
534
|
+
approvalId: string;
|
|
535
|
+
expiresAt: string;
|
|
536
|
+
} | {
|
|
537
|
+
status: 'ok';
|
|
538
|
+
result: O;
|
|
539
|
+
};
|
|
540
|
+
interface ApprovalTool<I, O> {
|
|
541
|
+
name: string;
|
|
542
|
+
description: string;
|
|
543
|
+
parameters: Record<string, unknown>;
|
|
544
|
+
openaiSpec(): OpenAIToolSpec;
|
|
545
|
+
invoke(input: I): Promise<Result<ToolInvokeResult<O>>>;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
interface VerifyOptions {
|
|
549
|
+
/** Override the signing key resolved from `STACKBONE_APPROVAL_SIGNING_KEY` / `approvalSigningKey`. */
|
|
550
|
+
signingKey?: string;
|
|
551
|
+
/** Reject signatures whose timestamp is older than this (seconds). Default 300. */
|
|
552
|
+
toleranceSeconds?: number;
|
|
553
|
+
/** Inject a clock for tests. Defaults to `Date.now`. */
|
|
554
|
+
now?: () => number;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
type ApprovalTopic = string;
|
|
558
|
+
declare const DECISION_STATUSES: readonly ["approved", "rejected", "timed_out", "cancelled"];
|
|
559
|
+
type DecisionStatus = (typeof DECISION_STATUSES)[number];
|
|
560
|
+
type ApprovalStatus = 'pending' | DecisionStatus;
|
|
561
|
+
type Decision<T = unknown> = {
|
|
562
|
+
status: 'approved' | 'rejected';
|
|
563
|
+
payload: T;
|
|
564
|
+
approver: ApproverInfo;
|
|
565
|
+
decidedAt: string;
|
|
566
|
+
reason?: string;
|
|
567
|
+
} | {
|
|
568
|
+
status: 'timed_out';
|
|
569
|
+
decidedAt: string;
|
|
570
|
+
} | {
|
|
571
|
+
status: 'cancelled';
|
|
572
|
+
decidedAt: string;
|
|
573
|
+
reason?: string;
|
|
574
|
+
};
|
|
575
|
+
interface ApproverInfo {
|
|
576
|
+
id: string;
|
|
577
|
+
email: string;
|
|
578
|
+
name?: string;
|
|
579
|
+
}
|
|
580
|
+
interface ApprovalRequestOptions<T = unknown> {
|
|
581
|
+
topic: ApprovalTopic;
|
|
582
|
+
payload: T;
|
|
583
|
+
title?: string;
|
|
584
|
+
description?: string;
|
|
585
|
+
/** Path on the agent the control plane will POST the decision to (resolved against the agent's public URL server-side). */
|
|
586
|
+
onDecide: string;
|
|
587
|
+
/** ISO 8601 duration (`'24h'`) or milliseconds. Default 24h. */
|
|
588
|
+
timeout?: string | number;
|
|
589
|
+
onTimeout?: 'reject' | 'approve' | 'ignore';
|
|
590
|
+
approver?: string;
|
|
591
|
+
/** Same `(topic, idempotencyKey)` returns the same `approvalId` instead of creating a new one. */
|
|
592
|
+
idempotencyKey?: string;
|
|
593
|
+
metadata?: Record<string, unknown>;
|
|
594
|
+
}
|
|
595
|
+
interface ApprovalRequest {
|
|
596
|
+
approvalId: string;
|
|
597
|
+
status: 'pending';
|
|
598
|
+
callbackUrl: string;
|
|
599
|
+
expiresAt: string;
|
|
600
|
+
}
|
|
601
|
+
interface ApprovalRecord<T = unknown> {
|
|
602
|
+
approvalId: string;
|
|
603
|
+
topic: ApprovalTopic;
|
|
604
|
+
status: ApprovalStatus;
|
|
605
|
+
payload: T;
|
|
606
|
+
decision?: Decision<T>;
|
|
607
|
+
createdAt: string;
|
|
608
|
+
expiresAt: string;
|
|
609
|
+
metadata?: Record<string, unknown>;
|
|
610
|
+
}
|
|
611
|
+
interface ApprovalListOptions {
|
|
612
|
+
status?: ApprovalStatus;
|
|
613
|
+
topic?: ApprovalTopic;
|
|
614
|
+
cursor?: string;
|
|
615
|
+
/** 1..100. Default 50. */
|
|
616
|
+
limit?: number;
|
|
617
|
+
}
|
|
618
|
+
interface ApprovalListResult<T = unknown> {
|
|
619
|
+
items: ApprovalRecord<T>[];
|
|
620
|
+
nextCursor?: string;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* `client.approval` — AGENT-LOCAL. The HITL pause is recorded directly in the
|
|
624
|
+
* agent's own `stackbone_platform.approvals` table over the shared
|
|
625
|
+
* `client.database` pool; there is no control-plane POST anymore. The RESUME
|
|
626
|
+
* half stays Studio-driven: a human decides in Studio, the control plane signs
|
|
627
|
+
* an HMAC callback to `callbackUrl`, and the agent reconciles it with
|
|
628
|
+
* `verify()` (local crypto — see `verify.ts`).
|
|
629
|
+
*
|
|
630
|
+
* The write populates only the columns the cloud `create()` used to set
|
|
631
|
+
* (topic/payload/callback_url/idempotency_key/fallback/metadata/timeout_at;
|
|
632
|
+
* `schema` was sourced from a DTO field the SDK options object does not carry,
|
|
633
|
+
* so it stays NULL). `workspace_id`/`agent_id`/`run_id` stay NULL exactly as
|
|
634
|
+
* they were on the control-plane path — the idempotency `ON CONFLICT` relies on
|
|
635
|
+
* `workspace_id` being NULL with `NULLS NOT DISTINCT` (migration 0008).
|
|
636
|
+
*/
|
|
637
|
+
declare class ApprovalFacade {
|
|
638
|
+
private readonly _resolved;
|
|
639
|
+
/** Lazy accessor for `client.database` — reaches the shared `Sql` via `.shared().$client`. */
|
|
640
|
+
private readonly _getDatabase;
|
|
641
|
+
constructor(_resolved: ResolvedConfig,
|
|
642
|
+
/** Lazy accessor for `client.database` — reaches the shared `Sql` via `.shared().$client`. */
|
|
643
|
+
_getDatabase: () => DatabaseModule);
|
|
644
|
+
request<T = unknown>(options: ApprovalRequestOptions<T>): Promise<Result<ApprovalRequest>>;
|
|
645
|
+
cancel(approvalId: string, reason?: string): Promise<Result<void>>;
|
|
646
|
+
get<T = unknown>(approvalId: string): Promise<Result<ApprovalRecord<T>>>;
|
|
647
|
+
list<T = unknown>(options?: ApprovalListOptions): Promise<Result<ApprovalListResult<T>>>;
|
|
648
|
+
/**
|
|
649
|
+
* Local crypto verification — does not touch the database, so it has the same
|
|
650
|
+
* shape it had on the control-plane surface. Auditing rule: a method gates
|
|
651
|
+
* iff it issues a datapath call; this one never does.
|
|
652
|
+
*/
|
|
653
|
+
verify<T = unknown>(request: Request, options?: VerifyOptions): Promise<Result<Decision<T>>>;
|
|
654
|
+
/**
|
|
655
|
+
* Pure factory — returns an `ApprovalTool` whose `invoke()` ultimately calls
|
|
656
|
+
* back into `ApprovalFacade.request`.
|
|
657
|
+
*/
|
|
658
|
+
tool<I, O>(spec: ApprovalToolSpec<I, O>): ApprovalTool<I, O>;
|
|
659
|
+
private sql;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
declare class ConfigFacade {
|
|
663
|
+
private readonly _resolved;
|
|
664
|
+
/** Lazy accessor for `client.database` — see `SecretsFacade`. */
|
|
665
|
+
private readonly _getDatabase;
|
|
666
|
+
constructor(_resolved: ResolvedConfig,
|
|
667
|
+
/** Lazy accessor for `client.database` — see `SecretsFacade`. */
|
|
668
|
+
_getDatabase: () => DatabaseModule);
|
|
669
|
+
get<T = unknown>(key: string): Promise<Result<T>>;
|
|
670
|
+
/**
|
|
671
|
+
* Keys absent from the agent's config come back as omissions in the
|
|
672
|
+
* response — the returned map only contains entries the agent DB actually
|
|
673
|
+
* has, so callers must access fields with `?.` (the return type is
|
|
674
|
+
* `Partial<T>`).
|
|
675
|
+
*/
|
|
676
|
+
getMany<T extends Record<string, unknown> = Record<string, unknown>>(keys: string[]): Promise<Result<Partial<T>>>;
|
|
677
|
+
private loadPayload;
|
|
678
|
+
private sql;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* A prompt's current/active state. Key-based (workspace-unique). `template` is
|
|
683
|
+
* the current version's content; `version` is the live version number.
|
|
684
|
+
*/
|
|
685
|
+
interface Prompt {
|
|
686
|
+
key: string;
|
|
687
|
+
name: string;
|
|
688
|
+
description: string | null;
|
|
689
|
+
/** The current version's content. */
|
|
690
|
+
template: string;
|
|
691
|
+
/** The live version number. The first `create` produces version `1`. */
|
|
692
|
+
version: number;
|
|
693
|
+
/** `{{var}}` names referenced by the current version. */
|
|
694
|
+
variables: readonly string[];
|
|
695
|
+
metadata: Record<string, unknown> | null;
|
|
696
|
+
/** ISO 8601 UTC timestamp. */
|
|
697
|
+
createdAt: string;
|
|
698
|
+
updatedAt: string;
|
|
699
|
+
}
|
|
700
|
+
interface GetPromptOptions {
|
|
701
|
+
/** Pin a specific version. Omitted -> current/latest. */
|
|
702
|
+
version?: number;
|
|
703
|
+
}
|
|
704
|
+
interface ListPromptsOptions {
|
|
705
|
+
/** 1..100. Default 50. */
|
|
706
|
+
limit?: number;
|
|
707
|
+
}
|
|
708
|
+
interface ListPromptsResult {
|
|
709
|
+
/** Current version of each (non-deleted) prompt. */
|
|
710
|
+
items: readonly Prompt[];
|
|
711
|
+
}
|
|
712
|
+
interface CreatePromptRequest {
|
|
713
|
+
key: string;
|
|
714
|
+
name: string;
|
|
715
|
+
/** The version-1 content. */
|
|
716
|
+
template: string;
|
|
717
|
+
description?: string;
|
|
718
|
+
metadata?: Record<string, unknown>;
|
|
719
|
+
}
|
|
720
|
+
interface UpdatePromptOptions {
|
|
721
|
+
/** New content. Appends a new immutable version. */
|
|
722
|
+
template?: string;
|
|
723
|
+
name?: string;
|
|
724
|
+
description?: string | null;
|
|
725
|
+
/** Replaces the prompt-head metadata. */
|
|
726
|
+
metadata?: Record<string, unknown>;
|
|
727
|
+
}
|
|
728
|
+
interface DeletePromptOptions {
|
|
729
|
+
/** Reserved for future per-version deletes; ignored today (soft-deletes the key). */
|
|
730
|
+
version?: number;
|
|
731
|
+
}
|
|
732
|
+
interface DeletePromptResult {
|
|
733
|
+
key: string;
|
|
734
|
+
/** Number of prompt heads soft-deleted (0 or 1). */
|
|
735
|
+
deleted: number;
|
|
736
|
+
}
|
|
737
|
+
interface CompilePromptResult {
|
|
738
|
+
/** The rendered string. */
|
|
739
|
+
output: string;
|
|
740
|
+
/** The version the compile ran against. */
|
|
741
|
+
version: number;
|
|
742
|
+
}
|
|
743
|
+
declare class PromptsFacade {
|
|
744
|
+
private readonly _resolved;
|
|
745
|
+
/**
|
|
746
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
747
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
748
|
+
* reaching into a sibling module's implementation file.
|
|
749
|
+
*/
|
|
750
|
+
private readonly _getDatabase;
|
|
751
|
+
constructor(_resolved: ResolvedConfig,
|
|
752
|
+
/**
|
|
753
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
754
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
755
|
+
* reaching into a sibling module's implementation file.
|
|
756
|
+
*/
|
|
757
|
+
_getDatabase: () => DatabaseModule);
|
|
758
|
+
get(key: string, options?: GetPromptOptions): Promise<Result<Prompt>>;
|
|
759
|
+
compile(key: string, variables: Record<string, unknown>, options?: GetPromptOptions): Promise<Result<CompilePromptResult>>;
|
|
760
|
+
list(options?: ListPromptsOptions): Promise<Result<ListPromptsResult>>;
|
|
761
|
+
create(request: CreatePromptRequest): Promise<Result<Prompt>>;
|
|
762
|
+
update(key: string, options: UpdatePromptOptions): Promise<Result<Prompt>>;
|
|
763
|
+
delete(key: string, _options?: DeletePromptOptions): Promise<Result<DeletePromptResult>>;
|
|
764
|
+
private sql;
|
|
765
|
+
private mapReadError;
|
|
766
|
+
private mapWriteError;
|
|
767
|
+
}
|
|
768
|
+
|
|
470
769
|
/**
|
|
471
770
|
* Public surface behind `client.ai`. Wraps the official `openai` SDK with a
|
|
472
771
|
* `baseURL` override pointing to OpenRouter, so 300+ models are reachable
|
|
@@ -618,176 +917,17 @@ interface RequestOptions$1 {
|
|
|
618
917
|
signal?: AbortSignal;
|
|
619
918
|
}
|
|
620
919
|
|
|
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 {
|
|
920
|
+
/**
|
|
921
|
+
* Handle returned by `client.rag.ingestAsync`. The job id is allocated
|
|
922
|
+
* synchronously against `stackbone_rag_jobs` so the caller can track / cancel
|
|
923
|
+
* the work via the `/api/rag/jobs/:jobId/*` surface; `events` is a streaming
|
|
924
|
+
* channel of progress events (ADR §D9 shape); `result` settles when the
|
|
925
|
+
* pipeline finishes (or fails / is cancelled).
|
|
926
|
+
*/
|
|
927
|
+
interface IngestAsyncHandle {
|
|
672
928
|
jobId: string;
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
interface IngestJobWriter {
|
|
676
|
-
/**
|
|
677
|
-
* Allocates a `stackbone_rag_jobs` row in `running` state and returns its
|
|
678
|
-
* id. Caller is expected to surface this id to the user before any
|
|
679
|
-
* progress event fires so a `client.rag.ingestAsync` consumer can
|
|
680
|
-
* immediately address the job (e.g. `POST /api/rag/jobs/:jobId/cancel`).
|
|
681
|
-
*/
|
|
682
|
-
start(args: IngestJobStartArgs): Promise<Result<{
|
|
683
|
-
jobId: string;
|
|
684
|
-
}>>;
|
|
685
|
-
/** Idempotent — last write wins on `progress` jsonb. */
|
|
686
|
-
progress(args: IngestJobProgressArgs): Promise<void>;
|
|
687
|
-
/** Marks the row terminal (status='succeeded'). */
|
|
688
|
-
complete(args: IngestJobCompleteArgs): Promise<void>;
|
|
689
|
-
/** Marks the row terminal (status='failed') and stores the error message. */
|
|
690
|
-
fail(args: IngestJobFailArgs): Promise<void>;
|
|
691
|
-
/**
|
|
692
|
-
* Polled between chunk batches. Returning `true` makes the pipeline abort
|
|
693
|
-
* with `rag_ingest_cancelled`, leaving the row in whatever terminal state
|
|
694
|
-
* the writer transitions it to next (typically `cancelled`).
|
|
695
|
-
*/
|
|
696
|
-
isCancelled(args: {
|
|
697
|
-
jobId: string;
|
|
698
|
-
}): Promise<boolean>;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
interface ParseOptions {
|
|
702
|
-
/** MIME type override. If omitted, the parser sniffs the input (Blob.type or PDF magic bytes). */
|
|
703
|
-
mime?: string;
|
|
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>>;
|
|
929
|
+
events: AsyncIterable<RagIngestProgress>;
|
|
930
|
+
result: Promise<Result<IngestResponse>>;
|
|
791
931
|
}
|
|
792
932
|
/**
|
|
793
933
|
* `client.rag` — `pgvector`-backed retrieval. Two shapes per write/read:
|
|
@@ -807,9 +947,9 @@ interface IngestAsyncHandle {
|
|
|
807
947
|
* consumer; memory and queues will adopt the same accessor when they
|
|
808
948
|
* land.
|
|
809
949
|
*
|
|
810
|
-
* Schema readiness:
|
|
811
|
-
*
|
|
812
|
-
*
|
|
950
|
+
* Schema readiness: the canonical schema (`stackbone_platform.rag_*`) is
|
|
951
|
+
* provisioned by the platform migrator, present on every install. When an
|
|
952
|
+
* operation still hits `42P01 relation does not exist`, the pipeline returns
|
|
813
953
|
* `SdkError('rag_schema_missing')` with an actionable hint.
|
|
814
954
|
*/
|
|
815
955
|
/**
|
|
@@ -884,10 +1024,11 @@ declare class RagModule {
|
|
|
884
1024
|
deleteWhere(filter: Record<string, unknown>, options?: DeleteOptions): Promise<Result<DeleteResponse>>;
|
|
885
1025
|
retrieve(request: RetrieveRequest): Promise<Result<RetrieveHit[]>>;
|
|
886
1026
|
/**
|
|
887
|
-
* Drops the legacy ad-hoc RAG
|
|
888
|
-
* pre-feature-30 agents
|
|
889
|
-
*
|
|
890
|
-
*
|
|
1027
|
+
* Drops the legacy ad-hoc RAG tables (`rag_chunks` / `_rag_meta`) that
|
|
1028
|
+
* pre-feature-30 agents provisioned on first ingest. Kept for backwards
|
|
1029
|
+
* compatibility so an old agent can clean those up after upgrading. The
|
|
1030
|
+
* canonical RAG schema (`stackbone_platform.rag_*`) is owned by the platform
|
|
1031
|
+
* migrator — this method never touches it.
|
|
891
1032
|
*/
|
|
892
1033
|
reset(): Promise<Result<void>>;
|
|
893
1034
|
/**
|
|
@@ -908,365 +1049,41 @@ declare class RagModule {
|
|
|
908
1049
|
* Future cross-module transaction propagation plugs a tx-bound `Sql`
|
|
909
1050
|
* into the pipeline at the same call site, replacing the pool-bound
|
|
910
1051
|
* one without touching the rest of RAG.
|
|
911
|
-
*
|
|
912
|
-
* Configuration errors (`STACKBONE_POSTGRES_URL` unset) flow up as a
|
|
913
|
-
* `Result.err` with the same `database_not_configured` shape
|
|
914
|
-
* `client.database` raises, instead of throwing through the public
|
|
915
|
-
* surface.
|
|
916
|
-
*/
|
|
917
|
-
private sql;
|
|
918
|
-
private withPipeline;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
/** Subset of `RequestInit['body']` we serialize without modification. */
|
|
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.
|
|
1052
|
+
*
|
|
1053
|
+
* Configuration errors (`STACKBONE_POSTGRES_URL` unset) flow up as a
|
|
1054
|
+
* `Result.err` with the same `database_not_configured` shape
|
|
1055
|
+
* `client.database` raises, instead of throwing through the public
|
|
1056
|
+
* surface.
|
|
1219
1057
|
*/
|
|
1220
|
-
|
|
1058
|
+
private sql;
|
|
1059
|
+
private withPipeline;
|
|
1221
1060
|
}
|
|
1222
1061
|
|
|
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 {
|
|
1062
|
+
declare class SecretsFacade {
|
|
1235
1063
|
private readonly _resolved;
|
|
1236
|
-
private _processor?;
|
|
1237
|
-
private readonly _loggers;
|
|
1238
|
-
private _sql;
|
|
1239
|
-
private _sqlResolved;
|
|
1240
|
-
constructor(_resolved: ResolvedConfig);
|
|
1241
1064
|
/**
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
* Platform Ops.
|
|
1065
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
1066
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
1067
|
+
* reaching into a sibling module's implementation file.
|
|
1246
1068
|
*/
|
|
1247
|
-
|
|
1248
|
-
|
|
1069
|
+
private readonly _getDatabase;
|
|
1070
|
+
private _cipher?;
|
|
1071
|
+
constructor(_resolved: ResolvedConfig,
|
|
1249
1072
|
/**
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
1253
|
-
* file handle / buffered timer stays stable across repeated calls inside
|
|
1254
|
-
* the same run.
|
|
1073
|
+
* Lazy accessor for `client.database`. The facade pulls the shared
|
|
1074
|
+
* `postgres-js` `Sql` via `getDatabase().shared().$client` — never
|
|
1075
|
+
* reaching into a sibling module's implementation file.
|
|
1255
1076
|
*/
|
|
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>;
|
|
1077
|
+
_getDatabase: () => DatabaseModule);
|
|
1078
|
+
get(name: string): Promise<Result<string>>;
|
|
1261
1079
|
/**
|
|
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.
|
|
1080
|
+
* Names absent from the agent come back as omissions in the response — the
|
|
1081
|
+
* returned map only contains entries the agent DB actually has. Callers that
|
|
1082
|
+
* need "all-or-nothing" semantics should diff the keys.
|
|
1267
1083
|
*/
|
|
1268
|
-
|
|
1269
|
-
private
|
|
1084
|
+
getMany(names: string[]): Promise<Result<Record<string, string>>>;
|
|
1085
|
+
private cipher;
|
|
1086
|
+
private sql;
|
|
1270
1087
|
}
|
|
1271
1088
|
|
|
1272
1089
|
interface StorageObject {
|
|
@@ -1363,30 +1180,6 @@ declare class StorageBucket {
|
|
|
1363
1180
|
private resolve;
|
|
1364
1181
|
}
|
|
1365
1182
|
|
|
1366
|
-
/**
|
|
1367
|
-
* OAuth connections (Notion, GDrive, Slack…).
|
|
1368
|
-
*
|
|
1369
|
-
* Contract gating: not gated. The OAuth surface targets third-party
|
|
1370
|
-
* providers, not the Stackbone Agent Protocol, so there is no entry in
|
|
1371
|
-
* `MODULE_CAPABILITIES` (see `contract/capability-registry.ts`). When the
|
|
1372
|
-
* runtime lands and a Stackbone-mediated capability shows up (e.g. control
|
|
1373
|
-
* plane OAuth broker), we add the row and wire the gate.
|
|
1374
|
-
*/
|
|
1375
|
-
declare class ConnectionsFacade {
|
|
1376
|
-
constructor(_resolved: ResolvedConfig, _http: HttpClient);
|
|
1377
|
-
list(): Promise<Result<readonly never[]>>;
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
/** Emit events to the organization event bus. */
|
|
1381
|
-
declare class EventsFacade {
|
|
1382
|
-
private readonly _http;
|
|
1383
|
-
private readonly _gate;
|
|
1384
|
-
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1385
|
-
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1386
|
-
gate?: ModuleGate);
|
|
1387
|
-
emit(_name: string, _payload: unknown): Promise<Result<void>>;
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
1183
|
/**
|
|
1391
1184
|
* Where a memory lives. Default `'user'`. `'session'` is short-lived and
|
|
1392
1185
|
* collapsed (or dropped) by `endSession()`; `'agent'` is shared across every
|
|
@@ -1505,96 +1298,201 @@ declare class MemoryModule {
|
|
|
1505
1298
|
endSession(_sessionId: string, _options?: EndSessionOptions): Promise<Result<EndSessionResult>>;
|
|
1506
1299
|
}
|
|
1507
1300
|
|
|
1301
|
+
/** Subset of `RequestInit['body']` we serialize without modification. */
|
|
1302
|
+
type SerializedBody = NonNullable<RequestInit['body']>;
|
|
1508
1303
|
/**
|
|
1509
|
-
*
|
|
1510
|
-
*
|
|
1304
|
+
* Per-status overrides facades attach when an HTTP status carries a
|
|
1305
|
+
* non-canonical meaning for the endpoint — e.g. a `POST` whose `409` means
|
|
1306
|
+
* "already exists" instead of the default "conflict". Keys are HTTP statuses,
|
|
1307
|
+
* values are domain codes the transport should surface verbatim (no prefix
|
|
1308
|
+
* concatenation — the override is the final code).
|
|
1309
|
+
*
|
|
1310
|
+
* The value is typed against the catalog (`SdkErrorCode`) so an endpoint
|
|
1311
|
+
* cannot smuggle a wire code that isn't in the documented inventory.
|
|
1511
1312
|
*/
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
/**
|
|
1526
|
-
|
|
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;
|
|
1313
|
+
type ErrorOverrides = Record<number, SdkErrorCode>;
|
|
1314
|
+
/**
|
|
1315
|
+
* Tells the transport how to remap HTTP status codes onto the surface's
|
|
1316
|
+
* domain code prefix. `404 → '<prefix>_not_found'`, `401 → '<prefix>_unauthorized'`,
|
|
1317
|
+
* etc. Endpoint-specific overrides win over the canonical mapping.
|
|
1318
|
+
*
|
|
1319
|
+
* `prefix` is constrained to `SdkErrorPrefix` (the catalog's prefix tuple) so
|
|
1320
|
+
* an unknown surface name doesn't compile — keeps the wire codes the
|
|
1321
|
+
* transport emits inside the typed inventory.
|
|
1322
|
+
*/
|
|
1323
|
+
interface ErrorMapping {
|
|
1324
|
+
/** Prefix concatenated with `_not_found`, `_unauthorized`, …. Required for any domain remap. */
|
|
1325
|
+
prefix: SdkErrorPrefix;
|
|
1326
|
+
/** Per-status final code that overrides the canonical mapping. */
|
|
1327
|
+
overrides?: ErrorOverrides;
|
|
1537
1328
|
}
|
|
1538
|
-
interface
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1329
|
+
interface RequestOptions extends Omit<RequestInit, 'body' | 'signal'> {
|
|
1330
|
+
/** Scalar query params. `undefined` values are dropped, others get coerced via `String()`. */
|
|
1331
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
1332
|
+
/**
|
|
1333
|
+
* Repeated string-array params validated and serialised by the transport.
|
|
1334
|
+
* Empty arrays / blank elements reject upstream with `<prefix>_invalid_request`
|
|
1335
|
+
* (falls back to `http_invalid_request` when no `errorMapping` is set). Each
|
|
1336
|
+
* array is joined with `,` to match the canonical control-plane shape.
|
|
1337
|
+
*/
|
|
1338
|
+
arrayParams?: Record<string, readonly string[]>;
|
|
1339
|
+
body?: SerializedBody | Record<string, unknown> | unknown[] | null;
|
|
1340
|
+
signal?: AbortSignal;
|
|
1341
|
+
/** Allow retrying non-idempotent requests (POST, PATCH). Off by default to prevent duplicate writes. */
|
|
1342
|
+
idempotent?: boolean;
|
|
1542
1343
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1344
|
+
/**
|
|
1345
|
+
* Shape every facade method ideally collapses into: "POST to /path with this
|
|
1346
|
+
* body, validate against this schema, surface errors as `<prefix>_*`". The
|
|
1347
|
+
* transport handles validation, HTTP→domain remapping, querystring assembly
|
|
1348
|
+
* and the `Result` envelope so facades stay tiny orchestrators.
|
|
1349
|
+
*
|
|
1350
|
+
* `arrayParams` keys default to `errorMapping.prefix` for the validation
|
|
1351
|
+
* error code (e.g. an empty `names: []` becomes `secrets_invalid_request`).
|
|
1352
|
+
*/
|
|
1353
|
+
interface TransportRequest<T> extends RequestOptions {
|
|
1354
|
+
method: string;
|
|
1355
|
+
path: string;
|
|
1356
|
+
/**
|
|
1357
|
+
* Zod schema (or any object with a `.safeParse()` method) the parsed body
|
|
1358
|
+
* must validate against. On failure the transport surfaces
|
|
1359
|
+
* `<prefix>_invalid_response` (or `http_invalid_response` if no prefix is
|
|
1360
|
+
* configured) with the schema issues attached to `meta.issues`.
|
|
1361
|
+
*/
|
|
1362
|
+
responseSchema?: ResponseSchema<T>;
|
|
1363
|
+
/** HTTP→domain remapping. Omit to let `http_*` codes flow through unchanged. */
|
|
1364
|
+
errorMapping?: ErrorMapping;
|
|
1548
1365
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1366
|
+
/**
|
|
1367
|
+
* Minimal contract the transport needs from a schema — `safeParse`. Compatible
|
|
1368
|
+
* with Zod (`z.object(…).safeParse(value)`) and any hand-rolled validator that
|
|
1369
|
+
* follows the same shape. Avoids leaking Zod's full surface into the transport
|
|
1370
|
+
* signature.
|
|
1371
|
+
*/
|
|
1372
|
+
interface ResponseSchema<T> {
|
|
1373
|
+
safeParse(input: unknown): {
|
|
1374
|
+
success: true;
|
|
1375
|
+
data: T;
|
|
1376
|
+
} | {
|
|
1377
|
+
success: false;
|
|
1378
|
+
error: {
|
|
1379
|
+
issues: unknown;
|
|
1380
|
+
};
|
|
1381
|
+
};
|
|
1552
1382
|
}
|
|
1553
|
-
interface
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1383
|
+
interface HttpClientOptions {
|
|
1384
|
+
/** Default 30_000. Set to 0 to disable. */
|
|
1385
|
+
timeout?: number;
|
|
1386
|
+
/** Default 3. Set to 0 to disable. */
|
|
1387
|
+
retryCount?: number;
|
|
1388
|
+
/** Initial backoff in ms; doubles each attempt with ±15% jitter. Default 500. */
|
|
1389
|
+
retryDelay?: number;
|
|
1390
|
+
/** Override the global fetch (useful for tests). */
|
|
1391
|
+
fetch?: typeof fetch;
|
|
1557
1392
|
}
|
|
1558
1393
|
/**
|
|
1559
|
-
*
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1394
|
+
* Shared HTTP client used by every facade that talks to the Stackbone
|
|
1395
|
+
* control plane. Wraps `fetch` with timeout, exponential backoff with jitter,
|
|
1396
|
+
* idempotent-only retries, response validation (Zod-compatible schemas) and
|
|
1397
|
+
* a uniform `Result` envelope so facades never throw at the SDK boundary.
|
|
1398
|
+
*
|
|
1399
|
+
* Each facade tells the transport its error prefix (`'secrets'`, `'config'`,
|
|
1400
|
+
* `'approval'`) so 404/401/403/429/5xx remap to surface-specific codes —
|
|
1401
|
+
* `secrets_not_found`, `approval_unauthorized`, etc. Endpoint-specific
|
|
1402
|
+
* remaps (e.g. `409 → 'secrets_already_exists'`) flow through
|
|
1403
|
+
* `errorMapping.overrides`. Transport-level failures (network, timeout,
|
|
1404
|
+
* parse) keep the `http_*` prefix the README contract documents.
|
|
1565
1405
|
*
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1568
|
-
* (no entry in `MODULE_CAPABILITIES` — see `contract/capability-registry.ts`).
|
|
1569
|
-
* When the runtime lands we add the row (e.g. `prompts.read_write`) and
|
|
1570
|
-
* wire the gate the same way `secrets` / `config` do.
|
|
1406
|
+
* Reads `STACKBONE_API_URL` and `STACKBONE_AGENT_JWT` lazily on every request so
|
|
1407
|
+
* env-var rotation is picked up without restarting the client.
|
|
1571
1408
|
*/
|
|
1572
|
-
declare class
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1409
|
+
declare class HttpClient {
|
|
1410
|
+
private readonly resolved;
|
|
1411
|
+
private readonly timeout;
|
|
1412
|
+
private readonly retryCount;
|
|
1413
|
+
private readonly retryDelay;
|
|
1414
|
+
private readonly fetchImpl;
|
|
1415
|
+
constructor(resolved: ResolvedConfig, options?: HttpClientOptions);
|
|
1416
|
+
/**
|
|
1417
|
+
* Convenience for `request({ method: 'GET', path, ... })`. Kept so legacy
|
|
1418
|
+
* callers (tests, pending facades) keep compiling — the deep work happens
|
|
1419
|
+
* in `request()`.
|
|
1420
|
+
*/
|
|
1421
|
+
get<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1422
|
+
post<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1423
|
+
put<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1424
|
+
patch<T>(path: string, body?: RequestOptions['body'], options?: RequestOptions): Promise<Result<T>>;
|
|
1425
|
+
delete<T>(path: string, options?: RequestOptions): Promise<Result<T>>;
|
|
1426
|
+
/**
|
|
1427
|
+
* The transport entry point every facade should target. Handles base-URL
|
|
1428
|
+
* resolution, header injection, body/querystring serialisation, retry +
|
|
1429
|
+
* timeout, response validation and HTTP→domain error remapping.
|
|
1430
|
+
*/
|
|
1431
|
+
request<T>(req: TransportRequest<T>): Promise<Result<T>>;
|
|
1432
|
+
private computeBackoff;
|
|
1580
1433
|
}
|
|
1581
1434
|
|
|
1435
|
+
/**
|
|
1436
|
+
* Enqueue a one-shot (or deferred) job. The opaque `payload` is whatever the
|
|
1437
|
+
* agent's `receive` handler expects — the core never inspects it.
|
|
1438
|
+
*/
|
|
1582
1439
|
interface PublishRequest {
|
|
1583
|
-
|
|
1584
|
-
|
|
1440
|
+
/** Opaque job name (e.g. `send-email`). Surfaced in the Studio inspector. */
|
|
1441
|
+
name: string;
|
|
1442
|
+
/** Opaque job payload — JSON-serialisable; the core forwards it untouched. */
|
|
1443
|
+
payload: unknown;
|
|
1444
|
+
/** Per-job override of the platform retry default. Omitted → platform default. */
|
|
1585
1445
|
retries?: number;
|
|
1446
|
+
/** Deferred delivery in milliseconds. Omitted / 0 → immediate. */
|
|
1586
1447
|
delay?: number;
|
|
1448
|
+
/** Idempotency hint — a duplicate publish with the same key collapses to one job. */
|
|
1587
1449
|
deduplicationId?: string;
|
|
1588
|
-
headers?: Record<string, string>;
|
|
1589
1450
|
}
|
|
1451
|
+
/** Register / update a dynamic per-installation cron schedule. */
|
|
1452
|
+
interface ScheduleRequest {
|
|
1453
|
+
/** Schedule name, unique per install. Re-scheduling the same name updates it in place. */
|
|
1454
|
+
name: string;
|
|
1455
|
+
/** Opaque payload delivered on every fire. */
|
|
1456
|
+
payload: unknown;
|
|
1457
|
+
/** Cron expression evaluated by the dispatcher. */
|
|
1458
|
+
cron: string;
|
|
1459
|
+
/** IANA timezone the cron pattern runs in. Defaults to UTC. */
|
|
1460
|
+
tz?: string;
|
|
1461
|
+
/** Per-job override of the platform retry default. */
|
|
1462
|
+
retries?: number;
|
|
1463
|
+
}
|
|
1464
|
+
/** Cancel a previously registered schedule by name. */
|
|
1465
|
+
interface UnscheduleRequest {
|
|
1466
|
+
name: string;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* `client.queues` — the agent's handle on the BullMQ-backed job system. The
|
|
1470
|
+
* agent never touches Redis: each method makes an authenticated call to the
|
|
1471
|
+
* control plane (the dispatcher owns the queue + the clock).
|
|
1472
|
+
*
|
|
1473
|
+
* - `publish` enqueues a one-shot job (optionally deferred via `delay`, with a
|
|
1474
|
+
* per-job `retries` override and an optional `deduplicationId`).
|
|
1475
|
+
* - `schedule` / `unschedule` manage dynamic per-user cron schedules.
|
|
1476
|
+
* - `listSchedules` lists the install's active schedules.
|
|
1477
|
+
*
|
|
1478
|
+
* Authentication reuses the existing `STACKBONE_AGENT_JWT` channel injected at
|
|
1479
|
+
* provisioning (the `HttpClient` attaches the bearer + install header), so the
|
|
1480
|
+
* core scopes every enqueue to the agent's own install.
|
|
1481
|
+
*
|
|
1482
|
+
* Contract gating: every method awaits the `queues.jobs` module gate before
|
|
1483
|
+
* touching the network, so a stale datapath / missing capability surfaces as
|
|
1484
|
+
* a gating error rather than a failed POST.
|
|
1485
|
+
*/
|
|
1590
1486
|
declare class QueuesModule {
|
|
1487
|
+
private readonly _http;
|
|
1591
1488
|
private readonly _gate;
|
|
1592
|
-
constructor(resolved: ResolvedConfig,
|
|
1489
|
+
constructor(resolved: ResolvedConfig, _http: HttpClient,
|
|
1593
1490
|
/** Test seam — see `ModuleGate`. Defaults to the lazy contract gate. */
|
|
1594
1491
|
gate?: ModuleGate);
|
|
1595
|
-
publish(
|
|
1596
|
-
|
|
1597
|
-
|
|
1492
|
+
publish(request: PublishRequest): Promise<Result<PublishJobResponse>>;
|
|
1493
|
+
schedule(request: ScheduleRequest): Promise<Result<ScheduleJobResponse>>;
|
|
1494
|
+
unschedule(request: UnscheduleRequest): Promise<Result<UnscheduleJobResponse>>;
|
|
1495
|
+
listSchedules(): Promise<Result<ListSchedulesResponse>>;
|
|
1598
1496
|
}
|
|
1599
1497
|
|
|
1600
1498
|
/**
|
|
@@ -1603,18 +1501,25 @@ declare class QueuesModule {
|
|
|
1603
1501
|
* groups each surface by where its state lives:
|
|
1604
1502
|
*
|
|
1605
1503
|
* - `surfaces/agent-local/` — Postgres branch owned by the agent
|
|
1606
|
-
* (`database`, `rag`).
|
|
1504
|
+
* (`database`, `rag`, `secrets`, `config`, `approval`). `secrets`,
|
|
1505
|
+
* `config` and `approval` moved here from the control plane: the agent now
|
|
1506
|
+
* reads its secrets/config and records HITL pauses directly against its own
|
|
1507
|
+
* `stackbone_platform.*` tables (decrypting secrets with the per-agent key),
|
|
1508
|
+
* with no control-plane round-trip. The approval RESUME callback stays
|
|
1509
|
+
* Studio-signed and is reconciled locally by `approval.verify()`.
|
|
1607
1510
|
* - `surfaces/external/` — managed partner SDKs the SDK wraps directly
|
|
1608
|
-
* (`ai` / OpenRouter, `storage` / S3 + R2 / MinIO
|
|
1609
|
-
*
|
|
1511
|
+
* (`ai` / OpenRouter, `storage` / S3 + R2 / MinIO). Observability is not a
|
|
1512
|
+
* creator surface: agent code observes runs through the handler-scoped
|
|
1513
|
+
* `ctx.logger` (and plain `console.*`, which the wrapper captures and
|
|
1514
|
+
* correlates), while the platform primitives live in the
|
|
1515
|
+
* `@stackbone/sdk/observability` subpath the runtime wires directly.
|
|
1610
1516
|
* - `surfaces/control-plane/` — thin HTTP calls to Stackbone's control
|
|
1611
|
-
* plane (
|
|
1517
|
+
* plane (none today after secrets/config/approval moved agent-local).
|
|
1612
1518
|
* - `surfaces/pending/` — public surface is stable but the runtime is
|
|
1613
1519
|
* not built yet. They are wired as live `client.X` accessors so MVP
|
|
1614
1520
|
* agent code can be authored against the eventual contract today; each
|
|
1615
1521
|
* method returns `Result<{ error: { code: "not_implemented" } }>` until
|
|
1616
|
-
* the backing runtime ships. Today: queues, memory
|
|
1617
|
-
* connections, events.
|
|
1522
|
+
* the backing runtime ships. Today: queues, memory.
|
|
1618
1523
|
*
|
|
1619
1524
|
* Each accessor builds its surface on first access and caches it — env
|
|
1620
1525
|
* vars and partner SDK init costs are paid only when the agent actually
|
|
@@ -1632,15 +1537,12 @@ declare class StackboneClient {
|
|
|
1632
1537
|
private _storage?;
|
|
1633
1538
|
private _ai?;
|
|
1634
1539
|
private _rag?;
|
|
1635
|
-
private _observability?;
|
|
1636
1540
|
private _approval?;
|
|
1637
1541
|
private _secrets?;
|
|
1638
1542
|
private _config?;
|
|
1639
1543
|
private _queues?;
|
|
1640
1544
|
private _memory?;
|
|
1641
|
-
private _events?;
|
|
1642
1545
|
private _prompts?;
|
|
1643
|
-
private _connections?;
|
|
1644
1546
|
private _httpClient?;
|
|
1645
1547
|
private readonly _contractStore;
|
|
1646
1548
|
constructor(resolved: ResolvedConfig);
|
|
@@ -1649,16 +1551,16 @@ declare class StackboneClient {
|
|
|
1649
1551
|
get storage(): StorageModule;
|
|
1650
1552
|
get ai(): AiModule;
|
|
1651
1553
|
get rag(): RagModule;
|
|
1652
|
-
get observability(): ObservabilityModule;
|
|
1653
1554
|
get approval(): ApprovalFacade;
|
|
1654
1555
|
get secrets(): SecretsFacade;
|
|
1655
1556
|
get config(): ConfigFacade;
|
|
1656
1557
|
/**
|
|
1657
|
-
*
|
|
1658
|
-
* `
|
|
1659
|
-
*
|
|
1660
|
-
* Gated against `queues.
|
|
1661
|
-
* missing paths are exercised the same way
|
|
1558
|
+
* Live surface — the agent's handle on the BullMQ-backed job system.
|
|
1559
|
+
* `publish` / `schedule` / `unschedule` / `listSchedules` make authenticated
|
|
1560
|
+
* calls to the control-plane dispatcher (`/api/v1/agent/queues/*`); the
|
|
1561
|
+
* agent never touches Redis. Gated against `queues.jobs` so the
|
|
1562
|
+
* handshake-blocked / capability-missing paths are exercised the same way
|
|
1563
|
+
* as the other gated surfaces.
|
|
1662
1564
|
*/
|
|
1663
1565
|
get queues(): QueuesModule;
|
|
1664
1566
|
/**
|
|
@@ -1668,23 +1570,14 @@ declare class StackboneClient {
|
|
|
1668
1570
|
*/
|
|
1669
1571
|
get memory(): MemoryModule;
|
|
1670
1572
|
/**
|
|
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.
|
|
1573
|
+
* Live agent-local surface — reads prompts from `stackbone_platform.prompts`
|
|
1574
|
+
* in the agent DB over the shared `client.database` pool and compiles
|
|
1575
|
+
* templates with the local `@stackbone/prompt-compiler`. Constructed with
|
|
1576
|
+
* the same `(resolved, () => this.database)` shape as `secrets`/`config`;
|
|
1577
|
+
* like them it is NOT wired to the control-plane handshake gate (it talks to
|
|
1578
|
+
* the agent's own tables, not a control-plane datapath).
|
|
1680
1579
|
*/
|
|
1681
1580
|
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
1581
|
/**
|
|
1689
1582
|
* Read-only view of the most recently resolved Stackbone Agent Protocol
|
|
1690
1583
|
* contract for this client's `stackboneApiUrl`. Returns `null` until a
|
|
@@ -1781,13 +1674,13 @@ interface InvokeContext<I extends z.ZodType> {
|
|
|
1781
1674
|
logger: Logger;
|
|
1782
1675
|
}
|
|
1783
1676
|
/**
|
|
1784
|
-
* The
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1677
|
+
* The synchronous `invoke` capability. Mirrors the wrapper's three HTTP verbs
|
|
1678
|
+
* (`/invoke`, `/health`, `/schema`): `invoke` is the only request path, its
|
|
1679
|
+
* `output` is the `/invoke` response body, and its schemas drive `/schema`.
|
|
1787
1680
|
*
|
|
1788
1681
|
* The fields are deliberately enumerated (no `[k: string]: unknown` escape
|
|
1789
|
-
* hatch) so the
|
|
1790
|
-
*
|
|
1682
|
+
* hatch) so the precise type the creator gets from `defineAgent` rejects
|
|
1683
|
+
* unknown keys on the block at compile time.
|
|
1791
1684
|
*/
|
|
1792
1685
|
interface InvokeCapability<I extends z.ZodType, O extends z.ZodType> {
|
|
1793
1686
|
/** Zod schema for the parsed `/invoke` request body. */
|
|
@@ -1796,47 +1689,232 @@ interface InvokeCapability<I extends z.ZodType, O extends z.ZodType> {
|
|
|
1796
1689
|
output: O;
|
|
1797
1690
|
/** Creator-supplied handler. Returns either the parsed output or a promise of it. */
|
|
1798
1691
|
run: (ctx: InvokeContext<I>) => z.infer<O> | Promise<z.infer<O>>;
|
|
1799
|
-
/** Optional per-invocation timeout in milliseconds.
|
|
1692
|
+
/** Optional per-invocation timeout in milliseconds. */
|
|
1693
|
+
timeoutMs?: number;
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* A named **job handler** — a sibling of `invoke` at the top level of the spec,
|
|
1697
|
+
* reached asynchronously via `client.queues.publish({ name })` (the BullMQ
|
|
1698
|
+
* dispatcher pushes the job back to `/invoke` with that name; the wrapper routes
|
|
1699
|
+
* it here instead of to `invoke.run`).
|
|
1700
|
+
*
|
|
1701
|
+
* Same shape as `invoke` with one difference: `output` is **optional**. A job is
|
|
1702
|
+
* fire-and-forget — the dispatcher only checks the 2xx, the return value is
|
|
1703
|
+
* journalled, not delivered to a caller. Declaring `output` self-validates the
|
|
1704
|
+
* job's result; omitting it lets `run` return anything (or nothing).
|
|
1705
|
+
*/
|
|
1706
|
+
interface JobCapability<I extends z.ZodType, O extends z.ZodType = z.ZodType> {
|
|
1707
|
+
/** Zod schema for the job payload (`publish`'s `payload`, revalidated here). */
|
|
1708
|
+
input: I;
|
|
1709
|
+
/** Optional Zod schema for the job's return value. Validated + journalled, never returned. */
|
|
1710
|
+
output?: O;
|
|
1711
|
+
/** Creator-supplied job handler. May return nothing — the result is not delivered. */
|
|
1712
|
+
run: (ctx: InvokeContext<I>) => z.infer<O> | void | Promise<z.infer<O> | void>;
|
|
1713
|
+
/** Optional per-job timeout in milliseconds. */
|
|
1800
1714
|
timeoutMs?: number;
|
|
1801
1715
|
}
|
|
1802
1716
|
/**
|
|
1803
|
-
*
|
|
1717
|
+
* Loose, non-generic view of a single capability (invoke or a job handler).
|
|
1718
|
+
* The wrapper / `loadSpec` read the spec through this — the precise per-handler
|
|
1719
|
+
* typing lives on the value `defineAgent` returns to the creator, not here.
|
|
1804
1720
|
*
|
|
1805
|
-
* `
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1721
|
+
* `run` is a method signature (not an arrow property) on purpose: it makes the
|
|
1722
|
+
* parameter bivariant so every concrete `InvokeCapability` / `JobCapability`
|
|
1723
|
+
* (whose `run` is typed against its own narrow input) is assignable to it, which
|
|
1724
|
+
* is what lets `AgentSpec` carry a string index signature of capabilities.
|
|
1725
|
+
*/
|
|
1726
|
+
interface AnyCapability {
|
|
1727
|
+
input: z.ZodType;
|
|
1728
|
+
output?: z.ZodType | undefined;
|
|
1729
|
+
run(ctx: InvokeContext<z.ZodType>): unknown;
|
|
1730
|
+
timeoutMs?: number | undefined;
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* The declarative shape every Stackbone agent returns from `src/index.ts`, as
|
|
1734
|
+
* the wrapper / `loadSpec` consume it: the **loose runtime view**. Every
|
|
1735
|
+
* capability — `invoke` and each named job handler — is an `AnyCapability`
|
|
1736
|
+
* (`output` optional, `run` loose). `invoke` is required; the string index
|
|
1737
|
+
* carries the job handlers, reached by name through the BullMQ dispatcher.
|
|
1809
1738
|
*
|
|
1810
|
-
*
|
|
1811
|
-
*
|
|
1739
|
+
* The precise per-handler typing (each `ctx.input` inferred, `invoke.output`
|
|
1740
|
+
* required) lives on the value `defineAgent` returns, which is assignable to
|
|
1741
|
+
* this. At runtime `invoke.output` is always present — the spec seed forces it —
|
|
1742
|
+
* so the wrapper reads it behind a small guard.
|
|
1812
1743
|
*/
|
|
1813
|
-
interface AgentSpec
|
|
1814
|
-
|
|
1744
|
+
interface AgentSpec {
|
|
1745
|
+
/** The synchronous entry: the `/invoke` response + the `GET /schema` source. */
|
|
1746
|
+
invoke: AnyCapability;
|
|
1747
|
+
/** Named job handlers, reached via `client.queues.publish({ name })`. */
|
|
1748
|
+
[name: string]: AnyCapability;
|
|
1815
1749
|
}
|
|
1816
1750
|
/**
|
|
1817
|
-
*
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1820
|
-
*
|
|
1751
|
+
* Top-level keys the platform owns. A job handler may not use these names.
|
|
1752
|
+
* Only `invoke` for now; the contract adds `health` / `schema` / `events` /
|
|
1753
|
+
* `schedules` here if/when they become declarable capabilities (see the named
|
|
1754
|
+
* job handlers ADR).
|
|
1755
|
+
*/
|
|
1756
|
+
declare const RESERVED_HANDLER_NAMES: readonly ["invoke"];
|
|
1757
|
+
/**
|
|
1758
|
+
* Hard ceiling on any capability's `timeoutMs`. The platform saga kills the
|
|
1759
|
+
* Machine before the wrapper would time out at higher values, so a creator
|
|
1760
|
+
* declaring a 10-minute timeout would publish a contract the runtime cannot
|
|
1761
|
+
* keep. Surface that conflict at boot instead of as a mysterious mid-invocation
|
|
1821
1762
|
* SIGKILL in production.
|
|
1822
1763
|
*/
|
|
1823
1764
|
declare const INVOKE_TIMEOUT_HARD_CAP_MS = 300000;
|
|
1824
1765
|
/**
|
|
1825
|
-
*
|
|
1766
|
+
* One handler block, generic **directly** on its input Zod schema `I`. `input: I`
|
|
1767
|
+
* is a single-level type parameter (not a nested indexed access like
|
|
1768
|
+
* `S['input']`), which is the shape TypeScript can invert when reading the spec
|
|
1769
|
+
* literal back — so every handler's `ctx.input` is inferred from its own schema.
|
|
1770
|
+
* `output` stays loose (`z.ZodType`) and `run`'s return is `unknown`: a handler's
|
|
1771
|
+
* result is validated against `output` at runtime (the wrapper), not at the type
|
|
1772
|
+
* layer, so output inference is not needed for the contract to hold.
|
|
1773
|
+
*/
|
|
1774
|
+
type HandlerBlock<I extends z.ZodType> = {
|
|
1775
|
+
input: I;
|
|
1776
|
+
output?: z.ZodType;
|
|
1777
|
+
run: (ctx: InvokeContext<I>) => unknown | Promise<unknown>;
|
|
1778
|
+
timeoutMs?: number;
|
|
1779
|
+
};
|
|
1780
|
+
/**
|
|
1781
|
+
* Per-key precise shape of the spec the creator writes. `T` maps each handler
|
|
1782
|
+
* name to its **input** schema; `[K in keyof T]: HandlerBlock<T[K]>` is a
|
|
1783
|
+
* homomorphic mapped type whose value (`input: T[K]`) is a single-level access,
|
|
1784
|
+
* so TypeScript infers `T` back from the literal and keeps the concrete key
|
|
1785
|
+
* names. The `& { invoke: ... }` arm makes `invoke` required and forces its
|
|
1786
|
+
* `output` to be present (a job's `output` is optional).
|
|
1787
|
+
*/
|
|
1788
|
+
type AgentSpecParam<T extends Record<string, z.ZodType>> = {
|
|
1789
|
+
[K in keyof T]: HandlerBlock<T[K]>;
|
|
1790
|
+
} & {
|
|
1791
|
+
invoke: {
|
|
1792
|
+
input: z.ZodType;
|
|
1793
|
+
output: z.ZodType;
|
|
1794
|
+
};
|
|
1795
|
+
};
|
|
1796
|
+
/**
|
|
1797
|
+
* Anchor + guard for `defineAgent`:
|
|
1826
1798
|
*
|
|
1827
|
-
* 1. **Inference anchor** —
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
* 2. **Boot-time invariant** —
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
1834
|
-
* requires the runtime check too.
|
|
1799
|
+
* 1. **Inference anchor** — each handler (`invoke` and every named job) infers
|
|
1800
|
+
* its own `ctx.input` from its `input` schema. The creator never repeats a
|
|
1801
|
+
* type.
|
|
1802
|
+
* 2. **Boot-time invariant** — `invoke` is required; every other top-level key
|
|
1803
|
+
* must be a valid handler object reached via `client.queues.publish({ name })`.
|
|
1804
|
+
* The TypeScript signature already shapes this; the runtime guard is the
|
|
1805
|
+
* safety net for creators forcing a shape through `as any`.
|
|
1835
1806
|
*
|
|
1836
1807
|
* Returns the spec verbatim so callers can `export default defineAgent({...})`
|
|
1837
1808
|
* and the wrapper can read the same object back through dynamic import.
|
|
1838
1809
|
*/
|
|
1839
|
-
declare
|
|
1810
|
+
declare function defineAgent<T extends Record<string, z.ZodType>>(spec: AgentSpecParam<T>): {
|
|
1811
|
+
[K in keyof T]: HandlerBlock<T[K]>;
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
interface InvocationContext {
|
|
1815
|
+
/** The envelope's `invocationId` — surfaced on log lines as `trace_id`. */
|
|
1816
|
+
readonly invocationId: string;
|
|
1817
|
+
/** The run this invoke belongs to — surfaced on log lines as `run_id`. */
|
|
1818
|
+
readonly runId: string;
|
|
1819
|
+
/**
|
|
1820
|
+
* The handler this invoke routed to — `invoke` for a direct entry or the
|
|
1821
|
+
* named job handler (e.g. `demo-log-job`) the dispatcher delivered to.
|
|
1822
|
+
* Surfaced on log lines as `handler` so the Studio logs trail can tell which
|
|
1823
|
+
* entrypoint emitted each line. Optional so a caller that binds a context
|
|
1824
|
+
* without routing info (older tests, lifecycle hooks) still type-checks.
|
|
1825
|
+
*/
|
|
1826
|
+
readonly handler?: string;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Runs `fn` with `context` bound as the active invocation. The context stays
|
|
1830
|
+
* available to every synchronous and asynchronous continuation rooted in this
|
|
1831
|
+
* call (i.e. across `await`s inside `fn`). Returns whatever `fn` returns.
|
|
1832
|
+
*/
|
|
1833
|
+
declare function runWithInvocationContext<T>(context: InvocationContext, fn: () => T): T;
|
|
1834
|
+
/**
|
|
1835
|
+
* Returns the active invocation context, or `undefined` when called outside any
|
|
1836
|
+
* invoke (process boot, lifecycle events, tests that don't set one up).
|
|
1837
|
+
*/
|
|
1838
|
+
declare function getInvocationContext(): InvocationContext | undefined;
|
|
1839
|
+
|
|
1840
|
+
/** The subset of the global `console` this bridge replaces. */
|
|
1841
|
+
interface ConsoleLike {
|
|
1842
|
+
log: (...args: unknown[]) => void;
|
|
1843
|
+
info: (...args: unknown[]) => void;
|
|
1844
|
+
warn: (...args: unknown[]) => void;
|
|
1845
|
+
error: (...args: unknown[]) => void;
|
|
1846
|
+
debug: (...args: unknown[]) => void;
|
|
1847
|
+
}
|
|
1848
|
+
interface InstallConsoleCaptureOptions {
|
|
1849
|
+
/** Console object to patch. Defaults to the global `console`. Test seam. */
|
|
1850
|
+
console?: ConsoleLike;
|
|
1851
|
+
/** Sink for `log` / `info` / `debug`. Defaults to `process.stdout`. Test seam. */
|
|
1852
|
+
stdout?: LogSink;
|
|
1853
|
+
/** Sink for `warn` / `error`. Defaults to `process.stderr`. Test seam. */
|
|
1854
|
+
stderr?: LogSink;
|
|
1855
|
+
/** Clock for the `ts` field. Defaults to `() => new Date()`. Test seam. */
|
|
1856
|
+
now?: () => Date;
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Replaces `console.log/info/debug/warn/error` so that, while an invocation
|
|
1860
|
+
* context is active, each call is re-emitted as a structured log line carrying
|
|
1861
|
+
* `run_id` / `trace_id`. Returns a function that restores the originals.
|
|
1862
|
+
* Calling it twice on the same console is a no-op (returns a no-op restore).
|
|
1863
|
+
*/
|
|
1864
|
+
declare function installInvocationConsoleCapture(options?: InstallConsoleCaptureOptions): () => void;
|
|
1865
|
+
|
|
1866
|
+
declare const STORED_TO_SDK_ENV: Readonly<Record<string, string>>;
|
|
1867
|
+
/** Encrypted envelope row as stored in `stackbone_platform.secrets`. */
|
|
1868
|
+
interface SystemSecretRow {
|
|
1869
|
+
name: string;
|
|
1870
|
+
version: string;
|
|
1871
|
+
nonce: Buffer | Uint8Array;
|
|
1872
|
+
ciphertext: Buffer | Uint8Array;
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Maps the `is_system` rows to the SDK env names, decrypting ONLY the rows that
|
|
1876
|
+
* have a known mapping, then derives two env-only channels. Existing values are
|
|
1877
|
+
* never overwritten (an explicit container/Machine override always wins). The
|
|
1878
|
+
* SDK env names this call populated are returned (sorted) so the caller can log
|
|
1879
|
+
* an observable, value-free "what got rehydrated" line — values are never
|
|
1880
|
+
* logged. Shared by both the cloud neon reader and the postgres-js reader so
|
|
1881
|
+
* they cannot drift on the mapping.
|
|
1882
|
+
*
|
|
1883
|
+
* Decryption is lazy on purpose: an unmapped stored row is skipped WITHOUT
|
|
1884
|
+
* decrypting it, so a non-envelope row (e.g. a probe row a stubbed query
|
|
1885
|
+
* returns) never throws. The two derived channels:
|
|
1886
|
+
* - `STACKBONE_POSTGRES_URL` from `databaseUrl` (the SDK's `client.database`
|
|
1887
|
+
* reads this name; the cloud harness's own Neon driver keeps `DATABASE_URL`).
|
|
1888
|
+
* - `STACKBONE_APPROVAL_SIGNING_KEY` from `HMAC_SECRET` so the SDK's approval
|
|
1889
|
+
* callback verifier reconciles with the signer (which signs with the same
|
|
1890
|
+
* boot-bundle HMAC).
|
|
1891
|
+
*/
|
|
1892
|
+
declare function rehydrateSystemSecretsRows(env: NodeJS.ProcessEnv, rows: readonly SystemSecretRow[], cipher: SecretCipher, databaseUrl: string): string[];
|
|
1893
|
+
/**
|
|
1894
|
+
* Reads the `is_system` rows from `stackbone_platform.secrets`. Injectable so
|
|
1895
|
+
* tests run against a stub without a real Postgres; production opens a
|
|
1896
|
+
* short-lived `postgres-js` connection (see `loadSystemSecretsIntoEnv`).
|
|
1897
|
+
*/
|
|
1898
|
+
type SystemSecretsReader = () => Promise<SystemSecretRow[]>;
|
|
1899
|
+
interface LoadSystemSecretsArgs {
|
|
1900
|
+
/** Connection string for the agent's Postgres (`STACKBONE_POSTGRES_URL`). */
|
|
1901
|
+
readonly databaseUrl: string;
|
|
1902
|
+
/** Per-agent base64 key the rows are decrypted with (`STACKBONE_SECRET_KEY`). */
|
|
1903
|
+
readonly secretKey: string;
|
|
1904
|
+
/** Target env to populate. Defaults to `process.env`. */
|
|
1905
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
1906
|
+
/** Override the row reader (tests inject a stub). */
|
|
1907
|
+
readonly reader?: SystemSecretsReader;
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Reads every `is_system` row from the agent's Postgres via `postgres-js`,
|
|
1911
|
+
* decrypts each with the per-agent key, and rehydrates them into `env` under
|
|
1912
|
+
* the SDK's expected names. Used by the `stackbone dev` emulator and the
|
|
1913
|
+
* self-host runtime entry to give the agent the same credentials the cloud
|
|
1914
|
+
* harness rehydrates — without depending on a Neon HTTP endpoint, which local
|
|
1915
|
+
* dev does not have.
|
|
1916
|
+
*/
|
|
1917
|
+
declare function loadSystemSecretsIntoEnv(args: LoadSystemSecretsArgs): Promise<string[]>;
|
|
1840
1918
|
|
|
1841
1919
|
/**
|
|
1842
1920
|
* Reserved error codes. Order is intentional: keep them flat (string) so
|
|
@@ -1968,4 +2046,4 @@ interface JsonSchemaDocument {
|
|
|
1968
2046
|
*/
|
|
1969
2047
|
declare const analyzeAgentSchemas: (pair: AgentSchemaPair) => SchemaIntrospectionResult;
|
|
1970
2048
|
|
|
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
|
|
2049
|
+
export { type AddMemoryRequest, type AgentSchemaPair, type AgentSpec, type AnyCapability, type ApprovalListOptions, type ApprovalListResult, type ApprovalRecord, type ApprovalRequest, type ApprovalRequestOptions, type ApprovalStatus, type ApprovalTool, type ApprovalToolSpec, type ApprovalTopic, type ApproverInfo, type ClientConfig, type CompilePromptResult, type ConsoleLike, type CreatePromptRequest, type Decision, type DecisionStatus, type DeleteAllMemoryRequest, type DeletePromptOptions, type DeletePromptResult, type EndSessionOptions, type EndSessionResult, type FatalConstruct, type GeneratedImage, type GetPromptOptions, INVOCATION_ID_HEADER, INVOKE_TIMEOUT_HARD_CAP_MS, type ImageGenerateParams, type ImagesResponse, type IngestAsyncHandle, type InstallConsoleCaptureOptions, type InvocationContext, type InvokeCapability, type InvokeContext, type InvokeEnvelopeError, type InvokeErrorEnvelope, type InvokeRequest, type InvokeResponse, type InvokeSuccessEnvelope, type JobCapability, type JsonSchemaDocument, type ListMemoryRequest, type ListMemoryResult, type ListOptions, type ListPromptsOptions, type ListPromptsResult, type LoadSystemSecretsArgs, type LogSink, type Logger, type LoggerBindings, type MemoryContent, type MemoryHistoryEntry, type MemoryHistoryEvent, type MemoryHit, type MemoryItem, type MemoryScope, type ModelsListResponse, type OpenRouterModel, type Prompt, type PublishRequest, RESERVED_ERROR_CODES, RESERVED_HANDLER_NAMES, RUN_ID_HEADER, type ReservedErrorCode, type Result, STORED_TO_SDK_ENV, type ScheduleRequest, type SchemaDiagnostic, type SchemaHalf, type SchemaIntrospectionResult, type SdkError, type SdkErrorCode, type SearchMemoryOptions, type SignedUrl, type SignedUrlOptions, StackboneClient, type StorageBody, type StorageObject, type StructuredLoggerOptions, type SystemSecretRow, type SystemSecretsReader, type UnscheduleRequest, type UpdateMemoryOptions, type UpdatePromptOptions, type UploadOptions, type VerifyOptions, type WarnConstruct, analyzeAgentSchemas, createClient, createStructuredLogger, defineAgent, getInvocationContext, installInvocationConsoleCapture, invokeRequestSchema, isReservedErrorCode, isSdkErrorCode, loadSystemSecretsIntoEnv, rehydrateSystemSecretsRows, runWithInvocationContext };
|