@stackbone/sdk 0.1.0-alpha.5 → 0.1.0-alpha.6
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 +2 -0
- package/index.cjs +106 -36
- package/index.cjs.map +1 -1
- package/index.d.cts +126 -4
- package/index.d.ts +126 -4
- package/index.js +106 -36
- package/index.js.map +1 -1
- package/observability/index.cjs +1 -1
- package/observability/index.cjs.map +1 -1
- package/observability/index.d.cts +1 -1
- package/observability/index.d.ts +1 -1
- package/observability/index.js +1 -1
- package/observability/index.js.map +1 -1
- package/package.json +1 -1
- package/stackbone-sdk-0.1.0-alpha.6.tgz +0 -0
- package/stackbone-sdk-0.1.0-alpha.5.tgz +0 -0
package/index.js
CHANGED
|
@@ -8870,7 +8870,8 @@ var journalTerminalStatusSchema = z.enum([
|
|
|
8870
8870
|
]);
|
|
8871
8871
|
var journalJobStatusSchema = z.enum([
|
|
8872
8872
|
...journalTerminalStatusSchema.options,
|
|
8873
|
-
"running"
|
|
8873
|
+
"running",
|
|
8874
|
+
"lost"
|
|
8874
8875
|
]);
|
|
8875
8876
|
var jobStatusSchema = z.enum([
|
|
8876
8877
|
...bullmqJobStatusSchema.options,
|
|
@@ -8972,6 +8973,27 @@ z.object({
|
|
|
8972
8973
|
queue: z.string(),
|
|
8973
8974
|
status: jobStatusSchema
|
|
8974
8975
|
});
|
|
8976
|
+
z.object({
|
|
8977
|
+
items: z.array(bullmqJobSchema)
|
|
8978
|
+
});
|
|
8979
|
+
z.object({
|
|
8980
|
+
purged: z.number().int().nonnegative()
|
|
8981
|
+
});
|
|
8982
|
+
z.object({
|
|
8983
|
+
payload: jsonObject(),
|
|
8984
|
+
retries: z.number().int().nonnegative().optional(),
|
|
8985
|
+
delay: z.number().int().nonnegative().optional()
|
|
8986
|
+
});
|
|
8987
|
+
z.object({
|
|
8988
|
+
message_id: z.string(),
|
|
8989
|
+
queue: queueNameSchema
|
|
8990
|
+
});
|
|
8991
|
+
z.object({
|
|
8992
|
+
name: queueNameSchema
|
|
8993
|
+
});
|
|
8994
|
+
z.object({
|
|
8995
|
+
limit: z.coerce.number().int().min(1).max(50).default(10)
|
|
8996
|
+
});
|
|
8975
8997
|
|
|
8976
8998
|
// ../shared/validators/src/lib/agents/queue-jobs.ts
|
|
8977
8999
|
var MAX_JOB_ATTEMPTS = 20;
|
|
@@ -9930,7 +9952,7 @@ var ApprovalFacade = class {
|
|
|
9930
9952
|
const fallback = onTimeoutToFallback(options.onTimeout);
|
|
9931
9953
|
const callbackUrl = options.onDecide;
|
|
9932
9954
|
try {
|
|
9933
|
-
const rows = await
|
|
9955
|
+
const rows = await this._getDatabase().runShared((db) => db`
|
|
9934
9956
|
INSERT INTO stackbone_platform.approvals (
|
|
9935
9957
|
topic, payload, callback_url, idempotency_key,
|
|
9936
9958
|
fallback, metadata, timeout_at
|
|
@@ -9949,7 +9971,7 @@ var ApprovalFacade = class {
|
|
|
9949
9971
|
RETURNING id, callback_url,
|
|
9950
9972
|
to_char(created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS created_at,
|
|
9951
9973
|
to_char(timeout_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS timeout_at
|
|
9952
|
-
|
|
9974
|
+
`);
|
|
9953
9975
|
const row = rows[0];
|
|
9954
9976
|
if (!row) {
|
|
9955
9977
|
return err({
|
|
@@ -9984,13 +10006,13 @@ var ApprovalFacade = class {
|
|
|
9984
10006
|
const patch = reason ? {
|
|
9985
10007
|
cancelReason: reason
|
|
9986
10008
|
} : {};
|
|
9987
|
-
await
|
|
10009
|
+
await this._getDatabase().runShared((db) => db`
|
|
9988
10010
|
UPDATE stackbone_platform.approvals
|
|
9989
10011
|
SET status = 'cancelled',
|
|
9990
10012
|
decided_at = now(),
|
|
9991
10013
|
metadata = metadata || ${JSON.stringify(patch)}::jsonb
|
|
9992
10014
|
WHERE id = ${approvalId}::uuid AND status = 'pending'
|
|
9993
|
-
|
|
10015
|
+
`);
|
|
9994
10016
|
return ok(void 0);
|
|
9995
10017
|
} catch (cause) {
|
|
9996
10018
|
return err({
|
|
@@ -10010,14 +10032,14 @@ var ApprovalFacade = class {
|
|
|
10010
10032
|
const sql = this.sql();
|
|
10011
10033
|
if (sql.error) return err(sql.error);
|
|
10012
10034
|
try {
|
|
10013
|
-
const rows = await
|
|
10035
|
+
const rows = await this._getDatabase().runShared((db) => db`
|
|
10014
10036
|
SELECT id, topic, status, payload, metadata,
|
|
10015
10037
|
to_char(created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS created_at,
|
|
10016
10038
|
to_char(timeout_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS timeout_at
|
|
10017
10039
|
FROM stackbone_platform.approvals
|
|
10018
10040
|
WHERE id = ${approvalId}::uuid
|
|
10019
10041
|
LIMIT 1
|
|
10020
|
-
|
|
10042
|
+
`);
|
|
10021
10043
|
const row = rows[0];
|
|
10022
10044
|
if (!row) {
|
|
10023
10045
|
return err({
|
|
@@ -10042,7 +10064,7 @@ var ApprovalFacade = class {
|
|
|
10042
10064
|
if (sql.error) return err(sql.error);
|
|
10043
10065
|
const limit = clampLimit(options.limit);
|
|
10044
10066
|
try {
|
|
10045
|
-
const rows = await
|
|
10067
|
+
const rows = await this._getDatabase().runShared((db) => db`
|
|
10046
10068
|
SELECT id, topic, status, payload, metadata,
|
|
10047
10069
|
to_char(created_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS created_at,
|
|
10048
10070
|
to_char(timeout_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS timeout_at
|
|
@@ -10051,7 +10073,7 @@ var ApprovalFacade = class {
|
|
|
10051
10073
|
AND (${options.topic ?? null}::text IS NULL OR topic = ${options.topic ?? null})
|
|
10052
10074
|
ORDER BY created_at DESC, id DESC
|
|
10053
10075
|
LIMIT ${limit}
|
|
10054
|
-
|
|
10076
|
+
`);
|
|
10055
10077
|
return ok({
|
|
10056
10078
|
items: rows.map((row) => toRecord(row))
|
|
10057
10079
|
});
|
|
@@ -10217,9 +10239,9 @@ var ConfigFacade = class {
|
|
|
10217
10239
|
const sql = this.sql();
|
|
10218
10240
|
if (sql.error) return err(sql.error);
|
|
10219
10241
|
try {
|
|
10220
|
-
const rows = await
|
|
10242
|
+
const rows = await this._getDatabase().runShared((db) => db`
|
|
10221
10243
|
SELECT payload FROM stackbone_platform.agent_config WHERE id = 1
|
|
10222
|
-
|
|
10244
|
+
`);
|
|
10223
10245
|
return ok(rows[0]?.payload ?? null);
|
|
10224
10246
|
} catch (cause) {
|
|
10225
10247
|
return err({
|
|
@@ -10293,6 +10315,10 @@ function isPoolTerminatedError(error) {
|
|
|
10293
10315
|
return typeof code === "string" && POOL_TERMINATED_ERROR_CODES.has(code);
|
|
10294
10316
|
}
|
|
10295
10317
|
__name(isPoolTerminatedError, "isPoolTerminatedError");
|
|
10318
|
+
function invalidatePoolHandle() {
|
|
10319
|
+
cachedHandle = null;
|
|
10320
|
+
}
|
|
10321
|
+
__name(invalidatePoolHandle, "invalidatePoolHandle");
|
|
10296
10322
|
async function runWithColdStartRetry(firstAttempt, retryAttempt) {
|
|
10297
10323
|
try {
|
|
10298
10324
|
return await firstAttempt();
|
|
@@ -10461,6 +10487,48 @@ var DatabaseModule = class {
|
|
|
10461
10487
|
// a half-baked tx. The contract gate fires before the first attempt.
|
|
10462
10488
|
transaction = /* @__PURE__ */ __name((...args) => withColdStartRetry(() => getDatabaseHandle(this._databaseUrl).transaction(...args), this._gate), "transaction");
|
|
10463
10489
|
/**
|
|
10490
|
+
* Cross-surface raw-SQL runner carrying the SAME cold-start resilience the
|
|
10491
|
+
* query-builder verbs above get. Sibling agent-local surfaces (`approval`,
|
|
10492
|
+
* `config`, `secrets`, `prompts`) that issue tagged-template queries over the
|
|
10493
|
+
* shared postgres-js `Sql` MUST execute them through this instead of caching
|
|
10494
|
+
* `shared().$client` once and calling it directly. Otherwise a dead pooled
|
|
10495
|
+
* socket — Fly Machines suspending the Machine between invokes, or a
|
|
10496
|
+
* WSL2/Docker localhost relay dropping an idle connection during a long LLM
|
|
10497
|
+
* step — surfaces as an unretried `CONNECTION_ENDED`/`ECONNRESET`. That is
|
|
10498
|
+
* exactly the asymmetry that let `client.approval.request` fail while the
|
|
10499
|
+
* `client.database.insert` right before it recovered.
|
|
10500
|
+
*
|
|
10501
|
+
* `fn` receives a FRESH `$client` on each attempt, so the single retry runs
|
|
10502
|
+
* against the rebuilt pool. Whole-operation granularity is replayed as a
|
|
10503
|
+
* unit — pass a multi-statement `sql.begin(...)` transaction or a helper that
|
|
10504
|
+
* fires several queries and the replay re-runs all of it; postgres-js never
|
|
10505
|
+
* commits a half-applied tx, so that is safe. SQLSTATE application errors
|
|
10506
|
+
* pass straight through without a retry. Like `shared()`/`raw()`, the
|
|
10507
|
+
* contract gate is intentionally NOT consulted here — gating surfaces gate at
|
|
10508
|
+
* their own call sites.
|
|
10509
|
+
*/
|
|
10510
|
+
runShared(fn) {
|
|
10511
|
+
return withColdStartRetry(() => fn(getDatabaseHandle(this._databaseUrl).$client));
|
|
10512
|
+
}
|
|
10513
|
+
/**
|
|
10514
|
+
* `Result`-shaped sibling of `runShared` for surfaces whose driver swallows
|
|
10515
|
+
* the dead-socket error into a returned `Result.err.cause` instead of
|
|
10516
|
+
* throwing it — today only `client.rag`, whose `RagPipeline` returns a
|
|
10517
|
+
* `Result` rather than throwing. `attempt` runs once; if it comes back with a
|
|
10518
|
+
* pool-terminated `cause`, the pool is rebuilt and `attempt` replays exactly
|
|
10519
|
+
* once, then its result (success or the fresh error) is returned verbatim.
|
|
10520
|
+
* The `Result` contract is preserved end to end: a still-dead socket on the
|
|
10521
|
+
* replay surfaces as the surface's own `Result.err`, never as a raw throw.
|
|
10522
|
+
*/
|
|
10523
|
+
async runSharedResult(attempt) {
|
|
10524
|
+
const first = await attempt();
|
|
10525
|
+
if (!first.error || !isPoolTerminatedError(first.error.cause)) return first;
|
|
10526
|
+
process.stderr.write(`[stackbone/sdk] database cold-start reconnect: rebuilding pool after ${first.error.cause.code ?? "unknown"}
|
|
10527
|
+
`);
|
|
10528
|
+
invalidatePoolHandle();
|
|
10529
|
+
return attempt();
|
|
10530
|
+
}
|
|
10531
|
+
/**
|
|
10464
10532
|
* Canonical accessor for the **shared-handles pattern**. Returns the
|
|
10465
10533
|
* process-wide Drizzle handle backing `client.database`. Cross-surface
|
|
10466
10534
|
* SDK consumers (RAG today, memory / queues tomorrow) call this — never
|
|
@@ -10562,12 +10630,12 @@ var PromptsFacade = class {
|
|
|
10562
10630
|
const sql = this.sql();
|
|
10563
10631
|
if (sql.error) return err(sql.error);
|
|
10564
10632
|
try {
|
|
10565
|
-
const head = await fetchHead(
|
|
10633
|
+
const head = await this._getDatabase().runShared((db) => fetchHead(db, key));
|
|
10566
10634
|
if (!head) {
|
|
10567
10635
|
return err(notFound(key));
|
|
10568
10636
|
}
|
|
10569
10637
|
if (options?.version !== void 0 && options.version !== head.current_version) {
|
|
10570
|
-
const content = await fetchVersionContent(
|
|
10638
|
+
const content = await this._getDatabase().runShared((db) => fetchVersionContent(db, key, options.version));
|
|
10571
10639
|
if (content === null) {
|
|
10572
10640
|
return err({
|
|
10573
10641
|
code: "prompts_not_found",
|
|
@@ -10618,7 +10686,7 @@ var PromptsFacade = class {
|
|
|
10618
10686
|
const sql = this.sql();
|
|
10619
10687
|
if (sql.error) return err(sql.error);
|
|
10620
10688
|
try {
|
|
10621
|
-
const rows = await
|
|
10689
|
+
const rows = await this._getDatabase().runShared((db) => db`
|
|
10622
10690
|
SELECT p.key, p.name, p.description, p.current_version, p.metadata,
|
|
10623
10691
|
p.created_at, p.updated_at, v.content
|
|
10624
10692
|
FROM stackbone_platform.prompts p
|
|
@@ -10627,7 +10695,7 @@ var PromptsFacade = class {
|
|
|
10627
10695
|
WHERE p.deleted_at IS NULL
|
|
10628
10696
|
ORDER BY p.key
|
|
10629
10697
|
LIMIT ${limit}
|
|
10630
|
-
|
|
10698
|
+
`);
|
|
10631
10699
|
return ok({
|
|
10632
10700
|
items: rows.map((row) => rowToPrompt(row, row.current_version, row.content))
|
|
10633
10701
|
});
|
|
@@ -10654,7 +10722,7 @@ var PromptsFacade = class {
|
|
|
10654
10722
|
if (sql.error) return err(sql.error);
|
|
10655
10723
|
const variables = extractVars(request.template);
|
|
10656
10724
|
try {
|
|
10657
|
-
const head = await
|
|
10725
|
+
const head = await this._getDatabase().runShared((db) => db.begin(async (tx) => {
|
|
10658
10726
|
await tx`
|
|
10659
10727
|
INSERT INTO stackbone_platform.prompts
|
|
10660
10728
|
(key, name, description, current_version, metadata)
|
|
@@ -10682,7 +10750,7 @@ var PromptsFacade = class {
|
|
|
10682
10750
|
WHERE p.key = ${request.key}
|
|
10683
10751
|
`;
|
|
10684
10752
|
return rows[0];
|
|
10685
|
-
});
|
|
10753
|
+
}));
|
|
10686
10754
|
if (!head) {
|
|
10687
10755
|
return err({
|
|
10688
10756
|
code: "prompts_unavailable",
|
|
@@ -10715,7 +10783,7 @@ var PromptsFacade = class {
|
|
|
10715
10783
|
const sql = this.sql();
|
|
10716
10784
|
if (sql.error) return err(sql.error);
|
|
10717
10785
|
try {
|
|
10718
|
-
const head = await
|
|
10786
|
+
const head = await this._getDatabase().runShared((db) => db.begin(async (tx) => {
|
|
10719
10787
|
await tx`SELECT pg_advisory_xact_lock(hashtext(${`prompts:${key}`}))`;
|
|
10720
10788
|
const existing = await tx`
|
|
10721
10789
|
SELECT p.key, p.name, p.description, p.current_version, p.metadata,
|
|
@@ -10759,7 +10827,7 @@ var PromptsFacade = class {
|
|
|
10759
10827
|
WHERE p.key = ${key}
|
|
10760
10828
|
`;
|
|
10761
10829
|
return rows[0] ?? null;
|
|
10762
|
-
});
|
|
10830
|
+
}));
|
|
10763
10831
|
if (head === null) {
|
|
10764
10832
|
return err(notFound(key));
|
|
10765
10833
|
}
|
|
@@ -10774,12 +10842,12 @@ var PromptsFacade = class {
|
|
|
10774
10842
|
const sql = this.sql();
|
|
10775
10843
|
if (sql.error) return err(sql.error);
|
|
10776
10844
|
try {
|
|
10777
|
-
const rows = await
|
|
10845
|
+
const rows = await this._getDatabase().runShared((db) => db`
|
|
10778
10846
|
UPDATE stackbone_platform.prompts
|
|
10779
10847
|
SET deleted_at = now(), updated_at = now()
|
|
10780
10848
|
WHERE key = ${key} AND deleted_at IS NULL
|
|
10781
10849
|
RETURNING key
|
|
10782
|
-
|
|
10850
|
+
`);
|
|
10783
10851
|
const deleted = rows.length;
|
|
10784
10852
|
if (deleted === 0) {
|
|
10785
10853
|
return err(notFound(key));
|
|
@@ -11863,8 +11931,10 @@ var RagModule = class {
|
|
|
11863
11931
|
const sql = this.sql();
|
|
11864
11932
|
if (sql.error) return err(sql.error);
|
|
11865
11933
|
try {
|
|
11866
|
-
await
|
|
11867
|
-
|
|
11934
|
+
await this._getDatabase().runShared(async (db) => {
|
|
11935
|
+
await db`DROP TABLE IF EXISTS rag_chunks`;
|
|
11936
|
+
await db`DROP TABLE IF EXISTS _rag_meta`;
|
|
11937
|
+
});
|
|
11868
11938
|
return ok(void 0);
|
|
11869
11939
|
} catch (cause) {
|
|
11870
11940
|
return err(toRagError(cause, "Reset failed"));
|
|
@@ -11914,13 +11984,15 @@ var RagModule = class {
|
|
|
11914
11984
|
}
|
|
11915
11985
|
}
|
|
11916
11986
|
async withPipeline(embedder, run) {
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
embedder
|
|
11921
|
-
|
|
11987
|
+
return this._getDatabase().runSharedResult(async () => {
|
|
11988
|
+
const pipeline = new RagPipeline({
|
|
11989
|
+
sqlProvider: /* @__PURE__ */ __name(() => this.sql(), "sqlProvider"),
|
|
11990
|
+
...embedder && {
|
|
11991
|
+
embedder
|
|
11992
|
+
}
|
|
11993
|
+
});
|
|
11994
|
+
return await run(pipeline);
|
|
11922
11995
|
});
|
|
11923
|
-
return await run(pipeline);
|
|
11924
11996
|
}
|
|
11925
11997
|
};
|
|
11926
11998
|
function countChunks(request) {
|
|
@@ -15803,12 +15875,12 @@ var SecretsFacade = class {
|
|
|
15803
15875
|
if (sql.error) return err(sql.error);
|
|
15804
15876
|
let rows;
|
|
15805
15877
|
try {
|
|
15806
|
-
rows = await
|
|
15878
|
+
rows = await this._getDatabase().runShared((db) => db`
|
|
15807
15879
|
SELECT version, nonce, ciphertext
|
|
15808
15880
|
FROM stackbone_platform.secrets
|
|
15809
15881
|
WHERE name = ${name}
|
|
15810
15882
|
LIMIT 1
|
|
15811
|
-
|
|
15883
|
+
`);
|
|
15812
15884
|
} catch (cause) {
|
|
15813
15885
|
return err({
|
|
15814
15886
|
code: "secrets_unavailable",
|
|
@@ -15854,11 +15926,11 @@ var SecretsFacade = class {
|
|
|
15854
15926
|
if (sql.error) return err(sql.error);
|
|
15855
15927
|
let rows;
|
|
15856
15928
|
try {
|
|
15857
|
-
rows = await
|
|
15929
|
+
rows = await this._getDatabase().runShared((db) => db`
|
|
15858
15930
|
SELECT name, version, nonce, ciphertext
|
|
15859
15931
|
FROM stackbone_platform.secrets
|
|
15860
15932
|
WHERE name = ANY(${names})
|
|
15861
|
-
|
|
15933
|
+
`);
|
|
15862
15934
|
} catch (cause) {
|
|
15863
15935
|
return err({
|
|
15864
15936
|
code: "secrets_unavailable",
|
|
@@ -17267,9 +17339,7 @@ var STORED_TO_SDK_ENV = {
|
|
|
17267
17339
|
S3_REGION: "STACKBONE_S3_REGION",
|
|
17268
17340
|
// OpenRouter (`client.ai`) — identity.
|
|
17269
17341
|
OPENROUTER_API_KEY: "OPENROUTER_API_KEY",
|
|
17270
|
-
OPENROUTER_BASE_URL: "OPENROUTER_BASE_URL"
|
|
17271
|
-
// Axiom (observability) — identity.
|
|
17272
|
-
AXIOM_TOKEN: "AXIOM_TOKEN"
|
|
17342
|
+
OPENROUTER_BASE_URL: "OPENROUTER_BASE_URL"
|
|
17273
17343
|
};
|
|
17274
17344
|
function rehydrateSystemSecretsRows(env, rows, cipher, databaseUrl) {
|
|
17275
17345
|
const applied = [];
|